1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #13824 from fjmolinas/pr_openwsn

pkg/openwsn: re-integrate the network stack as a package
This commit is contained in:
Alexandre Abadie 2020-06-30 14:08:10 +02:00 committed by GitHub
commit d98ddfad9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 4232 additions and 0 deletions

View File

@ -899,6 +899,10 @@ ifneq (,$(filter iotlab-m3 wsn430-v1_3b wsn430-v1_4,$(BOARD)))
endif
endif
ifneq (,$(filter openv-%,$(MAKECMDGOALS)))
include $(RIOTBASE)/dist/tools/openvisualizer/makefile.openvisualizer.inc.mk
endif
# Include desvirt Makefile
include $(RIOTTOOLS)/desvirt/Makefile.desvirt

View File

@ -61,6 +61,23 @@ extern "C" {
#define CONFIG_ZTIMER_USEC_MIN (2)
/** @} */
/**
* @name OpenWSN timing constants
*
* @{
*/
/* Measured 440us + ~10% */
#define PORT_maxTxDataPrepare (500/PORT_US_PER_TICK)
/* Measured 200us + ~10% */
#define PORT_maxRxAckPrepare (300/PORT_US_PER_TICK)
/* Measured 300us + ~10% */
#define PORT_maxRxDataPrepare (350/PORT_US_PER_TICK)
/* Measured 316us + ~10% */
#define PORT_maxTxAckPrepare (350/PORT_US_PER_TICK)
/* Measured 360us with openwsn_sctimer_rtt */
#define PORT_delayTx (360/PORT_US_PER_TICK)
/** @} */
/**
* @name Define the interface to the AT86RF231 radio
*

View File

@ -57,6 +57,23 @@ extern "C" {
#define AT86RF2XX_PARAM_SLEEP GPIO_PIN(PA, 20)
#define AT86RF2XX_PARAM_RESET GPIO_PIN(PB, 15)
/**
* @name OpenWSN timing constants
*
* @{
*/
/* Measured 800us + ~10% */
#define PORT_maxTxDataPrepare (900/PORT_US_PER_TICK)
/* Measured 450us + ~10% */
#define PORT_maxRxAckPrepare (500/PORT_US_PER_TICK)
/* Measured 500us + ~10% */
#define PORT_maxRxDataPrepare (550/PORT_US_PER_TICK)
/* Measured 660us + ~10% */
#define PORT_maxTxAckPrepare (750/PORT_US_PER_TICK)
/* Measured 650us with openwsn_sctimer_rtt */
#define PORT_delayTx (650/PORT_US_PER_TICK)
/** @} */
/**
* @name LED pin definitions and handlers
* @{

236
dist/tools/openvisualizer/logging.conf vendored Normal file
View File

@ -0,0 +1,236 @@
# Note Expects 'logDir' passed in with location for file output.
#============================ formatters ===============================
[formatters]
keys=std, console
[formatter_std]
format=%(asctime)s [%(name)s:%(levelname)s] %(message)s
datefmt=%H:%M:%S
[formatter_console]
class=openvisualizer.main.ColoredFormatter
format=%(asctime)s [%(name)s:%(levelname)s] %(message)s
datefmt=%H:%M:%S
#============================ handlers =================================
[handlers]
keys=std,console
[handler_std]
class=handlers.RotatingFileHandler
# args: filename, open mode, max file size, backup file count
args=('LOG_PATH/openv-server.log', 'a', 2000000, 5)
formatter=std
[handler_console]
class=StreamHandler
args=()
formatter=console
#============================ loggers ==================================
[loggers]
keys=
root,
EventBusMonitor,
EventBusClient,
OpenTun,
OpenTunMacOS,
OpenTunWindows,
OpenTunLinux,
MoteConnector,
MoteProbe,
MoteState,
OpenParser,
ParserData,
ParserPrintf,
ParserIEC,
ParserStatus,
Parser,
OpenHdlc,
OpenLbr,
SixLowPanFrag,
RPL,
SourceRoute,
JRC,
Topology,
OpenVisualizerServer,
Utils,
OVWebServer,
OVtracer,
CoAP
[logger_root]
level=ERROR
handlers=std
[logger_EventBusMonitor]
level=ERROR
handlers=std
propagate=0
qualname=EventBusMonitor
[logger_EventBusClient]
level=ERROR
handlers=std
propagate=0
qualname=EventBusClient
[logger_OpenTun]
level=ERROR
handlers=std
propagate=0
qualname=OpenTun
[logger_OpenTunWindows]
level=INFO
handlers=std, console
propagate=0
qualname=OpenTunWindows
[logger_OpenTunLinux]
level=INFO
handlers=std, console
propagate=0
qualname=OpenTunLinux
[logger_OpenTunMacOS]
level=INFO
handlers=std, console
propagate=0
qualname=OpenTunMacOS
[logger_MoteConnector]
level=ERROR
handlers=std
propagate=0
qualname=MoteConnector
[logger_MoteProbe]
level=INFO
handlers=std, console
propagate=0
qualname=MoteProbe
[logger_MoteState]
level=ERROR
handlers=std
propagate=0
qualname=MoteState
[logger_OpenParser]
level=ERROR
handlers=std
propagate=0
qualname=OpenParser
[logger_Parser]
level=ERROR
handlers=std
propagate=0
qualname=Parser
[logger_ParserIEC]
level=VERBOSE
handlers=std, console
propagate=0
qualname=ParserIEC
[logger_ParserData]
level=ERROR
handlers=std
propagate=0
qualname=ParserData
[logger_ParserPrintf]
level=INFO
handlers=std, console
propagate=0
qualname=ParserPrintf
[logger_ParserPacket]
level=ERROR
handlers=std
propagate=0
qualname=ParserPacket
[logger_ParserStatus]
level=ERROR
handlers=std
propagate=0
qualname=ParserStatus
[logger_OpenHdlc]
level=ERROR
handlers=std
propagate=0
qualname=OpenHdlc
[logger_OpenLbr]
level=ERROR
handlers=std
propagate=0
qualname=OpenLbr
[logger_SixLowPanFrag]
level=INFO
handlers=std
propagate=0
qualname=SixLowPanFrag
[logger_RPL]
level=INFO
handlers=std, console
propagate=0
qualname=RPL
[logger_SourceRoute]
level=ERROR
handlers=std
propagate=0
qualname=SourceRoute
[logger_JRC]
level=VERBOSE
handlers=std, console
propagate=0
qualname=JRC
[logger_Topology]
level=ERROR
handlers=std
propagate=0
qualname=Topology
[logger_OpenVisualizerServer]
level=VERBOSE
handlers=std, console
propagate=0
qualname=OpenVisualizerServer
[logger_Utils]
level=VERBOSE
handlers=std, console
propagate=0
qualname=Utils
[logger_OVWebServer]
level=INFO
handlers=std
propagate=0
qualname=OVWebServer
[logger_OVtracer]
level=INFO
handlers=std, console
propagate=0
qualname=OVtracer
[logger_CoAP]
level=INFO
handlers=std, console
propagate=0
qualname=coap

View File

@ -0,0 +1,93 @@
.PHONY: openv-clean openv-setroot openv-term openv-termroot
# Use a single board with openv
# ===================================
#
# OpenVisualizer runs on port 9000 by default, if that ports conflicts or
# multiple instances are spawned you will need to specify the port, eg.
# * `OPENV_FLAGS='--port=9001`
#
# Not all logs for openvisualizer are piped to the terminal, more detailed logs
# are stored in $(BINDIR)/openv-server.log
#
# More info at https://github.com/fjmolinas/openvisualizer/blob/develop_SW-318-RIOT/README.md
#
# Supported:
# * openv-term
# * openv-termroot
# * openv-termtun
# * openv-setroot
# * openv-clean
# * openv-serial
#
# Prerequisites
# -------------
#
# * Install openvisualizer:
# * git clone -b develop_SW-318-RIOT https://github.com/fjmolinas/openvisualizer.git
# * cd openvisualizer
# * pip2 install .
#
# * If using iotlab nodes, pre-requisites in makefile.iotlab.single.inc.mk
#
# * For `openv-termtun` it will require root must be able to ssh into iotlab
#
# Use full path in case it needs to be run with sudo
OPENV_SERVER_PATH := $(shell which openv-server)
OPENV_CLIENT_PATH := $(shell which openv-client)
OPENV_SERIAL_PATH := $(shell which openv-serial)
# Openvisualizer requires to know where openwsn-fw is located
OPENV_OPENWSN_FW_PATH ?= --fw-path=$(RIOTBASE)/build/pkg/openwsn
OPENV_DEFAULT_FLAGS += $(OPENV_OPENWSN_FW_PATH)
OPENV_DEFAULT_FLAGS ?=
ifneq (,$(IOTLAB_NODE))
OPENV_MOTE ?= $(IOTLAB_NODE)
OPENV_DEFAULT_FLAGS += --iotlab-motes=$(IOTLAB_NODE)
else
OPENV_MOTE += $(PORT)
OPENV_DEFAULT_FLAGS += --port-mask=$(OPENV_MOTE) --baudrate=$(BAUD)
endif
# Use modified logging configuration
OPENV_LOG_CONFIG = $(BINDIR)/logging.conf
OPENV_LOG_FILE = $(BINDIR)/openv-server.log
OPENV_DEFAULT_FLAGS += --lconf=$(OPENV_LOG_CONFIG)
$(OPENV_LOG_CONFIG): $(LAST_MAKEFILEDIR)/logging.conf
$(Q)cp $^ $@.tmp
$(Q)sed -i 's#LOG_PATH#'"$(BINDIR)"'#g' $@.tmp
$(Q)mv $@.tmp $@
# Start tun interface
ifneq (,$(filter openv-termtun,$(MAKECMDGOALS)))
OPENV_DEFAULT_FLAGS += --opentun
endif
# Optional flags to pass through command line
OPENV_FLAGS ?=
openv-term: $(OPENV_LOG_CONFIG)
openv-term: $(TERMDEPS)
$(Q)$(OPENV_SERVER_PATH) $(OPENV_DEFAULT_FLAGS) $(OPENV_FLAGS)
openv-termroot: $(OPENV_LOG_CONFIG)
openv-termroot: $(TERMDEPS)
$(Q)$(OPENV_SERVER_PATH) $(OPENV_DEFAULT_FLAGS) $(OPENV_FLAGS) --root=$(OPENV_MOTE)
openv-termtun: $(OPENV_LOG_CONFIG)
openv-termtun: $(TERMDEPS)
sudo $(OPENV_SERVER_PATH) $(OPENV_DEFAULT_FLAGS) $(OPENV_FLAGS) --root=$(OPENV_MOTE)
openv-setroot:
$(Q)$(OPENV_CLIENT_PATH) $(OPENV_OPENWSN_FW_PATH) $(OPENV_FLAGS) root=$(OPENV_MOTE)
openv-clean:
$(Q)rm -rf $(OPENV_LOG_CONFIG)
$(Q)rm -rf $(OPENV_LOG_FILE)
openv-serial:
$(Q)$(OPENV_SERIAL_PATH) --port=$(PORT) --baudrate=$(BAUD)

51
pkg/openwsn/Makefile Normal file
View File

@ -0,0 +1,51 @@
PKG_NAME=openwsn
PKG_URL=https://github.com/openwsn-berkeley/openwsn-fw.git
PKG_VERSION=cbcf622bd9369fcfc8455a5fb9349de2ed3c3a46
PKG_LICENSE=BSD-3-Clause
include $(RIOTBASE)/pkg/pkg.mk
# openwsn_% RIOT Modules or PSEUDOMODULES that don't have custom rules
IGNORE_MODULES := openwsn_leds \
openwsn_debugpins \
openwsn_radio \
openwsn_serial \
openwsn_sctimer% \
openwsn_cryptoengine \
#
OPENWSN_MODULES := $(filter-out $(IGNORE_MODULES),$(filter openwsn_%,$(USEMODULE)))
.PHONY: openwsn_%
OPENWSN_LOG_LEVEL ?= LOG_NONE
CFLAGS += -Wno-array-bounds
CFLAGS += -Wno-implicit-fallthrough
CFLAGS += -Wno-implicit-function-declaration
CFLAGS += -Wno-incompatible-pointer-types
CFLAGS += -Wno-maybe-uninitialized
CFLAGS += -Wno-old-style-definition
CFLAGS += -Wno-return-type
CFLAGS += -Wno-sign-compare
CFLAGS += -Wno-unused-parameter
CFLAGS += -Wno-strict-prototypes
CFLAGS += -DLOG_LEVEL=$(OPENWSN_LOG_LEVEL)
OPENWSN_PATH_openstack = openstack
OPENWSN_PATH_openapps = openapps
OPENWSN_PATH_drivers = drivers/common
OPENWSN_PATH_scheduler = kernel/openos
OPENWSN_PATH_cjoin = openapps/cjoin
OPENWSN_PATH_opencoap = openapps/opencoap
OPENWSN_PATH_mac_low = openstack/02a-MAClow
OPENWSN_PATH_mac_high = openstack/02b-MAChigh
OPENWSN_PATH_iphc = openstack/03a-IPHC
OPENWSN_PATH_ipv6 = openstack/03b-IPv6
OPENWSN_PATH_transport = openstack/04-TRAN
OPENWSN_PATH_crosslayers = openstack/cross-layers
all: $(OPENWSN_MODULES)
openwsn_%:
"$(MAKE)" -C $(PKG_SOURCE_DIR)/$(OPENWSN_PATH_$*) -f $(RIOTBASE)/Makefile.base MODULE=$@

71
pkg/openwsn/Makefile.dep Normal file
View File

@ -0,0 +1,71 @@
ifneq (,$(filter openwsn_openstack,$(USEMODULE)))
USEMODULE += openwsn_cjoin
USEMODULE += openwsn_iphc
USEMODULE += openwsn_ipv6
USEMODULE += openwsn_mac_low
USEMODULE += openwsn_mac_high
USEMODULE += openwsn_transport
USEMODULE += openwsn_crosslayers
USEMODULE += openwsn_drivers
USEMODULE += openwsn_sctimer
USEMODULE += openwsn_radio
DEFAULT_MODULE += auto_init_openwsn
USEMODULE += luid
USEMODULE += netdev_default
endif
ifneq (,$(filter openwsn_scheduler,$(USEMODULE)))
USEMODULE += core_thread_flags
endif
ifneq (,$(filter openwsn_cjoin,$(USEMODULE)))
USEMODULE += openwsn_openapps
USEMODULE += openwsn_opencoap
USEMODULE += openwsn_cryptoengine
endif
ifneq (,$(filter openwsn_cryptoengine,$(USEMODULE)))
USEMODULE += crypto_3des
USEMODULE += cipher_modes
endif
ifneq (,$(filter openwsn_sctimer,$(USEMODULE)))
ifeq (,$(filter openwsn_sctimer_ztimer,$(USEMODULE)))
USEMODULE += openwsn_sctimer_rtt
endif
endif
ifneq (,$(filter openwsn_sctimer_ztimer,$(USEMODULE)))
USEMODULE += ztimer_usec
USEMODULE += ztimer_msec
FEATURES_OPTIONAL += periph_rtt
ifneq (,$(filter periph_rtt,$(FEATURES_USED)))
USEMODULE += ztimer_periph_rtt
endif
endif
ifneq (,$(filter openwsn_sctimer_rtt,$(USEMODULE)))
FEATURES_REQUIRED += periph_rtt
endif
ifneq (,$(filter openwsn_serial,$(USEMODULE)))
USEMODULE += openwsn_drivers
USEMODULE += ztimer_usec
FEATURES_REQUIRED += periph_uart
FEATURES_OPTIONAL += periph_uart_nonblocking
endif
ifneq (,$(filter openwsn_leds openwsn_debugpins,$(USEMODULE)))
FEATURES_REQUIRED += periph_gpio
endif
ifneq (,$(filter openwsn_debugpins,$(USEMODULE)))
FEATURES_REQUIRED += periph_gpio_irq
endif
# This port currently requires setting ISR_STACKSIZE
FEATURES_BLACKLIST += arch_esp32 arch_esp8266 arch_riscv arch_avr8

View File

@ -0,0 +1,40 @@
INCLUDES += -I$(PKGDIRBASE)/openwsn \
-I$(PKGDIRBASE)/openwsn/kernel \
-I$(PKGDIRBASE)/openwsn/inc \
-I$(PKGDIRBASE)/openwsn/drivers/common \
-I$(PKGDIRBASE)/openwsn/bsp/boards/ \
-I$(PKGDIRBASE)/openwsn/openstack/ \
-I$(PKGDIRBASE)/openwsn/openstack/02a-MAClow \
-I$(PKGDIRBASE)/openwsn/openstack/02b-MAChigh \
-I$(PKGDIRBASE)/openwsn/openstack/03a-IPHC \
-I$(PKGDIRBASE)/openwsn/openstack/03b-IPv6 \
-I$(PKGDIRBASE)/openwsn/openstack/04-TRAN \
-I$(PKGDIRBASE)/openwsn/openstack/cross-layers \
-I$(PKGDIRBASE)/openwsn/openapps \
-I$(PKGDIRBASE)/openwsn/openapps/cjoin \
-I$(PKGDIRBASE)/openwsn/openapps/opencoap \
-I$(RIOTBASE)/pkg/openwsn/include \
DIRS += $(RIOTBASE)/pkg/openwsn/contrib
PSEUDOMODULES += openwsn_serial
PSEUDOMODULES += openwsn_debugpins
PSEUDOMODULES += openwsn_leds
PSEUDOMODULES += openwsn_sctimer%
PSEUDOMODULES += openwsn_cryptoengine
PSEUDOMODULES += openwsn_radio
# In OpenWSN the ISR stack is shared with the network stack. OpenWSN stack is
# 2Kb, this means that the ISR stack in OpenWSN might have up to 2Kb available.
# To keep the same marging increase the ISR stack to 2Kb as well. In practice
# 1Kb should be enough.
CFLAGS += -DISR_STACKSIZE=2048
# at86rf2xx state machine is in enhanced mode by default, OpenWSN requires
# basic mode.
ifneq (,$(filter at86rf2xx,$(USEMODULE)))
CFLAGS += -DAT86RF2XX_BASIC_MODE
endif
# LLVM ARM shows issues with missing definitions fot stdatomic
TOOLCHAINS_BLACKLIST += llvm

View File

@ -0,0 +1,13 @@
MODULE = openwsn
SRC := $(filter-out sctimer_% ,$(wildcard *.c))
ifneq (,$(filter openwsn_sctimer_rtt,$(USEMODULE)))
SRC += sctimer_rtt.c
endif
ifneq (,$(filter openwsn_sctimer_ztimer,$(USEMODULE)))
SRC += sctimer_ztimer.c
endif
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2018 Hamburg University of Applied Sciences
* 2020 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_openwsn
* @{
*
* @file
* @brief RIOT adaption of the "board" bsp module
*
* @author Thomas Watteyne <watteyne@eecs.berkeley.edu>, February 2012
* @author Tengfei Chang <tengfei.chang@gmail.com>, July 2012
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>, July 2017
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*
* @}
*/
#include <stdio.h>
#include "sctimer.h"
#include "radio.h"
#include "thread.h"
#include "periph/pm.h"
#include "openwsn_board.h"
#include "openwsn_debugpins.h"
#include "openwsn_debugpins_params.h"
#include "openwsn_leds.h"
#include "openwsn_leds_params.h"
#include "openwsn_uart.h"
#ifdef MODULE_PM_LAYERED
#include "pm_layered.h"
#endif
#define LOG_LEVEL LOG_NONE
#include "log.h"
void board_init_openwsn(void)
{
LOG_DEBUG("[openwsn/board]: init\n");
#ifdef MODULE_PM_LAYERED
/* sleeping is currently not supported, block all sleep modes */
for (uint8_t i = 0; i < PM_NUM_MODES; i++) {
pm_block(i);
}
#endif
if (IS_USED(MODULE_OPENWSN_LEDS)) {
LOG_DEBUG("[openwsn/board]: leds init\n");
ledpins_riot_init(openwsn_leds_params);
}
if (IS_USED(MODULE_OPENWSN_DEBUGPINS)) {
LOG_DEBUG("[openwsn/board]: debugpins init\n");
openwsn_debugpins_init(openwsn_debugpins_params);
}
if (IS_USED(MODULE_OPENWSN_SCTIMER)) {
LOG_DEBUG("[openwsn/board]: sctimer init\n");
sctimer_init();
}
if (IS_USED(MODULE_OPENWSN_SERIAL)) {
LOG_DEBUG("[openwsn/board]: uart init\n");
uart_init_openwsn();
}
}
void board_sleep(void)
{
/* sleep is handled by `pm_layered` */
}
void board_reset(void)
{
LOG_DEBUG("[openwsn/board]: reset\n");
pm_reboot();
}

View File

@ -0,0 +1,104 @@
/*
* Copyright (C) 2020 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_openwsn
* @{
*
* @file
* @brief RIOT adaption definition of the "cryptoengine" module
*
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*
* @}
*/
#include <stdio.h>
#include "opendefs.h"
#include "crypto/ciphers.h"
#include "crypto/modes/ecb.h"
#include "crypto/modes/ccm.h"
/* CCM does not restrict the input message size, but these function will
only be called with a message length of at most 125b in OpenWSN */
#define MAX_MESSAGE_LEN (125U)
owerror_t cryptoengine_aes_ccms_enc(uint8_t *a, uint8_t len_a, uint8_t *m,
uint8_t *len_m, uint8_t *nonce, uint8_t l,
uint8_t *key, uint8_t len_mac)
{
cipher_t cipher;
int ret, len;
uint8_t tmp_buff[MAX_MESSAGE_LEN + CCM_MAC_MAX_LEN];
ret = cipher_init(&cipher, CIPHER_AES_128, key, CCM_BLOCK_SIZE);
if (ret != 1) {
return E_FAIL;
}
len = cipher_encrypt_ccm(&cipher, a, len_a, len_mac, l,
nonce, 15 - l, m, *len_m, tmp_buff);
if (len < 0) {
return E_FAIL;
}
*len_m = len;
memcpy(m, tmp_buff, *len_m);
return E_SUCCESS;
}
owerror_t cryptoengine_aes_ccms_dec(uint8_t *a, uint8_t len_a, uint8_t *m,
uint8_t *len_m, uint8_t *nonce, uint8_t l,
uint8_t *key, uint8_t len_mac)
{
cipher_t cipher;
int ret, len;
uint8_t tmp_buff[MAX_MESSAGE_LEN];
ret = cipher_init(&cipher, CIPHER_AES_128, key, CCM_BLOCK_SIZE);
if (ret != 1) {
return E_FAIL;
}
len = cipher_decrypt_ccm(&cipher, a, len_a, len_mac, l, nonce, 15 - l, m,
*len_m, tmp_buff);
if (len < 0) {
return E_FAIL;
}
*len_m = len;
memcpy(m, tmp_buff, *len_m);
return E_SUCCESS;
}
owerror_t cryptoengine_aes_ecb_enc(uint8_t *buffer, uint8_t *key)
{
cipher_t cipher;
int ret, len;
ret = cipher_init(&cipher, CIPHER_AES_128, key, CCM_BLOCK_SIZE);
if (ret != 1) {
return E_FAIL;
}
len = cipher_encrypt_ecb(&cipher, buffer, CCM_BLOCK_SIZE, buffer);
if (len < 0) {
return E_FAIL;
}
return E_SUCCESS;
}

View File

@ -0,0 +1,195 @@
/*
* Copyright (C) 2017 Hamburg University of Applied Sciences
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_openwsn
* @{
*
* @file
* @brief RIOT adaption of the "debugpins" bsp module
*
* @author Michael Frey <michael.frey@msasafety.com>
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*
* @}
*/
#include "debugpins.h"
#include "openwsn_debugpins.h"
#include <stdint.h>
#include <string.h>
/* holds the internal configuration for debugpins */
static debugpins_config_t _configuration = {
GPIO_UNDEF,
GPIO_UNDEF,
GPIO_UNDEF,
GPIO_UNDEF,
GPIO_UNDEF,
GPIO_UNDEF
};
static void _set_checked(gpio_t pin)
{
if (IS_USED(MODULE_OPENWSN_DEBUGPINS)) {
if (pin != GPIO_UNDEF){
gpio_set(pin);
}
}
}
static void _clear_checked(gpio_t pin)
{
if (IS_USED(MODULE_OPENWSN_DEBUGPINS)) {
if (pin != GPIO_UNDEF){
gpio_clear(pin);
}
}
}
static void _toggle_checked(gpio_t pin)
{
if (IS_USED(MODULE_OPENWSN_DEBUGPINS)) {
if (pin != GPIO_UNDEF){
gpio_toggle(pin);
}
}
}
static void _init_checked(gpio_t pin)
{
if (IS_USED(MODULE_OPENWSN_DEBUGPINS)) {
if (pin != GPIO_UNDEF){
gpio_init(pin, GPIO_OUT);
}
}
}
void openwsn_debugpins_init(const debugpins_config_t *user_config)
{
if (IS_USED(MODULE_OPENWSN_DEBUGPINS)) {
memset(&_configuration, GPIO_UNDEF, sizeof(debugpins_config_t));
if (user_config != NULL) {
memcpy(&_configuration, user_config, sizeof(debugpins_config_t));
debugpins_init();
}
}
else {
(void) user_config;
}
}
void debugpins_init(void)
{
_init_checked(_configuration.frame);
_init_checked(_configuration.slot);
_init_checked(_configuration.fsm);
_init_checked(_configuration.task);
_init_checked(_configuration.isr);
_init_checked(_configuration.radio);
debugpins_frame_clr();
debugpins_slot_clr();
debugpins_fsm_clr();
debugpins_task_clr();
debugpins_isr_clr();
debugpins_radio_clr();
}
void debugpins_frame_toggle(void)
{
_toggle_checked(_configuration.frame);
}
void debugpins_frame_clr(void)
{
_clear_checked(_configuration.frame);
}
void debugpins_frame_set(void)
{
_set_checked(_configuration.frame);
}
void debugpins_slot_toggle(void)
{
_toggle_checked(_configuration.slot);
}
void debugpins_slot_clr(void)
{
_clear_checked(_configuration.slot);
}
void debugpins_slot_set(void)
{
_set_checked(_configuration.slot);
}
void debugpins_fsm_toggle(void)
{
_toggle_checked(_configuration.fsm);
}
void debugpins_fsm_clr(void)
{
_clear_checked(_configuration.fsm);
}
void debugpins_fsm_set(void)
{
_set_checked(_configuration.fsm);
}
void debugpins_task_toggle(void)
{
_toggle_checked(_configuration.task);
}
void debugpins_task_clr(void)
{
_clear_checked(_configuration.task);
}
void debugpins_task_set(void)
{
_set_checked(_configuration.task);
}
void debugpins_isr_toggle(void)
{
_toggle_checked(_configuration.isr);
}
void debugpins_isr_clr(void)
{
_clear_checked(_configuration.isr);
}
void debugpins_isr_set(void)
{
_set_checked(_configuration.isr);
}
void debugpins_radio_toggle(void)
{
_toggle_checked(_configuration.radio);
}
void debugpins_radio_clr(void)
{
_clear_checked(_configuration.radio);
}
void debugpins_radio_set(void)
{
_set_checked(_configuration.radio);
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2017 Hamburg University of Applied Sciences
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_openwsn
* @{
*
* @file
* @brief RIOT adaption definition of the "eui64" bsp module
*
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
*
* @}
*/
#include "string.h"
#include "eui64.h"
#include "openwsn_radio.h"
#include "luid.h"
#include "net/netdev.h"
#include "net/netopt.h"
#include "net/ieee802154.h"
extern openwsn_radio_t openwsn_radio;
void eui64_get(uint8_t *addressToWrite)
{
eui64_t eui64;
if (openwsn_radio.dev->driver->get(openwsn_radio.dev, NETOPT_ADDRESS_LONG,
&eui64,
sizeof(eui64_t)) == sizeof(eui64_t)) {
memcpy(addressToWrite, eui64.uint8, sizeof(eui64.uint8));
}
else {
luid_get_eui64((eui64_t *) addressToWrite);
}
}

236
pkg/openwsn/contrib/leds.c Normal file
View File

@ -0,0 +1,236 @@
/*
* Copyright (C) 2017 Hamburg University of Applied Sciences
* 2020 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_openwsn
* @{
*
* @file
*
* @author Michael Frey <michael.frey@msasafety.com>
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*
* @}
*/
#include "leds.h"
#include "openwsn_leds.h"
#include "board.h"
#include "periph/gpio.h"
#include <stdint.h>
#include <string.h>
/** holds the internal configuration for debug pins */
static leds_config_t _configuration = {
GPIO_UNDEF,
GPIO_UNDEF,
GPIO_UNDEF,
GPIO_UNDEF,
GPIO_LED_HIGH,
};
static void _toggle_checked(gpio_t pin)
{
if (IS_USED(MODULE_OPENWSN_LEDS)) {
if (pin != GPIO_UNDEF) {
gpio_toggle(pin);
}
}
}
static void _init_checked(gpio_t pin)
{
if (IS_USED(MODULE_OPENWSN_LEDS)) {
if (pin != GPIO_UNDEF) {
gpio_init(pin, GPIO_OUT);
}
}
}
static void _write_checked(gpio_t pin, uint8_t on_state)
{
if (IS_USED(MODULE_OPENWSN_LEDS)) {
if (pin != GPIO_UNDEF) {
gpio_write(pin, on_state);
}
}
}
static uint8_t _is_on_checked(gpio_t pin)
{
if (IS_USED(MODULE_OPENWSN_LEDS)) {
uint8_t ret = 0;
if (pin != GPIO_UNDEF) {
ret = gpio_read(pin);
}
return ret;
}
else {
return 0;
}
}
static void _blink_checked(gpio_t pin)
{
if (IS_USED(MODULE_OPENWSN_LEDS)) {
/* toggle for ~10s if ztimer is used */
for (uint8_t i = 0; i < 100; i++) {
_toggle_checked(pin);
for (uint32_t i = 0; i < (CLOCK_CORECLOCK / 50); i++) {
/* Make sure for loop is not optimized out */
__asm__ ("");
}
}
}
}
void ledpins_riot_init(const leds_config_t *user_config)
{
if (IS_USED(MODULE_OPENWSN_LEDS)) {
if (user_config != NULL) {
memcpy(&_configuration, user_config, sizeof(leds_config_t));
leds_init();
}
}
else {
(void)user_config;
}
}
void leds_init(void)
{
_init_checked(_configuration.error);
_init_checked(_configuration.sync);
_init_checked(_configuration.radio);
_init_checked(_configuration.debug);
leds_all_off();
}
void leds_error_on(void)
{
_write_checked(_configuration.error, _configuration.led_on);
}
void leds_error_off(void)
{
_write_checked(_configuration.error, ~_configuration.led_on);
}
void leds_error_toggle(void)
{
_toggle_checked(_configuration.error);
}
uint8_t leds_error_isOn(void)
{
return _is_on_checked(_configuration.error);
}
void leds_error_blink(void)
{
_blink_checked(_configuration.error);
}
void leds_radio_on(void)
{
_write_checked(_configuration.radio, _configuration.led_on);
}
void leds_radio_off(void)
{
_write_checked(_configuration.radio, ~_configuration.led_on);
}
void leds_radio_toggle(void)
{
_toggle_checked(_configuration.radio);
}
uint8_t leds_radio_isOn(void)
{
return _is_on_checked(_configuration.radio);
}
void leds_sync_on(void)
{
_write_checked(_configuration.sync, _configuration.led_on);
}
void leds_sync_off(void)
{
_write_checked(_configuration.sync, ~_configuration.led_on);
}
void leds_sync_toggle(void)
{
_toggle_checked(_configuration.sync);
}
uint8_t leds_sync_isOn(void)
{
return _is_on_checked(_configuration.sync);
}
void leds_debug_on(void)
{
_write_checked(_configuration.debug, _configuration.led_on);
}
void leds_debug_off(void)
{
_write_checked(_configuration.debug, ~_configuration.led_on);
}
void leds_debug_toggle(void)
{
_toggle_checked(_configuration.debug);
}
uint8_t leds_debug_isOn(void)
{
return _is_on_checked(_configuration.debug);
}
void leds_all_on(void)
{
leds_error_on();
leds_radio_on();
leds_sync_on();
leds_debug_on();
}
void leds_all_off(void)
{
leds_error_off();
leds_radio_off();
leds_sync_off();
leds_debug_off();
}
void leds_all_toggle(void)
{
leds_error_toggle();
leds_radio_toggle();
leds_sync_toggle();
leds_debug_toggle();
}
void leds_circular_shift(void)
{
/** not implemented */
}
void leds_increment(void)
{
/** not implemented */
}

View File

@ -0,0 +1,122 @@
/*
* Copyright (C) 2018 Hamburg University of Applied Sciences
* 2020 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @{
* @file
* @brief OpenWSN bootstraping functions implementation
*
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*/
#include "scheduler.h"
#include "openstack.h"
#include "radio.h"
#include "idmanager.h"
#include "openwsn.h"
#include "openwsn_board.h"
#include "openwsn_radio.h"
#ifdef MODULE_AT86RF2XX
#include "at86rf2xx.h"
#include "at86rf2xx_params.h"
#endif
#define LOG_LEVEL LOG_NONE
#include "log.h"
#define OPENWSN_SCHED_NAME "openwsn"
#define OPENWSN_SCHED_PRIO (THREAD_PRIORITY_MAIN - 4)
#define OPENWSN_SCHED_STACKSIZE (2048)
#ifdef MODULE_AT86RF2XX
static at86rf2xx_t at86rf2xx_dev;
#endif
static char _stack[OPENWSN_SCHED_STACKSIZE];
static kernel_pid_t _pid = KERNEL_PID_UNDEF;
static void *_event_loop(void *arg);
kernel_pid_t openwsn_get_pid(void)
{
return _pid;
}
#ifdef MODULE_OPENWSN_RADIO
void openwsn_set_addr_16b(netdev_t* dev)
{
uint8_t addr[IEEE802154_SHORT_ADDRESS_LEN];
dev->driver->get(dev, NETOPT_ADDRESS, addr, IEEE802154_SHORT_ADDRESS_LEN);
open_addr_t id;
id.type = ADDR_16B;
memcpy(&id.addr_16b, addr, IEEE802154_SHORT_ADDRESS_LEN);
idmanager_setMyID(&id);
}
#endif
int openwsn_bootstrap(void)
{
LOG_DEBUG("[openwsn]: init RIOT board\n");
board_init_openwsn();
#ifdef MODULE_AT86RF2XX
netdev_t *netdev = (netdev_t *)&at86rf2xx_dev.netdev.netdev;
at86rf2xx_setup(&at86rf2xx_dev, &at86rf2xx_params[0]);
(void) netdev;
#endif
#ifdef MODULE_OPENWSN_RADIO
LOG_DEBUG("[openwsn]: init radio\n");
if (openwsn_radio_init(netdev)) {
LOG_ERROR("[openwsn]: failed to init radio\n");
return -1;
}
#endif
/* Initiate Id manager here and not in `openstack_init` function to allow
overriding the short id address before additional stack components are
initiated */
idmanager_init();
#ifdef MODULE_OPENWSN_RADIO
/* override 16b address to avoid short address collision */
openwsn_set_addr_16b(netdev);
#endif
LOG_DEBUG("[openwsn]: network thread\n");
_pid = thread_create(_stack, OPENWSN_SCHED_STACKSIZE, OPENWSN_SCHED_PRIO,
THREAD_CREATE_STACKTEST, _event_loop, NULL,
OPENWSN_SCHED_NAME);
if (_pid <= 0) {
LOG_ERROR("[openwsn]: couldn't create thread\n");
return -1;
}
return _pid;
}
static void *_event_loop(void *arg)
{
(void)arg;
LOG_DEBUG("[openwsn]: init scheduler\n");
scheduler_init();
LOG_DEBUG("[openwsn]: init openstack\n");
/* Disable IRQ while scheduler is not ready to start */
unsigned irq_state = irq_disable();
openstack_init();
LOG_DEBUG("[openwsn]: start scheduler loop\n");
scheduler_start(irq_state);
return NULL;
}

268
pkg/openwsn/contrib/radio.c Normal file
View File

@ -0,0 +1,268 @@
/*
* Copyright (C) 2018 Hamburg University of Applied Sciences
* 2020 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_openwsn
* @{
*
* @file
* @brief RIOT adaption of the "radio" bsp module
*
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
* @author Oliver Hahm <oliver.hahm@inria.fr>
* @author Francisco Molina <francois-xavier.molina@inria.fr>
* @}
*/
#include <sys/uio.h>
#include "leds.h"
#include "debugpins.h"
#include "sctimer.h"
#include "net/netopt.h"
#include "net/ieee802154.h"
#include "net/netdev/ieee802154.h"
#include "openwsn.h"
#include "openwsn_radio.h"
#define LOG_LEVEL LOG_NONE
#include "log.h"
openwsn_radio_t openwsn_radio;
static void _event_cb(netdev_t *dev, netdev_event_t event);
/* stores the NETDEV_EVENT_ISR capture time to tag the following NETDEV_EVENT */
static PORT_TIMER_WIDTH _txrx_event_capture_time = 0;
int openwsn_radio_init(netdev_t *netdev)
{
assert(netdev);
LOG_DEBUG("[openwsn/radio]: initialize riot-adaptation\n");
openwsn_radio.dev = netdev;
if (netdev->driver->init(netdev)) {
LOG_ERROR("[openwsn/radio]: unable to initialize device\n");
return -1;
}
netdev->event_callback = _event_cb;
LOG_DEBUG("[openwsn/radio]: put radio in standby\n");
netopt_state_t state = NETOPT_STATE_STANDBY;
netdev->driver->set(netdev, NETOPT_STATE, &(state), sizeof(state));
LOG_DEBUG("[openwsn/radio]: set needed netdev options\n");
netopt_enable_t enable;
/* Enable needed IRQs */
enable = NETOPT_ENABLE;
netdev->driver->set(netdev, NETOPT_TX_START_IRQ, &(enable), sizeof(enable));
enable = NETOPT_ENABLE;
netdev->driver->set(netdev, NETOPT_RX_START_IRQ, &(enable), sizeof(enable));
enable = NETOPT_ENABLE;
netdev->driver->set(netdev, NETOPT_RX_END_IRQ, &(enable), sizeof(enable));
enable = NETOPT_ENABLE;
netdev->driver->set(netdev, NETOPT_TX_END_IRQ, &(enable), sizeof(enable));
enable = NETOPT_DISABLE;
/* Enable basic mode, no AUTOACK. no CSMA , no frame filtering */
netdev->driver->set(netdev, NETOPT_AUTOACK, &(enable), sizeof(enable));
enable = NETOPT_DISABLE;
netdev->driver->set(netdev, NETOPT_CSMA, &(enable), sizeof(enable));
enable = NETOPT_ENABLE;
netdev->driver->set(netdev, NETOPT_RAWMODE, &(enable), sizeof(enable));
uint8_t retrans = 0;
/* MAC layer will handle retransmissions */
netdev->driver->set(netdev, NETOPT_RETRANS, &(retrans), sizeof(uint8_t));
/* Enable TX with preloading */
enable = NETOPT_ENABLE;
netdev->driver->set(netdev, NETOPT_PRELOADING, &(enable), sizeof(enable));
/* Set default PANID */
uint16_t panid = OPENWSN_PANID;
netdev->driver->set(netdev, NETOPT_NID, &(panid), sizeof(uint16_t));
return 0;
}
void radio_setStartFrameCb(radio_capture_cbt cb)
{
openwsn_radio.startFrame_cb = cb;
}
void radio_setEndFrameCb(radio_capture_cbt cb)
{
openwsn_radio.endFrame_cb = cb;
}
void radio_reset(void)
{
netopt_state_t state = NETOPT_STATE_RESET;
openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_STATE, &(state),
sizeof(netopt_state_t));
state = NETOPT_STATE_STANDBY;
openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_STATE, &(state),
sizeof(netopt_state_t));
}
void radio_setFrequency(uint8_t frequency, radio_freq_t tx_or_rx)
{
(void)tx_or_rx;
uint16_t chan = frequency;
openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_CHANNEL, &(chan),
sizeof(chan));
}
void radio_rfOn(void)
{
netopt_state_t state = NETOPT_STATE_IDLE;
openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_STATE, &(state),
sizeof(netopt_state_t));
}
void radio_rfOff(void)
{
netopt_state_t state = NETOPT_STATE_STANDBY;
openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_STATE, &(state),
sizeof(netopt_state_t));
debugpins_radio_clr();
leds_radio_off();
}
void radio_loadPacket(uint8_t *packet, uint16_t len)
{
/* NETOPT_PRELOADING is enabled in radio_init so this will only load the
packet */
/* OpenWSN `len` accounts for the FCS field which is set by default by
netdev, so remove from the actual packet `len` */
iolist_t pkt = {
.iol_base = (void *)packet,
.iol_len = (size_t)(len - IEEE802154_FCS_LEN),
};
if (openwsn_radio.dev->driver->send(openwsn_radio.dev, &pkt) < 0) {
LOG_DEBUG("[openwsn/radio]: couldn't load pkt\n");
}
LOG_DEBUG("[openwsn/radio]: loaded radio packet\n");
}
void radio_txEnable(void)
{
debugpins_radio_set();
leds_radio_on();
}
void radio_txNow(void)
{
netopt_state_t state = NETOPT_STATE_TX;
openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_STATE, &state,
sizeof(netopt_state_t));
}
void radio_rxEnable(void)
{
debugpins_radio_set();
leds_radio_on();
netopt_state_t state = NETOPT_STATE_IDLE;
openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_STATE, &(state),
sizeof(state));
}
void radio_rxNow(void)
{
/* nothing to do */
}
void radio_getReceivedFrame(uint8_t *bufRead,
uint8_t *lenRead,
uint8_t maxBufLen,
int8_t *rssi,
uint8_t *lqi,
bool *crc)
{
/* OpenWSN packets are 130 bytes to hold all required data for an SPI
transaction since in some implementations it's used directly in the SPI
shift register:
- 1B spi address, 1B length, 125B data, 2B CRC, 1B LQI
In RIOT we don't do this so maxBufLen is irrelevant, packet size will
always be enough to hold IEEE802154_FRAME_LEN_MAX, but in practice only
125B of data are copied into bufRead.
*/
(void)maxBufLen;
netdev_ieee802154_rx_info_t rx_info;
int bytes_expected = openwsn_radio.dev->driver->recv(openwsn_radio.dev,
NULL, 0,
NULL);
if (bytes_expected < (int)(IEEE802154_ACK_FRAME_LEN - IEEE802154_FCS_LEN)) {
/* drop invalid packet */
openwsn_radio.dev->driver->recv(openwsn_radio.dev, NULL, bytes_expected,
NULL);
radio_rxEnable();
return;
}
int nread = openwsn_radio.dev->driver->recv(openwsn_radio.dev, bufRead,
bytes_expected, &rx_info);
/* FCS is skipped by netdev in the returned length, but OpenWSN includes
IEEE802154_FCS_LEN in its length value */
*lenRead = nread + IEEE802154_FCS_LEN;
/* get rssi, lqi & crc */
*rssi = rx_info.rssi;
*lqi = rx_info.lqi;
/* only valid crc frames are currently accepted */
*crc = 1;
radio_rxEnable();
}
static void _event_cb(netdev_t *dev, netdev_event_t event)
{
(void)dev;
if (event == NETDEV_EVENT_ISR) {
/* capture the time */
debugpins_isr_set();
_txrx_event_capture_time = sctimer_readCounter();
openwsn_radio.dev->driver->isr(openwsn_radio.dev);
debugpins_isr_clr();
}
else {
LOG_DEBUG("[openwsn/radio]: event triggered -> %i\n", event);
switch (event) {
case NETDEV_EVENT_RX_STARTED:
openwsn_radio.startFrame_cb(_txrx_event_capture_time);
LOG_DEBUG("[openwsn/radio]: NETDEV_EVENT_RX_STARTED\n");
break;
case NETDEV_EVENT_TX_STARTED:
openwsn_radio.startFrame_cb(_txrx_event_capture_time);
LOG_DEBUG("[openwsn/radio]: NETDEV_EVENT_TX_STARTED\n");
break;
case NETDEV_EVENT_RX_COMPLETE:
openwsn_radio.endFrame_cb(_txrx_event_capture_time);
LOG_DEBUG("[openwsn/radio]: NETDEV_EVENT_RX_COMPLETE\n");
break;
case NETDEV_EVENT_TX_COMPLETE:
openwsn_radio.endFrame_cb(_txrx_event_capture_time);
LOG_DEBUG("[openwsn/radio]: NETDEV_EVENT_TX_COMPLETE\n");
break;
default:
break;
}
}
}

