diff --git a/examples/nimble_scanner/Makefile b/examples/nimble_scanner/Makefile new file mode 100644 index 0000000000..346eba9985 --- /dev/null +++ b/examples/nimble_scanner/Makefile @@ -0,0 +1,30 @@ +# name of your application +APPLICATION = nimble_scanner + +# If no BOARD is found in the environment, use this default: +BOARD ?= nrf52dk + +# So far, NimBLE only works on nRF52 based platforms +BOARD_WHITELIST := nrf52dk nrf52840dk + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +# We use the xtimer and the shell in this example +USEMODULE += xtimer +USEMODULE += shell + +# configure and use Nimble +USEPKG += nimble +USEMODULE += nimble_scanner +USEMODULE += nimble_scanlist + +# 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: +DEVELHELP ?= 1 + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +include $(RIOTBASE)/Makefile.include diff --git a/examples/nimble_scanner/README.md b/examples/nimble_scanner/README.md new file mode 100644 index 0000000000..91044e3ce5 --- /dev/null +++ b/examples/nimble_scanner/README.md @@ -0,0 +1,29 @@ +# About +This application demonstrates the usage of the `NimBLE` BLE stack as a scanner. +It makes use of the RIOT specific `nimble_scanner` adaption module, abstracting +`NimBLE`s APIs for easier access. + +# Usage +This example provides a `scan` shell command. Issue `scan help` for more +information on its usage. + +Example output: +``` +> 2019-03-26 14:59:33,158 - INFO # main(): This is RIOT! (Version: X) +2019-03-26 14:59:33,161 - INFO # NimBLE Scanner Example Application +2019-03-26 14:59:33,164 - INFO # Type `scan help` for more information +> scan help +2019-03-26 15:00:29,214 - INFO # scan help +2019-03-26 15:00:29,216 - INFO # usage: scan [timeout in ms] +> scan 50 +2019-03-26 14:59:41,289 - INFO # scan 50 +2019-03-26 14:59:41,343 - INFO # Scanning for 50ms now ... dome +2019-03-26 14:59:41,343 - INFO # +2019-03-26 14:59:41,344 - INFO # Results: +2019-03-26 14:59:41,351 - INFO # [ 0] 3c:8a:28:86:40:90 (RANDOM) "undefined", adv_msg_cnt: 1, adv_int: 0us, last_rssi: -59 +2019-03-26 14:59:41,359 - INFO # [ 1] 5c:8a:fd:28:9f:c5 (RANDOM) "undefined", adv_msg_cnt: 1, adv_int: 0us, last_rssi: -87 +2019-03-26 14:59:41,367 - INFO # [ 2] 22:e6:6e:3a:a8:74 (RANDOM) "undefined", adv_msg_cnt: 1, adv_int: 0us, last_rssi: -84 +2019-03-26 14:59:41,375 - INFO # [ 3] 15:03:93:bb:8b:98 (RANDOM) "undefined", adv_msg_cnt: 1, adv_int: 0us, last_rssi: -85 +2019-03-26 14:59:41,383 - INFO # [ 4] 61:3a:2e:73:9d:74 (RANDOM) "undefined", adv_msg_cnt: 1, adv_int: 0us, last_rssi: -63 +2019-03-26 14:59:41,384 - INFO # +``` diff --git a/examples/nimble_scanner/main.c b/examples/nimble_scanner/main.c new file mode 100644 index 0000000000..0cc959f81c --- /dev/null +++ b/examples/nimble_scanner/main.c @@ -0,0 +1,89 @@ +/* + * 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 examples + * @{ + * + * @file + * @brief Example for using NimBLE as a BLE scanner + * + * @author Hauke Petersen + * + * @} + */ + +#include +#include +#include + +#include "xtimer.h" +#include "shell.h" +#include "shell_commands.h" + +#include "nimble_scanner.h" +#include "nimble_scanlist.h" + +/* default scan duration (1s) */ +#define DEFAULT_DURATION (1000000U) + +int _cmd_scan(int argc, char **argv) +{ + uint32_t timeout = DEFAULT_DURATION; + + if ((argc == 2) && (memcmp(argv[1], "help", 4) == 0)) { + printf("usage: %s [timeout in ms]\n", argv[0]); + return 0; + } + if (argc >= 2) { + timeout = (uint32_t)(atoi(argv[1]) * 1000); + } + + nimble_scanlist_clear(); + printf("Scanning for %ums now ...", (unsigned)(timeout / 1000)); + nimble_scanner_start(); + xtimer_usleep(timeout); + nimble_scanner_stop(); + puts(" done\n\nResults:"); + nimble_scanlist_print(); + puts(""); + + return 0; +} + +static const shell_command_t _commands[] = { + { "scan", "trigger a BLE scann", _cmd_scan }, + { NULL, NULL, NULL } +}; + +int main(void) +{ + puts("NimBLE Scanner Example Application"); + puts("Type `scan help` for more information"); + + /* in this example, we want Nimble to scan 'full time', so we set the + * window equal the interval */ + struct ble_gap_disc_params scan_params = { + .itvl = BLE_GAP_LIM_DISC_SCAN_INT, + .window = BLE_GAP_LIM_DISC_SCAN_WINDOW, + .filter_policy = 0, /* don't use */ + .limited = 0, /* no limited discovery */ + .passive = 0, /* no passive scanning */ + . filter_duplicates = 0, /* no duplicate filtering */ + }; + + /* initialize the nimble scanner */ + nimble_scanlist_init(); + nimble_scanner_init(&scan_params, nimble_scanlist_update); + + /* start shell */ + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + return 0; +} diff --git a/pkg/nimble/Makefile b/pkg/nimble/Makefile index a6663a4ed8..475a5fff5b 100644 --- a/pkg/nimble/Makefile +++ b/pkg/nimble/Makefile @@ -71,4 +71,7 @@ nimble_addr: nimble_scanlist: "$(MAKE)" -C $(TDIR)/scanlist +nimble_scanner: + "$(MAKE)" -C $(TDIR)/scanner + include $(RIOTBASE)/pkg/pkg.mk diff --git a/pkg/nimble/Makefile.include b/pkg/nimble/Makefile.include index e637c34770..19b6d34af6 100644 --- a/pkg/nimble/Makefile.include +++ b/pkg/nimble/Makefile.include @@ -76,3 +76,6 @@ endif ifneq (,$(filter nimble_scanlist,$(USEMODULE))) INCLUDES += -I$(RIOTPKG)/nimble/scanlist/include endif +ifneq (,$(filter nimble_scanner,$(USEMODULE))) + INCLUDES += -I$(RIOTPKG)/nimble/scanner/include +endif diff --git a/pkg/nimble/scanner/Makefile b/pkg/nimble/scanner/Makefile new file mode 100644 index 0000000000..03f11d44ba --- /dev/null +++ b/pkg/nimble/scanner/Makefile @@ -0,0 +1,3 @@ +MODULE = nimble_scanner + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/nimble/scanner/include/nimble_scanner.h b/pkg/nimble/scanner/include/nimble_scanner.h new file mode 100644 index 0000000000..b1178e5745 --- /dev/null +++ b/pkg/nimble/scanner/include/nimble_scanner.h @@ -0,0 +1,85 @@ +/* + * 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. + */ + +/** + * @defgroup ble_nimble_scanner NimBLE Scanner Helper + * @ingroup ble_nimble + * @brief Helper module to simplify the usage of NimBLE in scanning mode + * @{ + * + * @file + * @brief Scanner abstraction for NimBLE + * + * @author Hauke Petersen + */ + +#ifndef NIMBLE_SCANNER_H +#define NIMBLE_SCANNER_H + +#include + +#include "host/ble_hs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Return values used by this submodule + */ +enum { + NIMBLE_SCANNER_OK = 0, + NIMBLE_SCANNER_SCANNING = 1, + NIMBLE_SCANNER_STOPPED = 2, + NIMBLE_SCANNER_ERR = -1, +}; + +/** + * @brief Callback signature triggered by this module for each discovered + * advertising packet + */ +typedef void(*nimble_scanner_cb)(const ble_addr_t *addr, int8_t rssi, + const uint8_t *ad, size_t ad_len); + +/** + * @brief Initialize the scanner module + * + * @param[in] params scan parameters to use, pass NULL to use NimBLE's + * default parameters + * @param[in] disc_cb callback triggered of each received advertising packet + * + * @return NIMBLE_SCANNER_OK on success + * @return NIMBLE_SCANNER_ERR if putting NimBLE into discovery mode failed + */ +int nimble_scanner_init(const struct ble_gap_disc_params *params, + nimble_scanner_cb disc_cb); + +/** + * @brief Start scanning using timing parameters configured on initialization + */ +int nimble_scanner_start(void); + +/** + * @brief Stop scanning + */ +void nimble_scanner_stop(void); + +/** + * @brief Get the current scanning status + * + * @return NIMBLE_SCANNER_SCANNING if currently scanning + * @return NIMBLE_SCANNER_STOPPED if the scanner is stopped + */ +int nimble_scanner_status(void); + +#ifdef __cplusplus +} +#endif + +#endif /* NIMBLE_SCANNER_H */ +/** @} */ diff --git a/pkg/nimble/scanner/nimble_scanner.c b/pkg/nimble/scanner/nimble_scanner.c new file mode 100644 index 0000000000..f45363c4b9 --- /dev/null +++ b/pkg/nimble/scanner/nimble_scanner.c @@ -0,0 +1,95 @@ +/* + * 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 ble_nimble_scanner + * @{ + * + * @file + * @brief Implementation of a scanner abstraction for NimBLE + * + * @author Hauke Petersen + * + * @} + */ + +#include + +#include "nimble_riot.h" +#include "nimble_scanner.h" + +#include "host/ble_gap.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +static nimble_scanner_cb _disc_cb = NULL; +static struct ble_gap_disc_params _scan_params = { 0 }; + +static int _on_scan_evt(struct ble_gap_event *event, void *arg) +{ + /* only interested in the DISC event */ + if (event->type == BLE_GAP_EVENT_DISC) { + _disc_cb(&event->disc.addr, event->disc.rssi, + event->disc.data, (size_t)event->disc.length_data); + } + else { + /* this should never happen */ + DEBUG("[scanner] unknown event triggered (%i)\n", (int)event->type); + assert(0); + } + + return 0; +} + +int nimble_scanner_start(void) +{ + if (ble_gap_disc_active() == 0) { + int res = ble_gap_disc(nimble_riot_own_addr_type, BLE_HS_FOREVER, + &_scan_params, _on_scan_evt, NULL); + if (res != 0) { + DEBUG("[scanner] err: start failed (%i)\n", res); + return NIMBLE_SCANNER_ERR; + } + } + + return NIMBLE_SCANNER_OK; +} + +void nimble_scanner_stop(void) +{ + if (ble_gap_disc_active() == 1) { + int res = ble_gap_disc_cancel(); + /* the above should always succeed */ + assert(res == 0); + (void)res; + } +} + +int nimble_scanner_status(void) +{ + return (ble_gap_disc_active()) + ? NIMBLE_SCANNER_SCANNING + : NIMBLE_SCANNER_STOPPED; +} + +int nimble_scanner_init(const struct ble_gap_disc_params *params, + nimble_scanner_cb disc_cb) +{ + assert(disc_cb); + + if (params) { + memcpy(&_scan_params, params, sizeof(_scan_params)); + } + else { + memset(&_scan_params, 0, sizeof(_scan_params)); + } + _disc_cb = disc_cb; + + return NIMBLE_SCANNER_OK; +}