diff --git a/IoT in Five days.html b/IoT in Five days.html index eea2f06..556b32c 100644 --- a/IoT in Five days.html +++ b/IoT in Five days.html @@ -2177,8 +2177,18 @@
This "IoT in five days" release version correspond to:
Version: 1.0
+Version: 1.1
Date: 7th February 2016
+Date: 24th June 2016
Marco Zennaro received his M.Sc. degree in Electronic Engineering from University of Trieste in Italy. He defended his PhD thesis on “Wireless Sensor Networks for Development: Potentials and Open Issues” at KTH-Royal Institute of Technology, Stockholm, Sweden. -His research interest is in ICT4D, the use of ICT for development. In particular, he is interested in Wireless Networks and in Wireless Sensor Networks in developing countries. He has been giving lectures on Wireless technologies in more than 20 countries.
-When not traveling, he is the editor of the wsnblog.com. He is coauthor of the book “Wireless Networking for the Developing World”.
+His research interest is in ICT4D, the use of ICT for development. In particular, he is interested in Wireless Networks and in Wireless Sensor Networks in developing countries. He has been giving lectures on Wireless technologies in more than 20 countries. He is coauthor of the book “Wireless Networking for the Developing World”.Ermanno Pietrosemoli is currently a researcher at the Telecommunications/ICT for Development Lab of the International Centre for Theoretical Physics in Trieste, Italy, and President of Fundación Escuela Latinoamericana de Redes, “EsLaRed”, a non-profit organization that promotes ICT in Latin America through training and development projects. EsLaRed was awarded the 2008 Jonathan B.Postel Service Award by the Internet Society.
@@ -3370,6 +3376,27 @@+ + | +
+
+
+If you are using Ubuntu probably you would not be able to run wireshark as non-root user (if you miss this installation option). Type the following to enable non-root: +
+
+
+
+
+ |
+
There are several ways to install Contiki, from scratch by installing from sources or using -virtual environments, depending on the flavour and time availability.
+There are several ways to install Contiki, from scratch by installing from sources or using virtual environments, depending on the flavour and time availability.
The remainder of the book assumes Contiki will run in an Unix environment, -as the virtualized environments run on Ubuntu.
+The remainder of the book assumes Contiki will run in an Unix environment, as the virtualized environments run on Ubuntu.
The following instructions will guide you to install Contiki on your machine. These instructions were tested in Ubuntu-like devices (version 12.04 and onwards). If you are looking for a ready to use setup, skip this section and download one of the Virtual Machines in the next section.
+This will install support for the ARM Cortex-M3 and MSP430 platforms, as well as support for Cooja, the Contiki’s emulator to be discussed in the next sections.
+The "IoT in five days" Virtual Machine has a comprehensive lists of additional tools and libraries installed, check the next section for a complete list.
+The following are the minimal recomended libraries to run Contiki.
+To install the toolchain and required dependencies, run in a terminal the following:
+sudo apt-get update
+sudo apt-get install gcc-arm-none-eabi gdb-arm-none-eabi
+sudo apt-get -y install build-essential automake gettext
+sudo apt-get -y install gcc-arm-none-eabi curl graphviz unzip wget
+sudo apt-get -y install gcc gcc-msp430
+sudo apt-get -y install openjdk-7-jdk openjdk-7-jre ant
+# Install Homebrew - http://brew.sh/
+brew tap PX4/homebrew-px4
+brew update
+
+# Install GCC Arm Toolchain
+brew install gcc-arm-none-eabi-49
+Download the GCC ARM toolchain (Windows installer) from:
+Tested with gcc-arm-none-eabi-5_3-2016q1-20160330-win32.exe
.
Execute and select the add path to environment variable
option.
Next download MINGW, install and make sure the following packages are selected: mingw32-base, mingw32-gcc-g++, msys-base.
+Under "All Packages" select the msys-mintty package for terminal support. This will install MINTTY
in the default location C:\MinGW\msys\1.0\bin\mintty.exe
. Create a shortcut and add this to the Shortcut target command:
C:\MinGW\msys\1.0\bin\mintty.exe /bin/bash -l
Add the following paths to your $path
environment variable:
C:\MinGW\bin;C:\MinGW\msys\1.0\bin
Run MINTTY
and execute the following commands:
mingw-get update
+mingw-get install msys-wget
+mingw-get install msys-zlib
+mingw-get install msys-unzip
+Contiki source code is actively supported by contributors from universities, research centers and developers from all over the world.
The latest Contiki release is 3.0, the release tag is available at:
+Nevertheless you should use the latest commit available, as Contiki releases are produced on a yearly base. Many bug fixes, new features and improved support is normally present on the latest master
branch.
To grab the source code open a terminal and execute the following:
+ + | +
+
+
+At the moment of this release, the current Contiki commit corresponds to the
+
+
+
+Replace
+
+As Contiki ensures the platform and application support by using a strict code revision procedure and regression tests, this is a safe point if you encounter any problem. Be sure to update your Contiki local repository if using |
+
To install the toolchain and required dependencies, run in a terminal the following:
-sudo echo "deb http://ppa.launchpad.net/terry.guo/gcc-arm-embedded/ubuntu trusty main" > /etc/apt/sources.list.d/gcc-arm-embedded.list
-sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-key FE324A81C208C89497EFC6246D1D8367A3421AFB
-sudo apt-get update
-sudo apt-get -y install build-essential automake gettext
-sudo apt-get -y install gcc-arm-none-eabi curl graphviz unzip wget
-sudo apt-get -y install gcc gcc-msp430
-sudo apt-get -y install openjdk-7-jdk openjdk-7-jre ant
+Additionally there is a branch
available with the IoT in five days
content in a workshop-like format. The branch is available at:
This will install support for the ARM Cortex-M3 and MSP430 platforms, as well as -support for Cooja, the Contiki’s emulator to be discussed in the next sections.
+It is recomended to add this branch
as a remote repository
of the already cloned Contiki repository, so you can keep track of the development done in the master
branch.
cd /home/user/contiki
+git remote add iot-workshop https://github.com/alignan/contiki
+git fetch iot-workshop
+git checkout iot-workshop
Instant Contiki is an entire Contiki development environment in a single download. It is an Ubuntu Linux virtual machine and has Contiki OS and all the development tools, compilers, and simulators required already pre-installed.
Grab Instant Contiki from the Contiki website:
+The above commands will add the https://github.com/alignan/contiki
repository to the list of remote
repositories, making the iot-workshop
branch available in your machine as a working copy
. Now you can track any changes and updates.
http://www.contiki-os.org/start.html
+This branch has ready to use examples and applications to guide you through Contiki and building real IoT applications. In the next sections this content will be further discussed.
The latest Instant Contiki release is 3.0, following the Contiki 3.0 source code release.
This book is heavily based on this release, the use of earlier versions is not recommended.
The release tag is available at:
+Even if installing Contiki should be straightforward, there are already built Virtual Machines available for you to download and use out of the box.
https://github.com/contiki-os/contiki/tree/release-3-0
+The available Virtual Machines images are available for VMWare.
Nevertheless you should use the latest commit available, as Contiki releases are produced on a yearly base. Many bug fixes, new features and improved support is normally present on the latest master
branch.
Download VMWare player for Windowws and Linux to run Contiki’s virtual machine, it is free and widely used.
- - | -
-
+At the moment of this release, the current Contiki commit corresponds to the In OSX you can download VMWare Fusion
+ Instant Contiki Virtual Machine
-
Instant Contiki is an entire Contiki development environment in a single download. It is an Ubuntu Linux virtual machine and has Contiki OS and all the development tools, compilers, and simulators required already pre-installed.
-
-As Contiki ensures the platform and application support by using a strict code revision procedure and regression tests, this is a safe point if you encounter any problem. Be sure to update your Contiki local repository if using |
-
Grab Instant Contiki from the Contiki website:
Download VMWare player for Windowws and Linux to run Contiki’s virtual machine, it is free and widely used.
+In OSX you can download VMWare Fusion
+The latest Instant Contiki release is 3.0, following the Contiki 3.0 source code release.
Using VMWare just open the Instant_Contiki_Ubuntu_12.04_32-bit.vmx
file, if prompted about the VM source just choose I copied it
then wait for the virtual Ubuntu Linux boot up.
Notice the Instant Contiki
does not have the IoT workshop
branch with the examples of this book, read the previous section on how to clone.
A Virtual Machine with the content of the book is provided as a free download from the following location:
+In a nutshell it packs everything the Instant Contiki
has, but it has been built using Ubuntu Server 16.04 LTS (Xenial)
instead. Additionally several packages has been installed to make development easier, including a fully-configured Eclipse IDE workspace.
The "IoT in five days" book, its source and ready to use Contiki examples are included.
+Log into the Virtual Machine. The password and user name is user
.
The "IoT in five days" Virtual Machine has already the iot-workshop
branch cloned and available, with the suggested book examples and a ready-to-use Contiki configuration.
arm-gcc-none-eabi-gcc --version
+arm-none-eabi-gcc --version
arm-none-eabi-gcc (GNU Tools for ARM Embedded Processors) 4.9.3 20150529 (release) [ARM/embedded-4_9-branch revision 224288]
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
@@ -4605,13 +4762,44 @@ <
+If you cloned the iot-workshop
branch (or if you are using the "IoT in five days" Virtual Machine), you will find also the following folder:
+
+
+contiki/examples/zolertia/tutorial
+
+
+The content of the tutorial
is:
+
+
+
+
+
+The examples work out of the box for both Zolertia Z1
and RE-Mote
platforms to be shown next.
+
+
+The examples are written to be followed in order, so be sure to start from the very first and make your way to the last in an ordered fashion.
+
+
In the following sections we will cover the examples and specific platform files for the Zolertia platforms.
For the remainder of the book we will use Zolertia Z1 and RE-Mote hardware development platforms. Other platforms can be used as well, but are out of the scope of this book.
+For the remainder of the book we will use the Zolertia Z1 and RE-Mote hardware development platforms. Other platforms can be used as well, but are out of the scope of this book.
Contiki drivers and libraries are (normally) platform independent, most examples can be run in both Z1 and RE-Mote platforms by taking into consideration specific platform settings.
@@ -4647,10 +4835,10 @@The RE-Mote has a Zoul on board and also packs:
+The RE-Mote (revision A) has a Zoul on board and also packs:
The Zolertia Z1 mote can run TinyOS, Contiki OS, OpenWSN and RIOT, and has been used actively for over 5 years in universities, research and development centers and in commercial products in more than 43 countries, being featured in more than 50 scientific publications.
@@ -4737,7 +4925,7 @@cd examples/hello-world
+cd examples/zolertia/tutorial/01-basics
make TARGET=zoul savetarget
make hello-world
+make 01-hello-world
To start compiling the code (ignore the warnings), if everything works OK you should see something like:
+if everything works OK you should see something like for the Z1
mote:
CC symbols.c
AR contiki-z1.a
-CC hello-world.c
-CC ../../platform/z1/./contiki-z1-main.c
-LD hello-world.z1
-rm obj_z1/contiki-z1-main.o hello-world.co
+CC 01-hello-world.c
+CC ../../../../../platform/z1/./contiki-z1-main.c
+LD 01-hello-world.z1
+rm obj_z1/contiki-z1-main.o 01-hello-world.co
The hello-world.z1
file should have been created and we are ready to flash the application to the device.
The 01-hello-world.z1
file should have been created and we are ready to flash the application to the device.
At any given point you can override the saved target and redefine at compilation time by running instead:
+And likewise if we were to compile for the RE-Mote
platform:
make TARGET=zoul hello-world
- CC hello-world.c
- LD hello-world.elf
-arm-none-eabi-objcopy -O binary --gap-fill 0xff hello-world.elf hello-world.bin
+make TARGET=zoul 01-hello-world
+ CC 01-hello-world.c
+ LD 01-hello-world.elf
+arm-none-eabi-objcopy -O binary --gap-fill 0xff 01-hello-world.elf 01-hello-world.bin
+ + | +
-
+This will ignore the saved If a |
+
In the following sections and chapters the examples can be compiled for both the Z1 and RE-Mote platform, unless specified otherwise.
+In the following sections and chapters the examples can be compiled for both the Z1 and RE-Mote platforms.
The next step is adding an LED (light emitting diode) to interact with our application.
+This section will show how to use the LED (light emitting diode) to interact with our application. Also it will be shown how to use the on-board user button
to trigger specific events and change the way our application works.
You have to add the dev/leds.h
which is the library to manage the LEDs. To check the available functions go to core/dev/leds.h
.
In the Z1 mote these LEDs are defined in platform/z1/platform-conf.h
, as well as other hardware definitions.
In the Z1
mote these LEDs are defined in platform/z1/platform-conf.h
, as well as other hardware definitions.
The RE-Mote uses an RGB LED, basically 3-channel LEDs in a single device, allowing to show any color by the proper combination of Blue, Red and Green. In platforms/zoul/remote/board.h
header the following are defined:
The RE-Mote
uses an RGB LED, basically 3-channel LEDs in a single device, allowing to show any color by the proper combination of Blue, Red and Green. In platforms/zoul/remote/board.h
header the following are defined:
Now try to turn on only the red LED and see what happens, create a new example named test-leds.c
:
Accordingly you need to add dev/button-sensor.h
to add support for the user button
.
The 02-led-and-button.c
example show how the configuration is done.
#include "contiki.h"
#include "dev/leds.h"
+#include "dev/button-sensor.h"
#include <stdio.h>
/*-------------------------------------------------*/
-PROCESS(test_leds_process, "Test LEDs");
-AUTOSTART_PROCESSES(&test_leds_process);
+PROCESS(led_button_process, "LEDs and button example process");
+AUTOSTART_PROCESSES(&led_button_process);
/*-------------------------------------------------*/
-PROCESS_THREAD(test_leds_process, ev, data)
+PROCESS_THREAD(led_button_process, ev, data)
{
PROCESS_BEGIN();
- leds_on(LEDS_RED);
+ SENSORS_ACTIVATE(button_sensor); (1)
+
+ while(1) { (2)
+ printf("Press the User Button\n");
+ PROCESS_WAIT_EVENT_UNTIL(ev == sensors_event && data == &button_sensor); (3)
+ /* When the user button is pressed, we toggle the LED on/off... */
+ leds_toggle(LEDS_GREEN); (4)
+
+ /*
+ * And we print its status: when zero the LED is off, else on.
+ * The number printed when the sensor is on is the LED ID, this value is
+ * used as a mask, to allow turning on and off multiple LED at the same
+ * time (for example using "LEDS_GREEN + LEDS_RED" or "LEDS_ALL"
+ */
+ printf("The sensor is: %u\n", leds_get()); (5)
+ }
+
PROCESS_END();
}
Now let’s compile and upload the new project with:
-make clean && make test-leds.upload
-The make clean
command is used to erase previously compiled objects.
Now the red LED should be on!
-- - | -
-
-
-If you make changes to the source code, you must rebuild the files, otherwise your change might not be pulled in. It is always recommended to do a |
+1 | +The LED initialization is done at booting by the platform itself, so there is no need to initialize again. The user button is required to initialize |
PROCESS_END()
macro.user button
user button
is pressed, alternate between turning the LED on and offYou can use printf to visualize on the console what is happening in your application. It is really useful to debug your code, as you can print values of variables, when certain block of code is being executed, etc.
-Let’s try to print the status of the LED, using the unsigned char leds_get(void);
function that is available in the documented functions (see above).
Get the LED status and print it on the screen, modify the current test-leds.c
file as follows:
Now let’s compile and upload the new project with:
#include "contiki.h"
-#include "dev/leds.h"
-#include <stdio.h>
-char hello[] = "hello from the mote!";
-/*-------------------------------------------------*/
-PROCESS(test_leds_process, "Test LEDs");
-AUTOSTART_PROCESSES(&test_leds_process);
-/*-------------------------------------------------*/
-PROCESS_THREAD(test_leds_process, ev, data)
-{
- PROCESS_BEGIN();
- leds_on(LEDS_RED);
- printf("%s\n", hello);
- printf("The LED %u is %u\n", LEDS_RED, leds_get());
- PROCESS_END();
-}
+make clean && make 02-led-and-button.upload
If one LED is on, you will get the LED number, which is defined by each platform in its platform-conf.h
or board.h
header.
The make clean
command is used to erase previously compiled objects.
- + | -Exercise: what happens when you turn on more than one LED? What number do you get? are these the same numbers for the Z1 and the RE-Mote? - | -
We now want to detect if the user button has been pressed.
-The button in Contiki is regarded as a sensor. We are going to use the core/dev/button-sensor.h
library.
If you make changes to the source code, you must rebuild the files, otherwise your change might not be pulled in. It is always recommended to do a make clean
command before compiling.
- - | -
-The RE-Mote platform has extra button functionalities, such as detect long-press sequences, enabling to further expand the events that can be triggered using the button. Check platform/zoul/dev/button-sensor.c for more details, and examples/zolertia/zoul/zoul-demo.c for an example.
|
Create a new example called test-button.c
and add the dev/button-sensor.h
header and button event as follows:
The RE-Mote
user and reset buttons are shown below:
#include "contiki.h"
-#include "dev/leds.h"
-#include "dev/button-sensor.h"
-#include <stdio.h>
-/*-------------------------------------------------*/
-PROCESS(test_button_process, "Test button");
-AUTOSTART_PROCESSES(&test_button_process);
-/*-------------------------------------------------*/
-PROCESS_THREAD(test_button_process, ev, data)
-{
- PROCESS_BEGIN();
- SENSORS_ACTIVATE(button_sensor);
- while(1) {
- PROCESS_WAIT_EVENT_UNTIL((ev==sensors_event) &&
- (data == &button_sensor));
- printf("I pushed the button!\n");
- }
- PROCESS_END();
-}
+
This process has an infinite loop, given by the wait()
, to wait for the button the be pressed. The two conditions have to be met (event from a sensor and that event is the button being pressed), as soon as you press the button, you get the string printed.
For our implementation we are going to choose the event timer, because we want to change the application behavior when the timer expires every given period.
+The 03-timers.c
example implements the aforementioned timers and shows their functionality.
We create a timer structure and set the timer to expire after a given number of seconds. When the timer expires we execute the code and restart the timer.
+First let’s see how the timer’s libraries are included:
+/* The event timer library */
+#include "sys/etimer.h"
+
+/* The seconds timer library */
+#include "sys/stimer.h"
+
+/* The regular timer library */
+#include "sys/timer.h"
+
+/* The callback timer library */
+#include "sys/ctimer.h"
+
+/* The "real-time" timer library */
+#include "sys/rtimer.h"
+Create an example called test-timer.c
as follows:
Next each timer needs to have its corresponding timer structure defined, this will allow the application to store the configuration and operational values of each timer at request.
#include "contiki.h"
-#include "dev/leds.h"
-#include <stdio.h>
-/*-------------------------------------------------*/
-#define SECONDS 2
-/*-------------------------------------------------*/
+/* The following are the structures used when you need to include a timer, it
+ * serves to keep the timer information.
+ */
+static struct timer nt;
+static struct stimer st;
+static struct etimer et;
+static struct ctimer ct;
+static struct rtimer rt;
+
Then we need to implement callbacks
, generally these are functions called by the application whenever something occurs. As said before, both ctimer
and rtimer
are capable of invoking a function whenever an event happens (like a timer expiration). Below is the format of the callbacks
:
/*---------------------------------------------------------------------------*/
+static void
+rtimer_callback_example(struct rtimer *timer, void *ptr)
+{
+ /* Something happens here */
+}
+/*---------------------------------------------------------------------------*/
+static void
+ctimer_callback_example(void *ptr)
+{
+ /* Something happens here */
+}
+/*---------------------------------------------------------------------------*/
+The actual 03-timers.c
has the missing snippet of code in each callback, we will return later to these.
Here’s what our thread looks like:
+/*-------------------------------------------------*/
PROCESS(test_timer_process, "Test timer");
AUTOSTART_PROCESSES(&test_timer_process);
/*-------------------------------------------------*/
PROCESS_THREAD(test_timer_process, ev, data)
{
PROCESS_BEGIN();
- static struct etimer et;
- while(1) {
- etimer_set(&et, CLOCK_SECOND*SECONDS); (1)
- PROCESS_WAIT_EVENT(); (2)
- if(etimer_expired(&et)) {
- printf("Hello world!\n");
- etimer_reset(&et);
- }
+ static uint32_t ticks = 0;
+ timer_set(&nt, CLOCK_SECOND); (1)(2)
+ while(!timer_expired(&nt)) { (3)
+ ticks++;
}
- PROCESS_END();
+ printf("timer, ticks: \t%ld\n", ticks);
}
PROCESS_WAIT_EVENT()
waits for any event to happen.timer
, it will keep running up to CLOCK_SECOND
timer
has expiredCLOCK_SECOND
to count how many ticks you have in one second? Try to blink the LED for a certain number of seconds. Make a new application that starts only when the button is pressed and stops when the button is pressed again.
+Exercise: can you print the value of CLOCK_SECOND
to count how many ticks you have in one second?
The remaining of our thread is as follows:
+ ticks = 0;
+ stimer_set(&st, 1); (1)
+ while(!stimer_expired(&st)) { (2)
+ ticks++;
+ }
+ printf("stimer, ticks: \t%ld\n", ticks);
+1 | +Same as timer , but its time-base is seconds |
+
2 | +And we also need to check if the stimer has expired |
+
A sensor is a transducer whose purpose is to sense or detect a characteristic of its environment, providing a corresponding output, generally as an electrical or optical signal, related to the quantity of the measured variable.
+Probably now in this point it is clear the disadvantages of using the previous timers: we need to manually check for expiration in a while-loop, which means a lot of wasted CPU cycles.
Sensors in Contiki are implemented as follow:
+The next timer allow us to yield our application, meaning it will generate an event informing the application about its expiration, thus the system can perform other tasks in the meantime.
SENSORS_SENSOR (sensor, SENSOR_NAME, value, configure, status);
+ ticks = 0;
+ etimer_set(&et, CLOCK_SECOND * 2);
+
+ ticks++;
+ PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);
+ printf("etimer, now: \t%ld\n", ticks);
This means that a method to configure
the sensor, poll the sensor status
and request a value
have to be implemented. The sensor
structure contains pointers to these functions. The arguments for each function are shown below.
When the etimer
is scheduled, we yield our application and wait for a PROCESS_EVENT_TIMER
event, this is a type of process event sent by the timer to notify applications.
struct sensors_sensor {
- char * type;
- int (* value) (int type);
- int (* configure) (int type, int value);
- int (* status) (int type);
-};
++ + | +
+
+
+If you run the example the first noticeable thing is the |
+
To better understand please refer to the platform/zoul/dev/adc-sensors.c
, for an example of how analogue external sensors can be implemented (to be further discussed in the next sections).
The next timer to test is the ctimer
. Let’s see how it is implemented in the example:
ticks++;
+ ctimer_set(&ct, CLOCK_SECOND * 2, ctimer_callback_example, &ticks);
+
+ /* And we keep the process halt while we wait for the callback timer to
+ * expire.
+ */
+
+ while(1) {
+ PROCESS_YIELD();
+ }
+The following functions and macros can be used to work with the sensor:
+As comented before the ctimer
will invoke a function whenever it expires, in this case the ctimer_callback_example
function after 2 seconds since triggered. Notice we are also passing to the ctimer_callback_example
the content of the variable ticks
, meaning we can pass as argument any type of variable to the callback function, as it defines this parameter as void *ptr
. Let’s check now the complete callback implementation:
SENSORS_ACTIVATE(sensor) (1)
-SENSORS_DEACTIVATE(sensor) (2)
-sensor.value(type); (3)
+static void
+ctimer_callback_example(void *ptr)
+{
+ uint32_t *ctimer_ticks = ptr; (1)
+ printf("ctimer, now: \t%ld\n", *ctimer_ticks);
+
+ /* The real timer allows execution of real-time tasks (with predictable
+ * execution times).
+ * The function RTIMER_NOW() is used to get the current system time in ticks
+ * and RTIMER_SECOND specifies the number of ticks per second.
+ */
+
+ (*ctimer_ticks)++; (2)
+ rtimer_set(&rt, RTIMER_NOW() + RTIMER_SECOND, 0, (3)
+ rtimer_callback_example, ctimer_ticks);
+}
1 | -Enable the sensor, typically configures and turns the sensor on | +Create a pointer to the variable passed as argument |
2 | -Disables the sensor, it is useful to save power | +Increment the variable ticks using our pointer |
3 | -Request a value to the sensor. As one sensor chip might have different types of readings (temperature, humidity, etc.), this is used to specify which measure to take. | -
- - | -
-
-
-Although this is the default way to implement sensors in Contiki, there might be alternatives, depending on the sensor and developer. Always check the specific sensor driver implementation for details. Normally sensors are provided in the same location as the example, in the |
+Schedule a rtimer to expire after 1 second and pass as argument our pointer to the ticks variable |
The SENSORS
macro provides external linkage to the sensors defined for the platform and application, allowing to iterate between the available sensors. The following are the sensors defined as default for the RE-Mote platform:
SENSORS (&button_sensor, &vdd3_sensor, &cc2538_temp_sensor, &adc_sensors);
-In our example when ctimer
expires it will set a real-timer event using the rtimer
type of timer. The RTIMER_NOW()
is a macro used to retrieve the current tick value (rtimer
is always ticking thus running in the background), we use this as base value then add the number of ticks in which we want our event to be triggered in the future. The RTIMER_SECOND
in the same fashion as CLOCK_SECOND
defines the number of ticks in a second.
- + |
-
The button is considered a sensor in Contiki, and it shares the same sensor implementation +Print the value of |
This macro extends to:
+When rtimer
expires it invokes its corresponding callback function, let’s check its full implementation in our example:
const struct sensors_sensor *sensors[] = {
- &button_sensor,
- &vdd3_sensor,
- &cc2538_temp_sensor,
- &adc_sensors,
- ((void *)0)
-};
+static void
+rtimer_callback_example(struct rtimer *timer, void *ptr)
+{
+ uint32_t *rtimer_ticks = ptr;
+ printf("rtimer, now: \t%ld\n", *rtimer_ticks);
+
+ /* We can restart the ctimer and keep the counting going */
+ (*rtimer_ticks)++;
+ ctimer_restart(&ct);
+}
The number of sensors can be found with the SENSORS_NUM
macro:
#define SENSORS_NUM (sizeof(sensors) / sizeof(struct sensors_sensor *))
+As done in the ctimer
callback we also create a pointer to the original ticks
variable, print and increment its value. We restart the ctimer
using the initial expiration value configured before. From now on, ctimer
callback will configure a new rtimer
timer and viceversa, incrementing the ticks
value in a linear way.
Sensors can be of two types: analogue or digital. In the following sections examples will be shown for both types, detailing how to connect and use both the platform’s internal sensors as well as the external ones.
+We learned in previous section how to create a PROCESS
and the meaning of the PROCESS_BEGIN()
and PROCESS_END()
macros. Let’s take a closer look at how processes are implemented in Contiki.
Analogue sensors typically require being connected to an ADC (analogue to digital converter) to translate the analogue (continuous) reading to an equivalent digital value in millivolts. The quality and resolution of the measure depends on both the ADC resolution (up to 12 bits in the Z1 and RE-Mote) and the sampling frequency.
+A process thread contains the code of the process. The process thread is a single protothread that is invoked from the process scheduler. A protothread is a way to structure code in a way that allows the system to run other activities when the code is waiting for something to happen. The concept of protothreads was developed within the context of Contiki.
As a rule of thumb, the sampling frequency must be at least twice that of the phenomenon you are measuring. As an example, if you want to sample human speech (which may contain frequencies up to 8 kHz) you need to sample at twice that frequency (16 kHz).
-Protothread provides a way for C functions to work in a way that is similar to threads, without the memory overhead of threads. Reducing the memory overhead is important on the memory-constrained systems on which Contiki runs.
The protothreads starts and ends with two special macros, PROCESS_BEGIN
and PROCESS_END()
. Between these macros, a set of protothread functions can be used.
The Z1 mote has a built-in voltage sensor provided as an ADC input channel, as well as a generic implementation to read external analogue sensors.
+Libraries and applications in Contiki uses processes to run and to communicate with other modules, by creating its own processes or using the already available. The timers for example use the existing PROCESS_EVENT_TIMER
, and applications could also use the PROCESS_EVENT_EXITED
process to indicate an early exit. These are event identifiers
reserved by the Contiki Kernel.
- - | -
-
Analogue sensors can be connected to both the Z1 and the RE-Mote over phidget ports, which are basically 3-pin connectors (Ground, VCC and signal) with 2.54 mm spacing. This is a legacy name from the Z1 release, as these ports were meant for the commercially available Phidget sensors. Nowadays there are also sensors from other providers, such as seeedstudio that have the same pin-out but with a different pin spacing (2 mm). This is not a problem, as there are cables to adapt the pin spacing. +As probably noticed by now, we normally refer to yielding and waiting for a process. Contiki has two execution contexts: cooperative and preemptive. Processes are cooperative and sequential, interrupts (button, sensors events) and the real-timer are preemptive.
-
+
-Figure 34. Analogue sensors
+Figure 36. Execution contexts: processes and interrupts
|
-
As we have learned before, there are two ways so far to yield a protothread:
The ADC results in the RE-Mote returns the reading in voltage.
+PROCESS_YIELD()
will wait for any type of process event to happen, it will not check which process has occured. Often a check like if(ev == PROCESS_EVENT_NAME){}
is used to catch one or more processes.
For the Z1 mote the ADC output must be converted by means of the following formula: We multiply the measured value by the voltage reference and divide the product by the ADC’s maximum output. As the resolution of the ADC is 12 bits (212 = 4096), we get:
+On the other hand the PROCESS_WAIT_EVENT_UNTIL(…)
allows to catch a process event direclty, in the case of the etimer
as previously shown, we can yield until:
Voltage, mV = (units * Vref) / 4096;+
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER)
Or simply:
The voltage reference limits the range of our measure, meaning if we use a Vref
of 2.5 V, the sensor will saturate when reading a higher voltage. The reference can be chosen while configuring the ADC, for both the Z1 and RE-Mote normally 3.3 V is used.
PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et))
Both the Z1 and the RE-Mote allow up to 6 analogue sensors to be connected, but only two phidget connectors can be soldered at the same time.
+To catch just an etimer
event from timer et
.
For the RE-Mote it is possible to have one phidget connector for a 3.3 V analogue sensor (ADC1) and another for 5 V sensors (ADC3).
+Similar to PROCESS_YIELD()
, the PROCESS_PAUSE()
macro will pause the thread, but instead of waiting for an event to happen it will temporatily pause the process, by posting an event to itself and yielding. This allows the system to execute other processes and whenever done then resume our process.
- - | -
-
-The RE-Mote is based on an ARM Cortex-M3, this allows to map any given pin to a controller (as opposite to the MSP430, for which a pin has predefined functions other than GPIO), but only pins in the PA port can be used as ADC. +The |
-
The 04-processes.c
example shows how to create and terminate process, more importantly how to communicate between process.
For the Z1 mote is possible to have the following combinations (see Figure below):
-Two phidget connectors for 3.3 V sensors.
-Two phidget connectors for 5 V sensors.
-Two phidget connectors, one for 3.3 V and one for 5 V sensors.
-Processes are useful as they allow to run different parts of our application, in its own semantic context, allowing a cleaner and efficient implementation. For example imagine a process running all the networking part, while separate processes take readings from individual sensors, each one running on its own context.
In the snippet below the ADC channels ADC0 (5V1), ADC3 (5V2), ADC1 (3V1) and ADC7 (3V2) are mapped by default to be used as input for external analogue sensors.
+The example consist of 3 processes, one will spawn another process and receive events about a variable being incremented. When the variable value reaches a limit, the first process will terminate its child process. A third process listening in stand-by will receive a notification about the terminated process and restart it again.
/* MemReg6 == P6.0/A0 == 5V 1 */
-ADC12MCTL0 = (INCH_0 + SREF_0);
-/* MemReg7 == P6.3/A3 == 5V 2 */
-ADC12MCTL1 = (INCH_3 + SREF_0);
-/* MemReg8 == P6.1/A1 == 3V 1 */
-ADC12MCTL2 = (INCH_1 + SREF_0);
-/* MemReg9 == P6.7/A7 == 3V_2 */
-ADC12MCTL3 = (INCH_7 + SREF_0);
+PROCESS(process1, "Main process");
+PROCESS(process2, "Auxiliary process");
+PROCESS(process3, "Another auxiliary process");
+
+/* But we are only going to automatically start the first two */
+AUTOSTART_PROCESSES(&process1, &process2);
+We define the three processes to run, but notice we only automatically start the first two. As opposite to what we have been working with before, after the Z1
or RE-Mote
platform finishes booting it will only call the processes declared within the AUTOSTART_PROCESSES(…)
macro, leaving process3
not started for now.
To read from a sensor connected to the ADC7 channel of the Z1 mote, in my code I would need to make the following steps:
+Let’s review how the first process is implemented:
#include "dev/z1-phidgets.h" (1)
-SENSORS_ACTIVATE(phidgets); (2)
-printf("Phidget 5V 1:%d\n", phidgets.value(PHIDGET3V_2)); (3)
+PROCESS_THREAD(process1, ev, data)
+{
+ PROCESS_BEGIN();
+ static uint8_t counter;
+ printf("Process 1 started\n");
+ process_start(&process3, "Process 1"); (1)
+
+ while(1) {
+ PROCESS_YIELD(); (2)
+ if(ev == event_from_process3) { (3)
+ counter = *((uint8_t *)data); (4)
+ printf("Process 3 has requested shutdown in %u seconds\n", counter);
+ etimer_set(&et1, CLOCK_SECOND);
+ }
+
+ if(ev == PROCESS_EVENT_TIMER) { (5)
+ if(counter <= 0) {
+ process_exit(&process3); (6)
+ } else {
+ printf("Process 3 will be terminated in: %u\n", counter);
+ counter--;
+ leds_toggle(LEDS_RED);
+ etimer_reset(&et1);
+ }
+ }
+ }
+ PROCESS_END();
+}
1 | -Include the driver header | +When process1 starts, it will launch process3 , when starting a process one can pass variables or data as a argument (in this example a string "Process 1") |
2 | -Enable the ADC sensors, as default all 4 ADC channels are enabled | +And then will wait for any event… |
3 | -Request a reading | +Note the process1 has two additional arguments: ev and data , when a process posts information to another process, it uses these arguments to pass information such as a pointer to a variable. |
+
4 | +If we receive an event from process3 it will mean that process has requested an early termination, so we start an etimer … |
+|
5 | +And when our etimer expires… |
+|
6 | +We will terminate process3 |
The Z1 is powered at 3.3 V, but when connected over the USB (standard voltage 5 V) it allows to connect 5 V sensors to the phidget ports, namely 5V1 (ADC3) and 5V2 (ADC0) as there is a voltage divider in the input to adapt the reading from 5 V to 3.3 V.
-Details about the Z1 implementation of the analogue sensors driver are available at platform/z1/dev/z1-phidgets.c
.There is also a driver for the MSP430 internal voltage sensor at platform/z1/dev/battery-sensor.c
, with an example at examples/zolertia/z1/test-battery.c
.
to see the ADC implementation with an example, let’s connect the Phidget 1142 precision Light Sensor.
-The example called test-phidgets.c
in examples/zolertia/z1
will read values from an analog sensor and print them to the terminal.
Connect the light sensor to the 3V2 Phidget connector and compile the example:
+As seen above process3
is terminated by process1
whenever "something" happens at process3
, but what about process2
?
make clean && make test-phidgets.upload && make z1-reset && make login
+PROCESS_THREAD(process2, ev, data)
+{
+ PROCESS_BEGIN();
+ printf("Process 2 started\n");
+
+ while(1) {
+ PROCESS_YIELD();
+ if(ev == PROCESS_EVENT_EXITED) { (1)
+ printf("* Process 3 has been stopped by Process 1!\n");
+ etimer_set(&et2, CLOCK_SECOND * 5); (2)
+ }
+
+ if(ev == PROCESS_EVENT_TIMER) { (3)
+ printf("Process 2 is restarting Process 3\n");
+ process_start(&process3, "Process 2"); (4)
+ }
+ }
+
+ PROCESS_END();
+}
- - | -
-
-
-You can pipeline commands to be executed one after another, in this case the |
+1 | +When process3 is terminated by process1 , we receive a notification |
+
2 | +And start a 5-second timer… | +||
3 | +When the etimer expires… |
+||
4 | +process2 will launch back again process3 , and will send its own name as string |
This is the result:
+We have now read the implementation of process1
and process2
and learned how to start and end processes, and receive data using the data
argument. Let’s check how process3
is implemented:
Starting 'Test Button & Phidgets'
-Please press the User Button
-Phidget 5V 1:123
-Phidget 5V 2:301
-Phidget 3V 1:1710
-Phidget 3V 2:2202
-The light sensor is connected to the 3V2 connector, so the raw value is 2202. Try to illuminate the sensor with a flashlight (from your mobile phone, for example) and then cover it with your hand so that no light can reach it.
+PROCESS_THREAD(process3, ev, data)
+{
+ PROCESS_BEGIN();
+ static char *parent;
+ parent = (char * )data; (1)
+ static uint8_t counter; (2)
+
+ printf("Process 3 started by %s\n", parent);
+ event_from_process3 = process_alloc_event(); (3)
+ etimer_set(&et3, CLOCK_SECOND);
+
+ counter = 0;
+
+ while(1) {
+ PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et3));
+ counter++;
+ leds_toggle(LEDS_GREEN);
+
+ if(counter == 10) {
+ process_post(&process1, event_from_process3, &counter); (4)
+ }
+ etimer_reset(&et3); (5)
+ }
+ PROCESS_END();
+}
From the Phidget website we have the following information about the sensor:
Parameter | -Value | -||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Light level max (5 V) |
-1000 lux |
-||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sensor type |
-Light |
-
Light level min |
-1 lux |
+1 | +Create a pointer to read the processes name passed as string argument to us |
Supply Voltage Min |
-2.4 V |
+2 | +Create a variable to increment |
Supply Voltage Max |
-5.5 V |
+3 | +Allocate a process event to use and communicate to other processes |
Max current consumption |
-5mA |
+4 | +When the counter reaches 10, send an event_from_process3 event to process1 with a pointer to the counter variable |
Light level max (3.3 V) |
-660 lux |
+5 | +Reset the etimer using the current expiration value previously configured |
As you can see, the light sensor can be connected to either the 5 V or the 3.3 V Phidget connector. The max measurable value depends on where you connect it.
+The event_from_process3
process event was previously declared in our example as:
The formula to translate SensorValue into luminosity is:
-Luminosity(lux)=SensorValue
process_event_t event_from_process3;
The RE-Mote platform also allows to connect 5 V sensors to the ADC3 port, using the same voltage divider as the Z1 mote. As seen in the next snippet, the ADC3 port is mapped to the PA2 pin.
+A sensor is a transducer whose purpose is to sense or detect a characteristic of its environment, providing a corresponding output, generally as an electrical or optical signal, related to the quantity of the measured variable.
+Sensors in Contiki are implemented as follow:
/*
- * This driver supports analogue sensors connected to ADC1, ADC2 and AND3 inputs
- * This is controlled by the type argument of the value() function. Possible
- * choices are:
- * - REMOTE_SENSORS_ADC1 (channel 5)
- * - REMOTE_SENSORS_ADC2 (channel 4)
- * - REMOTE_SENSORS_ADC3 (channel 2)
- */
+SENSORS_SENSOR (sensor, SENSOR_NAME, value, configure, status);
This means that a method to configure
the sensor, poll the sensor status
and request a value
have to be implemented. The sensor
structure contains pointers to these functions. The arguments for each function are shown below.
struct sensors_sensor {
+ char * type;
+ int (* value) (int type);
+ int (* configure) (int type, int value);
+ int (* status) (int type);
+};
+ + | +
-
+Then to read data from an attached sensor (as we did in the previous example): +If you need to further expand the available functions (change the |
+
To better understand please refer to the platform/zoul/dev/adc-sensors.c
, for an example of how analogue external sensors can be implemented (to be further discussed in the next sections).
The following functions and macros can be used to work with the sensor:
#include "dev/zoul-sensors.h" (1)
-adc_sensors.configure(SENSORS_HW_INIT, REMOTE_SENSORS_ADC_ALL); (2)
-printf("Phidget ADC2 = %d raw\n", adc_sensors.value(REMOTE_SENSORS_ADC2)); (3)
-printf("Phidget ADC3 = %d raw\n", adc_sensors.value(REMOTE_SENSORS_ADC3));
+SENSORS_ACTIVATE(sensor) (1)
+SENSORS_DEACTIVATE(sensor) (2)
+sensor.value(type); (3)
1 | -Include the ADC driver header | +Enable the sensor, typically configures and turns the sensor on |
2 | -Enable and configure the ADC channels (can selectively choose which to enable) | +Disables the sensor, it is useful to save power |
3 | -Request a reading | +Request a value to the sensor. As one sensor chip might have different types of readings (temperature, humidity, etc.), this is used to specify which measure to take. |
Check the RE-Mote example at example/zolertia/zoul/zoul-demo.c
for more details.
- + |
-Exercise: make the sensor take readings as fast as possible. Print to the screen the ADC raw values and the millivolts (as this sensor is linear, the voltage corresponds to the luxes). What are the max and min values you can get? What is the average light value of the room? Create an application that turns the red LED on when it is dark and the green LED on when the room is bright. In between, all the LEDs should be off. Add a timer and measure the light every 10 seconds.
+
+
Although this is the default way to implement sensors in Contiki, there might be alternatives, depending on the sensor and developer. Always check the specific sensor driver implementation for details. Normally sensors are provided in the same location as the example, in the |
The SENSORS
macro provides external linkage to the sensors defined for the platform and application, allowing to iterate between the available sensors. The following are the sensors defined as default for the RE-Mote platform:
Digital sensors are normally interfaced over a digital communication protocol such as I2C, SPI, 1-Wire, Serial or depending on the manufacturer, a proprietary protocol normally on a ticking clock.
+SENSORS (&button_sensor, &vdd3_sensor, &cc2538_temp_sensor, &adc_sensors);
Digital sensors allow a more extended set of commands (turn on, turn off, configure interrupts). With a digital light sensor for example, you could set a threshold value and let the sensor send an interrupt when reached, without the need for continuous polling.
+ + | +
-
-
+Remember to check the specific sensor information and data sheet for more information. -The button is considered a sensor in Contiki, and it shares the same sensor implementation -
- The Z1 mote has two built in digital sensors: temperature and 3-axis accelerometer. Let us start with the latter, there is an example called |
+
The ADXL345 is an I2C ultra-low power sensor able to read up to 16g, well suited for mobile device applications. It -measures the static acceleration of gravity in tilt-sensing applications, as well as dynamic acceleration resulting from motion or shock. Its high resolution (3.9mg/LSB) enables measurement of inclination changes smaller than 1.0°.
+This macro extends to:
[37] DoubleTap detected! (0xE3) -- DoubleTap Tap
-x: -1 y: 12 z: 223
-[38] Tap detected! (0xC3) -- Tap
-x: -2 y: 8 z: 220
-x: 2 y: 4 z: 221
-x: 3 y: 5 z: 221
-x: 4 y: 5 z: 222
-The accelerometer can read data in the x, y and z axis and has three types of interrupts: a single tap, a double tap and a free-fall (be careful to not damage the mote!).
+const struct sensors_sensor *sensors[] = {
+ &button_sensor,
+ &vdd3_sensor,
+ &cc2538_temp_sensor,
+ &adc_sensors,
+ ((void *)0)
+};
The code has two threads, one for the interruptions and the other for the LEDs. When Contiki starts, it triggers both processes.
The led_process
thread triggers a timer that waits before turning off the LEDs. This is mostly done to filter the rapid signal coming from the accelerometer. The other process is the acceleration. It assigns the callback for the led_off
event.
-Interrupts can happen at any given time, are non periodic and totally asynchronous.
For the Z1
mote the SENSORS
macro is defined within the platforms/z1/contiki-z1-main.c
:
Interrupts can be triggered by external sources (sensors, GPIOs, Watchdog Timer, etc) and should be cleared as soon as possible. When an interrupts happens, the interrupt handler (which is a process that checks the interrupt registers to find out which is the interrupt source) manages it and forwards it to the subscribed callback.
+SENSORS(&button_sensor);
In this example, the accelerometer is initialized and then the interrupts are mapped to a specific callback functions. Interrupt source 1 is mapped to the free fall callback handler and the tap interrupts are mapped to the interrupt source 2.
+The number of sensors can be found with the SENSORS_NUM
macro:
/*
- * Start and setup the accelerometer with default
- * values, _i.e_ no interrupts enabled.
- */
-accm_init();
-/* Register the callback functions */
-ACCM_REGISTER_INT1_CB(accm_ff_cb);
-ACCM_REGISTER_INT2_CB(accm_tap_cb);
+#define SENSORS_NUM (sizeof(sensors) / sizeof(struct sensors_sensor *))
We then need to enable the interrupts like this:
+Sensors can be of two types: analogue or digital. In the following sections examples will be shown for both types, detailing how to connect and use both the platform’s internal sensors as well as the external ones.
accm_set_irq(ADXL345_INT_FREEFALL,
- ADXL345_INT_TAP +
- ADXL345_INT_DOUBLETAP);
+Analogue sensors typically require being connected to an ADC (analogue to digital converter) to translate the analogue (continuous) reading to an equivalent digital value in millivolts. The quality and resolution of the measure depends on both the ADC resolution (up to 12 bits in the Z1 and RE-Mote) and the sampling frequency.
+As a rule of thumb, the sampling frequency must be at least twice that of the phenomenon you are measuring. As an example, if you want to sample human speech (which may contain frequencies up to 8 kHz) you need to sample at twice that frequency (16 kHz).
+In the while loop we read the values from each axis every second. If there are no interrupts, this will be the only thing shown in the terminal.
+The Z1
mote and the RE-Mote
have a built-in voltage sensor provided as an ADC input channel, as well as a generic implementation to read external analogue sensors.
For the next example we will use the ZIG-SHT25, an I2C digital temperature and humidity sensor based on the SHT25 sensor from Sensirion.
+The ADC results in the RE-Mote
returns the reading in voltage directly, there’s no need to further conversion.
For the Z1 mote the ADC output must be converted by means of the following formula: We multiply the measured value by the voltage reference and divide the product by the ADC’s maximum output. As the resolution of the ADC is 12 bits (212 = 4096), we get:
+Voltage, mV = (units * Vref) / 4096;
Parameter | -Value | -
---|---|
Max current consumption |
-300 uA |
-
Sensor type |
-Temperature and Humidity |
-
Supply Voltage [V] |
-2.1 - 3.6 |
-
Energy Consumption |
-3.2 uW (at 8 bit, 1 measurement / s) |
-
Data range |
-0-100 % RH (humidity), -40-125ºC (temperature) |
-
Max resolution |
-14 bits (temperature), 12 bits (humidity) |
-
The ZIG-SHT25 sensor example is available in both examples/zolertia/zoul/test-sht25.c
and examples/zolertia/z1/test-sht25.c
, an output example is given below:
The voltage reference limits the range of our measure, meaning if we use a Vref
of 2.5 V, the sensor will saturate when reading a higher voltage. The reference can be chosen while configuring the ADC, for both the Z1
and RE-Mote
normally 3.3 V is used.
Both the Z1
and the RE-Mote
allow up to 6 analogue sensors to be connected, but only two analogue connectors can be soldered at the same time.
For the RE-Mote
it is possible to have one analogue connector for a 3.3 V analogue sensor (ADC1) and another for 5 V sensors (ADC3).
Starting 'SHT25 test'
-Temperature 23.71 ºC
-Humidity 42.95 % RH
-Temperature 23.71 ºC
-Humidity 42.95 % RH
-Temperature 23.71 ºC
-Humidity 42.95 % RH
-Temperature 23.71 ºC
-Humidity 42.98 %RH
+
As usual you can check the driver implementation at the platform/z1/dev
and platform/zoul/dev
directories. Check out these locations for other sensors and examples to guide you to implement your own.
Note the location of the analogue connectors in the image below.
+Cooja is the Contiki network emulator. Cooja allows large and small networks of Contiki motes to be emulated at the hardware level, which is slower but allows precise inspection of the system behavior, or at a less detailed level, which is faster and allows simulation of larger networks.
+For the Z1
mote is possible to have the following combinations (see Figure below):
Cooja is a highly useful tool for Contiki development as it allows developers to test their code and systems long before running it on the target hardware. Developers regularly set up new simulations both to debug their software and to verify the behavior of their systems.
+Two phidget connectors for 3.3 V sensors.
+Two phidget connectors for 5 V sensors.
+Two phidget connectors, one for 3.3 V and one for 5 V sensors.
+Most of the examples below can be emulated in Cooja as well (except the sensor ones).
+In the terminal window go to the Cooja directory:
+In the snippet below the ADC channels ADC0 (5V1), ADC3 (5V2), ADC1 (3V1) and ADC7 (3V2) are mapped by default to be used as input for external analogue sensors.
cd contiki/tools/cooja
+/* MemReg6 == P6.0/A0 == 5V 1 */
+ADC12MCTL0 = (INCH_0 + SREF_0);
+/* MemReg7 == P6.3/A3 == 5V 2 */
+ADC12MCTL1 = (INCH_3 + SREF_0);
+/* MemReg8 == P6.1/A1 == 3V 1 */
+ADC12MCTL2 = (INCH_1 + SREF_0);
+/* MemReg9 == P6.7/A7 == 3V_2 */
+ADC12MCTL3 = (INCH_7 + SREF_0);
Start Cooja with the command:
+To read from a sensor connected to the ADC7 channel of the Z1
mote, in my code I would need to make the following steps:
ant run
+#include "dev/z1-phidgets.h" (1)
+SENSORS_ACTIVATE(phidgets); (2)
+printf("Phidget 5V 1:%d\n", phidgets.value(PHIDGET3V_2)); (3)
When Cooja is compiled, it will show a blue empty window. Now that Cooja is up and running, we can try it out with an example simulation.
+1 | +Include the driver header | +
2 | +Enable the ADC sensors, as default all 4 ADC channels are enabled | +
3 | +Request a reading | +
Go to File
, Open Simulation
, Browse
then navigate to the examples/hello-world
and select the hello-world-example.csc
. This will load the previously saved hello world example.
The Z1
is powered at 3.3 V, but when connected over the USB (standard voltage 5 V) it allows to connect 5 V sensors to the phidget ports, namely 5V1 (ADC3) and 5V2 (ADC0) as there is a voltage divider in the input to adapt the reading from 5 V to 3.3V.
If you want to edit an example (for istance, to use a different type of platform), instead of choosing the Browse
option, select Open and Reconfigure
, this will walk you through the steps to configure the example.
Details about the Z1
implementation of the analogue sensors driver are available at platform/z1/dev/z1-phidgets.c
.
Click the File
menu and click New simulation
. Cooja now opens up the Create new simulation
dialog. In this dialog, we may choose to give our simulation a new name, but for this example, we’ll just stick with My simulation
. Leave the other options at default values. Click the Create
button.
There is also a driver for the MSP430 internal voltage sensor at platform/z1/dev/battery-sensor.c
, with an example at examples/zolertia/z1/test-battery.c
.
Cooja brings up the new simulation. You can choose what you want to visualize by using the Tools
menu. The Network
window shows all the motes in the simulated network, it should be empty now since we have no motes in our simulation. The Timeline
window shows all communication events in the simulation over time - very handy for understanding what goes on in the network. The Mote output
window shows all serial port printouts from all the motes. The Notes
window is where we can put notes for our simulation. And the Simulation control
window is where we start, pause, and reload our simulation.
#include "dev/battery-sensor.h"
+/*---------------------------------------------------------------------------*/
+PROCESS(test_battery_process, "Battery Sensor Test");
+AUTOSTART_PROCESSES(&test_battery_process);
+/*---------------------------------------------------------------------------*/
+PROCESS_THREAD(test_battery_process, ev, data)
+{
+ static uint32_t battery;
+ PROCESS_BEGIN();
+ SENSORS_ACTIVATE(battery_sensor);
+
+ while(1) {
+ battery = battery_sensor.value(0);
+ battery *= 5000;
+ battery /= 4096;
+ printf("Battery: %u.%02uV\n", (uint16_t)battery / 1000, (uint16_t)battery % 1000);
+ }
+ PROCESS_END();
+}
Before we can simulate our network, we must add one or more motes. We do this via the Motes
menu, where we click on Add motes
. Since this is the first mote we add, we must first create a mote type to add. Click Create new mote type
and select one of the available mote types. For this example, we click Z1 mote
to create an emulated Z1 mote type. Cooja opens the Create Mote Type
dialog, in which we can choose a name for our mote type as well as the Contiki application that our mote type will run.
When the USB is connected or the battery is full, the result is similar to:
For this example, we stick with the suggested name, and click on the Browse
button on the right hand side to choose our Contiki application.
Battery: 3.21V
+Battery: 3.18V
+Battery: 3.20V
+Battery: 3.18V
+Battery: 3.20V
+Battery: 3.20V
+Battery: 3.18V
+Battery: 3.20V
+Battery: 3.18V
It is possible also to connect an external analogue sensor, like the Phidget 1142 precision Light Sensor or the Grove/Seeedstudio Light Sensor (P).
In the previous section we covered some of the core features of Contiki, basics of sensors and a general overview of how the applications are built, programmed and simulated in Contiki. This section introduces the wireless communication, details about radios, and basics ideas about configuring our platforms.
+The very first step is understanding how our platform is configured.
+Each platform implements its own set of default values and configurations, to be used by underlying modules like the radio, the serial port, etc.
+The places to visit for the Zolertia platform are the following:
-Specific hardware settings: parameters such as the default I2C pins, ADC channels, module-specific pin assignment and platform information can be found at platform/zoul/remote/dev/board.h
and platform/z1/platform-conf.h
.
Specific Contiki settings: UART settings, MAC driver, radio channel, IPv6, RIME and network buffer configuration, among others, can be found at platform/zoul/contiki-conf.h
and platform/z1/contiki-conf.h
.
The example called test-phidgets.c
in examples/zolertia/z1
will read values from an analog sensor and print them to the terminal.
As a general good practice, user configurable parameters are normally allowed to be overridden by the applications, this also serves as a guideline to discern which values can be changed by the casual user, from those meant to be changed only if you really know what you are doing. Below is an example:
+Connect the light sensor to the 3V2 Phidget connector and compile the example:
#ifndef UART0_CONF_BAUD_RATE
-#define UART0_CONF_BAUD_RATE 115200
-#endif
-make clean && make test-phidgets.upload && make z1-reset && make login
By defining UART0_CONF_BAUD_RATE
in our application’s project-conf.h
, we can change the default 115200 baud rate. Notice that generally it is a good practice to add CONF
to the user configurable parameters.
In the next section we will review the most notable parameters to configure, but as usual depending on your application and setup, the best way to ensure everything is properly set is by reviewing the specific platform configuration files, and modify or redefine accordingly.
+This is the result:
+Starting 'Test Button & Phidgets'
+Please press the User Button
+Phidget 5V 1:123
+Phidget 5V 2:301
+Phidget 3V 1:1710
+Phidget 3V 2:2202
To start working you must first define the Node ID of each node, this will be used to generate the mote’s MAC address and the IPv6 addresses (link-local and global).
The RE-Mote platform comes with two pre-loaded MAC addresses stored in its internal flash memory, but the user can instead choose a hardcoded one. The following switches at platform/zoul/contiki-conf.h
selects the chosen one.
/**
- * \name IEEE address configuration
- *
- * Used to generate our RIME & IPv6 address
- * @{
- */
-/**
- * \brief Location of the IEEE address
- * 0 => Read from InfoPage,
- * 1 => Use a hardcoded address, configured by IEEE_ADDR_CONF_ADDRESS
- */
-#ifndef IEEE_ADDR_CONF_HARDCODED
-#define IEEE_ADDR_CONF_HARDCODED 0
-#endif
-
-/**
- * \brief Location of the IEEE address in the InfoPage when
- * IEEE_ADDR_CONF_HARDCODED is defined as 0
- * 0 => Use the primary address location
- * 1 => Use the secondary address location
- */
-#ifndef IEEE_ADDR_CONF_USE_SECONDARY_LOCATION
-#define IEEE_ADDR_CONF_USE_SECONDARY_LOCATION 0
-#endif
-The light sensor is connected to the 3V2 connector, so the raw value is 2202. Try to illuminate the sensor with a flashlight (from your mobile phone, for example) and then cover it with your hand so that no light can reach it.
If using your own hardcoded address, the following define can be overridden by the application:
-#ifndef IEEE_ADDR_CONF_ADDRESS
-#define IEEE_ADDR_CONF_ADDRESS { 0x00, 0x12, 0x4B, 0x00, 0x89, 0xAB, 0xCD, 0xEF }
-#endif
+From the Phidget website we have the following information about the sensor:
Parameter | +Value | +
---|---|
Light level max (5 V) |
+1000 lux |
+
Sensor type |
+Light |
+
Light level min |
+1 lux |
+
Supply Voltage Min |
+2.4 V |
+
Supply Voltage Max |
+5.5 V |
+
Max current consumption |
+5mA |
+
Light level max (3.3 V) |
+660 lux |
+
As you can see, the light sensor can be connected to either the 5 V or the 3.3 V analogue connector. The max measurable value depends on where you connect it.
The formula to translate SensorValue into luminosity is:
+Luminosity(lux)=SensorValue
Let’s use the ID from the mote list:
+The RE-Mote platform also allows to connect 5 V sensors to the ADC3 port, using the same voltage divider as the Z1 mote. As seen in the next snippet, the ADC3 port is mapped to the PA2 pin.
Reference Device Description
---------------------------------------------------
-Z1RC3301 /dev/ttyUSB0 Silicon Labs Zolertia Z1
-The node ID should be 3301
(decimal) if no previously saved node ID is found in the flash memory.
/*
+ * This driver supports analogue sensors connected to ADC1, ADC2 and AND3 inputs
+ * This is controlled by the type argument of the value() function. Possible
+ * choices are:
+ * - REMOTE_SENSORS_ADC1 (channel 5)
+ * - REMOTE_SENSORS_ADC2 (channel 4)
+ * - REMOTE_SENSORS_ADC3 (channel 2)
+ */
Let’s see how Contiki uses this to derive a full IPv6 and MAC address. At platforms/z1/contiki-z1-main.c
#ifdef SERIALNUM
- if(!node_id) {
- PRINTF("Node id is not set, using Z1 product ID\n");
- node_id = SERIALNUM;
- }
-#endif
-node_mac[0] = 0xc1; /* Hardcoded for Z1 */
-node_mac[1] = 0x0c; /* Hardcoded for Revision C */
-node_mac[2] = 0x00; /* Hardcoded to arbitrary even number so that the 802.15.4 MAC address is compatible with an Ethernet MAC address - byte 0 (byte 2 in the DS ID) */
-node_mac[3] = 0x00; /* Hardcoded */
-node_mac[4] = 0x00; /* Hardcoded */
-node_mac[5] = 0x00; /* Hardcoded */
-node_mac[6] = node_id >> 8;
-node_mac[7] = node_id & 0xff;
-}
+
So the mote should have the following addresses:
+Then to read data from an attached sensor (as we did in the previous example):
MAC c1:0c:00:00:00:00:0c:e5
-Node id is set to 3301.
-Tentative link-local IPv6 address fe80:0000:0000:0000:c30c:0000:0000:0ce5
+#include "dev/zoul-sensors.h" (1)
+adc_sensors.configure(SENSORS_HW_INIT, REMOTE_SENSORS_ADC_ALL); (2)
+printf("ADC1 = %d raw\n", adc_sensors.value(REMOTE_SENSORS_ADC1)); (3)
+printf("ADC3 = %d raw\n", adc_sensors.value(REMOTE_SENSORS_ADC3));
Where 0xce5
is the hex value corresponding to 3301
. The global address is only set when an IPv6 prefix is assigned (by now you should know this from earlier sections).
1 | +Include the ADC driver header | +
2 | +Enable and configure the ADC channels (can selectively choose which to enable) | +
3 | +Request a reading | +
If instead you wish to have your own addressing scheme, you can edit the node_mac values at contiki-z1-main.c
file. If you wish to replace the node id value obtained from the product id, you need to store a new one in the flash memory, luckily there is already an application to do so:
Note the ADC3 voltage reading has to be multiplied by 3/2 to get the actual 0-5V range value, as there is a voltage divider with a 500K/200K relationship.
Go to examples/zolertia/z1
location and replace the 158
for your own required value:
Now let’s connect a Grove Light Sensor
to the RE-Mote
:
make clean && make burn-nodeid.upload nodeid=158 nodemac=158 && make z1-reset && make login
+
You should see the following:
+Compile and program the RE-Mote example at example/zolertia/zoul/zoul-demo.c
, the output should be similar to:
MAC c1:0c:00:00:00:00:0c:e5 Ref ID: 3301
-Contiki-2.6-1803-g03f57ae started. Node id is set to 3301.
-CSMA ContikiMAC, channel check rate 8 Hz, radio channel 26
-Tentative link-local IPv6 address fe80:0000:0000:0000:c30c:0000:0000:0ce5
-Starting 'Burn node id'
-Burning node id 158
-Restored node id 158
+connecting to /dev/ttyUSB0 (115200) [OK]
+-----------------------------------------
+Counter = 0x00000000
+VDD = 3299 mV
+Temperature = 33333 mC
+ADC1 = 112 raw
+ADC3 = 1516 raw
As you can see, now the node ID has been changed to 158, when you restart the mote you should see that the changes have been applied:
+The above result was taken with the light sensor covered, so aproximately with no light the voltage value is 0.112V. This light sensor has a light-dependent resistor (LDR), the output in voltage units correlates to the equivalent LDR. The Grove wiki page shows how the resistance/luxes graphs to obtain proper lux values, but probably a better option is to calibrate by using a known lux source, or just knowing the smaller the value the darkest the ambient is.
MAC c1:0c:00:00:00:00:00:9e Ref ID: 3301
-Contiki-2.6-1803-g03f57ae started. Node id is set to 158.
-CSMA ContikiMAC, channel check rate 8 Hz, radio channel 26
-Tentative link-local IPv6 address fe80:0000:0000:0000:c30c:0000:0000:009e
++ + | ++Exercise: make the sensor take readings as fast as possible. Print to the screen the ADC raw values and the millivolts (as this sensor is linear, the voltage corresponds to the luxes). What are the max and min values you can get? What is the average light value of the room? Create an application that turns the red LED on when it is dark and the green LED on when the room is bright. In between, all the LEDs should be off. Add a timer and measure the light every 10 seconds. + | +
Note the RE-Mote
has built-in voltage and core temperature sensors. The voltage sensor provides the voltage level of the CC2538
system on chip, whereas the core temperature gives the operation temperature. As shown in the previous code snippet, the zoul-demo.c
example also shows how to use both sensors:
printf("VDD = %d mV\n",
+ vdd3_sensor.value(CC2538_SENSORS_VALUE_TYPE_CONVERTED));
+
+ printf("Temperature = %d mC\n",
+ cc2538_temp_sensor.value(CC2538_SENSORS_VALUE_TYPE_CONVERTED));
The bandwidth and allowed channels depend on the operating frequency band. They will be determined by the spectrum regulation agency of the country, along with maximum transmitted power allowable. -.The IEEE 802.15.4 standard
+Both sensors are initialized at booting by the RE-Mote
.
The IEEE 802.15.4 is a standard for wireless communication, it specifies the physical and media access control layers for low-rate wireless personal area networks (LR-WPANs).
+Digital sensors are normally interfaced over a digital communication protocol such as I2C, SPI, 1-Wire, Serial or depending on the manufacturer, a proprietary protocol normally on a ticking clock.
The standard specifies the use of the 868-868.8 MHz (in Europe and many other countries), the 902-928 MHz (in United States, Canada, and some Latin America countries), or the world-wide 2.400-2.4835 GHz band part of the Industrial Scientific and Medical applications (ISM).
+Digital sensors allow a more extended set of commands (turn on, turn off, configure interrupts). With a digital light sensor for example, you could set a threshold value and let the sensor send an interrupt when reached, without the need for continuous polling.
+Remember to check the specific sensor information and data sheet for more information.
In practice the 2.4 GHz band is being heavily used due to its world-wide availability. The ZigBee proprietary protocol by the ZigBee alliance was one of the early adopters of IEEE 802.15.4. It did so leveraging the physical and MAC layer of IEEE 802.15.4, specifying on top additional routing and networking functionality to build mesh networks.
+The Z1 mote has two built in digital sensors: temperature and 3-axis accelerometer. The ADXL345
accelerometer has an example named test-adxl345.c
in examples/zolertia/z1
showing how the sensor works.
Quite recently the Thread Group -has proposed its own simplified IPv6-based mesh networking protocol for connecting products around the home to each other, to the Internet and to the cloud. This will surely boost the adoption of IEEE 802.15.4 and IPv6.
+The ADXL345
is an I2C ultra-low power sensor able to read up to 16g, well suited for mobile device applications. It measures the static acceleration of gravity in tilt-sensing applications, as well as dynamic acceleration resulting from motion or shock. Its high resolution (3.9mg/LSB) enables measurement of inclination changes smaller than 1.0°.
[37] DoubleTap detected! (0xE3) -- DoubleTap Tap
+x: -1 y: 12 z: 223
+[38] Tap detected! (0xC3) -- Tap
+x: -2 y: 8 z: 220
+x: 2 y: 4 z: 221
+x: 3 y: 5 z: 221
+x: 4 y: 5 z: 222
The accelerometer can read data in the x, y and z axis and has three types of interrupts: a single tap, a double tap and a free-fall (be careful to not damage the mote!).
Having an accelerometer on-board is quite useful: you can detect if anyone is tampering with the device, or detect if someone falls, monitor vibration and generate an alarm if exceeds a given threshold (causing a component to break), etc.
As the 2.4 GHz band is also used by other technologies like WiFi and Bluetooth, this spectrum is shared and overlaps might occur. The Figure below shows the channel allocation of the 2.4 GHz IEEE 802.15.4, and the recommended channels to avoid interferences with other co-located devices. With the rise of the Bluetooth Low Energy, and the ubiquitous WiFi present in our lives, the selection of a proper operating channel is crucial in any deployment.
+The code has two threads, one for the interruptions and the other for the LEDs. When Contiki starts, it triggers both processes.
The led_process
thread triggers a timer that waits before turning off the LEDs. This is mostly done to filter the rapid signal coming from the accelerometer. The other process is the acceleration. It assigns the callback for the led_off
event.
+Interrupts can happen at any given time, are non periodic and totally asynchronous.
Interrupts can be triggered by external sources (sensors, GPIOs, Watchdog Timer, etc) and should be cleared as soon as possible. When an interrupts happens, the interrupt handler (which is a process that checks the interrupt registers to find out which is the interrupt source) manages it and forwards it to the subscribed callback.
The default channel of the RE-Mote is defined as follows:
+In this example, the accelerometer is initialized and then the interrupts are mapped to a specific callback functions. Interrupt source 1 is mapped to the free fall callback handler and the tap interrupts are mapped to the interrupt source 2.
#ifndef CC2538_RF_CONF_CHANNEL
-#define CC2538_RF_CONF_CHANNEL 26
-#endif /* CC2538_RF_CONF_CHANNEL */
+/*
+ * Start and setup the accelerometer with default
+ * values, _i.e_ no interrupts enabled.
+ */
+SENSORS_ACTIVATE(adxl345);
+/* Register the callback functions */
+ACCM_REGISTER_INT1_CB(accm_ff_cb);
+ACCM_REGISTER_INT2_CB(accm_tap_cb);
The Z1 mote defines its default channel as:
+We then need to enable the interrupts like this:
#ifdef RF_CHANNEL
-#define CC2420_CONF_CHANNEL RF_CHANNEL
-#endif
-
-#ifndef CC2420_CONF_CHANNEL
-#define CC2420_CONF_CHANNEL 26
-#endif /* CC2420_CONF_CHANNEL */
-accm_set_irq(ADXL345_INT_FREEFALL,
+ ADXL345_INT_TAP +
+ ADXL345_INT_DOUBLETAP);
The radio channel can be defined from the application’s project-conf.h
or the Makefile
.
The radio drivers in Contiki are implemented to comply with the struct radio_driver
in core/dev/radio.h
. This abstraction allows to interact with the radio using a standardized API, independently of the radio hardware. The functions to set and read radio parameters are explained below.
/** Get a radio parameter value. */
-radio_result_t (* get_value)(radio_param_t param, radio_value_t *value);
-
-/** Set a radio parameter value. */
-radio_result_t (* set_value)(radio_param_t param, radio_value_t value);
+In the while loop we read the values from each axis every second. If there are no interrupts, this will be the only thing shown in the terminal.
+ + | ++Exercise: put the mote in different positions and check the values of the accelerometer. Try to understand which is x, y and z. Measure the maximum acceleration by shaking the mote. Turn on and off the LED according to the acceleration on one axis. + | +
To change the channel from the application use RADIO_PARAM_CHANNEL
as follows:
The TMP102
digital temperature sensor on-board the Z1
platform has a +/-5ºC accuracy and can measure temperatures from -25ºC to 85ºC. There is an example ready to use at examples/zolertia/z1/test-tmp102.c
showing how the sensor works.
rd = NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, value);
-Where value
can be any value from 11 to 26, and rd will be either RADIO_RESULT_INVALID_VALUE
or RADIO_RESULT_OK
.
The RE-Mote has a dual 2.4 GHz and 863-950 MHz RF interface, which can be selected alternatively, or used simultaneously (the latter currently not supported in Contiki at the moment).
-As default the RE-Mote uses the IEEE 802.15.4g mandatory mode for the 868 MHz band, configured for 2-GFSK modulation, 50 kbps data rate and with 33 channels available.
+PROCESS_THREAD(temp_process, ev, data)
+{
+ PROCESS_BEGIN();
+ int16_t temp;
+ SENSORS_ACTIVATE(tmp102);
+ while(1) {
+ etimer_set(&et, TMP102_READ_INTERVAL);
+ PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et));
+ temp = tmp102.value(TMP102_READ);
+ printf("Temp = %d.%02d\n", temp / 100, temp % 100);
+ }
+ PROCESS_END();
+}
The RE-Mote uses the Texas Instruments CC1200 RF transceiver, referred to in Contiki as dev/cc1200
. The default configuration file is located in dev/cc1200/cc1200-802154g-863-870-fsk-50kbps.c
.
To change channels from the application we use the RF API:
+The output is similar to below:
rd = NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, value);
+Temp = 27.75
+Temp = 28.12
+Temp = 28.31
+Temp = 28.18
+Temp = 28.12
+Temp = 28.06
+ + | +
-
+Where The TMP102 is placed close to the CP2102 Serial to USB converter of the |
+
The radio frequency power transmission is that at the output of the transmitter before reaching the antenna. The higher the transmission power the higher the wireless range, but the power consumption usually increases as well. The range is heavily dependent on the frequency, the antenna used and its height above the ground.
+External digital sensors can be connected to the Z1
and RE-Mote
platforms.
The RE-Mote platform uses the CC2538 built-in 2.4 GHz radio. As default the transmission power is set to 3 dBm (2 mW) in the cpu/cc2538/cc2538-rf.h
header as shown below.
As shown in Figure 8, the RE-Mote
and the Z1
allows I2C and SPI sensors to be connected to the Ziglet
port: a 5-pin connector with 2.54mm pitch spacing as follows:
CC2538_RF_TX_POWER_RECOMMENDED 0xD5
+
This recommended value is taken from the SmartRF Studio.
+For the next example we will use the SHT25, an I2C digital temperature and humidity sensor from Sensirion.
Other values and its corresponding output power levels are shown in the next table.
+TX Power (dBm) | +Parameter | Value | |
---|---|---|---|
-24 |
-0x00 |
+Max current consumption |
+300 uA |
+7 |
-0xFF |
+Sensor type |
+Temperature and Humidity |
+5 |
-0xED |
+Supply Voltage [V] |
+2.1 - 3.6 |
+3 |
-0xD5 |
+Energy Consumption |
+3.2 uW (at 8 bit, 1 measurement / s) |
+1 |
-0xC5 |
+Data range |
+0-100 % RH (humidity), -40-125ºC (temperature) |
0 |
-0xB6 |
+Max resolution |
+14 bits (temperature), 12 bits (humidity) |
As usual you can check the driver implementation at the platform/z1/dev
and platform/zoul/dev
directories. Check out these locations for other sensors and examples to guide you to implement your own.
When adding an external sensor to your application, you will need to tell the compiler to include the sensor libraries. In the previous examples this was not required as the platforms include its on-board sensor automatically. Adding the external sensor libraries to the application is done in the Makefile
. This is how the RE-Mote
Makefile looks like:
DEFINES+=PROJECT_CONF_H=\"project-conf.h\" (1)
+
+CONTIKI_PROJECT = zoul-demo test-tsl2563 test-sht25 test-power-mgmt (2)
+CONTIKI_PROJECT += test-bmp085-bmp180 test-motion test-rotation-sensor
+CONTIKI_PROJECT += test-grove-light-sensor test-grove-loudness-sensor
+CONTIKI_PROJECT += test-weather-meter test-grove-gyro test-lcd test-iaq
+CONTIKI_PROJECT += test-pm10-sensor test-vac-sensor test-aac-sensor
+CONTIKI_PROJECT += test-zonik
+
+CONTIKI_TARGET_SOURCEFILES += tsl2563.c sht25.c bmpx8x.c motion-sensor.c (3)
+CONTIKI_TARGET_SOURCEFILES += adc-sensors.c weather-meter.c grove-gyro.c
+CONTIKI_TARGET_SOURCEFILES += rgb-bl-lcd.c pm10-sensor.c iaq.c zonik.c
+
+all: $(CONTIKI_PROJECT)
+
+CONTIKI = ../../..
+include $(CONTIKI)/Makefile.include
+-1 |
-0xB0 |
+1 | +The location of the project-conf.h file for specific application configuration. |
-3 |
-0xA1 |
+2 | +The test examples to compile automatically if no application is defined. If you run make it will compile all of them at once! |
-5 |
-0x91 |
+3 | +The name of the libraries to include. As default it will search the platform/dev folder |
The sht25.c
is the library of the SHT25 sensor.
The SHT25 sensor example is available in both examples/zolertia/zoul/test-sht25.c
and examples/zolertia/z1/test-sht25.c
.
/*---------------------------------------------------------------------------*/
+#include "dev/sht25.h"
+/*---------------------------------------------------------------------------*/
+PROCESS(remote_sht25_process, "SHT25 test");
+AUTOSTART_PROCESSES(&remote_sht25_process);
+/*---------------------------------------------------------------------------*/
+static struct etimer et;
+/*---------------------------------------------------------------------------*/
+PROCESS_THREAD(remote_sht25_process, ev, data)
+{
+ int16_t temperature, humidity;
+
+ PROCESS_BEGIN();
+ SENSORS_ACTIVATE(sht25); (1)
+
+ /* Check if the sensor voltage operation is over 2.25V */
+ if(sht25.value(SHT25_VOLTAGE_ALARM)) { (2)
+ printf("Voltage is lower than recommended for the sensor operation\n");
+ PROCESS_EXIT();
+ }
+
+ /* Configure the sensor for maximum resolution (14-bit temperature, 12-bit
+ * relative humidity), this will require up to 85ms for the temperature
+ * integration, and 29ms for the relative humidity (this is the default
+ * setting at power on). To achieve a faster integration time at the cost
+ * of a lower resolution, change the value below accordingly, see sht25.h.
+ */
+ sht25.configure(SHT25_RESOLUTION, SHT2X_RES_14T_12RH); (3)
+
+ /* Let it spin and read sensor data */
+
+ while(1) {
+ etimer_set(&et, CLOCK_SECOND);
+ PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et));
+ temperature = sht25.value(SHT25_VAL_TEMP); (4)
+ printf("Temperature %02d.%02d ºC, ", temperature / 100, temperature % 100);
+ humidity = sht25.value(SHT25_VAL_HUM); (5)
+ printf("Humidity %02d.%02d RH\n", humidity / 100, humidity % 100);
+ }
+ PROCESS_END();
+}
+-7 |
-0x88 |
+1 | +Enable the sensor |
-9 |
-0x72 |
+2 | +Check if the sensor powering voltage is below 2.25V. The sensor requires at least 2.25V to work properly, else it might yield unreliable measures |
-11 |
-0x62 |
+3 | +Configure the sensor resolution |
-13 |
-0x58 |
+4 | +Retrieve a temperature reading |
-15 |
-0x42 |
+5 | +Retrieve a humidity reading |
An output example is given below:
+Starting 'SHT25 test'
+Temperature 23.71 ºC
+Humidity 42.95 % RH
+Temperature 23.71 ºC
+Humidity 42.95 % RH
+Temperature 23.71 ºC
+Humidity 42.95 % RH
+Temperature 23.71 ºC
+Humidity 42.98 %RH
+The current consumption can go from 24 mA to 34 mA when changing the transmission power from 0 dBm to 7 dBm (AN125).
+The next section will continue with the walkthrough on how to work with the on-board sensors, shown in the analogue and digital sensors section.
+ + | +
-
+The Z1 mote uses the Texas Instrument CC2420 RF transceiver. As default the transmission power is set to 0 dBm (1 mW), which is the maximum allowed by the radio. +Normally when working with sensors it is often recommeded to operate as follows: +
+
+
+
-
-The available output power and its corresponding configuration values are listed in the table below, as well as the current consumption at each level. +This ensures the sensor is configured as expected. It is quite common to have more than one digital sensor sharing the same communication bus (I2C/SPI), or having a driver configuring the ADC ports with a general configuration thus overriding the expected one.
+ On-board sensors and ADC
-
+For both platforms the transmission power can be changed with: +The
+
The following snippet shows how is this done:
-
+
-
+Where The key is the
+
-Whenever we compile using
-
Changing the transmission power for the RE-Mote (CC1200)
-
+As mentioned earlier, the RE-Mote has an on-board sub-1 GHz interface based on the CC1120 radio transceiver, configured to operate in the 863-950 MHz bands. The maximum transmission power allowed depends on the specific band and regulations in place, which can also impose limits on the maximum antenna gain, be sure to check the local regulations before changing the output power. +For the
+
+
+
-
+The regulations are country specific and out of the scope of this section. +And for the
+
+
+
-
-The following values are taken from the SmartRF Studio, using default IEEE 802.15.4g ETSI compliant configuration. +The
-
+These values correspond to the So the value we would need to use is 0x14 in hexadecimal or 20 in decimal base. +
+
+The conversion of a single pin number to the required pin mask can be done using the following macro: +
+
+
+
+Also the actual port register address can be obtained from the port number by using the following macro: +
+
+
+
+In the
+
+The
+
+
+
+
+
+
+Then in a loop we read the pin status (if it is high or low), and toggle its state. Notice the
+
+In this example if pin
+
+
+
+
+
+
+The
+
+
+
+
+
+
+We need to configure first the pin function (we select the pin as GPIO by setting the pin mas value on the
+
+
+
+
+
+
+And then to set the pin high or low we use the
+ Wireless with Contiki+
+
+
+In the previous section we covered some of the core features of Contiki, basics of sensors and a general overview of how the applications are built, programmed and simulated in Contiki. This section introduces the wireless communication, details about radios, and in general how to configure our platforms. +
+
+Addressing and Radio Frequency basics+
+
+The very first step is understanding how our platform is configured. +
+
+Each platform implements its own set of default values and configurations, to be used by underlying modules like the radio, the serial port, etc. +
+
+The places to visit for the Zolertia platform are the following: +
+
+
+
+As a general good practice, user configurable parameters are normally allowed to be overridden by the applications, this also serves as a guideline to discern which values can be changed by the casual user, from those meant to be changed only if you really know what you are doing. Below is an example: +
+
+
+
+
+
+
+By defining
+
+
+
+In the next section we will review the most notable parameters to configure, but as usual depending on your application and setup, the best way to ensure everything is properly set is by reviewing the specific platform configuration files, and modify or redefine accordingly. +
+
+Device addressing+
+
+To start working you must first define the Node ID of each node, this will be used to generate the mote’s MAC address and the IPv6 addresses (link-local and global). +
+
+RE-Mote addresses+
+
+The
+
+
+
+
+
+
+If using your own hardcoded address, the following define can be overridden by the application: +
+
+
+
+
+
+
+Z1 mote addresses+
+
+Let’s use the ID from the mote list: +
+
+
+
+
+
+
+The node ID should be
+
+Let’s see how Contiki uses this to derive a full IPv6 and MAC address. At
+
+
+
+
+
+
+So the mote should have the following addresses: +
+
+
+
+
+
+
+Where
+
+If instead you wish to have your own addressing scheme, you can edit the node_mac values at
+
+Go to
+
+
+
+
+
+
+You should see the following: +
+
+
+
+
+
+
+As you can see, now the node ID has been changed to 158, when you restart the mote you should see that the changes have been applied: +
+
+
+
+
+
+
+Set the bandwidth and channel+
+
+The bandwidth and allowed channels depend on the operating frequency band. They will be determined by the spectrum regulation agency of the country, along with maximum transmitted power allowable. +.The IEEE 802.15.4 standard +
+
+
+
+
+
+The IEEE 802.15.4 is a standard for wireless communication, it specifies the physical and media access control layers for low-rate wireless personal area networks (LR-WPANs). +
+
+The standard specifies the use of the 868-868.8 MHz (in Europe and many other countries), the 902-928 MHz (in United States, Canada, and some Latin America countries), or the world-wide 2.400-2.4835 GHz band part of the Industrial Scientific and Medical applications (ISM). +
+
+
+
+
+Figure 48. IEEE 802.15.4 2.4 GHz regulation requirements (electronicdesign.com, 2013)
+
+
+In practice the 2.4 GHz band is being heavily used due to its world-wide availability. The ZigBee proprietary protocol by the ZigBee alliance was one of the early adopters of IEEE 802.15.4. It did so leveraging the physical and MAC layer of IEEE 802.15.4, specifying on top additional routing and networking functionality to build mesh networks. +
+
+Quite recently the Thread Group +has proposed its own simplified IPv6-based mesh networking protocol for connecting products around the home to each other, to the Internet and to the cloud. +
+
+
+
+
+Figure 49. Thread layers and standards (Thread group, 2015)
+
+
+Working at 2.4 GHz+
+
+As the 2.4 GHz band is also used by other technologies like WiFi and Bluetooth, this spectrum is shared and overlaps might occur. The Figure below shows the channel allocation of the 2.4 GHz IEEE 802.15.4, and the recommended channels to avoid interferences with other co-located devices. With the rise of the Bluetooth Low Energy, and the ubiquitous WiFi present in our lives, the selection of a proper operating channel is crucial in any deployment. +
+
+
+
+
+Figure 50. Channel assignment
+
+
+The default channel of the RE-Mote is defined as follows: +
+
+
+
+
+
+
+The Z1 mote defines its default channel as: +
+
+
+
+
+
+
+The radio channel can be defined from the application’s
+
+The radio drivers in Contiki are implemented to comply with the
+
+
+
+
+
+
+To change the channel from the application use
+
+
+
+
+
+
+Where
+
+Working at 863-950 MHz+
+
+The RE-Mote has a dual 2.4 GHz and 863-950 MHz RF interface, which can be selected alternatively, or used simultaneously (the latter currently not supported in Contiki at the moment). +
+
+As default the RE-Mote uses the IEEE 802.15.4g mandatory mode for the 868 MHz band, configured for 2-GFSK modulation, 50 kbps data rate and with 33 channels available. +
+
+The RE-Mote uses the Texas Instruments CC1200 RF transceiver, referred to in Contiki as
+
+To change channels from the application we use the RF API: +
+
+
+
+
+
+
+Where
+
+Set the transmission power+
+
+The radio frequency power transmission is that at the output of the transmitter before reaching the antenna. The higher the transmission power the higher the wireless range, but the power consumption usually increases as well. The range is heavily dependent on the frequency, the antenna used and its height above the ground. +
+
+Changing the transmission power for the Z1 (CC2420) and the RE-Mote (CC2538)+
+
+The RE-Mote platform uses the CC2538 built-in 2.4 GHz radio. As default the transmission power is set to 3 dBm (2 mW) in the
+
+
+
+
+
+
+This recommended value is taken from the SmartRF Studio. +
+
+Other values and its corresponding output power levels are shown in the next table. +
+
+
+
+The current consumption can go from 24 mA to 34 mA when changing the transmission power from 0 dBm to 7 dBm (AN125). +
+
+The Z1 mote uses the Texas Instrument CC2420 RF transceiver. As default the transmission power is set to 0 dBm (1 mW), which is the maximum allowed by the radio. +
+
+The available output power and its corresponding configuration values are listed in the table below, as well as the current consumption at each level. +
+
+For both platforms the transmission power can be changed with: +
+
+
+
+
+
+
+Where
+
+Changing the transmission power for the RE-Mote (CC1200)+
+
+As mentioned earlier, the RE-Mote has an on-board sub-1 GHz interface based on the CC1120 radio transceiver, configured to operate in the 863-950 MHz bands. The maximum transmission power allowed depends on the specific band and regulations in place, which can also impose limits on the maximum antenna gain, be sure to check the local regulations before changing the output power. +
+
+The regulations are country specific and out of the scope of this section. +
+
+The following values are taken from the SmartRF Studio, using default IEEE 802.15.4g ETSI compliant configuration. +
+
+These values correspond to the
+
+As default the CC1200 driver in Contiki starts with the maximum transmission power, defined as follows: +
+
+
+
+
+
+
+The minimum and maximum allowed values are set in
+
+
+
+
+
+
+The CC1200 driver calculates the proper
+
+
+
+
+
+
+Where
+
+Checking the wireless link+
+
+Due to the changing environment conditions that normally affect the wireless systems, such as rain, interferences, obstacles, reflections, etc., measuring the wireless medium and links quality is important. +
+
+Checking the wireless medium should be done in three stages: before deploying your network, at deployment phase and later at network runtime, to ensure that the nodes create and select the best available routes. +
+
+
+
+Link Quality Estimation
+
+
+Link Quality Estimation is an integral part of assuring reliability in wireless networks. Various link estimation metrics have been proposed to effectively measure the quality of wireless links. +
+
+
+
+
+Figure 51. Link quality estimation process
+
+
+The ETX metric, or expected transmission count, is a measure of the quality of a path between two nodes in a wireless packet data network. ETX is the number of expected transmissions of a packet necessary for it to be received without error at its destination. This number varies from one to infinity. An ETX of one indicates a perfect transmission medium, where an ETX of infinity represents a completely non-functional link. Note that ETX is an expected transmission count for a future event, as opposed to an actual count of a past events. It is hence a real number, generally not an integer. +
+
+ETX can be used as the routing metric. Routes with a lower metric are preferred. In a route that includes multiple hops, the metric is the sum of the ETX of the individual hops. +
+
+Below we describe how to read the LQI and RSSI to have a first approximation of the link conditions. +
+
+
+
+What is RSSI?
+
+
+RSSI (Received Signal Strenght Indicator) is a generic radio receiver technology metric used internally in a wireless networking device to determine the amount of radio energy received in a given channel. The end-user will likely observe an RSSI value when measuring the signal strength of a wireless network through the use of a wireless network monitoring tool like Wireshark, Kismet or Inssider. +
+
+The image below shows how the Packet Reception Rate (PRR) dramatically decreases as the CC2420 RSSI values worsen. +
+
+
+
+
+Figure 52. Packet reception rate vs RSSI
+
+
+There is no standardized relationship of any particular physical parameter to the RSSI reading, Vendors and chipset makers provide their own accuracy, granularity, and range for the actual power (measured in mW or dBm) and the corresponding RSSI values. +
+
+There are 2 different types of RSSI readings available: +
+
+
+
+The first measurement can be read using the radio API as follows: +
+
+
+
+
+
+
+Where
+
+To read the RSSI value of a correctly decoded received packet, at the
+
+
+
+
+
+
+More information about the
+
+For the CC2420 radio frequency transceiver on the Z1 mote, the RSSI can range from 0 to -100, values close to 0 mean good links while values close to -100 are indicators of a bad link, which could be due to multiple factors such as distance, environment, obstacles, interferences, etc. +
+
+
+
+What is LQI?
+
+
+LQI (Link Quality Indicator) is a digital value often provide by Chipset vendors as an indicator of how well a signal is demodulated, in terms of the strength and quality of the received packet, thus indicating a good or bad wireless medium. +
+
+The example below shows how the Packet Reception Rate decreases as the LQI decreases. +
+
+
+
+
+Figure 53. Packet reception rate vs LQI
+
+
+To read the LQI value we use the Radio API: +
+
+
+
+
+
+
+Where
+
+The CC2420 radio used by the Z1 mote typically ranges from 110 (indicates a maximum quality frame) to 50 (typically the lowest quality frames detectable by the transceiver). +
+
+Detailed information about the CC2538 LQI calculation is found in the CC2538 user guide. +
+
+Configure the MAC layer+
+
+
+
+MAC protocols
+
+
+Medium Access Control (MAC) protocols describe the medium access adopted in a network, by establishing the rules that specify when a given node is allowed to transmit packets. +
+
+Protocols can be classified as contention-based or reservation-based protocols. +
+
+The first are based on Carrier Sensing for detecting medium activity and are prone to collisions and lower efficiency at heavy loads, but are easy to implement. The second group is efficient in terms of throughput and energy, but require precise synchronization and is less adaptable to dynamic traffic. +
+
+The medium access implementation in Contiki has 3 different layers: Framer, Radio Duty-Cycle (RDC) and Medium Access Control (MAC). +
+
+
+
+
+Figure 54. Contiki MAC stack
+
+
+The network layer can be accessed through the global variables
+
+The variables are located in
+
+MAC driver+
+
+Contiki provides two MAC drivers: CSMA and NullMAC +
+
+CSMA (Carrier-Sense Multiple Access) receives incoming packets from the RDC layer and uses the RDC layer to transmit packets. If the RDC layer or the radio layer detects that the medium is busy, the MAC layer may retransmit the packet at a later point in time. CSMA protocol keeps a list of packets sent to each of the neighbors and calculate statistics such as number of retransmissions, collisions, deferrals, etc. The medium access check is performed by the RDC driver. +
+
+NullMAC is a simple pass-through protocol. It calls the appropriate RDC functions. +
+
+As default both Z1 mote and RE-Mote uses the CSMA driver. +
+
+
+
+
+
+
+Alternatively, a user can choose NullMAC as follow: +
+
+
+
+
+
+
+RDC driver+
+
+Radio Duty-Cycle (RDC) layer handles the sleep period of nodes. This layer decides when packets will be transmitted and ensures that nodes are awake when packets are to be received. +
+
+The implementation of Contiki’s RDC protocols are available in
+
+
+
+
+
+
+RDC drivers try to keep the radio off as much as possible, periodically checking the wireless medium for radio activity. When activity is detected, the radio is kept on to check if it has to receive the packet, or it can go back to sleep. +
+
+The channel check rate is given in Hz, specifying the number of times the channel is checked per second, and the default channel check rate is 8 Hz. Channel check rates are given in powers of two and typical settings are 2, 4, 8, and 16 Hz. +
+
+
+
+
+
+
+A packet must generally be retransmitted or "strobed" until the receiver is on and receives it. This increments the power consumption of the transmitter and increases the radio traffic, but the power savings at the receiver compensates for this and there is a net overall power saving. +
+
+One alternative to optimize the RDC is to enable "phase optimization", which delays strobing until just before the receiver is expected to be awake. This however requires a good time synchronization between the transmitter and the receiver (more details in RDC Phase Optimization). To enable phase optimization change the 0 below to one. +
+
+
+
+
+
+
+Framer driver+
+
+The Framer driver is actually a set of functions to frame the data to be transmitted, and to parse the received data. The Framer implementations are located in
+
+In the RE-Mote platform the following configuration is the default: +
+
+
+
+
+
+
+Meaning that when IPv6 is used, the
+
+The
+
+The
+ IPv6 and Routing+
+
+One of Contiki’s most prominent feature is the support of IP protocols, being one of the first embedded operating systems to provide IPv6 support. +
+
+Alternatively Contiki also supports IPv4 and non-IP communication (Rime), however the remainder of this book will focus in IPv6. There is a good set of rime examples available at
+
+IPv6+
+
+The uIP is an Open Source TCP/IP stack designed to be used even with tiny 8 and 16 bit microcontrollers. It was initially developed by Adam Dunkels while at the Swedish Institute of Computer Science (SICS), licensed under a BSD style license, and further developed by a wide group of developers. +
+
+The implementation details of the uIP/uIPv6 is out of the scope of this section. The remainder of this section explains the basic configurations at the platform and application level. +
+
+To enable IPv6 the following has to be defined, either in the application’s
+
+
+
+
+
+
+
+
+
+
+
+
+
-RPL+
+
+There are several routing flavors to chose, but ultimately all do the same thing: ensure that packets arrive at the right destination. This is done in different ways depending on factors such as the routing metric (how a route is qualified as better than others), whether the routing is done dynamically or statically, etc. +
+
+In Contiki the default routing protocol is RPL. Other protocols such as Ad hoc On-Demand Distance Vector (AODV) are out of the scope of this section. +
+
+The specifics of the RPL implementation are out of the scope of this section, we merely describe the common configurations and provide a brief introduction to RPL. For more details, check the RPL implementation at
+
+
+
+What is RPL?
+
+
+RPL is an IPv6 routing protocol for low power and lossy networks designed by the IETF Routing Over Low power and Lossy network (ROLL) group, used as the de facto routing protocol in Contiki. RPL is a proactive distance vector protocol, it starts finding the routes as soon as the RPL network is initialized. +
+
+
+
+
+Figure 55. RPL in the protocol stack
+
+
+It supports three traffic patterns: +
+
+
+
+RPL builds Destination Oriented DAGs (DODAGs) rooted towards one sink (DAG ROOT) identified by a unique identifier DODAGID. The DODAGs are optimized using an Objective Function (OF) metric identified by an Objective Code Point (OCP), which indicates the dynamic constraints and the metrics such as hop count, latency, expected transmission count, parents selection, energy consumption, etc. A rank number is assigned to each node which can be used to determine its relative position and distance to the root in the DODAG. +
+
+Within a given network, there may be multiple, logically independent RPL instances. An RPL node may belong to multiple RPL instances, and may act as a router in some and as a leaf in others. A set of multiple DODAGs can be in an RPL INSTANCE and a node can be a member of multiple RPL INSTANCEs, but can belong to at most one DODAG per DAG INSTANCE. +
+
+A trickle timer mechanism regulates DODAG Information Object (DIO) message transmissions, which are used to build and maintain upwards routes of the DODAG, advertising its RPL instance, DODAG ID, RANK and DODAG version number. +
+
+A node can request DODAG information by sending DODAG Information Solicitation messages (DIS), soliciting DIO messages from its neighborhoods to update its routing information and join an instance. +
+
+Nodes have to monitor DIO messages before joining a DODAG, and then join a DODAG by selecting a parent Node from its neighbors using its advertised latency, OF and RANK. Destination Advertisement Object (DAO) messages are used to maintain downward routes by selecting the preferred parent with lower rank and sending a packet to the DAG ROOT through each of the intermediate Nodes. +
+
+RPL has two mechanisms to repair the topology of the DODAG, one to avoid looping and allow nodes to join/rejoin, and other called global repair. Global repair is initiated at the DODAG ROOT by incrementing the DODAG Version Number to create a new DODAG Version. +
+
+More information about RPL can be found in RFC6550. +
+
+Routing support is enabled as default in the Z1 mote and RE-Mote platform. To enable routing the following has to be enabled: +
+
+
+
-
As default the CC1200 driver in Contiki starts with the maximum transmission power, defined as follows: +To enable RPL add the following to your application’s
-
+
-
The minimum and maximum allowed values are set in The following is the default configuration done in the RE-Mote:
-
+
+
-
The CC1200 driver calculates the proper The
-
+
-
+Where The Expected Transmissions metric (ETX) measure how many tries it takes to receive an acknowledgment (ACK) of a sent packet, keeping a moving average for each neighbor, computing the sum of all ETXs to build the routes.
+
As default Contiki uses
- Checking the wireless link-
-
+Due to the changing environment conditions that normally affect the wireless systems, such as rain, interferences, obstacles, reflections, etc., measuring the wireless medium and links quality is important.
+ Set up a wireless sniffer
-
-Checking the wireless medium should be done in three stages: before deploying your network, at deployment phase and later at network runtime, to ensure that the nodes create and select the best available routes. +A packet sniffer is a must-have tool for any wireless network application, it allows to see what you are transmitting over the air, verifying both that the transmissions are taking place, the frames/packets are properly formatted, and that the communication is happening on a given channel.
-
- Link Quality Estimation
-
Link Quality Estimation is an integral part of assuring reliability in wireless networks. Various link estimation metrics have been proposed to effectively measure the quality of wireless links. +There are commercial options available, such as the Texas Instruments SmartRF packet Sniffer, which can be used with a CC2531 USB dongle to capture packets like the one below.
-
+
-Figure 42. Link quality estimation process
+Figure 56. Sniffer packet capture
-
The ETX metric, or expected transmission count, is a measure of the quality of a path between two nodes in a wireless packet data network. ETX is the number of expected transmissions of a packet necessary for it to be received without error at its destination. This number varies from one to infinity. An ETX of one indicates a perfect transmission medium, where an ETX of infinity represents a completely non-functional link. Note that ETX is an expected transmission count for a future event, as opposed to an actual count of a past events. It is hence a real number, generally not an integer. +We will use for this exercise the SenSniff application, paired with a RE-Mote and Wireshark (already installed in instant Contiki). This setup will allow us to understand how the wireless communication is done in Contiki.
-
+ETX can be used as the routing metric. Routes with a lower metric are preferred. In a route that includes multiple hops, the metric is the sum of the ETX of the individual hops. +To program the RE-Mote or a Z1 as a packet Sniffer:
+
+
-
-Below we describe how to read the LQI and RSSI to have a first approximation of the link conditions. +Compile and program:
+
-
+What is RSSI?
+
+
-
+RSSI (Received Signal Strenght Indicator) is a generic radio receiver technology metric used internally in a wireless networking device to determine the amount of radio energy received in a given channel. The end-user will likely observe an RSSI value when measuring the signal strength of a wireless network through the use of a wireless network monitoring tool like Wireshark, Kismet or Inssider. +Or +
+
+
+
-
-The image below shows how the Packet Reception Rate (PRR) dramatically decreases as the CC2420 RSSI values worsen. +Note this application has its own
+
+
+The
-
+
-
Figure 43. Packet rejection rate versus received signal strenght indicator
-
+There is no standardized relationship of any particular physical parameter to the RSSI reading, Vendors and chipset makers provide their own accuracy, granularity, and range for the actual power (measured in mW or dBm) and the corresponding RSSI values. +For the Z1 mote launch the sensniff application with the following command:
+
+
-
+There are 2 different types of RSSI readings available: +For the RE-Mote: +
+
+
-
-
-
The first measurement can be read using the radio API as follows: +Sensniff will read data from the mote over the serial port, dissect the frames and pipe to
-
+
-
+Where Which is not worrisome, it only means that the other pipe endpoint is not connected. You can also save the sniffed frames for later opening with wireshark, adding the following argument to the above command
+
+
+
Open another terminal and launch wireshark with the following command, which will add the pipe as a capture interface:
-
+
-
+More information about the Select the
+
+
+
+Figure 57. Capture options
-
-For the CC2420 radio frequency transceiver on the Z1 mote, the RSSI can range from 0 to -100, values close to 0 mean good links while values close to -100 are indicators of a bad link, which could be due to multiple factors such as distance, environment, obstacles, interferences, etc. +Make sure that the pipe is configured to capture packets in promiscuous mode, if needed you can increase the buffer size, but 1 MB is normally enough.
+
-
What is LQI?
-
-
+LQI (Link Quality Indicator) is a digital value often provide by Chipset vendors as an indicator of how well a signal is demodulated, in terms of the strength and quality of the received packet, thus indicating a good or bad wireless medium. + +Figure 58. Interface settings
-
The example below shows how the Packet Reception Rate decreases as the LQI decreases. +Now the captured frames should start to appear on screen.
-
+
-Figure 44. Packet rejection rate versus link quality indicator
+Figure 59. Captured frames
+
+You can add specific filters to limit the frames being shown on screen, for this example click at the
+
+
+Figure 60. Wireshark filters
-
To read the LQI value we use the Radio API: +When closing the Sensniff python application, a session information is provided reporting the following statistics:
-
+
-
-Where Now that we have a sniffer configured and ready to use, let’s create a first wireless application and start sniffing packets!
-
+The CC2420 radio used by the Z1 mote typically ranges from 110 (indicates a maximum quality frame) to 50 (typically the lowest quality frames detectable by the transceiver).
+
+UDP on IPv6 and the Border Router
-
-Detailed information about the CC2538 LQI calculation is found in the CC2538 user guide. -Now that we have covered the mote configurations and the MAC and routing layers, let us set up a UDP network.
- Configure the MAC layer
-
MAC protocols
+What is UDP?
-
Medium Access Control (MAC) protocols describe the medium access adopted in a network, by establishing the rules that specify when a given node is allowed to transmit packets. +UDP (User Datagram Protocol) is a communications protocol that offers a limited amount of services for messages exchanged among devices in a network that uses the Internet Protocol (IP).
-
Protocols can be classified as contention-based or reservation-based protocols. +UDP is an alternative to the Transmission Control Protocol (TCP) and, together with IP, is sometimes referred to as UDP/IP. Like the Transmission Control Protocol, UDP uses the Internet Protocol to actually get a data unit (called a datagram) from one computer to another.
-
+The first are based on Carrier Sensing for detecting medium activity and are prone to collisions and lower efficiency at heavy loads, but are easy to implement. The second group is efficient in terms of throughput and energy, but require precise synchronization and is less adaptable to dynamic traffic. +Unlike TCP, UDP does not provide message fragmentation and reassembling at the other end, this means that the application must be able to make sure that the entire message has arrived and is in the right order. +
+
Network applications that want to save processing time because they have very small data units to exchange (and therefore very little message reassembling to do) may prefer UDP to TCP
-
-The medium access implementation in Contiki has 3 different layers: Framer, Radio Duty-Cycle (RDC) and Medium Access Control (MAC). +The UDP implementation is Contiki resides in
+
+ The UDP API+
+
+We need to create a socket for the connection, this is done using the
-
+
-
Figure 45. Contiki MAC stack
-
-The network layer can be accessed through the global variables After creating the UDP socket structure, we need to register the UDP socket. This is done with the
- The variables are located in
+
+
-
-
-MAC driver-
-
Contiki provides two MAC drivers: CSMA and NullMAC
-
+CSMA (Carrier-Sense Multiple Access) receives incoming packets from the RDC layer and uses the RDC layer to transmit packets. If the RDC layer or the radio layer detects that the medium is busy, the MAC layer may retransmit the packet at a later point in time. CSMA protocol keeps a list of packets sent to each of the neighbors and calculate statistics such as number of retransmissions, collisions, deferrals, etc. The medium access check is performed by the RDC driver. +As the UDP socket has been created and registered, let us listen on a given port. The
+
+
+
-
NullMAC is a simple pass-through protocol. It calls the appropriate RDC functions. +The
-
As default both Z1 mote and RE-Mote uses the CSMA driver. +A UDP socket can be connected to a remote port, but not to a remote IP address, by providing a
-
+
-
Alternatively, a user can choose NullMAC as follow: +To send data over a connected UDP socket it must have been connected to a remote address and port with
-
-
-
-
RDC driver-
-
Radio Duty-Cycle (RDC) layer handles the sleep period of nodes. This layer decides when packets will be transmitted and ensures that nodes are awake when packets are to be received.
-
The implementation of Contiki’s RDC protocols are available in To send data over a UDP socket without being connected we use the function
-
+
-
-
RDC drivers try to keep the radio off as much as possible, periodically checking the wireless medium for radio activity. When activity is detected, the radio is kept on to check if it has to receive the packet, or it can go back to sleep.
-
The channel check rate is given in Hz, specifying the number of times the channel is checked per second, and the default channel check rate is 8 Hz. Channel check rates are given in powers of two and typical settings are 2, 4, 8, and 16 Hz. +To close a UDP socket previously registered with
-
+
-
-
A packet must generally be retransmitted or "strobed" until the receiver is on and receives it. This increments the power consumption of the transmitter and increases the radio traffic, but the power savings at the receiver compensates for this and there is a net overall power saving.
-
One alternative to optimize the RDC is to enable "phase optimization", which delays strobing until just before the receiver is expected to be awake. This however requires a good time synchronization between the transmitter and the receiver (more details in RDC Phase Optimization). To enable phase optimization change the 0 below to one. +Each UDP socket has a callback function that is registered as part of the call to
-
+
+
Alternatively there is another UDP library called
- Framer driver+UDP Link-Local multicast example
-
The Framer driver is actually a set of functions to frame the data to be transmitted, and to parse the received data. The Framer implementations are located in This is the first example of the
-
-In the RE-Mote platform the following configuration is the default: -
-
+
-
+
+The
+
-
Meaning that when IPv6 is used, the We will also learn how to use the
-
The You will need at least two Zolertia motes, the
-
+The Flash the two Zolertia devices as:
+
+
-
- IPv6 and Routing-
-
+One of Contiki’s most prominent feature is the support of IP protocols, being one of the first embedded operating systems to provide IPv6 support.
+
+
+
The
-
+
-
- RPL
-
-There are several routing flavors to chose, but ultimately all do the same thing: ensure that packets arrive at the right destination. This is done in different ways depending on factors such as the routing metric (how a route is qualified as better than others), whether the routing is done dynamically or statically, etc. +As probably noticed, the default values for the 2.4GHz band channel is 26, not 15… why is now different? let’s take a closer look at the code:
- In Contiki the default routing protocol is RPL. Other protocols such as Ad hoc On-Demand Distance Vector (AODV) are out of the scope of this section. +
+
+
-
-
-The specifics of the RPL implementation are out of the scope of this section, we merely describe the common configurations and provide a brief introduction to RPL. For more details, check the RPL implementation at
-
- What is RPL?
-
-RPL is an IPv6 routing protocol for low power and lossy networks designed by the IETF Routing Over Low power and Lossy network (ROLL) group, used as the de facto routing protocol in Contiki. RPL is a proactive distance vector protocol, it starts finding the routes as soon as the RPL network is initialized. +The
+
-
+
-
Figure 46. RPL in the protocol stack
-
-It supports three traffic patterns: -
-
+
But where are the
+
-
-A trickle timer mechanism regulates DODAG Information Object (DIO) message transmissions, which are used to build and maintain upwards routes of the DODAG, advertising its RPL instance, DODAG ID, RANK and DODAG version number. +The
- A node can request DODAG information by sending DODAG Information Solicitation messages (DIS), soliciting DIO messages from its neighborhoods to update its routing information and join an instance. +
+
+
-
-
Nodes have to monitor DIO messages before joining a DODAG, and then join a DODAG by selecting a parent Node from its neighbors using its advertised latency, OF and RANK. Destination Advertisement Object (DAO) messages are used to maintain downward routes by selecting the preferred parent with lower rank and sending a packet to the DAG ROOT through each of the intermediate Nodes.
-
RPL has two mechanisms to repair the topology of the DODAG, one to avoid looping and allow nodes to join/rejoin, and other called global repair. Global repair is initiated at the DODAG ROOT by incrementing the DODAG Version Number to create a new DODAG Version. +Notice the
-
+More information about RPL can be found in RFC6550. +The
+
+
+
+
Now we know we are disabling Radio-Duty cycling and using
-
Routing support is enabled as default in the Z1 mote and RE-Mote platform. To enable routing the following has to be enabled: +In the same
-
+
+
+
-
To enable RPL add the following to your application’s Let’s go back to the
-
+
-
The following is the default configuration done in the RE-Mote: +First we have configured an UDP connection, passing as an argument the
-
+
-
The As probably you have already noticed, we are sending the on-board sensor readings via wireless to the all-nodes multicast address:
-
+
-
-The Expected Transmissions metric (ETX) measure how many tries it takes to receive an acknowledgment (ACK) of a sent packet, keeping a moving average for each neighbor, computing the sum of all ETXs to build the routes. +The
- As default Contiki uses
+
-
+
- Set up a sniffer
-
A packet sniffer is a must-have tool for any wireless network application, it allows to see what you are transmitting over the air, verifying both that the transmissions are taking place, the frames/packets are properly formatted, and that the communication is happening on a given channel. +Notice the
-
-There are commercial options available, such as the Texas Instruments SmartRF packet Sniffer, which can be used with a CC2531 USB dongle to capture packets like the one below. -
-
-
-
-Figure 47. Sniffer packet capture
+Program at least two
-
+We will use for this exercise the SenSniff application, paired with a RE-Mote and Wireshark (already installed in instant Contiki). This setup will allow us to understand how the wireless communication is done in Contiki. +You should be receiving messages from the other devices! Write down the node ID of other motes. This will be useful later.
+
-
Compile and program: +To change the sending interval you can also modify the values at:
-
+
+
+
+ The Border Router
-
-Open a new terminal, and clone the sensniff project in your home folder: +The border router or edge router is typically a device sitting at the edge of our network, which allow us to talk to outside networks using its built-in network interfaces, such as WiFi, Ethernet, Serial, etc.
+
-
+
+
Figure 61. The border router
-
-Then launch the sensniff application with the following command: -
-
-
+
+In Contiki the current and most used border router application implements a serial-based interface called SLIP, it allows to connect a given mote to a host using scripts like
+
A simplification of
-
Sensniff will read data from the mote over the serial port, dissect the frames and pipe to The border router application is located at
-
+
-
-Which is not worrisome, it only means that the other pipe endpoint is not connected. You can also save the sniffed frames for later opening with wireshark, adding the following argument to the above command The snippet above bootstraps until a valid prefix has been given. Once the prefix has been assigned, the node will set the prefix and convert itself in the root node (DODAG).
-
+
By default the border-router applications includes a built-in web server, displaying information about the network, such as the immediate neighbors (1-hop away) and the known routes to nodes in their networks. To enable the web server, the
-
Open another terminal and launch wireshark with the following command, which will add the pipe as a capture interface: +The following assumes to use a RE-Mote platform, but the Z1 mote can be used as well.
-
+
-
-Select the To compile, flash the mote and connect the border router to your host; run:
+
-
+
-
Figure 48. Capture options
-
-Make sure that the pipe is configured to capture packets in promiscuous mode, if needed you can increase the buffer size, but 1 MB is normally enough. +By default it will try to connect to a mote at port
+
-
+
-
Figure 49. Interface settings
-
-Now the captured frames should start to appear on screen. +Or if you want to specify a different USB port:
+
-
+
-
Figure 50. Captured frames
-
-You can add specific filters to limit the frames being shown on screen, for this example click at the You can also compile and run the
+
-
+
-
Figure 51. Wireshark filters
-
When closing the Sensniff python application, a session information is provided reporting the following statistics: +And to run with specific arguments, i.e. connect to a specific serial port, name your tunnel connection with a specific name, or proxify to a given address and port, use the following:
-
+
+
-
+
+Run
- The Border Router
-
-The border router or edge router is typically a device sitting at the edge of our network, which allow us to talk to outside networks using its built-in network interfaces, such as WiFi, Ethernet, Serial, etc. +To enable IPv6 forwarding you need to run:
-
-
+
+
-
Figure 52. The border router
+
+
To make this change permanent, edit the
-
+
+
+In Contiki the current and most used border router application implements a serial-based interface called SLIP, it allows to connect a given mote to a host using scripts like
-
The border router application is located at The output of the
-
-
The snippet above bootstraps until a valid prefix has been given. Once the prefix has been assigned, the node will set the prefix and convert itself in the root node (DODAG). +This will create a
-
+Normally it is preferable to configure the border router as a non-sleeping device, so that the radio receiver is always on. You can configure the border router settings using the Note the Border Router uses Stateless Auto Configuration SLAC to create its IPv6 address, by using the IPv6 prefix given to the
+
-
- Hands on: installing the border router
-
The following assumes to use a RE-Mote platform, but the Z1 mote can be used as well. +We can make a
-
+
-
To compile, flash the mote and connect the border router to your host; run: +Furthermore, we can open a web browser and enter the Border Router’s IPv6 address, in this example:
-
+
-
-By default it will try to connect to a mote at port
+
-
+
+
Figure 62. Border Router web service
-
-You can also compile and run the The built-in web service will show the Border Router’s neighbors list and its routes. This is quite handy to check if the devices in our deployment have joined our Border Router’s network, retrieve their IPv6 addresses and even make a
+
-
+
+Note we have been used a local IPv6 prefix, routable only in our network for testing purposes. It is also possible to assign the
-
+
+
Figure 63. Border Router web service with IPv6 global address
-
-And to run with specific arguments, i.e. connect to a specific serial port, name your tunnel connection with a specific name, or proxify to a given address and port, use the following: +Using a public and globally reachable IPv6 prefix will make our Border Router, and subsequently all devices inside our IPv6/6LoWPAN network accessible over Internet:
+
-
-
+
+
Figure 64. IPv6 online ping6
-
Run This means our 6LoWPAN network of constrained devices is reachable from anywhere in the world, and viceversa.
+
-If you launch the sniffer you will be able to see how the Border Router starts sending the RPL control messages, such as
- UDP and TCP basics+
+
+
- What is UDP?
-
-
-UDP (User Datagram Protocol) is a communications protocol that offers a limited amount of services for messages exchanged among devices in a network that uses the Internet Protocol (IP). +
-
-UDP is an alternative to the Transmission Control Protocol (TCP) and, together with IP, is sometimes referred to as UDP/IP. Like the Transmission Control Protocol, UDP uses the Internet Protocol to actually get a data unit (called a datagram) from one computer to another. +Figure 65. Wireshark capture of a node joining a RPL network
-
+Unlike TCP, UDP does not provide message fragmentation and reassembling at the other end, this means that the application must be able to make sure that the entire message has arrived and is in the right order.
+ Hands on: connecting an IPv6 UDP network to our host
-
+Network applications that want to save processing time because they have very small data units to exchange (and therefore very little message reassembling to do) may prefer UDP to TCP +In the
+
+The example shows how to send sensor information to an UDP server running on the host network, even on a different network over Internet.
+
The UDP server is implemented in a python script, it allows to forward data also to other services.
-
-The UDP implementation is Contiki resides in The UDP server will send data to the MQTT broker of the Eclipse IoT foundation (at
- The UDP API
-
We need to create a socket for the connection, this is done using the Depending on the device the UDP client runs (
-
+
-
-After creating the UDP socket structure, we need to register the UDP socket. This is done with the
-
-
+
+This example is the easiest way if you want to send data to a service or server outside your network, and you don’t have an IPv6 network on your own. The Border Router as seen before can use a local
+
This example requires at least two Zolertia devices: A Border Router and an UDP client. You can use alternatively
-
As the UDP socket has been created and registered, let us listen on a given port. The You will also need to install the following python library if using the MQTT forwarder:
-
+
-
The You will need at least two Zolertia motes, the
-
-A UDP socket can be connected to a remote port, but not to a remote IP address, by providing a The application (on the networking perspective) will be similar to:
+
-
+
+
Figure 66. UDP client and server network architecture
-
-To send data over a connected UDP socket it must have been connected to a remote address and port with And from the application service perspective the MQTT forwarder application will look like:
+
-
+
+
Figure 67. UDP client and server MQTT application
-
-To send data over a UDP socket without being connected we use the function And for the IFTTT example, we can implement a simple use case related to preventive maintenance: if the battery is close to deplete, or the device has been tampered (sensed by the accelerometer), or the radio link is close to fade (due to an unexpected obstacle), then schedule a calendar task to the maintenance crew.
+
-
-
+
+
Figure 68. UDP client and server IFTTT application
-
-To close a UDP socket previously registered with
-
-
+
-Let’s start!
-
Each UDP socket has a callback function that is registered as part of the call to In the
-
-
-
-
Alternatively there is another UDP library called
- Hands on: UDP example
-
The objective of this example is to grasp the concepts shown in the preceding sections. We will create a UDP broadcast application using the As we are assuming we are going to use the
-
There is a UDP broadcast example which uses RPL at: +To verify that we have set the address correctly:
-
+
-
Open the The
-
+
-
-
The above adds the IPv6 stack and RPL routing protocol to our application.
-
The Printing at boot something similar to:
-
+
-
-This is the main uIP library. +Remember addresses starting with
-
-
+
+
+As we know the prefix given to the Border Router is
+
You can verify this later by checking the Border Router’s web service as done in the previous section.
-
This structure allows storing the UDP connection information and mapped callback in which to process any received message. It is initialized in the following call: +The UDP connection is created in the following block:
-
+
-
This passes to the simple-udp application the ports from/to in charge of handling the broadcasts, and the callback function to handle received broadcasts. We pass the NULL parameter as the destination address to allow packets from any address. +As you have probably noticed, we are using the same
-
The receiver callback function is shown below: +Upon receiving a message (from the UDP server for example) the
-
+
-
This application first sets a timer and when the timer expires triggers a randomly generated new timer interval (between 1 and the sending interval) to avoid flooding the network. Then it sets the IP address to the link local all-nodes multicast address as follows: +And as done in the previous example the devices will send data from its on-board sensors using the
-
+
-
-And then use the
-
-
+
-The sensors information is sent periodically as configured by the
-
To extend the available address information, there is a library which allows to print the IPv6 addresses in a friendlier way, add this to the top of the file: +The code snippet related to the payload sent to the UDP server address is shown next:
-
+
-
So we can now print the multicast address, add this before the Compile and program the mote:
-
+
-
Now let’s modify our receiver callback and print more information about the incoming message, replace the existing receiver code with the following: +The output should be something similar to:
-
+
-
-Before uploading your code, override the default target by writing in the terminal: -
-
-
+
-Remember that you can also compile for the Z1 platform.
-
Remember you can also use the Z1 mote as target. +Remember to check the network and connection by using
-
-Now clean any previous compiled code, compile, upload your code, restart the mote and print the serial output to screen (all in one command!): +Once you found the node’s global IPv6 assigned address, make a
+
-
+
+
Figure 69. ICMPv6 ping messages
-
-You will see the following result: -
-
-
-
-
+With the Sniffer you are able to see the wireless messages within the 6LoWPAN network, but you can also capture in Wireshark the network traffic through the
-
-
-To change the sending interval you can also modify the values at: -
-
+
-
-
-There is a Wireshark capture saved as an example for you to take a look in
-
+Hands on: connecting an IPv6 UDP network to our host+Running the UDP server MQTT forwarder
-
In the To execute the
-
+
-
To verify that we have set the address correctly let’s print the server address, in the This is the expected output when running and receiving a UDP packet from a
-
+
-
The UDP connection is created in the following block: +If we run the
-
+
+
-
The script content is below: +If you want to change the default MQTT topic, change the following in
-
+
-
+To execute the If you have an android mobile phone you can download the
+
+
+
+Figure 70. MyMQTT android app
-
+
+
+ Running the UDP server IFTTT forwarder
-
-This is the expected output when running and receiving a UDP packet: +Create an IFTTT account and subscribe to the Maker Channel:
+
+
-
+
+
Figure 71. IFTTT Maker channel
-
-The Server then echoes back the message to the UDP client to the given And copy your
+
+
-
+
+
+Figure 72. IFTTT Maker channel configuration values
+
Then create the Recipe you want, for example to automatically create a calendar entry to the maintenance crew if the battery level is critical or the temperature of the room is too high, send an email whenever someones pushes the button, etc… you have plenty of channels to choose!
-
+
-Figure 53. Z1 mote talking to the PC host
+Figure 73. IFTTT example recipe
+
-
+In the
+ TCP on IPv6What is TCP?
@@ -7853,8 +9695,8 @@ What is T
The TCP implementation is Contiki resides in
- Hands on: TCP example-
-
+Now let us put to practice the TCP API described before and browse a TCP application. The
+ Hands on: TCP example
-
-The
-
-
+
-Now let us put to practice the TCP API described before and browse a TCP application. The Then let us open the
|
-
1 | +Initial configuration values, as described earlier | +
2 | +Creates the client ID, publish and subscribe topics. The initial state STATE_INIT is set and the publish_periodic_timer event is scheduled |
+
3 | +Handles the publish_periodic_timer , this is where the application actually starts |
+
The application example itself can be understood as a finite state machine, although it seems complicated it is actually very straightforward.
+Basically the state_machine(…)
function sequentially handles the mqtt registration, configuration and connection to the Broker, then subscribe to the required topic. The code snippet below simplifies the state machine implementation to the basic taks done in each state:
static void
+state_machine(void)
+{
+ switch(state) {
+ case STATE_INIT: (1)
+ mqtt_register(&conn, &mqtt_demo_process, client_id, mqtt_event,
+ MAX_TCP_SEGMENT_SIZE);
+ state = STATE_REGISTERED;
- uip_icmp6_echo_reply_callback_add(&echo_reply_notification, (3)
- echo_reply_handler);
- etimer_set(&echo_request_timer, conf.def_rt_ping_interval); (4)
+ case STATE_REGISTERED: (2)
+ if(uip_ds6_get_global(ADDR_PREFERRED) != NULL) {
+ /* Registered and with a public IP. Connect */
+ connect_to_broker();
+ }
+ etimer_set(&publish_periodic_timer, NET_CONNECT_PERIODIC);
+ return;
+ break;
- while(1) {
+ case STATE_CONNECTING: (3)
+ break;
- PROCESS_YIELD();
+ case STATE_CONNECTED: (4)
+ /* Notice there's no "break" here, it will continue to subscribe */
- if(ev == sensors_event && data == PUBLISH_TRIGGER) { (5)
- if(state == STATE_ERROR) {
- connect_attempt = 1;
- state = STATE_REGISTERED;
+ case STATE_PUBLISHING: (5)
+ if(mqtt_ready(&conn) && conn.out_buffer_sent) {
+ /* Connected. Publish */
+ if(state == STATE_CONNECTED) {
+ subscribe();
+ state = STATE_PUBLISHING;
+ } else {
+ publish();
}
+ etimer_set(&publish_periodic_timer, conf.pub_interval);
+ return;
}
+ break;
- if((ev == PROCESS_EVENT_TIMER && data == &publish_periodic_timer) ||
- ev == PROCESS_EVENT_POLL ||
- (ev == sensors_event && data == PUBLISH_TRIGGER)) { (6)
- state_machine();
+ case STATE_DISCONNECTED: (6)
+ if(connect_attempt < RECONNECT_ATTEMPTS ||
+ RECONNECT_ATTEMPTS == RETRY_FOREVER) {
+ mqtt_disconnect(&conn);
+ etimer_set(&publish_periodic_timer, interval);
+ state = STATE_REGISTERED;
+ return;
}
+ break;
+ }
- if(ev == PROCESS_EVENT_TIMER && data == &echo_request_timer) { (7)
- ping_parent();
- etimer_set(&echo_request_timer, conf.def_rt_ping_interval);
- }
+ /* If we didn't return so far, reschedule ourselves */
+ etimer_set(&publish_periodic_timer, STATE_MACHINE_PERIODIC);
+}
+1 | +Entry point, register the mqtt connection and move to the STATE_REGISTERED event |
+
2 | +Attempts to connect to the broker. If the node has not joined the network (doesn’t have a valid IPv6 global address) it retries later. If the node has a valid address, then calls the mqtt_connect function and sets the state to STATE_CONNECTING , then sets the publish_periodic_timer with a faster pace |
+
3 | +This event just informs the user about the connection attempts. When the MQTT connection to the broker has been made, the MQTT_EVENT_CONNECTED is triggered at the mqtt_event callback handler |
+
4 | +As we are connected now, proceed and publish. | +
5 | +Checks if the MQTT connection is OK in mqtt_ready , then subscribe and publish |
+
6 | +Handles any disconnection event triggered from MQTT_EVENT_DISCONNECTED |
+
The mqtt_event
function is a callback handler in which we a notification from the mqtt library whenever an event happens. The mqtt_event
updates the status of the application so the state_machine
know what to do next.
static void
+mqtt_event(struct mqtt_connection *m, mqtt_event_t event, void *data)
+{
+ switch(event) {
+ case MQTT_EVENT_CONNECTED: { (1)
+ state = STATE_CONNECTED;
+ break;
+ }
+ case MQTT_EVENT_DISCONNECTED: { (2)
+ state = STATE_DISCONNECTED;
+ process_poll(&mqtt_demo_process);
+ break;
+ }
+ case MQTT_EVENT_PUBLISH: { (3)
+ pub_handler(msg_ptr->topic, strlen(msg_ptr->topic), msg_ptr->payload_chunk,
+ msg_ptr->payload_length);
+ break;
+ }
+ case MQTT_EVENT_SUBACK: { (4)
+ break;
+ }
+ case MQTT_EVENT_UNSUBACK: { (5)
+ break;
+ }
+ case MQTT_EVENT_PUBACK: { (6)
+ break;
}
-
- PROCESS_END();
}
STATE_INIT
is set and the publish_periodic_timer
event is scheduledmqtt_demo_process
process and the state_machine
function is invoked.STATE_ERROR
by pressing the user buttonpublish_periodic_timer
and button events, this is where the application actually startsWhen the construct_client_id
is first called with the STATE_INIT
, the state_machine
is called. A brief walkthrough of the state machine is shown next. Notice how in some events (like STATE_INIT
) immediately the driver jumps to the next events as there is not a break
statement.
When receiving an event from a topic we are subscribed to, the MQTT_EVENT_PUBLISH
event is triggered and the pub_handler
is called. The example allows to turn the red LED on and off alternatively.
The default topic the example subscribe to is zolertia/cmd/leds
, specifically to change the LED status we would need to publish to the zolertia/cmd/leds
topic with value 1
to turn the LED on, and 0
otherwise.
static void
-state_machine(void)
+pub_handler(const char *topic, uint16_t topic_len, const uint8_t *chunk,
+ uint16_t chunk_len)
{
- switch(state) {
-
- case STATE_INIT: (1)
- /* If we have just been configured register MQTT connection */
- mqtt_register(&conn, &mqtt_demo_process, client_id, mqtt_event,
- MAX_TCP_SEGMENT_SIZE);
- state = STATE_REGISTERED;
-
- case STATE_REGISTERED: (2)
- if(uip_ds6_get_global(ADDR_PREFERRED) != NULL) {
- connect_to_broker();
- } else {
- leds_on(STATUS_LED);
- ctimer_set(&ct, NO_NET_LED_DURATION, publish_led_off, NULL);
- }
- etimer_set(&publish_periodic_timer, NET_CONNECT_PERIODIC);
+ /* If we don't like the length, ignore */
+ if(topic_len != 17 || chunk_len != 1) {
+ printf("Incorrect topic or chunk len. Ignored\n");
return;
- break;
-
- case STATE_CONNECTING: (3)
- leds_on(STATUS_LED);
- ctimer_set(&ct, CONNECTING_LED_DURATION, publish_led_off, NULL);
- /* Not connected yet. Wait */
- DBG("Connecting (%u)\n", connect_attempt);
- break;
+ }
- case STATE_CONNECTED: (4)
- /* Don't subscribe unless we are a registered device */
- if(strncasecmp(conf.org_id, QUICKSTART, strlen(conf.org_id)) == 0) {
- DBG("Using 'quickstart': Skipping subscribe\n");
- state = STATE_PUBLISHING;
+ if(strncmp(&topic[13], "leds", 4) == 0) {
+ if(chunk[0] == '1') {
+ leds_on(LEDS_RED);
+ printf("Turning LED RED on!\n");
+ } else if(chunk[0] == '0') {
+ leds_off(LEDS_RED);
+ printf("Turning LED RED off!\n");
}
+ return;
+ }
+}
+Alternatively we could subscribe to a topic zolertia/cmd/#
and handle different types of commands, we would need to parse the topic
string and handle the topic and payload (chunk
string).
The publish
function create the string data to be published. Below is a snippet of the function highlighting only the most relevant parts.
static void
+publish(void)
+{
+ int len;
+ uint16_t aux;
+ int remaining = APP_BUFFER_SIZE; (1)
+ buf_ptr = app_buffer; (2)
- case STATE_PUBLISHING: (5)
- /* If the timer expired, the connection is stable. */
- if(timer_expired(&connection_life)) {
- /*
- * Intentionally using 0 here instead of 1: We want RECONNECT_ATTEMPTS
- * attempts if we disconnect after a successful connect
- */
- connect_attempt = 0;
- }
+ len = snprintf(buf_ptr, remaining, (3)
+ "{"
+ "\"d\":{"
+ "\"myName\":\"%s\","
+ "\"Seq no\":%d,"
+ "\"Uptime (sec)\":%lu",
+ BOARD_STRING, seq_nr_value, clock_seconds());
- if(mqtt_ready(&conn) && conn.out_buffer_sent) {
- /* Connected. Publish */
- if(state == STATE_CONNECTED) {
- subscribe();
- state = STATE_PUBLISHING;
- } else {
- leds_on(STATUS_LED);
- ctimer_set(&ct, PUBLISH_LED_ON_DURATION, publish_led_off, NULL);
- publish();
- }
- etimer_set(&publish_periodic_timer, conf.pub_interval);
- return;
- } else {
- /*
- * Our publish timer fired, but some MQTT packet is already in flight
- * (either not sent at all, or sent but not fully ACKd).
- *
- * This can mean that we have lost connectivity to our broker or that
- * simply there is some network delay. In both cases, we refuse to
- * trigger a new message and we wait for TCP to either ACK the entire
- * packet after retries, or to timeout and notify us.
- */
- }
- break;
+ if(len < 0 || len >= remaining) { (4)
+ printf("Buffer too short. Have %d, need %d + \\0\n", remaining, len);
+ return;
+ }
- case STATE_DISCONNECTED: (6)
- DBG("Disconnected\n");
- if(connect_attempt < RECONNECT_ATTEMPTS ||
- RECONNECT_ATTEMPTS == RETRY_FOREVER) {
- /* Disconnect and backoff */
- clock_time_t interval;
- mqtt_disconnect(&conn);
- connect_attempt++;
+ remaining -= len; (5)
+ buf_ptr += len; (6)
- interval = connect_attempt < 3 ? RECONNECT_INTERVAL << connect_attempt :
- RECONNECT_INTERVAL << 3;
+ /* Put our Default route's string representation in a buffer */
+ char def_rt_str[64];
+ memset(def_rt_str, 0, sizeof(def_rt_str));
+ ipaddr_sprintf(def_rt_str, sizeof(def_rt_str), uip_ds6_defrt_choose());
+ len = snprintf(buf_ptr, remaining, ",\"Def Route\":\"%s\"",
+ def_rt_str); (7)
- DBG("Disconnected. Attempt %u in %lu ticks\n", connect_attempt, interval);
+ aux = cc2538_temp_sensor.value(CC2538_SENSORS_VALUE_TYPE_CONVERTED);
+ len = snprintf(buf_ptr, remaining, ",\"Core Temp\":\"%u.%02u\"",
+ aux / 1000, aux % 1000); (8)
- etimer_set(&publish_periodic_timer, interval);
+ aux = adc_zoul.value(ZOUL_SENSORS_ADC1);
+ len = snprintf(buf_ptr, remaining, ",\"ADC1\":\"%u\"", aux);
- state = STATE_REGISTERED;
- return;
- } else {
- /* Max reconnect attempts reached. Enter error state */
- state = STATE_ERROR;
- DBG("Aborting connection after %u attempts\n", connect_attempt - 1);
- }
- break;
+ aux = adc_zoul.value(ZOUL_SENSORS_ADC3);
+ len = snprintf(buf_ptr, remaining, ",\"ADC3\":\"%u\"", aux);
- case STATE_CONFIG_ERROR: (7)
- /* Idle away. The only way out is a new config */
- printf("Bad configuration.\n");
- return;
+ len = snprintf(buf_ptr, remaining, "}}"); (9)
- case STATE_ERROR: (8)
- default:
- leds_on(STATUS_LED);
- /*
- * 'default' should never happen.
- *
- * If we enter here it's because of some error. Stop timers. The only thing
- * that can bring us out is a new config event
- */
- printf("Default case: State=0x%02x\n", state);
- return;
- }
+ mqtt_publish(&conn, NULL, pub_topic, (uint8_t *)app_buffer,
+ strlen(app_buffer), MQTT_QOS_LEVEL_0, MQTT_RETAIN_OFF);
- /* If we didn't return so far, reschedule ourselves */
- etimer_set(&publish_periodic_timer, STATE_MACHINE_PERIODIC);
+ printf("APP - Publish to %s: %s\n", pub_topic, app_buffer);
}
STATE_REGISTERED
eventremaining
variable to store the remaining bytes in the buffer in which we are storing the string to be publishedmqtt_connect
function and sets the state to STATE_CONNECTING
, then sets the publish_periodic_timer
with a faster paceMQTT_EVENT_CONNECTED
is triggered at the mqtt_event
callback handlersnprintf
creates a formatted string at the position pointed by the buf_otr
pointer. The JSON string follows IBMM quickstart format.STATE_PUBLISHING
and just go aheadsnprintf
is negative, or exceeds the remaining bytes in our buffer, it means the buffer is full. If this is the case for you, either increase the APPP_BUFFER_SIZE
value, or decrease the string content and lenghtmqtt_ready
, then subscribe and publishMQTT_EVENT_DISCONNECTED
buf_ptr
pointer to the end of the newly created string, so the next string to be written starts at the end of the previous one.RE-Mote
sensors are shown but for the Z1
mote is also done in the applicationThe publish
function create the string data to be published. Below is a snippet of the function highlighting only the most relevant parts. The example publishes periodically the following:
The mqtt_publish
updates the MQTT broker with the new values, publishing to the specified pub_topic
. The default topic is zolertia/evt/status
as done in the construct_pub_topic
function.
Device name.
-An incremental sequence number.
-Device uptime (in seconds).
-On-chip temperature.
-Battery value.
-Now compile and upload the example:
static void
-publish(void)
-{
- len = snprintf(buf_ptr, remaining,
- "{"
- "\"d\":{"
- "\"myName\":\"%s\","
- "\"Seq #\":%d,"
- "\"Uptime (sec)\":%lu",
- BOARD_STRING, seq_nr_value, clock_seconds());
-
- len = snprintf(buf_ptr, remaining, ",\"Def Route\":\"%s\",\"RSSI (dBm)\":%d",
- def_rt_str, def_rt_rssi);
-
- len = snprintf(buf_ptr, remaining, ",\"On-Chip Temp (mC)\":%d",
- cc2538_temp_sensor.value(CC2538_SENSORS_VALUE_TYPE_CONVERTED));
-
- len = snprintf(buf_ptr, remaining, ",\"VDD3 (mV)\":%d",
- vdd3_sensor.value(CC2538_SENSORS_VALUE_TYPE_CONVERTED));
-
- mqtt_publish(&conn, NULL, pub_topic, (uint8_t *)app_buffer,
- strlen(app_buffer), MQTT_QOS_LEVEL_0, MQTT_RETAIN_OFF);
-
- DBG("APP - Publish!\n");
-}
+make mqtt-example.upload
+Compile and program a Border Router device as shown in the previous chapter. Verify the Border Router is online by making a ping6
request to it, then browse the Border Router’s web service and also ping6
the MQTT node.
+ + | +
-
+The The next screenshoots shows images of both Z1 and RE-Mote MQTT nodes, images and snippets are shown indistinctively. + |
+
When receiving an event from a topic we are subscribed to, the MQTT_EVENT_PUBLISH
event is triggered and the pub_handler
is called. The example allows to turn the red LED on and off alternatively.
The green LED will blink at a faster pace while the MQTT node is trying to connect to the MQTT broker, then when connected it will stop blinking.
The default topic the example subscribe to is iot-2/cmd/+/fmt/json
, specifically to change the LED status we would need to publish to the iot-2/cmd/leds/fmt/json
topic with value 1
to turn the LED on, and 0
otherwise.
Whenever the node publishes to the MQTT broker it will turn on the green LED, and off when it finishes publishing.
static void
-pub_handler(const char *topic, uint16_t topic_len, const uint8_t *chunk,
- uint16_t chunk_len)
-{
- if(strncmp(&topic[10], "leds", 4) == 0) {
- if(chunk[0] == '1') {
- leds_on(LEDS_RED);
- } else if(chunk[0] == '0') {
- leds_off(LEDS_RED);
- }
- return;
- }
-}
+Rime started with address 193.12.0.0.0.0.19.208
+MAC c1:0c:00:00:00:00:13:d0 Ref ID: 43981
+Contiki-3.x-2577-gea0738b started. Node id is set to 5072.
+CSMA nullrdc, channel check rate 128 Hz, radio channel 26
+Tentative link-local IPv6 address fe80:0000:0000:0000:c30c:0000:0000:13d0
+Starting 'MQTT Demo'
+MQTT Demo Process
+Subscription topic zolertia/cmd/leds
+Init
+Registered. Connect attempt 1
+APP - MQTT Disconnect. Reason 3
+Disconnected
+Disconnected. Attempt 2 in 1024 ticks
+Registered. Connect attempt 2
+Connecting (2)
+APP - Application has a MQTT connection
+APP - Subscribing to zolertia/cmd/leds
+APP - Application is subscribed to topic successfully
+Publishing
+APP - Publish to zolertia/evt/status: {"d":{"myName":"Zolertia Z1 Node","Seq no":1,"Uptime (sec)":63,"Def Route":"fe80::212:4b00:615:ab25","Temp":"2.768","X axis":"86"}}
+There is a python script named mqtt-client.py
you could use to subscribe to the MQTT broker and topic, and receive notifications whenever the MQTT node publishes.
In case of a Z1 node publishing to mosquitto’s MQTT broker:
+$ python mqtt-client.py
+connecting to fd00::1
+Connected with result code 0
+Subscribed to zolertia/evt/status
+Subscribed to zolertia/cmd/leds
+zolertia/evt/status {"d":{"myName":"Zolertia Z1 Node","Seq no":1,"Uptime (sec)":63,"Def Route":"fe80::212:4b00:615:ab25","Temp":"27.68","X axis":"86"}}
+For the RE-Mote:
+$ python mqtt-client.py
+connecting to fd00::1
+Connected with result code 0
+Subscribed to zolertia/evt/status
+Subscribed to zolertia/cmd/leds
+zolertia/evt/status {"d":{"myName":"Zolertia RE-Mote platform","Seq #":5,"Uptime (sec)":239,"Def Route":"fe80::212:4b00:615:ab25","Core Temp":"34.523","ADC1":"2280","ADC3":"1452"}}
+As explained before, the MQTT node subscribes to the following topic:
+zolertia/cmd/led
Note the +
in the subscribed topic, this allows to have different subsets of topics, such as leds
.
Sending as payload 1
will turn on the red LED, and a 0
off.
Execute this from the command line:
+mosquitto_pub -h "test.mosquitto.org" -t "zolertia/cmd/led" -m "1" -q 0
+The above command will publish to the cmd
topic, all nodes subscribed to it will turn its red LED on.
APP - Application received a publish on topic 'zolertia/cmd/leds'. Payload size is 1 bytes. Content:
+Pub Handler: topic='zolertia/cmd/leds' (len=17), chunk_len=1
+Turning LED RED on!
+
+APP - Application received a publish on topic 'zolertia/cmd/leds'. Payload size is 1 bytes. Content:
+Pub Handler: topic='zolertia/cmd/leds' (len=17), chunk_len=1
+Turning LED RED off!
++ + | +
+
+
+Remember in Chapter 4 a MQTT Android application was shown, try to configure and command your |
+
At the present time the Ubidots example was to be merged to Contiki, however the functional example can be browsed and copied from George Oikonomou repository
+The original example and libraries were developed by George Oikonomou, available from George Oikonomou repository. At the time of writting it is not merged to Contiki, but available in the iot-workshop
branch we are currently using in the book.
The Contiki’s Ubidots Library was written by George Oikonomou.
+The Ubidots example is located at:
+examples/zolertia/tutorial/99-apps/ubidots-example
.
The Ubidots example is located at examples/ipv6/ubidots
.
Ubidots application is implemented at apps/ubidots
.
Ubidots application is implemented at apps/ubidots
.
You will need a minimum of two Zolertia devices: a Border Router and a node acting as the Ubidots client.
+This example works for both Z1
nodes and zoul
platforms like the RE-Mote
, each one will publish data from an attached SHT21 temperature and humidity sensor, shown in previous sections.
Ubidots application uses TCP sockets to connect to the host things.ubidots.com
, which has the following IPv4 and IPv6 endpoints:
To check what’s going on enable the debug print statements in the ubidots.c
file, search for #define DEBUG DEBUG_NONE
and replace with:
As default the ubidots
application uses the things.ubidots.com
remote host, however if no NAT/NAT64 service is available to resolve the host address, the IPv6 endpoint can be explicitly defined as:
First, you will have to register an account with Ubidots, create your data +source and variables and request an authentication token.
+Create an account with Ubidots and log in
+Create a single data source
+Under this data source, create two variables: One for the uptime and another for the sequence number.
+Go to your account and request a short API token.
+There are three things to configure in the example:
+The choice between IP and IPv6. If you are planning to connect to Ubidots over NAT64, you will also want to configure the Ubidots server’s IP address in a NAT64 format.
+The authentication token
+The variable IDs
+The example will build for IPv6 by default. If you have a NAT64 enabled Border Router or a similar software, open the example’s Makefile
. Change CONTIKI_WITH_IPV6=1
to WITH_IP64=1
.
In the project-conf.h
file configure the host address. If you don’t specify one, the Ubidots library will try to resolve the host name.
#define UBIDOTS_CONF_REMOTE_HOST "2607:f0d0:2101:39::2"
+/* IPv6 address of things.ubidots.com is "2607:f0d0:2101:39::2", leave
+ * commented to resolve the host name. The NAT64 address is "::ffff:3217:7c44"
+ */
+#define UBIDOTS_CONF_REMOTE_HOST "2607:f0d0:2101:39::2"
#define UBIDOTS_DEMO_CONF_UPTIME "XXXX"
-#define UBIDOTS_DEMO_CONF_SEQUENCE "XXXX"
+
The last step is to assign an Ubidot’s fixed Short Token so we don’t have to request one when it expires, get one and add this to the Makefile
, the file should look like this:
This will be the value to be defined in UBIDOTS_CONF_AUTH_TOKEN
.
As we are to post temperature and humidity values to Ubidots, create the variables and copy their Variable ID
.
DEFINES+=PROJECT_CONF_H=\"project-conf.h\"
-CONTIKI_PROJECT = ubidots-demo
-APPS = ubidots
-UBIDOTS_WITH_AUTH_TOKEN=XXXXXXXX
-ifdef UBIDOTS_WITH_AUTH_TOKEN
- DEFINES+=UBIDOTS_CONF_AUTH_TOKEN=\"$(UBIDOTS_WITH_AUTH_TOKEN)\"
-endif
-all: $(CONTIKI_PROJECT)
-CONTIKI_WITH_IPV6 = 1
-CONTIKI = ../../..
-include $(CONTIKI)/Makefile.include
+
Note that you should replace the UBIDOTS_WITH_AUTH_TOKEN
without using "" quotes.
Next in the project-conf.h
file replace accordingly:
/* User configuration */
+#define POST_PERIOD (CLOCK_SECOND * 40)
+#define VARIABLE_BUF_LEN 16
+#define UBIDOTS_CONF_AUTH_TOKEN ""
+#define VARKEY_TEMPERATURE ""
+#define VARKEY_HUMIDITY ""
+#define UBIDOTS_CONF_IN_BUFFER_SIZE 64
+Now everything should be set, let’s compile and program a Z1 mote!
+Now compile and program:
make TARGET=z1 savetarget
-make clean && make ubidots-demo.upload && make z1-reset && make login
+make ubidots-client.upload
Compile and program a Border Router device as shown in the previous chapter. Verify the Border Router is online by making a ping6
request to it, then browse the Border Router’s web service and also ping6
the ubidots-client
node.
You should see the following output:
The above shows how the ubidots-client
node connects to the Ubidots server, and publishes data.
To visualize the data in a friendlier way, Ubidots provides ready to use dashboards with different visualization options (linear plot, scatter, tables, etc).
+The values are displayed using a Multi-line chart and a Table-Values dashboard.
+