View File

@ -0,0 +1,169 @@
/*
* Copyright (C) 2017 Hamburg University of Applied Sciences
* 2020 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_openwsn
* @{
*
* For details on the implementation check pkg/openwsn/doc.txt
*
* @file
* @brief RTT based adaptation of "sctimer" bsp module
*
* @author Tengfei Chang <tengfei.chang@gmail.com>, July 2012
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>, July 2017
* @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>, April 2019
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*
* @}
*/
#include <stdatomic.h>
#include "sctimer.h"
#include "debugpins.h"
#include "board.h"
#include "periph/rtt.h"
#define LOG_LEVEL LOG_NONE
#include "log.h"
/**
* @brief Maximum counter difference to not consider an ISR late, this
* should account for the largest timer interval OpenWSN
* scheduler might work with. When running only the stack this
* should not be more than SLOT_DURATION, but when using cjoin
* it is 65535ms
*/
#ifndef SCTIMER_LOOP_THRESHOLD
#define SCTIMER_LOOP_THRESHOLD (2 * PORT_TICS_PER_MS * 65535)
#endif
/* OpenWSN needs at least 32 tics per ms,use time division to reach that
if needed */
#ifdef RTT_FREQUENCY
#if RTT_FREQUENCY < 32768U
#define SCTIMER_TIME_DIVISION (1)
#if (SCTIMER_FREQUENCY % RTT_FREQUENCY) != 0
#error "RTT_FREQUENCY not supported"
#endif
#endif
#endif
#ifdef SCTIMER_TIME_DIVISION
#define SCTIMER_PRESCALER __builtin_ctz( \
SCTIMER_FREQUENCY / RTT_FREQUENCY)
#define SCTIMER_TIME_DIVISION_MASK (RTT_MAX_VALUE >> SCTIMER_PRESCALER)
#define SCTIMER_PRESCALER_MASK (~SCTIMER_TIME_DIVISION_MASK)
#define SCTIMER_PRESCALER_SHIFT __builtin_ctz(SCTIMER_TIME_DIVISION_MASK)
static uint32_t _prescaler;
static atomic_bool _enable;
#endif
static sctimer_cbt sctimer_cb;
static void sctimer_isr_internal(void *arg)
{
(void)arg;
if (sctimer_cb != NULL) {
debugpins_isr_set();
sctimer_cb();
debugpins_isr_clr();
}
}
void sctimer_init(void)
{
rtt_init();
sctimer_cb = NULL;
#ifdef SCTIMER_TIME_DIVISION
_prescaler = 0;
_enable = false;
#endif
}
void sctimer_set_callback(sctimer_cbt cb)
{
sctimer_cb = cb;
}
#ifdef SCTIMER_TIME_DIVISION
uint32_t _update_val(uint32_t val, uint32_t now)
{
now = now & SCTIMER_PRESCALER_MASK;
val = val >> SCTIMER_PRESCALER;
/* Check if next value would cause an overflow */
if ((now - val) > SCTIMER_LOOP_THRESHOLD && _enable && now > val) {
_prescaler += (1 << SCTIMER_PRESCALER_SHIFT);
_enable = false;
}
/* Make sure it only updates the _prescaler once per overflow cycle */
if (val > SCTIMER_LOOP_THRESHOLD && val < 2 * SCTIMER_LOOP_THRESHOLD) {
_enable = true;
}
val |= _prescaler;
return val;
}
#endif
void sctimer_setCompare(uint32_t val)
{
unsigned state = irq_disable();
uint32_t now = rtt_get_counter();
#ifdef SCTIMER_TIME_DIVISION
val = _update_val(val, now);
#endif
if ((int32_t)now - val < SCTIMER_LOOP_THRESHOLD && now > val) {
rtt_set_alarm((now + RTT_MIN_OFFSET) & RTT_MAX_VALUE,
sctimer_isr_internal, NULL);
}
else {
if ((int32_t)val - now < RTT_MIN_OFFSET) {
rtt_set_alarm((now + RTT_MIN_OFFSET) & RTT_MAX_VALUE,
sctimer_isr_internal, NULL);
}
else {
rtt_set_alarm(val & RTT_MAX_VALUE, sctimer_isr_internal,
NULL);
}
}
irq_restore(state);
LOG_DEBUG("[sctimer]: set cb to %" PRIu32 " at %" PRIu32 "\n",
(uint32_t) val, now);
}
uint32_t sctimer_readCounter(void)
{
uint32_t now = rtt_get_counter();
#ifdef SCTIMER_TIME_DIVISION
now &= SCTIMER_TIME_DIVISION_MASK;
now = (now << SCTIMER_PRESCALER);
#endif
LOG_DEBUG("[sctimer]: now %" PRIu32 "\n", now);
return now;
}
void sctimer_enable(void)
{
rtt_poweron();
}
void sctimer_disable(void)
{
rtt_poweroff();
}

