Learn. Make. Repeat.

August 15, 2018

Nordic nRF52840 QR Code based Thread Commissioning


I recently wrote about using Thread and MQTT-SN on the Nordic nRF52840 multiprotocol SoC. One thing I did not cover in detail is Thread Commisioning - a secure process by which a device joins a Thread network. I side-stepped the process by passing the network credentials directly to the device using the Thread CLI.

There are different types of commissioning, and Thread has a Mesh Commisioning Protocol (MeshCop) to facilitate this process. In On-mesh commissioning, a node which is part of the network, which has the required credientials, adds the joiner to the network. External commissioning, on the other hand, uses a device outside the Thread network, like a mobile phone, to add new nodes to the network.

The Thread Group provides a mobile app for external commissioning. Unfortunately we found it to be very unstable (crashed on three different Android phones), and it’s for Android only in any case. I wish the Thread Group would either release the source code for this app or build a stable cross-platform solution. But since we don’t want to wait around for that to happen, I thought - Why not use a camera on the Raspberry Pi itself to do the commissioning?

But before we get to the Pi, let’s talk about how to generate QR codes for Thread commissioning.

Preparing QR Codes for Thread Commissioning

The QR code commissioning works by encoding the Joining Device Credential and the EUI-64 of the device into an image. The scanning device parses this information and passes it to the external commissioner. The Joiner Device Credential from the Thread specification, is:

a device-specific string of all uppercase alphanumeric characters (0-9 and A-Y, excluding I, O, Q and Z for readability), with a length between 6 and 32 characters

The EUI-64 is a 64 bit unique identifier for the device. For nRF52840, it’s the MAC ID (which is actually 48 bits).

The QR code is generated using a string of the following form:


You can use an online tool to generate the QR code - just use the above string in the URL field. Print it out, and you have your commissioning weapon ready.

By the way, you can get the EUI-64 of your device very conveniently by opening up a serial terminal and using the Thread CLI.

> eui64

Reading QR Code on the Raspberry Pi

Now that you have the QR code, how do you read it on the Pi? The goodwill of open source developers come to the rescue again, in the form of ZBar.

Here’s how you install it:

sudo apt-get install zbar-tools

Now in order for the above to work (it looks for dev/video0), you need enable a module on the Pi.

sudo rpi-update
sudo modprobe bcm2835-v4l2

To ensue that this is done every time the Pi boots, you can add bcm2835-v4l2 to /etc/modules.


The Nordic Raspberry Pi border image does not come with any GUI. (An unnecessary omission, making it difficult to use). So in order to understand zbar-tools, I ended up testing it with a Pi installation with proper GUI. But you don’t have to go through that necessarily.

No test out zbar-tools on the Pi as follows:

zbarcam --prescale=640x480

This is what you will see:


The program will continuously try to detect and print out the decoded strings from QR codes in the camera stream. The prescale option is needed because without it, the detection is too slow to be useful.

QR Code Commissioning

So now we know how to parse the QR codes. Now let’s write a Python program to use this information for commissioning.

def main():
  # start zbarcam process
  proc = subprocess.Popen(['zbarcam','--nodisplay', '--prescale=640x480'],
    while True:
      line = proc.stdout.readline()
      if line != '':
        # data is of the form:
        # QR-Code:v=1&&eui=2b749b2cc8427dd7&&cc=ENUT02
        data = line.strip()
        # parse
        i1 = data.find("eui=")
        eui = data[i1+1+3:i1+4+16]
        i2 = data.find("cc=")
        cc = data[i2+3:]
        print("Found device: (%s, %s)" % (eui, cc))
        commission_joiner(eui, cc)
  except Exception as ex:
  # kill process['sudo', 'kill', str(, '-s', 'SIGINT'])

The above code runs zbarcam and reads its output. Note the nodisplay option - we need this on Nordic’s Pi image since it has no GUI. (In any case we don’t need to display the video on screen.)

The EUI-64 and joiner credential are extracted from the string and passed on to methods that enable commissioning on the border router, and then join the node. We use wpanctl for these actions.

# enable commissioning
def commission_enable():
  proc = subprocess.Popen(['wpanctl','commissioner', '-e'],
  while True:
    line = proc.stdout.readline()
    if line != '':

# perform commissioning
def commission_joiner(eui, cc):
  proc = subprocess.Popen(['wpanctl','commissioner', '--joiner-add',
    cc, eui],stdout=subprocess.PIPE)  
  while True:
    line = proc.stdout.readline()
    if line != '':

Actually the wpanctl commission -e command needs to be run only once, and will ouput a warning on subsequent calls. (But since I am lazy, we’ll ignore that warning rather than handle it.) wpanctl commissioner –joiner-add cc eui64 will do the actual joining.

Here’s a typical output:

$ python 
Found device: (2b749b2cc8427dd7, ENUT02)
Commissioner command applied.
Joiner added.

Now we’ve added the joiner from the border router. Let’s now look at how to join the nework from the device.

Code on The Joiner

Our Joiner is an nRF52840-DK, and the code we’re using is adapted from the Nordic NFC MeshCoP example. Our code works as follows:

On pressing button 3 (label) on the board, we start a single-short timer that calls otJoinerStart with the joiner credential (ENUT02 in our case). This method has a callback as shown below:

static void joiner_callback(otError error, void * p_context)
	uint32_t err_code;

    if (error == OT_ERROR_NONE)
        err_code = bsp_thread_commissioning_indication_set(BSP_INDICATE_COMMISSIONING_SUCCESS);

        error = otThreadSetEnabled(thread_ot_instance_get(), true);
        ASSERT(error == OT_ERROR_NONE);
        if (m_app.joiner_retry_count < MAX_JOINER_RETRY_COUNT)

            err_code = app_timer_start(m_joiner_timer, 
                APP_TIMER_TICKS(JOINER_DELAY), thread_ot_instance_get());
            m_app.joiner_retry_count = 0;
            m_app.commissioning_in_progress = false;

            err_code = bsp_thread_commissioning_indication_set(

If the joining was successful, the above code calls otThreadSetEnabled() which starts Thread on the node. On faliure, it keeps retrying a few times.

Once the node joins successfully, the LEDs on the board will be stable and LED 2 (label) will light up.

If you really want to be sure that commissioning worked, you can open up a serial terminal and use the Thread CLI to verify that masterkey is same as the one you used to setup wpantund on the Pi border router.

In the code, button 4 (label) is hooked up to call otInstanceFactoryReset() - great for testing.

In Action

You can see the QR code Thread commissioning in action below.


And that’s how you can use a camera on the Raspberry Pi Border Router to commission your Thread device. Although this method may look like external commissioning, since we’re actually using a separate program to parse the information and run wpanctl to manually add the joiner, this looks more like a variation of on-mesh commissioning to me. Who knows? Maybe the Thread experts will tell us. In any case, it works.


You can download code for this project here:

About this Article

Title: Nordic nRF52840 QR Code based Thread Commissioning

Author: Mahesh Venkitachalam


First Published: 16 Aug 2018