diff options
| author | Dan Halbert <halbert@halwitz.org> | 2018-12-28 22:55:29 -0500 |
|---|---|---|
| committer | Dan Halbert <halbert@halwitz.org> | 2018-12-28 23:34:23 -0500 |
| commit | 4d1f0ec07be5e9cacaf0072a0f987871b8505ec4 (patch) | |
| tree | 2cb328eb77d8c9732ecd24580b33869fa8a3c505 | |
| parent | 4167bf5b241eec4e5a04c36d7f7c66d43a8bb921 (diff) | |
Add Broadcaster. Reset correctly on reload.
| -rwxr-xr-x | ports/nrf/Makefile | 1 | ||||
| -rw-r--r-- | ports/nrf/bluetooth/ble_drv.c | 7 | ||||
| -rw-r--r-- | ports/nrf/bluetooth/ble_drv.h | 3 | ||||
| -rw-r--r-- | ports/nrf/common-hal/bleio/Adapter.c | 20 | ||||
| -rw-r--r-- | ports/nrf/common-hal/bleio/Broadcaster.c | 113 | ||||
| -rw-r--r-- | ports/nrf/common-hal/bleio/Broadcaster.h | 47 | ||||
| -rw-r--r-- | ports/nrf/common-hal/bleio/LocalPeripheral.c | 329 | ||||
| -rw-r--r-- | ports/nrf/common-hal/bleio/LocalPeripheral.h | 64 | ||||
| -rw-r--r-- | ports/nrf/common-hal/bleio/__init__.c | 7 | ||||
| -rw-r--r-- | ports/nrf/common-hal/bleio/__init__.h | 42 | ||||
| -rw-r--r-- | ports/nrf/supervisor/port.c | 3 | ||||
| -rw-r--r-- | shared-bindings/bleio/Broadcaster.c | 140 | ||||
| -rw-r--r-- | shared-bindings/bleio/Broadcaster.h | 38 | ||||
| -rw-r--r-- | shared-bindings/bleio/LocalPeripheral.c | 250 | ||||
| -rw-r--r-- | shared-bindings/bleio/LocalPeripheral.h | 39 | ||||
| -rw-r--r-- | shared-bindings/bleio/__init__.c | 2 | ||||
| -rw-r--r-- | shared-module/bleio/__init__.h | 38 |
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 |