View File

@ -0,0 +1,163 @@
/*
* Copyright (C) 2020 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_openwsn
* @{
*
* @file
* @brief Ztimer based adaptation of "sctimer" bsp module
*
* For details on the implementation check pkg/openwsn/doc.txt
*
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*
* @}
*/
#include "sctimer.h"
#include "debugpins.h"
#include "ztimer.h"
#include "ztimer/convert.h"
#include "ztimer/convert_frac.h"
#include "ztimer/convert_shift.h"
#include "ztimer/config.h"
#include "periph_conf.h"
#define LOG_LEVEL LOG_NONE
#include "log.h"
/**
* @brief Maximum counter difference to not consider an ISR late, this
* should account for the largest timer interval OpenWSN
* scheduler might work with. When running only the stack this
* should not be more than SLOT_DURATION, but when using cjoin
* it is 65535ms
*/
#ifndef SCTIMER_LOOP_THRESHOLD
#define SCTIMER_LOOP_THRESHOLD (2 * PORT_TICS_PER_MS * 65535)
#endif
#if CONFIG_ZTIMER_MSEC_BASE_FREQ > 32768U
static ztimer_convert_frac_t _ztimer_convert_frac_32768;
#define ZTIMER_32768_CONVERT_LOWER_FREQ CONFIG_ZTIMER_MSEC_BASE_FREQ
#define ZTIMER_32768_CONVERT_LOWER (ZTIMER_MSEC_BASE)
/* cppcheck-suppress preprocessorErrorDirective
* (reason: cppcheck fails to see that CONFIG_ZTIMER_MSEC_BASE_FREQ
* is set in ztimer/config.h to a non zero value */
#elif (CONFIG_ZTIMER_MSEC_BASE_FREQ < 32768U) && \
((32768U % CONFIG_ZTIMER_MSEC_BASE_FREQ) == 0)
static ztimer_convert_shift_t _ztimer_convert_shift_32768;
#define ZTIMER_32768_CONVERT_HIGHER_FREQ CONFIG_ZTIMER_MSEC_BASE_FREQ
#define ZTIMER_32768_CONVERT_HIGHER (ZTIMER_MSEC_BASE)
#elif (CONFIG_ZTIMER_MSEC_BASE_FREQ < 32768U) && \
((32768U % CONFIG_ZTIMER_MSEC_BASE_FREQ) != 0)
#error No suitable ZTIMER_MSEC_BASE config. Maybe add USEMODULE += ztimer_usec?
#endif
static sctimer_cbt _sctimer_cb;
static ztimer_t _ztimer_sctimer;
static ztimer_clock_t *ZTIMER_32768 = NULL;
static void sctimer_isr_internal(void *arg)
{
(void)arg;
if (_sctimer_cb != NULL) {
debugpins_isr_set();
_sctimer_cb();
debugpins_isr_clr();
}
}
void sctimer_init(void)
{
#if CONFIG_ZTIMER_MSEC_BASE_FREQ > 32768U
ZTIMER_32768 = &_ztimer_convert_frac_32768.super.super;
/* cppcheck-suppress preprocessorErrorDirective
* (reason: cppcheck fails to see that CONFIG_ZTIMER_MSEC_BASE_FREQ
* is set in ztimer/config.h to a non zero value */
#elif (CONFIG_ZTIMER_MSEC_BASE_FREQ < 32768U) && \
(32768U % CONFIG_ZTIMER_MSEC_BASE_FREQ == 0)
ZTIMER_32768 = &_ztimer_convert_shift_32768.super.super;
#elif CONFIG_ZTIMER_MSEC_BASE_FREQ == 32768U
ZTIMER_32768 = ZTIMER_MSEC_BASE;
#else
#error Invalid ZTIMER_MSEC_BASE_FREQ config. Maybe add USEMODULE += ztimer_usec?
#endif
#if defined(ZTIMER_32768_CONVERT_LOWER)
LOG_DEBUG("[sctimer]: ZTIMER_32768 convert_frac from %lu to 32768\n",
(long unsigned)ZTIMER_32768_CONVERT_LOWER_FREQ);
ztimer_convert_frac_init(&_ztimer_convert_frac_32768,
ZTIMER_32768_CONVERT_LOWER,
SCTIMER_FREQUENCY,
ZTIMER_32768_CONVERT_LOWER_FREQ);
#elif defined(ZTIMER_32768_CONVERT_HIGHER)
LOG_DEBUG("[sctimer]: ZTIMER_32768 convert_shift %lu to 32768\n",
(long unsigned)ZTIMER_32768_CONVERT_HIGHER_FREQ);
ztimer_convert_shift_up_init(&_ztimer_convert_shift_32768,
ZTIMER_32768_CONVERT_HIGHER,
__builtin_ctz(SCTIMER_FREQUENCY /
CONFIG_ZTIMER_MSEC_BASE_FREQ));
#endif
_ztimer_sctimer.callback = sctimer_isr_internal;
}
void sctimer_set_callback(sctimer_cbt cb)
{
_sctimer_cb = cb;
}
void sctimer_setCompare(uint32_t val)
{
unsigned state = irq_disable();
uint32_t now = ztimer_now(ZTIMER_32768);
/* if the next compare value (isr) to schedule is already later than
the required value, but close enough to think we have been slow
in scheduling it, trigger the ISR right away */
if (now > val) {
if (now - val < SCTIMER_LOOP_THRESHOLD) {
ztimer_set(ZTIMER_32768, &_ztimer_sctimer, 0);
}
else {
ztimer_set(ZTIMER_32768, &_ztimer_sctimer,
UINT32_MAX - now + val);
}
}
else {
ztimer_set(ZTIMER_32768, &_ztimer_sctimer, val - now);
}
irq_restore(state);
LOG_DEBUG("[sctimer]: set cb to %" PRIu32 " at %" PRIu32 "\n",
val, now);
}
uint32_t sctimer_readCounter(void)
{
uint32_t now = ztimer_now(ZTIMER_32768);
LOG_DEBUG("[sctimer]: now %" PRIu32 "\n", now);
return now;
}
void sctimer_enable(void)
{
/* not supported, sctimer does not control RTT or ztimer */
}
void sctimer_disable(void)
{
/* not supported, sctimer does not control RTT or ztimer */
}

