diff --git a/pkg/nimble/Makefile b/pkg/nimble/Makefile index 5face4c675..1f866716bb 100644 --- a/pkg/nimble/Makefile +++ b/pkg/nimble/Makefile @@ -77,6 +77,9 @@ nimble_drivers_nrf5x: nimble_addr: "$(MAKE)" -C $(TDIR)/addr/ +nimble_autoadv: + "$(MAKE)" -C $(TDIR)/autoadv + nimble_autoconn: "$(MAKE)" -C $(TDIR)/autoconn diff --git a/pkg/nimble/Makefile.dep b/pkg/nimble/Makefile.dep index 6eefc0746a..85603d9a65 100644 --- a/pkg/nimble/Makefile.dep +++ b/pkg/nimble/Makefile.dep @@ -39,6 +39,10 @@ ifneq (,$(filter nimble_addr,$(USEMODULE))) USEMODULE += bluetil_addr endif +ifneq (,$(filter nimble_autoadv,$(USEMODULE))) + USEMODULE += bluetil_ad +endif + ifneq (,$(filter nimble_autoconn_%,$(USEMODULE))) USEMODULE += nimble_autoconn endif diff --git a/pkg/nimble/Makefile.include b/pkg/nimble/Makefile.include index 4209adcf9b..fa3191052d 100644 --- a/pkg/nimble/Makefile.include +++ b/pkg/nimble/Makefile.include @@ -76,6 +76,9 @@ endif ifneq (,$(filter nimble_addr,$(USEMODULE))) INCLUDES += -I$(RIOTPKG)/nimble/addr/include endif +ifneq (,$(filter nimble_autoadv,$(USEMODULE))) + INCLUDES += -I$(RIOTPKG)/nimble/autoadv/include +endif ifneq (,$(filter nimble_autoconn,$(USEMODULE))) INCLUDES += -I$(RIOTPKG)/nimble/autoconn/include endif diff --git a/pkg/nimble/autoadv/Makefile b/pkg/nimble/autoadv/Makefile new file mode 100644 index 0000000000..24d0f78ede --- /dev/null +++ b/pkg/nimble/autoadv/Makefile @@ -0,0 +1,3 @@ +MODULE = nimble_autoadv + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/nimble/autoadv/README.md b/pkg/nimble/autoadv/README.md new file mode 100644 index 0000000000..2a15468094 --- /dev/null +++ b/pkg/nimble/autoadv/README.md @@ -0,0 +1,28 @@ +# NimBLE automated advertisements + +Module for automated bluetooth advertising. Advertising is restarted on +disconnect events automatically. Defaults to the following characteristics: + - General discoverable mode (BLE_GAP_DISC_MODE_GEN) + - Undirected connectable mode (BLE_GAP_CONN_MODE_UND) + - No expiration (BLE_HS_FOREVER) + - No name + +It can be enabled by adding +``` +USEMODULE += nimble_autoadv +``` +to your makefile. + +If your application is calling functions from nimble, e.g. +ble_svc_gap_device_name_set(), NIMBLE_AUTOADV_START_MANUALLY should be set to 1 +with the following line in your Makefile: +``` +CFLAGS += -DNIMBLE_AUTOADV_START_MANUALLY=1 +``` +Then the application should call nimble_autoadv_adv_start() after all of its +nimble calls to prevent errors like BLE_HS_EBUSY. + +To specify a device name add the following line to your Makefile: +``` +CFLAGS += -DNIMBLE_AUTOADV_DEVICE_NAME='"Riot OS device"' +``` diff --git a/pkg/nimble/autoadv/include/nimble_autoadv.h b/pkg/nimble/autoadv/include/nimble_autoadv.h new file mode 100644 index 0000000000..38f1d1f1f2 --- /dev/null +++ b/pkg/nimble/autoadv/include/nimble_autoadv.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2020 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_autoadv Automated advertising + * @ingroup pkg_nimble + * + * @brief Module for automated bluetooth advertising. Advertising is + * restarted on disconnect events automatically. Defaults to the + * following characteristics: + * - General discoverable mode (BLE_GAP_DISC_MODE_GEN) + * - Undirected connectable mode (BLE_GAP_CONN_MODE_UND) + * - No expiration (BLE_HS_FOREVER) + * - No name + * + * @{ + * + * @file + * + * @author Hendrik van Essen + */ + +#ifndef NIMBLE_AUTOADV_H +#define NIMBLE_AUTOADV_H + +#include "host/ble_gap.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** +* @brief Name of the device for the advertising procedure. If this is not + * defined, it will be defined as NULL, resulting in not configuring + * a name at all. +*/ +#ifndef NIMBLE_AUTOADV_DEVICE_NAME + #define NIMBLE_AUTOADV_DEVICE_NAME NULL +#endif + +/** +* @brief If an application is calling functions from nimble, e.g. + * ble_svc_gap_device_name_set(), NIMBLE_AUTOADV_START_MANUALLY should + * be set to 1 and then the application should call + * nimble_autoadv_start() after all of its nimble calls to prevent + * errors like BLE_HS_EBUSY. + * + * Defined as 0 by default. +*/ +#ifndef NIMBLE_AUTOADV_START_MANUALLY + #define NIMBLE_AUTOADV_START_MANUALLY 0 +#endif + +/** +* @brief Initialize autoadv module. +*/ +void nimble_autoadv_init(void); + +/** + * @brief Set struct for additional arguments specifying the particulars of + * the advertising procedure. Uses memcpy internally. + * + * If there is an active advertising process, it will be restarted. + * + * @param[in] params struct with customized additional arguments + */ +void nimble_autoadv_set_ble_gap_adv_params(struct ble_gap_adv_params *params); + +/** + * @brief Add a new field to the given advertising data. + * + * If there is an active advertising process, it will be restarted. + * + * @param[in] type field type to add + * @param[in] data payload for the field + * @param[in] data_len length of the payload in bytes + * + * @return BLUETIL_AD_OK if the new field was added + * @return BLUETIL_AD_NOMEM if there is not enough space to write add field + */ +int nimble_autoadv_add_field(uint8_t type, const void *data, size_t data_len); + +/** + * @brief Set the duration for the advertising procedure. + * + * If there is an active advertising process, it will be restarted. + * + * @param[in] duration_ms duration of advertising procedure in ms + */ +void nimble_auto_adv_set_adv_duration(int32_t duration_ms); + +/** + * @brief Set the callback for gap events. Callback is used for the logic when + * to start the advertising procedure. + * + * If there is an active advertising process, it will be restarted. + * + * @param[in] cb The callback to associate with this advertising + * procedure. If advertising ends, the event is reported + * through this callback. If advertising results in a + * connection, the connection inherits this callback as its + * event-reporting mechanism. + * + * @param[in] cb_arg The optional argument to pass to the callback function. + */ +void nimble_auto_adv_set_gap_cb(ble_gap_event_fn *cb, void *cb_arg); + +/** + * @brief Start the automated advertising procedure. + * + * Needs to be called manually when NIMBLE_AUTOADV_START_MANUALLY was + * set to true and after every call of nimble_autoadv_stop() to start + * advertising again. + */ +void nimble_autoadv_start(void); + +/** + * @brief Stop the automated advertising procedure. After calling this, you + * have to call nimble_autoadv_start() manually to restart the process. + */ +void nimble_autoadv_stop(void); + +/** + * @brief Reset all data regarding the advertising process. + * Following characteristics will be applied: + * - General discoverable mode (BLE_GAP_DISC_MODE_GEN) + * - Undirected connectable mode (BLE_GAP_CONN_MODE_UND) + * - No expiration (BLE_HS_FOREVER) + * - No name + */ +void nimble_autoadv_reset(void); + +#ifdef __cplusplus +} +#endif +/** @} */ +#endif /* NIMBLE_AUTOADV_H */ diff --git a/pkg/nimble/autoadv/nimble_autoadv.c b/pkg/nimble/autoadv/nimble_autoadv.c new file mode 100644 index 0000000000..5ece78d1c8 --- /dev/null +++ b/pkg/nimble/autoadv/nimble_autoadv.c @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2020 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_autoadv + * + * @{ + * + * @file + * + * @author Hendrik van Essen + * + * @} + */ + +#include +#include + +#include "nimble_riot.h" + +#include "host/ble_hs.h" +#include "host/ble_gap.h" +#include "net/bluetil/ad.h" + +#include "nimble_autoadv.h" + +/* settings for advertising procedure */ +static struct ble_gap_adv_params _advp; + +/* duration of the advertisement procedure */ +static int32_t _adv_duration; + +/* buffer for _ad */ +static uint8_t buf[BLE_HS_ADV_MAX_SZ]; + +/* advertising data struct */ +static bluetil_ad_t _ad; + +/* GAP callback function */ +static ble_gap_event_fn *_gap_cb; + +/* arguments for GAP callback function */ +static void *_gap_cb_arg; + +void nimble_autoadv_start(void); + +static int _gap_event_cb(struct ble_gap_event *event, void *arg) +{ + (void) arg; + + switch (event->type) { + + case BLE_GAP_EVENT_CONNECT: + if (event->connect.status != 0) { + // failed, ensure advertising is restarted + nimble_autoadv_start(); + } + break; + + case BLE_GAP_EVENT_DISCONNECT: + nimble_autoadv_start(); + break; + } + + return 0; +} + +void nimble_autoadv_init(void) +{ + nimble_autoadv_reset(); + + if (!NIMBLE_AUTOADV_START_MANUALLY) { + nimble_autoadv_start(); + } +} + +int nimble_autoadv_add_field(uint8_t type, const void *data, size_t data_len) +{ + int rc = bluetil_ad_add(&_ad, type, data, data_len); + + if (rc != BLUETIL_AD_OK) { + return rc; + } + + if (ble_gap_adv_active()) { + nimble_autoadv_start(); + } + + return rc; +} + +void nimble_autoadv_set_ble_gap_adv_params(struct ble_gap_adv_params *params) +{ + memcpy(&_advp, params, sizeof(struct ble_gap_adv_params)); + + if (ble_gap_adv_active()) { + nimble_autoadv_start(); + } +} + +void nimble_auto_adv_set_adv_duration(int32_t duration_ms) +{ + _adv_duration = duration_ms; + + if (ble_gap_adv_active()) { + nimble_autoadv_start(); + } +} + +void nimble_auto_adv_set_gap_cb(ble_gap_event_fn *cb, void *cb_arg) +{ + _gap_cb = cb; + _gap_cb_arg = cb_arg; + + if (ble_gap_adv_active()) { + nimble_autoadv_start(); + } +} + +void nimble_autoadv_start(void) +{ + int rc; + (void) rc; + + rc = ble_gap_adv_stop(); + assert(rc == BLE_HS_EALREADY || rc == 0); + + rc = ble_gap_adv_set_data(_ad.buf, _ad.pos); + assert(rc == 0); + + rc = ble_gap_adv_start(nimble_riot_own_addr_type, NULL, _adv_duration, &_advp, _gap_cb, _gap_cb_arg); + assert(rc == 0); +} + +void nimble_autoadv_stop(void) +{ + int rc; + (void) rc; + + rc = ble_gap_adv_stop(); + assert(rc == BLE_HS_EALREADY || rc == 0); +} + +void nimble_autoadv_reset(void) +{ + _gap_cb = &_gap_event_cb; + _gap_cb_arg = NULL; + + _adv_duration = BLE_HS_FOREVER; + + memset(&_advp, 0, sizeof _advp); + _advp.conn_mode = BLE_GAP_CONN_MODE_UND; + _advp.disc_mode = BLE_GAP_DISC_MODE_GEN; + + int rc = 0; + (void) rc; + + rc = bluetil_ad_init_with_flags(&_ad, buf, sizeof(buf), BLUETIL_AD_FLAGS_DEFAULT); + assert(rc == BLUETIL_AD_OK); + + if (NIMBLE_AUTOADV_DEVICE_NAME != NULL) { + rc = bluetil_ad_add_name(&_ad, NIMBLE_AUTOADV_DEVICE_NAME); + assert(rc == BLUETIL_AD_OK); + } + + if (ble_gap_adv_active()) { + nimble_autoadv_start(); + } +} diff --git a/pkg/nimble/contrib/nimble_riot.c b/pkg/nimble/contrib/nimble_riot.c index 8f2239019f..4ac72b2cd2 100644 --- a/pkg/nimble/contrib/nimble_riot.c +++ b/pkg/nimble/contrib/nimble_riot.c @@ -145,4 +145,9 @@ void nimble_riot_init(void) assert(res == NIMBLE_AUTOCONN_OK); nimble_autoconn_enable(); #endif + +#ifdef MODULE_NIMBLE_AUTOADV + extern void nimble_autoadv_init(void); + nimble_autoadv_init(); +#endif }