Provides a RIOT adaption of the OpenWSN network stack. More...
Provides a RIOT adaption of the OpenWSN network stack.
This implementation integrates the OpenWSN full network stack (UDP, IPv6 (6LoWPAN), RPL, 6TiSCH) into RIOT.
It can be used instead of GNRC on supported 802.15.4 radios, and compared to GNRC, provides a full 6TiSCH implementation. It does not yet support RIOT's sock API, so applications will have to be written against OpenWSN's API.
This port provides a new RIOT "board" to the OpenWSN software. In this way RIOT's hardware abstraction connects to OpenWSN's interfaces.
The simple scheduling mechanism in OpenWSN is run in a RIOT thread with second highest priority after the radio thread (THREAD_PRIORITY_MAIN - 4).
The current port of OpenWSN currently needs a root node that works along an external tool that performs routing and handles join procedure: Openvisualizer
The first thing a new mote will need to do is to find a network. On boot it will actively be listening for enhanced beacons. Once a beacon is received it will adjust its timers drift and synchronize with the network. Re-synchronization will be happening constantly to compensate for oscillator and timer drifts.
Once synchronized the node will need to join the network. OpenWSN uses CoJP constrained join protocol an the stack itself only handles Join Requests. The JRC (join registrar/coordinator, a central entity) is not running on the root node, but alongside it, in the OpenVisualizer
external tool. This functionality is now optional but can be included through the openwsn_cjoin
module.
Once joined the device has the required keys to start listening to DIS (DODAG Information Solicitation) messages and to send DIO (DODAG Information Object) requests. Once it knows about the topology of the network it is able to send packets.
OpenWSN uses source routing. This means that unless the recipient of a packet is one of the parents in the RPL tree the packet will have to go up the tree to the root node. But in OpenWSN RPL implementation the node does not know how to route, instead it is OpenVisualizer
which generates an SRH (Source Routing Header), attaches to the incoming packet and sends it down the tree.
Following, details about the implementation of selected hardware modules.
The sctimer
("single compare timer") in OpenWSN is the lowest timer abstraction which is used by the higher layer timer module opentimers
. In the end it is responsible for scheduling on the MAC layer. To enable low power energy modes, this timer usually uses the RTC (real time clock) or RTT (real time timer) module.
This port has two possible implementations or sctimer, one on top of periph_rtt and another on top of ztimer, sctimer_rtt
and sctimer_ztimer
respectively. If possible ztimer
should be preferred, being a virtual timer it will allow RIOT applications/modules to use the low level RTT timer. But ztimer (any virtual timer) has some overhead which can be costly depending on the different platforms used, specially when openserial
is required. More on this in openserial and Known Issues.
In order to get the most portable code, this implementation uses ztimer and defines a new ztimer_clock
(ZTIMER_32768
) that operates at 32768Khz to have a resolution of ~30usec/tick (same as OpenWSN).
When available ZTIMER_32768
will be built on top of periph_rtt
to get low power capabilities. If not it will be built on top of a regular timer. In either case it will be shifted up if the base frequency is lower than 32768Hz or frac if higher.
When next interrupt to schedule is already late, current time, implementations in OpenWSN directly trigger a hardware interrupt. Until able to trigger sw isr directly a callback is set 0 ticks in the future, which internally will be set to now + RTT_MIN_OFFSET
.
In order to reduce overhead this implementation uses bare RTT. It expects a RTT running at 32768Hz to have a resolution of ~30usec/tick (same as OpenWSN). If RTT_FREQUENCY
is lower than 32768Hz then a simple time-division mechanism will be used to speed up the clock. This only works if RTT_FREQUENCY
is 32768Hz/2.
When next interrupt to schedule is already late, current time, Implementations in OpenWSN directly trigger a hardware interrupt. Until able to trigger sw isr directly a callback is set RTT_MIN_OFFSET
ticks in the future.
The radio adaptation runs in its own thread with the highest priority (THREAD_PRIORITY_MAIN - 4
) and maps to RIOT's netdev API.
Hardware MAC layer features such as CSMA/CA, ACK handling and retransmissions are handled by OpenWSN, so the radio driver must support disabling AUTOACK and CSMA handling by the hardware. Frame filtering must as well be disabled.
The radio adaptation preloads the buffer so NETOPT_PRELOADING
must be supported.
OpenWSN needs to be notified when a frame reception/transmission starts and when it ends. Therefore radio drivers need to support the following netdev events:
- `NETDEV_EVENT_RX_STARTED` - `NETDEV_EVENT_TX_STARTED` - `NETDEV_EVENT_RX_COMPLETE` - `NETDEV_EVENT_TX_COMPLETE`
OpenWSN expects to recover crc information on every received frame even if it will simply drop frames with invalid crc. The stack can function correctly if radio drivers automatically drop frames with an invalid crc (i.e. the stack doesn't get notified about these frames).
In RIOT, the first configured uart device is mapped to STDIO in most cases. In OpenWSN however, the openserial
tool uses uart to feed external software running on a host computer such as Openvisualizer. To enable use of these tools, an uart adaptation is provided.
This is provided through the openwsn_serial
(openserial
) module. It will use the next available uart that is not used by STDIO (checking STDIO_UART_DEV). When multiple uart are available STDIO and openserial
can be used in parallel. If stdio_null
is used then openserial
will use STDIO_UART_DEV
, otherwise it will use the next available uart. e.g. If STDIO_UART_DEV = UART_DEV(1)
OPENWSN_UART_DEV = UART_DEV(0)
if there are uarts.
OpenWSN uart abstraction makes use of tx hardware interrupts to execute a previously registered callback after every byte is sent out. These interrupts are currently not defined in RIOT in a generic way, so instead a timer is set to fire shortly after a byte is written.
It uses ztimer
to set the timer since it's already pulled in as a dependency.
As was mentioned before any OpenWSN network will require a root node which is connected to an OpenVisualizer
instance running on a host computer. Interaction between OpenVisualizer
and the root-node is done over serial. As OpenWSN uses source routing, this means that ultimately all network traffic must go from the root node to OpenVisualizer
and back.
OpenSerial uses software flow control (XonXoff) to turn off serial activity while time critical TSCH operation are ongoing. But software flow control can cause issues since delays in the serial pipe (either because of a remote connection, buffers, etc..) can lead to bytes being transmitted when one side is not yet ready.
Serial data is transmitted as High-Level Data Link Control (HDLC) frames. Since network traffic is tunneled through the serial pipe, in order to to have a stable connection these packets must not be lost. Packets can be lost in multiple ways but at the end it reduces to bytes being overridden in the uart reception buffer.
Since uart reception is interrupted based if the uart ISR is not serviced for too long then bytes start overriding each other in the reception buffer. In an application where only OpenWSN is running then there are 3 functions/operations that run in ISR or with disabled ISR.
(a) opentimers_timer_callback
(OpenWSN timer abstraction callback) (b) sctimer_setCompare
and sctimer_readCounter
(c) ztimer_handler
(a) and (b) are closely related since opentimers_timer_callback
will itself call sctimer_setCompare
and sctimer_readCounter
. And this itself will depend on ztimer_set
and ztimer_now
or rtt_get_counter
or rtt_set_alarm
execution time.
(c) is also related to (a) (b) since ztimer_handler
will also call opentimers_timer_callback
as well as the underlying rtt
functions.
Since a 115200 baudrate means ~1byte every 10us, none of the above can take longer than that or bytes could be lost at that baudrate.
Because of the above mentioned reasons, it is preferable to use sctimer_rtt
for the root node or "border router" on an OpenWSN network, since this reduces the likeliness of packets being lost. For non root nodes, OpenSerial only provides debugging information so no special care needs to be taken.
It is also recommended that the root node should act as a border router running only the OpenWSN stack to avoid other threads/ISR disrupting serial reception.
So far, this has been successfully tested on iotlab-m3
,nucleo-f103
and samr21-xpro
, all based on at86rf23x radios.
To join a network a node must first receive EB (enhanced beacons). Once an EB is received the node will be synchronized with the network. Synchronization times are not deterministic, they depend on the following:
SLOTFRAME_LENGTH*SLOTDURATION*P_CHANNEL*P_EB
SLOTFRAME_LENGTH
in OpenWSN is 101, and this port uses 20ms as the slotOffset duration. P_EB
specifies the a probability for a node to transmit an EB. By default it's 10%, that means that on average it will take 10 tries before an EB is transmitted. P_CHANNEL
is the probability for the transmitter's and receiver channel to match. If channel hopping is disabled this means that the average worst case scenario is 101*20ms*10 ~= 20s
, so 20s for synchronization to take place. EB_PORTION
can be changed to increase the likelihood of EB to be sent. This can also be achieved by reducing SLOTFRAME_LENGTH
, but the later can have an impact on the MSF (Minimal Scheduling Function). If too few cells are available this could increase the likelihood of collisions.
On the other hand if channel hopping is enabled then the joining node picks a random channel to start listening on. The transmitter also picks a random channel to start transmitting on and then follows a channel hopping template. This would take on average ~8 tries for it to hit the correct channel for the first time, and then it would hit it every 16 hops, on average this could lead synchronization times of around 320s
.
If nodes are having trouble in staying synchronized increasing P_EB
by reducing the value of EB_PORTION
can also be done. Note that EB_PORTION
and SLOTFRAME_LENGTH
are not configurable by default so need to be overridden with CFLAGS
.
See Tune parameters in OpenWSN for more details.
Timing is essential for OpenWSN to work properly. For optimal results most parameters in board_info.h
should be measured for the specific hardware used. OpenWSN has done that for most of their boards. These values can not be taken directly from OpenWSN since they do not necessarily use the same TIMER's or clock speeds.
For more details on those parameters refer to: https://openwsn.atlassian.net/wiki/spaces/OW/pages/688251/State+Machine
Since all these parameters are HW dependent, it also means that hybrid networks (different type of underlying hardware) might desynchronize often, or not manage to keep in sync at all.
Print messages during TSCH operation should be avoided since these can disrupt TSCH timings.
The OpenWSN software provides different hooks all over the stack to toggle different LEDs as well as debug pins to examine state and scheduling of a node. Default configuration files are provided for both. The LED configuration maps to RIOTs LEDX_PIN
definitions, if available. On Nucleo boards LED0 line is shared SPI, so is not used.
The default configuration can be overwritten by setting OPENWSN_LEDS_DEFAULT
in the form of leds_config_t
. The debugpins work similarly by setting OPENWSN_DEBUGPINS_BOARD
in the form of debugpins_config_t
.
The default configuration maps to OpenWSN reference hardware openmote-b
.
The following modules are optional and can be disabled if not needed, or to lower the stack footprint.
openwsn_cjoin
: this enabled the use of Constrained Join Protocol (CoJP)openwsn_6lo_frag
: this enable 6LoWPAN fragmentationopenwsn_iee802154e_security
: enable link layer securityopenwsn_adaptive_msf
: allow the MSF algorithm to dynamically remove and allocate slotsList of some items which are helpful to explore the functionality of OpenWSN:
tests/pkg_openwsn/README.md
for more details.CFLAGS=-DIEEE802154E_SINGLE_CHANNEL=26
).sniffer_aggregator
on the IoT-LAB testbed.To test Openserial on a given platform the target make openv-serial
can be used on a BOARD flashed with tests/pkg_openwsn
(USEMODULE=openwsn_serial
) must be included as well. The following output should appear:
```
Iterations: 100 Packet length: 100 Echo timeout: 2
Test Progress:
[####################################] 100%
Pkts send: 100 Echo success: 100 Echo timeout: 0 Echo corrupted: 0 ```
The test should be considered passing if success rate is > 98%.
The following errors might be visible when using openwsn_serial
:
[OPENSERIAL] wrong CRC in input Buffer
Since a timer is set to simulate a uart transmit interrupt, it can happen that the interrupt is missed if another interrupt occurs during that time, this seems to lead to the input buffer missing a byte and so CRC fails. More details where given in the openserial
section.
[IEEE802154E] wdDataDuration overflows while at state 19 in slotOffset 0
This error can show up when the radio starts receiving (receives the SFD) and therefore triggers a NETDEV_RX_STARTED
but then no NETDEV_TX_STARTED
event follows. This happens when packets with invalid CRC are received. netdev currently silently drops these packets without notifying upper layers. But this does not affect the stack operation, so they can be ignored.
[IEEE802154E] large timeCorr.: -18 ticks (code loc. 0)
Most crystals used to clock the RTT will drift even those with a very similarly drift (10-30ppm). It's is normal then for motes adjust their timerCorr as long as it stays within the above mentioned margins and if motes are able to stay synchronized over the long run. If there aren't then maybe board_info.h parameters require tuning for the specific platform.
[JRC:ERROR] Type-error in conversion of 5N=ex
This errors happen when a node tries to rejoin the network. This error is only associated to a log print, so can be ignored.
[coap:WARNING] coapRcBadRequest(reason=OSCOAP unprotect failed: oscoapError(reason=Replay protection failed))
The join procedure uses a replay window. If a node had already joined the network and for some reason attempts to rejoin again, then the replay windows will need to expire for it's join request to be accepted.
The following errors are platform specific.
sctimer_rtt
can be used and the max. tested baudrate for openserial is of 19200 bauds.Other errors:
TSCH state machine disable occurs in IRQ context and disables IRQ during time critical sections. This can cause bytes sent over stdio to be missed.
sctimer
to trigger an ISR immediately using software interrupts.RTT_FREQUENCY
is not configurable for most platforms, implementations should be adapted to make this configurable to be able to set RTT_FREQUENCY
to 32768Hz or the closer possible value.It would be desirable to achieve an extraction of the MAC layer.
Immediate future steps:
openmote-b
)Files | |
file | board_info.h |
file | openwsn.h |
file | openwsn_board.h |
RIOT adaption-specific definition of the "uart" bsp module. | |
file | openwsn_debugpins.h |
Provides an adaption of OpenWSN debug pin handling to RIOTs handling of GPIOs. | |
file | openwsn_debugpins_params.h |
Default configuration for the OpenWSN debugpins. | |
file | openwsn_leds.h |
Provides an adaption of OpenWSN led handling to RIOTs handling of LEDs and/or GPIOs. | |
file | openwsn_leds_params.h |
Default configuration for the OpenWSN leds. | |
file | openwsn_log.h |
System logging header OpenWSN definitions. | |
file | openwsn_radio.h |
RIOT adaption of the "radio" bsp module definitions. | |
file | openwsn_uart.h |
RIOT adaption-specific definition of the "uart" bsp module. | |
file | scheduler_types.h |
RIOT scheduler types variable declaration. | |