184
pkg/openwsn/contrib/uart.c Normal file
View File

@ -0,0 +1,184 @@
/*
* Copyright (C) 2018 Hamburg University of Applied Sciences
* 2020 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_openwsn
* @{
*
* @file
* @brief RIOT adaption of the OpenWSN "uart" bsp module.
*
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*
* @}
*/
#include <stdatomic.h>
#include "board.h"
#include "periph/uart.h"
#ifdef MODULE_ZTIMER_USEC
#include "ztimer.h"
#endif
#include "openwsn_uart.h"
#define XOFF 0x13
#define XON 0x11
#define XONXOFF_ESCAPE 0x12
#define XONXOFF_MASK 0x10
typedef struct {
uart_tx_cbt txCb;
uart_rx_cbt rxCb;
atomic_bool fXonXoffEscaping;
atomic_char xonXoffEscapedByte;
} uart_vars_t;
static uart_vars_t _uart_vars;
static atomic_char _uart_rx_byte;
#ifdef MODULE_ZTIMER_USEC
static ztimer_t _ztimer_tx_uart;
#endif
static void _openwsn_uart_write(const uint8_t *data)
{
if (IS_USED(MODULE_OPENWSN_SERIAL)) {
uart_write(OPENWSN_UART_DEV, data, 1);
#ifdef MODULE_ZTIMER_USEC
ztimer_set(ZTIMER_USEC, &_ztimer_tx_uart, 0);
#endif
}
}
static void _riot_rx_cb(void *arg, uint8_t data)
{
(void)arg;
if (IS_USED(MODULE_OPENWSN_SERIAL)) {
_uart_rx_byte = data;
if (_uart_vars.rxCb) {
_uart_vars.rxCb();
}
}
else {
(void)data;
}
}
static void _riot_tx_cb(void *arg)
{
(void)arg;
if (IS_USED(MODULE_OPENWSN_SERIAL)) {
if (_uart_vars.fXonXoffEscaping == 0x01) {
_uart_vars.fXonXoffEscaping = 0x00;
_uart_vars.xonXoffEscapedByte ^= XONXOFF_MASK;
_openwsn_uart_write((uint8_t *)&(_uart_vars.xonXoffEscapedByte));
}
else {
if (_uart_vars.txCb) {
_uart_vars.txCb();
}
}
}
}
void uart_enableInterrupts(void)
{
/* unused in RIOT */
}
void uart_disableInterrupts(void)
{
/* unused in RIOT */
}
void uart_clearRxInterrupts(void)
{
/* unused in RIOT */
}
void uart_clearTxInterrupts(void)
{
/* unused in RIOT */
}
void uart_init_openwsn(void)
{
if (IS_USED(MODULE_OPENWSN_SERIAL)) {
uart_init(OPENWSN_UART_DEV, OPENWSN_UART_BAUDRATE, \
(uart_rx_cb_t)_riot_rx_cb, NULL);
#ifdef MODULE_ZTIMER_USEC
_ztimer_tx_uart.callback = &_riot_tx_cb;
#else
(void) _riot_tx_cb;
#endif
}
}
void uart_setCallbacks(uart_tx_cbt txCb, uart_rx_cbt rxCb)
{
if (IS_USED(MODULE_OPENWSN_SERIAL)) {
_uart_vars.txCb = txCb;
_uart_vars.rxCb = rxCb;
}
else {
(void)rxCb;
(void)txCb;
}
}
void uart_setCTS(bool state)
{
if (IS_USED(MODULE_OPENWSN_SERIAL)) {
uint8_t byte;
if (state == true) {
byte = XON;
}
else {
byte = XOFF;
}
_openwsn_uart_write(&byte);
}
else {
(void)state;
}
}
void uart_writeByte(uint8_t byteToWrite)
{
if (IS_USED(MODULE_OPENWSN_SERIAL)) {
if (byteToWrite == XON || byteToWrite == XOFF || \
byteToWrite == XONXOFF_ESCAPE) {
uint8_t byte = XONXOFF_ESCAPE;
_uart_vars.fXonXoffEscaping = 0x01;
_uart_vars.xonXoffEscapedByte = byteToWrite;
_openwsn_uart_write(&byte);
}
else {
_openwsn_uart_write(&byteToWrite);
}
}
else {
(void)byteToWrite;
}
}
inline uint8_t uart_readByte(void)
{
if (IS_USED(MODULE_OPENWSN_SERIAL)) {
return _uart_rx_byte;
}
else {
return 0x00;
}
}

