Deploying Contiki-NG firmware on multiple sensors with OpenTestbed

In this tutorial we will compile Contiki-NG firmware, and deploy it on multiple sensors at once with OpenTestbed.

OpenTestbed allows you to manage multiple sensors, spread over multiple devices via an MQTT-server. It was initially developed at Inria, and has subsequently been adapted for use on w-iLab.1 .

It supports:

  • automatic discovery of all available sensor motes;
  • flashing a firmware (from a URL or from a file);
  • sending serial data to one or more motes simultaneously;
  • listening to the serial data sent by the motes.

Deploy an OpenTestbed instance

An ESpec has been developed to deploy OpenTestbed on one or more nodes. It can be obtained from the espec_minimal branch of OpenTestbed .

To use it:

  1. Reserve 2 nodes on w-iLab.1 testbed
  2. Adapt the RSpec located in deployment/otb.rspec to refer to your reserved sensor nodes. Change the component_id-attributes to refer to the nodes that you reserved.
  3. Once your reservation has started, swap in the resources with the ESpec. In jFed: click Open ESpec, select the folder with your adapted ESpec and start the experiment.
  4. Wait for the resources to be swapped in, and the software to be installed via Ansible.

Install Contiki-NG

Download and install Contiki-NG and it’s dependencies as follows on one of the sensor-nodes:

