summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Halbert <halbert@halwitz.org>2018-12-28 22:55:29 -0500
committerDan Halbert <halbert@halwitz.org>2018-12-28 23:34:23 -0500
commit4d1f0ec07be5e9cacaf0072a0f987871b8505ec4 (patch)
tree2cb328eb77d8c9732ecd24580b33869fa8a3c505
parent4167bf5b241eec4e5a04c36d7f7c66d43a8bb921 (diff)
Add Broadcaster. Reset correctly on reload.
-rwxr-xr-xports/nrf/Makefile1
-rw-r--r--ports/nrf/bluetooth/ble_drv.c7
-rw-r--r--ports/nrf/bluetooth/ble_drv.h3
-rw-r--r--ports/nrf/common-hal/bleio/Adapter.c20
-rw-r--r--ports/nrf/common-hal/bleio/Broadcaster.c113
-rw-r--r--ports/nrf/common-hal/bleio/Broadcaster.h47
-rw-r--r--ports/nrf/common-hal/bleio/LocalPeripheral.c329
-rw-r--r--ports/nrf/common-hal/bleio/LocalPeripheral.h64
-rw-r--r--ports/nrf/common-hal/bleio/__init__.c7
-rw-r--r--ports/nrf/common-hal/bleio/__init__.h42
-rw-r--r--ports/nrf/supervisor/port.c3
-rw-r--r--shared-bindings/bleio/Broadcaster.c140
-rw-r--r--shared-bindings/bleio/Broadcaster.h38
-rw-r--r--shared-bindings/bleio/LocalPeripheral.c250
-rw-r--r--shared-bindings/bleio/LocalPeripheral.h39
-rw-r--r--shared-bindings/bleio/__init__.c2
-rw-r--r--shared-module/bleio/__init__.h38
17 files changed, 1125 insertions, 18 deletions
diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile
index c427cdd26..58db910c1 100755
--- a/ports/nrf/Makefile
+++ b/ports/nrf/Makefile
@@ -172,6 +172,7 @@ ifneq ($(SD), )
SRC_COMMON_HAL += \
bleio/__init__.c \
bleio/Adapter.c \
+ bleio/Broadcaster.c \
bleio/Characteristic.c \
bleio/Descriptor.c \
bleio/LocalPeripheral.c \
diff --git a/ports/nrf/bluetooth/ble_drv.c b/ports/nrf/bluetooth/ble_drv.c
index 0b6daf711..41d51cfb4 100644
--- a/ports/nrf/bluetooth/ble_drv.c
+++ b/ports/nrf/bluetooth/ble_drv.c
@@ -45,7 +45,12 @@ typedef struct event_handler {
ble_drv_evt_handler_t func;
} event_handler_t;
-static event_handler_t *m_event_handlers;
+static event_handler_t *m_event_handlers = NULL;
+
+void ble_drv_reset() {
+ // Linked-list members will be gc'd.
+ m_event_handlers = NULL;
+}
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);
diff --git a/ports/nrf/bluetooth/ble_drv.h b/ports/nrf/bluetooth/ble_drv.h
index 0443b41b8..696aff1da 100644
--- a/ports/nrf/bluetooth/ble_drv.h
+++ b/ports/nrf/bluetooth/ble_drv.h
@@ -43,11 +43,14 @@
#define BLE_CONN_CFG_TAG_CUSTOM 1
#define MSEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000) / (RESOLUTION))
+// 0.625 msecs (625 usecs)
+#define ADV_INTERVAL_UNIT_FLOAT_SECS (0.000625)
#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_reset();
void ble_drv_add_event_handler(ble_drv_evt_handler_t func, void *param);
#endif // MICROPY_INCLUDED_NRF_BLUETOOTH_BLE_DRV_H
diff --git a/ports/nrf/common-hal/bleio/Adapter.c b/ports/nrf/common-hal/bleio/Adapter.c
index 318502e95..cd116711e 100644
--- a/ports/nrf/common-hal/bleio/Adapter.c
+++ b/ports/nrf/common-hal/bleio/Adapter.c
@@ -44,17 +44,11 @@ STATIC void softdevice_assert_handler(uint32_t id, uint32_t pc, uint32_t info) {
STATIC uint32_t ble_stack_enable(void) {
nrf_clock_lf_cfg_t clock_config = {
.source = NRF_CLOCK_LF_SRC_XTAL,
-#if (BLE_API_VERSION == 4)
.accuracy = NRF_CLOCK_LF_ACCURACY_20_PPM
-#else
- .xtal_accuracy = NRF_CLOCK_LF_XTAL_ACCURACY_20_PPM
-#endif
};
-#if (BLUETOOTH_SD == 140)
// The SD takes over the POWER IRQ and will fail if the IRQ is already in use
nrfx_power_uninit();
-#endif
uint32_t err_code = sd_softdevice_enable(&clock_config, softdevice_assert_handler);
if (err_code != NRF_SUCCESS)
@@ -64,17 +58,10 @@ STATIC uint32_t ble_stack_enable(void) {
if (err_code != NRF_SUCCESS)
return err_code;
- uint32_t app_ram_start;
-#if (BLE_API_VERSION == 2)
- ble_enable_params_t ble_enable_params = {
- .gatts_enable_params.attr_tab_size = BLE_GATTS_ATTR_TAB_SIZE_DEFAULT,
- .gap_enable_params.central_conn_count = 1,
- .gap_enable_params.periph_conn_count = 1,
- };
+ // Start with no event handlers, etc.
+ ble_drv_reset();
- app_ram_start = 0x200039c0;
- err_code = sd_ble_enable(&ble_enable_params, &app_ram_start);
-#else
+ uint32_t app_ram_start;
app_ram_start = 0x20004000;
ble_cfg_t ble_conf;
@@ -100,7 +87,6 @@ STATIC uint32_t ble_stack_enable(void) {
return err_code;
err_code = sd_ble_enable(&app_ram_start);
-#endif
return err_code;
}
diff --git a/ports/nrf/common-hal/bleio/Broadcaster.c b/ports/nrf/common-hal/bleio/Broadcaster.c
new file mode 100644
index 000000000..508ede1d6
--- /dev/null
+++ b/ports/nrf/common-hal/bleio/Broadcaster.c
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Dan Halbert for Adafruit Industries
+ *
+ * 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 <string.h>
+
+#include "ble.h"
+#include "ble_drv.h"
+#include "ble_hci.h"
+#include "nrf_soc.h"
+#include "py/runtime.h"
+
+#include "common-hal/bleio/Broadcaster.h"
+#include "shared-bindings/bleio/Adapter.h"
+#include "shared-bindings/bleio/Broadcaster.h"
+
+static uint8_t m_adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET;
+
+STATIC void check_data_fit(size_t pos, size_t data_len) {
+ if (pos + data_len >= BLE_GAP_ADV_SET_DATA_SIZE_MAX) {
+ mp_raise_ValueError(translate("Data too large for advertisement packet"));
+ }
+}
+
+void common_hal_bleio_broadcaster_construct(bleio_broadcaster_obj_t *self, mp_float_t interval) {
+ common_hal_bleio_adapter_set_enabled(true); // TODO -- Do this somewhere else maybe bleio __init__
+ const mp_float_t min = BLE_GAP_ADV_INTERVAL_MIN * ADV_INTERVAL_UNIT_FLOAT_SECS;
+ const mp_float_t max = BLE_GAP_ADV_INTERVAL_MAX * ADV_INTERVAL_UNIT_FLOAT_SECS;
+
+ if (interval < min || interval > max) {
+ // Would like to print range using the constants above, but vargs would convert to double.
+ mp_raise_ValueError(translate("interval not in range 0.0020 to 10.24"));
+ }
+ self->interval = interval;
+}
+
+
+void common_hal_bleio_broadcaster_start_advertising(bleio_broadcaster_obj_t *self, mp_buffer_info_t *data) {
+ size_t adv_data_pos = 0;
+ uint32_t err_code;
+
+ // Build up advertising packet.
+ check_data_fit(adv_data_pos, 1 + 1 + 1);
+ self->adv_data[adv_data_pos++] = 2;
+ self->adv_data[adv_data_pos++] = BLE_GAP_AD_TYPE_FLAGS;
+ self->adv_data[adv_data_pos++] = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
+
+ // Data is always send as manufacturer-specific data
+ check_data_fit(adv_data_pos, 1 + 1 + data->len);
+ self->adv_data[adv_data_pos++] = 1 + data->len;
+ self->adv_data[adv_data_pos++] = BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA;
+ memcpy(&(self->adv_data[adv_data_pos]), data->buf, data->len);
+ adv_data_pos += data->len;
+
+ ble_gap_adv_params_t m_adv_params = {
+ .interval = (uint32_t) (self->interval / ADV_INTERVAL_UNIT_FLOAT_SECS),
+ .properties.type = BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED,
+ .duration = BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED,
+ .filter_policy = BLE_GAP_ADV_FP_ANY,
+ .primary_phy = BLE_GAP_PHY_1MBPS,
+ };
+
+ common_hal_bleio_broadcaster_stop_advertising(self);
+
+ const ble_gap_adv_data_t ble_gap_adv_data = {
+ .adv_data.p_data = self->adv_data,
+ .adv_data.len = adv_data_pos,
+ };
+
+ err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &ble_gap_adv_data, &m_adv_params);
+ if (err_code == NRF_SUCCESS) {
+ err_code = sd_ble_gap_adv_start(m_adv_handle, BLE_CONN_CFG_TAG_CUSTOM);
+ }
+
+ if (err_code != NRF_SUCCESS) {
+ mp_raise_OSError_msg_varg(translate("Failed to start advertising, err 0x%04x"), err_code);
+ }
+}
+
+void common_hal_bleio_broadcaster_stop_advertising(bleio_broadcaster_obj_t *self) {
+
+ if (m_adv_handle == BLE_GAP_ADV_SET_HANDLE_NOT_SET) {
+ return;
+ }
+
+ const uint32_t err_code = sd_ble_gap_adv_stop(m_adv_handle);
+
+ if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE)) {
+ mp_raise_OSError_msg_varg(translate("Failed to stop advertising, err 0x%04x"), err_code);
+ }
+}
diff --git a/ports/nrf/common-hal/bleio/Broadcaster.h b/ports/nrf/common-hal/bleio/Broadcaster.h
new file mode 100644
index 000000000..72f93a443
--- /dev/null
+++ b/ports/nrf/common-hal/bleio/Broadcaster.h
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Artur Pacholec
+ * Copyright (c) 2018 Dan Halbert for Adafruit Industries
+ *
+ * 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 MICROPY_INCLUDED_COMMON_HAL_BLEIO_BROADCASTER_H
+#define MICROPY_INCLUDED_COMMON_HAL_BLEIO_BROADCASTER_H
+
+#include "ble.h"
+
+#include "shared-module/bleio/__init__.h"
+#include "shared-module/bleio/Address.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ // In seconds.
+ mp_float_t interval;
+ // The advertising data buffer is held by us, not by the SD, so we must
+ // maintain it and not change it. If we need to change its contents during advertising,
+ // there are tricks to get the SD to notice (see DevZone - TBS).
+ uint8_t adv_data[BLE_GAP_ADV_SET_DATA_SIZE_MAX];
+
+} bleio_broadcaster_obj_t;
+
+#endif // MICROPY_INCLUDED_COMMON_HAL_BLEIO_BROADCASTER_H
diff --git a/ports/nrf/common-hal/bleio/LocalPeripheral.c b/ports/nrf/common-hal/bleio/LocalPeripheral.c
new file mode 100644
index 000000000..2a502c8dd
--- /dev/null
+++ b/ports/nrf/common-hal/bleio/LocalPeripheral.c
@@ -0,0 +1,329 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * 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 <string.h>
+#include <stdio.h>
+
+#include "ble.h"
+#include "ble_drv.h"
+#include "ble_hci.h"
+#include "nrf_soc.h"
+#include "py/objstr.h"
+#include "py/runtime.h"
+#include "shared-bindings/bleio/Adapter.h"
+#include "shared-bindings/bleio/Characteristic.h"
+#include "shared-bindings/bleio/LocalPeripheral.h"
+#include "shared-bindings/bleio/Service.h"
+#include "shared-bindings/bleio/UUID.h"
+
+#define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS)
+#define BLE_MAX_CONN_INTERVAL MSEC_TO_UNITS(300, UNIT_0_625_MS)
+#define BLE_SLAVE_LATENCY 0
+#define BLE_CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS)
+
+#define BLE_ADV_LENGTH_FIELD_SIZE 1
+#define BLE_ADV_AD_TYPE_FIELD_SIZE 1
+#define BLE_AD_TYPE_FLAGS_DATA_SIZE 1
+
+static uint8_t m_adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET;
+
+STATIC void check_data_fit(size_t pos, size_t data_len) {
+ if (pos + data_len >= BLE_GAP_ADV_SET_DATA_SIZE_MAX) {
+ mp_raise_ValueError(translate("Data too large for advertisement packet"));
+ }
+}
+
+STATIC uint32_t add_services_to_advertisement(bleio_local_peripheral_obj_t *self, size_t* adv_data_pos_p, size_t uuid_len) {
+ uint32_t uuids_total_size = 0;
+ const mp_obj_list_t *service_list = MP_OBJ_TO_PTR(self->service_list);
+ uint32_t err_code = NRF_SUCCESS;
+
+ check_data_fit(*adv_data_pos_p, 1 + 1);
+
+ // Remember where length byte is; fill in later when we know the size.
+ const size_t length_pos = *adv_data_pos_p;
+ (*adv_data_pos_p)++;
+
+ self->adv_data[(*adv_data_pos_p)++] = (uuid_len == 16)
+ ? BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE
+ : BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE;
+
+ for (size_t i = 0; i < service_list->len; ++i) {
+ const bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[i]);
+ uint8_t encoded_size = 0;
+
+ // Skip services of the wrong length and secondary services.
+ if (common_hal_bleio_uuid_get_size(service->uuid) != uuid_len || service->is_secondary) {
+ continue;
+ }
+
+ ble_uuid_t uuid;
+ bleio_uuid_convert_to_nrf_ble_uuid(service->uuid, &uuid);
+
+ err_code = sd_ble_uuid_encode(&uuid, &encoded_size, &(self->adv_data[*adv_data_pos_p]));
+ if (err_code != NRF_SUCCESS) {
+ return err_code;
+ }
+
+ check_data_fit(*adv_data_pos_p, encoded_size);
+ uuids_total_size += encoded_size;
+ (*adv_data_pos_p) += encoded_size;
+ }
+
+ self->adv_data[length_pos] = 1 + uuids_total_size; // 1 for the field type.
+ return err_code;
+}
+
+
+
+STATIC uint32_t set_advertisement_data(bleio_local_peripheral_obj_t *self, bool connectable, mp_buffer_info_t *raw_data) {
+ common_hal_bleio_adapter_set_enabled(true);
+
+ size_t adv_data_pos = 0;
+ uint32_t err_code;
+
+ GET_STR_DATA_LEN(self->name, name_data, name_len);
+ if (name_len > 0) {
+ ble_gap_conn_sec_mode_t sec_mode;
+ BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
+
+ // We'll add the name after everything else, shortening it if necessary.
+ err_code = sd_ble_gap_device_name_set(&sec_mode, name_data, name_len);
+ if (err_code != NRF_SUCCESS) {
+ return err_code;
+ }
+ }
+
+ if (raw_data->len != 0) {
+ // User-supplied advertising packet.
+ check_data_fit(adv_data_pos, raw_data->len);
+ memcpy(&(self->adv_data[adv_data_pos]), raw_data->buf, raw_data->len);
+ adv_data_pos += raw_data->len;
+ } else {
+ // Build up advertising packet.
+ check_data_fit(adv_data_pos, 1 + 1 + 1);
+ self->adv_data[adv_data_pos++] = 2;
+ self->adv_data[adv_data_pos++] = BLE_GAP_AD_TYPE_FLAGS;
+ self->adv_data[adv_data_pos++] = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
+
+ // The 16-bit ids and 128-bit ids are grouped together by length, so find it whether we have
+ // 16 and/or 128-bit service UUIDs.
+
+ const mp_obj_list_t *service_list = MP_OBJ_TO_PTR(self->service_list);
+ if (service_list->len > 0) {
+ bool has_128bit_services = false;
+ bool has_16bit_services = false;
+
+ for (size_t i = 0; i < service_list->len; ++i) {
+ const bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[i]);
+
+ if (service->is_secondary) {
+ continue;
+ }
+
+ switch (common_hal_bleio_uuid_get_size(service->uuid)) {
+ case 16:
+ has_16bit_services = true;
+ break;
+ case 128:
+ has_128bit_services = true;
+ break;
+ }
+ }
+
+ // Add 16-bit service UUID's in a group, then 128-bit service UUID's.
+
+ if (has_16bit_services) {
+ err_code = add_services_to_advertisement(self, &adv_data_pos, 16);
+ if (err_code != NRF_SUCCESS) {
+ return err_code;
+ }
+ }
+
+ if (has_128bit_services) {
+ err_code = add_services_to_advertisement(self, &adv_data_pos, 128);
+ if (err_code != NRF_SUCCESS) {
+ return err_code;
+ }
+ }
+ }
+
+ // Always include TX power.
+ check_data_fit(adv_data_pos, 1 + 1 + 1);
+ self->adv_data[adv_data_pos++] = 1 + 1;
+ self->adv_data[adv_data_pos++] = BLE_GAP_AD_TYPE_TX_POWER_LEVEL;
+ self->adv_data[adv_data_pos++] = 0; // TODO - allow power level to be set later.
+
+ // We need room for at least a one-character name.
+ check_data_fit(adv_data_pos, 1 + 1 + 1);
+
+ // How big a name can we fit?
+ size_t bytes_left = BLE_GAP_ADV_SET_DATA_SIZE_MAX - adv_data_pos - 1 - 1;
+ size_t partial_name_len = MIN(bytes_left, name_len);
+ self->adv_data[adv_data_pos++] = 1 + partial_name_len;
+ self->adv_data[adv_data_pos++] = (partial_name_len == name_len)
+ ? BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME
+ : BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME;
+ memcpy(&(self->adv_data[adv_data_pos]), name_data, partial_name_len);
+ adv_data_pos += partial_name_len;
+ } // end of advertising packet construction
+
+ static ble_gap_adv_params_t m_adv_params = {
+ .interval = MSEC_TO_UNITS(1000, UNIT_0_625_MS),
+ .properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED,
+ .duration = BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED,
+ .filter_policy = BLE_GAP_ADV_FP_ANY,
+ .primary_phy = BLE_GAP_PHY_1MBPS,
+ };
+
+ if (!connectable) {
+ m_adv_params.properties.type = BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED;
+ }
+
+ common_hal_bleio_local_peripheral_stop_advertising(self);
+
+ const ble_gap_adv_data_t ble_gap_adv_data = {
+ .adv_data.p_data = self->adv_data,
+ .adv_data.len = adv_data_pos,
+ };
+
+ err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &ble_gap_adv_data, &m_adv_params);
+ if (err_code != NRF_SUCCESS) {
+ return err_code;
+ }
+
+ err_code = sd_ble_gap_adv_start(m_adv_handle, BLE_CONN_CFG_TAG_CUSTOM);
+
+ return err_code;
+}
+
+STATIC void on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
+ bleio_local_peripheral_obj_t *self = (bleio_local_peripheral_obj_t*)self_in;
+
+ switch (ble_evt->header.evt_id) {
+ case BLE_GAP_EVT_CONNECTED: {
+ // Central has connected.
+ ble_gap_conn_params_t conn_params;
+ self->conn_handle = ble_evt->evt.gap_evt.conn_handle;
+ sd_ble_gap_ppcp_get(&conn_params);
+ sd_ble_gap_conn_param_update(ble_evt->evt.gap_evt.conn_handle, &conn_params);
+ break;
+ }
+
+ case BLE_GAP_EVT_DISCONNECTED:
+ // Central has disconnected.
+ self->conn_handle = BLE_CONN_HANDLE_INVALID;
+ break;
+
+ case BLE_GAP_EVT_PHY_UPDATE_REQUEST: {
+ ble_gap_phys_t const phys = {
+ .rx_phys = BLE_GAP_PHY_AUTO,
+ .tx_phys = BLE_GAP_PHY_AUTO,
+ };
+ sd_ble_gap_phy_update(ble_evt->evt.gap_evt.conn_handle, &phys);
+ break;
+ }
+
+ case BLE_GAP_EVT_ADV_SET_TERMINATED:
+ // Someday may handle timeouts or limit reached.
+ break;
+
+ case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
+ sd_ble_gap_sec_params_reply(self->conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL);
+ break;
+
+ case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: {
+ ble_gap_evt_conn_param_update_request_t *request = &ble_evt->evt.gap_evt.params.conn_param_update_request;
+ sd_ble_gap_conn_param_update(self->conn_handle, &request->conn_params);
+ break;
+ }
+
+ case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST: {
+ sd_ble_gatts_exchange_mtu_reply(self->conn_handle, BLE_GATT_ATT_MTU_DEFAULT);
+ break;
+ }
+
+ }
+}
+
+
+void common_hal_bleio_local_peripheral_construct(bleio_local_peripheral_obj_t *self) {
+ common_hal_bleio_adapter_set_enabled(true); // TODO -- Do this somewhere else maybe bleio __init__
+
+ self->gatt_role = GATT_ROLE_NONE;
+ self->conn_handle = BLE_CONN_HANDLE_INVALID;
+
+ // Add all the services.
+
+ mp_obj_list_t *service_list = MP_OBJ_TO_PTR(self->service_list);
+ for (size_t service_idx = 0; service_idx < service_list->len; ++service_idx) {
+ bleio_service_obj_t *service = MP_OBJ_TO_PTR(service_list->items[service_idx]);
+
+ ble_uuid_t uuid;
+ bleio_uuid_convert_to_nrf_ble_uuid(service->uuid, &uuid);
+
+ uint8_t service_type = BLE_GATTS_SRVC_TYPE_PRIMARY;
+ if (service->is_secondary) {
+ service_type = BLE_GATTS_SRVC_TYPE_SECONDARY;
+ }
+
+ const uint32_t err_code = sd_ble_gatts_service_add(service_type, &uuid, &service->handle);
+ if (err_code != NRF_SUCCESS) {
+ mp_raise_OSError_msg_varg(translate("Failed to add service, err 0x%04x"), err_code);
+ }
+
+ // Once the service has been registered, its characteristics can be added.
+ common_hal_bleio_service_add_all_characteristics(service);
+ }
+}
+
+
+bool common_hal_bleio_local_peripheral_get_connected(bleio_local_peripheral_obj_t *self) {
+ return self->conn_handle != BLE_CONN_HANDLE_INVALID;
+}
+
+void common_hal_bleio_local_peripheral_start_advertising(bleio_local_peripheral_obj_t *self, bool connectable, mp_buffer_info_t *raw_data) {
+ if (connectable) {
+ ble_drv_add_event_handler(on_ble_evt, self);
+ }
+
+ const uint32_t err_code = set_advertisement_data(self, connectable, raw_data);
+ if (err_code != NRF_SUCCESS) {
+ mp_raise_OSError_msg_varg(translate("Failed to start advertising, err 0x%04x"), err_code);
+ }
+}
+
+void common_hal_bleio_local_peripheral_stop_advertising(bleio_local_peripheral_obj_t *self) {
+
+ if (m_adv_handle == BLE_GAP_ADV_SET_HANDLE_NOT_SET)
+ return;
+
+ const uint32_t err_code = sd_ble_gap_adv_stop(m_adv_handle);
+
+ if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE)) {
+ mp_raise_OSError_msg_varg(translate("Failed to stop advertising, err 0x%04x"), err_code);
+ }
+}
diff --git a/ports/nrf/common-hal/bleio/LocalPeripheral.h b/ports/nrf/common-hal/bleio/LocalPeripheral.h
new file mode 100644
index 000000000..8d84867ae
--- /dev/null
+++ b/ports/nrf/common-hal/bleio/LocalPeripheral.h
@@ -0,0 +1,64 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Artur Pacholec
+ * Copyright (c) 2018 Dan Halbert for Adafruit Industries
+ *
+ * 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 MICROPY_INCLUDED_COMMON_HAL_BLEIO_LOCALPERIPHERAL_H
+#define MICROPY_INCLUDED_COMMON_HAL_BLEIO_LOCALPERIPHERAL_H
+
+#include <stdbool.h>
+
+#include "ble.h"
+
+#include "shared-module/bleio/__init__.h"
+#include "shared-module/bleio/Address.h"
+
+// typedef struct {
+// mp_obj_base_t base;
+// bool is_peripheral;
+// mp_obj_t name;
+// bleio_address_obj_t address;
+// volatile uint16_t conn_handle;
+// mp_obj_t service_list;
+// mp_obj_t notif_handler;
+// mp_obj_t conn_handler;
+// } bleio_device_obj_t;
+
+typedef struct {
+ mp_obj_base_t base;
+ mp_obj_t name;
+ gatt_role_t gatt_role;
+ volatile uint16_t conn_handle;
+ mp_obj_t service_list;
+ mp_obj_t notif_handler;
+ mp_obj_t conn_handler;
+ // The advertising data buffer is held by us, not by the SD, so we must
+ // maintain it and not change it. If we need to change its contents during advertising,
+ // there are tricks to get the SD to notice (see DevZone - TBS).
+ uint8_t adv_data[BLE_GAP_ADV_SET_DATA_SIZE_MAX];
+
+} bleio_local_peripheral_obj_t;
+
+#endif // MICROPY_INCLUDED_COMMON_HAL_BLEIO_LOCALPERIPHERAL_H
diff --git a/ports/nrf/common-hal/bleio/__init__.c b/ports/nrf/common-hal/bleio/__init__.c
index f795e889b..5a8a7ff16 100644
--- a/ports/nrf/common-hal/bleio/__init__.c
+++ b/ports/nrf/common-hal/bleio/__init__.c
@@ -30,6 +30,13 @@
#include "shared-bindings/bleio/LocalPeripheral.h"
#include "common-hal/bleio/__init__.h"
+// Turn off BLE on a reset or reload.
+void bleio_reset() {
+ if (common_hal_bleio_adapter_get_enabled()) {
+ common_hal_bleio_adapter_set_enabled(false);
+ }
+}
+
// The singleton bleio.Adapter object, bound to bleio.adapter
// It currently only has properties and no state
const super_adapter_obj_t common_hal_bleio_adapter_obj = {
diff --git a/ports/nrf/common-hal/bleio/__init__.h b/ports/nrf/common-hal/bleio/__init__.h
new file mode 100644
index 000000000..9e044f37c
--- /dev/null
+++ b/ports/nrf/common-hal/bleio/__init__.h
@@ -0,0 +1,42 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Dan Halbert for Adafruit Industries
+ *
+ * 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 MICROPY_INCLUDED_COMMON_HAL_BLEIO_INIT_H
+#define MICROPY_INCLUDED_COMMON_HAL_BLEIO_INIT_H
+
+#include "shared-bindings/bleio/__init__.h"
+#include "shared-bindings/bleio/Adapter.h"
+
+#include "shared-module/bleio/__init__.h"
+
+// We assume variable length data.
+// 20 bytes max (23 - 3).
+#define GATT_MAX_DATA_LENGTH (BLE_GATT_ATT_MTU_DEFAULT - 3)
+
+gatt_role_t common_hal_bleio_device_get_gatt_role(mp_obj_t device);
+uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device);
+
+#endif // MICROPY_INCLUDED_COMMON_HAL_BLEIO_INIT_H
diff --git a/ports/nrf/supervisor/port.c b/ports/nrf/supervisor/port.c
index ece5bae29..cf0434793 100644
--- a/ports/nrf/supervisor/port.c
+++ b/ports/nrf/supervisor/port.c
@@ -38,6 +38,7 @@
#include "shared-module/gamepad/__init__.h"
#include "common-hal/microcontroller/Pin.h"
+#include "common-hal/bleio/__init__.h"
#include "common-hal/busio/I2C.h"
#include "common-hal/busio/SPI.h"
#include "common-hal/pulseio/PWMOut.h"
@@ -86,6 +87,8 @@ void reset_port(void) {
pulseout_reset();
timers_reset();
+ bleio_reset();
+
reset_all_pins();
}
diff --git a/shared-bindings/bleio/Broadcaster.c b/shared-bindings/bleio/Broadcaster.c
new file mode 100644
index 000000000..e24d157ec
--- /dev/null
+++ b/shared-bindings/bleio/Broadcaster.c
@@ -0,0 +1,140 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Dan Halbert for Adafruit Industries
+ *
+ * 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 "ble_drv.h"
+#include "py/runtime.h"
+
+#include "shared-bindings/bleio/Broadcaster.h"
+
+//| .. currentmodule:: bleio
+//|
+//| :class:`Broadcaster` -- A GAP Broadcaster
+//| =========================================================
+//|
+//| Implement a BLE broadcaster which sends data in advertising packets and does not connect.
+//| Used for beacons and other one-way data transmission.
+//|
+//| Usage::
+//|
+//| import bleio
+//| import time
+//|
+//|
+//| # Broadcast once a second.
+//| broadcaster = bleio.Broadcaster(interval=1)
+//| i = 0
+//| data = bytearray(1)
+//| # Broadcast a byte of data that's incremented once a minute
+//| while True:
+//| data[0] = i
+//| bytearray
+//| broadcaster.start_advertising(data)
+//| time.sleep(60)
+//| i += 1
+//|
+//| .. class:: Broadcaster(interval=1)
+//|
+//| Create a new Broadcaster object.
+
+//| :param float interval: how often to broadcast
+//|
+
+STATIC mp_obj_t bleio_broadcaster_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) {
+ mp_arg_check_num(n_args, n_kw, 0, 1, true);
+ bleio_broadcaster_obj_t *self = m_new_obj(bleio_broadcaster_obj_t);
+ self->base.type = &bleio_broadcaster_type;
+
+ mp_map_t kw_args;
+ mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args);
+
+ enum { ARG_interval };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_interval, MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(1)} },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ mp_float_t interval = mp_obj_get_float(args[ARG_interval].u_obj);
+
+ // Do port-specific initialization. interval will be validated.
+ common_hal_bleio_broadcaster_construct(self, interval);
+
+ return MP_OBJ_FROM_PTR(self);
+}
+
+//| .. method:: start_advertising(data)
+//|
+//| Start advertising the given manufacturer-specific data.
+//|
+//| :param buf data: Send data bytes in advertising packets, labeled as manufacturer-specific data
+//|
+STATIC mp_obj_t bleio_broadcaster_start_advertising(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ bleio_broadcaster_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
+
+ enum { ARG_data };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(args[ARG_data].u_obj, &bufinfo, MP_BUFFER_READ);
+
+ common_hal_bleio_broadcaster_start_advertising(self, &bufinfo);
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_broadcaster_start_advertising_obj, 0, bleio_broadcaster_start_advertising);
+
+//| .. method:: stop_advertising()
+//|
+//| Stop sending advertising packets.
+STATIC mp_obj_t bleio_broadcaster_stop_advertising(mp_obj_t self_in) {
+ bleio_broadcaster_obj_t *self = MP_OBJ_TO_PTR(self_in);
+
+ common_hal_bleio_broadcaster_stop_advertising(self);
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_broadcaster_stop_advertising_obj, bleio_broadcaster_stop_advertising);
+
+STATIC const mp_rom_map_elem_t bleio_broadcaster_locals_dict_table[] = {
+ // Methods
+ { MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_broadcaster_start_advertising_obj) },
+ { MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_broadcaster_stop_advertising_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(bleio_broadcaster_locals_dict, bleio_broadcaster_locals_dict_table);
+
+const mp_obj_type_t bleio_broadcaster_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_LocalPeripheral,
+ .make_new = bleio_broadcaster_make_new,
+ .locals_dict = (mp_obj_dict_t*)&bleio_broadcaster_locals_dict
+};
diff --git a/shared-bindings/bleio/Broadcaster.h b/shared-bindings/bleio/Broadcaster.h
new file mode 100644
index 000000000..8aa125af9
--- /dev/null
+++ b/shared-bindings/bleio/Broadcaster.h
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * 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 MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_BROADCASTER_H
+#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_BROADCASTER_H
+
+#include "common-hal/bleio/Broadcaster.h"
+
+extern const mp_obj_type_t bleio_broadcaster_type;
+
+extern void common_hal_bleio_broadcaster_construct(bleio_broadcaster_obj_t *self, mp_float_t interval);
+extern void common_hal_bleio_broadcaster_start_advertising(bleio_broadcaster_obj_t *self, mp_buffer_info_t *data);
+extern void common_hal_bleio_broadcaster_stop_advertising(bleio_broadcaster_obj_t *self);
+
+#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_BROADCASTER_H
diff --git a/shared-bindings/bleio/LocalPeripheral.c b/shared-bindings/bleio/LocalPeripheral.c
new file mode 100644
index 000000000..a40e0c043
--- /dev/null
+++ b/shared-bindings/bleio/LocalPeripheral.c
@@ -0,0 +1,250 @@
+/*
+ * 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 <string.h>
+#include <stdio.h>
+
+#include "ble_drv.h"
+#include "py/objarray.h"
+#include "py/objproperty.h"
+#include "py/objstr.h"
+#include "py/runtime.h"
+
+#include "shared-bindings/bleio/Adapter.h"
+#include "shared-bindings/bleio/AddressType.h"
+#include "shared-bindings/bleio/Characteristic.h"
+#include "shared-bindings/bleio/LocalPeripheral.h"
+#include "shared-bindings/bleio/Service.h"
+#include "shared-bindings/bleio/UUID.h"
+#include "shared-module/bleio/AdvertisementData.h"
+#include "shared-module/bleio/ScanEntry.h"
+
+#include "common-hal/bleio/LocalPeripheral.h"
+
+// TODO: Add unique MAC address part to name
+static const char default_name[] = "CIRCUITPY";
+
+//| .. currentmodule:: bleio
+//|
+//| :class:`LocalPeripheral` -- A BLE peripheral device
+//| =========================================================
+//|
+//| Implement a BLE peripheral which runs locally.
+//| Set up using the supplied services, and then allow advertising to be started and stopped.
+//|
+//| Usage::
+//|
+//| import bleio
+//|
+//| # Create a Characteristic.
+//| chara = bleio.Characteristic(bleio.UUID(0x2919), read=True, notify=True)
+//|
+//| # Create a Service providing that one Characteristic.
+//| serv = bleio.Service(bleio.UUID(0x180f), [chara])
+//|
+//| # Create a peripheral and start it up.
+//| periph = bleio.LocalPeripheral([service])
+//| periph.start_advertising()
+//|
+//| while not periph.connected():
+//| # Wait for connection.
+//| pass
+//|
+//| .. class:: LocalPeripheral(services, *, name='CIRCUITPY')
+//|
+//| Create a new LocalPeripheral object.
+
+//| :param iterable services: the Service objects representing services available from this peripheral.
+//| :param str name: The name used when advertising this peripheral
+//|
+
+STATIC mp_obj_t bleio_local_peripheral_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) {
+ mp_arg_check_num(n_args, n_kw, 0, 1, true);
+ bleio_local_peripheral_obj_t *self = m_new_obj(bleio_local_peripheral_obj_t);
+ self->base.type = &bleio_local_peripheral_type;
+ self->service_list = mp_obj_new_list(0, NULL);
+ self->notif_handler = mp_const_none;
+
+ mp_map_t kw_args;
+ mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args);
+
+ enum { ARG_services, ARG_name };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_services, MP_ARG_OBJ, {.u_obj = mp_const_none} },
+ { MP_QSTR_name, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ // If services is not an iterable, an exception will be thrown.
+ mp_obj_iter_buf_t iter_buf;
+ mp_obj_t iterable = mp_getiter(args[ARG_services].u_obj, &iter_buf);
+ mp_obj_t service;
+
+ while ((service = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
+ if (!MP_OBJ_IS_TYPE(service, &bleio_service_type)) {
+ mp_raise_ValueError(translate("services includes an object that is not a Service"));
+ }
+ bleio_service_obj_t *service_ptr = MP_OBJ_TO_PTR(service);
+ service_ptr->device = MP_OBJ_FROM_PTR(self);
+ mp_obj_list_append(self->service_list, service);
+ }
+
+ const mp_obj_t name = args[ARG_name].u_obj;
+ if (name == mp_const_none) {
+ self->name = mp_obj_new_str(default_name, strlen(default_name));
+ } else if (MP_OBJ_IS_STR(name)) {
+ self->name = name;
+ } else {
+ mp_raise_ValueError(translate("name must be a string"));
+ }
+
+ // Do port-specific initialization.
+ common_hal_bleio_local_peripheral_construct(self);
+
+ return MP_OBJ_FROM_PTR(self);
+}
+
+//| .. attribute:: connected
+//|
+//| True if connected to a BLE Central device.
+//|
+STATIC mp_obj_t bleio_local_peripheral_get_connected(mp_obj_t self_in) {
+ bleio_local_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in);
+
+ // Return list as a tuple so user won't be able to change it.
+ return mp_obj_new_bool(common_hal_bleio_local_peripheral_get_connected(self));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_local_peripheral_get_connected_obj, bleio_local_peripheral_get_connected);
+
+const mp_obj_property_t bleio_local_peripheral_connected_obj = {
+ .base.type = &mp_type_property,
+ .proxy = { (mp_obj_t)&bleio_local_peripheral_get_connected_obj,
+ (mp_obj_t)&mp_const_none_obj,
+ (mp_obj_t)&mp_const_none_obj },
+};
+
+//| .. attribute:: services
+//|
+//| A `tuple` of `bleio.Service` that are offered by this peripheral. (read-only)
+//|
+STATIC mp_obj_t bleio_local_peripheral_get_services(mp_obj_t self_in) {
+ bleio_local_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ // Return list as a tuple so user won't be able to change it.
+ mp_obj_list_t *service_list = MP_OBJ_TO_PTR(self->service_list);
+ return mp_obj_new_tuple(service_list->len, service_list->items);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_local_peripheral_get_services_obj, bleio_local_peripheral_get_services);
+
+const mp_obj_property_t bleio_local_peripheral_services_obj = {
+ .base.type = &mp_type_property,
+ .proxy = { (mp_obj_t)&bleio_local_peripheral_get_services_obj,
+ (mp_obj_t)&mp_const_none_obj,
+ (mp_obj_t)&mp_const_none_obj },
+};
+
+//| .. attribute:: name
+//|
+//| The peripheral's name, included when advertising. (read-only)
+//|
+STATIC mp_obj_t bleio_local_peripheral_get_name(mp_obj_t self_in) {
+ bleio_local_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in);
+
+ return self->name;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(bleio_local_peripheral_get_name_obj, bleio_local_peripheral_get_name);
+
+const mp_obj_property_t bleio_local_peripheral_name_obj = {
+ .base.type = &mp_type_property,
+ .proxy = { (mp_obj_t)&bleio_local_peripheral_get_name_obj,
+ (mp_obj_t)&mp_const_none_obj,
+ (mp_obj_t)&mp_const_none_obj },
+};
+
+//| .. method:: start_advertising(*, connectable=True, data=None)
+//|
+//| Starts advertising the peripheral. The peripheral's name and
+//| services are included in the advertisement packets.
+//|
+//| :param bool connectable: If `True` then other devices are allowed to connect to this peripheral.
+//| :param buf data: If not None, then send data bytes in advertising packets.
+//|
+STATIC mp_obj_t bleio_local_peripheral_start_advertising(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ bleio_local_peripheral_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
+
+ enum { ARG_connectable, ARG_data };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_connectable, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
+ { MP_QSTR_data, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ mp_buffer_info_t bufinfo = { 0 };
+ if (args[ARG_data].u_obj != mp_const_none) {
+ mp_get_buffer_raise(args[ARG_data].u_obj, &bufinfo, MP_BUFFER_READ);
+ }
+
+ common_hal_bleio_local_peripheral_start_advertising(self, args[ARG_connectable].u_bool, &bufinfo);
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_local_peripheral_start_advertising_obj, 0, bleio_local_peripheral_start_advertising);
+
+//| .. method:: stop_advertising()
+//|
+//| Stop sending advertising packets.
+STATIC mp_obj_t bleio_local_peripheral_stop_advertising(mp_obj_t self_in) {
+ bleio_local_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in);
+
+ common_hal_bleio_local_peripheral_stop_advertising(self);
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_local_peripheral_stop_advertising_obj, bleio_local_peripheral_stop_advertising);
+
+STATIC const mp_rom_map_elem_t bleio_local_peripheral_locals_dict_table[] = {
+ // Methods
+ { MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_local_peripheral_start_advertising_obj) },
+ { MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_local_peripheral_stop_advertising_obj) },
+
+ // Properties
+ { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_local_peripheral_connected_obj) },
+ { MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&bleio_local_peripheral_name_obj) },
+ { MP_ROM_QSTR(MP_QSTR_services), MP_ROM_PTR(&bleio_local_peripheral_services_obj) },
+};
+
+STATIC MP_DEFINE_CONST_DICT(bleio_local_peripheral_locals_dict, bleio_local_peripheral_locals_dict_table);
+
+const mp_obj_type_t bleio_local_peripheral_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_LocalPeripheral,
+ .make_new = bleio_local_peripheral_make_new,
+ .locals_dict = (mp_obj_dict_t*)&bleio_local_peripheral_locals_dict
+};
diff --git a/shared-bindings/bleio/LocalPeripheral.h b/shared-bindings/bleio/LocalPeripheral.h
new file mode 100644
index 000000000..bd6d5cbbb
--- /dev/null
+++ b/shared-bindings/bleio/LocalPeripheral.h
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * 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 MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_LOCALPERIPHERAL_H
+#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_LOCALPERIPHERAL_H
+
+#include "common-hal/bleio/LocalPeripheral.h"
+
+extern const mp_obj_type_t bleio_local_peripheral_type;
+
+extern void common_hal_bleio_local_peripheral_construct(bleio_local_peripheral_obj_t *self);
+extern bool common_hal_bleio_local_peripheral_get_connected(bleio_local_peripheral_obj_t *self);
+extern void common_hal_bleio_local_peripheral_start_advertising(bleio_local_peripheral_obj_t *device, bool connectable, mp_buffer_info_t *raw_data);
+extern void common_hal_bleio_local_peripheral_stop_advertising(bleio_local_peripheral_obj_t *device);
+
+#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_LOCALPERIPHERAL_H
diff --git a/shared-bindings/bleio/__init__.c b/shared-bindings/bleio/__init__.c
index 21242e07c..7e36eaa46 100644
--- a/shared-bindings/bleio/__init__.c
+++ b/shared-bindings/bleio/__init__.c
@@ -29,6 +29,7 @@
#include "shared-bindings/bleio/Address.h"
#include "shared-bindings/bleio/AddressType.h"
#include "shared-bindings/bleio/AdvertisementData.h"
+#include "shared-bindings/bleio/Broadcaster.h"
#include "shared-bindings/bleio/Characteristic.h"
#include "shared-bindings/bleio/Descriptor.h"
#include "shared-bindings/bleio/LocalPeripheral.h"
@@ -74,6 +75,7 @@ STATIC const mp_rom_map_elem_t bleio_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bleio) },
{ MP_ROM_QSTR(MP_QSTR_Address), MP_ROM_PTR(&bleio_address_type) },
{ MP_ROM_QSTR(MP_QSTR_AdvertisementData), MP_ROM_PTR(&bleio_advertisementdata_type) },
+ { MP_ROM_QSTR(MP_QSTR_Broadcaster), MP_ROM_PTR(&bleio_broadcaster_type) },
{ MP_ROM_QSTR(MP_QSTR_Characteristic), MP_ROM_PTR(&bleio_characteristic_type) },
{ MP_ROM_QSTR(MP_QSTR_Descriptor), MP_ROM_PTR(&bleio_descriptor_type) },
{ MP_ROM_QSTR(MP_QSTR_LocalPeripheral), MP_ROM_PTR(&bleio_local_peripheral_type) },
diff --git a/shared-module/bleio/__init__.h b/shared-module/bleio/__init__.h
new file mode 100644
index 000000000..07d148594
--- /dev/null
+++ b/shared-module/bleio/__init__.h
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Dan Halbert for Adafruit Industries
+ *
+ * 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 MICROPY_INCLUDED_SHARED_MODULE_BLEIO_INIT_H
+#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_INIT_H
+
+typedef enum {
+ GATT_ROLE_NONE,
+ GATT_ROLE_SERVER,
+ GATT_ROLE_CLIENT,
+} gatt_role_t;
+
+extern void bleio_reset(void);
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_INIT_H