diff --git a/Makefile.dep b/Makefile.dep index c0b24c5f550cfb900c9dfa24d92ce695b1249230..0e999f9411b9b587bc81101605e63952eab96310 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -698,6 +698,15 @@ ifneq (,$(filter benchmark,$(USEMODULE))) USEMODULE += xtimer endif +ifneq (,$(filter skald_%,$(USEMODULE))) + USEMODULE += skald +endif + +ifneq (,$(filter skald,$(USEMODULE))) + FEATURES_REQUIRED += radio_ble + USEMODULE += xtimer + USEMODULE += random +endif # always select gpio (until explicit dependencies are sorted out) FEATURES_OPTIONAL += periph_gpio diff --git a/boards/common/nrf52xxxdk/Makefile.dep b/boards/common/nrf52xxxdk/Makefile.dep index 5472bf8b8d8fd463a18815c0f10e5d348f90fe51..09f460dcc6367499130caacccca64e86f39454b8 100644 --- a/boards/common/nrf52xxxdk/Makefile.dep +++ b/boards/common/nrf52xxxdk/Makefile.dep @@ -1,3 +1,7 @@ ifneq (,$(filter saul_default,$(USEMODULE))) USEMODULE += saul_gpio endif + +ifneq (,$(filter skald,$(USEMODULE))) + USEMODULE += nrfble +endif diff --git a/boards/common/nrf52xxxdk/Makefile.features b/boards/common/nrf52xxxdk/Makefile.features index 56ed0022c03b13df2b60ce2c12b0963bfefcafc7..1783223faf9024647f0aa517abf18fe9b223021e 100644 --- a/boards/common/nrf52xxxdk/Makefile.features +++ b/boards/common/nrf52xxxdk/Makefile.features @@ -7,6 +7,8 @@ FEATURES_PROVIDED += periph_timer FEATURES_PROVIDED += periph_uart # Various other features (if any) +FEATURES_PROVIDED += radio_ble +FEATURES_PROVIDED += radio_nrfble FEATURES_PROVIDED += radio_nrfmin # The board MPU family (used for grouping by the CI system) diff --git a/cpu/nrf5x_common/Makefile b/cpu/nrf5x_common/Makefile index ef599b387d5510d053d265cd2ddfea8ed0686619..41f24c0a2df0e4e4ecba038c051736f666fae930 100644 --- a/cpu/nrf5x_common/Makefile +++ b/cpu/nrf5x_common/Makefile @@ -2,6 +2,9 @@ MODULE = cpu_common DIRS = periph # build one of the radio drivers, if enabled +ifneq (,$(filter nrfble,$(USEMODULE))) + DIRS += radio/nrfble +endif ifneq (,$(filter nrfmin,$(USEMODULE))) DIRS += radio/nrfmin endif diff --git a/cpu/nrf5x_common/include/nrfble.h b/cpu/nrf5x_common/include/nrfble.h new file mode 100644 index 0000000000000000000000000000000000000000..9c41b3ee443de400c9a5e445a7cd3726363bb8fe --- /dev/null +++ b/cpu/nrf5x_common/include/nrfble.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2017-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. + */ + +/** + * @defgroup drivers_nrf5x_nrfble NRF BLE radio driver + * @ingroup drivers_netdev + * @brief Radio driver for nRF5x SoCs for using the radio in BLE mode + * @{ + * + * @file + * @brief Interface definition for the nrfble radio driver + * + * @author Hauke Petersen <hauke.petersen@fu-berlin.de> + */ + +#ifndef NRFBLE_H +#define NRFBLE_H + +#include "net/netdev.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief nrfble channel configuration + * @{ + */ +#define NRFBLE_CHAN_MIN (0U) +#define NRFBLE_CHAN_MAX (39U) +/** @} */ + +/** + * @brief Default transmission power used + */ +#define NRFBLE_TXPOWER_DEFAULT (0) /* 0dBm */ + +/** + * @brief Setup the device driver's data structures + * + * @return pointer to the device's netdev struct + */ +netdev_t *nrfble_setup(void); + +#ifdef __cplusplus +} +#endif + +#endif /* NRFBLE_H */ +/** @} */ diff --git a/cpu/nrf5x_common/radio/nrfble/Makefile b/cpu/nrf5x_common/radio/nrfble/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..1f6a482e4fe16f6ecbf6ad792cc3224b8541fc24 --- /dev/null +++ b/cpu/nrf5x_common/radio/nrfble/Makefile @@ -0,0 +1,3 @@ +MODULE = nrfble + +include $(RIOTBASE)/Makefile.base diff --git a/cpu/nrf5x_common/radio/nrfble/nrfble.c b/cpu/nrf5x_common/radio/nrfble/nrfble.c new file mode 100644 index 0000000000000000000000000000000000000000..dd500cbde3972e536bb3627bb2ce361821ca61e5 --- /dev/null +++ b/cpu/nrf5x_common/radio/nrfble/nrfble.c @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2017-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 drivers_nrf5x_nrfble + * + * @note This driver is not thread safe and should only be used by a + * single thread at once! + * @{ + * + * @file + * @brief Bluetooth low energy radio driver for nRF5x SoCs + * + * @author Hauke Petersen <hauke.petersen@fu-berlin.de> + * + * @} + */ + +#include <errno.h> + +#include "cpu.h" +#include "assert.h" + +#include "nrfble.h" +#include "net/netdev/ble.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* driver specific device configuration */ +#define CONF_MODE RADIO_MODE_MODE_Ble_1Mbit +#define CONF_LEN (8U) +#define CONF_S0 (1U) +#define CONF_S1 (0U) +#define CONF_STATLEN (0U) +#define CONF_BASE_ADDR_LEN (3U) +#define CONF_ENDIAN RADIO_PCNF1_ENDIAN_Little +#define CONF_WHITENING RADIO_PCNF1_WHITEEN_Enabled +#define CONF_TIFS (150U) +#define CONF_CRC_LEN (0X3 | RADIO_CRCCNF_SKIPADDR_Msk) +#define CONF_CRC_POLY (0x00065b) + +/* used shortcuts */ +#define SHORTS_BASE (RADIO_SHORTS_READY_START_Msk | \ + RADIO_SHORTS_END_DISABLE_Msk) +#define SHORTS_RX (SHORTS_BASE | RADIO_SHORTS_DISABLED_TXEN_Msk) +#define SHORTS_TX (SHORTS_BASE | RADIO_SHORTS_DISABLED_RXEN_Msk) + +/* interrupt masks */ +#define INT_DIS (RADIO_INTENCLR_DISABLED_Msk | \ + RADIO_INTENCLR_ADDRESS_Msk) +#define INT_EN (RADIO_INTENSET_DISABLED_Msk | \ + RADIO_INTENSET_ADDRESS_Msk) + +/* driver internal radio states */ +#define STATE_IDLE (0x00) +#define STATE_RX (0x01) +#define STATE_TX (0x02) +#define STATE_BUSY (0x80) + +/* forward declaration of the netdev driver struct */ +static const netdev_driver_t netdev_driver; + +/* current radio state */ +static volatile uint8_t _state = STATE_IDLE; + +/* active radio context */ +static netdev_ble_ctx_t *_ctx = NULL; + +/* allocate the netdev device descriptor */ +netdev_t _nrfble_dev; + +/* map logical BLE channel values to actual radio frequencies */ +static const uint8_t _ble_chan_map[40] = { + [ 0] = 4, + [ 1] = 6, + [ 2] = 8, + [ 3] = 10, + [ 4] = 12, + [ 5] = 14, + [ 6] = 16, + [ 7] = 18, + [ 8] = 20, + [ 9] = 22, + [10] = 24, + [11] = 28, + [12] = 30, + [13] = 32, + [14] = 34, + [15] = 36, + [16] = 38, + [17] = 40, + [18] = 42, + [19] = 44, + [20] = 46, + [21] = 48, + [22] = 50, + [23] = 52, + [24] = 54, + [25] = 56, + [26] = 58, + [27] = 60, + [28] = 62, + [29] = 64, + [30] = 66, + [31] = 68, + [32] = 70, + [33] = 72, + [34] = 74, + [35] = 76, + [36] = 78, + [37] = 2, + [38] = 26, + [39] = 80, +}; + +/** + * @brief Set radio into idle (DISABLED) state + */ +static void _go_idle(void) +{ + if (!(_state & STATE_BUSY)) { + NRF_RADIO->INTENCLR = INT_DIS; + NRF_RADIO->SHORTS = 0; + NRF_RADIO->EVENTS_DISABLED = 0; + NRF_RADIO->TASKS_DISABLE = 1; + while (NRF_RADIO->EVENTS_DISABLED == 0) {} + _state = STATE_IDLE; + } +} + +/** + * @brief Set radio context (channel, CRC, whitening, and access address) + */ +static void _set_context(netdev_ble_ctx_t *ctx) +{ + if (ctx) { + assert(ctx->chan <= NRFBLE_CHAN_MAX); + + _ctx = ctx; + NRF_RADIO->FREQUENCY = _ble_chan_map[ctx->chan]; + NRF_RADIO->PREFIX0 = ctx->aa.raw[3]; + NRF_RADIO->BASE0 = (uint32_t)((ctx->aa.raw[0] << 8) | + (ctx->aa.raw[1] << 16) | + (ctx->aa.raw[2] << 24)); + NRF_RADIO->DATAWHITEIV = ctx->chan; + NRF_RADIO->CRCINIT = (ctx->crc & NETDEV_BLE_CRC_MASK); + } + else { + _go_idle(); + } +} + +static int16_t _nrfble_get_txpower(void) +{ + int8_t p = (int8_t)NRF_RADIO->TXPOWER; + if (p < 0) { + return (int16_t)(0xff00 | p); + } + return (int16_t)p; +} + +static void _nrfble_set_txpower(int16_t power) +{ + if (power > 2) { + NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Pos4dBm; + } + else if (power > -2) { + NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_0dBm; + } + else if (power > -6) { + NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg4dBm; + } + else if (power > -10) { + NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg8dBm; + } + else if (power > -14) { + NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg12dBm; + } + else if (power > -18) { + NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg16dBm; + } + else if (power > -25) { + NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg20dBm; + } + else { + NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg30dBm; + } +} + +/** + * @brief Radio interrupt routine + */ +void isr_radio(void) +{ + if (NRF_RADIO->EVENTS_ADDRESS) { + NRF_RADIO->EVENTS_ADDRESS = 0; + _state |= STATE_BUSY; + } + + if (NRF_RADIO->EVENTS_DISABLED) { + NRF_RADIO->EVENTS_DISABLED = 0; + + if (_state == (STATE_BUSY | STATE_RX)) { + _state = STATE_TX; + if (NRF_RADIO->CRCSTATUS) { + _ctx->crc |= NETDEV_BLE_CRC_OK; + } + else { + _ctx->crc &= ~(NETDEV_BLE_CRC_OK); + } + _nrfble_dev.event_callback(&_nrfble_dev, NETDEV_EVENT_RX_COMPLETE); + } + else { /* on TX done */ + _state = STATE_RX; + _nrfble_dev.event_callback(&_nrfble_dev, NETDEV_EVENT_TX_COMPLETE); + } + } + + cortexm_isr_end(); +} + +netdev_t *nrfble_setup(void) +{ + _nrfble_dev.driver = &netdev_driver; + _nrfble_dev.event_callback = NULL; + _nrfble_dev.context = NULL; +#ifdef MODULE_NETSTATS_L2 + memset(&_nrfble_dev.stats, 0, sizeof(netstats_t));; +#endif + return &_nrfble_dev; +} + +static int _nrfble_init(netdev_t *dev) +{ + (void)dev; + assert(_nrfble_dev.driver && _nrfble_dev.event_callback); + + /* power on the NRFs radio */ + NRF_RADIO->POWER = 1; + /* configure variable parameters to default values */ + NRF_RADIO->TXPOWER = NRFBLE_TXPOWER_DEFAULT; + /* always send from and listen to logical address 0 */ + NRF_RADIO->TXADDRESS = 0x00UL; + NRF_RADIO->RXADDRESSES = 0x01UL; + /* load driver specific configuration */ + NRF_RADIO->MODE = CONF_MODE; + /* configure data fields and packet length whitening and endianess */ + NRF_RADIO->PCNF0 = ((CONF_S1 << RADIO_PCNF0_S1LEN_Pos) | + (CONF_S0 << RADIO_PCNF0_S0LEN_Pos) | + (CONF_LEN << RADIO_PCNF0_LFLEN_Pos)); + NRF_RADIO->PCNF1 = ((CONF_WHITENING << RADIO_PCNF1_WHITEEN_Pos) | + (CONF_ENDIAN << RADIO_PCNF1_ENDIAN_Pos) | + (CONF_BASE_ADDR_LEN << RADIO_PCNF1_BALEN_Pos) | + (CONF_STATLEN << RADIO_PCNF1_STATLEN_Pos) | + (NETDEV_BLE_PDU_MAXLEN << RADIO_PCNF1_MAXLEN_Pos)); + /* set inter frame spacing to 150us */ + NRF_RADIO->TIFS = CONF_TIFS; + /* configure CRC length and polynomial */ + NRF_RADIO->CRCCNF = CONF_CRC_LEN; + NRF_RADIO->CRCPOLY = CONF_CRC_POLY; + /* enable global interrupts, but mask all local interrupts for now */ + NRF_RADIO->INTENCLR = 0xffffffff; + NVIC_EnableIRQ(RADIO_IRQn); + + DEBUG("[nrfble] initialization successful\n"); + return 0; +} + +static int _nrfble_send(netdev_t *dev, const iolist_t *data) +{ + (void)dev; + assert(data); + + NRF_RADIO->PACKETPTR = (uint32_t)data->iol_base; + NRF_RADIO->SHORTS = SHORTS_TX; + + /* in case no trx sequence is active, we start a new one now */ + if (_state == STATE_IDLE) { + _state = STATE_TX; + NRF_RADIO->EVENTS_DISABLED = 0; + NRF_RADIO->INTENSET = INT_EN; + NRF_RADIO->TASKS_TXEN = 1; + } + + return 0; +} + +static int _nrfble_recv(netdev_t *dev, void *buf, size_t len, void *info) +{ + (void)dev; + (void)len; + (void)info; + assert(buf && (len == sizeof(netdev_ble_pkt_t))); + + NRF_RADIO->PACKETPTR = (uint32_t)buf; + NRF_RADIO->SHORTS = SHORTS_RX; + + /* in case no trx sequence is active, we start a new one now */ + if (_state == STATE_IDLE) { + _state = STATE_RX; + NRF_RADIO->EVENTS_DISABLED = 0; + NRF_RADIO->INTENSET = INT_EN; + NRF_RADIO->TASKS_RXEN = 1; + } + + return 0; +} + +static int _nrfble_get(netdev_t *dev, netopt_t opt, void *val, size_t max_len) +{ + (void)dev; + (void)max_len; + + switch (opt) { + case NETOPT_TX_POWER: + assert(max_len >= sizeof(int16_t)); + *((int16_t *)val) = _nrfble_get_txpower(); + return sizeof(int16_t); + case NETOPT_DEVICE_TYPE: + assert(max_len >= sizeof(uint16_t)); + *((uint16_t *)val) = NETDEV_TYPE_BLE; + return sizeof(uint16_t); + default: + return -ENOTSUP; + } +} + +static int _nrfble_set(netdev_t *dev, netopt_t opt, const void *val, size_t len) +{ + (void)dev; + (void)len; + + switch (opt) { + case NETOPT_BLE_CTX: + _set_context((netdev_ble_ctx_t *)val); + return sizeof(netdev_ble_ctx_t); + case NETOPT_TX_POWER: + assert(len == sizeof(int16_t)); + _nrfble_set_txpower(*((const int16_t *)val)); + return sizeof(int16_t); + default: + return -ENOTSUP; + } +} + +/* export of the netdev interface */ +static const netdev_driver_t netdev_driver = { + .send = _nrfble_send, + .recv = _nrfble_recv, + .init = _nrfble_init, + .isr = NULL, + .get = _nrfble_get, + .set = _nrfble_set +}; diff --git a/drivers/include/net/netdev/ble.h b/drivers/include/net/netdev/ble.h new file mode 100644 index 0000000000000000000000000000000000000000..248c0bdeff7907a14725c8ade472e6cd3fc1af1f --- /dev/null +++ b/drivers/include/net/netdev/ble.h @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2017-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. + */ + +/** + * @defgroup drivers_netdev_ble netdev BLE mode + * @ingroup drivers_netdev_api + * @brief BLE adaption of netdev + * @{ + * + * @warning This API is experimental and in an early state - expect + * significant changes! + * + * # About + * + * BLE defines a very specific environment for the used radio, both in terms of + * communication sequences and in terms of timings. BLE communication is + * structured in so called events, where each event is a sequence of request and + * reply packets send between two peers. A radio context (frequency, CRC + * initializer, and access address) is used throughout such an event and + * typically changed for the next one. In addition, the timing of the packets + * sent in a sequence is fixed to an inter-frame-spacing of exactly 150us. + * + * To cater with these specific attributes of BLE, this interface tailors the + * generic netdev interface to be used for BLE radios. + * + * + * # Interface Adaption / Netdev Interpretation + * + * ## Transmission Sequence Based Approach + * + * To be able to handle the exact inter-packet-spacing if 150us seconds, this + * interface expects the device driver to stay in a continuous alternating + * RX-TX sequence, until it is manually aborted. While in this sequence, the + * radio driver needs to take care of switching to RX mode 150us after sending + * the last packet, and to send the next packet 150us after the last packet was + * received. + * + * Such a transmission sequence is started by calling either the radio's send + * or receive function while the radio is in idle/standby mode. + * + * Once a transmission sequence is in progress, the next packet to be send, or + * the next reception buffer to be used is specified also using the send/recv + * functions. They should be called in the `event_callback` right after the + * last transmission (RX or TX) was finished. + * + * The transmission sequence is aborted by calling `netdev_ble_stop(dev)` + * (`netdev->set(dev, NETOPT_BLE_CTX, NULL, 0)`). This will put the radio back + * into idle/standby mode. + * + * ## Radio Context + * + * As BLE uses time sliced channel hopping, the used channel needs to be set + * regularly. Additionally, also the used access address and the CRC initializer + * need to be set regularly, as they differ for different BLE connections. To + * make setting these values more efficient, this interface combines these three + * values in to a so called `radio context` and adds a `netopt` option to set + * all three values at once using `netdev_ble_set_ctx(dev, ctx)` + * (`netdev->set(dev, NETOPT_BLE_CTX, ctx, sizeof(netdev_ble_ctx_t))`). + * + * + * # Implementation Status and Limitations + * - This interface works for memory mapped radios only (no support for + * bus-connected devices). This is mainly for timing reasons. + * - No support for LE Data Length Extension (bigger packet size), yet + * + * @file + * @brief BLE specific adaption for the Netdev API + * + * @author Hauke Petersen <hauke.petersen@fu-berlin.de> + */ + +#ifndef NET_NETDEV_BLE_H +#define NET_NETDEV_BLE_H + +#include "net/netdev.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Maximum payload length of a standard BLE packet + */ +#define NETDEV_BLE_PDU_MAXLEN (37U) + +/** + * @brief Mask for the actual (3 byte) CRC data in the context's CRC field + */ +#define NETDEV_BLE_CRC_MASK (0x00ffffff) + +/** + * @brief Flag for marking a correct CRC on packet reception + */ +#define NETDEV_BLE_CRC_OK (0x80000000) + +/** + * @brief BLE packet structure (as defined by the BLE standard) + */ +typedef struct __attribute__((packed)) { + uint8_t flags; /**< header flags */ + uint8_t len; /**< actual length of PDU */ + uint8_t pdu[NETDEV_BLE_PDU_MAXLEN]; /**< protocol data unit (PDU) */ +} netdev_ble_pkt_t; + +/** + * @brief Radio context + */ +typedef struct { + union { + uint8_t raw[4]; /**< byte-wise access */ + uint32_t u32; /**< compact access */ + } aa; /**< access address */ + uint32_t crc; /**< CRC: 3 LSB for CRC, most + * significant bit for RX state*/ + uint8_t chan; /**< channel to use/used */ +} netdev_ble_ctx_t; + +/** + * @brief Send the given packet on the next occasion + * + * If a transmission sequence is in progress, the given packet will be send + * after 150us after receptions of the last packet. If no sequence is currently + * active, the packet will be send immediately and a new transmission sequence + * is started. + * + * @note Call this function only to start a new transmission sequence (radio + * is currently idle), or right after a packet was received. If called + * at any other point in time, the behavior is undefined. + * + * @param[in] dev radio to use for sending + * @param[in] pkt data to send + * + * @return 0 on success + * @return `< 0` on error + */ +static inline int netdev_ble_send(netdev_t *dev, netdev_ble_pkt_t *pkt) +{ + struct iolist data = { NULL, pkt, sizeof(netdev_ble_pkt_t) }; + return dev->driver->send(dev, &data); +} + +/** + * @brief Start listening for an incoming packet and write it into @p pkt + * + * If a transmission sequence is in progress, the radio will use the given + * buffer for reception when it goes in to RX mode 150us after sending the last + * packet. If no sequence is in progress, the radio will go into RX mode + * immediately (using the given RX buffer), and a new transmission sequence is + * started. + * + * @note Call this function only to start a new transmission sequence (radio + * is currently idle), or right after a packet was sent. If called + * at any other point in time, the behavior is undefined. + * + * @param[in] dev radio to use for receiving + * @param[out] pkt buffer to write new packet to + * + * @return 0 on success + * @return `< 0` on error + */ +static inline int netdev_ble_recv(netdev_t *dev, netdev_ble_pkt_t *pkt) +{ + return dev->driver->recv(dev, pkt, sizeof(netdev_ble_pkt_t), NULL); +} + +/** + * @brief Set the radio context for the given radio device + * + * @param[in] dev target radio device + * @param[in] ctx new radio context (CRC, channel, access address) + */ +static inline void netdev_ble_set_ctx(netdev_t *dev, netdev_ble_ctx_t *ctx) +{ + dev->driver->set(dev, NETOPT_BLE_CTX, ctx, sizeof(netdev_ble_ctx_t)); +} + +/** + * @brief Stop the ongoing RX/TX sequence + * + * @note This function has not effect if the radio is in the middle of a + * data transfer + * + * @param[in] dev target radio device + */ +static inline void netdev_ble_stop(netdev_t *dev) +{ + dev->driver->set(dev, NETOPT_BLE_CTX, NULL, 0); +} + +#ifdef __cplusplus +} +#endif + +#endif /* NET_NETDEV_BLE_H */ +/** @} */ diff --git a/examples/skald_eddystone/Makefile b/examples/skald_eddystone/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..a452cae29213a71c5183a59ca8c6cfbe930b999c --- /dev/null +++ b/examples/skald_eddystone/Makefile @@ -0,0 +1,21 @@ +# name of your application +APPLICATION = skald_eddystone + +# If no BOARD is found in the environment, use this default: +BOARD ?= nrf52dk + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +# include Skald +USEMODULE += skald_eddystone + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +# CFLAGS += -DDEVELHELP + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +include $(RIOTBASE)/Makefile.include diff --git a/examples/skald_eddystone/README.md b/examples/skald_eddystone/README.md new file mode 100644 index 0000000000000000000000000000000000000000..fa0d8f8f6ab0ada117d5d96ee05c06ed3004f47b --- /dev/null +++ b/examples/skald_eddystone/README.md @@ -0,0 +1,8 @@ +# Skald iBeacon Example + +This example demonstrates the usage of `Skald` for creating an Google +`Eddystone` beacon, advertising an`Eddystone-URI` and an `Eddystone-URL` +concurrently. + +Simply compile and flash, and verify your newly created beacon with any type of +BLE scanner. diff --git a/examples/skald_eddystone/main.c b/examples/skald_eddystone/main.c new file mode 100644 index 0000000000000000000000000000000000000000..2892cc85bd4313051a5eed1f134546f35d9aa08e --- /dev/null +++ b/examples/skald_eddystone/main.c @@ -0,0 +1,55 @@ +/* + * 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 BLE Eddystone beacon example using Skald + * + * @author Hauke Petersen <hauke.petersen@fu-berlin.de> + * + * @} + */ + +#include "log.h" + +#include "net/skald/eddystone.h" + +/* example of an Eddystone URI: + * - namespace (ASCII): 'supercool!' + * - instance (ASCII): `_RIOT_` */ +#define URI_NAMESPACE { 0x73, 0x75, 0x70, 0x65, 0x72, \ + 0x63, 0x6f, 0x6f, 0x6c, 0x21 } +#define URI_INSTANCE { 0x5f, 0x52, 0x49, 0x4f, 0x54, 0x5f } + +/* advertise this short URL, points to https://www.riot-os.org */ +#define URL "bit.ly/2Ep11dm" +/* calibrated TX power value */ +#define TX_PWR (0U) + + +/* allocate two advertising contexts, one for Eddystone-URL and one for + * Eddystone-URI */ +static skald_ctx_t _ctx_uid; +static skald_ctx_t _ctx_url; + +int main(void) +{ + LOG_INFO("Skald and the tail of Eddystone\n"); + + /* advertise the defined URI */ + skald_eddystone_uid_t uid = { URI_NAMESPACE, URI_INSTANCE }; + skald_eddystone_uid_adv(&_ctx_uid, &uid, TX_PWR); + + /* also advertise the defined short-URL */ + skald_eddystone_url_adv(&_ctx_url, EDDYSTONE_URL_HTTPS, URL, TX_PWR); + + return 0; +} diff --git a/examples/skald_ibeacon/Makefile b/examples/skald_ibeacon/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..da517defcfcf1cf43f70d72b3b9c9e5387fdda37 --- /dev/null +++ b/examples/skald_ibeacon/Makefile @@ -0,0 +1,21 @@ +# name of your application +APPLICATION = skald_ibeacon + +# If no BOARD is found in the environment, use this default: +BOARD ?= nrf52dk + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +# include Skald +USEMODULE += skald_ibeacon + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +# CFLAGS += -DDEVELHELP + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +include $(RIOTBASE)/Makefile.include diff --git a/examples/skald_ibeacon/README.md b/examples/skald_ibeacon/README.md new file mode 100644 index 0000000000000000000000000000000000000000..37911394ddffa22e32114f8cb7c9d266c0c5f988 --- /dev/null +++ b/examples/skald_ibeacon/README.md @@ -0,0 +1,6 @@ +# Skald iBeacon Example + +This example demonstrates the usage of `Skald` for creating an Apple `iBeacon`. + +Simply compile and flash, and verify your newly created beacon with any type of +BLE scanner. diff --git a/examples/skald_ibeacon/main.c b/examples/skald_ibeacon/main.c new file mode 100644 index 0000000000000000000000000000000000000000..fe9ac9d1042da97f900763a97556778e98ca1fe1 --- /dev/null +++ b/examples/skald_ibeacon/main.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2017-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 Setting up an iBeacon using Skald + * + * @author Hauke Petersen <hauke.petersen@fu-berlin.de> + * + * @} + */ + +#include "log.h" + +#include "net/skald/ibeacon.h" + +/* configure the iBeacon */ +#define UUID { 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, \ + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, } +#define MAJOR (0x0023) +#define MINOR (0x0017) +#define TXPOWER (0U) + +/* allocate a single advertising context */ +static skald_ctx_t _ctx; + +int main(void) +{ + LOG_INFO("Skald and the tail of the iBeacon\n"); + + /* this will configure the iBeacon and start advertising it */ + skald_uuid_t uuid = { UUID }; + skald_ibeacon_advertise(&_ctx, &uuid, MAJOR, MINOR, TXPOWER); + + return 0; +} diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 4fec53670a87cb39bf0bb3b8222ae95fd60f4d30..15b562684e3efafd5f4f6fb89c65ad99b94717a6 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -117,3 +117,8 @@ PSEUDOMODULES += stm32_periph_% # declare periph submodules as pseudomodules, but exclude periph_common PSEUDOMODULES += periph_% NO_PSEUDOMODULES += periph_common + +# Submodules and auto-init code provided by Skald +PSEUDOMODULES += auto_init_skald +PSEUDOMODULES += skald_ibeacon +PSEUDOMODULES += skald_eddystone diff --git a/sys/Makefile b/sys/Makefile index 76701bd74d5398987f56821c50c553722df3e284..cc6d2ab074bcc643ea4ec4a2649237a1c804287d 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -124,6 +124,9 @@ endif ifneq (,$(filter nanocoap,$(USEMODULE))) DIRS += net/application_layer/nanocoap endif +ifneq (,$(filter skald,$(USEMODULE))) + DIRS += net/skald +endif DIRS += $(dir $(wildcard $(addsuffix /Makefile, $(USEMODULE)))) diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index 098b9cd554c49d2ed08f726a9ee9fc5a3f0ec1a5..fcff85bec8a39388994da000c78c9f322f83bdba 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -80,6 +80,10 @@ #include "net/gnrc/ipv6/nib.h" #endif +#ifdef MODULE_SKALD +#include "net/skald.h" +#endif + #define ENABLE_DEBUG (0) #include "debug.h" @@ -151,6 +155,10 @@ void auto_init(void) DEBUG("Auto init gnrc_ipv6_nib module.\n"); gnrc_ipv6_nib_init(); #endif +#ifdef MODULE_SKALD + DEBUG("Auto init Skald\n"); + skald_init(); +#endif /* initialize network devices */ #ifdef MODULE_AUTO_INIT_GNRC_NETIF diff --git a/sys/include/net/ble.h b/sys/include/net/ble.h new file mode 100644 index 0000000000000000000000000000000000000000..ba92caafe5088e7b4c5697caabfa565414b672ec --- /dev/null +++ b/sys/include/net/ble.h @@ -0,0 +1,405 @@ +/* + * 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. + */ + +/** + * @defgroup net_ble BLE defines + * @ingroup net + * @brief General values defined by the BT standard + * @{ + * + * @file + * @brief General BLE values as defined by the BT standard + * + * @author Hauke Petersen <hauke.petersen@fu-berlin.de> + */ + +#ifndef NET_BLE_H +#define NET_BLE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name BT version constants + * @{ + */ +#define BLE_VERSION_40 (0x06) +#define BLE_VERSION_41 (0x07) +#define BLE_VERSION_42 (0x08) +#define BLE_VERSION_50 (0x09) +/** @} */ + +/** + * @name Collections of general BLE constants + * @{ + */ +#define BLE_AA_LEN (4U) /**< access address length */ +#define BLE_ADDR_LEN (6U) /**< link layer address length */ +#define BLE_CRC_LEN (3U) /**< CRC length */ +#define BLE_CHANMAP_LEN (5U) /**< channel map length */ +#define BLE_CHAN_NUMOF (40U) /**< number of available channels */ +#define BLE_CHAN_ADV_NUMOF (3U) /**< number of advertising channels */ +#define BLE_CHAN_DAT_NUMOF (37U) /**< number of data channels */ +/** @} */ + +/** + * @name GATT Declaration UUIDs + * + * @see https://www.bluetooth.com/specifications/gatt/declarations + * @{ + */ +#define BLE_DECL_PRI_SERVICE (0x2800) +#define BLE_DECL_SEC_SERVICE (0x2801) +#define BLE_DECL_INCLUDE (0x2802) +#define BLE_DECL_CHAR (0x2803) +/** @} */ + +/** + * @name GATT descriptor UUIDs + * + * @see https://www.bluetooth.com/specifications/gatt/descriptors + * @{ + */ +#define BLE_DESC_AGGR_FMT (0x2905) +#define BLE_DESC_EXT_PROP (0x2900) +#define BLE_DESC_PRES_FMT (0x2904) +#define BLE_DESC_USER_DESC (0x2901) +#define BLE_DESC_CLIENT_CONFIG (0x2902) +#define BLE_DESC_ENV_CONFIG (0x290b) +#define BLE_DESC_ENV_MEASUREMENT (0x290c) +#define BLE_DESC_ENV_TRIGGER_SETTING (0x290d) +#define BLE_DESC_EXT_REPORT_REF (0x2907) +#define BLE_DESC_NUMOF_DIGITS (0x2909) +#define BLE_DESC_REPORT_REF (0x2908) +#define BLE_DESC_SERVER_CONFIG (0x2903) +#define BLE_DESC_TIME_TRIGGER_SETTING (0x290e) +#define BLE_DESC_VALID_RANGE (0x2906) +#define BLE_DESC_VALUE_TRIGGER_SETTING (0x290a) +/** @} */ + +/** + * @name Characteristic format types (8-bit) + * + * @see https://www.bluetooth.com/specifications/assigned-numbers/format-types + * @{ + */ +#define BLE_UNIT_BLE_FMT_BOOL (0x01) +#define BLE_UNIT_BLE_FMT_2bit (0x02) +#define BLE_UNIT_BLE_FMT_NIBBLE (0x03) +#define BLE_UNIT_BLE_FMT_UINT8 (0x04) +#define BLE_UNIT_BLE_FMT_UINT12 (0x05) +#define BLE_UNIT_BLE_FMT_UINT16 (0x06) +#define BLE_UNIT_BLE_FMT_UINT24 (0x07) +#define BLE_UNIT_BLE_FMT_UINT32 (0x08) +#define BLE_UNIT_BLE_FMT_UINT48 (0x09) +#define BLE_UNIT_BLE_FMT_UINT64 (0x0A) +#define BLE_UNIT_BLE_FMT_UINT128 (0x0B) +#define BLE_UNIT_BLE_FMT_SINT8 (0x0C) +#define BLE_UNIT_BLE_FMT_SINT12 (0x0D) +#define BLE_UNIT_BLE_FMT_SINT16 (0x0E) +#define BLE_UNIT_BLE_FMT_SINT24 (0x0F) +#define BLE_UNIT_BLE_FMT_SINT32 (0x10) +#define BLE_UNIT_BLE_FMT_SINT48 (0x11) +#define BLE_UNIT_BLE_FMT_SINT64 (0x12) +#define BLE_UNIT_BLE_FMT_SINT128 (0x13) +#define BLE_UNIT_BLE_FMT_FLOAT32 (0x14) +#define BLE_UNIT_BLE_FMT_FLOAT64 (0x15) +#define BLE_UNIT_BLE_FMT_SFLOAT (0x16) +#define BLE_UNIT_BLE_FMT_FLOAT (0x17) +#define BLE_UNIT_BLE_FMT_DUINT16 (0x18) +#define BLE_UNIT_BLE_FMT_UTF8 (0x19) +#define BLE_UNIT_BLE_FMT_UTF16 (0x1A) +#define BLE_UNIT_BLE_FMT_STRUCT (0x1B) +/** @} */ + +/** + * @name Units (16-bit) + * + * @see https://www.bluetooth.com/specifications/assigned-numbers/units + * @{ + */ +#define BLE_UNIT_NONE (0x2700) /**< no unit */ +#define BLE_UNIT_BLE_UNIT_METRE (0x2701) /**< length [metre] */ +#define BLE_UNIT_KILOGRAM (0x2702) /**< mass [kilogram] */ +#define BLE_UNIT_SECOND (0x2703) /**< time [second] */ +#define BLE_UNIT_AMPERE (0x2704) /**< electric_current [ampere] */ +#define BLE_UNIT_KELVIN (0x2705) /**< thermodynamic_temperature [kelvin] */ +#define BLE_UNIT_MOLE (0x2706) /**< amount_of_substance [mole] */ +#define BLE_UNIT_CANDELA (0x2707) /**< luminous_intensity [candela] */ +#define BLE_UNIT_SQUARE_METRES (0x2710) /**< area [square_metres] */ +#define BLE_UNIT_CUBIC_METRES (0x2711) /**< volume [cubic_metres] */ +#define BLE_UNIT_METRES_PER_SECOND (0x2712) /**< velocity [metres_per_second] */ +#define BLE_UNIT_METRES_PER_SECOND_SQUARED (0x2713) /**< acceleration [metres_per_second_squared] */ +#define BLE_UNIT_RECIPROCAL_METRE (0x2714) /**< wavenumber [reciprocal_metre] */ +#define BLE_UNIT_KG_PER_CUBIC_METRE (0x2715) /**< density [kilogram_per_cubic_metre] */ +#define BLE_UNIT_KG_PER_SQUARE_METRE (0x2716) /**< surface_density [kilogram_per_square_metre] */ +#define BLE_UNIT_CUBIC_METRE_PER_KILOGRAM (0x2717) /**< specific_volume [cubic_metre_per_kilogram] */ +#define BLE_UNIT_AMPERE_PER_SQUARE_METRE (0x2718) /**< current_density [ampere_per_square_metre] */ +#define BLE_UNIT_AMPERE_PER_METRE (0x2719) /**< magnetic_field_strength [ampere_per_metre] */ +#define BLE_UNIT_MOLE_PER_CUBIC_METRE (0x271a) /**< amount_concentration [mole_per_cubic_metre] */ +#define BLE_UNIT_KILOGRAM_PER_CUBIC_METRE (0x271b) /**< mass_concentration [kilogram_per_cubic_metre] */ +#define BLE_UNIT_CANDELA_PER_SQUARE_METRE (0x271c) /**< luminance [candela_per_square_metre] */ +#define BLE_UNIT_REFRACTIVE_INDEX (0x271d) /**< refractive index */ +#define BLE_UNIT_RELATIVE_PERMEABILITY (0x271e) /**< relative permeability */ +#define BLE_UNIT_RADIAN (0x2720) /**< plane_angle [radian] */ +#define BLE_UNIT_STERADIAN (0x2721) /**< solid_angle [steradian] */ +#define BLE_UNIT_HERTZ (0x2722) /**< frequency [hertz] */ +#define BLE_UNIT_NEWTON (0x2723) /**< force [newton] */ +#define BLE_UNIT_PASCAL (0x2724) /**< pressure [pascal] */ +#define BLE_UNIT_JOULE (0x2725) /**< energy [joule] */ +#define BLE_UNIT_WATT (0x2726) /**< power [watt] */ +#define BLE_UNIT_COULOMB (0x2727) /**< electric_charge [coulomb] */ +#define BLE_UNIT_VOLT (0x2728) /**< electric_potential_difference [volt] */ +#define BLE_UNIT_FARAD (0x2729) /**< capacitance [farad] */ +#define BLE_UNIT_OHM (0x272a) /**< electric_resistance [ohm] */ +#define BLE_UNIT_SIEMENS (0x272b) /**< electric_conductance [siemens] */ +#define BLE_UNIT_WEBER (0x272c) /**< magnetic_flux [weber] */ +#define BLE_UNIT_TESLA (0x272d) /**< magnetic_flux_density [tesla] */ +#define BLE_UNIT_HENRY (0x272e) /**< inductance [henry] */ +#define BLE_UNIT_DEGREE_CELSIUS (0x272f) /**< thermodynamic_temperature [degree_celsius] */ +#define BLE_UNIT_LUMEN (0x2730) /**< luminous_flux [lumen] */ +#define BLE_UNIT_LUX (0x2731) /**< illuminance [lux] */ +#define BLE_UNIT_BECQUEREL (0x2732) /**< activity_referred_to_a_radionuclide [becquerel] */ +#define BLE_UNIT_GRAY (0x2733) /**< absorbed_dose [gray] */ +#define BLE_UNIT_SIEVERT (0x2734) /**< dose_equivalent [sievert] */ +#define BLE_UNIT_KATAL (0x2735) /**< catalytic_activity [katal] */ +#define BLE_UNIT_PASCAL_SECOND (0x2740) /**< dynamic_viscosity [pascal_second] */ +#define BLE_UNIT_NEWTON_METRE (0x2741) /**< moment_of_force [newton_metre] */ +#define BLE_UNIT_NEWTON_PER_METRE (0x2742) /**< surface_tension [newton_per_metre] */ +#define BLE_UNIT_RADIAN_PER_SECOND (0x2743) /**< angular_velocity [radian_per_second] */ +#define BLE_UNIT_RADIAN_PER_SECOND_SQUARED (0x2744) /**< angular_acceleration [radian_per_second_squared] */ +#define BLE_UNIT_HEAT_FLUX_WATT_PER_M2 (0x2745) /**< heat_flux_density [watt_per_square_metre] */ +#define BLE_UNIT_JOULE_PER_KELVIN (0x2746) /**< heat_capacity [joule_per_kelvin] */ +#define BLE_UNIT_JOULE_PER_KG_KELVIN (0x2747) /**< specific_heat_capacity [joule_per_kilogram_kelvin] */ +#define BLE_UNIT_JOULE_PER_KG (0x2748) /**< specific_energy [joule_per_kilogram] */ +#define BLE_UNIT_WATT_PER_METRE_KELVIN (0x2749) /**< thermal_conductivity [watt_per_metre_kelvin] */ +#define BLE_UNIT_JOULE_PER_CUBIC_METRE (0x274a) /**< energy_density [joule_per_cubic_metre] */ +#define BLE_UNIT_VOLT_PER_METRE (0x274b) /**< electric_field_strength [volt_per_metre] */ +#define BLE_UNIT_COULOMB_PER_CUBIC_METRE (0x274c) /**< electric_charge_density [coulomb_per_cubic_metre] */ +#define BLE_UNIT_COULOMB_PER_M2 (0x274d) /**< surface_charge_density [coulomb_per_square_metre] */ +#define BLE_UNIT_FLUX_COULOMB_PER_M2 (0x274e) /**< electric_flux_density [coulomb_per_square_metre] */ +#define BLE_UNIT_FARAD_PER_METRE (0x274f) /**< permittivity [farad_per_metre] */ +#define BLE_UNIT_HENRY_PER_METRE (0x2750) /**< permeability [henry_per_metre] */ +#define BLE_UNIT_JOULE_PER_MOLE (0x2751) /**< molar_energy [joule_per_mole] */ +#define BLE_UNIT_JOULE_PER_MOLE_KELVIN (0x2752) /**< molar_entropy [joule_per_mole_kelvin] */ +#define BLE_UNIT_COULOMB_PER_KG (0x2753) /**< exposure [coulomb_per_kilogram] */ +#define BLE_UNIT_GRAY_PER_SECOND (0x2754) /**< absorbed_dose_rate [gray_per_second] */ +#define BLE_UNIT_WATT_PER_STERADIAN (0x2755) /**< radiant_intensity [watt_per_steradian] */ +#define BLE_UNIT_WATT_PER_M2_STERADIAN (0x2756) /**< radiance [watt_per_square_metre_steradian] */ +#define BLE_UNIT_KATAL_PER_CUBIC_METRE (0x2757) /**< catalytic_activity_concentration [katal_per_cubic_metre] */ +#define BLE_UNIT_MINUTE (0x2760) /**< time [minute] */ +#define BLE_UNIT_HOUR (0x2761) /**< time [hour] */ +#define BLE_UNIT_DAY (0x2762) /**< time [day] */ +#define BLE_UNIT_ANGLE_DEGREE (0x2763) /**< plane_angle [degree] */ +#define BLE_UNIT_ANGLE_MINUTE (0x2764) /**< plane_angle [minute] */ +#define BLE_UNIT_ANGLE_SECOND (0x2765) /**< plane_angle [second] */ +#define BLE_UNIT_HECTARE (0x2766) /**< area [hectare] */ +#define BLE_UNIT_LITRE (0x2767) /**< volume [litre] */ +#define BLE_UNIT_TONNE (0x2768) /**< mass [tonne] */ +#define BLE_UNIT_BAR (0x2780) /**< pressure [bar] */ +#define BLE_UNIT_MILLIMETRE_OF_MERCURY (0x2781) /**< pressure [millimetre_of_mercury] */ +#define BLE_UNIT_NGSTRM (0x2782) /**< length [ngstrm] */ +#define BLE_UNIT_NAUTICAL_MILE (0x2783) /**< length [nautical_mile] */ +#define BLE_UNIT_BARN (0x2784) /**< area [barn] */ +#define BLE_UNIT_KNOT (0x2785) /**< velocity [knot] */ +#define BLE_UNIT_NEPER (0x2786) /**< logarithmic_radio_quantity [neper] */ +#define BLE_UNIT_BEL (0x2787) /**< logarithmic_radio_quantity [bel] */ +#define BLE_UNIT_YARD (0x27a0) /**< length [yard] */ +#define BLE_UNIT_PARSEC (0x27a1) /**< length [parsec] */ +#define BLE_UNIT_INCH (0x27a2) /**< length [inch] */ +#define BLE_UNIT_FOOT (0x27a3) /**< length [foot] */ +#define BLE_UNIT_MILE (0x27a4) /**< length [mile] */ +#define BLE_UNIT_POUND_FORCE_PER_SQU_INCH (0x27a5) /**< pressure [pound_force_per_square_inch] */ +#define BLE_UNIT_KILOMETRE_PER_HOUR (0x27a6) /**< velocity [kilometre_per_hour] */ +#define BLE_UNIT_MILE_PER_HOUR (0x27a7) /**< velocity [mile_per_hour] */ +#define BLE_UNIT_REVOLUTION_PER_MINUTE (0x27a8) /**< angular_velocity [revolution_per_minute] */ +#define BLE_UNIT_GRAM_CALORIE (0x27a9) /**< energy [gram_calorie] */ +#define BLE_UNIT_KG_CALORIE (0x27aa) /**< energy [kilogram_calorie] */ +#define BLE_UNIT_KILOWATT_HOUR (0x27ab) /**< energy [kilowatt_hour] */ +#define BLE_UNIT_DEGREE_FAHRENHEIT (0x27ac) /**< thermodynamic_temperature [degree_fahrenheit] */ +#define BLE_UNIT_PERCENTAGE (0x27ad) /**< percentage */ +#define BLE_UNIT_PER_MILLE (0x27ae) /**< per mille */ +#define BLE_UNIT_BEATS_PER_MINUTE (0x27af) /**< period [beats_per_minute] */ +#define BLE_UNIT_AMPERE_HOURS (0x27b0) /**< electric_charge [ampere_hours] */ +#define BLE_UNIT_MILLIGRAM_PER_DECILITRE (0x27b1) /**< mass_density [milligram_per_decilitre] */ +#define BLE_UNIT_MILLIMOLE_PER_LITRE (0x27b2) /**< mass_density [millimole_per_litre] */ +#define BLE_UNIT_YEAR (0x27b3) /**< time [year] */ +#define BLE_UNIT_MONTH (0x27b4) /**< time [month] */ +#define BLE_UNIT_COUNT_PER_CUBIC_METRE (0x27b5) /**< concentration [count_per_cubic_metre] */ +#define BLE_UNIT_WATT_PER_SQUARE_METRE (0x27b6) /**< irradiance [watt_per_square_metre] */ +#define BLE_UNIT_MLIT_PER_KG_PER_MINUTE (0x27b7) /**< transfer_rate [milliliter_per_kilogram_per_minute] */ +#define BLE_UNIT_POUND (0x27b8) /**< mass [pound] */ +#define BLE_UNIT_METABOLIC_EQU (0x27b9) /**< metabolic equivalent */ +#define BLE_UNIT_STEP_PER_MINUTE (0x27ba) /**< steps per minute */ +#define BLE_UNIT_STROKE_PER_MINUTE (0x27bc) /**< strokes per minute */ +#define BLE_UNIT_KILOMETER_PER_MINUTE (0x27bd) /**< velocity [kilometer_per_minute] */ +#define BLE_UNIT_LUMEN_PER_WATT (0x27be) /**< luminous_efficacy [lumen_per_watt] */ +#define BLE_UNIT_LUMEN_HOUR (0x27bf) /**< luminous_energy [lumen_hour] */ +#define BLE_UNIT_LUX_HOUR (0x27c0) /**< luminous_exposure [lux_hour] */ +#define BLE_UNIT_GRAM_PER_SECOND (0x27c1) /**< mass_flow [gram_per_second] */ +#define BLE_UNIT_LITRE_PER_SECOND (0x27c2) /**< volume_flow [litre_per_second] */ +/** @} */ + +/** + * @name ATT protocol opcodes + * @{ + */ +#define BLE_ATT_ERROR_RESP (0x01) +#define BLE_ATT_MTU_REQ (0x02) +#define BLE_ATT_MTU_RESP (0x03) +#define BLE_ATT_FIND_INFO_REQ (0x04) +#define BLE_ATT_FIND_INFO_RESP (0x05) +#define BLE_ATT_FIND_BY_VAL_REQ (0x06) +#define BLE_ATT_FIND_BY_VAL_RESP (0x07) +#define BLE_ATT_READ_BY_TYPE_REQ (0x08) +#define BLE_ATT_READ_BY_TYPE_RESP (0x09) +#define BLE_ATT_READ_REQ (0x0a) +#define BLE_ATT_READ_RESP (0x0b) +#define BLE_ATT_READ_BLOB_REQ (0x0c) +#define BLE_ATT_READ_BLOB_RESP (0x0d) +#define BLE_ATT_READ_MUL_REQ (0x0e) +#define BLE_ATT_READ_MUL_RESP (0x0f) +#define BLE_ATT_READ_BY_GROUP_TYPE_REQ (0x10) +#define BLE_ATT_READ_BY_GROUP_TYPE_RESP (0x11) +#define BLE_ATT_WRITE_REQ (0x12) +#define BLE_ATT_WRITE_RESP (0x13) +#define BLE_ATT_WRITE_COMMAND (0x52) +#define BLE_ATT_PREP_WRITE_REQ (0x16) +#define BLE_ATT_PREP_WRITE_RESP (0x17) +#define BLE_ATT_EXEC_WRITE_REQ (0x18) +#define BLE_ATT_EXEC_WRITE_RESP (0x19) +#define BLE_ATT_VAL_NOTIFICATION (0x1b) +#define BLE_ATT_VAL_INDICATION (0x1d) +#define BLE_ATT_VAL_CONFIRMATION (0x1e) +#define BLE_ATT_SIGNED_WRITE_CMD (0xd2) +/** @} */ + +/** + * @name ATT protocol error codes + * @{ + */ +#define BLE_ATT_INVALID_HANDLE (0x01) +#define BLE_ATT_READ_NOT_PERMITTED (0x02) +#define BLE_ATT_WRITE_NOT_PERMITTED (0x03) +#define BLE_ATT_INVALID_PDU (0x04) +#define BLE_ATT_INSUFFICIENT_AUTHEN (0x05) +#define BLE_ATT_REQUEST_NOT_SUP (0x06) +#define BLE_ATT_INVALID_OFFSET (0x07) +#define BLE_ATT_INSUFFICIENT_AUTHOR (0x08) +#define BLE_ATT_PREPARE_QUEUE_FULL (0x09) +#define BLE_ATT_ATTRIBUTE_NOT_FOUND (0x0a) +#define BLE_ATT_ATTRIBUTE_NOT_LONG (0x0b) +#define BLE_ATT_INSUFFICENT_KEY_SIZE (0x0c) +#define BLE_ATT_INVALID_ATTR_VAL_LEN (0x0d) +#define BLE_ATT_ULIKELY_ERROR (0x0e) +#define BLE_ATT_INSUFFICIENT_ENCRYPTION (0x0f) +#define BLE_ATT_UNSUPPORTED_GROUP_TYPE (0x10) +#define BLE_ATT_INSUFFICIENT_RESSOURCES (0x11) +/** @} */ + +/** + * @name ATT property flags + * @{ + */ +#define BLE_ATT_BROADCAST (0x01) +#define BLE_ATT_READ (0x02) +#define BLE_ATT_WRITE_WO_RESP (0x04) +#define BLE_ATT_WRITE (0x08) +#define BLE_ATT_NOTIFY (0x10) +#define BLE_ATT_INDICATE (0x20) +#define BLE_ATT_AUTH_SIGNED_WRITES (0x40) +#define BLE_ATT_EXT_PROPERTIES (0x80) +/** @} */ + +/** + * @name Flags used in certain types of ATT PDUs + * @{ + */ +#define BLE_ATT_FORMAT_U16 (0x01) /**< used in FIND_INFO_RESP */ +#define BLE_ATT_FORMAT_U128 (0x02) /**< used in FIND_INFO_RESP */ +/** @} */ + +/** + * @name Flags used in GAP advertisement packets + * @{ + */ +#define BLE_GAP_DISCOVER_LIM (0x01) +#define BLE_GAP_DISCOVERABLE (0x02) +#define BLE_GAP_FLAG_BREDR_NOTSUP (0x04) +/** @} */ + +/** + * @name BLE advertising packet types + * @{ + */ +#define BLE_PDU_MASK (0x0f) +#define BLE_ADV_IND (0x00) +#define BLE_DIRECT_IND (0x01) +#define BLE_ADV_NONCON_IND (0x02) +#define BLE_SCAN_REQ (0x03) +#define BLE_AUX_SCAN_REQ (0x03) +#define BLE_SCAN_RESP (0x04) +#define BLE_CONNECT_IND (0x05) +#define BLE_AUX_CONNECT_REQ (0x05) +#define BLE_ADV_SCAN_IND (0x06) +#define BLE_ADV_EXT_IND (0x07) +#define BLE_AUX_ADV_IND (0x07) +#define BLE_AUX_SCAN_RSP (0x07) +#define BLE_AUX_SYNC_IND (0x07) +#define BLE_AUX_CHAIN_IND (0x07) +#define BLE_CONNECT_RESP (0x08) +/** @} */ + +/** + * @name Advertising packet flags + * @{ + */ +#define BLE_LL_FLAG_CHSEL (0x20) +#define BLE_LL_FLAG_TXADD (0x40) +#define BLE_LL_FLAG_RXADD (0x80) +/** @} */ + +/** + * @name Link layer control message opcodes + * @{ + */ +#define BLE_LL_CONN_UPDATE_IND (0x00) +#define BLE_LL_CHANNEL_MAP_IND (0x01) +#define BLE_LL_TERMINATE_IND (0x02) +#define BLE_LL_ENC_REQ (0x03) +#define BLE_LL_ENC_RSP (0x04) +#define BLE_LL_START_ENC_REQ (0x05) +#define BLE_LL_START_ENC_RSP (0x06) +#define BLE_LL_UNKNOWN_RSP (0x07) +#define BLE_LL_FEATURE_REQ (0x08) +#define BLE_LL_FEATURE_RSP (0x09) +#define BLE_LL_PAUSE_ENC_REQ (0x0a) +#define BLE_LL_PAUSE_ENC_RSP (0x0b) +#define BLE_LL_VERSION_IND (0x0c) +#define BLE_LL_REJECT_IND (0x0d) +#define BLE_LL_SLAVE_FEATURE_REQ (0x0e) +#define BLE_LL_CONN_PARAM_REQ (0x0f) +#define BLE_LL_CONN_PARAM_RSP (0x10) +#define BLE_LL_REJECT_EXT_IND (0x11) +#define BLE_LL_PING_REQ (0x12) +#define BLE_LL_PING_RSP (0x13) +#define BLE_LL_LENGTH_REQ (0x14) +#define BLE_LL_LENGTH_RSP (0x15) +#define BLE_LL_PHY_REQ (0x16) +#define BLE_LL_PHY_RSP (0x17) +#define BLE_LL_PHY_UPDATE_IND (0x18) +#define BLE_LL_MIN_USED_CHAN_IND (0x19) +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* NET_BLE_H */ +/** @} */ diff --git a/sys/include/net/eddystone.h b/sys/include/net/eddystone.h new file mode 100644 index 0000000000000000000000000000000000000000..31c8fd280f2200a360432556b36aa68baac15ede --- /dev/null +++ b/sys/include/net/eddystone.h @@ -0,0 +1,63 @@ +/* + * 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. + */ + +/** + * @defgroup net_eddystone Eddystone + * @ingroup net + * @brief General values defined by the BLE Eddystone beacon format + * + * @see https://github.com/google/eddystone/blob/master/protocol-specification.md + * @{ + * + * @file + * @brief Constants defined by the Eddystone specification + * + * @author Hauke Petersen <hauke.petersen@fu-berlin.de> + */ + +#ifndef NET_EDDYSTONE_H +#define NET_EDDYSTONE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Collection of general Eddystone constants + * @{ + */ +#define EDDYSTONE_NAMESPACE_LEN (10U) +#define EDDYSTONE_INSTANCE_LEN (6U) +/** @} */ + +/** + * @name URL scheme prefix values + * @{ + */ +#define EDDYSTONE_URL_HTTP_WWW (0x00) /**< `http://www.URL` */ +#define EDDYSTONE_URL_HTTPS_WWWW (0x01) /**< `https://www.URL` */ +#define EDDYSTONE_URL_HTTP (0x02) /**< `http://URL` */ +#define EDDYSTONE_URL_HTTPS (0x03) /**< `https://URL` */ +/** @} */ + +/** + * @name Eddystone frame types + * @{ + */ +#define EDDYSTONE_UID (0x00) +#define EDDYSTONE_URL (0x10) +#define EDDYSTONE_TLM (0x20) +#define EDDYSTONE_EID (0x30) +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* NET_EDDYSTONE_H */ +/** @} */ diff --git a/sys/include/net/netopt.h b/sys/include/net/netopt.h index d921aa72ed3e2d34cf8448505749ca7ca2fcaf7d..61e1965e2ad3c5dfee4708197685bd6f602b8755 100644 --- a/sys/include/net/netopt.h +++ b/sys/include/net/netopt.h @@ -532,6 +532,8 @@ typedef enum { */ NETOPT_TX_RETRIES_NEEDED, + NETOPT_BLE_CTX, /**< set radio context (channel, CRC, AA) */ + /* add more options if needed */ /** diff --git a/sys/include/net/skald.h b/sys/include/net/skald.h new file mode 100644 index 0000000000000000000000000000000000000000..4583341c53cb02f295272d02990933e0a5c15b17 --- /dev/null +++ b/sys/include/net/skald.h @@ -0,0 +1,124 @@ +/* + * 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. + */ + +/** + * @defgroup net_skald Skald, who advertises to the world + * @ingroup net + * @brief Skald, a minimalistic BLE advertising stack + * + * # About + * + * Skald is a very minimalistic BLE implementation, implementing the + * `broadcaster` role only. With this focus, the stack allows for setting up + * different kind of beacons using an extremely low memory footprint. + * + * # Design Decisions and Limitations + * - support for local addresses only (using `luid` to generate them) + * - advertising interval is configured during compile time, override by setting + * `CFLAGS+=-DSKALD_INTERVAL=xxx` + * - advertising channels are configured during compile time, override by + * setting `CFLAGS+=-DSKALD_ADV_CHAN={37,39}` + * + * # Implementation state + * Supported: + * - advertising of custom GAP payloads + * - iBeacon (full support) + * - Eddystone (partly supported) + * + * Limitations: + * - currently Skald supports random static addresses only (generated using + * the `luid` module) + * + * @{ + * @file + * @brief Skald's basic interface + * + * @author Hauke Petersen <hauke.petersen@fu-berlin.de> + */ + +#ifndef NET_SKALD_H +#define NET_SKALD_H + +#include <stdint.h> + +#include "xtimer.h" +#include "net/ble.h" +#include "net/netdev/ble.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Static advertising interval + */ +#ifndef SKALD_INTERVAL +#define SKALD_INTERVAL (1 * US_PER_SEC) +#endif + +/** + * @brief Static list of used advertising channels + */ +#ifndef SKALD_ADV_CHAN +#define SKALD_ADV_CHAN { 37, 38, 39 } +#endif + +/** + * @brief UUID representation format used by Skald + */ +typedef struct { + uint8_t u8[16]; /**< UUID with byte-wise access */ +} skald_uuid_t; + +/** + * @brief Advertising context holding the advertising data and state + */ +typedef struct { + netdev_ble_pkt_t pkt; /**< packet holding the advertisement (GAP) data */ + xtimer_t timer; /**< timer for scheduling advertising events */ + uint32_t last; /**< last timer trigger (for offset compensation) */ + uint8_t cur_chan; /**< keep track of advertising channels */ +} skald_ctx_t; + +/** + * @brief Initialize Skald and the underlying radio + */ +void skald_init(void); + +/** + * @brief Start advertising the given packet + * + * The packet will be send out each advertising interval (see SKALD_INTERVAL) on + * each of the defined advertising channels (see SKALD_ADV_CHAN). + * + * @param[in,out] ctx start advertising this context + */ +void skald_adv_start(skald_ctx_t *ctx); + +/** + * @brief Stop the ongoing advertisement + * + * @param[in,out] ctx stop advertising this context + */ +void skald_adv_stop(skald_ctx_t *ctx); + +/** + * @brief Generate a random public address + * + * @note @p buf must be able to hold BLE_ADDR_LEN (6) bytes + * + * @param[out] buf the generated address is written to this buffer + */ +void skald_generate_random_addr(uint8_t *buf); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_SKALD_H */ +/** @} */ diff --git a/sys/include/net/skald/eddystone.h b/sys/include/net/skald/eddystone.h new file mode 100644 index 0000000000000000000000000000000000000000..9c8ddd63651fa54b9e5d9056ea87818c4fb4d432 --- /dev/null +++ b/sys/include/net/skald/eddystone.h @@ -0,0 +1,83 @@ +/* + * 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. + */ + +/** + * @defgroup net_skald_eddystone Skald meets Eddy + * @ingroup net_skald + * @brief Skald's Eddystone implementation + * + * # About + * This module allows for creation and advertisement of Eddystone beacons (see + * https://github.com/google/eddystone). + * + * + * # Implementation state + * supported: + * - Eddystone-UID + * - Eddystone-URL + * + * not (yet) supported: + * - Eddystone-TLM + * - Eddystone-EID + * + * @{ + * @file + * @brief Skald's basic interface + * + * @author Hauke Petersen <hauke.petersen@fu-berlin.de> + */ + +#ifndef NET_SKALD_EDDYSTONE_H +#define NET_SKALD_EDDYSTONE_H + +#include "net/eddystone.h" +#include "net/skald.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Unique and opaque 16-byte beacon id format used by Eddystone + */ +typedef struct __attribute__((packed)) { + uint8_t namespace[EDDYSTONE_NAMESPACE_LEN]; /**< 10-byte namespace */ + uint8_t instance[EDDYSTONE_INSTANCE_LEN]; /**< 6-byte instance */ +} skald_eddystone_uid_t; + +/** + * @brief Advertise Eddystone-UID data + * + * @see https://github.com/google/eddystone/tree/master/eddystone-uid + * + * @param[out] ctx advertising context + * @param[in] uid UID to advertise + * @param[in] tx_pwr calibrated TX power to be advertised by the beacon + */ +void skald_eddystone_uid_adv(skald_ctx_t *ctx, + const skald_eddystone_uid_t *uid, uint8_t tx_pwr); + +/** + * @brief Advertise Eddystone-URL data + * + * @see https://github.com/google/eddystone/tree/master/eddystone-url + * + * @param[out] ctx advertising context + * @param[in] scheme encoded URL scheme prefix + * @param[in] url (short) url as \0 terminated string + * @param[in] tx_pwr calibrated TX power to be advertised by the beacon + */ +void skald_eddystone_url_adv(skald_ctx_t *ctx, + uint8_t scheme, const char *url, uint8_t tx_pwr); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_SKALD_EDDYSTONE_H */ +/** @} */ diff --git a/sys/include/net/skald/ibeacon.h b/sys/include/net/skald/ibeacon.h new file mode 100644 index 0000000000000000000000000000000000000000..0f8743006815fe33eba7f273df46c6be9c098968 --- /dev/null +++ b/sys/include/net/skald/ibeacon.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + +/** + * @defgroup net_skald_ibeacon Skald about iBeacon + * @ingroup net_skald + * @brief Skald's simple iBeacon abstraction + * + * # About + * This Skald module supports the creation and advertisement of BLE iBeacons as + * defined by Apple (see https://developer.apple.com/ibeacon/). + * + * # Implementation state + * - all known iBeacon properties are supported + * + * @{ + * @file + * @brief Skald's basic interface + * + * @author Hauke Petersen <hauke.petersen@fu-berlin.de> + */ + +#ifndef NET_SKALD_IBEACON_H +#define NET_SKALD_IBEACON_H + +#include "net/skald.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Configure the IBeacon payload and start advertising + * + * @param[out] ctx advertising context + * @param[in] uuid UUID advertised by the iBeacon + * @param[in] major the iBeacon's major number + * @param[in] minor the iBeacon's minor number + * @param[in] txpower calibrated TX power to be advertised by the beacon + */ +void skald_ibeacon_advertise(skald_ctx_t *ctx, const skald_uuid_t *uuid, + uint16_t major, uint16_t minor, uint8_t txpower); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_SKALD_IBEACON_H */ +/** @} */ diff --git a/sys/net/crosslayer/netopt/netopt.c b/sys/net/crosslayer/netopt/netopt.c index 951d2f0a90e1f215d834275809fc6107d1aa2759..e9ba46d5a436104bbf5f6e21c5330df85d34f0de 100644 --- a/sys/net/crosslayer/netopt/netopt.c +++ b/sys/net/crosslayer/netopt/netopt.c @@ -89,6 +89,7 @@ static const char *_netopt_strmap[] = { [NETOPT_IQ_INVERT] = "NETOPT_IQ_INVERT", [NETOPT_TX_RETRIES_NEEDED] = "NETOPT_TX_RETRIES_NEEDED", [NETOPT_6LO_IPHC] = "NETOPT_6LO_IPHC", + [NETOPT_BLE_CTX] = "NETOPT_BLE_CTX", [NETOPT_NUMOF] = "NETOPT_NUMOF", }; diff --git a/sys/net/skald/Makefile b/sys/net/skald/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..c835b4d8ddc51de4dbc3d9c1c0611cbed34bd3b1 --- /dev/null +++ b/sys/net/skald/Makefile @@ -0,0 +1,11 @@ +SRC = skald.c + +ifneq (,$(filter skald_ibeacon,$(USEMODULE))) + SRC += skald_ibeacon.c +endif + +ifneq (,$(filter skald_eddystone,$(USEMODULE))) + SRC += skald_eddystone.c +endif + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/skald/skald.c b/sys/net/skald/skald.c new file mode 100644 index 0000000000000000000000000000000000000000..858f8b03a5816a42394ae5e38d6dd4346feae305 --- /dev/null +++ b/sys/net/skald/skald.c @@ -0,0 +1,158 @@ +/* + * 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 net_skald + * @{ + * + * @file + * @brief Skald's link layer implementation + * + * @author Hauke Petersen <hauke.petersen@fu-berlin.de> + * + * @} + */ + +#include <stdint.h> + +#include "assert.h" +#include "random.h" +#include "luid.h" + +#include "net/netdev/ble.h" +#include "net/skald.h" + +/* include fitting radio driver */ +#if defined(MODULE_NRFBLE) +#include "nrfble.h" +/* add other BLE radio drivers once implemented - and potentially move to + * auto-init at some point */ +#else +#error "[skald] error: unable to find any netdev-ble capable radio" +#endif + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define JITTER_MIN (0U) /* 0ms */ +#define JITTER_MAX (10000U) /* 10ms */ + +#define ADV_CHAN_NUMOF sizeof(_adv_chan) +#define ADV_AA (0x8e89bed6) /* access address */ +#define ADV_CRC (0x00555555) /* CRC initializer */ + +static const uint8_t _adv_chan[] = SKALD_ADV_CHAN; + +static netdev_ble_ctx_t _ble_ctx = { + .aa.u32 = ADV_AA, + .crc = ADV_CRC, +}; + +static netdev_t *_radio; + +static void _stop_radio(void) +{ + netdev_ble_stop(_radio); + _radio->context = NULL; +} + +static void _sched_next(skald_ctx_t *ctx) +{ + ctx->last += SKALD_INTERVAL; + /* schedule next advertising event, adding a random jitter between + * 0ms and 10ms (see spec v5.0-vol6-b-4.4.2.2.1) */ + ctx->last += random_uint32_range(JITTER_MIN, JITTER_MAX); + /* compensate the time passed since the timer triggered last by using the + * current value of the timer */ + xtimer_set(&ctx->timer, (ctx->last - xtimer_now_usec())); +} + +static void _on_adv_evt(void *arg) +{ + skald_ctx_t *ctx = (skald_ctx_t *)arg; + + /* advertise on the next adv channel - or skip this event if the radio is + * busy */ + if ((ctx->cur_chan < ADV_CHAN_NUMOF) && (_radio->context == NULL)) { + _radio->context = ctx; + _ble_ctx.chan = _adv_chan[ctx->cur_chan]; + netdev_ble_set_ctx(_radio, &_ble_ctx); + netdev_ble_send(_radio, &ctx->pkt); + ++ctx->cur_chan; + } + else { + ctx->cur_chan = 0; + _sched_next(ctx); + } +} + +static void _on_radio_evt(netdev_t *netdev, netdev_event_t event) +{ + (void)netdev; + + if (event == NETDEV_EVENT_TX_COMPLETE) { + skald_ctx_t *ctx = _radio->context; + _stop_radio(); + xtimer_set(&ctx->timer, 150); + } +} + +void skald_init(void) +{ + assert(dev); + + /* setup and a fitting radio driver - potentially move to auto-init at some + * point */ +#if defined(MODULE_NRFBLE) + _radio = nrfble_setup(); +#endif + + _radio->event_callback = _on_radio_evt; + _radio->driver->init(_radio); +} + +void skald_adv_start(skald_ctx_t *ctx) +{ + assert(ctx); + + /* make sure the given context is not advertising at the moment */ + skald_adv_stop(ctx); + + /* initialize advertising context */ + ctx->timer.callback = _on_adv_evt; + ctx->timer.arg = ctx; + ctx->last = xtimer_now_usec(); + ctx->cur_chan = 0; + ctx->pkt.flags = (BLE_ADV_NONCON_IND | BLE_LL_FLAG_TXADD); + + /* start advertising */ + _sched_next(ctx); +} + +void skald_adv_stop(skald_ctx_t *ctx) +{ + assert(ctx); + + xtimer_remove(&ctx->timer); + if (_radio->context == (void *)ctx) { + _stop_radio(); + } +} + +void skald_generate_random_addr(uint8_t *buf) +{ + assert(buf); + + luid_get(buf, BLE_ADDR_LEN); + /* swap byte 0 and 5, so that the unique byte given by luid does not clash + * with universal/local and individual/group bits of address */ + uint8_t tmp = buf[5]; + buf[5] = buf[0]; + /* make address individual and local */ + buf[0] = ((tmp & 0xfc) | 0x02); +} diff --git a/sys/net/skald/skald_eddystone.c b/sys/net/skald/skald_eddystone.c new file mode 100644 index 0000000000000000000000000000000000000000..847d6f09d807821d2cb62bf1189ef22ea52dc6f4 --- /dev/null +++ b/sys/net/skald/skald_eddystone.c @@ -0,0 +1,109 @@ +/* + * 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 net_skald_eddystone + * @{ + * + * @file + * @brief Skald's Eddystone implementation + * + * @author Hauke Petersen <hauke.petersen@fu-berlin.de> + * + * @} + */ + +#include <string.h> + +#include "assert.h" +#include "net/skald/eddystone.h" + +#define PREAMBLE_LEN (11U) +#define PA_LEN (7U) +#define PB_LEN (3U) + +#define URL_HDR_LEN (6U) + +#define UID_LEN (23U) + +typedef struct __attribute__((packed)) { + uint8_t txadd[BLE_ADDR_LEN]; + uint8_t pa[PA_LEN]; + uint8_t service_data_len; + uint8_t pb[PB_LEN]; + uint8_t type; +} pre_t; + +typedef struct __attribute__((packed)) { + pre_t pre; + uint8_t tx_pwr; + uint8_t namespace[EDDYSTONE_NAMESPACE_LEN]; + uint8_t instance[EDDYSTONE_INSTANCE_LEN]; + uint8_t reserved[2]; +} eddy_uid_t; + +typedef struct __attribute__((packed)) { + pre_t pre; + uint8_t tx_pwr; + uint8_t scheme; + uint8_t url[]; +} eddy_url_t; + +/* ćonstant GAP data preamble parts, containing the following GAP fields: + * - flags: BR/EDR not support set + * - complete list of 16-bit UUIDs: holding the Eddystone UUID only (0xfeaa) + * - service data of type 0xfeaa (Eddystone) */ +static const uint8_t _pa[PA_LEN] = { 0x02, 0x01, 0x04, 0x03, 0x03, 0xaa, 0xfe }; +static const uint8_t _pb[PB_LEN] = { 0x16, 0xaa, 0xfe }; + +static void _init_pre(pre_t *data, uint8_t type, uint8_t len) +{ + skald_generate_random_addr(data->txadd); + memcpy(data->pa, _pa, PA_LEN); + memcpy(data->pb, _pb, PB_LEN); + data->service_data_len = len; + data->type = type; +} + +void skald_eddystone_uid_adv(skald_ctx_t *ctx, + const skald_eddystone_uid_t *uid, uint8_t tx_pwr) +{ + assert(ctx && uid); + + eddy_uid_t *pdu = (eddy_uid_t *)ctx->pkt.pdu; + _init_pre(&pdu->pre, EDDYSTONE_UID, UID_LEN); + + pdu->tx_pwr = tx_pwr; + memcpy(pdu->namespace, uid->namespace, EDDYSTONE_NAMESPACE_LEN); + memcpy(pdu->instance, uid->instance, EDDYSTONE_INSTANCE_LEN); + memset(pdu->reserved, 0, 2); + + /* start advertising */ + ctx->pkt.len = sizeof(eddy_uid_t); + skald_adv_start(ctx); +} + +void skald_eddystone_url_adv(skald_ctx_t *ctx, + uint8_t scheme, const char *url, uint8_t tx_pwr) +{ + assert(url && ctx); + size_t len = strlen(url); + assert(len <= (NETDEV_BLE_PDU_MAXLEN - (URL_HDR_LEN + PREAMBLE_LEN))); + + eddy_url_t *pdu = (eddy_url_t *)ctx->pkt.pdu; + _init_pre(&pdu->pre, EDDYSTONE_URL, (URL_HDR_LEN + len)); + + /* set remaining service data fields */ + pdu->tx_pwr = tx_pwr; + pdu->scheme = scheme; + memcpy(pdu->url, url, len); + + /* start advertising */ + ctx->pkt.len = (sizeof(pre_t) + 2 + len); + skald_adv_start(ctx); +} diff --git a/sys/net/skald/skald_ibeacon.c b/sys/net/skald/skald_ibeacon.c new file mode 100644 index 0000000000000000000000000000000000000000..7d67eb7da22069b20f39208904a5122c3c5515b1 --- /dev/null +++ b/sys/net/skald/skald_ibeacon.c @@ -0,0 +1,63 @@ +/* + * 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 net_skald_ibeacon + * @{ + * + * @file + * @brief Skald's iBeacon implementation + * + * @author Hauke Petersen <hauke.petersen@fu-berlin.de> + * + * @} + */ + +#include <string.h> + +#include "byteorder.h" + +#include "net/skald/ibeacon.h" + +#define PREFIX_LEN (9U) + +/** + * @brief PDU format for iBeacon packets + */ +typedef struct __attribute__((packed)) { + uint8_t txadd[BLE_ADDR_LEN]; + uint8_t prefix[PREFIX_LEN]; + skald_uuid_t uuid; + be_uint16_t major; + be_uint16_t minor; + uint8_t txpower; +} ibeacon_t; + +/* constant GAP type value fields, fixed for the iBeacon format */ +static const uint8_t prefix[PREFIX_LEN] = { 0x02, 0x01, 0x06, 0x1a, 0xff, + 0x4c, 0x00, 0x02, 0x15 }; + +void skald_ibeacon_advertise(skald_ctx_t *ctx, const skald_uuid_t *uuid, + uint16_t major, uint16_t minor, uint8_t txpower) +{ + /* configure the iBeacon PDU */ + ibeacon_t *pdu = (ibeacon_t *)ctx->pkt.pdu; + + ctx->pkt.len = (uint8_t)sizeof(ibeacon_t); + + skald_generate_random_addr(pdu->txadd); + memcpy(pdu->prefix, prefix, PREFIX_LEN); + memcpy(&pdu->uuid, uuid, sizeof(skald_uuid_t)); + be_uint16_t tmp = byteorder_htons(major); + memcpy(&pdu->major, &tmp, sizeof(uint16_t)); + tmp = byteorder_htons(minor); + memcpy(&pdu->minor, &tmp, sizeof(uint16_t)); + pdu->txpower = txpower; + + skald_adv_start(ctx); +}