summaryrefslogtreecommitdiff
path: root/ports/nrf/bluetooth
diff options
context:
space:
mode:
authorarturo182 <arturo182@tlen.pl>2018-07-22 16:23:04 +0200
committerarturo182 <arturo182@tlen.pl>2018-10-21 15:55:25 +0200
commitd5a71a4b8a7ffe91ba688c733f10858cc428e9a6 (patch)
treec6593360f59a5d6a76e9eb3295f99235865ee9ad /ports/nrf/bluetooth
parent17f13ecc2cf10975bf5ff0631ce4326b9ffbbcf5 (diff)
nrf: Move bluetooth driver to the 'bluetooth' folder
Diffstat (limited to 'ports/nrf/bluetooth')
-rw-r--r--ports/nrf/bluetooth/ble_drv.c97
-rw-r--r--ports/nrf/bluetooth/ble_drv.h53
-rw-r--r--ports/nrf/bluetooth/ble_uart.c275
-rw-r--r--ports/nrf/bluetooth/ble_uart.h39
-rw-r--r--ports/nrf/bluetooth/bluetooth_common.mk43
-rwxr-xr-xports/nrf/bluetooth/download_ble_stack.sh72
-rw-r--r--ports/nrf/bluetooth/ringbuffer.h99
7 files changed, 678 insertions, 0 deletions
diff --git a/ports/nrf/bluetooth/ble_drv.c b/ports/nrf/bluetooth/ble_drv.c
new file mode 100644
index 000000000..0b6daf711
--- /dev/null
+++ b/ports/nrf/bluetooth/ble_drv.c
@@ -0,0 +1,97 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Glenn Ruben Bakke
+ * Copyright (c) 2018 Artur Pacholec
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "ble.h"
+#include "ble_drv.h"
+#include "nrf_nvic.h"
+#include "nrf_sdm.h"
+#include "py/misc.h"
+
+nrf_nvic_state_t nrf_nvic_state = { 0 };
+
+__attribute__((aligned(4)))
+static uint8_t m_ble_evt_buf[sizeof(ble_evt_t) + (BLE_GATT_ATT_MTU_DEFAULT)];
+
+typedef struct event_handler {
+ struct event_handler *next;
+ void *param;
+ ble_drv_evt_handler_t func;
+} event_handler_t;
+
+static event_handler_t *m_event_handlers;
+
+void ble_drv_add_event_handler(ble_drv_evt_handler_t func, void *param) {
+ event_handler_t *handler = m_new_ll(event_handler_t, 1);
+ handler->next = NULL;
+ handler->param = param;
+ handler->func = func;
+
+ if (m_event_handlers == NULL) {
+ m_event_handlers = handler;
+ return;
+ }
+
+ event_handler_t *it = m_event_handlers;
+ while (it->next != NULL) {
+ if ((it->func == func) && (it->param == param)) {
+ m_free(handler);
+ return;
+ }
+
+ it = it->next;
+ }
+
+ it->next = handler;
+}
+
+void SD_EVT_IRQHandler(void) {
+ uint32_t evt_id;
+ while (sd_evt_get(&evt_id) != NRF_ERROR_NOT_FOUND) {
+// sd_evt_handler(evt_id);
+ }
+
+ while (1) {
+ uint16_t evt_len = sizeof(m_ble_evt_buf);
+ const uint32_t err_code = sd_ble_evt_get(m_ble_evt_buf, &evt_len);
+ if (err_code != NRF_SUCCESS) {
+ if (err_code == NRF_ERROR_DATA_SIZE) {
+ printf("NRF_ERROR_DATA_SIZE\n");
+ }
+
+ break;
+ }
+
+ event_handler_t *it = m_event_handlers;
+ while (it != NULL) {
+ it->func((ble_evt_t *)m_ble_evt_buf, it->param);
+ it = it->next;
+ }
+ }
+}
diff --git a/ports/nrf/bluetooth/ble_drv.h b/ports/nrf/bluetooth/ble_drv.h
new file mode 100644
index 000000000..426354a19
--- /dev/null
+++ b/ports/nrf/bluetooth/ble_drv.h
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Glenn Ruben Bakke
+ * Copyright (c) 2018 Artur Pacholec
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLUETOOTH_LE_DRIVER_H__
+#define BLUETOOTH_LE_DRIVER_H__
+
+#include "ble.h"
+
+#if (BLUETOOTH_SD == 132) && (BLE_API_VERSION == 2)
+#define NRF52
+#endif
+
+#define MAX_TX_IN_PROGRESS 10
+
+#ifndef BLE_GATT_ATT_MTU_DEFAULT
+ #define BLE_GATT_ATT_MTU_DEFAULT GATT_MTU_SIZE_DEFAULT
+#endif
+
+#define BLE_CONN_CFG_TAG_CUSTOM 1
+
+#define MSEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000) / (RESOLUTION))
+#define UNIT_0_625_MS (625)
+#define UNIT_10_MS (10000)
+
+typedef void (*ble_drv_evt_handler_t)(ble_evt_t*, void*);
+
+void ble_drv_add_event_handler(ble_drv_evt_handler_t func, void *param);
+
+#endif // BLUETOOTH_LE_DRIVER_H__
diff --git a/ports/nrf/bluetooth/ble_uart.c b/ports/nrf/bluetooth/ble_uart.c
new file mode 100644
index 000000000..b1d54eefa
--- /dev/null
+++ b/ports/nrf/bluetooth/ble_uart.c
@@ -0,0 +1,275 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Glenn Ruben Bakke
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#if BLUETOOTH_SD
+
+#include <string.h>
+#include "ble_uart.h"
+#include "ringbuffer.h"
+#include "py/mphal.h"
+#include "lib/utils/interrupt_char.h"
+
+#if MICROPY_PY_BLE_NUS
+
+static bleio_uuid_obj_t uuid_obj_service = {
+ .base.type = &bleio_uuid_type,
+ .type = UUID_128_BIT,
+ .value = {0x01, 0x00}
+};
+
+static bleio_uuid_obj_t uuid_obj_char_tx = {
+ .base.type = &bleio_uuid_type,
+ .type = UUID_128_BIT,
+ .value = {0x03, 0x00}
+};
+
+static bleio_uuid_obj_t uuid_obj_char_rx = {
+ .base.type = &bleio_uuid_type,
+ .type = UUID_128_BIT,
+ .value = {0x02, 0x00}
+};
+
+static ubluepy_service_obj_t ble_uart_service = {
+ .base.type = &ubluepy_service_type,
+ .p_uuid = &uuid_obj_service,
+ .type = UBLUEPY_SERVICE_PRIMARY
+};
+
+static ubluepy_characteristic_obj_t ble_uart_char_rx = {
+ .base.type = &ubluepy_characteristic_type,
+ .p_uuid = &uuid_obj_char_rx,
+ .props = UBLUEPY_PROP_WRITE | UBLUEPY_PROP_WRITE_WO_RESP,
+ .attrs = 0,
+};
+
+static ubluepy_characteristic_obj_t ble_uart_char_tx = {
+ .base.type = &ubluepy_characteristic_type,
+ .p_uuid = &uuid_obj_char_tx,
+ .props = UBLUEPY_PROP_NOTIFY,
+ .attrs = UBLUEPY_ATTR_CCCD,
+};
+
+static ubluepy_peripheral_obj_t ble_uart_peripheral = {
+ .base.type = &ubluepy_peripheral_type,
+ .conn_handle = 0xFFFF,
+};
+
+static volatile bool m_cccd_enabled;
+static volatile bool m_connected;
+
+ringBuffer_typedef(uint8_t, ringbuffer_t);
+
+static ringbuffer_t m_rx_ring_buffer;
+static ringbuffer_t * mp_rx_ring_buffer = &m_rx_ring_buffer;
+static uint8_t m_rx_ring_buffer_data[128];
+
+static ubluepy_advertise_data_t m_adv_data_uart_service;
+
+#if BLUETOOTH_WEBBLUETOOTH_REPL
+static ubluepy_advertise_data_t m_adv_data_eddystone_url;
+#endif // BLUETOOTH_WEBBLUETOOTH_REPL
+
+int mp_hal_stdin_rx_chr(void) {
+ while (isBufferEmpty(mp_rx_ring_buffer)) {
+ ;
+ }
+
+ uint8_t byte;
+ bufferRead(mp_rx_ring_buffer, byte);
+ return (int)byte;
+}
+
+bool mp_hal_stdin_any(void) {
+ return !isBufferEmpty(mp_rx_ring_buffer);
+}
+
+void mp_hal_stdout_tx_strn(const char *str, size_t len) {
+ uint8_t *buf = (uint8_t *)str;
+ size_t send_len;
+
+ while (len > 0) {
+ if (len >= 20) {
+ send_len = 20; // (GATT_MTU_SIZE_DEFAULT - 3)
+ } else {
+ send_len = len;
+ }
+
+ ubluepy_characteristic_obj_t * p_char = &ble_uart_char_tx;
+
+ ble_drv_attr_s_notify(p_char->p_service->p_periph->conn_handle,
+ p_char->handle,
+ send_len,
+ buf);
+
+ len -= send_len;
+ buf += send_len;
+ }
+}
+
+void mp_hal_stdout_tx_strn_cooked(const char *str, mp_uint_t len) {
+ mp_hal_stdout_tx_strn(str, len);
+}
+
+STATIC void gap_event_handler(mp_obj_t self_in, uint16_t event_id, uint16_t conn_handle, uint16_t length, uint8_t * data) {
+ ubluepy_peripheral_obj_t * self = MP_OBJ_TO_PTR(self_in);
+
+ if (event_id == 16) { // connect event
+ self->conn_handle = conn_handle;
+ m_connected = true;
+ } else if (event_id == 17) { // disconnect event
+ self->conn_handle = 0xFFFF; // invalid connection handle
+ m_connected = false;
+ ble_uart_advertise();
+ }
+}
+
+STATIC void gatts_event_handler(mp_obj_t self_in, uint16_t event_id, uint16_t attr_handle, uint16_t length, uint8_t * data) {
+ ubluepy_peripheral_obj_t * self = MP_OBJ_TO_PTR(self_in);
+ (void)self;
+
+ if (event_id == 80) { // gatts write
+ if (ble_uart_char_tx.cccd_handle == attr_handle) {
+ m_cccd_enabled = true;
+ } else if (ble_uart_char_rx.handle == attr_handle) {
+ for (uint16_t i = 0; i < length; i++) {
+ #if MICROPY_KBD_EXCEPTION
+ if (data[i] == mp_interrupt_char) {
+ mp_keyboard_interrupt();
+ } else
+ #endif
+ {
+ bufferWrite(mp_rx_ring_buffer, data[i]);
+ }
+ }
+ }
+ }
+}
+
+void ble_uart_init0(void) {
+ uint8_t base_uuid[] = {0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x00, 0x00, 0x40, 0x6E};
+ uint8_t uuid_vs_idx;
+
+ (void)ble_drv_uuid_add_vs(base_uuid, &uuid_vs_idx);
+
+ uuid_obj_service.uuid_vs_idx = uuid_vs_idx;
+ uuid_obj_char_tx.uuid_vs_idx = uuid_vs_idx;
+ uuid_obj_char_rx.uuid_vs_idx = uuid_vs_idx;
+
+ (void)ble_drv_service_add(&ble_uart_service);
+ ble_uart_service.char_list = mp_obj_new_list(0, NULL);
+
+ // add TX characteristic
+ ble_uart_char_tx.service_handle = ble_uart_service.handle;
+ bool retval = ble_drv_characteristic_add(&ble_uart_char_tx);
+ if (retval) {
+ ble_uart_char_tx.p_service = &ble_uart_service;
+ }
+ mp_obj_list_append(ble_uart_service.char_list, MP_OBJ_FROM_PTR(&ble_uart_char_tx));
+
+ // add RX characteristic
+ ble_uart_char_rx.service_handle = ble_uart_service.handle;
+ retval = ble_drv_characteristic_add(&ble_uart_char_rx);
+ if (retval) {
+ ble_uart_char_rx.p_service = &ble_uart_service;
+ }
+ mp_obj_list_append(ble_uart_service.char_list, MP_OBJ_FROM_PTR(&ble_uart_char_rx));
+
+ // setup the peripheral
+ ble_uart_peripheral.service_list = mp_obj_new_list(0, NULL);
+ mp_obj_list_append(ble_uart_peripheral.service_list, MP_OBJ_FROM_PTR(&ble_uart_service));
+ ble_uart_service.p_periph = &ble_uart_peripheral;
+
+ ble_drv_gap_event_handler_set(MP_OBJ_FROM_PTR(&ble_uart_peripheral), gap_event_handler);
+ ble_drv_gatts_event_handler_set(MP_OBJ_FROM_PTR(&ble_uart_peripheral), gatts_event_handler);
+
+ ble_uart_peripheral.conn_handle = 0xFFFF;
+
+ char device_name[] = "mpus";
+
+ mp_obj_t service_list = mp_obj_new_list(0, NULL);
+ mp_obj_list_append(service_list, MP_OBJ_FROM_PTR(&ble_uart_service));
+
+ mp_obj_t * services = NULL;
+ mp_uint_t num_services;
+ mp_obj_get_array(service_list, &num_services, &services);
+
+ m_adv_data_uart_service.p_services = services;
+ m_adv_data_uart_service.num_of_services = num_services;
+ m_adv_data_uart_service.p_device_name = (uint8_t *)device_name;
+ m_adv_data_uart_service.device_name_len = strlen(device_name);
+ m_adv_data_uart_service.connectable = true;
+ m_adv_data_uart_service.p_data = NULL;
+
+#if BLUETOOTH_WEBBLUETOOTH_REPL
+ // for now point eddystone URL to https://goo.gl/x46FES => https://glennrub.github.io/webbluetooth/micropython/repl/
+ static uint8_t eddystone_url_data[27] = {0x2, 0x1, 0x6,
+ 0x3, 0x3, 0xaa, 0xfe,
+ 19, 0x16, 0xaa, 0xfe, 0x10, 0xee, 0x3, 'g', 'o', 'o', '.', 'g', 'l', '/', 'x', '4', '6', 'F', 'E', 'S'};
+ // eddystone url adv data
+ m_adv_data_eddystone_url.p_data = eddystone_url_data;
+ m_adv_data_eddystone_url.data_len = sizeof(eddystone_url_data);
+ m_adv_data_eddystone_url.connectable = false;
+#endif
+
+ m_cccd_enabled = false;
+
+ // initialize ring buffer
+ m_rx_ring_buffer.size = sizeof(m_rx_ring_buffer_data) + 1;
+ m_rx_ring_buffer.start = 0;
+ m_rx_ring_buffer.end = 0;
+ m_rx_ring_buffer.elems = m_rx_ring_buffer_data;
+
+ m_connected = false;
+
+ ble_uart_advertise();
+}
+
+void ble_uart_advertise(void) {
+#if BLUETOOTH_WEBBLUETOOTH_REPL
+ while (!m_connected) {
+ (void)ble_drv_advertise_data(&m_adv_data_uart_service);
+ mp_hal_delay_ms(500);
+ (void)ble_drv_advertise_data(&m_adv_data_eddystone_url);
+ mp_hal_delay_ms(500);
+ }
+
+ ble_drv_advertise_stop();
+#else
+ (void)ble_drv_advertise_data(&m_adv_data_uart_service);
+#endif // BLUETOOTH_WEBBLUETOOTH_REPL
+}
+
+bool ble_uart_connected(void) {
+ return (m_connected);
+}
+
+bool ble_uart_enabled(void) {
+ return (m_cccd_enabled);
+}
+
+#endif // MICROPY_PY_BLE_NUS
+
+#endif // BLUETOOTH_SD
diff --git a/ports/nrf/bluetooth/ble_uart.h b/ports/nrf/bluetooth/ble_uart.h
new file mode 100644
index 000000000..b57a75229
--- /dev/null
+++ b/ports/nrf/bluetooth/ble_uart.h
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Glenn Ruben Bakke
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLUETOOTH_LE_UART_H__
+#define BLUETOOTH_LE_UART_H__
+
+#include <stdbool.h>
+
+#include "ble_drv.h"
+
+void ble_uart_init0(void);
+void ble_uart_advertise(void);
+bool ble_uart_connected(void);
+bool ble_uart_enabled(void);
+
+#endif // BLUETOOTH_LE_UART_H__
diff --git a/ports/nrf/bluetooth/bluetooth_common.mk b/ports/nrf/bluetooth/bluetooth_common.mk
new file mode 100644
index 000000000..7dad38e9e
--- /dev/null
+++ b/ports/nrf/bluetooth/bluetooth_common.mk
@@ -0,0 +1,43 @@
+ifeq ($(SD), s132)
+ CFLAGS += -DBLUETOOTH_SD=132
+
+ifeq ($(SOFTDEV_VERSION), 2.0.1)
+ CFLAGS += -DBLE_API_VERSION=2
+else ifeq ($(SOFTDEV_VERSION), 5.0.0)
+ CFLAGS += -DBLE_API_VERSION=4
+endif
+else ifeq ($(SD), s140)
+ CFLAGS += -DBLUETOOTH_SD=140
+ CFLAGS += -DBLE_API_VERSION=4
+else
+$(error Incorrect softdevice set flag)
+endif
+
+CFLAGS += -DBLUETOOTH_SD_DEBUG=1
+
+INC += -Ibluetooth/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)_API/include
+INC += -Ibluetooth/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)_API/include/$(MCU_VARIANT)
+
+SOFTDEV_HEX_NAME = $(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)_softdevice.hex
+SOFTDEV_HEX_PATH = bluetooth/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)
+SOFTDEV_HEX = $(SOFTDEV_HEX_PATH)/$(SOFTDEV_HEX_NAME)
+
+define STACK_MISSING_ERROR
+
+
+###### ERROR: Bluetooth LE Stack not found ############
+# #
+# The build target requires a Bluetooth LE stack. #
+# $(SOFTDEV_VERSION_LONG) Bluetooth LE stack not found. #
+# #
+# Please run the download script: #
+# #
+# bluetooth/download_ble_stack.sh #
+# #
+#######################################################
+
+endef
+
+ifeq ($(shell test ! -e $(SOFTDEV_HEX) && echo -n no),no)
+ $(error $(STACK_MISSING_ERROR))
+endif
diff --git a/ports/nrf/bluetooth/download_ble_stack.sh b/ports/nrf/bluetooth/download_ble_stack.sh
new file mode 100755
index 000000000..385dbb451
--- /dev/null
+++ b/ports/nrf/bluetooth/download_ble_stack.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+
+function download_s132_nrf52_2_0_1
+{
+ echo ""
+ echo "####################################"
+ echo "### Downloading s132_nrf52_2.0.1 ###"
+ echo "####################################"
+ echo ""
+
+ mkdir -p "${1}/s132_nrf52_2.0.1"
+ cd "${1}/s132_nrf52_2.0.1"
+ wget https://www.nordicsemi.com/eng/nordic/download_resource/51479/6/84640562/95151
+ mv 95151 temp.zip
+ unzip -u temp.zip
+ rm temp.zip
+ cd -
+}
+
+function download_s132_nrf52_5_0_0
+{
+ echo ""
+ echo "####################################"
+ echo "### Downloading s132_nrf52_5.0.0 ###"
+ echo "####################################"
+ echo ""
+
+ mkdir -p "${1}/s132_nrf52_5.0.0"
+ cd "${1}/s132_nrf52_5.0.0"
+
+ wget http://www.nordicsemi.com/eng/nordic/download_resource/58987/11/7198220/116068
+ mv 116068 temp.zip
+ unzip -u temp.zip
+ rm temp.zip
+ cd -
+}
+
+function download_s140_nrf52_6_1_0
+{
+ echo ""
+ echo "####################################"
+ echo "### Downloading s140_nrf52_6.1.0 ###"
+ echo "####################################"
+ echo ""
+ mkdir -p "${1}/s140_nrf52_6.1.0"
+ cd "${1}/s140_nrf52_6.1.0"
+ wget https://www.nordicsemi.com/eng/nordic/download_resource/60624/25/88218841/116072
+ mv 116072 temp.zip
+ unzip -u temp.zip
+ rm temp.zip
+ cd -
+}
+
+SCRIPT_DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+if [ $# -eq 0 ]; then
+ echo "No Bluetooth LE stack defined, downloading all."
+ download_s132_nrf52_2_0_1 "${SCRIPT_DIR}"
+ download_s132_nrf52_5_0_0 "${SCRIPT_DIR}"
+ download_s140_nrf52_6_1_0 "${SCRIPT_DIR}"
+else
+ case $1 in
+ "s132_nrf52_2_0_1" )
+ download_s132_nrf52_2_0_1 "${SCRIPT_DIR}" ;;
+ "s132_nrf52_5_0_0" )
+ download_s132_nrf52_5_0_0 "${SCRIPT_DIR}" ;;
+ "s140_nrf52_6_1_0" )
+ download_s140_nrf52_6_1_0 "${SCRIPT_DIR}" ;;
+ esac
+fi
+
+exit 0
diff --git a/ports/nrf/bluetooth/ringbuffer.h b/ports/nrf/bluetooth/ringbuffer.h
new file mode 100644
index 000000000..3438b5c9b
--- /dev/null
+++ b/ports/nrf/bluetooth/ringbuffer.h
@@ -0,0 +1,99 @@
+/* The MIT License (MIT)
+ *
+ * Copyright (c) 2013 Philip Thrasher
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * Philip Thrasher's Crazy Awesome Ring Buffer Macros!
+ *
+ * Below you will find some naughty macros for easy owning and manipulating
+ * generic ring buffers. Yes, they are slightly evil in readability, but they
+ * are really fast, and they work great.
+ *
+ * Example usage:
+ *
+ * #include <stdio.h>
+ *
+ * // So we can use this in any method, this gives us a typedef
+ * // named 'intBuffer'.
+ * ringBuffer_typedef(int, intBuffer);
+ *
+ * int main() {
+ * // Declare vars.
+ * intBuffer myBuffer;
+ *
+ * bufferInit(myBuffer,1024,int);
+ *
+ * // We must have the pointer. All of the macros deal with the pointer.
+ * // (except for init.)
+ * intBuffer* myBuffer_ptr;
+ * myBuffer_ptr = &myBuffer;
+ *
+ * // Write two values.
+ * bufferWrite(myBuffer_ptr,37);
+ * bufferWrite(myBuffer_ptr,72);
+ *
+ * // Read a value into a local variable.
+ * int first;
+ * bufferRead(myBuffer_ptr,first);
+ * assert(first == 37); // true
+ *
+ * int second;
+ * bufferRead(myBuffer_ptr,second);
+ * assert(second == 72); // true
+ *
+ * return 0;
+ * }
+ *
+ */
+
+#ifndef _ringbuffer_h
+#define _ringbuffer_h
+
+#define ringBuffer_typedef(T, NAME) \
+ typedef struct { \
+ int size; \
+ volatile int start; \
+ volatile int end; \
+ T* elems; \
+ } NAME
+
+#define bufferInit(BUF, S, T) \
+ BUF.size = S+1; \
+ BUF.start = 0; \
+ BUF.end = 0; \
+ BUF.elems = (T*)calloc(BUF.size, sizeof(T))
+
+
+#define bufferDestroy(BUF) free(BUF->elems)
+#define nextStartIndex(BUF) ((BUF->start + 1) % BUF->size)
+#define nextEndIndex(BUF) ((BUF->end + 1) % BUF->size)
+#define isBufferEmpty(BUF) (BUF->end == BUF->start)
+#define isBufferFull(BUF) (nextEndIndex(BUF) == BUF->start)
+
+#define bufferWrite(BUF, ELEM) \
+ BUF->elems[BUF->end] = ELEM; \
+ BUF->end = (BUF->end + 1) % BUF->size; \
+ if (isBufferEmpty(BUF)) { \
+ BUF->start = nextStartIndex(BUF); \
+ }
+
+#define bufferRead(BUF, ELEM) \
+ ELEM = BUF->elems[BUF->start]; \
+ BUF->start = nextStartIndex(BUF);
+
+#endif