420
pkg/openwsn/doc.txt Normal file
View File

@ -0,0 +1,420 @@
/**
@defgroup pkg_openwsn OpenWSN network stack
@ingroup pkg
@ingroup net
@brief Provides a RIOT adaption of the OpenWSN network stack
@see https://github.com/openwsn-berkeley/openwsn-fw
@experimental
# OpenWSN RIOT Port
This implementation integrates the [OpenWSN](https://github.com/openwsn-berkeley/openwsn-fw)
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](https://github.com/openwsn-berkeley/openvisualizer)
## Joining a network
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](https://datatracker.ietf.org/doc/draft-ietf-6tisch-minimal-security/)
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.
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.
## Hardware abstraction implementation
Following, details about the implementation of selected hardware modules.
### sctimer
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.
#### sctimer_ztimer
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`.
#### sctimer_rtt
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.
### radio
The radio adaptation runs in its own thread with the highest priority
(`THREAD_PRIORITY_MAIN - 4`) and maps to RIOT's @ref 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).
### uart
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](https://github.com/openwsn-berkeley/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.
### Openserial
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.
1. The last byte in an HDCL frame is received
2. Serial pipe delays hindering XonXoff operation
3. Interrupts are masked or higher/equal ISR are running
1. When the last byte of a frame is received some parsing and handling of the
frame occur. This takes some time and during that time OpenSerial can't
handle more incoming bytes. This in practice limits the baudrate depending
on the CPU's speed.
2. This can occur when a debugger/virtual-port might act as a
buffer between the mote and the host, or when there is a latency in the
connection (for example tcp connection to iotlab).
3. 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.
## Tested Platforms and Pin configurations
So far, this has been successfully tested on `iotlab-m3`,`nucleo-f103` and
`samr21-xpro`, all based on at86rf23x radios.
### Synchronization
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`.
### Timing
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.
### ledpins & debugpins
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`.
## Testing and debugging
List of some items which are helpful to explore the functionality of OpenWSN:
- LED pins and debug pins as mentioned above in combination with a logic analyzer.
The expected behavior is described in:
[OpenWSN wiki](https://openwsn.atlassian.net/wiki/spaces/OW/pages/688257/Schedules).
- The provided test application provides a UDP client and server. If the UDP
server is able to receive packets, the mechanism is considered to work correctly.
You should also be able to ping the device from your host. See
`tests/pkg_openwsn/README.md` for more details.
- To speed up synchronization and make sniffing easier you can disable channel
hopping by setting (`CFLAGS=-DIEEE802154E_SINGLE_CHANNEL=26`).
- To sniff the packets either use a 802.15.4 capable board and follow at:
https://github.com/RIOT-OS/applications/blob/master/sniffer/tools/README.md.
Alternatively use a Raspberry Pi with an external radio such as Openlabs and
incorporate Linux WPAN tools. In addition to that, there's also other
hardware such as the ATUSB IEEE 802.15.4 USB Adapter which can directly be
used on your Linux computer with WPAN tools installed. If you conduct your
experiments on the IoT-LAB testbed you might want to use a:
[sniffer profile](https://www.iot-lab.info/tutorials/radio-sniffer).
- To explore the channel hopping mechanism there are rather expensive
multi-channel sniffers such as the BeamLogic 802.15.4 Site Analyzer that can
sniff all channels simultaneously. Alternatively you can set up multiple
separate sniffer devices locally or make use of the `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:
```
Test Setup:
------------------
Iterations: 100
Packet length: 100
Echo timeout: 2
Test Progress:
[####################################] 100%
Test Statistics:
------------------
Pkts send: 100
Echo success: 100
Echo timeout: 0
Echo corrupted: 0
```
The test should be considered passing if success rate is > 98%.
## Known Issues
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.
- samr21-xpro issues:
- The serial debugger hinders Openserial operation, an ftdi device must
be used.
- sam0 issues:
- sam0 requires 180us busy loops every time an alarm is set or the
counter is read. Because of the later only `sctimer_rtt` can be used
and the max. tested baudrate for openserial is of 19200 bauds.
- iotlab-m3 issues:
- openserial does not work reliably over 57600 bauds or when using
sctimer_ztimer.
Other errors:
- missed characters over stdio
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.
## Todos
- `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.
- The UART wrapper uses ztimer to fake an interrupt after one byte
has been sent. This should also be done with software interrupts.
## Future Steps
The OpenWSN community is working on refactoring their code base. As
one of the outputs of this modules like `cjoin`, `udp`, `coap` will become
optional. Once this is upstream the support for this pkg should be adapted.
With above mentioned re-works the extraction of the MAC layer might be favored.
As more immediate future steps:
- Follow up PR to add OpenWSN sock
- Complete support of OpenWSN default HW (`openmote-b`)
- Add support for other 802.15.4 network drivers
*/

View File

@ -0,0 +1,143 @@
/*
* Copyright (C) 2017 Hamburg University of Applied Sciences
* 2020 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_openwsn
* @{
*
* @file
*
* @author Thomas Watteyne <watteyne@eecs.berkeley.edu>, February 2012
* @author Tengfei Chang <tengfei.chang@gmail.com>, July 2012
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>, July 2017
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*
* @}
*/
#ifndef BOARD_INFO_H
#define BOARD_INFO_H
#include <stdint.h>
#include <string.h>
#include "board.h"
#include "periph_conf.h"
#include "timex.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name OpenWSN interrupt handling wrappers
* @{
*/
#define INTERRUPT_DECLARATION() unsigned irq_state;
#define DISABLE_INTERRUPTS() irq_state = irq_disable();
#define ENABLE_INTERRUPTS() irq_restore(irq_state);
#define SCHEDULER_WAKEUP() /* unused by RIOT */
#define SCHEDULER_ENABLE_INTERRUPT() /* unused by RIOT */
/** @} */
/**
* @name OpenWSN platform dependent definitions
* @{
*/
/* Always 32bit when using ztimer */
#if RTT_MAX_VALUE == UINT32_MAX || !((RTT_MAX_VALUE + 1) & (RTT_MAX_VALUE)) || \
IS_USED(MODULE_OPENWSN_SCTIMER_ZTIMER)
#define PORT_TIMER_WIDTH uint32_t
#define PORT_RADIOTIMER_WIDTH uint32_t
#else
#error "RTT_MAX_VALUE not supported"
#endif
#if __SIZEOF_POINTER__ == 2
#define PORT_SIGNED_INT_WIDTH int16_t
#else
#define PORT_SIGNED_INT_WIDTH int32_t
#endif
#define SCTIMER_FREQUENCY (32768U)
/* 32 ticks @32768Hz */
#define PORT_TICS_PER_MS (SCTIMER_FREQUENCY / MS_PER_SEC)
/* 30 us per tick @32768Hz */
#define PORT_US_PER_TICK (US_PER_SEC / SCTIMER_FREQUENCY)
/** @} */
/**
* @name OpenWSN IEEE802154E timings
* @{
*
* @note These parameters are BOARD and CPU specific.
Values can't be taken directly from OpenWSN since they do not necessarily use
the same BSP configuration (timers, clock speed, etc.)
For precise synchronization these values should be measured and tuned for
every BOARD.
To understand the relationship between these values and OpenWSN state machine
as well as how they can be measured refer to:
- https://openwsn.atlassian.net/wiki/spaces/OW/pages/688251/State+Machine
- https://openwsn.atlassian.net/wiki/spaces/OW/pages/688255/Timing+Constants
*/
/* standard slot duration is 10ms but code execution time for most OpenWSN
supported BOARDS takes longer than 10ms, so use the default 20ms upstream
slot */
#ifndef SLOTDURATION
#define SLOTDURATION 20 /* in milliseconds */
#endif
#if SLOTDURATION == 20
#ifndef PORT_TsSlotDuration /* 655 ticks at @32768Hz */
#define PORT_TsSlotDuration ((SCTIMER_FREQUENCY * SLOTDURATION) / MS_PER_SEC)
#endif
/* Execution speed related parameters */
#ifndef PORT_maxTxDataPrepare
#define PORT_maxTxDataPrepare (3355 / PORT_US_PER_TICK ) /* ~110 ticks at @32768Hz */
#endif
#ifndef PORT_maxRxAckPrepare
#define PORT_maxRxAckPrepare (610 / PORT_US_PER_TICK ) /* ~20 ticks at @32768Hz */
#endif
#ifndef PORT_maxRxDataPrepare
#define PORT_maxRxDataPrepare (1000 / PORT_US_PER_TICK ) /* ~33 ticks at@32768Hz */
#endif
#ifndef PORT_maxTxAckPrepare
#define PORT_maxTxAckPrepare (1525 / PORT_US_PER_TICK ) /* ~50 ticks at@32768Hz */
#endif
/* Radio speed related parameters */
#ifndef PORT_delayTx
#define PORT_delayTx (300 / PORT_US_PER_TICK ) /* ~10 ticks at@32768Hz */
#endif
#ifndef PORT_delayRx
#define PORT_delayRx (0 / PORT_US_PER_TICK ) /* ~0 ticks at@32768Hz */
#endif
#else
#error "Only 20ms slot duration is currently supported"
#endif
/** @} */ /* SLOTDURATION == 20 */
/**
* @name Adaptive sync accuracy
*
* Used for synchronization in heterogeneous networks (different BOARDs)
* Not supported yet, dummy value needs to be provided.
* @{
*/
#define SYNC_ACCURACY (1) /* ticks */
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* BOARD_INFO_H */

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2018 Hamburg University of Applied Sciences
* 2020 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_openwsn
*
* @{
*
* @file
*
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*/
#ifndef OPENWSN_H
#define OPENWSN_H
#ifdef __cplusplus
extern "C" {
#endif
#include "thread.h"
/**
* @brief Default PANID for OpenWSN network
*/
#ifndef OPENWSN_PANID
#define OPENWSN_PANID (0xCAFE)
#endif
/**
* @brief Initializes OpenWSN thread
*
* @return PID of OpenWSN thread
* @return -1 on initialization error
*/
int openwsn_bootstrap(void);
/**
* @brief get PID of OpenWsn thread.
*
* @return PID of OpenWsn thread
*/
kernel_pid_t openwsn_get_pid(void);
#ifdef __cplusplus
}
#endif
#endif /* OPENWSN_H */
/** @} */

View File

@ -0,0 +1,71 @@
/*
* Copyright (C) 2017 Hamburg University of Applied Sciences
* 2020 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_openwsn
* @{
*
* RIOT HAL is provided as a new "board", a "RIOT board" to OpenWSN hardware
* abstraction interfaces.
*
* @file
* @brief RIOT adaption-specific definition of the "uart" bsp module.
*
* @author Thomas Watteyne <watteyne@eecs.berkeley.edu>, February 2012
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>, July 2017
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*
* @}
*/
#ifndef OPENWSN_BOARD_H
#define OPENWSN_BOARD_H
#include "board_info.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief OpenWSN `board.h` enum definitions
*/
typedef enum {
DO_NOT_KICK_SCHEDULER,
KICK_SCHEDULER,
} kick_scheduler_t;
/**
* @brief Initialize OpenWSN bsp board adaptation
*
* Most initialization functions are not needed since there are initialized by
* RIOT's `auto_init` module. Only OpenWSN specific interfaces will be initialized
* here: `sctimer`, `openwsn_leds`, `openwsn_debugpins` & `uart_ow`.
*
*/
void board_init_openwsn(void);
/**
* @brief Resets the board
*
*/
void board_reset(void);
/**
* @brief Function definitions that we do not use in RIOT but that need to be
* defined for OpenWSN. Sleep is handled by `pm_layered` if enabled for
* the platform.
*/
void board_sleep(void);
#ifdef __cplusplus
}
#endif
#endif /* OPENWSN_BOARD_H */

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2017 Hamburg University of Applied Sciences
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_openwsn
* @{
*
* @file
* @brief Provides an adaption of OpenWSN debug pin handling
* to RIOTs handling of GPIOs.
*
* @author Michael Frey <michael.frey@msasafety.com>
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
*
* @}
*/
#ifndef OPENWSN_DEBUGPINS_H
#define OPENWSN_DEBUGPINS_H
#include "periph/gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief struct holding configuration of OpenWSN debug pins
*/
typedef struct debugpins_config {
gpio_t frame; /**< debug pin for frames */
gpio_t slot; /**< debug pin for slots */
gpio_t fsm; /**< debug pin for fsm */
gpio_t task; /**< debug pin for tasks */
gpio_t isr; /**< debug pin for interrupt service routines */
gpio_t radio; /**< debug pin for the radio */
} debugpins_config_t;
/**
* Sets the debug pins for a specific board for OpenWSN
*
* @param[in] user_config A configuration of GPIO pins used for debugging.
* Unused pins need to be defined as GPIO_UNDEF.
*/
void openwsn_debugpins_init(const debugpins_config_t *user_config);
#ifdef __cplusplus
}
#endif
#endif /* OPENWSN_DEBUGPINS_H */

