summaryrefslogtreecommitdiff
path: root/ports/nrf/drivers
diff options
context:
space:
mode:
authorGlenn Ruben Bakke <glennbakke@gmail.com>2017-10-04 21:46:48 +0200
committerGlenn Ruben Bakke <glennbakke@gmail.com>2017-10-04 21:46:48 +0200
commit44e2cb415f0b571e36b9d08def533c018fa99f1f (patch)
tree92bcc0f49b7612d3ffb366688deefe225abc9b5c /ports/nrf/drivers
parentbcab2ba0a80297100919366add6bada140c6ed75 (diff)
ports/nrf: Moving nrf51/52 port to new ports directory
Diffstat (limited to 'ports/nrf/drivers')
-rw-r--r--ports/nrf/drivers/bluetooth/ble_drv.c1065
-rw-r--r--ports/nrf/drivers/bluetooth/ble_drv.h129
-rw-r--r--ports/nrf/drivers/bluetooth/ble_uart.c266
-rw-r--r--ports/nrf/drivers/bluetooth/ble_uart.h42
-rw-r--r--ports/nrf/drivers/bluetooth/bluetooth_common.mk55
-rwxr-xr-xports/nrf/drivers/bluetooth/download_ble_stack.sh74
-rw-r--r--ports/nrf/drivers/bluetooth/ringbuffer.h99
-rw-r--r--ports/nrf/drivers/softpwm.c257
-rw-r--r--ports/nrf/drivers/softpwm.h13
-rw-r--r--ports/nrf/drivers/ticker.c162
-rw-r--r--ports/nrf/drivers/ticker.h30
11 files changed, 2192 insertions, 0 deletions
diff --git a/ports/nrf/drivers/bluetooth/ble_drv.c b/ports/nrf/drivers/bluetooth/ble_drv.c
new file mode 100644
index 000000000..2bb6fac2d
--- /dev/null
+++ b/ports/nrf/drivers/bluetooth/ble_drv.c
@@ -0,0 +1,1065 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 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 <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "py/runtime.h"
+#include "ble_drv.h"
+#include "mpconfigport.h"
+#include "nrf_sdm.h"
+#include "ble_gap.h"
+#include "ble.h" // sd_ble_uuid_encode
+
+
+#define BLE_DRIVER_VERBOSE 0
+#if BLE_DRIVER_VERBOSE
+#define BLE_DRIVER_LOG printf
+#else
+#define BLE_DRIVER_LOG(...)
+#endif
+
+#define BLE_ADV_LENGTH_FIELD_SIZE 1
+#define BLE_ADV_AD_TYPE_FIELD_SIZE 1
+#define BLE_AD_TYPE_FLAGS_DATA_SIZE 1
+
+#define MSEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000) / (RESOLUTION))
+#define UNIT_0_625_MS (625)
+#define UNIT_10_MS (10000)
+#define APP_CFG_NON_CONN_ADV_TIMEOUT 0 // Disable timeout.
+#define NON_CONNECTABLE_ADV_INTERVAL MSEC_TO_UNITS(100, UNIT_0_625_MS)
+
+#define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(12, UNIT_0_625_MS)
+#define BLE_MAX_CONN_INTERVAL MSEC_TO_UNITS(12, UNIT_0_625_MS)
+#define BLE_SLAVE_LATENCY 0
+#define BLE_CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS)
+
+#define SD_TEST_OR_ENABLE() \
+if (ble_drv_stack_enabled() == 0) { \
+ (void)ble_drv_stack_enable(); \
+}
+
+static volatile bool m_adv_in_progress;
+static volatile bool m_tx_in_progress;
+
+static ble_drv_gap_evt_callback_t gap_event_handler;
+static ble_drv_gatts_evt_callback_t gatts_event_handler;
+
+static mp_obj_t mp_gap_observer;
+static mp_obj_t mp_gatts_observer;
+
+#if (BLUETOOTH_SD == 130) || (BLUETOOTH_SD == 132)
+static volatile bool m_primary_service_found;
+static volatile bool m_characteristic_found;
+static volatile bool m_write_done;
+
+static volatile ble_drv_adv_evt_callback_t adv_event_handler;
+static volatile ble_drv_gattc_evt_callback_t gattc_event_handler;
+static volatile ble_drv_disc_add_service_callback_t disc_add_service_handler;
+static volatile ble_drv_disc_add_char_callback_t disc_add_char_handler;
+static volatile ble_drv_gattc_char_data_callback_t gattc_char_data_handle;
+
+static mp_obj_t mp_adv_observer;
+static mp_obj_t mp_gattc_observer;
+static mp_obj_t mp_gattc_disc_service_observer;
+static mp_obj_t mp_gattc_disc_char_observer;
+static mp_obj_t mp_gattc_char_data_observer;
+#endif
+
+#if (BLUETOOTH_SD != 100) && (BLUETOOTH_SD != 110)
+#include "nrf_nvic.h"
+
+#ifdef NRF52
+nrf_nvic_state_t nrf_nvic_state = {0};
+#endif // NRF52
+
+#endif // (BLUETOOTH_SD != 100)
+
+#if (BLUETOOTH_SD == 100 ) || (BLUETOOTH_SD == 110)
+void softdevice_assert_handler(uint32_t pc, uint16_t line_number, const uint8_t * p_file_name) {
+ BLE_DRIVER_LOG("ERROR: SoftDevice assert!!!");
+}
+#else
+void softdevice_assert_handler(uint32_t id, uint32_t pc, uint32_t info) {
+ BLE_DRIVER_LOG("ERROR: SoftDevice assert!!!");
+}
+#endif
+uint32_t ble_drv_stack_enable(void) {
+ m_adv_in_progress = false;
+ m_tx_in_progress = false;
+
+#if (BLUETOOTH_SD == 100) || (BLUETOOTH_SD == 110)
+#if BLUETOOTH_LFCLK_RC
+ uint32_t err_code = sd_softdevice_enable(NRF_CLOCK_LFCLKSRC_RC_250_PPM_250MS_CALIBRATION,
+ softdevice_assert_handler);
+#else
+ uint32_t err_code = sd_softdevice_enable(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM,
+ softdevice_assert_handler);
+#endif // BLUETOOTH_LFCLK_RC
+#else
+#if BLUETOOTH_LFCLK_RC
+ nrf_clock_lf_cfg_t clock_config = {
+ .source = NRF_CLOCK_LF_SRC_RC,
+ .rc_ctiv = 16,
+ .rc_temp_ctiv = 2,
+ .xtal_accuracy = 0
+ };
+#else
+ nrf_clock_lf_cfg_t clock_config = {
+ .source = NRF_CLOCK_LF_SRC_XTAL,
+ .rc_ctiv = 0,
+ .rc_temp_ctiv = 0,
+ .xtal_accuracy = NRF_CLOCK_LF_XTAL_ACCURACY_20_PPM
+ };
+#endif
+ uint32_t err_code = sd_softdevice_enable(&clock_config,
+ softdevice_assert_handler);
+#endif
+
+ BLE_DRIVER_LOG("SoftDevice enable status: " UINT_FMT "\n", (uint16_t)err_code);
+
+#if NRF51
+ err_code = sd_nvic_EnableIRQ(SWI2_IRQn);
+#else
+ err_code = sd_nvic_EnableIRQ(SWI2_EGU2_IRQn);
+#endif
+
+ BLE_DRIVER_LOG("IRQ enable status: " UINT_FMT "\n", (uint16_t)err_code);
+
+ // Enable BLE stack.
+ ble_enable_params_t ble_enable_params;
+ memset(&ble_enable_params, 0x00, sizeof(ble_enable_params));
+ ble_enable_params.gatts_enable_params.attr_tab_size = BLE_GATTS_ATTR_TAB_SIZE_DEFAULT;
+ ble_enable_params.gatts_enable_params.service_changed = 0;
+#if (BLUETOOTH_SD == 132)
+ ble_enable_params.gap_enable_params.periph_conn_count = 1;
+ ble_enable_params.gap_enable_params.central_conn_count = 1;
+#endif
+
+
+#if (BLUETOOTH_SD == 100) || (BLUETOOTH_SD == 110)
+ err_code = sd_ble_enable(&ble_enable_params);
+#else
+
+#if (BLUETOOTH_SD == 132)
+ uint32_t app_ram_start = 0x200039c0;
+ err_code = sd_ble_enable(&ble_enable_params, &app_ram_start); // 8K SD headroom from linker script.
+ BLE_DRIVER_LOG("BLE ram size: " UINT_FMT "\n", (uint16_t)app_ram_start);
+#else
+ err_code = sd_ble_enable(&ble_enable_params, (uint32_t *)0x20001870);
+#endif
+
+#endif
+
+ BLE_DRIVER_LOG("BLE enable status: " UINT_FMT "\n", (uint16_t)err_code);
+
+ // set up security mode
+ ble_gap_conn_params_t gap_conn_params;
+ ble_gap_conn_sec_mode_t sec_mode;
+
+ BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
+
+ const char device_name[] = "micr";
+
+ if ((err_code = sd_ble_gap_device_name_set(&sec_mode,
+ (const uint8_t *)device_name,
+ strlen(device_name))) != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Cannot apply GAP parameters."));
+ }
+
+ // set connection parameters
+ memset(&gap_conn_params, 0, sizeof(gap_conn_params));
+
+ gap_conn_params.min_conn_interval = BLE_MIN_CONN_INTERVAL;
+ gap_conn_params.max_conn_interval = BLE_MAX_CONN_INTERVAL;
+ gap_conn_params.slave_latency = BLE_SLAVE_LATENCY;
+ gap_conn_params.conn_sup_timeout = BLE_CONN_SUP_TIMEOUT;
+
+ if (sd_ble_gap_ppcp_set(&gap_conn_params) != 0) {
+
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Cannot set PPCP parameters."));
+ }
+
+ return err_code;
+}
+
+void ble_drv_stack_disable(void) {
+ sd_softdevice_disable();
+}
+
+uint8_t ble_drv_stack_enabled(void) {
+ uint8_t is_enabled;
+ uint32_t err_code = sd_softdevice_is_enabled(&is_enabled);
+ (void)err_code;
+
+ BLE_DRIVER_LOG("Is enabled status: " UINT_FMT "\n", (uint16_t)err_code);
+
+ return is_enabled;
+}
+
+void ble_drv_address_get(ble_drv_addr_t * p_addr) {
+ SD_TEST_OR_ENABLE();
+
+ ble_gap_addr_t local_ble_addr;
+#if (BLUETOOTH_SD == 132 && BLE_API_VERSION == 3)
+ uint32_t err_code = sd_ble_gap_addr_get(&local_ble_addr);
+#else
+ uint32_t err_code = sd_ble_gap_address_get(&local_ble_addr);
+#endif
+
+ if (err_code != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can not query for the device address."));
+ }
+
+ BLE_DRIVER_LOG("ble address, type: " HEX2_FMT ", " \
+ "address: " HEX2_FMT ":" HEX2_FMT ":" HEX2_FMT ":" \
+ HEX2_FMT ":" HEX2_FMT ":" HEX2_FMT "\n", \
+ local_ble_addr.addr_type, \
+ local_ble_addr.addr[5], local_ble_addr.addr[4], local_ble_addr.addr[3], \
+ local_ble_addr.addr[2], local_ble_addr.addr[1], local_ble_addr.addr[0]);
+
+ p_addr->addr_type = local_ble_addr.addr_type;
+ memcpy(p_addr->addr, local_ble_addr.addr, 6);
+}
+
+bool ble_drv_uuid_add_vs(uint8_t * p_uuid, uint8_t * idx) {
+ SD_TEST_OR_ENABLE();
+
+ if (sd_ble_uuid_vs_add((ble_uuid128_t const *)p_uuid, idx) != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can not add Vendor Specific 128-bit UUID."));
+ }
+
+ return true;
+}
+
+bool ble_drv_service_add(ubluepy_service_obj_t * p_service_obj) {
+ SD_TEST_OR_ENABLE();
+
+ if (p_service_obj->p_uuid->type > BLE_UUID_TYPE_BLE) {
+
+ ble_uuid_t uuid;
+ uuid.type = p_service_obj->p_uuid->uuid_vs_idx;
+ uuid.uuid = p_service_obj->p_uuid->value[0];
+ uuid.uuid += p_service_obj->p_uuid->value[1] << 8;
+
+ if (sd_ble_gatts_service_add(p_service_obj->type,
+ &uuid,
+ &p_service_obj->handle) != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can not add Service."));
+ }
+ } else if (p_service_obj->p_uuid->type == BLE_UUID_TYPE_BLE) {
+ BLE_DRIVER_LOG("adding service\n");
+
+ ble_uuid_t uuid;
+ uuid.type = p_service_obj->p_uuid->type;
+ uuid.uuid = p_service_obj->p_uuid->value[0];
+ uuid.uuid += p_service_obj->p_uuid->value[1] << 8;
+
+ if (sd_ble_gatts_service_add(p_service_obj->type,
+ &uuid,
+ &p_service_obj->handle) != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can not add Service."));
+ }
+ }
+ return true;
+}
+
+bool ble_drv_characteristic_add(ubluepy_characteristic_obj_t * p_char_obj) {
+ ble_gatts_char_md_t char_md;
+ ble_gatts_attr_md_t cccd_md;
+ ble_gatts_attr_t attr_char_value;
+ ble_uuid_t uuid;
+ ble_gatts_attr_md_t attr_md;
+
+ memset(&char_md, 0, sizeof(char_md));
+
+ char_md.char_props.broadcast = (p_char_obj->props & UBLUEPY_PROP_BROADCAST) ? 1 : 0;
+ char_md.char_props.read = (p_char_obj->props & UBLUEPY_PROP_READ) ? 1 : 0;
+ char_md.char_props.write_wo_resp = (p_char_obj->props & UBLUEPY_PROP_WRITE_WO_RESP) ? 1 : 0;
+ char_md.char_props.write = (p_char_obj->props & UBLUEPY_PROP_WRITE) ? 1 : 0;
+ char_md.char_props.notify = (p_char_obj->props & UBLUEPY_PROP_NOTIFY) ? 1 : 0;
+ char_md.char_props.indicate = (p_char_obj->props & UBLUEPY_PROP_INDICATE) ? 1 : 0;
+#if 0
+ char_md.char_props.auth_signed_wr = (p_char_obj->props & UBLUEPY_PROP_NOTIFY) ? 1 : 0;
+#endif
+
+
+ char_md.p_char_user_desc = NULL;
+ char_md.p_char_pf = NULL;
+ char_md.p_user_desc_md = NULL;
+ char_md.p_sccd_md = NULL;
+
+ // if cccd
+ if (p_char_obj->attrs & UBLUEPY_ATTR_CCCD) {
+ memset(&cccd_md, 0, sizeof(cccd_md));
+ BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
+ BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
+ cccd_md.vloc = BLE_GATTS_VLOC_STACK;
+ char_md.p_cccd_md = &cccd_md;
+ } else {
+ char_md.p_cccd_md = NULL;
+ }
+
+ uuid.type = p_char_obj->p_uuid->type;
+ uuid.uuid = p_char_obj->p_uuid->value[0];
+ uuid.uuid += p_char_obj->p_uuid->value[1] << 8;
+
+ memset(&attr_md, 0, sizeof(attr_md));
+
+ BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
+ BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
+
+ attr_md.vloc = BLE_GATTS_VLOC_STACK;
+ attr_md.rd_auth = 0;
+ attr_md.wr_auth = 0;
+ attr_md.vlen = 1;
+
+ memset(&attr_char_value, 0, sizeof(attr_char_value));
+
+ attr_char_value.p_uuid = &uuid;
+ attr_char_value.p_attr_md = &attr_md;
+ attr_char_value.init_len = sizeof(uint8_t);
+ attr_char_value.init_offs = 0;
+ attr_char_value.max_len = (GATT_MTU_SIZE_DEFAULT - 3);
+
+ ble_gatts_char_handles_t handles;
+
+ if (sd_ble_gatts_characteristic_add(p_char_obj->service_handle,
+ &char_md,
+ &attr_char_value,
+ &handles) != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can not add Characteristic."));
+ }
+
+ // apply handles to object instance
+ p_char_obj->handle = handles.value_handle;
+ p_char_obj->user_desc_handle = handles.user_desc_handle;
+ p_char_obj->cccd_handle = handles.cccd_handle;
+ p_char_obj->sccd_handle = handles.sccd_handle;
+
+ return true;
+}
+
+bool ble_drv_advertise_data(ubluepy_advertise_data_t * p_adv_params) {
+ SD_TEST_OR_ENABLE();
+
+ uint8_t byte_pos = 0;
+
+ uint8_t adv_data[BLE_GAP_ADV_MAX_SIZE];
+
+ if (p_adv_params->device_name_len > 0) {
+ ble_gap_conn_sec_mode_t sec_mode;
+
+ BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
+
+ if (sd_ble_gap_device_name_set(&sec_mode,
+ p_adv_params->p_device_name,
+ p_adv_params->device_name_len) != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can not apply device name in the stack."));
+ }
+
+ BLE_DRIVER_LOG("Device name applied\n");
+
+ adv_data[byte_pos] = (BLE_ADV_AD_TYPE_FIELD_SIZE + p_adv_params->device_name_len);
+ byte_pos += BLE_ADV_LENGTH_FIELD_SIZE;
+ adv_data[byte_pos] = BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME;
+ byte_pos += BLE_ADV_AD_TYPE_FIELD_SIZE;
+ memcpy(&adv_data[byte_pos], p_adv_params->p_device_name, p_adv_params->device_name_len);
+ // increment position counter to see if it fits, and in case more content should
+ // follow in this adv packet.
+ byte_pos += p_adv_params->device_name_len;
+ }
+
+ // Add FLAGS only if manually controlled data has not been used.
+ if (p_adv_params->data_len == 0) {
+ // set flags, default to disc mode
+ adv_data[byte_pos] = (BLE_ADV_AD_TYPE_FIELD_SIZE + BLE_AD_TYPE_FLAGS_DATA_SIZE);
+ byte_pos += BLE_ADV_LENGTH_FIELD_SIZE;
+ adv_data[byte_pos] = BLE_GAP_AD_TYPE_FLAGS;
+ byte_pos += BLE_AD_TYPE_FLAGS_DATA_SIZE;
+ adv_data[byte_pos] = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
+ byte_pos += 1;
+ }
+
+ if (p_adv_params->num_of_services > 0) {
+
+ bool type_16bit_present = false;
+ bool type_128bit_present = false;
+
+ for (uint8_t i = 0; i < p_adv_params->num_of_services; i++) {
+ ubluepy_service_obj_t * p_service = (ubluepy_service_obj_t *)p_adv_params->p_services[i];
+ if (p_service->p_uuid->type == UBLUEPY_UUID_16_BIT) {
+ type_16bit_present = true;
+ }
+
+ if (p_service->p_uuid->type == UBLUEPY_UUID_128_BIT) {
+ type_128bit_present = true;
+ }
+ }
+
+ if (type_16bit_present) {
+ uint8_t size_byte_pos = byte_pos;
+
+ // skip length byte for now, apply total length post calculation
+ byte_pos += BLE_ADV_LENGTH_FIELD_SIZE;
+
+ adv_data[byte_pos] = BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE;
+ byte_pos += BLE_ADV_AD_TYPE_FIELD_SIZE;
+
+ uint8_t uuid_total_size = 0;
+ uint8_t encoded_size = 0;
+
+ for (uint8_t i = 0; i < p_adv_params->num_of_services; i++) {
+ ubluepy_service_obj_t * p_service = (ubluepy_service_obj_t *)p_adv_params->p_services[i];
+
+ ble_uuid_t uuid;
+ uuid.type = p_service->p_uuid->type;
+ uuid.uuid = p_service->p_uuid->value[0];
+ uuid.uuid += p_service->p_uuid->value[1] << 8;
+ // calculate total size of uuids
+ if (sd_ble_uuid_encode(&uuid, &encoded_size, NULL) != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can not encode UUID, to check length."));
+ }
+
+ // do encoding into the adv buffer
+ if (sd_ble_uuid_encode(&uuid, &encoded_size, &adv_data[byte_pos]) != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can encode UUID into the advertisment packet."));
+ }
+
+ BLE_DRIVER_LOG("encoded uuid for service %u: ", 0);
+ for (uint8_t j = 0; j < encoded_size; j++) {
+ BLE_DRIVER_LOG(HEX2_FMT " ", adv_data[byte_pos + j]);
+ }
+ BLE_DRIVER_LOG("\n");
+
+ uuid_total_size += encoded_size; // size of entry
+ byte_pos += encoded_size; // relative to adv data packet
+ BLE_DRIVER_LOG("ADV: uuid size: %u, type: %u, uuid: %x%x, vs_idx: %u\n",
+ encoded_size, p_service->p_uuid->type,
+ p_service->p_uuid->value[1],
+ p_service->p_uuid->value[0],
+ p_service->p_uuid->uuid_vs_idx);
+ }
+
+ adv_data[size_byte_pos] = (BLE_ADV_AD_TYPE_FIELD_SIZE + uuid_total_size);
+ }
+
+ if (type_128bit_present) {
+ uint8_t size_byte_pos = byte_pos;
+
+ // skip length byte for now, apply total length post calculation
+ byte_pos += BLE_ADV_LENGTH_FIELD_SIZE;
+
+ adv_data[byte_pos] = BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE;
+ byte_pos += BLE_ADV_AD_TYPE_FIELD_SIZE;
+
+ uint8_t uuid_total_size = 0;
+ uint8_t encoded_size = 0;
+
+ for (uint8_t i = 0; i < p_adv_params->num_of_services; i++) {
+ ubluepy_service_obj_t * p_service = (ubluepy_service_obj_t *)p_adv_params->p_services[i];
+
+ ble_uuid_t uuid;
+ uuid.type = p_service->p_uuid->uuid_vs_idx;
+ uuid.uuid = p_service->p_uuid->value[0];
+ uuid.uuid += p_service->p_uuid->value[1] << 8;
+
+ // calculate total size of uuids
+ if (sd_ble_uuid_encode(&uuid, &encoded_size, NULL) != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can not encode UUID, to check length."));
+ }
+
+ // do encoding into the adv buffer
+ if (sd_ble_uuid_encode(&uuid, &encoded_size, &adv_data[byte_pos]) != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can encode UUID into the advertisment packet."));
+ }
+
+ BLE_DRIVER_LOG("encoded uuid for service %u: ", 0);
+ for (uint8_t j = 0; j < encoded_size; j++) {
+ BLE_DRIVER_LOG(HEX2_FMT " ", adv_data[byte_pos + j]);
+ }
+ BLE_DRIVER_LOG("\n");
+
+ uuid_total_size += encoded_size; // size of entry
+ byte_pos += encoded_size; // relative to adv data packet
+ BLE_DRIVER_LOG("ADV: uuid size: %u, type: %x%x, uuid: %u, vs_idx: %u\n",
+ encoded_size, p_service->p_uuid->type,
+ p_service->p_uuid->value[1],
+ p_service->p_uuid->value[0],
+ p_service->p_uuid->uuid_vs_idx);
+ }
+
+ adv_data[size_byte_pos] = (BLE_ADV_AD_TYPE_FIELD_SIZE + uuid_total_size);
+ }
+ }
+
+ if ((p_adv_params->data_len > 0) && (p_adv_params->p_data != NULL)) {
+ if (p_adv_params->data_len + byte_pos > BLE_GAP_ADV_MAX_SIZE) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can not fit data into the advertisment packet."));
+ }
+
+ memcpy(adv_data, p_adv_params->p_data, p_adv_params->data_len);
+ byte_pos += p_adv_params->data_len;
+ }
+
+ // scan response data not set
+ uint32_t err_code;
+ if ((err_code = sd_ble_gap_adv_data_set(adv_data, byte_pos, NULL, 0)) != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can not apply advertisment data. status: 0x" HEX2_FMT, (uint16_t)err_code));
+ }
+ BLE_DRIVER_LOG("Set Adv data size: " UINT_FMT "\n", byte_pos);
+
+ static ble_gap_adv_params_t m_adv_params;
+
+ // initialize advertising params
+ memset(&m_adv_params, 0, sizeof(m_adv_params));
+ if (p_adv_params->connectable) {
+ m_adv_params.type = BLE_GAP_ADV_TYPE_ADV_IND;
+ } else {
+ m_adv_params.type = BLE_GAP_ADV_TYPE_ADV_NONCONN_IND;
+ }
+
+ m_adv_params.p_peer_addr = NULL; // undirected advertisement
+ m_adv_params.fp = BLE_GAP_ADV_FP_ANY;
+ m_adv_params.interval = MSEC_TO_UNITS(100, UNIT_0_625_MS); // approx 8 ms
+ m_adv_params.timeout = 0; // infinite advertisment
+
+ ble_drv_advertise_stop();
+
+ err_code = sd_ble_gap_adv_start(&m_adv_params);
+ if (err_code != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can not start advertisment. status: 0x" HEX2_FMT, (uint16_t)err_code));
+ }
+
+ m_adv_in_progress = true;
+
+ return true;
+}
+
+void ble_drv_advertise_stop(void) {
+ if (m_adv_in_progress == true) {
+ uint32_t err_code;
+ if ((err_code = sd_ble_gap_adv_stop()) != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can not stop advertisment. status: 0x" HEX2_FMT, (uint16_t)err_code));
+ }
+ }
+ m_adv_in_progress = false;
+}
+
+void ble_drv_attr_s_read(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data) {
+ ble_gatts_value_t gatts_value;
+ memset(&gatts_value, 0, sizeof(gatts_value));
+
+ gatts_value.len = len;
+ gatts_value.offset = 0;
+ gatts_value.p_value = p_data;
+
+ uint32_t err_code = sd_ble_gatts_value_get(conn_handle,
+ handle,
+ &gatts_value);
+ if (err_code != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can not read attribute value. status: 0x" HEX2_FMT, (uint16_t)err_code));
+ }
+
+}
+
+void ble_drv_attr_s_write(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data) {
+ ble_gatts_value_t gatts_value;
+ memset(&gatts_value, 0, sizeof(gatts_value));
+
+ gatts_value.len = len;
+ gatts_value.offset = 0;
+ gatts_value.p_value = p_data;
+
+ uint32_t err_code = sd_ble_gatts_value_set(conn_handle, handle, &gatts_value);
+
+ if (err_code != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can not write attribute value. status: 0x" HEX2_FMT, (uint16_t)err_code));
+ }
+}
+
+void ble_drv_attr_s_notify(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data) {
+ uint16_t hvx_len = len;
+ ble_gatts_hvx_params_t hvx_params;
+
+ memset(&hvx_params, 0, sizeof(hvx_params));
+
+ hvx_params.handle = handle;
+ hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
+ hvx_params.offset = 0;
+ hvx_params.p_len = &hvx_len;
+ hvx_params.p_data = p_data;
+
+ while (m_tx_in_progress) {
+ ;
+ }
+
+ m_tx_in_progress = true;
+ uint32_t err_code;
+ if ((err_code = sd_ble_gatts_hvx(conn_handle, &hvx_params)) != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can not notify attribute value. status: 0x" HEX2_FMT, (uint16_t)err_code));
+ }
+}
+
+void ble_drv_gap_event_handler_set(mp_obj_t obj, ble_drv_gap_evt_callback_t evt_handler) {
+ mp_gap_observer = obj;
+ gap_event_handler = evt_handler;
+}
+
+void ble_drv_gatts_event_handler_set(mp_obj_t obj, ble_drv_gatts_evt_callback_t evt_handler) {
+ mp_gatts_observer = obj;
+ gatts_event_handler = evt_handler;
+}
+
+#if (BLUETOOTH_SD == 130) || (BLUETOOTH_SD == 132)
+
+void ble_drv_gattc_event_handler_set(mp_obj_t obj, ble_drv_gattc_evt_callback_t evt_handler) {
+ mp_gattc_observer = obj;
+ gattc_event_handler = evt_handler;
+}
+
+void ble_drv_adv_report_handler_set(mp_obj_t obj, ble_drv_adv_evt_callback_t evt_handler) {
+ mp_adv_observer = obj;
+ adv_event_handler = evt_handler;
+}
+
+
+void ble_drv_attr_c_read(uint16_t conn_handle, uint16_t handle, mp_obj_t obj, ble_drv_gattc_char_data_callback_t cb) {
+
+ mp_gattc_char_data_observer = obj;
+ gattc_char_data_handle = cb;
+
+ uint32_t err_code = sd_ble_gattc_read(conn_handle,
+ handle,
+ 0);
+ if (err_code != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can not read attribute value. status: 0x" HEX2_FMT, (uint16_t)err_code));
+ }
+
+ while (gattc_char_data_handle != NULL) {
+ ;
+ }
+}
+
+void ble_drv_attr_c_write(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data, bool w_response) {
+
+ ble_gattc_write_params_t write_params;
+
+ if (w_response) {
+ write_params.write_op = BLE_GATT_OP_WRITE_REQ;
+ } else {
+ write_params.write_op = BLE_GATT_OP_WRITE_CMD;
+ }
+
+ write_params.flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_CANCEL;
+ write_params.handle = handle;
+ write_params.offset = 0;
+ write_params.len = len;
+ write_params.p_value = p_data;
+
+ m_write_done = !w_response;
+
+ uint32_t err_code = sd_ble_gattc_write(conn_handle, &write_params);
+
+ if (err_code != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can not write attribute value. status: 0x" HEX2_FMT, (uint16_t)err_code));
+ }
+
+ while (m_write_done != true) {
+ ;
+ }
+}
+
+void ble_drv_scan_start(void) {
+ SD_TEST_OR_ENABLE();
+
+ ble_gap_scan_params_t scan_params;
+ scan_params.active = 1;
+ scan_params.interval = MSEC_TO_UNITS(100, UNIT_0_625_MS);
+ scan_params.window = MSEC_TO_UNITS(100, UNIT_0_625_MS);
+ scan_params.timeout = 0; // Infinite
+
+#if (BLUETOOTH_SD == 130)
+ scan_params.selective = 0;
+ scan_params.p_whitelist = NULL;
+#elif (BLUETOOTH_SD == 132 && BLE_API_VERSION == 3)
+ scan_params.use_whitelist = 0;
+#endif
+
+ uint32_t err_code;
+ if ((err_code = sd_ble_gap_scan_start(&scan_params)) != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can not start scanning. status: 0x" HEX2_FMT, (uint16_t)err_code));
+ }
+}
+
+void ble_drv_scan_stop(void) {
+ sd_ble_gap_scan_stop();
+}
+
+void ble_drv_connect(uint8_t * p_addr, uint8_t addr_type) {
+ SD_TEST_OR_ENABLE();
+
+ ble_gap_scan_params_t scan_params;
+ scan_params.active = 1;
+ scan_params.interval = MSEC_TO_UNITS(100, UNIT_0_625_MS);
+ scan_params.window = MSEC_TO_UNITS(100, UNIT_0_625_MS);
+ scan_params.timeout = 0; // infinite
+
+#if (BLUETOOTH_SD == 130)
+ scan_params.selective = 0;
+ scan_params.p_whitelist = NULL;
+#elif (BLUETOOTH_SD == 132 && BLE_API_VERSION == 3)
+ scan_params.use_whitelist = 0;
+#endif
+
+ ble_gap_addr_t addr;
+ memset(&addr, 0, sizeof(addr));
+
+ addr.addr_type = addr_type;
+ memcpy(addr.addr, p_addr, 6);
+
+ BLE_DRIVER_LOG("GAP CONNECTING: "HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT":"HEX2_FMT", type: %d\n",
+ addr.addr[0], addr.addr[1], addr.addr[2], addr.addr[3], addr.addr[4], addr.addr[5], addr.addr_type);
+
+ ble_gap_conn_params_t conn_params;
+
+// (void)sd_ble_gap_ppcp_get(&conn_params);
+
+ // set connection parameters
+ memset(&conn_params, 0, sizeof(conn_params));
+
+ conn_params.min_conn_interval = BLE_MIN_CONN_INTERVAL;
+ conn_params.max_conn_interval = BLE_MAX_CONN_INTERVAL;
+ conn_params.slave_latency = BLE_SLAVE_LATENCY;
+ conn_params.conn_sup_timeout = BLE_CONN_SUP_TIMEOUT;
+
+ uint32_t err_code;
+ if ((err_code = sd_ble_gap_connect(&addr, &scan_params, &conn_params)) != 0) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_OSError,
+ "Can not connect. status: 0x" HEX2_FMT, (uint16_t)err_code));
+ }
+}
+
+bool ble_drv_discover_services(mp_obj_t obj, uint16_t conn_handle, uint16_t start_handle, ble_drv_disc_add_service_callback_t cb) {
+ BLE_DRIVER_LOG("Discover primary services. Conn handle: 0x" HEX2_FMT "\n",
+ conn_handle);
+
+ mp_gattc_disc_service_observer = obj;
+ disc_add_service_handler = cb;
+
+ m_primary_service_found = false;
+
+ uint32_t err_code;
+ err_code = sd_ble_gattc_primary_services_discover(conn_handle,
+ start_handle,
+ NULL);
+ if (err_code != 0) {
+ return false;
+ }
+
+ // busy loop until last service has been iterated
+ while (disc_add_service_handler != NULL) {
+ ;
+ }
+
+ if (m_primary_service_found) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool ble_drv_discover_characteristic(mp_obj_t obj,
+ uint16_t conn_handle,
+ uint16_t start_handle,
+ uint16_t end_handle,
+ ble_drv_disc_add_char_callback_t cb) {
+ BLE_DRIVER_LOG("Discover characteristicts. Conn handle: 0x" HEX2_FMT "\n",
+ conn_handle);
+
+ mp_gattc_disc_char_observer = obj;
+ disc_add_char_handler = cb;
+
+ ble_gattc_handle_range_t handle_range;
+ handle_range.start_handle = start_handle;
+ handle_range.end_handle = end_handle;
+
+ m_characteristic_found = false;
+
+ uint32_t err_code;
+ err_code = sd_ble_gattc_characteristics_discover(conn_handle, &handle_range);
+ if (err_code != 0) {
+ return false;
+ }
+
+ // busy loop until last service has been iterated
+ while (disc_add_char_handler != NULL) {
+ ;
+ }
+
+ if (m_characteristic_found) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void ble_drv_discover_descriptors(void) {
+
+}
+
+#endif
+
+static void ble_evt_handler(ble_evt_t * p_ble_evt) {
+// S132 event ranges.
+// Common 0x01 -> 0x0F
+// GAP 0x10 -> 0x2F
+// GATTC 0x30 -> 0x4F
+// GATTS 0x50 -> 0x6F
+// L2CAP 0x70 -> 0x8F
+ switch (p_ble_evt->header.evt_id) {
+ case BLE_GAP_EVT_CONNECTED:
+ BLE_DRIVER_LOG("GAP CONNECT\n");
+ m_adv_in_progress = false;
+ gap_event_handler(mp_gap_observer, p_ble_evt->header.evt_id, p_ble_evt->evt.gap_evt.conn_handle, p_ble_evt->header.evt_len - (2 * sizeof(uint16_t)), NULL);
+
+ ble_gap_conn_params_t conn_params;
+ (void)sd_ble_gap_ppcp_get(&conn_params);
+ (void)sd_ble_gap_conn_param_update(p_ble_evt->evt.gap_evt.conn_handle, &conn_params);
+ break;
+
+ case BLE_GAP_EVT_DISCONNECTED:
+ BLE_DRIVER_LOG("GAP DISCONNECT\n");
+ gap_event_handler(mp_gap_observer, p_ble_evt->header.evt_id, p_ble_evt->evt.gap_evt.conn_handle, p_ble_evt->header.evt_len - (2 * sizeof(uint16_t)), NULL);
+ break;
+
+ case BLE_GATTS_EVT_HVC:
+ gatts_event_handler(mp_gatts_observer, p_ble_evt->header.evt_id, p_ble_evt->evt.gatts_evt.params.hvc.handle, p_ble_evt->header.evt_len - (2 * sizeof(uint16_t)), NULL);
+ break;
+
+ case BLE_GATTS_EVT_WRITE:
+ BLE_DRIVER_LOG("GATTS write\n");
+
+ uint16_t handle = p_ble_evt->evt.gatts_evt.params.write.handle;
+ uint16_t data_len = p_ble_evt->evt.gatts_evt.params.write.len;
+ uint8_t * p_data = &p_ble_evt->evt.gatts_evt.params.write.data[0];
+
+ gatts_event_handler(mp_gatts_observer, p_ble_evt->header.evt_id, handle, data_len, p_data);
+ break;
+
+ case BLE_GAP_EVT_CONN_PARAM_UPDATE:
+ BLE_DRIVER_LOG("GAP CONN PARAM UPDATE\n");
+ break;
+
+ case BLE_GATTS_EVT_SYS_ATTR_MISSING:
+ // No system attributes have been stored.
+ (void)sd_ble_gatts_sys_attr_set(p_ble_evt->evt.gatts_evt.conn_handle, NULL, 0, 0);
+ break;
+
+#if (BLUETOOTH_SD == 132 && BLE_API_VERSION == 3)
+ case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST:
+ BLE_DRIVER_LOG("GATTS EVT EXCHANGE MTU REQUEST\n");
+ (void)sd_ble_gatts_exchange_mtu_reply(p_ble_evt->evt.gatts_evt.conn_handle, 23); // MAX MTU size
+ break;
+#endif
+
+ case BLE_EVT_TX_COMPLETE:
+ BLE_DRIVER_LOG("BLE EVT TX COMPLETE\n");
+ m_tx_in_progress = false;
+ break;
+
+ case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
+ BLE_DRIVER_LOG("BLE EVT SEC PARAMS REQUEST\n");
+ // pairing not supported
+ (void)sd_ble_gap_sec_params_reply(p_ble_evt->evt.gatts_evt.conn_handle,
+ BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP,
+ NULL, NULL);
+ break;
+
+#if (BLUETOOTH_SD == 130) || (BLUETOOTH_SD == 132)
+ case BLE_GAP_EVT_ADV_REPORT:
+ BLE_DRIVER_LOG("BLE EVT ADV REPORT\n");
+ ble_drv_adv_data_t adv_data = {
+ .p_peer_addr = p_ble_evt->evt.gap_evt.params.adv_report.peer_addr.addr,
+ .addr_type = p_ble_evt->evt.gap_evt.params.adv_report.peer_addr.addr_type,
+ .is_scan_resp = p_ble_evt->evt.gap_evt.params.adv_report.scan_rsp,
+ .rssi = p_ble_evt->evt.gap_evt.params.adv_report.rssi,
+ .data_len = p_ble_evt->evt.gap_evt.params.adv_report.dlen,
+ .p_data = p_ble_evt->evt.gap_evt.params.adv_report.data,
+ .adv_type = p_ble_evt->evt.gap_evt.params.adv_report.type
+ };
+
+ // TODO: Fix unsafe callback to possible undefined callback...
+ adv_event_handler(mp_adv_observer,
+ p_ble_evt->header.evt_id,
+ &adv_data);
+ break;
+
+ case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST:
+ BLE_DRIVER_LOG("BLE EVT CONN PARAM UPDATE REQUEST\n");
+
+ (void)sd_ble_gap_conn_param_update(p_ble_evt->evt.gap_evt.conn_handle,
+ &p_ble_evt->evt.gap_evt.params.conn_param_update_request.conn_params);
+ break;
+
+ case BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP:
+ BLE_DRIVER_LOG("BLE EVT PRIMARY SERVICE DISCOVERY RESPONSE\n");
+ BLE_DRIVER_LOG(">>> service count: %d\n", p_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.count);
+
+ for (uint16_t i = 0; i < p_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.count; i++) {
+ ble_gattc_service_t * p_service = &p_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[i];
+
+ ble_drv_service_data_t service;
+ service.uuid_type = p_service->uuid.type;
+ service.uuid = p_service->uuid.uuid;
+ service.start_handle = p_service->handle_range.start_handle;
+ service.end_handle = p_service->handle_range.end_handle;
+
+ disc_add_service_handler(mp_gattc_disc_service_observer, &service);
+ }
+
+ if (p_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.count > 0) {
+ m_primary_service_found = true;
+ }
+
+ // mark end of service discovery
+ disc_add_service_handler = NULL;
+
+ break;
+
+ case BLE_GATTC_EVT_CHAR_DISC_RSP:
+ BLE_DRIVER_LOG("BLE EVT CHAR DISCOVERY RESPONSE\n");
+ BLE_DRIVER_LOG(">>> characteristic count: %d\n", p_ble_evt->evt.gattc_evt.params.char_disc_rsp.count);
+
+ for (uint16_t i = 0; i < p_ble_evt->evt.gattc_evt.params.char_disc_rsp.count; i++) {
+ ble_gattc_char_t * p_char = &p_ble_evt->evt.gattc_evt.params.char_disc_rsp.chars[i];
+
+ ble_drv_char_data_t char_data;
+ char_data.uuid_type = p_char->uuid.type;
+ char_data.uuid = p_char->uuid.uuid;
+ char_data.decl_handle = p_char->handle_decl;
+ char_data.value_handle = p_char->handle_value;
+
+ char_data.props |= (p_char->char_props.broadcast) ? UBLUEPY_PROP_BROADCAST : 0;
+ char_data.props |= (p_char->char_props.read) ? UBLUEPY_PROP_READ : 0;
+ char_data.props |= (p_char->char_props.write_wo_resp) ? UBLUEPY_PROP_WRITE_WO_RESP : 0;
+ char_data.props |= (p_char->char_props.write) ? UBLUEPY_PROP_WRITE : 0;
+ char_data.props |= (p_char->char_props.notify) ? UBLUEPY_PROP_NOTIFY : 0;
+ char_data.props |= (p_char->char_props.indicate) ? UBLUEPY_PROP_INDICATE : 0;
+ #if 0
+ char_data.props |= (p_char->char_props.auth_signed_wr) ? UBLUEPY_PROP_NOTIFY : 0;
+ #endif
+
+ disc_add_char_handler(mp_gattc_disc_char_observer, &char_data);
+ }
+
+ if (p_ble_evt->evt.gattc_evt.params.char_disc_rsp.count > 0) {
+ m_characteristic_found = true;
+ }
+
+ // mark end of characteristic discovery
+ disc_add_char_handler = NULL;
+
+ break;
+
+ case BLE_GATTC_EVT_READ_RSP:
+ BLE_DRIVER_LOG("BLE EVT READ RESPONSE, offset: 0x"HEX2_FMT", length: 0x"HEX2_FMT"\n",
+ p_ble_evt->evt.gattc_evt.params.read_rsp.offset,
+ p_ble_evt->evt.gattc_evt.params.read_rsp.len);
+
+ gattc_char_data_handle(mp_gattc_char_data_observer,
+ p_ble_evt->evt.gattc_evt.params.read_rsp.len,
+ p_ble_evt->evt.gattc_evt.params.read_rsp.data);
+
+ // mark end of read
+ gattc_char_data_handle = NULL;
+
+ break;
+
+ case BLE_GATTC_EVT_WRITE_RSP:
+ BLE_DRIVER_LOG("BLE EVT WRITE RESPONSE\n");
+ m_write_done = true;
+ break;
+
+ case BLE_GATTC_EVT_HVX:
+ BLE_DRIVER_LOG("BLE EVT HVX RESPONSE\n");
+ break;
+#endif
+
+ default:
+ BLE_DRIVER_LOG(">>> unhandled evt: 0x" HEX2_FMT "\n", p_ble_evt->header.evt_id);
+ break;
+ }
+}
+
+static uint8_t m_ble_evt_buf[sizeof(ble_evt_t) + (GATT_MTU_SIZE_DEFAULT)] __attribute__ ((aligned (4)));
+
+#ifdef NRF51
+void SWI2_IRQHandler(void) {
+#else
+void SWI2_EGU2_IRQHandler(void) {
+#endif
+
+ uint32_t evt_id;
+ uint32_t err_code;
+ do {
+ err_code = sd_evt_get(&evt_id);
+ // TODO: handle non ble events
+ } while (err_code != NRF_ERROR_NOT_FOUND && err_code != NRF_SUCCESS);
+
+ uint16_t evt_len = sizeof(m_ble_evt_buf);
+ do {
+ err_code = sd_ble_evt_get(m_ble_evt_buf, &evt_len);
+ ble_evt_handler((ble_evt_t *)m_ble_evt_buf);
+ } while (err_code != NRF_ERROR_NOT_FOUND && err_code != NRF_SUCCESS);
+}
+
+#endif // BLUETOOTH_SD
diff --git a/ports/nrf/drivers/bluetooth/ble_drv.h b/ports/nrf/drivers/bluetooth/ble_drv.h
new file mode 100644
index 000000000..d8b715467
--- /dev/null
+++ b/ports/nrf/drivers/bluetooth/ble_drv.h
@@ -0,0 +1,129 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 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_DRIVER_H__
+#define BLUETOOTH_LE_DRIVER_H__
+
+#if BLUETOOTH_SD
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "modubluepy.h"
+
+typedef struct {
+ uint8_t addr[6];
+ uint8_t addr_type;
+} ble_drv_addr_t;
+
+typedef struct {
+ uint8_t * p_peer_addr;
+ uint8_t addr_type;
+ bool is_scan_resp;
+ int8_t rssi;
+ uint8_t data_len;
+ uint8_t * p_data;
+ uint8_t adv_type;
+} ble_drv_adv_data_t;
+
+typedef struct {
+ uint16_t uuid;
+ uint8_t uuid_type;
+ uint16_t start_handle;
+ uint16_t end_handle;
+} ble_drv_service_data_t;
+
+typedef struct {
+ uint16_t uuid;
+ uint8_t uuid_type;
+ uint8_t props;
+ uint16_t decl_handle;
+ uint16_t value_handle;
+} ble_drv_char_data_t;
+
+typedef void (*ble_drv_gap_evt_callback_t)(mp_obj_t self, uint16_t event_id, uint16_t conn_handle, uint16_t length, uint8_t * data);
+typedef void (*ble_drv_gatts_evt_callback_t)(mp_obj_t self, uint16_t event_id, uint16_t attr_handle, uint16_t length, uint8_t * data);
+typedef void (*ble_drv_gattc_evt_callback_t)(mp_obj_t self, uint16_t event_id, uint16_t attr_handle, uint16_t length, uint8_t * data);
+typedef void (*ble_drv_adv_evt_callback_t)(mp_obj_t self, uint16_t event_id, ble_drv_adv_data_t * data);
+typedef void (*ble_drv_disc_add_service_callback_t)(mp_obj_t self, ble_drv_service_data_t * p_service_data);
+typedef void (*ble_drv_disc_add_char_callback_t)(mp_obj_t self, ble_drv_char_data_t * p_desc_data);
+typedef void (*ble_drv_gattc_char_data_callback_t)(mp_obj_t self, uint16_t length, uint8_t * p_data);
+
+uint32_t ble_drv_stack_enable(void);
+
+void ble_drv_stack_disable(void);
+
+uint8_t ble_drv_stack_enabled(void);
+
+void ble_drv_address_get(ble_drv_addr_t * p_addr);
+
+bool ble_drv_uuid_add_vs(uint8_t * p_uuid, uint8_t * idx);
+
+bool ble_drv_service_add(ubluepy_service_obj_t * p_service_obj);
+
+bool ble_drv_characteristic_add(ubluepy_characteristic_obj_t * p_char_obj);
+
+bool ble_drv_advertise_data(ubluepy_advertise_data_t * p_adv_params);
+
+void ble_drv_advertise_stop(void);
+
+void ble_drv_gap_event_handler_set(mp_obj_t obs, ble_drv_gap_evt_callback_t evt_handler);
+
+void ble_drv_gatts_event_handler_set(mp_obj_t obj, ble_drv_gatts_evt_callback_t evt_handler);
+
+void ble_drv_gattc_event_handler_set(mp_obj_t obj, ble_drv_gattc_evt_callback_t evt_handler);
+
+void ble_drv_attr_s_read(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data);
+
+void ble_drv_attr_c_read(uint16_t conn_handle, uint16_t handle, mp_obj_t obj, ble_drv_gattc_char_data_callback_t cb);
+
+void ble_drv_attr_s_write(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data);
+
+void ble_drv_attr_s_notify(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data);
+
+void ble_drv_attr_c_write(uint16_t conn_handle, uint16_t handle, uint16_t len, uint8_t * p_data, bool w_response);
+
+void ble_drv_scan_start(void);
+
+void ble_drv_scan_stop(void);
+
+void ble_drv_adv_report_handler_set(mp_obj_t obj, ble_drv_adv_evt_callback_t evt_handler);
+
+void ble_drv_connect(uint8_t * p_addr, uint8_t addr_type);
+
+bool ble_drv_discover_services(mp_obj_t obj, uint16_t conn_handle, uint16_t start_handle, ble_drv_disc_add_service_callback_t cb);
+
+bool ble_drv_discover_characteristic(mp_obj_t obj,
+ uint16_t conn_handle,
+ uint16_t start_handle,
+ uint16_t end_handle,
+ ble_drv_disc_add_char_callback_t cb);
+
+void ble_drv_discover_descriptors(void);
+
+#endif // BLUETOOTH_SD
+
+#endif // BLUETOOTH_LE_DRIVER_H__
diff --git a/ports/nrf/drivers/bluetooth/ble_uart.c b/ports/nrf/drivers/bluetooth/ble_uart.c
new file mode 100644
index 000000000..cc829750c
--- /dev/null
+++ b/ports/nrf/drivers/bluetooth/ble_uart.c
@@ -0,0 +1,266 @@
+/*
+ * 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 "hal/hal_time.h"
+
+#if MICROPY_PY_BLE_NUS
+
+#if BLUETOOTH_WEBBLUETOOTH_REPL
+#include "hal_time.h"
+#endif // BLUETOOTH_WEBBLUETOOTH_REPL
+
+static ubluepy_uuid_obj_t uuid_obj_service = {
+ .base.type = &ubluepy_uuid_type,
+ .type = UBLUEPY_UUID_128_BIT,
+ .value = {0x01, 0x00}
+};
+
+static ubluepy_uuid_obj_t uuid_obj_char_tx = {
+ .base.type = &ubluepy_uuid_type,
+ .type = UBLUEPY_UUID_128_BIT,
+ .value = {0x03, 0x00}
+};
+
+static ubluepy_uuid_obj_t uuid_obj_char_rx = {
+ .base.type = &ubluepy_uuid_type,
+ .type = UBLUEPY_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;
+}
+
+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;
+ }
+}
+
+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++) {
+ 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/drivers/bluetooth/ble_uart.h b/ports/nrf/drivers/bluetooth/ble_uart.h
new file mode 100644
index 000000000..e67176a26
--- /dev/null
+++ b/ports/nrf/drivers/bluetooth/ble_uart.h
@@ -0,0 +1,42 @@
+/*
+ * 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__
+
+#if BLUETOOTH_SD
+
+#include "modubluepy.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_SD
+
+#endif // BLUETOOTH_LE_UART_H__
diff --git a/ports/nrf/drivers/bluetooth/bluetooth_common.mk b/ports/nrf/drivers/bluetooth/bluetooth_common.mk
new file mode 100644
index 000000000..38c604e04
--- /dev/null
+++ b/ports/nrf/drivers/bluetooth/bluetooth_common.mk
@@ -0,0 +1,55 @@
+
+SOFTDEV_HEX_NAME ?=
+SOFTDEV_HEX_PATH ?=
+
+ifeq ($(SD), s110)
+ INC += -Idrivers/bluetooth/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)_API/include
+ CFLAGS += -DBLUETOOTH_SD_DEBUG=1
+ CFLAGS += -DBLUETOOTH_SD=110
+ SOFTDEV_HEX_NAME = $(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)_softdevice.hex
+ SOFTDEV_HEX_PATH = drivers/bluetooth/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)
+
+else ifeq ($(SD), s120)
+ $(error No BLE wrapper available yet)
+else ifeq ($(SD), s130)
+ $(error No BLE wrapper available yet)
+else ifeq ($(SD), s132)
+ INC += -Idrivers/bluetooth/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)_API/include
+ INC += -Idrivers/bluetooth/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)_API/include/$(MCU_VARIANT)
+ CFLAGS += -DBLUETOOTH_SD_DEBUG=1
+ CFLAGS += -DBLUETOOTH_SD=132
+
+ifeq ($(SOFTDEV_VERSION), 2.0.1)
+ CFLAGS += -DBLE_API_VERSION=2
+else ifeq ($(SOFTDEV_VERSION), 3.0.0)
+ CFLAGS += -DBLE_API_VERSION=3
+endif
+
+ SOFTDEV_HEX_NAME = $(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)_softdevice.hex
+ SOFTDEV_HEX_PATH = drivers/bluetooth/$(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION)
+else
+ $(error Incorrect softdevice set flag)
+endif
+
+define STACK_MISSING_ERROR
+
+
+###### ERROR: Bluetooth LE Stack not found ############
+# #
+# The build target requires a Bluetooth LE stack. #
+# $(SD)_$(MCU_VARIANT)_$(SOFTDEV_VERSION) Bluetooth LE stack not found. #
+# #
+# Please run the download script: #
+# #
+# drivers/bluetooth/download_ble_stack.sh #
+# #
+#######################################################
+
+endef
+
+
+SOFTDEV_HEX = $(SOFTDEV_HEX_PATH)/$(SOFTDEV_HEX_NAME)
+
+ifeq ($(shell test ! -e $(SOFTDEV_HEX) && echo -n no),no)
+ $(error $(STACK_MISSING_ERROR))
+endif
diff --git a/ports/nrf/drivers/bluetooth/download_ble_stack.sh b/ports/nrf/drivers/bluetooth/download_ble_stack.sh
new file mode 100755
index 000000000..537742605
--- /dev/null
+++ b/ports/nrf/drivers/bluetooth/download_ble_stack.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+
+function download_s110_nrf51_8_0_0
+{
+ echo ""
+ echo "####################################"
+ echo "### Downloading s110_nrf51_8.0.0 ###"
+ echo "####################################"
+ echo ""
+
+ mkdir -p $1/s110_nrf51_8.0.0
+ cd $1/s110_nrf51_8.0.0
+ wget https://www.nordicsemi.com/eng/nordic/download_resource/45846/3/78153065/80234
+ mv 80234 temp.zip
+ unzip -u temp.zip
+ rm temp.zip
+ cd -
+}
+
+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_3_0_0
+{
+ echo ""
+ echo "####################################"
+ echo "### Downloading s132_nrf52_3.0.0 ###"
+ echo "####################################"
+ echo ""
+
+ mkdir -p $1/s132_nrf52_3.0.0
+ cd $1/s132_nrf52_3.0.0
+
+ wget https://www.nordicsemi.com/eng/nordic/download_resource/56261/6/26298825/108144
+ mv 108144 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_s110_nrf51_8_0_0 ${SCRIPT_DIR}
+ download_s132_nrf52_2_0_1 ${SCRIPT_DIR}
+ download_s132_nrf52_3_0_0 ${SCRIPT_DIR}
+else
+ case $1 in
+ "s110_nrf51" )
+ download_s110_nrf51_8_0_0 ${SCRIPT_DIR} ;;
+ "s132_nrf52_2_0_1" )
+ download_s132_nrf52_2_0_1 ${SCRIPT_DIR} ;;
+ "s132_nrf52_3_0_0" )
+ download_s132_nrf52_3_0_0 ${SCRIPT_DIR} ;;
+ esac
+fi
+
+exit 0
diff --git a/ports/nrf/drivers/bluetooth/ringbuffer.h b/ports/nrf/drivers/bluetooth/ringbuffer.h
new file mode 100644
index 000000000..3438b5c9b
--- /dev/null
+++ b/ports/nrf/drivers/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
diff --git a/ports/nrf/drivers/softpwm.c b/ports/nrf/drivers/softpwm.c
new file mode 100644
index 000000000..22564f7d0
--- /dev/null
+++ b/ports/nrf/drivers/softpwm.c
@@ -0,0 +1,257 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Mark Shannon
+ *
+ * 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 "py/mphal.h"
+
+#if MICROPY_PY_MACHINE_SOFT_PWM
+
+#include "stddef.h"
+#include "py/runtime.h"
+#include "py/gc.h"
+#include "hal_timer.h"
+#include "hal_gpio.h"
+#include "pin.h"
+
+#include "ticker.h"
+
+#define CYCLES_PER_MICROSECONDS 16
+
+#define MICROSECONDS_PER_TICK 16
+#define CYCLES_PER_TICK (CYCLES_PER_MICROSECONDS*MICROSECONDS_PER_TICK)
+// This must be an integer multiple of MICROSECONDS_PER_TICK
+#define MICROSECONDS_PER_MACRO_TICK 6000
+#define MILLISECONDS_PER_MACRO_TICK 6
+
+#define PWM_TICKER_INDEX 2
+
+// Default period of 20ms
+#define DEFAULT_PERIOD ((20*1000)/MICROSECONDS_PER_TICK)
+
+typedef struct _pwm_event {
+ uint16_t time;
+ uint8_t pin;
+ uint8_t turn_on;
+} pwm_event;
+
+typedef struct _pwm_events {
+ uint8_t count;
+ uint16_t period;
+ uint32_t all_pins;
+ pwm_event events[1];
+} pwm_events;
+
+static const pwm_events OFF_EVENTS = {
+ .count = 1,
+ .period = DEFAULT_PERIOD,
+ .all_pins = 0,
+ .events = {
+ {
+ .time = 1024,
+ .pin = 31,
+ .turn_on = 0
+ }
+ }
+};
+
+#define active_events MP_STATE_PORT(pwm_active_events)
+#define pending_events MP_STATE_PORT(pwm_pending_events)
+
+void softpwm_init(void) {
+ active_events = &OFF_EVENTS;
+ pending_events = NULL;
+}
+
+static uint8_t next_event = 0;
+
+static inline int32_t pwm_get_period_ticks(void) {
+ const pwm_events *tmp = pending_events;
+ if (tmp == NULL)
+ tmp = active_events;
+ return tmp->period;
+}
+
+#if 0
+void pwm_dump_events(const pwm_events *events) {
+ printf("Count %d, period %d, all pins %d\r\n", events->count, events->period, events->all_pins);
+ for (uint32_t i = 0; i < events->count; i++) {
+ const pwm_event *event = &events->events[i];
+ printf("Event. pin: %d, duty cycle: %d, turn_on: %d\r\n",
+ event->pin, event->time, event->turn_on);
+ }
+}
+
+void pwm_dump_state(void) {
+ while(pending_events);
+ pwm_dump_events(active_events);
+}
+#endif
+
+static const pwm_events *swap_pending(const pwm_events *in) {
+ __disable_irq();
+ const pwm_events *result = pending_events;
+ pending_events = in;
+ __enable_irq();
+ return result;
+}
+
+static pwm_events *copy_events(const pwm_events *orig, uint32_t count) {
+ pwm_events *events = m_malloc(sizeof(pwm_events) + (count-1)*sizeof(pwm_event));
+ events->count = count;
+ uint32_t copy = count > orig->count ? orig->count : count;
+ for (uint32_t i = 0; i < copy; i++) {
+ events->events[i] = orig->events[i];
+ }
+ return events;
+}
+
+static int find_pin_in_events(const pwm_events *events, uint32_t pin) {
+ for (int i = 0; i < events->count; i++) {
+ if (events->events[i].pin == pin)
+ return i;
+ }
+ return -1;
+}
+
+static void sort_events(pwm_events *events) {
+ // Insertion sort
+ for (int32_t i = 1; i < events->count; i++) {
+ pwm_event x = events->events[i];
+ int32_t j;
+ for (j = i - 1; j >= 0 && events->events[j].time > x.time; j--) {
+ events->events[j+1] = events->events[j];
+ }
+ events->events[j+1] = x;
+ }
+}
+
+int32_t pwm_callback(void) {
+ int32_t tdiff;
+ const pwm_events *events = active_events;
+ const pwm_event *event = &events->events[next_event];
+ int32_t tnow = (event->time*events->period)>>10;
+ do {
+ if (event->turn_on) {
+ hal_gpio_pin_set(0, event->pin);
+ next_event++;
+ } else {
+ hal_gpio_out_clear(0, events->all_pins);
+ next_event = 0;
+ tnow = 0;
+ if (pending_events) {
+ events = pending_events;
+ active_events = events;
+ pending_events = NULL;
+ }
+ }
+ event = &events->events[next_event];
+ tdiff = ((event->time*events->period)>>10) - tnow;
+ } while (tdiff == 0);
+ return tdiff;
+}
+
+void pwm_start(void) {
+ set_ticker_callback(PWM_TICKER_INDEX, pwm_callback, 120);
+}
+
+void pwm_stop(void) {
+ clear_ticker_callback(PWM_TICKER_INDEX);
+}
+
+static void pwm_set_period_ticks(int32_t ticks) {
+ const pwm_events *old_events = swap_pending(NULL);
+ if (old_events == NULL) {
+ old_events = active_events;
+ }
+ pwm_events *events = copy_events(old_events, old_events->count);
+ events->all_pins = old_events->all_pins;
+ events->period = ticks;
+ pending_events = events;
+}
+
+int pwm_set_period_us(int32_t us) {
+ if ((us < 256) ||
+ (us > 1000000)) {
+ return -1;
+ }
+ pwm_set_period_ticks(us/MICROSECONDS_PER_TICK);
+ return 0;
+}
+
+int32_t pwm_get_period_us(void) {
+ return pwm_get_period_ticks()*MICROSECONDS_PER_TICK;
+}
+
+void pwm_set_duty_cycle(int32_t pin, uint32_t value) {
+ if (value >= (1<<10)) {
+ value = (1<<10)-1;
+ }
+ uint32_t turn_on_time = 1024-value;
+ const pwm_events *old_events = swap_pending(NULL);
+ if (old_events == NULL) {
+ old_events = active_events;
+ }
+ if (((1<<pin)&old_events->all_pins) == 0) {
+ hal_gpio_cfg_pin(0, pin, HAL_GPIO_MODE_OUTPUT, HAL_GPIO_PULL_DISABLED);
+ }
+ int ev = find_pin_in_events(old_events, pin);
+ pwm_events *events;
+ if (ev < 0 && value == 0) {
+ return;
+ } else if (ev < 0) {
+ events = copy_events(old_events, old_events->count+1);
+ events->all_pins = old_events->all_pins | (1<<pin);
+ events->events[old_events->count].time = turn_on_time;
+ events->events[old_events->count].pin = pin;
+ events->events[old_events->count].turn_on = 1;
+ } else if (value == 0) {
+ events = copy_events(old_events, old_events->count-1);
+ events->all_pins = old_events->all_pins & ~(1<<pin);
+ if (ev < old_events->count-1) {
+ events->events[ev] = old_events->events[old_events->count-1];
+ }
+ } else {
+ events = copy_events(old_events, old_events->count);
+ events->all_pins = old_events->all_pins;
+ events->events[ev].time = turn_on_time;
+ }
+ events->period = old_events->period;
+ sort_events(events);
+ pending_events = events;
+ return;
+}
+
+void pwm_release(int32_t pin) {
+ pwm_set_duty_cycle(pin, 0);
+ const pwm_events *ev = active_events;
+ int i = find_pin_in_events(ev, pin);
+ if (i < 0)
+ return;
+ // If i >= 0 it means that `ev` is in RAM, so it safe to discard the const qualifier
+ ((pwm_events *)ev)->events[i].pin = 31;
+ hal_gpio_pin_clear(0, pin);
+}
+
+#endif // MICROPY_PY_MACHINE_SOFT_PWM
diff --git a/ports/nrf/drivers/softpwm.h b/ports/nrf/drivers/softpwm.h
new file mode 100644
index 000000000..a73c15cd8
--- /dev/null
+++ b/ports/nrf/drivers/softpwm.h
@@ -0,0 +1,13 @@
+#ifndef __MICROPY_INCLUDED_LIB_PWM_H__
+#define __MICROPY_INCLUDED_LIB_PWM_H__
+
+void softpwm_init(void);
+void pwm_start(void);
+void pwm_stop(void);
+
+int pwm_set_period_us(int32_t us);
+int32_t pwm_get_period_us(void);
+void pwm_set_duty_cycle(int32_t pin, int32_t value);
+void pwm_release(int32_t pin);
+
+#endif // __MICROPY_INCLUDED_LIB_PWM_H__
diff --git a/ports/nrf/drivers/ticker.c b/ports/nrf/drivers/ticker.c
new file mode 100644
index 000000000..aa730d643
--- /dev/null
+++ b/ports/nrf/drivers/ticker.c
@@ -0,0 +1,162 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Mark Shannon
+ *
+ * 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 "py/mphal.h"
+
+#if MICROPY_PY_MACHINE_SOFT_PWM
+
+#include "ticker.h"
+#include "hal_irq.h"
+
+#define FastTicker NRF_TIMER1
+#define FastTicker_IRQn TIMER1_IRQn
+#define FastTicker_IRQHandler TIMER1_IRQHandler
+
+#define SlowTicker_IRQn SWI0_IRQn
+#define SlowTicker_IRQHandler SWI0_IRQHandler
+
+// Ticker callback function called every MACRO_TICK
+static volatile callback_ptr slow_ticker;
+
+void ticker_init(callback_ptr slow_ticker_callback) {
+ slow_ticker = slow_ticker_callback;
+
+ NRF_TIMER_Type *ticker = FastTicker;
+#ifdef NRF51
+ ticker->POWER = 1;
+#endif
+ __NOP();
+ ticker_stop();
+ ticker->TASKS_CLEAR = 1;
+ ticker->CC[3] = MICROSECONDS_PER_MACRO_TICK;
+ ticker->MODE = TIMER_MODE_MODE_Timer;
+ ticker->BITMODE = TIMER_BITMODE_BITMODE_24Bit << TIMER_BITMODE_BITMODE_Pos;
+ ticker->PRESCALER = 4; // 1 tick == 1 microsecond
+ ticker->INTENSET = TIMER_INTENSET_COMPARE3_Msk;
+ ticker->SHORTS = 0;
+
+#ifdef NRF51
+ hal_irq_priority(FastTicker_IRQn, 1);
+#else
+ hal_irq_priority(FastTicker_IRQn, 2);
+#endif
+
+ hal_irq_priority(SlowTicker_IRQn, 3);
+ hal_irq_priority(SlowTicker_IRQn, 3);
+
+ hal_irq_enable(SlowTicker_IRQn);
+}
+
+/* Start and stop timer 1 including workarounds for Anomaly 73 for Timer
+* http://www.nordicsemi.com/eng/content/download/29490/494569/file/nRF51822-PAN%20v3.0.pdf
+*/
+void ticker_start(void) {
+ hal_irq_enable(FastTicker_IRQn);
+#ifdef NRF51
+ *(uint32_t *)0x40009C0C = 1; // for Timer 1
+#endif
+ FastTicker->TASKS_START = 1;
+}
+
+void ticker_stop(void) {
+ hal_irq_disable(FastTicker_IRQn);
+ FastTicker->TASKS_STOP = 1;
+#ifdef NRF51
+ *(uint32_t *)0x40009C0C = 0; // for Timer 1
+#endif
+}
+
+int32_t noop(void) {
+ return -1;
+}
+
+volatile uint32_t ticks;
+
+static ticker_callback_ptr callbacks[3] = { noop, noop, noop };
+
+void FastTicker_IRQHandler(void) {
+ NRF_TIMER_Type *ticker = FastTicker;
+ ticker_callback_ptr *call = callbacks;
+ if (ticker->EVENTS_COMPARE[0]) {
+ ticker->EVENTS_COMPARE[0] = 0;
+ ticker->CC[0] += call[0]()*MICROSECONDS_PER_TICK;
+ }
+ if (ticker->EVENTS_COMPARE[1]) {
+ ticker->EVENTS_COMPARE[1] = 0;
+ ticker->CC[1] += call[1]()*MICROSECONDS_PER_TICK;
+ }
+ if (ticker->EVENTS_COMPARE[2]) {
+ ticker->EVENTS_COMPARE[2] = 0;
+ ticker->CC[2] += call[2]()*MICROSECONDS_PER_TICK;
+ }
+ if (ticker->EVENTS_COMPARE[3]) {
+ ticker->EVENTS_COMPARE[3] = 0;
+ ticker->CC[3] += MICROSECONDS_PER_MACRO_TICK;
+ ticks += MILLISECONDS_PER_MACRO_TICK;
+ hal_irq_pending(SlowTicker_IRQn);
+ }
+}
+
+
+static const uint32_t masks[3] = {
+ TIMER_INTENCLR_COMPARE0_Msk,
+ TIMER_INTENCLR_COMPARE1_Msk,
+ TIMER_INTENCLR_COMPARE2_Msk,
+};
+
+int set_ticker_callback(uint32_t index, ticker_callback_ptr func, int32_t initial_delay_us) {
+ if (index > 3)
+ return -1;
+ NRF_TIMER_Type *ticker = FastTicker;
+ callbacks[index] = noop;
+ ticker->INTENCLR = masks[index];
+ ticker->TASKS_CAPTURE[index] = 1;
+ uint32_t t = FastTicker->CC[index];
+ // Need to make sure that set tick is aligned to lastest tick
+ // Use CC[3] as a reference, as that is always up-to-date.
+ int32_t cc3 = FastTicker->CC[3];
+ int32_t delta = t+initial_delay_us-cc3;
+ delta = (delta/MICROSECONDS_PER_TICK+1)*MICROSECONDS_PER_TICK;
+ callbacks[index] = func;
+ ticker->INTENSET = masks[index];
+ FastTicker->CC[index] = cc3 + delta;
+ return 0;
+}
+
+int clear_ticker_callback(uint32_t index) {
+ if (index > 3)
+ return -1;
+ FastTicker->INTENCLR = masks[index];
+ callbacks[index] = noop;
+ return 0;
+}
+
+void SlowTicker_IRQHandler(void)
+{
+ slow_ticker();
+}
+
+#endif // MICROPY_PY_MACHINE_SOFT_PWM
diff --git a/ports/nrf/drivers/ticker.h b/ports/nrf/drivers/ticker.h
new file mode 100644
index 000000000..6ac87cd50
--- /dev/null
+++ b/ports/nrf/drivers/ticker.h
@@ -0,0 +1,30 @@
+#ifndef __MICROPY_INCLUDED_LIB_TICKER_H__
+#define __MICROPY_INCLUDED_LIB_TICKER_H__
+
+/*************************************
+ * 62.5kHz (16µs cycle time) ticker.
+ ************************************/
+
+#include "nrf.h"
+
+typedef void (*callback_ptr)(void);
+typedef int32_t (*ticker_callback_ptr)(void);
+
+void ticker_init(callback_ptr slow_ticker_callback);
+void ticker_start(void);
+void ticker_stop(void);
+
+int clear_ticker_callback(uint32_t index);
+int set_ticker_callback(uint32_t index, ticker_callback_ptr func, int32_t initial_delay_us);
+
+int set_low_priority_callback(callback_ptr callback, int id);
+
+#define CYCLES_PER_MICROSECONDS 16
+
+#define MICROSECONDS_PER_TICK 16
+#define CYCLES_PER_TICK (CYCLES_PER_MICROSECONDS*MICROSECONDS_PER_TICK)
+// This must be an integer multiple of MICROSECONDS_PER_TICK
+#define MICROSECONDS_PER_MACRO_TICK 6000
+#define MILLISECONDS_PER_MACRO_TICK 6
+
+#endif // __MICROPY_INCLUDED_LIB_TICKER_H__ \ No newline at end of file