Previously, I have designed a small circuit board with a cc2520 rf-tranceiver and a stm32f4 microcontroller (see this post). After porting the driver for the cc2520 to ChibiOS for a few tests, I decided to port Contiki to support this platform as well. As this is the first time that I work with Contiki, uipv6 and 6LoWPAN, this was quite a challenge for me. Nevertheless, I managed to make the following features work:
- The cc2520 radio
- The RPL border router using the USB connector
- printf for debugging
- Many applications, such as the webserver, telnet, udp
- I made a driver for ws2811 LEDs that uses DMA and a timer
In this post, I will describe what the essential steps were to port Contiki to this board and how to use my port. I have uploaded the Contiki port together with a few example applications to github. You can download it here.
Motivation for doing this project
The Internet of Things is something that has bees discussed and worked on quite a lot recently. The idea of having even the smallest devices connected to the internet with rather simple hardware seems very interesting to me, and the Contiki OS is something that enables TCP/IP over small resource-limited devices. So, my main motivation for this project is to gain deep knowledge about how Contiki works and what is required to create a Contiki-device from scratch.
As microcontrollers will get more powerful and cheaper over time, I decided to use the STM32F4 for this mote, even though it might be a bit overpowered at this point. Because the STM32F4 is rather advanced and powerful, it does not impose any limitation on my learning. Thus, I can experiment with buffer sizes, advanced peripherals, simultaneous applications etc. at the same time more or less without restrictions. Also, as there wasn’t any similar port in the Contiki tree, I had to implement most of the details of the port by myself, which helped me understand many details about Contiki.
Contiki is used on devices connected to the internet, and therefore every platform and port should provide at least the following functionality to make it useful:
- Radio-chip drivers
- IPv6 with 6LoWPAN
- Border-router implementation
- Some way to print debug information
This will allow many of the already existing applications to be re-used and make the platform useful.
There are two directories that have to be added to Contiki for each new port, namely:
Microcontroller-specific code belongs here. This includes:
- Hardware libraries: Peripheral-drivers, usb-stack etc. for this specific microcontroller.
- clock: The implementation of the Contiki system clock for this MCU.
- rtimer: A high-precision timer for certain timing-critical features.
- USB-SLIP: Serial Line Internet Protocol. Input and output handlers for the Contiki SLIP implementation belong here. For this port, I used usb-cdc (see this post) to implement this. This board will show up as a serial port on the computer (/dev/ttyACMx) and the existing Contiki tools can be used the make a border-router for example. Note that my implementation of this is a bit weird because I had to work around some problems with the USB stack.
- Startup-code: Low-level initialization code for this microcontroller.
- System calls: Map the write system call somewhere (e.g. to UART or USB) to make printf useful for debugging.
- Watchdog: Some things require it to compile, but my implementation is currently empty and only serves to make the compilation work.
- Interrupts: The interrupts for cpu-dependent tasks can be put here (I used a single file for this).
- Linker script: A linker-script for this microcontroller.
- Makefile: A makefile to coordinate the cpu-dependent compilation. This one can be a bit tricky. For example, I had to compile code that contains interrupts in a different way so that weak bindings would be overridden correctly.
In my case, I have added contiki/cpu/arm/stm32f4 and implemented the things mentioned above.
Platform-specific code belongs here. This includes:
- low-level radio drivers: Contiki already had some drivers for the cc2520, but I had to add code to initialize the SPI, set up pin-interrupts etc.
- contiki-main: A main-function that initializes the hardware, handles sleeping and starts some processes. I based this one on the main function of some of the other platforms.
- LED drivers: A simple low level driver to switch on and off LEDs.
- Other drivers: I also added a driver for the ws2811 LEDs and a UART driver for the SLIP interface.
- Configuration: The file contiki-conf.h also belongs here. It contains configuration parameters about which radio channel to use, the RDC-driver etc. I also created a file platform-conf.h that has some more hardware-specific configuration parameters such as some low-level macros for the SPI driver.
- Makefile: A quite simple makefile to build the platform-specific files.
In my case, I have added the directory contiki/platform/stm32-bv and implemented the things mentioned above.
My Contiki port on github gives a concrete example with this particular implementation.
Using this Contiki port with my motes
In order to upload a program to the motes, a programmer can be made from for example a STM32F4-discovery board by removing a few jumpers and connecting a cable to the SWD header. I will put some details about that here later.
Here is a summary of all the steps that are required to test/use this contiki port:
- Make sure that a working toolchain is installed. See this post for more details.
- Download my Contiki port from github.
- Go to examples/stm32-bv/rpl-border-router and run (with the programmer connected, see this post):
make TARGET=stm32-bv clean make TARGET=stm32-bv NODEID=1 upload
- Connect the border router (replace the port with the one you get, e.g. /dev/ttyACM0):
make connect-router TTY_PORT=/dev/ttyACM0
- From another terminal, try to ping the border router
- Open a web browser and see if the webserver works by opening http://[aaaa::200:0:0:1]/. You should see something like this:
- In another terminal, go to examples/stm32-bv/test and run the following with the programmer connected to another mote:
make TARGET=stm32-bv clean make TARGET=stm32-bv NODEID=6 PRINTF_VCP=0 upload
- After giving a few seconds for the routes to be built, try to ping the other mote:
- Open the web page again http://[aaaa::200:0:0:1]/. You should see something like this:
You can also try examples/stm32-bv/webserver-ipv6 on another mote. Then, if, for example, NODEID=7, you can open http://[aaaa::200:0:0:7]/processes.shtml in a browser and access a web page on that mote. That page should look like this:
Some notes about this usage example:
- In order to apply the passed arguments (e.g. NODEID=x), clean has to be run on the project first.
- The PRINTF_VCP-flag specifies that debug output should be redirected to the USB virtual serial port by default. When it is enabled, the program can freeze until the output is read, so if you don’t plan to use it, switch it off.
- The NODEID=x-flag will make x the last part of the IP address for that mote.
I have also made a simple driver for ws2811 addressable RGB LEDs. This driver uses DMA and a timer, so the CPU does not have to do anything. You can read more about ws2811 here. This example can be built and uploaded in the way described above. There is also a simple control client written in Qt that can be used to control the LEDs over UDP. This client looks like the following:
It can be build from the contiki/examples/stm32-bv/ws2811/QtClient directory by running:
qmake make ./QtLEDClient
The data pin of the LEDs can now be connected to PB8 (pin9 on the header). Note a pullup-resistor (about 330 ohms) has to be connected to 5V from that pin since it runs in open-drain mode. This is because the ws2811 don’t work too well with a 3.3v signal.
This example shows all the parts that are required to implement a simple UDP server and a client that can be used from a desktop computer.
As the border router shows up as a network adapter, the application on the computer can use common network techniques to communicate with the devices in a seamless way. And this also works with multihop routing. Isn’t that cool?
It is possible to start a GDB server with a simple command from this Contiki port. From the directory with the application you want to debug, run:
make TARGET=stm32-bv clean make TARGET=stm32-bv NODEID=7 DEBUG_BUILD=1 upload make TARGET=stm32-bv debug-start
you should see something like this after the last command:
$ make TARGET=stm32-bv debug-start openocd -f ../../..//platform/stm32-bv/stm32-bv_openocd.cfg Open On-Chip Debugger 0.7.0-dev-snapshot (2013-08-05-13:24) Licensed under GNU GPL v2 For bug reports, read http://openocd.sourceforge.net/doc/doxygen/bugs.html adapter speed: 1000 kHz srst_only separate srst_nogate srst_open_drain Info : clock speed 1000 kHz Info : stm32f4x.cpu: hardware has 6 breakpoints, 0 watchpoints
The DEBUG_BUILD=1 option will cause the project to compile without optimizations and the sleep mode of the stm32f4 disabled (if the core is sleeping, the debug interface does not work). Now you can connect to the GDB server in the usual way. Maybe I will write instructions about that later, but before that you might find some hints in this post.