View File

@ -0,0 +1,80 @@
/*
* Copyright (C) 2017 Hamburg University of Applied Sciences
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_openwsn
* @{
*
* @file
* @brief Default configuration for the OpenWSN debugpins
*
* @author Michael Frey <michael.frey@msasafety.com>
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
*
* @}
*/
#ifndef OPENWSN_DEBUGPINS_PARAMS_H
#define OPENWSN_DEBUGPINS_PARAMS_H
#include "openwsn_debugpins.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name OpenWSN default debugpins configuration.
*
* Undefined by default.
* @{
*/
#ifndef OPENWSN_DEBUGPIN_FRAME
#define OPENWSN_DEBUGPIN_FRAME GPIO_UNDEF
#endif
#ifndef OPENWSN_DEBUGPIN_SLOT
#define OPENWSN_DEBUGPIN_SLOT GPIO_UNDEF
#endif
#ifndef OPENWSN_DEBUGPIN_FSM
#define OPENWSN_DEBUGPIN_FSM GPIO_UNDEF
#endif
#ifndef OPENWSN_DEBUGPIN_TASK
#define OPENWSN_DEBUGPIN_TASK GPIO_UNDEF
#endif
#ifndef OPENWSN_DEBUGPIN_ISR
#define OPENWSN_DEBUGPIN_ISR GPIO_UNDEF
#endif
#ifndef OPENWSN_DEBUGPIN_RADIO
#define OPENWSN_DEBUGPIN_RADIO GPIO_UNDEF
#endif
#define OPENWSN_DEBUGPINS_DEFAULT { .frame = OPENWSN_DEBUGPIN_FRAME, \
.slot = OPENWSN_DEBUGPIN_SLOT, \
.fsm = OPENWSN_DEBUGPIN_FSM, \
.task = OPENWSN_DEBUGPIN_TASK, \
.isr = OPENWSN_DEBUGPIN_ISR, \
.radio = OPENWSN_DEBUGPIN_RADIO}
/**@}*/
/**
* @brief OpenWSN debugpins configuration
*/
static const debugpins_config_t openwsn_debugpins_params[] =
{
#ifdef OPENWSN_DEBUGPINS_BOARD
OPENWSN_DEBUGPINS_BOARD,
#else
OPENWSN_DEBUGPINS_DEFAULT,
#endif
};
#ifdef __cplusplus
}
#endif
#endif /* OPENWSN_DEBUGPINS_PARAMS_H */

View File

@ -0,0 +1,66 @@
/*
* Copyright (C) 2017 Hamburg University of Applied Sciences
* 2020 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_openwsn
* @{
*
* @file
* @brief Provides an adaption of OpenWSN led handling
* to RIOTs handling of LEDs and/or GPIOs
*
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*
* @}
*/
#ifndef OPENWSN_LEDS_H
#define OPENWSN_LEDS_H
#include "periph/gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief OpenWSN leds pin configuration
* @{
*/
typedef struct leds_config {
gpio_t error; /**< error led */
gpio_t sync; /**< synchronization state led */
gpio_t radio; /**< radio activity led */
gpio_t debug; /**< debug led */
uint8_t led_on; /**< GPIO set to turn led on */
} leds_config_t;
/** @} */
/**
* @brief Led on state values
*/
enum {
GPIO_LED_LOW = 0,
GPIO_LED_HIGH
};
/**
* Sets the led pins for a specific board for OpenWSN
*
* @param[in] user_config A configuration of GPIO pins used for debugging.
*
* @note Unused pins need to be defined as GPIO_UNDEF
*/
void ledpins_riot_init(const leds_config_t *user_config);
#ifdef __cplusplus
}
#endif
#endif /* OPENWSN_LEDS_H */

View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2017 Hamburg University of Applied Sciences
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_openwsn
* @{
*
* @file
* @brief Default configuration for the OpenWSN leds
*
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
*
* @}
*/
#ifndef OPENWSN_LEDS_PARAMS_H
#define OPENWSN_LEDS_PARAMS_H
#include "board.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name OpenWSN default leds pins configuration.
*
* Default settings match OpenWSN openmote-b configuration.
*
* @note On Nucleo boards the LED pin is shared with SPI -> don't use it!
* @{
*/
#if defined (LED0_PIN) && !defined(MODULE_BOARDS_COMMON_NUCLEO)
#define OPENWSN_LEDPIN_ERROR LED0_PIN
#else
#define OPENWSN_LEDPIN_ERROR GPIO_UNDEF
#endif
#ifdef LED1_PIN
#define OPENWSN_LEDPIN_SYNC LED1_PIN
#else
#define OPENWSN_LEDPIN_SYNC GPIO_UNDEF
#endif
#ifdef LED2_PIN
#define OPENWSN_LEDPIN_RADIO LED2_PIN
#else
#define OPENWSN_LEDPIN_RADIO GPIO_UNDEF
#endif
#ifdef LED3_PIN
#define OPENWSN_LEDPIN_DEBUG LED3_PIN
#else
#define OPENWSN_LEDPIN_DEBUG GPIO_UNDEF
#endif
#ifndef OPENWSN_LED_ON_STATE
#define OPENWSN_LED_ON_STATE GPIO_LED_LOW
#endif
#define OPENWSN_LEDS_DEFAULT { .error = OPENWSN_LEDPIN_ERROR, \
.sync = OPENWSN_LEDPIN_SYNC, \
.radio = OPENWSN_LEDPIN_RADIO, \
.debug = OPENWSN_LEDPIN_DEBUG, \
.led_on = OPENWSN_LED_ON_STATE }
/**@}*/
/**
* @brief OpenWSN leds configuration
*/
static const leds_config_t openwsn_leds_params[] =
{
#ifdef OPENWSN_LEDS_BOARD
OPENWSN_LEDS_BOARD,
#else
OPENWSN_LEDS_DEFAULT,
#endif
};
#ifdef __cplusplus
}
#endif
#endif /* OPENWSN_LEDS_PARAMS_H */

View File

@ -0,0 +1,84 @@
/*
* Copyright (C) 2019 Hamburg University of Applied Sciences
* 2020 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_openwsn
* @{
*
* @file
* @brief RIOT adaption of the "radio" bsp module definitions
*
* The radio adaptation runs in an 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 stats 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), but it might print the following
* error if using `openwsn_serial`:
*
* - `[IEEE802154E] wdDataDuration overflows while at state 19 in slotOffset 0`
*
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>, April 2019
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*
* @}
*/
#ifndef OPENWSN_RADIO_H
#define OPENWSN_RADIO_H
#ifdef __cplusplus
extern "C" {
#endif
#include "net/netdev.h"
#include "radio.h"
/**
* @brief Initialize OpenWSN radio
*
* @param[in] netdev pointer to a netdev interface
*
* @return PID of OpenWSN thread
* @return -1 on initialization error
*/
int openwsn_radio_init(netdev_t *netdev);
/**
* @brief OpenWSN radio variables structure
*/
typedef struct {
radio_capture_cbt startFrame_cb; /**< start of frame capture callback */
radio_capture_cbt endFrame_cb; /**< end of frame capture callback */
netdev_t *dev; /**< netdev device */
} openwsn_radio_t;
#ifdef __cplusplus
}
#endif
#endif /* OPENWSN_RADIO_H */
/** @} */

View File

@ -0,0 +1,141 @@
/*
* Copyright (C) 2018 Hamburg University of Applied Sciences
* 2020 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup pkg_openwsn
* @{
*
* For details on the implementation check pkg/openwsn/doc.txt
*
* @file
* @brief RIOT adaption-specific definition of the "uart" bsp module.
*
* @author Thomas Watteyne <watteyne@eecs.berkeley.edu>, February 2012
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>, July 2018
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*
* @}
*/
#ifndef OPENWSN_UART_H
#define OPENWSN_UART_H
#include "stdint.h"
#include "board.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief UART device to use for STDIO
*
* Dont want to include the stdio header, so redefine the default value
*
*/
#ifndef STDIO_UART_DEV
#define STDIO_UART_DEV (UART_DEV(0))
#endif
/**
* @brief OpenWSN default uart dev.
*
* @note If only one UART is available that uart might collide with
* STDIO_UART_DEV, otherwise the next available UART will be used.
*
*/
#ifndef OPENWSN_UART_DEV
#ifdef MODULE_STDIO_NULL
#define OPENWSN_UART_DEV (STDIO_UART_DEV)
#else
#define OPENWSN_UART_DEV ((STDIO_UART_DEV + 1) % UART_NUMOF)
#endif
#endif
/**
* @brief OpenWSN uart baudrate
*/
#ifndef OPENWSN_UART_BAUDRATE
#ifndef STDIO_UART_BAUDRATE
#define OPENWSN_UART_BAUDRATE (115200U)
#else
#define OPENWSN_UART_BAUDRATE (STDIO_UART_BAUDRATE)
#endif
#endif
/**
* @brief Initialize OpenWSN uart
*
* This will initialize a uart device at STDIO_UART_BAUDRATE/8N1. It will also
* initialize a timer to set sw tx_isr.
*
*/
void uart_init_openwsn(void);
/**
* @brief OpenWSN uart tx callback type
*/
typedef void (*uart_tx_cbt)(void);
/**
* @brief OpenWSN uart rx callback type
*/
typedef void (*uart_rx_cbt)(void);
/**
* @brief OpenWSN uart tx callback type
*/
void uart_setCallbacks(uart_tx_cbt txCb, uart_rx_cbt rxCb);
/**
* @brief Sets software flow control CTS
*
* This function sends XON or XOFF bytes to "set" or "unset" CTS by sw.
*
* @param[in] state true sets CTS, false clears CTS
*/
void uart_setCTS(bool state);
/**
* @brief Write a single byte to the configured OpenWSN uart
*/
void uart_writeByte(uint8_t byteToWrite);
/**
* @brief Reads a single byte received through uart.
*
* OpenWSN will call this thrpugh uart_rx_cbt.
*
*/
uint8_t uart_readByte(void);
/**
* @brief Unused in RIOT, needs to be defined for OpenWSN
*/
void uart_enableInterrupts(void);
/**
* @brief Unused in RIOT, needs to be defined for OpenWSN
*/
void uart_disableInterrupts(void);
/**
* @brief Unused in RIOT, needs to be defined for OpenWSN
*/
void uart_clearRxInterrupts(void);
/**
* @brief Unused in RIOT, needs to be defined for OpenWSN
*/
void uart_clearTxInterrupts(void);
#ifdef __cplusplus
}
#endif
#endif /* OPENWSN_UART_H */

View File

