nRF 52840 Bluetooth Mesh Example

Raspberry Pi with Bluetooth

The "bullseye" Raspbian distro should include enough kernel mods to let the PB-ADV mesh work. Unfortunately, the version of bluez is 5.55, which is known to be flaky. You should enable the apt testing repo to install at least version 5.62.

This requires adding a line to /etc/apt/sources.list and creating a file beneath /etc/apt/preferences.d.

To do that, run these commands:

# Become root
sudo su -

# Add the testing repo
cat >>/etc/apt/sources.list <<EOF
deb testing main contrib non-free rpi

# Prioritize stable packages
cat >/etc/apt/preferences.d/prefs <<EOF
Package: *
Pin: release a=stable
Pin-Priority: 700

Package: *
Pin: release a=testing
Pin-Priority: 650

# Update the list of available packages
apt update

To get a working version of bluez, do the following:

$ sudo apt -t testing install bluez bluez-meshd

Now you’re ready to fire up a BLE mesh. Most of this is based upon the details from the Bluetooth Site

First manually stop the existing bluetoothd

# sudo service bluetooth stop

Then start the PB-ADV-based mesh.

# sudo /usr/libexec/bluetooth/bluetooth-meshd -nd --debug

This will leave the process running in the foreground to help with debugging, etc.

Secondly, on the Pi, use mesh-cfgclient

This can be done as non-root regular user.

$ mesh-cfgclient

If it fails silently, ensure you have a ~/.config directory.

If it complains about a missing config_db.json file, create one using the create command:

pi@raspberrypi:~ $ mesh-cfgclient

Warning: config file "/home/pi/.config/meshcfg/config_db.json" not found
Proxy added: org.bluez.mesh.Network1 (/org/bluez/mesh)
[mesh-cfgclient]# create
Created new node with token 176b9238fa48857c
Proxy added: org.bluez.mesh.Node1 (/org/bluez/mesh/node326d83779e8ccbaf254d4b604dfec674)
Proxy added: org.bluez.mesh.Management1 (/org/bluez/mesh/node326d83779e8ccbaf254d4b604dfec674)
Attached with path /org/bluez/mesh/node326d83779e8ccbaf254d4b604dfec674

On the board

Flash this example onto an nRF 52840

DEFMT_LOG=debug cargo run --release

If that fails…​

If you get an error mentioning a more recent version of probe-run…​

cargo install probe-run

…​for which, on linux, you may need libudev…​

sudo dnf install rust-libudev-devel

If the app quits after a few seconds, try flashing the softdevice.


On the pi, running mesh-cfgclient:

If you know your board’s UUID — look in its startup output — feed it to the the provision command. For example,

[mesh-cfgclient]# provision 066187E34A19B375F1A02A1E934DB15A

If you don’t have the UUID, you can discover it:

[mesh-cfgclient]# discover-unprovisioned on
Unprovisioned scan started

Wait for it to discover your nRF broadcasting the "please provision me" packets. Once you see the board’s UUID, provision it.

[mesh-cfgclient]# discover-unprovisioned off
[mesh-cfgclient]# provision 066187E34A19B375F1A02A1E934DB15A

If all goes well, you should see it assigning addresses and other happiness.

Do Stuff

Right now, nothing you can really do other than taking note of the primary unicast address it assigned to your board, and then:

[mesh-cfgclient]# menu config
[mesh-cfgclient]# target 00d9
Configuring node 00d9
[config: Target = 00d9]# beacon-get

Which should hopefully report back a 0x01 for the beacon-status.

If the packet gets lost, you’ll see a no response type of output.

This is where we are currently.

What might go wrong

If the packet goes lost, no retransmits of config stuff happens (yet) so just keep repeating it until you a response if you’ve successfully provisioned.

If the board complains about nRF Softdevice interrupts being disabled for too long, that’s because of the debug output using too much critical sections. You can reduce the log level or just try again.

If you want to start back from scratch, alter the to use the .force_reset() line (once) which will factory-reset the board.

On the pi side, after menu config and setting the target to the board:

[config: Target = 00d9]# node-reset

This will remove it from the DB.

You can then back and do the discover-unprovisioned on and the provision $UUID bits again.