Merge pull request #16364 from haukepetersen/add_nimble_rpble_pr
net/BLE: add support for RPL-over-BLE
This commit is contained in:
commit
be6115447e
@ -86,6 +86,9 @@ nimble_autoconn:
|
|||||||
nimble_netif:
|
nimble_netif:
|
||||||
$(QQ)"$(MAKE)" -C $(TDIR)/netif/
|
$(QQ)"$(MAKE)" -C $(TDIR)/netif/
|
||||||
|
|
||||||
|
nimble_rpble:
|
||||||
|
$(QQ)"$(MAKE)" -C $(TDIR)/rpble/
|
||||||
|
|
||||||
nimble_scanlist:
|
nimble_scanlist:
|
||||||
$(QQ)"$(MAKE)" -C $(TDIR)/scanlist
|
$(QQ)"$(MAKE)" -C $(TDIR)/scanlist
|
||||||
|
|
||||||
|
|||||||
@ -67,6 +67,13 @@ ifneq (,$(filter nimble_autoconn,$(USEMODULE)))
|
|||||||
USEMODULE += bluetil_ad
|
USEMODULE += bluetil_ad
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(filter nimble_rpble,$(USEMODULE)))
|
||||||
|
USEMODULE += gnrc_rpl
|
||||||
|
USEMODULE += nimble_netif
|
||||||
|
USEMODULE += nimble_scanner
|
||||||
|
USEMODULE += bluetil_ad
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq (,$(filter nimble_scanlist,$(USEMODULE)))
|
ifneq (,$(filter nimble_scanlist,$(USEMODULE)))
|
||||||
USEMODULE += nimble_addr
|
USEMODULE += nimble_addr
|
||||||
USEMODULE += bluetil_ad
|
USEMODULE += bluetil_ad
|
||||||
|
|||||||
@ -124,6 +124,9 @@ ifneq (,$(filter nimble_netif,$(USEMODULE)))
|
|||||||
CFLAGS += -DMYNEWT_VAL_BLE_LL_CFG_FEAT_DATA_LEN_EXT=1
|
CFLAGS += -DMYNEWT_VAL_BLE_LL_CFG_FEAT_DATA_LEN_EXT=1
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
ifneq (,$(filter nimble_rpble,$(USEMODULE)))
|
||||||
|
INCLUDES += -I$(RIOTPKG)/nimble/rpble/include
|
||||||
|
endif
|
||||||
ifneq (,$(filter nimble_scanlist,$(USEMODULE)))
|
ifneq (,$(filter nimble_scanlist,$(USEMODULE)))
|
||||||
INCLUDES += -I$(RIOTPKG)/nimble/scanlist/include
|
INCLUDES += -I$(RIOTPKG)/nimble/scanlist/include
|
||||||
endif
|
endif
|
||||||
|
|||||||
@ -52,6 +52,11 @@
|
|||||||
#endif
|
#endif
|
||||||
#include "controller/ble_ll.h"
|
#include "controller/ble_ll.h"
|
||||||
|
|
||||||
|
#ifdef MODULE_NIMBLE_RPBLE
|
||||||
|
#include "nimble_rpble.h"
|
||||||
|
#include "nimble_rpble_params.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
static char _stack_controller[NIMBLE_CONTROLLER_STACKSIZE];
|
static char _stack_controller[NIMBLE_CONTROLLER_STACKSIZE];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -160,4 +165,9 @@ void nimble_riot_init(void)
|
|||||||
extern void nimble_autoadv_init(void);
|
extern void nimble_autoadv_init(void);
|
||||||
nimble_autoadv_init();
|
nimble_autoadv_init();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef MODULE_NIMBLE_RPBLE
|
||||||
|
res = nimble_rpble_init(&nimble_rpble_params);
|
||||||
|
assert(res == 0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
3
pkg/nimble/rpble/Makefile
Normal file
3
pkg/nimble/rpble/Makefile
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
MODULE = nimble_rpble
|
||||||
|
|
||||||
|
include $(RIOTBASE)/Makefile.base
|
||||||
181
pkg/nimble/rpble/include/nimble_rpble.h
Normal file
181
pkg/nimble/rpble/include/nimble_rpble.h
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019-2021 Freie Universität Berlin
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup pkg_nimble_rpble RPL-over-BLE for NimBLE
|
||||||
|
* @ingroup pkg_nimble
|
||||||
|
* @brief RPL-over-BLE for Nimble implementation
|
||||||
|
*
|
||||||
|
* # About
|
||||||
|
* This module implements a BLE connection manager the manages BLE connections
|
||||||
|
* for (multi-hop) IP over BLE networks based on meta data provided by RPL.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* # Concept
|
||||||
|
* In their initial state, after power up or reboot, nodes start to scan for
|
||||||
|
* BLE advertisements containing a specific advertising data (AD) field which
|
||||||
|
* holds information about RPL DODAGs. For a configured amount of time, the node
|
||||||
|
* ranks all senders of the received advertising packets based on a given
|
||||||
|
* metric. After this time, the node selects the best fitting parent based on
|
||||||
|
* this ranking and tries to connect to that peer.
|
||||||
|
*
|
||||||
|
* After a node has successfully opened a connection to its parent node, the
|
||||||
|
* node starts to advertise its own RPL context data to accept connections
|
||||||
|
* from potential child nodes.
|
||||||
|
*
|
||||||
|
* This approach leads to a BLE network topology that is equal to the IP routing
|
||||||
|
* topology created by RPL on top.
|
||||||
|
*
|
||||||
|
* ## Advertising data structure
|
||||||
|
* To include RPL context information into (legacy) BLE advertisements, it must
|
||||||
|
* be encoded into the BLE advertising data format. This implementation uses
|
||||||
|
* a custom sub-format that is included into the 16-bit UUID Service Data
|
||||||
|
* (type: 0x16) field (Supplement to Bluetooth Core Specification CSSv8, 1.11).
|
||||||
|
* The 16-bit UUID in this field is set to the IPSS service ID (0x1820).
|
||||||
|
*
|
||||||
|
* The following sub-format is used to encode the RPL context data:
|
||||||
|
* ```
|
||||||
|
* byte 1: instance ID (1b)
|
||||||
|
* byte 2-17: DODAG ID (16b)
|
||||||
|
* byte 18: DODAG version (1b)
|
||||||
|
* byte 19: RPL role (1b)
|
||||||
|
* byte 20-21: rank (2b)
|
||||||
|
* byte 22: number of free BLE connection slots (1b)
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* ## Ranking of potential parents
|
||||||
|
* The currently implemented ranking metric is very simple: the potential parent
|
||||||
|
* node with the smallest rank is chosen. Additionally, nodes advertise the
|
||||||
|
* number of free BLE connection slots. In case where multiple nodes advertise
|
||||||
|
* the same RPL rank, the one with the largest number of open BLE connection
|
||||||
|
* slots is selected. The idea behind this is to balance the number of BLE
|
||||||
|
* connections per node, and with that also to balance the RPL DODAG.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* # Usage
|
||||||
|
* To use this module, simply include `nimble_rpble` into your build. If nothing
|
||||||
|
* is explicitly configured, the module will use the default configuration as
|
||||||
|
* specified in `pkg/nimble/rpble/include/nimble_rpble_params.h`.
|
||||||
|
*
|
||||||
|
* Once a node is configured as RPL root (e.g. using the `rpl root ..` shell
|
||||||
|
* command), it will automatically start to advertise itself. Non-RPL-root nodes
|
||||||
|
* will automatically scan for potential parent nodes and join the network as
|
||||||
|
* soon as they find fitting neighbors.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ## Implementation status
|
||||||
|
* In its current state, the implementation only works for environments where a
|
||||||
|
* single RPL network with a single DODAG are present. The DODAG ID, instance
|
||||||
|
* ID, and DODAG version are currently pretty much ignored when scanning for
|
||||||
|
* potential parents.
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief Interface for the nimble_rpble module
|
||||||
|
*
|
||||||
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NIMBLE_RPBLE_H
|
||||||
|
#define NIMBLE_RPBLE_H
|
||||||
|
|
||||||
|
#include "nimble_netif.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief rpble configuration parameters
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint32_t scan_itvl_ms; /**< scan interval when scanning for parents,
|
||||||
|
* in ms */
|
||||||
|
uint32_t scan_win_ms; /**< scan window when scanning for parents,
|
||||||
|
* in ms */
|
||||||
|
uint32_t adv_itvl_ms; /**< advertising interval used when advertising
|
||||||
|
* RPL context to child nodes, in ms */
|
||||||
|
uint32_t conn_scan_itvl_ms; /**< scan interval when connecting to parent,
|
||||||
|
* in ms */
|
||||||
|
uint32_t conn_scan_win_ms; /**< scan window when connecting to parent, in
|
||||||
|
* ms */
|
||||||
|
uint32_t conn_scan_to_ms; /**< timeout when connecting to parent, in ms */
|
||||||
|
uint32_t conn_itvl_min_ms; /**< lower bound of connection interval range,
|
||||||
|
* in ms */
|
||||||
|
uint32_t conn_itvl_max_ms; /**< upper bound of connection interval range,
|
||||||
|
in ms */
|
||||||
|
uint16_t conn_latency; /**< used slave latency for parent connection */
|
||||||
|
uint32_t conn_super_to_ms; /**< used supervision timeout for parent
|
||||||
|
* connection, in ms */
|
||||||
|
uint32_t eval_itvl_min_ms; /**< amount of time a node searches for
|
||||||
|
* potential parents, lower bound in ms */
|
||||||
|
uint32_t eval_itvl_max_ms; /**< amount of time a node searches for
|
||||||
|
* potential parents, upper bound in ms */
|
||||||
|
} nimble_rpble_cfg_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief RPL DODAG information
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint8_t inst_id; /**< instance ID */
|
||||||
|
uint8_t dodag_id[16]; /**< DODAG ID */
|
||||||
|
uint8_t version; /**< DODAG version */
|
||||||
|
uint8_t role; /**< RPL role of the node */
|
||||||
|
uint16_t rank; /**< the node's rank in the DODAG */
|
||||||
|
} nimble_rpble_ctx_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the nimble_rpble module with the given parameters
|
||||||
|
*
|
||||||
|
* @note This function must be called only once, typically during system
|
||||||
|
* initialization
|
||||||
|
*
|
||||||
|
* @param[in] cfg configuration parameters
|
||||||
|
*
|
||||||
|
* @return 0 on success
|
||||||
|
*/
|
||||||
|
int nimble_rpble_init(const nimble_rpble_cfg_t *cfg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update the used timing parameters
|
||||||
|
*
|
||||||
|
* @param[in] cfg configuration parameters
|
||||||
|
*
|
||||||
|
* @return 0 on success
|
||||||
|
*/
|
||||||
|
int nimble_rpble_param_update(const nimble_rpble_cfg_t *cfg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Register a callback that is called on BLE events
|
||||||
|
*
|
||||||
|
* The registered callback function is a simple pass-through of nimble_netif
|
||||||
|
* events. The callback is executed in the context of NimBLE's host thread.
|
||||||
|
*
|
||||||
|
* @param[in] cb event callback to register, set to NULL to remove
|
||||||
|
*/
|
||||||
|
int nimble_rpble_eventcb(nimble_netif_eventcb_t cb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update the current RPL context
|
||||||
|
*
|
||||||
|
* @note This function is meant to be called only by the RPL implementation
|
||||||
|
*
|
||||||
|
* @param[in] ctx current DODAG state
|
||||||
|
|
||||||
|
* @return 0 on success
|
||||||
|
* @return -EALREADY if the given context did not change
|
||||||
|
*/
|
||||||
|
int nimble_rpble_update(const nimble_rpble_ctx_t *ctx);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* NIMBLE_RPBLE_H */
|
||||||
|
/** @} */
|
||||||
99
pkg/nimble/rpble/include/nimble_rpble_params.h
Normal file
99
pkg/nimble/rpble/include/nimble_rpble_params.h
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Freie Universität Berlin
|
||||||
|
*
|
||||||
|
* 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_nimble_rpble
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
* @file
|
||||||
|
* @brief Default configuration for the nimble_netif_rpble module
|
||||||
|
*
|
||||||
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NIMBLE_RPBLE_PARAMS_H
|
||||||
|
#define NIMBLE_RPBLE_PARAMS_H
|
||||||
|
|
||||||
|
#include "nimble_rpble.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Default parameters used for the nimble_netif_rpble module
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#ifndef NIMBLE_RPBLE_SCAN_ITVL_MS
|
||||||
|
#define NIMBLE_RPBLE_SCAN_ITVL_MS 1200U
|
||||||
|
#endif
|
||||||
|
#ifndef NIMBLE_RPBLE_SCAN_WIN_MS
|
||||||
|
#define NIMBLE_RPBLE_SCAN_WIN_MS 120U
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NIMBLE_RPBLE_ADV_ITVL_MS
|
||||||
|
#define NIMBLE_RPBLE_ADV_ITVL_MS 100U
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NIMBLE_RPBLE_CONN_SCAN_ITVL_MS
|
||||||
|
#define NIMBLE_RPBLE_CONN_SCAN_ITVL_MS 120U
|
||||||
|
#endif
|
||||||
|
#ifndef NIMBLE_RPBLE_CONN_SCAN_WIN_MS
|
||||||
|
#define NIMBLE_RPBLE_CONN_SCAN_WIN_MS 120U
|
||||||
|
#endif
|
||||||
|
#ifndef NIMBLE_RPBLE_CONN_SCAN_TO_MS
|
||||||
|
#define NIMBLE_RPBLE_CONN_SCAN_TO_MS 360U
|
||||||
|
#endif
|
||||||
|
#ifndef NIMBLE_RPBLE_CONN_ITVL_MIN_MS
|
||||||
|
#define NIMBLE_RPBLE_CONN_ITVL_MIN_MS 90U
|
||||||
|
#endif
|
||||||
|
#ifndef NIMBLE_RPBLE_CONN_ITVL_MAX_MS
|
||||||
|
#define NIMBLE_RPBLE_CONN_ITVL_MAX_MS 110U
|
||||||
|
#endif
|
||||||
|
#ifndef NIMBLE_RPBLE_CONN_LATENCY
|
||||||
|
#define NIMBLE_RPBLE_CONN_LATENCY 0
|
||||||
|
#endif
|
||||||
|
#ifndef NIMBLE_RPBLE_CONN_SUPER_TO_MS
|
||||||
|
#define NIMBLE_RPBLE_CONN_SUPER_TO_MS 1650U
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NIMBLE_RPBLE_EVAL_ITVL_MIN_MS
|
||||||
|
#define NIMBLE_RPBLE_EVAL_ITVL_MIN_MS 12000U
|
||||||
|
#endif
|
||||||
|
#ifndef NIMBLE_RPBLE_EVAL_ITVL_MAX_MS
|
||||||
|
#define NIMBLE_RPBLE_EVAL_ITVL_MAX_MS 13000U
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NIMBLE_RPBLE_PARAMS
|
||||||
|
#define NIMBLE_RPBLE_PARAMS \
|
||||||
|
{ .scan_itvl_ms = NIMBLE_RPBLE_SCAN_ITVL_MS, \
|
||||||
|
.scan_win_ms = NIMBLE_RPBLE_SCAN_WIN_MS, \
|
||||||
|
.adv_itvl_ms = NIMBLE_RPBLE_ADV_ITVL_MS, \
|
||||||
|
.conn_scan_itvl_ms = NIMBLE_RPBLE_CONN_SCAN_ITVL_MS, \
|
||||||
|
.conn_scan_win_ms = NIMBLE_RPBLE_CONN_SCAN_WIN_MS, \
|
||||||
|
.conn_scan_to_ms = NIMBLE_RPBLE_CONN_SCAN_TO_MS, \
|
||||||
|
.conn_itvl_min_ms = NIMBLE_RPBLE_CONN_ITVL_MIN_MS, \
|
||||||
|
.conn_itvl_max_ms = NIMBLE_RPBLE_CONN_ITVL_MAX_MS, \
|
||||||
|
.conn_latency = NIMBLE_RPBLE_CONN_LATENCY, \
|
||||||
|
.conn_super_to_ms = NIMBLE_RPBLE_CONN_SUPER_TO_MS, \
|
||||||
|
.eval_itvl_min_ms = NIMBLE_RPBLE_EVAL_ITVL_MIN_MS, \
|
||||||
|
.eval_itvl_max_ms = NIMBLE_RPBLE_EVAL_ITVL_MAX_MS }
|
||||||
|
#endif
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief nimble_netif_rpble configuration
|
||||||
|
*/
|
||||||
|
static const nimble_rpble_cfg_t nimble_rpble_params = NIMBLE_RPBLE_PARAMS;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* NIMBLE_RPBLE_PARAMS_H */
|
||||||
|
/** @} */
|
||||||
347
pkg/nimble/rpble/nimble_rpble.c
Normal file
347
pkg/nimble/rpble/nimble_rpble.c
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019-2021 Freie Universität Berlin
|
||||||
|
*
|
||||||
|
* 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_nimble_rpble
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief RPL-over-BLE (rpble) implementation for NimBLE and GNRC
|
||||||
|
*
|
||||||
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||||
|
*
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "random.h"
|
||||||
|
#include "net/bluetil/ad.h"
|
||||||
|
#include "net/bluetil/addr.h"
|
||||||
|
#include "net/gnrc/rpl.h"
|
||||||
|
|
||||||
|
#include "nimble_netif.h"
|
||||||
|
#include "nimble_netif_conn.h"
|
||||||
|
#include "nimble_rpble.h"
|
||||||
|
#include "nimble_scanner.h"
|
||||||
|
#include "host/ble_gap.h"
|
||||||
|
#include "nimble/nimble_port.h"
|
||||||
|
|
||||||
|
#define ENABLE_DEBUG 0
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
/* parent state */
|
||||||
|
#define PARENT_NONE -1
|
||||||
|
#define PARENT_ROOT -2
|
||||||
|
#define SCORE_NONE 0U
|
||||||
|
|
||||||
|
/* RPL meta data context fields */
|
||||||
|
#define AD_SVC_DATA_LEN 23
|
||||||
|
#define POS_INST_ID 2
|
||||||
|
#define POS_DODAG_ID 3
|
||||||
|
#define POS_VERSION 19
|
||||||
|
#define POS_RANK 20
|
||||||
|
#define POS_FREE_SLOTS 22
|
||||||
|
|
||||||
|
/* keep the timing parameters for connections and advertisements */
|
||||||
|
static struct ble_gap_adv_params _adv_params = { 0 };
|
||||||
|
static struct ble_gap_conn_params _conn_params = { 0 };
|
||||||
|
static uint32_t _conn_scan_to; /* in ms */
|
||||||
|
|
||||||
|
/* local RPL context */
|
||||||
|
static nimble_rpble_ctx_t _local_rpl_ctx;
|
||||||
|
static int _current_parent = PARENT_NONE;
|
||||||
|
struct {
|
||||||
|
ble_addr_t addr; /**< address of highest scored potential parent */
|
||||||
|
uint16_t score; /* 0 := not used, larger is better! */
|
||||||
|
} _psel = { { 0 }, SCORE_NONE };
|
||||||
|
|
||||||
|
/* eval event used for periodical state updates */
|
||||||
|
static uint32_t _eval_itvl;
|
||||||
|
static struct ble_npl_callout _evt_eval;
|
||||||
|
|
||||||
|
static nimble_netif_eventcb_t _eventcb = NULL;
|
||||||
|
|
||||||
|
static uint16_t _psel_score(uint16_t rank, uint8_t free)
|
||||||
|
{
|
||||||
|
return (UINT16_MAX - rank + free);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _children_accept(void)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
(void)res;
|
||||||
|
|
||||||
|
/* stop advertising while we are updating the context */
|
||||||
|
nimble_netif_accept_stop();
|
||||||
|
|
||||||
|
/* we only start advertising (accepting) if we do have an active parent and
|
||||||
|
* if we have resources for new connections */
|
||||||
|
if ((_current_parent == PARENT_NONE) ||
|
||||||
|
(nimble_netif_conn_count(NIMBLE_NETIF_UNUSED) == 0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* compile RPL meta data into AD service data TLV field */
|
||||||
|
uint8_t sd[AD_SVC_DATA_LEN];
|
||||||
|
byteorder_htolebufs(sd, BLE_GATT_SVC_IPSS);
|
||||||
|
sd[POS_INST_ID] = _local_rpl_ctx.inst_id;
|
||||||
|
memcpy(&sd[POS_DODAG_ID], _local_rpl_ctx.dodag_id, 16);
|
||||||
|
sd[POS_VERSION] = _local_rpl_ctx.version;
|
||||||
|
byteorder_htolebufs(&sd[POS_RANK], _local_rpl_ctx.rank);
|
||||||
|
sd[POS_FREE_SLOTS] = (uint8_t)nimble_netif_conn_count(NIMBLE_NETIF_UNUSED);
|
||||||
|
|
||||||
|
/* generate the new advertising data */
|
||||||
|
bluetil_ad_t ad;
|
||||||
|
uint8_t buf[BLE_HS_ADV_MAX_SZ];
|
||||||
|
res = bluetil_ad_init_with_flags(&ad, buf, BLE_HS_ADV_MAX_SZ,
|
||||||
|
BLUETIL_AD_FLAGS_DEFAULT);
|
||||||
|
assert(res == BLUETIL_AD_OK);
|
||||||
|
res = bluetil_ad_add(&ad, BLE_GAP_AD_SERVICE_DATA, sd, sizeof(sd));
|
||||||
|
assert(res == BLUETIL_AD_OK);
|
||||||
|
|
||||||
|
/* start advertising this node */
|
||||||
|
res = nimble_netif_accept(ad.buf, ad.pos, &_adv_params);
|
||||||
|
assert(res == NIMBLE_NETIF_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _on_scan_evt(uint8_t type,
|
||||||
|
const ble_addr_t *addr, int8_t rssi,
|
||||||
|
const uint8_t *ad, size_t ad_len)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
|
/* filter out all non-connectible advertisements */
|
||||||
|
if (type != BLE_HCI_ADV_TYPE_ADV_IND) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if scanned node does actually speak rpble */
|
||||||
|
bluetil_ad_data_t sd_field;
|
||||||
|
bluetil_ad_t ads = { .buf = (uint8_t *)ad, .pos = ad_len, .size = ad_len };
|
||||||
|
res = bluetil_ad_find(&ads, BLE_GAP_AD_SERVICE_DATA, &sd_field);
|
||||||
|
if (res != BLUETIL_AD_OK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((sd_field.len != AD_SVC_DATA_LEN) ||
|
||||||
|
(byteorder_lebuftohs(sd_field.data) != BLE_GATT_SVC_IPSS)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @note Here we need to improve the filtering: so far, we consider every
|
||||||
|
* node we see that is capable of rplbe to be a parent. We should
|
||||||
|
* however also filter for instance ID and possibly the DODAG ID as
|
||||||
|
* well. On top, we should probably only consider nodes with >=
|
||||||
|
* version as parent
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* score and compare advertising peer */
|
||||||
|
uint16_t rank = byteorder_lebuftohs(&sd_field.data[POS_RANK]);
|
||||||
|
uint8_t free = sd_field.data[POS_FREE_SLOTS];
|
||||||
|
uint16_t score = _psel_score(rank, free);
|
||||||
|
|
||||||
|
/* our currently preferred parent might have updated its score in the mean
|
||||||
|
* time, so we need to check that */
|
||||||
|
if (memcmp(&_psel.addr, addr, sizeof(ble_addr_t)) == 0) {
|
||||||
|
_psel.score = score;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we consider only parents with a lower rank and remember the one with the
|
||||||
|
* best score */
|
||||||
|
if (((_local_rpl_ctx.rank == 0) || (_local_rpl_ctx.rank > rank)) &&
|
||||||
|
(score > _psel.score)) {
|
||||||
|
_psel.score = score;
|
||||||
|
memcpy(&_psel.addr, addr, sizeof(ble_addr_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _parent_find(void)
|
||||||
|
{
|
||||||
|
_psel.score = SCORE_NONE;
|
||||||
|
nimble_scanner_start();
|
||||||
|
ble_npl_callout_reset(&_evt_eval, _eval_itvl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _parent_find_stop(void)
|
||||||
|
{
|
||||||
|
ble_npl_callout_stop(&_evt_eval);
|
||||||
|
nimble_scanner_stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _parent_connect(struct ble_npl_event *ev)
|
||||||
|
{
|
||||||
|
(void)ev;
|
||||||
|
|
||||||
|
/* for now, we only try to connect to a parent if we have none */
|
||||||
|
assert(_current_parent == PARENT_NONE);
|
||||||
|
/* just in case this event is triggered while we were configured to be the
|
||||||
|
* RPL root */
|
||||||
|
if (_local_rpl_ctx.role == GNRC_RPL_ROOT_NODE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reset timer and stop scanner */
|
||||||
|
_parent_find_stop();
|
||||||
|
|
||||||
|
if (_psel.score == SCORE_NONE) {
|
||||||
|
/* no potential parent found, restarting search for one */
|
||||||
|
_parent_find();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* try to connect to parent */
|
||||||
|
int res = nimble_netif_connect(&_psel.addr, &_conn_params, _conn_scan_to);
|
||||||
|
if (res < 0) {
|
||||||
|
_parent_find();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_current_parent = res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _on_netif_evt(int handle, nimble_netif_event_t event,
|
||||||
|
const uint8_t *addr)
|
||||||
|
{
|
||||||
|
(void)addr;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case NIMBLE_NETIF_CONNECTED_MASTER:
|
||||||
|
/* parent selected */
|
||||||
|
assert(_current_parent == handle);
|
||||||
|
/* send a DIS once connected to a (new) parent) */
|
||||||
|
gnrc_rpl_send_DIS(NULL, (ipv6_addr_t *) &ipv6_addr_all_rpl_nodes,
|
||||||
|
NULL, 0);
|
||||||
|
break;
|
||||||
|
case NIMBLE_NETIF_CONNECTED_SLAVE:
|
||||||
|
/* child added */
|
||||||
|
_children_accept();
|
||||||
|
break;
|
||||||
|
case NIMBLE_NETIF_CLOSED_MASTER:
|
||||||
|
/* parent lost */
|
||||||
|
nimble_netif_accept_stop();
|
||||||
|
_current_parent = PARENT_NONE;
|
||||||
|
/* back to 0, now we need to find a new parent... */
|
||||||
|
_parent_find();
|
||||||
|
break;
|
||||||
|
case NIMBLE_NETIF_CLOSED_SLAVE:
|
||||||
|
/* child lost */
|
||||||
|
_children_accept();
|
||||||
|
break;
|
||||||
|
case NIMBLE_NETIF_ABORT_MASTER:
|
||||||
|
/* parent selection aborted */
|
||||||
|
nimble_netif_accept_stop();
|
||||||
|
_current_parent = PARENT_NONE;
|
||||||
|
_parent_find();
|
||||||
|
break;
|
||||||
|
case NIMBLE_NETIF_ABORT_SLAVE:
|
||||||
|
/* child selection aborted */
|
||||||
|
_children_accept();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* nothing to do for all other events */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pass events to high-level user if activated */
|
||||||
|
if (_eventcb) {
|
||||||
|
_eventcb(handle, event, addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int nimble_rpble_init(const nimble_rpble_cfg_t *cfg)
|
||||||
|
{
|
||||||
|
/* initialize state */
|
||||||
|
memset(&_local_rpl_ctx, 0, sizeof(_local_rpl_ctx));
|
||||||
|
|
||||||
|
/* initialize the eval event */
|
||||||
|
ble_npl_callout_init(&_evt_eval, nimble_port_get_dflt_eventq(),
|
||||||
|
_parent_connect, NULL);
|
||||||
|
|
||||||
|
return nimble_rpble_param_update(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int nimble_rpble_param_update(const nimble_rpble_cfg_t *cfg)
|
||||||
|
{
|
||||||
|
assert(cfg);
|
||||||
|
|
||||||
|
if (_current_parent == PARENT_NONE) {
|
||||||
|
_parent_find_stop();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nimble_netif_accept_stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t itvl = random_uint32_range(cfg->eval_itvl_min_ms,
|
||||||
|
cfg->eval_itvl_max_ms);
|
||||||
|
ble_npl_time_ms_to_ticks(itvl, &_eval_itvl);
|
||||||
|
|
||||||
|
_adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
|
||||||
|
_adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
|
||||||
|
_adv_params.itvl_min = BLE_GAP_ADV_ITVL_MS(cfg->adv_itvl_ms);
|
||||||
|
_adv_params.itvl_max = _adv_params.itvl_min;
|
||||||
|
|
||||||
|
_conn_params.scan_itvl = BLE_GAP_SCAN_ITVL_MS(cfg->conn_scan_itvl_ms);
|
||||||
|
_conn_params.scan_window = BLE_GAP_SCAN_WIN_MS(cfg->conn_scan_win_ms);
|
||||||
|
_conn_params.latency = cfg->conn_latency;
|
||||||
|
_conn_params.supervision_timeout =
|
||||||
|
BLE_GAP_SUPERVISION_TIMEOUT_MS(cfg->conn_super_to_ms);
|
||||||
|
_conn_params.itvl_min = BLE_GAP_CONN_ITVL_MS(cfg->conn_itvl_min_ms);
|
||||||
|
_conn_params.itvl_max = BLE_GAP_CONN_ITVL_MS(cfg->conn_itvl_max_ms);
|
||||||
|
_conn_scan_to = cfg->conn_scan_to_ms;
|
||||||
|
|
||||||
|
/* register event callback */
|
||||||
|
nimble_netif_eventcb(_on_netif_evt);
|
||||||
|
|
||||||
|
/* configure scanner */
|
||||||
|
struct ble_gap_disc_params scan_params = { 0 };
|
||||||
|
scan_params.itvl = BLE_GAP_SCAN_ITVL_MS(cfg->scan_itvl_ms);
|
||||||
|
scan_params.window = BLE_GAP_SCAN_WIN_MS(cfg->scan_win_ms);
|
||||||
|
scan_params.passive = 1;
|
||||||
|
scan_params.filter_duplicates = 1;
|
||||||
|
nimble_scanner_init(&scan_params, _on_scan_evt);
|
||||||
|
|
||||||
|
/* start to look for parents */
|
||||||
|
if (_current_parent == PARENT_NONE) {
|
||||||
|
_parent_find();
|
||||||
|
} else {
|
||||||
|
_children_accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nimble_rpble_eventcb(nimble_netif_eventcb_t cb)
|
||||||
|
{
|
||||||
|
_eventcb = cb;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nimble_rpble_update(const nimble_rpble_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
assert(ctx != NULL);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/* if the update context is equal to what we have, ignore it */
|
||||||
|
if (memcmp(&_local_rpl_ctx, ctx, sizeof(nimble_rpble_ctx_t)) == 0) {
|
||||||
|
ret = -EALREADY;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* save rpl context for future reference */
|
||||||
|
memcpy(&_local_rpl_ctx, ctx, sizeof(nimble_rpble_ctx_t));
|
||||||
|
if (ctx->role == GNRC_RPL_ROOT_NODE) {
|
||||||
|
_current_parent = PARENT_ROOT;
|
||||||
|
_parent_find_stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* advertise the updated context */
|
||||||
|
_children_accept();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
55
sys/include/net/gnrc/rpl/rpble.h
Normal file
55
sys/include/net/gnrc/rpl/rpble.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Freie Universität Berlin
|
||||||
|
*
|
||||||
|
* 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 net_gnrc_rpl
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief Glue code linking RPL with the NimBLE rpble connection manager
|
||||||
|
*
|
||||||
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NET_GNRC_RPL_RPBLE_H
|
||||||
|
#define NET_GNRC_RPL_RPBLE_H
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#if IS_USED(MODULE_NIMBLE_RPBLE)
|
||||||
|
#include "nimble_rpble.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if IS_USED(MODULE_NIMBLE_RPBLE)
|
||||||
|
static inline void gnrc_rpl_rpble_update(const gnrc_rpl_dodag_t *dodag)
|
||||||
|
{
|
||||||
|
nimble_rpble_ctx_t ctx;
|
||||||
|
ctx.inst_id = dodag->instance->id;
|
||||||
|
memcpy(ctx.dodag_id, &dodag->dodag_id, 16);
|
||||||
|
ctx.version = dodag->version;
|
||||||
|
ctx.rank = dodag->my_rank;
|
||||||
|
ctx.role = dodag->node_status;
|
||||||
|
nimble_rpble_update(&ctx);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline void gnrc_rpl_rpble_update(const gnrc_rpl_dodag_t *dodag)
|
||||||
|
{
|
||||||
|
(void)dodag;
|
||||||
|
/* do nothing here */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* NET_GNRC_RPL_RPBLE_H */
|
||||||
|
/** @} */
|
||||||
@ -35,6 +35,7 @@
|
|||||||
#include "gnrc_rpl_internal/globals.h"
|
#include "gnrc_rpl_internal/globals.h"
|
||||||
|
|
||||||
#include "net/gnrc/rpl.h"
|
#include "net/gnrc/rpl.h"
|
||||||
|
#include "net/gnrc/rpl/rpble.h"
|
||||||
#ifdef MODULE_GNRC_RPL_P2P
|
#ifdef MODULE_GNRC_RPL_P2P
|
||||||
#include "net/gnrc/rpl/p2p.h"
|
#include "net/gnrc/rpl/p2p.h"
|
||||||
#include "net/gnrc/rpl/p2p_dodag.h"
|
#include "net/gnrc/rpl/p2p_dodag.h"
|
||||||
@ -165,6 +166,7 @@ gnrc_rpl_instance_t *gnrc_rpl_root_init(uint8_t instance_id, const ipv6_addr_t *
|
|||||||
trickle_start(gnrc_rpl_pid, &dodag->trickle, GNRC_RPL_MSG_TYPE_TRICKLE_MSG,
|
trickle_start(gnrc_rpl_pid, &dodag->trickle, GNRC_RPL_MSG_TYPE_TRICKLE_MSG,
|
||||||
(1 << dodag->dio_min), dodag->dio_interval_doubl,
|
(1 << dodag->dio_min), dodag->dio_interval_doubl,
|
||||||
dodag->dio_redun);
|
dodag->dio_redun);
|
||||||
|
gnrc_rpl_rpble_update(dodag);
|
||||||
|
|
||||||
return inst;
|
return inst;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
#include "utlist.h"
|
#include "utlist.h"
|
||||||
|
|
||||||
#include "net/gnrc/rpl.h"
|
#include "net/gnrc/rpl.h"
|
||||||
|
#include "net/gnrc/rpl/rpble.h"
|
||||||
#ifdef MODULE_GNRC_RPL_P2P
|
#ifdef MODULE_GNRC_RPL_P2P
|
||||||
#include "net/gnrc/rpl/p2p.h"
|
#include "net/gnrc/rpl/p2p.h"
|
||||||
#include "net/gnrc/rpl/p2p_dodag.h"
|
#include "net/gnrc/rpl/p2p_dodag.h"
|
||||||
@ -359,6 +360,7 @@ static gnrc_rpl_parent_t *_gnrc_rpl_find_preferred_parent(gnrc_rpl_dodag_t *doda
|
|||||||
dodag->my_rank = dodag->instance->of->calc_rank(dodag, 0);
|
dodag->my_rank = dodag->instance->of->calc_rank(dodag, 0);
|
||||||
if (dodag->my_rank != old_rank) {
|
if (dodag->my_rank != old_rank) {
|
||||||
trickle_reset_timer(&dodag->trickle);
|
trickle_reset_timer(&dodag->trickle);
|
||||||
|
gnrc_rpl_rpble_update(dodag);
|
||||||
}
|
}
|
||||||
|
|
||||||
LL_FOREACH_SAFE(dodag->parents, elt, tmp) {
|
LL_FOREACH_SAFE(dodag->parents, elt, tmp) {
|
||||||
|
|||||||
27
tests/nimble_rpble_gnrc/Makefile
Normal file
27
tests/nimble_rpble_gnrc/Makefile
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
BOARD ?= nrf52dk
|
||||||
|
include ../Makefile.tests_common
|
||||||
|
|
||||||
|
# include shell support
|
||||||
|
USEMODULE += shell
|
||||||
|
USEMODULE += shell_commands
|
||||||
|
USEMODULE += ps
|
||||||
|
|
||||||
|
# Include GNRC and RPL
|
||||||
|
USEMODULE += gnrc_netdev_default
|
||||||
|
USEMODULE += gnrc_netif_single
|
||||||
|
USEMODULE += auto_init_gnrc_netif
|
||||||
|
USEMODULE += gnrc_ipv6_router_default
|
||||||
|
USEMODULE += gnrc_icmpv6_echo
|
||||||
|
USEMODULE += gnrc_rpl
|
||||||
|
USEMODULE += auto_init_gnrc_rpl
|
||||||
|
|
||||||
|
# configure and use Nimble
|
||||||
|
USEMODULE += bluetil_addr
|
||||||
|
USEMODULE += nimble_rpble
|
||||||
|
NIMBLE_MAX_CONN := 3
|
||||||
|
|
||||||
|
DEVELHELP = 0
|
||||||
|
|
||||||
|
TEST_ON_CI_WHITELIST += nrf52dk
|
||||||
|
|
||||||
|
include $(RIOTBASE)/Makefile.include
|
||||||
4
tests/nimble_rpble_gnrc/Makefile.ci
Normal file
4
tests/nimble_rpble_gnrc/Makefile.ci
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
BOARD_INSUFFICIENT_MEMORY := \
|
||||||
|
e104-bt5010a-tb \
|
||||||
|
e104-bt5011a-tb \
|
||||||
|
#
|
||||||
80
tests/nimble_rpble_gnrc/main.c
Normal file
80
tests/nimble_rpble_gnrc/main.c
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Freie Universität Berlin
|
||||||
|
*
|
||||||
|
* 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 examples
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief Example for showing 6LoWPAN over BLE using NimBLE and GNRC
|
||||||
|
*
|
||||||
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||||
|
*
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "msg.h"
|
||||||
|
#include "shell.h"
|
||||||
|
#include "nimble_rpble.h"
|
||||||
|
#include "net/bluetil/addr.h"
|
||||||
|
|
||||||
|
#define MAIN_QUEUE_SIZE (8)
|
||||||
|
static msg_t _main_msg_queue[MAIN_QUEUE_SIZE];
|
||||||
|
|
||||||
|
static void _dump_evt(const char *text, const uint8_t *addr)
|
||||||
|
{
|
||||||
|
printf("[ble_event] %s (", text);
|
||||||
|
bluetil_addr_print(addr);
|
||||||
|
printf(")\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _on_rpble_event(int handle, nimble_netif_event_t event,
|
||||||
|
const uint8_t *addr)
|
||||||
|
{
|
||||||
|
(void)handle;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case NIMBLE_NETIF_CONNECTED_MASTER:
|
||||||
|
_dump_evt("parent selected", addr);
|
||||||
|
break;
|
||||||
|
case NIMBLE_NETIF_CONNECTED_SLAVE:
|
||||||
|
_dump_evt("child added", addr);
|
||||||
|
break;
|
||||||
|
case NIMBLE_NETIF_CLOSED_MASTER:
|
||||||
|
_dump_evt("parent lost", addr);
|
||||||
|
break;
|
||||||
|
case NIMBLE_NETIF_CLOSED_SLAVE:
|
||||||
|
_dump_evt("child lost", addr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* not interested in any other BLE events here */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
puts("RPL-over-BLE Example Application");
|
||||||
|
|
||||||
|
/* register the custom event handler */
|
||||||
|
nimble_rpble_eventcb(_on_rpble_event);
|
||||||
|
|
||||||
|
/* we need a message queue for the thread running the shell in order to
|
||||||
|
* receive potentially fast incoming networking packets (ping6) */
|
||||||
|
msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE);
|
||||||
|
|
||||||
|
/* start shell */
|
||||||
|
char line_buf[SHELL_DEFAULT_BUFSIZE];
|
||||||
|
shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user