@ -108,6 +108,11 @@ void auto_init(void)
extern void openthread_bootstrap(void);
openthread_bootstrap();
}
if (IS_USED(MODULE_AUTO_INIT_OPENWSN)) {
LOG_DEBUG("Bootstrapping openwsn.\n");
extern void openwsn_bootstrap(void);
openwsn_bootstrap();
}
if (IS_USED(MODULE_GCOAP) &&
!IS_ACTIVE(CONFIG_GCOAP_NO_AUTO_INIT)) {
LOG_DEBUG("Auto init gcoap.\n");

View File

@ -42,6 +42,10 @@ extern "C" {
*/
#define CCM_BLOCK_SIZE 16
/**
* @brief Maximum length for the appended MAC
*/
#define CCM_MAC_MAX_LEN 16
/**
* @brief Encrypt and authenticate data of arbitrary length in ccm mode.

View File

@ -96,6 +96,7 @@ extern "C" {
#define IEEE802154_FRAME_LEN_MAX (127U) /**< maximum 802.15.4 frame length */
#define IEEE802154G_FRAME_LEN_MAX (2047U) /**< maximum 802.15.4g-2012 frame length */
#define IEEE802154_ACK_FRAME_LEN (5U) /**< ACK frame length */
/**
* For the SUN PHYs, the value is 1 ms expressed in symbol periods, rounded

View File

@ -0,0 +1,67 @@
BOARD ?= iotlab-m3
include ../Makefile.tests_common
# list of arm boards that provide at86rf2xx radios, can't require it so
# add whitelist
BOARD_WHITELIST = \
fox \
iotlab-m3 \
iotlab-a8-m3 \
samr21-xpro \
samr30-xpro \
#
## OpenWSN Modules
USEPKG += openwsn
USEMODULE += openwsn_openstack
USEMODULE += openwsn_scheduler
# Optional Module required for root nodes
# USEMODULE += openwsn_serial
# Optional Modules
USEMODULE += openwsn_leds
USEMODULE += openwsn_debugpins
ifneq (,$(filter openwsn_serial,$(USEMODULE)))
# Uncomment to use STDIO_UART_DEV as the uart for OpenWSN openserial
# USEMODULE += stdio_null
ifneq (,$(filter iotlab-m3 iotlab-a8-m3,$(BOARD)))
USEMODULE += stdio_null
endif
# OpenWSN serial module can't handle data at more than 115200 bauds/s,
# depending on the platform this might be even lower (e.g. 57600 bauds
# for iotlab-m3, 19200 for samr21-xpro).
# NOTE: baudrate can'y be changed when testing over IotLab.
OPENSERIAL_BAUD ?=
ifneq (,$(OPENSERIAL_BAUD))
CFLAGS += -DOPENWSN_UART_BAUDRATE=$(OPENSERIAL_BAUD)
ifneq (,$(filter stdio_null,$(USEMODULE)))
BAUD ?= $(OPENSERIAL_BAUD)
endif
endif
endif
# To enable debugging prints on OpenWSN code
# export OPENWSN_LOG_LEVEL ?= LOG_ERROR
## Test application Modules
USEMODULE += ipv6_addr
USEMODULE += ps
USEMODULE += od_string
USEMODULE += shell
USEMODULE += shell_commands
# ztimer is used instead of xtimer because it's a dependency of some
# OpenWSN modules.
USEMODULE += ztimer_usec
include $(RIOTBASE)/Makefile.include
# We want the highest possible frequency set for periph_rtt, but not all
# platforms can configure this value. use highest possible RTT_FREQUENCY
# for platforms that allow it
ifneq (,$(filter stm32,$(CPU)))
RTT_FREQUENCY ?= RTT_MAX_FREQUENCY
CFLAGS += -DRTT_FREQUENCY=$(RTT_FREQUENCY)
endif

316
tests/pkg_openwsn/README.md Normal file
View File

@ -0,0 +1,316 @@
# OpenWSN on RIOT
This test demonstrates the [OpenWSN](https://github.com/openwsn-berkeley/openwsn-fw) full
stack (UDP, IPv6, RPL, 6TiSCH) running on RIOT. When flashed, it will initialize the stack
and provide the user with some minimal shell commands to:
- print the own IPv6 address `ifconfig`
- change the UDP destination port `udp server start <port>`
- send a UDP packet `udp send <addr> <port> <data>`
A tun interface can also be set to allow traffic out of the network. This
allows pinging nodes in the network from the host.
Please note that this port is still in experimental status. For further information
about the port refer to the [pkg documentation](../../pkg/openwsn/doc.txt).
## Experimental setups
The following setup act as a starting point for testing and debugging. Either
build and flash local nodes or incorporate the [FIT IoT-LAB](https://www.iot-lab.info/)
Testbed. Please check the ports [documentation](../../pkg/openwsn/doc.txt) for information
about supported hardware platforms.
This port currently needs `openvisualizer`, please make sure you follow the
[pre-requisites](../../dist/tools/openvisualizer/makefile.openvisualizer.inc.mk)
to install a patched version of `openvisualizer`.
An OpenWSN root node acts as a border router, it tunnels IEEE80215.4.E
between `openvisualizer` and the network without looking at the frame content.
This means that a root node will not be reachable when 'pinging' from the host
or if trying to send udp packets to it.
Two test cases are described with different requirements:
1. From host [ping](#Communicating-with-host-(IPV6)) nodes in network over ipv6:
```
- 1 root node + tun interface
- +1 leaf node
```
2. Sending [UDP](#Communicating-over-udp) packets between nodes:
```
- 1 root node
- +2 leaf node
```
### Synchronization
There are 3 things that can be done to speed up and help nodes synchronization,
all related to how often a node will receive an EB (enhanced beacon).
- disable channel hopping
- increase EB_PORTION
- reduce SLOTFRAME_LENGTH
If you want to disable channel hopping to speed up synchronization you can set
a fix channel.
$ export CFLAGS=-DIEEE802154E_SINGLE_CHANNEL=17
Enhanced beacon are sent once per slotframe with a likelihood of 1/EB_PORTION.
The default value is EB_PORTION = 10 (10%), reducing the value of EB_PORTION
increases the frequency at which beacons are sent, this will also help nodes
to stay synchronized.
$ export CFLAGS=-DEB_PORTION=4
EB gets an opportunity to be sent (depending on 1/EB_PORTION) once in every
slotframe, reducing the soltframe length will cause EB to be sent more often.
Beware that this could have an impact on the MSF (Minimal Scheduling Function).
$ export CFLAGS=-DSLOTFRAME_LENGTH=51
See [documentation](../../pkg/openwsn/doc.txt#Synchronization) for more on
synchronization.
## IMPORTANT!
OpenWSN uses source routing and this means all network traffic must go through
from the root node to OpenVisualizer. If the root node configuration can't
handle the configured baudrate correctly this will lead to packet loss.
Currently these are the tested configurations:
(a) samr21-xpro network:
- leaf nodes using `openwsn_sctimer_rtt`
- root node using `openwsn_sctimer_rtt` and 19200 baudrate directly
connected to UART pins (not through the usb debugger)
(b) iotlab-m3 network:
- leaf nodes using `openwsn_sctimer_rtt` or `sctimer_ztimer`
- root node using `openwsn_sctimer_rtt` and 57600 baudrate
For more details on this please refer to [pkg documentation](../../pkg/openwsn/doc.txt).
## Testing configuration (a) (three local samr21-xpro nodes)
1. figure out each node's serial number (`edbg -l` output might help)
2. hook up the root node to an external USB-serial-converter. For that, connect
the converter's RXD/TXD pins to the node's PA22/PA23.
3. flash the root node:
$ SERIAL=${ROOT_SERIAL_NODE} OPENSERIAL_BAUD=19200 USEMODULE=openwsn_serial \
BOARD=samr21-xpro make flash -j4
4. flash the leaf nodes:
$ BOARD=samr21-xpro make all -j4
$ BOARD=samr21-xpro SERIAL=${LEAF_SERIAL_NODE0} make flash-only
$ BOARD=samr21-xpro SERIAL=${LEAF_SERIAL_NODE1} make flash-only
5. open a shell to the leaf nodes
so in two shell windows, do (one in each):
$ BOARD=samr21-xpro SERIAL=${LEAF_SERIAL_NODE0} make term
$ BOARD=samr21-xpro SERIAL=${LEAF_SERIAL_NODE1} make term
6. in a third shell, launch openvisualizer:
$ BOARD=samr21-xpro PORT=<USB-serial-port, e.g., /dev/ttyUSB0> BAUD=19200 \
make openv-termroot
## Testing configuration (b) (iotlab-m3 network on iotlab)
When using OpenVisualizer over iot-lab an ssh-tunnel is opened to connect to the
IoT-LAB motes' TCP port. For this to work you will need:
- A valid IoT-LAB [account](https://www.iot-lab.info/testbed/signup)
- Authenticate locally to IoT-LAB `$ iotlab-auth -u <login>`
1. Launch an experiment booking 3+ `iotlab-m3` nodes:
$ iotlab-experiment submit -d 120 -l 3,archi=m3:at86rf231+site=saclay
$ iotlab-experiment wait
$ iotlab-experiment get --nodes
Since multiple nodes where configured for the experiment `IOTLAB_NODE` needs
to be specified for every node, `IOTLAB_NODE=m3-%.saclay.iot-lab.info`
2. flash the root node
$ IOTLAB_NODE=${ROOT_IOTLAB_NODE} USEMODULE=openwsn_serial \
BOARD=iotlab-m3 make -C tests/pkg_openwsn flash
3. open a shell to the leaf nodes so in two shell windows, do (one in each):
$ BOARD=iotlab-m3 make -C tests/pkg_openwsn all -j4
$ BOARD=iotlab-m3 IOTLAB_NODE=${LEAF_IOTLAB_NODE0} make -C tests/pkg_openwsn flash-only
$ BOARD=iotlab-m3 IOTLAB_NODE=${LEAF_IOTLAB_NODE1} make -C tests/pkg_openwsn flash-only
4. open a shell to the leaf nodes
so in two shell windows, do (one in each):
$ BOARD=iotlab-m3 IOTLAB_NODE=${LEAF_IOTLAB_NODE0} make -C tests/pkg_openwsn term
$ BOARD=iotlab-m3 IOTLAB_NODE=${LEAF_IOTLAB_NODE1} make -C tests/pkg_openwsn term
5. in a third shell, launch openvisualizer:
$ BOARD=iotlab-m3 IOTLAB_NODE=${ROOT_IOTLAB_NODE} make -C tests/pkg_openwsn openv-termroot
### Network Setup
If (a) and (b) where followed then on each lead node you should be able
to see the ipv6 address:
main(): This is RIOT! (Version: 2020.04-devel-1649-g96fa9-pr_openwsn)
OpenWSN UDP test
You are running RIOT on a(n) iotlab-m3 board.
This board features a(n) stm32f1 MCU.
> ifconfig
ifconfig
Iface 3 HWaddr: 07:C2 NID: CA:FE
Long HWaddr: 96:35:9A:92:4E:3D:65:78
inet6 addr: fe80::9635:9a92:4e3d:6578
IEEE802154E sync: 0
6TiSCH joined: 0
NO RPL parent
On the root node Openvisualizer is launched and the DAGroot is setup.
[OpenVisualizerServer:INFO] Extracting firmware definitions.
[Utils:VERBOSE] Extracting firmware component names
[Utils:VERBOSE] Extracting firmware log descriptions.
[Utils:VERBOSE] Extracting 6top return codes.
[Utils:VERBOSE] Extracting 6top states.
[OpenVisualizerServer:INFO] Starting RPC server
[OpenVisualizerServer:SUCCESS] Setting mote 43eb as root
[ParserIEC:ERROR] 43eb [IEEE802154E] wrong state 1 in startSlot, at slotOffset 1
[RPL:INFO] registering DAGroot 82-6b-de-ec-58-34-65-78
The root node will now start sending beacons and other nodes will synchronize, and
join. If channel hopping is enabled this can take quite some time (see
[Synchronization](../../pkg/openwsn/doc.txt#Synchronization). Once leaf nodes
have joined the network when issuing `ifconfing` you should see:
ifconfig
Iface 3 HWaddr: 0F:F4 NID: CA:FE
Long HWaddr: 06:84:F6:65:10:6B:11:14
inet6 addr: bbbb::684:f665:106b:1114
IEEE802154E sync: 1
6TiSCH joined: 1
RPL rank: 2816
RPL parent: 2A:BA:F7:65:10:6B:11:14
RPL children:
RPL DODAG ID: bbbb::2aba:f765:106b:1114
The root node should start receiving RPL DAOs:
[RPL:INFO] received RPL DAO from bbbb:0:0:0:ab8:fc65:106b:1114
- parents:
bbbb:0:0:0:2aba:f765:106b:1114
- children:
bbbb:0:0:0:684:f665:106b:1114
[RPL:INFO] received RPL DAO from bbbb:0:0:0:684:f665:106b:1114
- parents:
bbbb:0:0:0:2aba:f765:106b:1114
- children:
bbbb:0:0:0:ab8:fc65:106b:1114
[RPL:INFO] received RPL DAO from bbbb:0:0:0:684:f665:106b:1114
- parents:
bbbb:0:0:0:2aba:f765:106b:1114
- children:
bbbb:0:0:0:ab8:fc65:106b:1114
Once DAOs for all nodes start being received the network is setup and you
should be able to send packets between nodes or ping from the host.
### Communicating over udp
On one node setup a udp-server:
> ifconfig
ifconfig
Iface 3 HWaddr: 0F:F4 NID: CA:FE
Long HWaddr: 06:84:F6:65:10:6B:11:14
inet6 addr: bbbb::684:f665:106b:1114
IEEE802154E sync: 1
6TiSCH joined: 1
RPL rank: 2816
RPL parent: 2A:BA:F7:65:10:6B:11:14
RPL children:
RPL DODAG ID: bbbb::2aba:f765:106b:1114
> udp server start 3000
udp server start 3000
Set UDP server port to 3000
On the other node send udp messages
> udp send bbbb::684:f665:106b:1114 3000 hello
udp send bbbb::684:f665:106b:1114 3000 hello
Send 5 byte over UDP to [bbbb::684:f665:106b:1114]:3000
> msg.l2_sendDoneError: 0
Send success
The first node should receive the message
> Received 12 bytes on port 3000
00000000 A6 28 00 00 00 02 00 68 65 6C 6C 6F .(.....hello
### Communicating with host (IPV6)
OpenVisualizer can set up a tun interface to communicate with the host computer.
This will require starting `OpenVisualizer` with root privileges. The only
difference with the previous setup is that the root node must be setup as
follows:
on iotlab:
$ IOTLAB_NODE=${ROOT_IOTLAB_NODE} BOARD=iotlab-m3 \
make -C tests/pkg_openwsn openv-termtun
on local boards:
$ PORT=<USB-serial-port, e.g., /dev/ttyUSB0> BOARD=samr21-xpro \
make -C tests/pkg_openwsn openv-termtun
Once DAOs are received you can ping nodes in the network from your host:
```
$ ping6 -s 40 -i 5 bbbb:0:0:0:2ab5:fc65:106b:1114
PING bbbb:0:0:0:2ab5:fc65:106b:1114(bbbb::2ab5:fc65:106b:1114) 40 data bytes
48 bytes from bbbb::2ab5:fc65:106b:1114: icmp_seq=1 ttl=64 time=1064 ms
48 bytes from bbbb::2ab5:fc65:106b:1114: icmp_seq=2 ttl=64 time=2111 ms
48 bytes from bbbb::2ab5:fc65:106b:1114: icmp_seq=3 ttl=64 time=1141 ms
48 bytes from bbbb::2ab5:fc65:106b:1114: icmp_seq=4 ttl=64 time=2197 ms
48 bytes from bbbb::2ab5:fc65:106b:1114: icmp_seq=5 ttl=64 time=1228 ms
48 bytes from bbbb::2ab5:fc65:106b:1114: icmp_seq=6 ttl=64 time=2306 ms
48 bytes from bbbb::2ab5:fc65:106b:1114: icmp_seq=7 ttl=64 time=1324 ms
```
If openserial is used on the leaf node you would get the following output:
```
16:02:38 [ParserIEC:INFO] 768f [ICMPv6ECHO] received an echo request
16:02:44 [ParserIEC:INFO] 768f [ICMPv6ECHO] received an echo request
16:02:48 [ParserIEC:INFO] 768f [ICMPv6ECHO] received an echo request
16:02:54 [ParserIEC:INFO] 768f [ICMPv6ECHO] received an echo request
```
Some considerations:
- Nodes duty cycle is ~0.5%, so nodes get a chance to transmit roughly every
2s, so the worst case scenario is ~4s RTT. This is increased for big payloads
since it will lead to fragmentation.
- If incoming packet rate is too fast the internal packet queue can be
be overloaded.

324
tests/pkg_openwsn/main.c Normal file
View File

@ -0,0 +1,324 @@
/*
* Copyright (C) 2017 Hamburg University of Applied Sciences
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief OpenWSN test application
*
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
*
* @}
*/
#include <stdio.h>
#include <stdlib.h>
#include "od.h"
#include "fmt.h"
#include "shell.h"
#include "openwsn.h"
#include "net/ipv6/addr.h"
#include "opendefs.h"
#include "scheduler.h"
#include "02a-MAClow/IEEE802154E.h"
#include "02b-MAChigh/neighbors.h"
#include "03b-IPv6/icmpv6rpl.h"
#include "04-TRAN/openudp.h"
#include "cjoin.h"
#include "cross-layers/openqueue.h"
#include "cross-layers/idmanager.h"
#include "cross-layers/packetfunctions.h"
extern idmanager_vars_t idmanager_vars;
extern icmpv6rpl_vars_t icmpv6rpl_vars;
extern neighbors_vars_t neighbors_vars;
extern openqueue_vars_t openqueue_vars;
extern schedule_vars_t schedule_vars;
extern scheduler_dbg_t scheduler_dbg;
udp_resource_desc_t uinject_vars;
char addr_str[IPV6_ADDR_MAX_STR_LEN];
void uinject_sendDone(OpenQueueEntry_t *msg, owerror_t error)
{
(void)error;
printf("msg.l2_sendDoneError: %x\n", msg->l2_sendDoneError);
openqueue_freePacketBuffer(msg);
puts("Send success");
}
void uinject_receive(OpenQueueEntry_t *pkt)
{
printf("Received %i bytes on port %i\n", (int)pkt->length,
pkt->l4_destination_port);
od_hex_dump(pkt->payload, pkt->length, OD_WIDTH_DEFAULT);
openqueue_freePacketBuffer(pkt);
}
void uinject_init(void)
{
uinject_vars.port = WKP_UDP_INJECT;
uinject_vars.callbackReceive = &uinject_receive;
uinject_vars.callbackSendDone = &uinject_sendDone;
openudp_register(&uinject_vars);
}
char *_array_2_string(const uint8_t *addr, size_t addr_len, char *out)
{
char *res = out;
assert((out != NULL) && ((addr != NULL) || (addr_len == 0U)));
out[0] = '\0';
for (size_t i = 0; i < addr_len; i++) {
out += fmt_byte_hex((out), *(addr++));
*(out++) = (i == (addr_len - 1)) ? '\0' : ':';
}
return res;
}
static int ifconfig_cmd(int argc, char **argv)
{
(void)argc;
(void)argv;
open_addr_t *hwaddr;
open_addr_t temp_my128bID;
memcpy(&temp_my128bID.addr_128b[0], &idmanager_vars.myPrefix.prefix, 8);
memcpy(&temp_my128bID.addr_128b[8], &idmanager_vars.my64bID.addr_64b, 8);
printf("Iface %d ", openwsn_get_pid());
hwaddr = idmanager_getMyID(ADDR_16B);
printf("\tHWaddr: %s ", _array_2_string(hwaddr->addr_16b, 2, addr_str));
hwaddr = idmanager_getMyID(ADDR_PANID);
printf("NID: %s\n", _array_2_string(hwaddr->panid, 2, addr_str));
printf("\n");
hwaddr = idmanager_getMyID(ADDR_64B);
printf("\t\tLong HWaddr: %s\n", _array_2_string(hwaddr->addr_64b, 8, addr_str));
ipv6_addr_to_str(addr_str, (ipv6_addr_t *)temp_my128bID.addr_128b,
sizeof(addr_str));
printf("\t\tinet6 addr: %s\n", addr_str);
printf("\n");
printf("\t\tIEEE802154E sync: %i\n", ieee154e_isSynch());
printf("\t\t6TiSCH joined: %i\n", cjoin_getIsJoined());
printf("\n");
if (idmanager_vars.isDAGroot) {
puts("\t\tNode is DAG root");
}
else {
if (icmpv6rpl_vars.haveParent) {
printf("\t\tRPL rank: %i\n", icmpv6rpl_vars.myDAGrank);
printf("\t\tRPL parent: %s\n", \
_array_2_string(neighbors_vars.neighbors[icmpv6rpl_vars.
ParentIndex].
addr_64b.addr_64b, 8, addr_str));
printf("\t\tRPL children:\n");
for (uint8_t i = 0; i < MAXNUMNEIGHBORS; i++) {
if ((neighbors_isNeighborWithHigherDAGrank(i)) == true) {
printf("\t\t\t%s\n", \
_array_2_string(neighbors_vars.neighbors[i].
addr_64b.addr_64b, 8, addr_str));
}
}
ipv6_addr_to_str(addr_str,
(ipv6_addr_t *)icmpv6rpl_vars.dao.DODAGID,
sizeof(addr_str));
printf("\t\tRPL DODAG ID: %16s\n", addr_str);
}
else {
puts("\t\tNO RPL parent");
}
}
return 0;
}
static int nc_cmd(int argc, char **argv)
{
(void)argc;
(void)argv;
for (int i = 0; i < MAXNUMNEIGHBORS; i++) {
_array_2_string(neighbors_vars.neighbors[i].addr_64b.addr_64b, 8,
addr_str);
if (memcmp(addr_str, "00:00:00:00:00:00:00:00", 8) != 0) {
printf("%02i. %s\n", i, addr_str);
}
}
return 0;
}
static const struct {
char *name;
int id;
} names[] = {
{ "?", COMPONENT_NULL },
{ "owsn", COMPONENT_OPENWSN },
{ "idmanager", COMPONENT_IDMANAGER },
{ "oqueue", COMPONENT_OPENQUEUE },
{ "oserial", COMPONENT_OPENSERIAL },
{ "pktfuncs", COMPONENT_PACKETFUNCTIONS },
{ "random", COMPONENT_RANDOM },
{ "radio", COMPONENT_RADIO },
{ "154", COMPONENT_IEEE802154 },
{ "154e", COMPONENT_IEEE802154E },
{ "6top2154e", COMPONENT_SIXTOP_TO_IEEE802154E },
{ "154e26top", COMPONENT_IEEE802154E_TO_SIXTOP },
{ "6top", COMPONENT_SIXTOP },
{ "neigh", COMPONENT_NEIGHBORS },
{ "sched", COMPONENT_SCHEDULE },
{ "6topres", COMPONENT_SIXTOP_RES },
{ "bridge", COMPONENT_OPENBRIDGE },
{ "iphc", COMPONENT_IPHC },
{ "frag", COMPONENT_FRAG },
{ "fwd", COMPONENT_FORWARDING },
{ "icmpv6", COMPONENT_ICMPv6 },
{ "icmpv6ech", COMPONENT_ICMPv6ECHO },
{ "icmpv6rtr", COMPONENT_ICMPv6ROUTER },
{ "icmpv6rpl", COMPONENT_ICMPv6RPL },
{ "udp", COMPONENT_OPENUDP },
{ "coap", COMPONENT_OPENCOAP },
{ "cjoin", COMPONENT_CJOIN },
{ "openoscoap", COMPONENT_OPENOSCOAP },
{ "c6t", COMPONENT_C6T },
{ "uinject", COMPONENT_UINJECT },
};
char *_get_name(int id)
{
for (unsigned i = 0; i < ARRAY_SIZE(names); i++) {
if (id == names[i].id) {
return names[i].name;
}
}
return NULL;
}
static int q_cmd(int argc, char **argv)
{
(void)argc;
(void)argv;
bool queue = 1;
for (uint8_t i = 0; i < QUEUELENGTH; i++) {
if (openqueue_vars.queue[i].creator || openqueue_vars.queue[i].owner) {
queue = 0;
uint8_t creator = openqueue_vars.queue[i].creator;
uint8_t owner = openqueue_vars.queue[i].owner;
printf("Creator: %.9s [%d], ", _get_name(creator), creator);
printf("Owner: %.9s [%d]\n", _get_name(owner), owner);
}
}
if (queue) {
puts("openqueue empty");
}
return 0;
}
static int q_rmv(int argc, char **argv)
{
if (argc < 2) {
printf("usage: %s [creator]\n", argv[0]);
return 1;
}
uint8_t creator = atoi(argv[1]);
if (creator == 0) {
printf("error: invalid input value\n");
return 1;
}
else {
printf("Removing entries created by: %.9s [%d]\n", _get_name(creator), creator);
openqueue_removeAllCreatedBy(creator);
}
return 0;
}
static int as_cmd(int argc, char **argv)
{
(void)argc;
(void)argv;
for (int i = 0; i < MAXACTIVESLOTS; i++) {
switch (schedule_vars.scheduleBuf[i].type) {
case CELLTYPE_TX:
printf("neigh: %s, slot: %03i, chan: %02i, type: TX\n", \
_array_2_string(
schedule_vars.scheduleBuf[i].neighbor.addr_64b,
8, addr_str),
schedule_vars.scheduleBuf[i].slotOffset, \
schedule_vars.scheduleBuf[i].channelOffset);
break;
case CELLTYPE_RX:
printf("slot: %03i, chan: %02i, type: RX\n", \
schedule_vars.scheduleBuf[i].slotOffset, \
schedule_vars.scheduleBuf[i].channelOffset);
break;
case CELLTYPE_TXRX:
printf("neigh: %s, slot: %03i, chan: %02i, type: RXTX\n", \
_array_2_string(
schedule_vars.scheduleBuf[i].neighbor.addr_64b,
8, addr_str),
schedule_vars.scheduleBuf[i].slotOffset, \
schedule_vars.scheduleBuf[i].channelOffset);
break;
default:
break;
}
}
return 0;
}
static int sc_cmd(int argc, char **argv)
{
(void)argc;
(void)argv;
/* TODO allow other prefixes via shell ?!? */
printf("Current tasks:%i\n", scheduler_dbg.numTasksCur);
printf("Max tasks: %i\n", scheduler_dbg.numTasksMax);
return 0;
}
extern int udp_cmd(int argc, char **argv);
static const shell_command_t shell_commands[] = {
{ "ifconfig", "Shows assigned IPv6 addresses", ifconfig_cmd },
{ "nc", "Shows neighbor table", nc_cmd },
{ "q", "Shows Openqueue", q_cmd },
{ "q-rmv", "Remove entries from creator in queue", q_rmv },
{ "as", "Shows active cells", as_cmd },
{ "sc", "Shows scheduler (openos) dbg states", sc_cmd },
{ "udp", "Send data over UDP and listen on UDP ports", udp_cmd },
{ NULL, NULL, NULL }
};
int main(void)
{
puts("OpenWSN UDP test");
printf("You are running RIOT on a(n) %s board.\n", RIOT_BOARD);
printf("This board features a(n) %s MCU.\n", RIOT_MCU);
uinject_init();
char line_buf[SHELL_DEFAULT_BUFSIZE];
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
}

195
tests/pkg_openwsn/udp.c Normal file
View File

@ -0,0 +1,195 @@
/*
* Copyright (C) 2015 Freie Universität Berlin
* Copyright (C) 2018 Hamburg University of Applied Sciences
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup tests
* @{
*
* @file
*
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
* @}
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "ztimer.h"
#include "net/ipv6.h"
#include "opendefs.h"
#include "scheduler.h"
#include "02a-MAClow/IEEE802154E.h"
#include "03b-IPv6/icmpv6rpl.h"
#include "04-TRAN/openudp.h"
#include "cross-layers/openqueue.h"
#include "cross-layers/idmanager.h"
#include "cross-layers/packetfunctions.h"
extern udp_resource_desc_t uinject_vars;
extern idmanager_vars_t idmanager_vars;
extern openudp_vars_t openudp_vars;
static uint16_t counter = 0;
OpenQueueEntry_t *pkt;
void push_pkt_cb(void){
owerror_t ret = openudp_send(pkt);
if (ret == E_FAIL) {
puts("could not send");
openqueue_freePacketBuffer(pkt);
}
}
static int udp_send(char *addr_str, char *port_str, char *data,
unsigned int num, unsigned int delay)
{
size_t data_len;
open_addr_t parentNeighbor;
ipv6_addr_t addr;
data_len = strlen(data);
uint8_t asnArray[data_len];
/* parse destination address */
if (ipv6_addr_from_str(&addr, addr_str) == NULL) {
puts("Error: unable to parse destination address, exit");
return 1;
}
for (unsigned int i = 0; i < num; i++) {
printf("Send %u byte over UDP to [%s]:%s\n",
(unsigned)data_len, addr_str, port_str);
/* don't run if not in synch */
if (ieee154e_isSynch() == FALSE) {
puts("Error: Node not in sync, exit");
return 1;
}
/* don't run on dagroot */
if (idmanager_getIsDAGroot()) {
puts("Error: Node is DAGROOT, exit");
return 1;
}
bool foundNeighbor = icmpv6rpl_getPreferredParentEui64(&parentNeighbor);
if (foundNeighbor==FALSE) {
puts("Error: No preferred parent EUI64, exit");
return 1;
}
/* get a free packet buffer */
pkt = openqueue_getFreePacketBuffer(COMPONENT_UINJECT);
if (pkt == NULL) {
puts("Error: could not create packet buffer, exit");
return 1;
}
pkt->owner = COMPONENT_UINJECT;
pkt->creator = COMPONENT_UINJECT;
pkt->l4_protocol = IANA_UDP;
pkt->l4_destination_port = atoi(port_str);
pkt->l4_sourcePortORicmpv6Type = uinject_vars.port;
pkt->l3_destinationAdd.type = ADDR_128B;
memcpy(&pkt->l3_destinationAdd.addr_128b[0], (void *)&addr, 16);
/* add payload */
packetfunctions_reserveHeaderSize(pkt, data_len);
memcpy(&pkt->payload[0], data, data_len);
packetfunctions_reserveHeaderSize(pkt, sizeof(uint16_t));
pkt->payload[1] = (uint8_t)((counter & 0xff00) >> 8);
pkt->payload[0] = (uint8_t)(counter & 0x00ff);
counter++;
packetfunctions_reserveHeaderSize(pkt, sizeof(asn_t));
ieee154e_getAsn(asnArray);
pkt->payload[0] = asnArray[0];
pkt->payload[1] = asnArray[1];
pkt->payload[2] = asnArray[2];
pkt->payload[3] = asnArray[3];
pkt->payload[4] = asnArray[4];
scheduler_push_task(push_pkt_cb, TASKPRIO_COAP);
ztimer_sleep(ZTIMER_USEC, delay);
}
return 0;
}
int udp_cmd(int argc, char **argv)
{
if (argc < 2) {
printf("usage: %s [send|server]\n", argv[0]);
return 1;
}
if (strcmp(argv[1], "send") == 0) {
uint32_t num = 1;
uint32_t delay = 1000000LU;
/* don't send as root */
if (idmanager_vars.isDAGroot) {
puts("Error: Node is root, exit");
return 1;
}
if (argc < 5) {
printf("usage: %s send <addr> <port> <hex data> [<num> [<delay in us>]]\n",
argv[0]);
return 1;
}
if (argc > 5) {
num = atoi(argv[5]);
}
if (argc > 6) {
delay = atoi(argv[6]);
}
return udp_send(argv[2], argv[3], argv[4], num, delay);
}
else if (strcmp(argv[1], "server") == 0) {
if (argc < 3) {
printf("usage: %s server [start|list]\n", argv[0]);
return 1;
}
if (strcmp(argv[2], "start") == 0) {
if (argc < 4) {
printf("usage %s server start <port>\n", argv[0]);
return 1;
}
uint16_t port = atoi(argv[3]);
uinject_vars.port = port;
printf("Set UDP server port to %" PRIu16 "\n", port);
return 0;
}
else if (strcmp(argv[2], "list") == 0) {
udp_resource_desc_t* resource = openudp_vars.resources;
printf("Open UDP Ports: ");
while (NULL != resource) {
printf("%i ", resource->port);
resource = resource->next;
}
puts("");
}
else {
puts("error: invalid command");
return 1;
}
}
else {
puts("error: invalid command");
return 1;
}
return 1;
}
/** @} */