$ git clone https://github.com/contiki-ng/contiki-ng.git
Cloning into 'contiki-ng'...
remote: Enumerating objects: 15, done.
remote: Counting objects: 100% (15/15), done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 123115 (delta 2), reused 6 (delta 1), pack-reused 123100
Receiving objects: 100% (123115/123115), 74.57 MiB | 28.15 MiB/s, done.
Resolving deltas: 100% (90766/90766), done.
Checking connectivity... done.
$ cd contiki-ng
$ git checkout -b release/v4.2
On branch release/v4.2
nothing to commit, working directory clean
$ git submodule update --init --recursive
Submodule 'arch/cpu/cc26xx-cc13xx/lib/cc13xxware' (https://github.com/contiki-ng/cc13xxware.git) registered for path 'arch/cpu/cc26xx-cc13xx/lib/cc13xxware'
Submodule 'arch/cpu/cc26xx-cc13xx/lib/cc2640r2-sdk' (https://github.com/contiki-ng/cc2640r2-sdk.git) registered for path 'arch/cpu/cc26xx-cc13xx/lib/cc2640r2-sdk'
Submodule 'arch/cpu/cc26xx-cc13xx/lib/cc26xxware' (https://github.com/contiki-ng/cc26xxware.git) registered for path 'arch/cpu/cc26xx-cc13xx/lib/cc26xxware'
...
Cloning into 'tools/sensniff'...
remote: Enumerating objects: 195, done.
remote: Total 195 (delta 0), reused 0 (delta 0), pack-reused 195
Receiving objects: 100% (195/195), 38.09 KiB | 0 bytes/s, done.
Resolving deltas: 100% (101/101), done.
Checking connectivity... done.
Submodule path 'tools/sensniff': checked out '3b3ac51b0a11a234a93ca50279181c648bf07976'

$ sudo apt-get update; sudo apt-get install -y srecord gcc-arm-none-eabi
Get:1 http://security.ubuntu.com/ubuntu xenial-security InRelease [107 kB]
Hit:2 http://us.archive.ubuntu.com/ubuntu xenial InRelease
Get:3 http://security.ubuntu.com/ubuntu xenial-security/main amd64 Packages [596 kB]
...
Setting up gcc-arm-none-eabi (15:4.9.3+svn231177-1) ...
Setting up libnewlib-dev (2.2.0+git20150830.5a3d536-1) ...
Setting up libnewlib-arm-none-eabi (2.2.0+git20150830.5a3d536-1) ...
Setting up libsrecord0v5 (1.58-1.1ubuntu1) ...
Setting up libstdc++-arm-none-eabi-newlib (15:4.9.3+svn227297-1+8) ...
Setting up srecord (1.58-1.1ubuntu1) ...
Processing triggers for libc-bin (2.23-0ubuntu3) ...

Compile Contiki-NG’s firmware

We will use the example ‘hello-world’ firmware in this example. You can build it as follows:

$ cd examples/hello-world

$ make TARGET=zoul BOARD=remote-revb hello-world
  MKDIR     build/zoul/remote-revb//obj
  CC        ../../arch/platform/zoul/./platform.c
  CC        ../../arch/platform/zoul/dev/leds-arch.c
  ...
  CC        hello-world.c
  LD        build/zoul/remote-revb//hello-world.elf
  OBJCOPY   build/zoul/remote-revb//hello-world.elf --> build/zoul/remote-revb//hello-world.i16hex
  SREC_CAT  build/zoul/remote-revb//hello-world.i16hex --> build/zoul/remote-revb//hello-world.hex
  OBJCOPY   build/zoul/remote-revb//hello-world.elf --> build/zoul/remote-revb//hello-world.bin
  CP        build/zoul/remote-revb//hello-world.elf --> build/zoul/remote-revb//hello-world.zoul
  CP        build/zoul/remote-revb//hello-world.zoul --> hello-world.zoul
rm hello-world.o build/zoul/remote-revb//obj/startup-gcc.o build/zoul/remote-revb//hello-world.i16hex

Let’s verify if the HEX-file looks al-right:

$ xxd build/zoul/remote-revb/hello-world.hex | head
00000000: 3a30 3230 3030 3030 3430 3032 3044 410a  :020000040020DA.
00000010: 3a32 3032 3030 3030 3036 3036 4330 3032  :20200000606C002
00000020: 3038 4432 3232 3030 3041 4241 4632 3030  08D222000ABAF200
00000030: 3041 3941 4632 3030 3041 3941 4632 3030  0A9AF2000A9AF200
00000040: 3041 3941 4632 3030 3041 3941 4632 3030  0A9AF2000A9AF200
00000050: 3030 3030 3030 3030 3041 420a 3a32 3032  000000000AB.:202
00000060: 3032 3030 3030 3030 3030 3030 3030 3030  0200000000000000
00000070: 3030 3030 3030 3030 3030 3030 3041 3941  0000000000000A9A
00000080: 4632 3030 3041 3941 4632 3030 3030 3030  F2000A9AF2000000
00000090: 3030 3030 3041 3941 4632 3030 3034 3342  00000A9AF200043B

Deploying the firmware with OpenTestbed via the CLI

We can now upload this hex-file with the OpenTestbed CLI. As part of the ESpec, the CLI-script was uploaded to /opt/opentestbed/cli.py, and the necessary environment variables were set in ~/.profile:

$ grep OTB_ ~/.profile
export OTB_TESTBED="wilab"
export OTB_BROKER="server.otb.wall2-ilabt-iminds-be.wall2.ilabt.iminds.be"

$ python /opt/opentestbed/cli.py
Usage: cli.py [OPTIONS] COMMAND [ARGS]...

Options:
  -t, --testbed [iotlab|opentestbed|wilab]
                                  [required]
  -b, --broker TEXT               The MQTT broker address  [required]
  -v, --verbose                   Increase verbosity
  --help                          Show this message and exit.

Commands:
  changelocation     Change location of otbox
  discovermotes      Discover motes connected to otboxes
  echo               Simple echo command
  listenserial       Listen to the output of mote with the given EUI64
  program-from-file  Flash firmware from local file
  program-from-url   Flash firmware from URL
  status             Request status of otboxes
  tomoteserialbytes  Send data to serial port of mote

These environment variables mean that we can omit the -t and -b options in the CLI.

To deploy our compiled hex-file on all the sensors, use the program-from-file command from the CLI. By omitting the mote identifiers that we want to deploy to, this command will default to flashing ALL motes:

$ python /opt/opentestbed/cli.py program-from-file build/zoul/remote-revb/hello-world.hex
m 00-12-4b-00-09-df-90-95: True
m 00-12-4b-00-09-df-2c-2c: True
m 00-12-4b-00-10-03-56-2f: True
m 00-12-4b-00-10-03-54-44: True

Each line represents a sensor, which is identified by it’s EUI64. As the CLI does not know how many sensors are deployed, it will keep listening for responses. To stop the CLI press CTRL+C.

Let’s verify that the serial has the output that we expect. For this we use the listenserial command of the CLI. This command expects the EUI64-identifier of the mote we want to listen to. We copy&paste the first EUI64-identifier from the output of the program-from-file command.

$ python /opt/opentestbed/cli.py listenserial 00-12-4b-00-09-df-90-95
Hello, world
Hello, world
Hello, world
^C

We can also request the status of each OpenTestbed-box with the status command:

$ python /opt/opentestbed/cli.py status
b    sensor1: {u'uptime': u'0:11:57.991773', u'software_version': u'1.1.1', u'currenttime':
u'Fri Dec 21 13:11:52 2018', u'location': u'not available', u'motes': [{u'firmware_description':
u'hello-world.hex', u'EUI64': u'00-12-4b-00-10-03-54-44', u'bootload_success': True, u'serialport':
u'/dev/ttyUSB0'}, {u'firmware_description': u'hello-world.hex', u'EUI64': u'00-12-4b-00-09-df-90-95',
u'bootload_success': True, u'serialport': u'/dev/ttyUSB1'}], u'host_name': u'sensor1', u'starttime':
u'Fri Dec 21 12:59:54 2018', u'IP_address': u'10.0.3.37 2001:6a8:1d80:2011:baae:edff:fe75:ab6a',
u'threads_name': [u'MainThread', u'SerialportHandler@/dev/ttyUSB0', u'sensor1_command_status',
u'heartbeat_thread', u'mqtt_loop_thread', u'SerialRxBytePublisher@/dev/ttyUSB1',
u'SerialRxBytePublisher@/dev/ttyUSB0', u'SerialportHandler@/dev/ttyUSB1']}
b    sensor2: {u'uptime': u'0:11:58.159916', u'software_version': u'1.1.1', u'currenttime':
u'Fri Dec 21 13:18:46 2018', u'location': u'not available', u'motes': [{u'firmware_description':
u'hello-world.hex', u'EUI64': u'00-12-4b-00-10-03-56-2f', u'bootload_success': True, u'serialport':
u'/dev/ttyUSB0'}, {u'firmware_description': u'hello-world.hex', u'EUI64': u'00-12-4b-00-09-df-2c-2c',
u'bootload_success': True, u'serialport': u'/dev/ttyUSB1'}], u'host_name': u'sensor2', u'starttime':
u'Fri Dec 21 13:06:48 2018', u'IP_address': u'10.0.3.38 2001:6a8:1d80:2011:baae:edff:fe75:bb20',
u'threads_name': [u'MainThread', u'SerialportHandler@/dev/ttyUSB0', u'sensor2_command_status',
u'heartbeat_thread', u'mqtt_loop_thread', u'SerialportHandler@/dev/ttyUSB1',
u'SerialRxBytePublisher@/dev/ttyUSB0', u'SerialRxBytePublisher@/dev/ttyUSB1']}