Refine _bleio

This PR refines the _bleio API. It was originally motivated by
the addition of a new CircuitPython service that enables reading
and modifying files on the device. Moving the BLE lifecycle outside
of the VM motivated a number of changes to remove heap allocations
in some APIs.

It also motivated unifying connection initiation to the Adapter class
rather than the Central and Peripheral classes which have been removed.
Adapter now handles the GAP portion of BLE including advertising, which
has moved but is largely unchanged, and scanning, which has been enhanced
to return an iterator of filtered results.

Once a connection is created (either by us (aka Central) or a remote
device (aka Peripheral)) it is represented by a new Connection class.
This class knows the current connection state and can discover and
instantiate remote Services along with their Characteristics and
Descriptors.

Relates to #586
This commit is contained in:
Scott Shawcroft 2019-09-14 12:40:24 -07:00
parent 84c0d6cdf8
commit ae30a1e5aa
No known key found for this signature in database
GPG Key ID: 9349BC7E64B1921E
60 changed files with 2887 additions and 2149 deletions

View File

@ -49,7 +49,7 @@ void mp_keyboard_interrupt(void) {
// Check to see if we've been CTRL-C'ed by autoreload or the user.
bool mp_hal_is_interrupted(void) {
return MP_STATE_VM(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception));
return MP_STATE_VM(mp_pending_exception) != NULL;
}
#endif

13
main.c
View File

@ -69,6 +69,11 @@
#include "shared-module/board/__init__.h"
#endif
#if CIRCUITPY_BLEIO
#include "shared-bindings/_bleio/__init__.h"
#include "supervisor/shared/bluetooth.h"
#endif
void do_str(const char *src, mp_parse_input_kind_t input_kind) {
mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0);
if (lex == NULL) {
@ -439,6 +444,10 @@ int __attribute__((used)) main(void) {
// Start serial and HID after giving boot.py a chance to tweak behavior.
serial_init();
#if CIRCUITPY_BLEIO
supervisor_start_bluetooth();
#endif
// Boot script is finished, so now go into REPL/main mode.
int exit_code = PYEXEC_FORCED_EXIT;
bool skip_repl = true;
@ -475,6 +484,10 @@ void gc_collect(void) {
displayio_gc_collect();
#endif
#if CIRCUITPY_BLEIO
common_hal_bleio_gc_collect();
#endif
// This naively collects all object references from an approximate stack
// range.
gc_collect_root((void**)sp, ((uint32_t)port_stack_get_top() - sp) / sizeof(uint32_t));

View File

@ -41,6 +41,10 @@
#include "common-hal/audiopwmio/PWMAudioOut.h"
#endif
#if CIRCUITPY_BLEIO
#include "supervisor/shared/bluetooth.h"
#endif
static bool running_background_tasks = false;
void background_tasks_reset(void) {
@ -62,6 +66,9 @@ void run_background_tasks(void) {
i2s_background();
#endif
#if CIRCUITPY_BLEIO
supervisor_bluetooth_background();
#endif
#if CIRCUITPY_DISPLAYIO
displayio_background();

View File

@ -38,6 +38,8 @@
#include "py/misc.h"
#include "py/mpstate.h"
#include "supervisor/shared/bluetooth.h"
nrf_nvic_state_t nrf_nvic_state = { 0 };
// Flag indicating progress of internal flash operation.
@ -52,6 +54,14 @@ void ble_drv_reset() {
sd_flash_operation_status = SD_FLASH_OPERATION_DONE;
}
void ble_drv_add_event_handler_entry(ble_drv_evt_handler_entry_t* entry, ble_drv_evt_handler_t func, void *param) {
entry->next = MP_STATE_VM(ble_drv_evt_handler_entries);
entry->param = param;
entry->func = func;
MP_STATE_VM(ble_drv_evt_handler_entries) = entry;
}
void ble_drv_add_event_handler(ble_drv_evt_handler_t func, void *param) {
ble_drv_evt_handler_entry_t *it = MP_STATE_VM(ble_drv_evt_handler_entries);
while (it != NULL) {
@ -64,11 +74,7 @@ void ble_drv_add_event_handler(ble_drv_evt_handler_t func, void *param) {
// Add a new handler to the front of the list
ble_drv_evt_handler_entry_t *handler = m_new_ll(ble_drv_evt_handler_entry_t, 1);
handler->next = MP_STATE_VM(ble_drv_evt_handler_entries);
handler->param = param;
handler->func = func;
MP_STATE_VM(ble_drv_evt_handler_entries) = handler;
ble_drv_add_event_handler_entry(handler, func, param);
}
void ble_drv_remove_event_handler(ble_drv_evt_handler_t func, void *param) {
@ -127,10 +133,20 @@ void SD_EVT_IRQHandler(void) {
break;
}
ble_evt_t* event = (ble_evt_t *)m_ble_evt_buf;
if (supervisor_bluetooth_hook(event)) {
continue;
}
ble_drv_evt_handler_entry_t *it = MP_STATE_VM(ble_drv_evt_handler_entries);
bool done = false;
while (it != NULL) {
it->func((ble_evt_t *)m_ble_evt_buf, it->param);
done = it->func(event, it->param) || done;
it = it->next;
}
if (!done) {
//mp_printf(&mp_plat_print, "Unhandled ble event: 0x%04x\n", event->header.evt_id);
}
}
}

View File

@ -29,6 +29,8 @@
#ifndef MICROPY_INCLUDED_NRF_BLUETOOTH_BLE_DRV_H
#define MICROPY_INCLUDED_NRF_BLUETOOTH_BLE_DRV_H
#include <stdbool.h>
#include "ble.h"
#define MAX_TX_IN_PROGRESS 10
@ -48,7 +50,7 @@
#define UNIT_1_25_MS (1250)
#define UNIT_10_MS (10000)
typedef void (*ble_drv_evt_handler_t)(ble_evt_t*, void*);
typedef bool (*ble_drv_evt_handler_t)(ble_evt_t*, void*);
typedef enum {
SD_FLASH_OPERATION_DONE,
@ -69,4 +71,7 @@ void ble_drv_reset(void);
void ble_drv_add_event_handler(ble_drv_evt_handler_t func, void *param);
void ble_drv_remove_event_handler(ble_drv_evt_handler_t func, void *param);
// Allow for user provided entries to prevent allocations outside the VM.
void ble_drv_add_event_handler_entry(ble_drv_evt_handler_entry_t* entry, ble_drv_evt_handler_t func, void *param);
#endif // MICROPY_INCLUDED_NRF_BLUETOOTH_BLE_DRV_H

View File

@ -16,6 +16,7 @@
0x00000000..0x00000FFF (4KB) Master Boot Record
*/
/* Specify the memory areas (S140 6.x.x) */
MEMORY
{
@ -26,7 +27,10 @@ MEMORY
FLASH_FATFS (r) : ORIGIN = 0x000AD000, LENGTH = 0x040000
/* 0x2000000 - RAM:ORIGIN is reserved for Softdevice */
RAM (xrw) : ORIGIN = 0x20004000, LENGTH = 0x20040000 - 0x20004000
/* SoftDevice 6.1.0 takes 0x1628 bytes (5.54 kb) minimum. */
/* To measure the minimum required amount of memory for given configuration, set this number
high enough to work and then check the mutation of the value done by sd_ble_enable. */
RAM (xrw) : ORIGIN = 0x20000000 + 16K, LENGTH = 256K - 16K
}
/* produce a link error if there is not this amount of RAM for these sections */
@ -38,7 +42,8 @@ _minimum_heap_size = 0;
/*_stack_end = ORIGIN(RAM) + LENGTH(RAM);*/
_estack = ORIGIN(RAM) + LENGTH(RAM);
/* RAM extents for the garbage collector */
/* RAM extents for the garbage collector and soft device init */
_ram_start = ORIGIN(RAM);
_ram_end = ORIGIN(RAM) + LENGTH(RAM);
_heap_end = 0x20020000; /* tunable */

View File

@ -26,6 +26,7 @@
* THE SOFTWARE.
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
@ -34,11 +35,18 @@
#include "nrfx_power.h"
#include "nrf_nvic.h"
#include "nrf_sdm.h"
#include "tick.h"
#include "py/gc.h"
#include "py/objstr.h"
#include "py/runtime.h"
#include "supervisor/shared/safe_mode.h"
#include "supervisor/usb.h"
#include "shared-bindings/_bleio/__init__.h"
#include "shared-bindings/_bleio/Adapter.h"
#include "shared-bindings/_bleio/Address.h"
#include "shared-bindings/_bleio/Connection.h"
#include "shared-bindings/_bleio/ScanEntry.h"
#include "shared-bindings/time/__init__.h"
#define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS)
#define BLE_MAX_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS)
@ -46,10 +54,13 @@
#define BLE_CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS)
STATIC void softdevice_assert_handler(uint32_t id, uint32_t pc, uint32_t info) {
mp_raise_msg_varg(&mp_type_AssertionError,
translate("Soft device assert, id: 0x%08lX, pc: 0x%08lX"), id, pc);
reset_into_safe_mode(NORDIC_SOFT_DEVICE_ASSERT);
}
bleio_connection_internal_t connections[BLEIO_TOTAL_CONNECTION_COUNT];
// Linker script provided ram start.
extern uint32_t _ram_start;
STATIC uint32_t ble_stack_enable(void) {
nrf_clock_lf_cfg_t clock_config = {
#if BOARD_HAS_32KHZ_XTAL
@ -78,34 +89,56 @@ STATIC uint32_t ble_stack_enable(void) {
// Start with no event handlers, etc.
ble_drv_reset();
uint32_t app_ram_start;
app_ram_start = 0x20004000;
// Set everything up to have one persistent code editing connection and one user managed
// connection. In the future we could move .data and .bss to the other side of the stack and
// dynamically adjust for different memory requirements of the SD based on boot.py
// configuration.
uint32_t app_ram_start = (uint32_t) &_ram_start;
ble_cfg_t ble_conf;
ble_conf.conn_cfg.conn_cfg_tag = BLE_CONN_CFG_TAG_CUSTOM;
ble_conf.conn_cfg.params.gap_conn_cfg.conn_count = BLE_GAP_CONN_COUNT_DEFAULT;
ble_conf.conn_cfg.params.gap_conn_cfg.conn_count = BLEIO_TOTAL_CONNECTION_COUNT;
// Event length here can influence throughput so perhaps make multiple connection profiles
// available.
ble_conf.conn_cfg.params.gap_conn_cfg.event_length = BLE_GAP_EVENT_LENGTH_DEFAULT;
err_code = sd_ble_cfg_set(BLE_CONN_CFG_GAP, &ble_conf, app_ram_start);
if (err_code != NRF_SUCCESS)
if (err_code != NRF_SUCCESS) {
return err_code;
}
memset(&ble_conf, 0, sizeof(ble_conf));
ble_conf.gap_cfg.role_count_cfg.periph_role_count = 1;
ble_conf.gap_cfg.role_count_cfg.adv_set_count = 1;
ble_conf.gap_cfg.role_count_cfg.periph_role_count = 2;
ble_conf.gap_cfg.role_count_cfg.central_role_count = 1;
err_code = sd_ble_cfg_set(BLE_GAP_CFG_ROLE_COUNT, &ble_conf, app_ram_start);
if (err_code != NRF_SUCCESS)
if (err_code != NRF_SUCCESS) {
return err_code;
}
memset(&ble_conf, 0, sizeof(ble_conf));
ble_conf.conn_cfg.conn_cfg_tag = BLE_CONN_CFG_TAG_CUSTOM;
ble_conf.conn_cfg.params.gatts_conn_cfg.hvn_tx_queue_size = MAX_TX_IN_PROGRESS;
err_code = sd_ble_cfg_set(BLE_CONN_CFG_GATTS, &ble_conf, app_ram_start);
if (err_code != NRF_SUCCESS)
if (err_code != NRF_SUCCESS) {
return err_code;
}
err_code = sd_ble_enable(&app_ram_start);
if (err_code != NRF_SUCCESS)
// Double the GATT Server attribute size to accomodate both the CircuitPython built-in service
// and anything the user does.
memset(&ble_conf, 0, sizeof(ble_conf));
ble_conf.gatts_cfg.attr_tab_size.attr_tab_size = BLE_GATTS_ATTR_TAB_SIZE_DEFAULT * 3;
err_code = sd_ble_cfg_set(BLE_GATTS_CFG_ATTR_TAB_SIZE, &ble_conf, app_ram_start);
if (err_code != NRF_SUCCESS) {
return err_code;
}
// TODO set ATT_MTU so that the maximum MTU we can negotiate is higher than the default.
// This sets app_ram_start to the minimum value needed for the settings set above.
err_code = sd_ble_enable(&app_ram_start);
if (err_code != NRF_SUCCESS) {
return err_code;
}
ble_gap_conn_params_t gap_conn_params = {
.min_conn_interval = BLE_MIN_CONN_INTERVAL,
@ -122,11 +155,108 @@ STATIC uint32_t ble_stack_enable(void) {
return err_code;
}
void common_hal_bleio_adapter_set_enabled(bool enabled) {
const bool is_enabled = common_hal_bleio_adapter_get_enabled();
STATIC bool adapter_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
bleio_adapter_obj_t *self = (bleio_adapter_obj_t*)self_in;
// For debugging.
// mp_printf(&mp_plat_print, "Adapter event: 0x%04x\n", ble_evt->header.evt_id);
switch (ble_evt->header.evt_id) {
case BLE_GAP_EVT_CONNECTED: {
// Find an empty connection
bleio_connection_internal_t *connection;
for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
connection = &connections[i];
if (connection->conn_handle == BLE_CONN_HANDLE_INVALID) {
break;
}
}
// Central has connected.
ble_gap_evt_connected_t* connected = &ble_evt->evt.gap_evt.params.connected;
connection->conn_handle = ble_evt->evt.gap_evt.conn_handle;
connection->connection_obj = mp_const_none;
ble_drv_add_event_handler_entry(&connection->handler_entry, connection_on_ble_evt, connection);
self->connection_objs = NULL;
// See if connection interval set by Central is out of range.
// If so, negotiate our preferred range.
ble_gap_conn_params_t conn_params;
sd_ble_gap_ppcp_get(&conn_params);
if (conn_params.min_conn_interval < connected->conn_params.min_conn_interval ||
conn_params.min_conn_interval > connected->conn_params.max_conn_interval) {
sd_ble_gap_conn_param_update(ble_evt->evt.gap_evt.conn_handle, &conn_params);
}
self->current_advertising_data = NULL;
break;
}
case BLE_GAP_EVT_DISCONNECTED: {
// Find the connection that was disconnected.
bleio_connection_internal_t *connection;
for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
connection = &connections[i];
if (connection->conn_handle == ble_evt->evt.gap_evt.conn_handle) {
break;
}
}
ble_drv_remove_event_handler(connection_on_ble_evt, connection);
connection->conn_handle = BLE_CONN_HANDLE_INVALID;
if (connection->connection_obj != mp_const_none) {
bleio_connection_obj_t* obj = connection->connection_obj;
obj->connection = NULL;
obj->disconnect_reason = ble_evt->evt.gap_evt.params.disconnected.reason;
}
self->connection_objs = NULL;
break;
}
case BLE_GAP_EVT_ADV_SET_TERMINATED:
self->current_advertising_data = NULL;
break;
default:
// For debugging.
// mp_printf(&mp_plat_print, "Unhandled adapter event: 0x%04x\n", ble_evt->header.evt_id);
return false;
break;
}
return true;
}
STATIC void get_address(bleio_adapter_obj_t *self, ble_gap_addr_t *address) {
uint32_t err_code;
err_code = sd_ble_gap_addr_get(address);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg(translate("Failed to get local address"));
}
}
char default_ble_name[] = { 'C', 'I', 'R', 'C', 'U', 'I', 'T', 'P', 'Y', 0, 0, 0, 0 , 0};
STATIC void bleio_adapter_reset_name(bleio_adapter_obj_t *self) {
uint8_t len = sizeof(default_ble_name) - 1;
ble_gap_addr_t local_address;
get_address(self, &local_address);
default_ble_name[len - 4] = nibble_to_hex_lower[local_address.addr[1] >> 4 & 0xf];
default_ble_name[len - 3] = nibble_to_hex_lower[local_address.addr[1] & 0xf];
default_ble_name[len - 2] = nibble_to_hex_lower[local_address.addr[0] >> 4 & 0xf];
default_ble_name[len - 1] = nibble_to_hex_lower[local_address.addr[0] & 0xf];
default_ble_name[len] = '\0'; // for now we add null for compatibility with C ASCIIZ strings
common_hal_bleio_adapter_set_name(self, (char*) default_ble_name);
}
void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enabled) {
const bool is_enabled = common_hal_bleio_adapter_get_enabled(self);
// Don't enable or disable twice
if ((is_enabled && enabled) || (!is_enabled && !enabled)) {
if (is_enabled == enabled) {
return;
}
@ -137,22 +267,34 @@ void common_hal_bleio_adapter_set_enabled(bool enabled) {
nrfx_power_uninit();
err_code = ble_stack_enable();
// Re-init USB hardware
init_usb_hardware();
} else {
err_code = sd_softdevice_disable();
// Re-init USB hardware
init_usb_hardware();
}
// Re-init USB hardware
init_usb_hardware();
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg(translate("Failed to change softdevice state"));
mp_raise_OSError_msg_varg(translate("Failed to change softdevice state, NRF_ERROR_%q"), MP_OBJ_QSTR_VALUE(base_error_messages[err_code - NRF_ERROR_BASE_NUM]));
}
// Add a handler for incoming peripheral connections.
if (enabled) {
for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
bleio_connection_internal_t *connection = &connections[i];
connection->conn_handle = BLE_CONN_HANDLE_INVALID;
}
bleio_adapter_reset_name(self);
ble_drv_add_event_handler_entry(&self->handler_entry, adapter_on_ble_evt, self);
} else {
ble_drv_reset();
self->scan_results = NULL;
self->current_advertising_data = NULL;
self->advertising_data = NULL;
self->scan_response_data = NULL;
}
}
bool common_hal_bleio_adapter_get_enabled(void) {
bool common_hal_bleio_adapter_get_enabled(bleio_adapter_obj_t *self) {
uint8_t is_enabled;
const uint32_t err_code = sd_softdevice_is_enabled(&is_enabled);
@ -163,22 +305,11 @@ bool common_hal_bleio_adapter_get_enabled(void) {
return is_enabled;
}
void get_address(ble_gap_addr_t *address) {
uint32_t err_code;
common_hal_bleio_adapter_set_enabled(true);
err_code = sd_ble_gap_addr_get(address);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg(translate("Failed to get local address"));
}
}
bleio_address_obj_t *common_hal_bleio_adapter_get_address(void) {
common_hal_bleio_adapter_set_enabled(true);
bleio_address_obj_t *common_hal_bleio_adapter_get_address(bleio_adapter_obj_t *self) {
common_hal_bleio_adapter_set_enabled(self, true);
ble_gap_addr_t local_address;
get_address(&local_address);
get_address(self, &local_address);
bleio_address_obj_t *address = m_new_obj(bleio_address_obj_t);
address->base.type = &bleio_address_type;
@ -187,18 +318,326 @@ bleio_address_obj_t *common_hal_bleio_adapter_get_address(void) {
return address;
}
mp_obj_t common_hal_bleio_adapter_get_default_name(void) {
common_hal_bleio_adapter_set_enabled(true);
ble_gap_addr_t local_address;
get_address(&local_address);
char name[] = { 'C', 'I', 'R', 'C', 'U', 'I', 'T', 'P', 'Y', 0, 0, 0, 0 };
name[sizeof(name) - 4] = nibble_to_hex_lower[local_address.addr[1] >> 4 & 0xf];
name[sizeof(name) - 3] = nibble_to_hex_lower[local_address.addr[1] & 0xf];
name[sizeof(name) - 2] = nibble_to_hex_lower[local_address.addr[0] >> 4 & 0xf];
name[sizeof(name) - 1] = nibble_to_hex_lower[local_address.addr[0] & 0xf];
return mp_obj_new_str(name, sizeof(name));
mp_obj_str_t* common_hal_bleio_adapter_get_name(bleio_adapter_obj_t *self) {
uint16_t len = 0;
sd_ble_gap_device_name_get(NULL, &len);
uint8_t buf[len];
uint32_t err_code = sd_ble_gap_device_name_get(buf, &len);
if (err_code != NRF_SUCCESS) {
return NULL;
}
return mp_obj_new_str((char*) buf, len);
}
void common_hal_bleio_adapter_set_name(bleio_adapter_obj_t *self, const char* name) {
ble_gap_conn_sec_mode_t sec;
sec.lv = 0;
sec.sm = 0;
sd_ble_gap_device_name_set(&sec, (const uint8_t*) name, strlen(name));
}
STATIC bool scan_on_ble_evt(ble_evt_t *ble_evt, void *scan_results_in) {
bleio_scanresults_obj_t *scan_results = (bleio_scanresults_obj_t*)scan_results_in;
if (ble_evt->header.evt_id == BLE_GAP_EVT_TIMEOUT &&
ble_evt->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_SCAN) {
shared_module_bleio_scanresults_set_done(scan_results, true);
ble_drv_remove_event_handler(scan_on_ble_evt, scan_results);
return true;
}
if (ble_evt->header.evt_id != BLE_GAP_EVT_ADV_REPORT) {
return false;
}
ble_gap_evt_adv_report_t *report = &ble_evt->evt.gap_evt.params.adv_report;
shared_module_bleio_scanresults_append(scan_results,
ticks_ms,
report->type.connectable,
report->type.scan_response,
report->rssi,
report->peer_addr.addr,
report->peer_addr.addr_type,
report->data.p_data,
report->data.len);
const uint32_t err_code = sd_ble_gap_scan_start(NULL, scan_results->common_hal_data);
if (err_code != NRF_SUCCESS) {
// TODO: Pass the error into the scan results so it can throw an exception.
scan_results->done = true;
}
return true;
}
mp_obj_t common_hal_bleio_adapter_start_scan(bleio_adapter_obj_t *self, uint8_t* prefixes, uint8_t prefix_length, bool extended, mp_int_t buffer_size, mp_float_t timeout, mp_float_t interval, mp_float_t window, mp_int_t minimum_rssi, bool active) {
if (self->scan_results != NULL) {
if (!shared_module_bleio_scanresults_get_done(self->scan_results)) {
mp_raise_RuntimeError(translate("Scan already in progess. Stop with stop_scan."));
}
self->scan_results = NULL;
}
self->scan_results = shared_module_bleio_new_scanresults(buffer_size, prefixes, prefix_length, minimum_rssi);
size_t max_packet_size = extended ? BLE_GAP_SCAN_BUFFER_EXTENDED_MAX_SUPPORTED : BLE_GAP_SCAN_BUFFER_MAX;
uint8_t *raw_data = m_malloc(sizeof(ble_data_t) + max_packet_size, false);
ble_data_t * sd_data = (ble_data_t *) raw_data;
self->scan_results->common_hal_data = sd_data;
sd_data->len = max_packet_size;
sd_data->p_data = raw_data + sizeof(ble_data_t);
ble_drv_add_event_handler(scan_on_ble_evt, self->scan_results);
uint32_t nrf_timeout = SEC_TO_UNITS(timeout, UNIT_10_MS);
if (timeout <= 0.0001) {
nrf_timeout = BLE_GAP_SCAN_TIMEOUT_UNLIMITED;
}
ble_gap_scan_params_t scan_params = {
.extended = extended,
.interval = SEC_TO_UNITS(interval, UNIT_0_625_MS),
.timeout = nrf_timeout,
.window = SEC_TO_UNITS(window, UNIT_0_625_MS),
.scan_phys = BLE_GAP_PHY_1MBPS,
.active = active
};
uint32_t err_code;
err_code = sd_ble_gap_scan_start(&scan_params, sd_data);
if (err_code != NRF_SUCCESS) {
self->scan_results = NULL;
ble_drv_remove_event_handler(scan_on_ble_evt, self->scan_results);
mp_raise_OSError_msg_varg(translate("Failed to start scanning, err 0x%04x"), err_code);
}
return MP_OBJ_FROM_PTR(self->scan_results);
}
void common_hal_bleio_adapter_stop_scan(bleio_adapter_obj_t *self) {
sd_ble_gap_scan_stop();
shared_module_bleio_scanresults_set_done(self->scan_results, true);
ble_drv_remove_event_handler(scan_on_ble_evt, self->scan_results);
self->scan_results = NULL;
}
typedef struct {
uint16_t conn_handle;
volatile bool done;
} connect_info_t;
STATIC bool connect_on_ble_evt(ble_evt_t *ble_evt, void *info_in) {
connect_info_t *info = (connect_info_t*)info_in;
switch (ble_evt->header.evt_id) {
case BLE_GAP_EVT_CONNECTED:
info->conn_handle = ble_evt->evt.gap_evt.conn_handle;
info->done = true;
break;
case BLE_GAP_EVT_TIMEOUT:
// Handle will be invalid.
info->done = true;
break;
default:
// For debugging.
// mp_printf(&mp_plat_print, "Unhandled central event: 0x%04x\n", ble_evt->header.evt_id);
return false;
break;
}
return true;
}
mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout, bool pair) {
ble_gap_addr_t addr;
addr.addr_type = address->type;
mp_buffer_info_t address_buf_info;
mp_get_buffer_raise(address->bytes, &address_buf_info, MP_BUFFER_READ);
memcpy(addr.addr, (uint8_t *) address_buf_info.buf, NUM_BLEIO_ADDRESS_BYTES);
ble_gap_scan_params_t scan_params = {
.interval = MSEC_TO_UNITS(100, UNIT_0_625_MS),
.window = MSEC_TO_UNITS(100, UNIT_0_625_MS),
.scan_phys = BLE_GAP_PHY_1MBPS,
// timeout of 0 means no timeout
.timeout = SEC_TO_UNITS(timeout, UNIT_10_MS),
};
ble_gap_conn_params_t conn_params = {
.conn_sup_timeout = MSEC_TO_UNITS(4000, UNIT_10_MS),
.min_conn_interval = MSEC_TO_UNITS(15, UNIT_1_25_MS),
.max_conn_interval = MSEC_TO_UNITS(300, UNIT_1_25_MS),
.slave_latency = 0, // number of conn events
};
connect_info_t event_info;
ble_drv_add_event_handler(connect_on_ble_evt, &event_info);
event_info.done = false;
uint32_t err_code = sd_ble_gap_connect(&addr, &scan_params, &conn_params, BLE_CONN_CFG_TAG_CUSTOM);
if (err_code != NRF_SUCCESS) {
ble_drv_remove_event_handler(connect_on_ble_evt, &event_info);
mp_raise_OSError_msg_varg(translate("Failed to start connecting, error 0x%04x"), err_code);
}
while (!event_info.done) {
RUN_BACKGROUND_TASKS;
}
ble_drv_remove_event_handler(connect_on_ble_evt, &event_info);
if (event_info.conn_handle == BLE_CONN_HANDLE_INVALID) {
mp_raise_OSError_msg(translate("Failed to connect: timeout"));
}
// Make the connection object and return it.
for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
bleio_connection_internal_t *connection = &connections[i];
if (connection->conn_handle == event_info.conn_handle) {
return bleio_connection_new_from_internal(connection);
}
}
mp_raise_OSError_msg(translate("Failed to connect: internal error"));
return mp_const_none;
}
// The nRF SD 6.1.0 can only do one concurrent advertisement so share the advertising handle.
uint8_t adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET;
STATIC void check_data_fit(size_t data_len) {
if (data_len > BLE_GAP_ADV_SET_DATA_SIZE_MAX) {
mp_raise_ValueError(translate("Data too large for advertisement packet"));
}
}
uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool connectable, float interval, uint8_t *advertising_data, uint16_t advertising_data_len, uint8_t *scan_response_data, uint16_t scan_response_data_len) {
if (self->current_advertising_data != NULL && self->current_advertising_data == self->advertising_data) {
return NRF_ERROR_BUSY;
}
// If the current advertising data isn't owned by the adapter then it must be an internal
// advertisement that we should stop.
if (self->current_advertising_data != NULL) {
common_hal_bleio_adapter_stop_advertising(self);
}
uint32_t err_code;
ble_gap_adv_params_t adv_params = {
.interval = SEC_TO_UNITS(interval, UNIT_0_625_MS),
.properties.type = connectable ? BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED
: 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,
};
const ble_gap_adv_data_t ble_gap_adv_data = {
.adv_data.p_data = advertising_data,
.adv_data.len = advertising_data_len,
.scan_rsp_data.p_data = scan_response_data_len > 0 ? scan_response_data : NULL,
.scan_rsp_data.len = scan_response_data_len,
};
err_code = sd_ble_gap_adv_set_configure(&adv_handle, &ble_gap_adv_data, &adv_params);
if (err_code != NRF_SUCCESS) {
return err_code;
}
err_code = sd_ble_gap_adv_start(adv_handle, BLE_CONN_CFG_TAG_CUSTOM);
if (err_code != NRF_SUCCESS) {
return err_code;
}
self->current_advertising_data = advertising_data;
return NRF_SUCCESS;
}
void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool connectable, mp_float_t interval, mp_buffer_info_t *advertising_data_bufinfo, mp_buffer_info_t *scan_response_data_bufinfo) {
if (self->current_advertising_data != NULL && self->current_advertising_data == self->advertising_data) {
mp_raise_OSError_msg(translate("Already advertising."));
}
// interval value has already been validated.
uint32_t err_code;
check_data_fit(advertising_data_bufinfo->len);
check_data_fit(scan_response_data_bufinfo->len);
// The advertising data buffers must not move, because the SoftDevice depends on them.
// So make them long-lived and reuse them onwards.
if (self->advertising_data == NULL) {
self->advertising_data = (uint8_t *) gc_alloc(BLE_GAP_ADV_SET_DATA_SIZE_MAX * sizeof(uint8_t), false, true);
}
if (self->scan_response_data == NULL) {
self->scan_response_data = (uint8_t *) gc_alloc(BLE_GAP_ADV_SET_DATA_SIZE_MAX * sizeof(uint8_t), false, true);
}
memcpy(self->advertising_data, advertising_data_bufinfo->buf, advertising_data_bufinfo->len);
memcpy(self->scan_response_data, scan_response_data_bufinfo->buf, scan_response_data_bufinfo->len);
err_code = _common_hal_bleio_adapter_start_advertising(self, connectable, interval, self->advertising_data, advertising_data_bufinfo->len, self->scan_response_data, scan_response_data_bufinfo->len);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to start advertising, NRF_ERROR_%q"), MP_OBJ_QSTR_VALUE(base_error_messages[err_code - NRF_ERROR_BASE_NUM]));
}
}
void common_hal_bleio_adapter_stop_advertising(bleio_adapter_obj_t *self) {
if (adv_handle == BLE_GAP_ADV_SET_HANDLE_NOT_SET)
return;
// TODO: Don't actually stop. Switch to advertising CircuitPython if we don't already have a connection.
const uint32_t err_code = sd_ble_gap_adv_stop(adv_handle);
self->current_advertising_data = NULL;
if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE)) {
mp_raise_OSError_msg_varg(translate("Failed to stop advertising, NRF_ERROR_%q"), MP_OBJ_QSTR_VALUE(base_error_messages[err_code - NRF_ERROR_BASE_NUM]));
}
}
bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self) {
for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
bleio_connection_internal_t *connection = &connections[i];
if (connection->conn_handle != BLE_CONN_HANDLE_INVALID) {
return true;
}
}
return false;
}
mp_obj_t common_hal_bleio_adapter_get_connections(bleio_adapter_obj_t *self) {
if (self->connection_objs != NULL) {
return self->connection_objs;
}
size_t total_connected = 0;
mp_obj_t items[BLEIO_TOTAL_CONNECTION_COUNT];
for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
bleio_connection_internal_t *connection = &connections[i];
if (connection->conn_handle != BLE_CONN_HANDLE_INVALID) {
if (connection->connection_obj == mp_const_none) {
connection->connection_obj = bleio_connection_new_from_internal(connection);
}
items[total_connected] = connection->connection_obj;
total_connected++;
}
}
self->connection_objs = mp_obj_new_tuple(total_connected, items);
return self->connection_objs;
}
void bleio_adapter_gc_collect(bleio_adapter_obj_t* adapter) {
gc_collect_root((void**)adapter, sizeof(bleio_adapter_obj_t) / sizeof(size_t));
gc_collect_root((void**)connections, sizeof(connections) / sizeof(size_t));
}
void bleio_adapter_reset(bleio_adapter_obj_t* adapter) {
common_hal_bleio_adapter_stop_scan(adapter);
common_hal_bleio_adapter_stop_advertising(adapter);
adapter->connection_objs = NULL;
for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
bleio_connection_internal_t *connection = &connections[i];
connection->connection_obj = mp_const_none;
}
}

View File

@ -30,9 +30,27 @@
#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_ADAPTER_H
#include "py/obj.h"
#include "py/objtuple.h"
#include "shared-bindings/_bleio/Connection.h"
#include "shared-bindings/_bleio/ScanResults.h"
#define BLEIO_TOTAL_CONNECTION_COUNT 2
extern bleio_connection_internal_t connections[BLEIO_TOTAL_CONNECTION_COUNT];
typedef struct {
mp_obj_base_t base;
} super_adapter_obj_t;
uint8_t* advertising_data;
uint8_t* scan_response_data;
uint8_t* current_advertising_data;
bleio_scanresults_obj_t* scan_results;
mp_obj_t name;
mp_obj_tuple_t *connection_objs;
ble_drv_evt_handler_entry_t handler_entry;
} bleio_adapter_obj_t;
void bleio_adapter_gc_collect(bleio_adapter_obj_t* adapter);
void bleio_adapter_reset(bleio_adapter_obj_t* adapter);
#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_ADAPTER_H

View File

@ -1,145 +0,0 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Dan Halbert for Adafruit Industries
* 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/__init__.h"
#include "shared-bindings/_bleio/Adapter.h"
#include "shared-bindings/_bleio/Central.h"
STATIC void central_on_ble_evt(ble_evt_t *ble_evt, void *central_in) {
bleio_central_obj_t *central = (bleio_central_obj_t*)central_in;
switch (ble_evt->header.evt_id) {
case BLE_GAP_EVT_CONNECTED:
central->conn_handle = ble_evt->evt.gap_evt.conn_handle;
central->waiting_to_connect = false;
break;
case BLE_GAP_EVT_TIMEOUT:
// Handle will be invalid.
central->waiting_to_connect = false;
break;
case BLE_GAP_EVT_DISCONNECTED:
central->conn_handle = BLE_CONN_HANDLE_INVALID;
break;
case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
sd_ble_gap_sec_params_reply(central->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(central->conn_handle, &request->conn_params);
break;
}
default:
// For debugging.
// mp_printf(&mp_plat_print, "Unhandled central event: 0x%04x\n", ble_evt->header.evt_id);
break;
}
}
void common_hal_bleio_central_construct(bleio_central_obj_t *self) {
common_hal_bleio_adapter_set_enabled(true);
self->remote_service_list = mp_obj_new_list(0, NULL);
self->conn_handle = BLE_CONN_HANDLE_INVALID;
}
void common_hal_bleio_central_connect(bleio_central_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout) {
common_hal_bleio_adapter_set_enabled(true);
ble_drv_add_event_handler(central_on_ble_evt, self);
ble_gap_addr_t addr;
addr.addr_type = address->type;
mp_buffer_info_t address_buf_info;
mp_get_buffer_raise(address->bytes, &address_buf_info, MP_BUFFER_READ);
memcpy(addr.addr, (uint8_t *) address_buf_info.buf, NUM_BLEIO_ADDRESS_BYTES);
ble_gap_scan_params_t scan_params = {
.interval = MSEC_TO_UNITS(100, UNIT_0_625_MS),
.window = MSEC_TO_UNITS(100, UNIT_0_625_MS),
.scan_phys = BLE_GAP_PHY_1MBPS,
// timeout of 0 means no timeout
.timeout = SEC_TO_UNITS(timeout, UNIT_10_MS),
};
ble_gap_conn_params_t conn_params = {
.conn_sup_timeout = MSEC_TO_UNITS(4000, UNIT_10_MS),
.min_conn_interval = MSEC_TO_UNITS(15, UNIT_1_25_MS),
.max_conn_interval = MSEC_TO_UNITS(300, UNIT_1_25_MS),
.slave_latency = 0, // number of conn events
};
self->waiting_to_connect = true;
uint32_t err_code = sd_ble_gap_connect(&addr, &scan_params, &conn_params, BLE_CONN_CFG_TAG_CUSTOM);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to start connecting, error 0x%04x"), err_code);
}
while (self->waiting_to_connect) {
RUN_BACKGROUND_TASKS;
}
if (self->conn_handle == BLE_CONN_HANDLE_INVALID) {
mp_raise_OSError_msg(translate("Failed to connect: timeout"));
}
}
void common_hal_bleio_central_disconnect(bleio_central_obj_t *self) {
sd_ble_gap_disconnect(self->conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
}
bool common_hal_bleio_central_get_connected(bleio_central_obj_t *self) {
return self->conn_handle != BLE_CONN_HANDLE_INVALID;
}
mp_obj_tuple_t *common_hal_bleio_central_discover_remote_services(bleio_central_obj_t *self, mp_obj_t service_uuids_whitelist) {
common_hal_bleio_device_discover_remote_services(MP_OBJ_FROM_PTR(self), service_uuids_whitelist);
// Convert to a tuple and then clear the list so the callee will take ownership.
mp_obj_tuple_t *services_tuple = mp_obj_new_tuple(self->remote_service_list->len,
self->remote_service_list->items);
mp_obj_list_clear(self->remote_service_list);
return services_tuple;
}
mp_obj_list_t *common_hal_bleio_central_get_remote_services(bleio_central_obj_t *self) {
return self->remote_service_list;
}

View File

@ -32,7 +32,7 @@
#include "shared-bindings/_bleio/Descriptor.h"
#include "shared-bindings/_bleio/Service.h"
static volatile bleio_characteristic_obj_t *m_read_characteristic;
#include "common-hal/_bleio/Adapter.h"
STATIC uint16_t characteristic_get_cccd(uint16_t cccd_handle, uint16_t conn_handle) {
uint16_t cccd;
@ -53,27 +53,6 @@ STATIC uint16_t characteristic_get_cccd(uint16_t cccd_handle, uint16_t conn_hand
return cccd;
}
STATIC void characteristic_on_gattc_read_rsp_evt(ble_evt_t *ble_evt, void *param) {
switch (ble_evt->header.evt_id) {
// More events may be handled later, so keep this as a switch.
case BLE_GATTC_EVT_READ_RSP: {
ble_gattc_evt_read_rsp_t *response = &ble_evt->evt.gattc_evt.params.read_rsp;
if (m_read_characteristic) {
m_read_characteristic->value = mp_obj_new_bytearray(response->len, response->data);
}
// Indicate to busy-wait loop that we've read the attribute value.
m_read_characteristic = NULL;
break;
}
default:
// For debugging.
// mp_printf(&mp_plat_print, "Unhandled characteristic event: 0x%04x\n", ble_evt->header.evt_id);
break;
}
}
STATIC void characteristic_gatts_notify_indicate(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, uint16_t hvx_type) {
uint16_t hvx_len = bufinfo->len;
@ -103,35 +82,14 @@ STATIC void characteristic_gatts_notify_indicate(uint16_t handle, uint16_t conn_
}
}
STATIC void characteristic_gattc_read(bleio_characteristic_obj_t *characteristic) {
const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device);
common_hal_bleio_check_connected(conn_handle);
// Set to NULL in event loop after event.
m_read_characteristic = characteristic;
ble_drv_add_event_handler(characteristic_on_gattc_read_rsp_evt, characteristic);
const uint32_t err_code = sd_ble_gattc_read(conn_handle, characteristic->handle, 0);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to read attribute value, err 0x%04x"), err_code);
}
while (m_read_characteristic != NULL) {
RUN_BACKGROUND_TASKS;
}
ble_drv_remove_event_handler(characteristic_on_gattc_read_rsp_evt, characteristic);
}
void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo) {
void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, uint16_t handle, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo) {
self->service = service;
self->uuid = uuid;
self->handle = BLE_GATT_HANDLE_INVALID;
self->props = props;
self->read_perm = read_perm;
self->write_perm = write_perm;
self->descriptor_list = mp_obj_new_list(0, NULL);
self->descriptor_list = NULL;
const mp_int_t max_length_max = fixed_length ? BLE_GATTS_FIX_ATTR_LEN_MAX : BLE_GATTS_VAR_ATTR_LEN_MAX;
if (max_length < 0 || max_length > max_length_max) {
@ -141,10 +99,18 @@ void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self,
self->max_length = max_length;
self->fixed_length = fixed_length;
common_hal_bleio_characteristic_set_value(self, initial_value_bufinfo);
if (service->is_remote) {
self->handle = handle;
} else {
common_hal_bleio_service_add_characteristic(self->service, self, initial_value_bufinfo);
}
if (initial_value_bufinfo != NULL) {
common_hal_bleio_characteristic_set_value(self, initial_value_bufinfo);
}
}
mp_obj_list_t *common_hal_bleio_characteristic_get_descriptor_list(bleio_characteristic_obj_t *self) {
bleio_descriptor_obj_t *common_hal_bleio_characteristic_get_descriptor_list(bleio_characteristic_obj_t *self) {
return self->descriptor_list;
}
@ -152,19 +118,19 @@ bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_character
return self->service;
}
mp_obj_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self) {
size_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self, uint8_t* buf, size_t len) {
// Do GATT operations only if this characteristic has been added to a registered service.
if (self->handle != BLE_GATT_HANDLE_INVALID) {
uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device);
uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection);
if (common_hal_bleio_service_get_is_remote(self->service)) {
// self->value is set by evt handler.
characteristic_gattc_read(self);
return common_hal_bleio_gattc_read(self->handle, conn_handle, buf, len);
} else {
self->value = common_hal_bleio_gatts_read(self->handle, conn_handle);
return common_hal_bleio_gatts_read(self->handle, conn_handle, buf, len);
}
}
return self->value;
return 0;
}
void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) {
@ -175,39 +141,40 @@ void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self,
mp_raise_ValueError(translate("Value length > max_length"));
}
self->value = mp_obj_new_bytes(bufinfo->buf, bufinfo->len);
// Do GATT operations only if this characteristic has been added to a registered service.
if (self->handle != BLE_GATT_HANDLE_INVALID) {
uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device);
if (common_hal_bleio_service_get_is_remote(self->service)) {
uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection);
// Last argument is true if write-no-reponse desired.
common_hal_bleio_gattc_write(self->handle, conn_handle, bufinfo,
(self->props & CHAR_PROP_WRITE_NO_RESPONSE));
} else {
// Always write the value locally even if no connections are active.
common_hal_bleio_gatts_write(self->handle, BLE_CONN_HANDLE_INVALID, bufinfo);
// Check to see if we need to notify or indicate any active connections.
for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) {
bleio_connection_internal_t *connection = &connections[i];
uint16_t conn_handle = connection->conn_handle;
if (connection->conn_handle == BLE_CONN_HANDLE_INVALID) {
continue;
}
bool sent = false;
uint16_t cccd = 0;
uint16_t cccd = 0;
const bool notify = self->props & CHAR_PROP_NOTIFY;
const bool indicate = self->props & CHAR_PROP_INDICATE;
if (notify | indicate) {
cccd = characteristic_get_cccd(self->cccd_handle, conn_handle);
}
const bool notify = self->props & CHAR_PROP_NOTIFY;
const bool indicate = self->props & CHAR_PROP_INDICATE;
if (notify | indicate) {
cccd = characteristic_get_cccd(self->cccd_handle, conn_handle);
}
// It's possible that both notify and indicate are set.
if (notify && (cccd & BLE_GATT_HVX_NOTIFICATION)) {
characteristic_gatts_notify_indicate(self->handle, conn_handle, bufinfo, BLE_GATT_HVX_NOTIFICATION);
sent = true;
}
if (indicate && (cccd & BLE_GATT_HVX_INDICATION)) {
characteristic_gatts_notify_indicate(self->handle, conn_handle, bufinfo, BLE_GATT_HVX_INDICATION);
sent = true;
}
if (!sent) {
common_hal_bleio_gatts_write(self->handle, conn_handle, bufinfo);
// It's possible that both notify and indicate are set.
if (notify && (cccd & BLE_GATT_HVX_NOTIFICATION)) {
characteristic_gatts_notify_indicate(self->handle, conn_handle, bufinfo, BLE_GATT_HVX_NOTIFICATION);
}
if (indicate && (cccd & BLE_GATT_HVX_INDICATION)) {
characteristic_gatts_notify_indicate(self->handle, conn_handle, bufinfo, BLE_GATT_HVX_INDICATION);
}
}
}
}
@ -252,7 +219,8 @@ void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *
mp_raise_OSError_msg_varg(translate("Failed to add descriptor, err 0x%04x"), err_code);
}
mp_obj_list_append(self->descriptor_list, MP_OBJ_FROM_PTR(descriptor));
descriptor->next = self->descriptor_list;
self->descriptor_list = descriptor;
}
void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate) {
@ -264,7 +232,7 @@ void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self,
mp_raise_ValueError(translate("Can't set CCCD on local Characteristic"));
}
const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->service->device);
const uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection);
common_hal_bleio_check_connected(conn_handle);
uint16_t cccd_value =

View File

@ -29,11 +29,12 @@
#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_CHARACTERISTIC_H
#include "shared-bindings/_bleio/Attribute.h"
#include "common-hal/_bleio/Descriptor.h"
#include "shared-module/_bleio/Characteristic.h"
#include "common-hal/_bleio/Service.h"
#include "common-hal/_bleio/UUID.h"
typedef struct {
typedef struct _bleio_characteristic_obj {
mp_obj_base_t base;
// Will be MP_OBJ_NULL before being assigned to a Service.
bleio_service_obj_t *service;
@ -45,7 +46,7 @@ typedef struct {
bleio_characteristic_properties_t props;
bleio_attribute_security_mode_t read_perm;
bleio_attribute_security_mode_t write_perm;
mp_obj_list_t *descriptor_list;
bleio_descriptor_obj_t *descriptor_list;
uint16_t user_desc_handle;
uint16_t cccd_handle;
uint16_t sccd_handle;

View File

@ -38,6 +38,7 @@
#include "tick.h"
#include "shared-bindings/_bleio/__init__.h"
#include "shared-bindings/_bleio/Connection.h"
#include "common-hal/_bleio/CharacteristicBuffer.h"
STATIC void write_to_ringbuf(bleio_characteristic_buffer_obj_t *self, uint8_t *data, uint16_t len) {
@ -50,7 +51,7 @@ STATIC void write_to_ringbuf(bleio_characteristic_buffer_obj_t *self, uint8_t *d
sd_nvic_critical_region_exit(is_nested_critical_region);
}
STATIC void characteristic_buffer_on_ble_evt(ble_evt_t *ble_evt, void *param) {
STATIC bool characteristic_buffer_on_ble_evt(ble_evt_t *ble_evt, void *param) {
bleio_characteristic_buffer_obj_t *self = (bleio_characteristic_buffer_obj_t *) param;
switch (ble_evt->header.evt_id) {
case BLE_GATTS_EVT_WRITE: {
@ -75,7 +76,11 @@ STATIC void characteristic_buffer_on_ble_evt(ble_evt_t *ble_evt, void *param) {
}
break;
}
default:
return false;
break;
}
return true;
}
// Assumes that timeout and buffer_size have been validated before call.
@ -150,6 +155,7 @@ void common_hal_bleio_characteristic_buffer_deinit(bleio_characteristic_buffer_o
bool common_hal_bleio_characteristic_buffer_connected(bleio_characteristic_buffer_obj_t *self) {
return self->characteristic != NULL &&
self->characteristic->service != NULL &&
self->characteristic->service->device != NULL &&
common_hal_bleio_device_get_conn_handle(self->characteristic->service->device) != BLE_CONN_HANDLE_INVALID;
(!self->characteristic->service->is_remote ||
(self->characteristic->service->connection != MP_OBJ_NULL &&
common_hal_bleio_connection_get_connected(self->characteristic->service->connection)));
}

View File

@ -0,0 +1,629 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2018 Dan Halbert for Adafruit Industries
* 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 "shared-bindings/_bleio/Connection.h"
#include <string.h>
#include <stdio.h>
#include "ble.h"
#include "ble_drv.h"
#include "ble_hci.h"
#include "nrf_soc.h"
#include "py/gc.h"
#include "py/objlist.h"
#include "py/objstr.h"
#include "py/qstr.h"
#include "py/runtime.h"
#include "shared-bindings/_bleio/__init__.h"
#include "shared-bindings/_bleio/Adapter.h"
#include "shared-bindings/_bleio/Attribute.h"
#include "shared-bindings/_bleio/Characteristic.h"
#include "shared-bindings/_bleio/Service.h"
#include "shared-bindings/_bleio/UUID.h"
#define BLE_ADV_LENGTH_FIELD_SIZE 1
#define BLE_ADV_AD_TYPE_FIELD_SIZE 1
#define BLE_AD_TYPE_FLAGS_DATA_SIZE 1
static const ble_gap_sec_params_t pairing_sec_params = {
.bond = 1,
.mitm = 0,
.lesc = 0,
.keypress = 0,
.oob = 0,
.io_caps = BLE_GAP_IO_CAPS_NONE,
.min_key_size = 7,
.max_key_size = 16,
.kdist_own = { .enc = 1, .id = 1},
.kdist_peer = { .enc = 1, .id = 1},
};
static volatile bool m_discovery_in_process;
static volatile bool m_discovery_successful;
static bleio_service_obj_t *m_char_discovery_service;
static bleio_characteristic_obj_t *m_desc_discovery_characteristic;
bool dump_events = false;
bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
bleio_connection_internal_t *self = (bleio_connection_internal_t*)self_in;
if (BLE_GAP_EVT_BASE <= ble_evt->header.evt_id && ble_evt->header.evt_id <= BLE_GAP_EVT_LAST &&
ble_evt->evt.gap_evt.conn_handle != self->conn_handle) {
return false;
}
if (BLE_GATTS_EVT_BASE <= ble_evt->header.evt_id && ble_evt->header.evt_id <= BLE_GATTS_EVT_LAST &&
ble_evt->evt.gatts_evt.conn_handle != self->conn_handle) {
return false;
}
// For debugging.
if (dump_events) {
mp_printf(&mp_plat_print, "Connection event: 0x%04x\n", ble_evt->header.evt_id);
}
switch (ble_evt->header.evt_id) {
case BLE_GAP_EVT_DISCONNECTED:
break;
case BLE_GAP_EVT_CONN_PARAM_UPDATE: // 0x12
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_PHY_UPDATE: // 0x22
break;
case BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST:
// SoftDevice will respond to a length update request.
sd_ble_gap_data_length_update(self->conn_handle, NULL, NULL);
break;
case BLE_GAP_EVT_DATA_LENGTH_UPDATE: // 0x24
break;
case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST: {
// We only handle MTU of size BLE_GATT_ATT_MTU_DEFAULT.
sd_ble_gatts_exchange_mtu_reply(self->conn_handle, BLE_GATT_ATT_MTU_DEFAULT);
break;
}
case BLE_GATTS_EVT_SYS_ATTR_MISSING:
sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0);
break;
case BLE_GATTS_EVT_HVN_TX_COMPLETE: // Capture this for now. 0x55
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_GAP_EVT_SEC_PARAMS_REQUEST: {
ble_gap_sec_keyset_t keyset = {
.keys_own = {
.p_enc_key = &self->bonding_keys.own_enc,
.p_id_key = NULL,
.p_sign_key = NULL,
.p_pk = NULL
},
.keys_peer = {
.p_enc_key = &self->bonding_keys.peer_enc,
.p_id_key = &self->bonding_keys.peer_id,
.p_sign_key = NULL,
.p_pk = NULL
}
};
sd_ble_gap_sec_params_reply(self->conn_handle, BLE_GAP_SEC_STATUS_SUCCESS,
&pairing_sec_params, &keyset);
break;
}
case BLE_GAP_EVT_LESC_DHKEY_REQUEST:
// TODO for LESC pairing:
// sd_ble_gap_lesc_dhkey_reply(...);
break;
case BLE_GAP_EVT_AUTH_STATUS: { // 0x19
// Pairing process completed
ble_gap_evt_auth_status_t* status = &ble_evt->evt.gap_evt.params.auth_status;
if (BLE_GAP_SEC_STATUS_SUCCESS == status->auth_status) {
// TODO _ediv = bonding_keys->own_enc.master_id.ediv;
self->pair_status = PAIR_PAIRED;
} else {
self->pair_status = PAIR_NOT_PAIRED;
}
break;
}
case BLE_GAP_EVT_SEC_INFO_REQUEST: { // 0x14
// Peer asks for the stored keys.
// - load key and return if bonded previously.
// - Else return NULL --> Initiate key exchange
ble_gap_evt_sec_info_request_t* sec_info_request = &ble_evt->evt.gap_evt.params.sec_info_request;
(void) sec_info_request;
//if ( bond_load_keys(_role, sec_req->master_id.ediv, &bkeys) ) {
//sd_ble_gap_sec_info_reply(_conn_hdl, &bkeys.own_enc.enc_info, &bkeys.peer_id.id_info, NULL);
//
//_ediv = bkeys.own_enc.master_id.ediv;
// } else {
sd_ble_gap_sec_info_reply(self->conn_handle, NULL, NULL, NULL);
// }
break;
}
case BLE_GAP_EVT_CONN_SEC_UPDATE: { // 0x1a
ble_gap_conn_sec_t* conn_sec = &ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec;
if (conn_sec->sec_mode.sm <= 1 && conn_sec->sec_mode.lv <= 1) {
// Security setup did not succeed:
// mode 0, level 0 means no access
// mode 1, level 1 means open link
// mode >=1 and/or level >=1 means encryption is set up
self->pair_status = PAIR_NOT_PAIRED;
} else {
//if ( !bond_load_cccd(_role, _conn_hdl, _ediv) ) {
if (true) { // TODO: no bonding yet
// Initialize system attributes fresh.
sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0);
}
// Not quite paired yet: wait for BLE_GAP_EVT_AUTH_STATUS SUCCESS.
self->ediv = self->bonding_keys.own_enc.master_id.ediv;
}
break;
}
default:
// For debugging.
if (dump_events) {
mp_printf(&mp_plat_print, "Unhandled connection event: 0x%04x\n", ble_evt->header.evt_id);
}
return false;
}
return true;
}
void bleio_connection_clear(bleio_connection_internal_t *self) {
self->remote_service_list = NULL;
self->conn_handle = BLE_CONN_HANDLE_INVALID;
self->pair_status = PAIR_NOT_PAIRED;
memset(&self->bonding_keys, 0, sizeof(self->bonding_keys));
}
bool common_hal_bleio_connection_get_connected(bleio_connection_obj_t *self) {
if (self->connection == NULL) {
return false;
}
return self->connection->conn_handle != BLE_CONN_HANDLE_INVALID;
}
void common_hal_bleio_connection_disconnect(bleio_connection_internal_t *self) {
sd_ble_gap_disconnect(self->conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
}
void common_hal_bleio_connection_pair(bleio_connection_internal_t *self) {
self->pair_status = PAIR_WAITING;
uint32_t err_code = sd_ble_gap_authenticate(self->conn_handle, &pairing_sec_params);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to start pairing, NRF_ERROR_%q"), MP_OBJ_QSTR_VALUE(base_error_messages[err_code - NRF_ERROR_BASE_NUM]));
}
while (self->pair_status == PAIR_WAITING) {
RUN_BACKGROUND_TASKS;
}
if (self->pair_status == PAIR_NOT_PAIRED) {
mp_raise_OSError_msg(translate("Failed to pair"));
}
}
// service_uuid may be NULL, to discover all services.
STATIC bool discover_next_services(bleio_connection_internal_t* connection, uint16_t start_handle, ble_uuid_t *service_uuid) {
m_discovery_successful = false;
m_discovery_in_process = true;
uint32_t err_code = sd_ble_gattc_primary_services_discover(connection->conn_handle, start_handle, service_uuid);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg(translate("Failed to discover services"));
}
// Wait for a discovery event.
while (m_discovery_in_process) {
MICROPY_VM_HOOK_LOOP;
}
return m_discovery_successful;
}
STATIC bool discover_next_characteristics(bleio_connection_internal_t* connection, bleio_service_obj_t *service, uint16_t start_handle) {
m_char_discovery_service = service;
ble_gattc_handle_range_t handle_range;
handle_range.start_handle = start_handle;
handle_range.end_handle = service->end_handle;
m_discovery_successful = false;
m_discovery_in_process = true;
uint32_t err_code = sd_ble_gattc_characteristics_discover(connection->conn_handle, &handle_range);
if (err_code != NRF_SUCCESS) {
return false;
}
// Wait for a discovery event.
while (m_discovery_in_process) {
MICROPY_VM_HOOK_LOOP;
}
return m_discovery_successful;
}
STATIC bool discover_next_descriptors(bleio_connection_internal_t* connection, bleio_characteristic_obj_t *characteristic, uint16_t start_handle, uint16_t end_handle) {
m_desc_discovery_characteristic = characteristic;
ble_gattc_handle_range_t handle_range;
handle_range.start_handle = start_handle;
handle_range.end_handle = end_handle;
m_discovery_successful = false;
m_discovery_in_process = true;
uint32_t err_code = sd_ble_gattc_descriptors_discover(connection->conn_handle, &handle_range);
if (err_code != NRF_SUCCESS) {
return false;
}
// Wait for a discovery event.
while (m_discovery_in_process) {
MICROPY_VM_HOOK_LOOP;
}
return m_discovery_successful;
}
STATIC void on_primary_srv_discovery_rsp(ble_gattc_evt_prim_srvc_disc_rsp_t *response, bleio_connection_internal_t* connection) {
bleio_service_obj_t* tail = connection->remote_service_list;
for (size_t i = 0; i < response->count; ++i) {
ble_gattc_service_t *gattc_service = &response->services[i];
bleio_service_obj_t *service = m_new_obj(bleio_service_obj_t);
service->base.type = &bleio_service_type;
// Initialize several fields at once.
bleio_service_from_connection(service, bleio_connection_new_from_internal(connection));
service->is_remote = true;
service->start_handle = gattc_service->handle_range.start_handle;
service->end_handle = gattc_service->handle_range.end_handle;
service->handle = gattc_service->handle_range.start_handle;
if (gattc_service->uuid.type != BLE_UUID_TYPE_UNKNOWN) {
// Known service UUID.
bleio_uuid_obj_t *uuid = m_new_obj(bleio_uuid_obj_t);
uuid->base.type = &bleio_uuid_type;
bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_service->uuid);
service->uuid = uuid;
} else {
// The discovery response contained a 128-bit UUID that has not yet been registered with the
// softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it.
// For now, just set the UUID to NULL.
service->uuid = NULL;
}
service->next = tail;
tail = service;
}
connection->remote_service_list = tail;
if (response->count > 0) {
m_discovery_successful = true;
}
m_discovery_in_process = false;
}
STATIC void on_char_discovery_rsp(ble_gattc_evt_char_disc_rsp_t *response, bleio_connection_internal_t* connection) {
for (size_t i = 0; i < response->count; ++i) {
ble_gattc_char_t *gattc_char = &response->chars[i];
bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t);
characteristic->base.type = &bleio_characteristic_type;
bleio_uuid_obj_t *uuid = NULL;
if (gattc_char->uuid.type != BLE_UUID_TYPE_UNKNOWN) {
// Known characteristic UUID.
uuid = m_new_obj(bleio_uuid_obj_t);
uuid->base.type = &bleio_uuid_type;
bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_char->uuid);
} else {
// The discovery response contained a 128-bit UUID that has not yet been registered with the
// softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it.
// For now, just leave the UUID as NULL.
}
bleio_characteristic_properties_t props =
(gattc_char->char_props.broadcast ? CHAR_PROP_BROADCAST : 0) |
(gattc_char->char_props.indicate ? CHAR_PROP_INDICATE : 0) |
(gattc_char->char_props.notify ? CHAR_PROP_NOTIFY : 0) |
(gattc_char->char_props.read ? CHAR_PROP_READ : 0) |
(gattc_char->char_props.write ? CHAR_PROP_WRITE : 0) |
(gattc_char->char_props.write_wo_resp ? CHAR_PROP_WRITE_NO_RESPONSE : 0);
// Call common_hal_bleio_characteristic_construct() to initalize some fields and set up evt handler.
common_hal_bleio_characteristic_construct(
characteristic, m_char_discovery_service, gattc_char->handle_value, uuid,
props, SECURITY_MODE_OPEN, SECURITY_MODE_OPEN,
GATT_MAX_DATA_LENGTH, false, // max_length, fixed_length: values may not matter for gattc
NULL);
mp_obj_list_append(m_char_discovery_service->characteristic_list, MP_OBJ_FROM_PTR(characteristic));
}
if (response->count > 0) {
m_discovery_successful = true;
}
m_discovery_in_process = false;
}
STATIC void on_desc_discovery_rsp(ble_gattc_evt_desc_disc_rsp_t *response, bleio_connection_internal_t* connection) {
for (size_t i = 0; i < response->count; ++i) {
ble_gattc_desc_t *gattc_desc = &response->descs[i];
// Remember handles for certain well-known descriptors.
switch (gattc_desc->uuid.uuid) {
case BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG:
m_desc_discovery_characteristic->cccd_handle = gattc_desc->handle;
break;
case BLE_UUID_DESCRIPTOR_SERVER_CHAR_CONFIG:
m_desc_discovery_characteristic->sccd_handle = gattc_desc->handle;
break;
case BLE_UUID_DESCRIPTOR_CHAR_USER_DESC:
m_desc_discovery_characteristic->user_desc_handle = gattc_desc->handle;
break;
default:
// TODO: sd_ble_gattc_descriptors_discover() can return things that are not descriptors,
// so ignore those.
// https://devzone.nordicsemi.com/f/nordic-q-a/49500/sd_ble_gattc_descriptors_discover-is-returning-attributes-that-are-not-descriptors
break;
}
bleio_descriptor_obj_t *descriptor = m_new_obj(bleio_descriptor_obj_t);
descriptor->base.type = &bleio_descriptor_type;
bleio_uuid_obj_t *uuid = NULL;
if (gattc_desc->uuid.type != BLE_UUID_TYPE_UNKNOWN) {
// Known descriptor UUID.
uuid = m_new_obj(bleio_uuid_obj_t);
uuid->base.type = &bleio_uuid_type;
bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_desc->uuid);
} else {
// The discovery response contained a 128-bit UUID that has not yet been registered with the
// softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it.
// For now, just leave the UUID as NULL.
}
common_hal_bleio_descriptor_construct(
descriptor, m_desc_discovery_characteristic, uuid,
SECURITY_MODE_OPEN, SECURITY_MODE_OPEN,
GATT_MAX_DATA_LENGTH, false, mp_const_empty_bytes);
descriptor->handle = gattc_desc->handle;
mp_obj_list_append(m_desc_discovery_characteristic->descriptor_list, MP_OBJ_FROM_PTR(descriptor));
}
if (response->count > 0) {
m_discovery_successful = true;
}
m_discovery_in_process = false;
}
STATIC bool discovery_on_ble_evt(ble_evt_t *ble_evt, mp_obj_t payload) {
bleio_connection_internal_t* connection = MP_OBJ_TO_PTR(payload);
switch (ble_evt->header.evt_id) {
case BLE_GAP_EVT_DISCONNECTED:
m_discovery_successful = false;
m_discovery_in_process = false;
break;
case BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP:
on_primary_srv_discovery_rsp(&ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp, connection);
break;
case BLE_GATTC_EVT_CHAR_DISC_RSP:
on_char_discovery_rsp(&ble_evt->evt.gattc_evt.params.char_disc_rsp, connection);
break;
case BLE_GATTC_EVT_DESC_DISC_RSP:
on_desc_discovery_rsp(&ble_evt->evt.gattc_evt.params.desc_disc_rsp, connection);
break;
default:
// For debugging.
// mp_printf(&mp_plat_print, "Unhandled discovery event: 0x%04x\n", ble_evt->header.evt_id);
return false;
break;
}
return true;
}
STATIC void discover_remote_services(bleio_connection_internal_t *self, mp_obj_t service_uuids_whitelist) {
ble_drv_add_event_handler(discovery_on_ble_evt, self);
// Start over with an empty list.
self->remote_service_list = NULL;
if (service_uuids_whitelist == mp_const_none) {
// List of service UUID's not given, so discover all available services.
uint16_t next_service_start_handle = BLE_GATT_HANDLE_START;
while (discover_next_services(self, next_service_start_handle, MP_OBJ_NULL)) {
// discover_next_services() appends to remote_services_list.
// Get the most recently discovered service, and then ask for services
// whose handles start after the last attribute handle inside that service.
const bleio_service_obj_t *service = self->remote_service_list;
next_service_start_handle = service->end_handle + 1;
}
} else {
mp_obj_iter_buf_t iter_buf;
mp_obj_t iterable = mp_getiter(service_uuids_whitelist, &iter_buf);
mp_obj_t uuid_obj;
while ((uuid_obj = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) {
mp_raise_ValueError(translate("non-UUID found in service_uuids_whitelist"));
}
bleio_uuid_obj_t *uuid = MP_OBJ_TO_PTR(uuid_obj);
ble_uuid_t nrf_uuid;
bleio_uuid_convert_to_nrf_ble_uuid(uuid, &nrf_uuid);
// Service might or might not be discovered; that's ok. Caller has to check
// Central.remote_services to find out.
// We only need to call this once for each service to discover.
discover_next_services(self, BLE_GATT_HANDLE_START, &nrf_uuid);
}
}
bleio_service_obj_t *service = self->remote_service_list;
while (service != NULL) {
// Skip the service if it had an unknown (unregistered) UUID.
if (service->uuid == NULL) {
service = service->next;
continue;
}
uint16_t next_char_start_handle = service->start_handle;
// Stop when we go past the end of the range of handles for this service or
// discovery call returns nothing.
// discover_next_characteristics() appends to the characteristic_list.
while (next_char_start_handle <= service->end_handle &&
discover_next_characteristics(self, service, next_char_start_handle)) {
// Get the most recently discovered characteristic, and then ask for characteristics
// whose handles start after the last attribute handle inside that characteristic.
const bleio_characteristic_obj_t *characteristic =
MP_OBJ_TO_PTR(service->characteristic_list->items[service->characteristic_list->len - 1]);
next_char_start_handle = characteristic->handle + 1;
}
// Got characteristics for this service. Now discover descriptors for each characteristic.
size_t char_list_len = service->characteristic_list->len;
for (size_t char_idx = 0; char_idx < char_list_len; ++char_idx) {
bleio_characteristic_obj_t *characteristic =
MP_OBJ_TO_PTR(service->characteristic_list->items[char_idx]);
const bool last_characteristic = char_idx == char_list_len - 1;
bleio_characteristic_obj_t *next_characteristic = last_characteristic
? NULL
: MP_OBJ_TO_PTR(service->characteristic_list->items[char_idx + 1]);
// Skip the characteristic if it had an unknown (unregistered) UUID.
if (characteristic->uuid == NULL) {
continue;
}
uint16_t next_desc_start_handle = characteristic->handle + 1;
// Don't run past the end of this service or the beginning of the next characteristic.
uint16_t next_desc_end_handle = next_characteristic == NULL
? service->end_handle
: next_characteristic->handle - 1;
// Stop when we go past the end of the range of handles for this service or
// discovery call returns nothing.
// discover_next_descriptors() appends to the descriptor_list.
while (next_desc_start_handle <= service->end_handle &&
next_desc_start_handle < next_desc_end_handle &&
discover_next_descriptors(self, characteristic,
next_desc_start_handle, next_desc_end_handle)) {
// Get the most recently discovered descriptor, and then ask for descriptors
// whose handles start after that descriptor's handle.
const bleio_descriptor_obj_t *descriptor = characteristic->descriptor_list;
next_desc_start_handle = descriptor->handle + 1;
}
}
service = service->next;
}
// This event handler is no longer needed.
ble_drv_remove_event_handler(discovery_on_ble_evt, self);
}
mp_obj_tuple_t *common_hal_bleio_connection_discover_remote_services(bleio_connection_obj_t *self, mp_obj_t service_uuids_whitelist) {
discover_remote_services(self->connection, service_uuids_whitelist);
// Convert to a tuple and then clear the list so the callee will take ownership.
mp_obj_tuple_t *services_tuple = service_linked_list_to_tuple(self->connection->remote_service_list);
self->connection->remote_service_list = NULL;
return services_tuple;
}
uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self) {
if (self == NULL || self->connection == NULL) {
return BLE_CONN_HANDLE_INVALID;
}
return self->connection->conn_handle;
}
mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t* internal) {
if (internal->connection_obj != mp_const_none) {
return internal->connection_obj;
}
bleio_connection_obj_t *connection = m_new_obj(bleio_connection_obj_t);
connection->base.type = &bleio_connection_type;
connection->connection = internal;
internal->connection_obj = connection;
return MP_OBJ_FROM_PTR(connection);
}

View File

@ -25,8 +25,8 @@
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_PERIPHERAL_H
#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_PERIPHERAL_H
#ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_CONNECTION_H
#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_CONNECTION_H
#include <stdbool.h>
@ -37,6 +37,7 @@
#include "common-hal/_bleio/__init__.h"
#include "shared-module/_bleio/Address.h"
#include "common-hal/_bleio/Service.h"
typedef enum {
PAIR_NOT_PAIRED,
@ -44,24 +45,35 @@ typedef enum {
PAIR_PAIRED,
} pair_status_t;
// We split the Connection object into two so that the internal mechanics can live outside of the
// VM. If it were one object, then we'd risk user code seeing a connection object of theirs be
// reused.
typedef struct {
mp_obj_base_t base;
mp_obj_t name;
volatile uint16_t conn_handle;
// Services provided by this peripheral.
mp_obj_list_t *service_list;
uint16_t conn_handle;
bool is_central;
// Remote services discovered when this peripheral is acting as a client.
mp_obj_list_t *remote_service_list;
bleio_service_obj_t *remote_service_list;
// The advertising data and scan response buffers are held by us, not by the SD, so we must
// maintain them and not change it. If we need to change the contents during advertising,
// there are tricks to get the SD to notice (see DevZone - TBS).
// EDIV: Encrypted Diversifier: Identifies LTK during legacy pairing.
bonding_keys_t bonding_keys;
uint16_t ediv;
uint8_t* advertising_data;
uint8_t* scan_response_data;
uint8_t adv_handle;
pair_status_t pair_status;
} bleio_peripheral_obj_t;
mp_obj_t connection_obj;
ble_drv_evt_handler_entry_t handler_entry;
} bleio_connection_internal_t;
#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_PERIPHERAL_H
typedef struct {
mp_obj_base_t base;
bleio_connection_internal_t* connection;
// The HCI disconnect reason.
uint8_t disconnect_reason;
} bleio_connection_obj_t;
bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in);
uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self);
mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t* connection);
#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_CONNECTION_H

View File

@ -33,8 +33,6 @@
#include "shared-bindings/_bleio/Service.h"
#include "shared-bindings/_bleio/UUID.h"
static volatile bleio_descriptor_obj_t *m_read_descriptor;
void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_characteristic_obj_t *characteristic, bleio_uuid_obj_t *uuid, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo) {
self->characteristic = characteristic;
self->uuid = uuid;
@ -61,62 +59,18 @@ bleio_characteristic_obj_t *common_hal_bleio_descriptor_get_characteristic(bleio
return self->characteristic;
}
STATIC void descriptor_on_gattc_read_rsp_evt(ble_evt_t *ble_evt, void *param) {
switch (ble_evt->header.evt_id) {
// More events may be handled later, so keep this as a switch.
case BLE_GATTC_EVT_READ_RSP: {
ble_gattc_evt_read_rsp_t *response = &ble_evt->evt.gattc_evt.params.read_rsp;
if (m_read_descriptor) {
m_read_descriptor->value = mp_obj_new_bytearray(response->len, response->data);
}
// Indicate to busy-wait loop that we've read the attribute value.
m_read_descriptor = NULL;
break;
}
default:
// For debugging.
// mp_printf(&mp_plat_print, "Unhandled descriptor event: 0x%04x\n", ble_evt->header.evt_id);
break;
}
}
STATIC void descriptor_gattc_read(bleio_descriptor_obj_t *descriptor) {
const uint16_t conn_handle =
common_hal_bleio_device_get_conn_handle(descriptor->characteristic->service->device);
common_hal_bleio_check_connected(conn_handle);
// Set to NULL in event loop after event.
m_read_descriptor = descriptor;
ble_drv_add_event_handler(descriptor_on_gattc_read_rsp_evt, descriptor);
const uint32_t err_code = sd_ble_gattc_read(conn_handle, descriptor->handle, 0);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to read attribute value, err 0x%04x"), err_code);
}
while (m_read_descriptor != NULL) {
MICROPY_VM_HOOK_LOOP;
}
ble_drv_remove_event_handler(descriptor_on_gattc_read_rsp_evt, descriptor);
}
mp_obj_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self) {
size_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self, uint8_t* buf, size_t len) {
// Do GATT operations only if this descriptor has been registered
if (self->handle != BLE_GATT_HANDLE_INVALID) {
uint16_t conn_handle = bleio_connection_get_conn_handle(self->characteristic->service->connection);
if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) {
descriptor_gattc_read(self);
return common_hal_bleio_gattc_read(self->handle, conn_handle, buf, len);
} else {
self->value = common_hal_bleio_gatts_read(
self->handle, common_hal_bleio_device_get_conn_handle(self->characteristic->service->device));
return common_hal_bleio_gatts_read(self->handle, conn_handle, buf, len);
}
}
return self->value;
return 0;
}
void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buffer_info_t *bufinfo) {
@ -131,7 +85,7 @@ void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buff
// Do GATT operations only if this descriptor has been registered.
if (self->handle != BLE_GATT_HANDLE_INVALID) {
uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(self->characteristic->service->device);
uint16_t conn_handle = bleio_connection_get_conn_handle(self->characteristic->service->connection);
if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) {
// false means WRITE_REQ, not write-no-response
common_hal_bleio_gattc_write(self->handle, conn_handle, bufinfo, false);

View File

@ -31,13 +31,15 @@
#include "py/obj.h"
#include "common-hal/_bleio/Characteristic.h"
#include "common-hal/_bleio/UUID.h"
typedef struct {
// Forward declare characteristic because it includes a Descriptor.
struct _bleio_characteristic_obj;
typedef struct _bleio_descriptor_obj {
mp_obj_base_t base;
// Will be MP_OBJ_NULL before being assigned to a Characteristic.
bleio_characteristic_obj_t *characteristic;
struct _bleio_characteristic_obj *characteristic;
bleio_uuid_obj_t *uuid;
mp_obj_t value;
uint16_t max_length;
@ -45,6 +47,7 @@ typedef struct {
uint16_t handle;
bleio_attribute_security_mode_t read_perm;
bleio_attribute_security_mode_t write_perm;
struct _bleio_descriptor_obj* next;
} bleio_descriptor_obj_t;
#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_DESCRIPTOR_H

View File

@ -1,361 +0,0 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2018 Dan Halbert for Adafruit Industries
* 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/gc.h"
#include "py/objlist.h"
#include "py/objstr.h"
#include "py/runtime.h"
#include "shared-bindings/_bleio/__init__.h"
#include "shared-bindings/_bleio/Adapter.h"
#include "shared-bindings/_bleio/Attribute.h"
#include "shared-bindings/_bleio/Characteristic.h"
#include "shared-bindings/_bleio/Peripheral.h"
#include "shared-bindings/_bleio/Service.h"
#include "shared-bindings/_bleio/UUID.h"
#define BLE_ADV_LENGTH_FIELD_SIZE 1
#define BLE_ADV_AD_TYPE_FIELD_SIZE 1
#define BLE_AD_TYPE_FLAGS_DATA_SIZE 1
static const ble_gap_sec_params_t pairing_sec_params = {
.bond = 1,
.mitm = 0,
.lesc = 0,
.keypress = 0,
.oob = 0,
.io_caps = BLE_GAP_IO_CAPS_NONE,
.min_key_size = 7,
.max_key_size = 16,
.kdist_own = { .enc = 1, .id = 1},
.kdist_peer = { .enc = 1, .id = 1},
};
STATIC void check_data_fit(size_t data_len) {
if (data_len > BLE_GAP_ADV_SET_DATA_SIZE_MAX) {
mp_raise_ValueError(translate("Data too large for advertisement packet"));
}
}
STATIC void peripheral_on_ble_evt(ble_evt_t *ble_evt, void *self_in) {
bleio_peripheral_obj_t *self = (bleio_peripheral_obj_t*)self_in;
// For debugging.
// mp_printf(&mp_plat_print, "Peripheral event: 0x%04x\n", ble_evt->header.evt_id);
switch (ble_evt->header.evt_id) {
case BLE_GAP_EVT_CONNECTED: {
// Central has connected.
ble_gap_evt_connected_t* connected = &ble_evt->evt.gap_evt.params.connected;
self->conn_handle = ble_evt->evt.gap_evt.conn_handle;
// See if connection interval set by Central is out of range.
// If so, negotiate our preferred range.
ble_gap_conn_params_t conn_params;
sd_ble_gap_ppcp_get(&conn_params);
if (conn_params.min_conn_interval < connected->conn_params.min_conn_interval ||
conn_params.min_conn_interval > connected->conn_params.max_conn_interval) {
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:
// TODO: Someday may handle timeouts or limit reached.
break;
case BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST:
// SoftDevice will respond to a length update request.
sd_ble_gap_data_length_update(self->conn_handle, NULL, NULL);
break;
case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST: {
// We only handle MTU of size BLE_GATT_ATT_MTU_DEFAULT.
sd_ble_gatts_exchange_mtu_reply(self->conn_handle, BLE_GATT_ATT_MTU_DEFAULT);
break;
}
case BLE_GATTS_EVT_SYS_ATTR_MISSING:
sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0);
break;
case BLE_GAP_EVT_SEC_PARAMS_REQUEST: {
ble_gap_sec_keyset_t keyset = {
.keys_own = {
.p_enc_key = &self->bonding_keys.own_enc,
.p_id_key = NULL,
.p_sign_key = NULL,
.p_pk = NULL
},
.keys_peer = {
.p_enc_key = &self->bonding_keys.peer_enc,
.p_id_key = &self->bonding_keys.peer_id,
.p_sign_key = NULL,
.p_pk = NULL
}
};
sd_ble_gap_sec_params_reply(self->conn_handle, BLE_GAP_SEC_STATUS_SUCCESS,
&pairing_sec_params, &keyset);
break;
}
case BLE_GAP_EVT_LESC_DHKEY_REQUEST:
// TODO for LESC pairing:
// sd_ble_gap_lesc_dhkey_reply(...);
break;
case BLE_GAP_EVT_AUTH_STATUS: {
// Pairing process completed
ble_gap_evt_auth_status_t* status = &ble_evt->evt.gap_evt.params.auth_status;
if (BLE_GAP_SEC_STATUS_SUCCESS == status->auth_status) {
// TODO _ediv = bonding_keys->own_enc.master_id.ediv;
self->pair_status = PAIR_PAIRED;
} else {
self->pair_status = PAIR_NOT_PAIRED;
}
break;
}
case BLE_GAP_EVT_SEC_INFO_REQUEST: {
// Peer asks for the stored keys.
// - load key and return if bonded previously.
// - Else return NULL --> Initiate key exchange
ble_gap_evt_sec_info_request_t* sec_info_request = &ble_evt->evt.gap_evt.params.sec_info_request;
(void) sec_info_request;
//if ( bond_load_keys(_role, sec_req->master_id.ediv, &bkeys) ) {
//sd_ble_gap_sec_info_reply(_conn_hdl, &bkeys.own_enc.enc_info, &bkeys.peer_id.id_info, NULL);
//
//_ediv = bkeys.own_enc.master_id.ediv;
// } else {
sd_ble_gap_sec_info_reply(self->conn_handle, NULL, NULL, NULL);
// }
break;
}
case BLE_GAP_EVT_CONN_SEC_UPDATE: {
ble_gap_conn_sec_t* conn_sec = &ble_evt->evt.gap_evt.params.conn_sec_update.conn_sec;
if (conn_sec->sec_mode.sm <= 1 && conn_sec->sec_mode.lv <= 1) {
// Security setup did not succeed:
// mode 0, level 0 means no access
// mode 1, level 1 means open link
// mode >=1 and/or level >=1 means encryption is set up
self->pair_status = PAIR_NOT_PAIRED;
} else {
//if ( !bond_load_cccd(_role, _conn_hdl, _ediv) ) {
if (true) { // TODO: no bonding yet
// Initialize system attributes fresh.
sd_ble_gatts_sys_attr_set(self->conn_handle, NULL, 0, 0);
}
// Not quite paired yet: wait for BLE_GAP_EVT_AUTH_STATUS SUCCESS.
self->ediv = self->bonding_keys.own_enc.master_id.ediv;
}
break;
}
default:
// For debugging.
// mp_printf(&mp_plat_print, "Unhandled peripheral event: 0x%04x\n", ble_evt->header.evt_id);
break;
}
}
void common_hal_bleio_peripheral_construct(bleio_peripheral_obj_t *self, mp_obj_t name) {
common_hal_bleio_adapter_set_enabled(true);
self->service_list = mp_obj_new_list(0, NULL);
// Used only for discovery when acting as a client.
self->remote_service_list = mp_obj_new_list(0, NULL);
self->name = name;
self->conn_handle = BLE_CONN_HANDLE_INVALID;
self->adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET;
self->pair_status = PAIR_NOT_PAIRED;
memset(&self->bonding_keys, 0, sizeof(self->bonding_keys));
}
void common_hal_bleio_peripheral_add_service(bleio_peripheral_obj_t *self, bleio_service_obj_t *service) {
ble_uuid_t uuid;
bleio_uuid_convert_to_nrf_ble_uuid(service->uuid, &uuid);
uint8_t service_type = BLE_GATTS_SRVC_TYPE_PRIMARY;
if (common_hal_bleio_service_get_is_secondary(service)) {
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);
}
mp_obj_list_append(self->service_list, MP_OBJ_FROM_PTR(service));
}
mp_obj_list_t *common_hal_bleio_peripheral_get_services(bleio_peripheral_obj_t *self) {
return self->service_list;
}
bool common_hal_bleio_peripheral_get_connected(bleio_peripheral_obj_t *self) {
return self->conn_handle != BLE_CONN_HANDLE_INVALID;
}
mp_obj_t common_hal_bleio_peripheral_get_name(bleio_peripheral_obj_t *self) {
return self->name;
}
void common_hal_bleio_peripheral_start_advertising(bleio_peripheral_obj_t *self, bool connectable, mp_float_t interval, mp_buffer_info_t *advertising_data_bufinfo, mp_buffer_info_t *scan_response_data_bufinfo) {
// interval value has already been validated.
if (connectable) {
ble_drv_add_event_handler(peripheral_on_ble_evt, self);
}
common_hal_bleio_adapter_set_enabled(true);
uint32_t err_code;
GET_STR_DATA_LEN(self->name, name_data, name_len);
if (name_len > 0) {
// Set device name, and make it available to anyone.
ble_gap_conn_sec_mode_t sec_mode;
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
err_code = sd_ble_gap_device_name_set(&sec_mode, name_data, name_len);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to set device name, err 0x%04x"), err_code);
}
}
check_data_fit(advertising_data_bufinfo->len);
// The advertising data buffers must not move, because the SoftDevice depends on them.
// So make them long-lived.
self->advertising_data = (uint8_t *) gc_alloc(BLE_GAP_ADV_SET_DATA_SIZE_MAX * sizeof(uint8_t), false, true);
memcpy(self->advertising_data, advertising_data_bufinfo->buf, advertising_data_bufinfo->len);
check_data_fit(scan_response_data_bufinfo->len);
self->scan_response_data = (uint8_t *) gc_alloc(BLE_GAP_ADV_SET_DATA_SIZE_MAX * sizeof(uint8_t), false, true);
memcpy(self->scan_response_data, scan_response_data_bufinfo->buf, scan_response_data_bufinfo->len);
ble_gap_adv_params_t adv_params = {
.interval = SEC_TO_UNITS(interval, UNIT_0_625_MS),
.properties.type = connectable ? BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED
: 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_peripheral_stop_advertising(self);
const ble_gap_adv_data_t ble_gap_adv_data = {
.adv_data.p_data = self->advertising_data,
.adv_data.len = advertising_data_bufinfo->len,
.scan_rsp_data.p_data = scan_response_data_bufinfo-> len > 0 ? self->scan_response_data : NULL,
.scan_rsp_data.len = scan_response_data_bufinfo->len,
};
err_code = sd_ble_gap_adv_set_configure(&self->adv_handle, &ble_gap_adv_data, &adv_params);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to configure advertising, err 0x%04x"), err_code);
}
err_code = sd_ble_gap_adv_start(self->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_peripheral_stop_advertising(bleio_peripheral_obj_t *self) {
if (self->adv_handle == BLE_GAP_ADV_SET_HANDLE_NOT_SET)
return;
const uint32_t err_code = sd_ble_gap_adv_stop(self->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);
}
}
void common_hal_bleio_peripheral_disconnect(bleio_peripheral_obj_t *self) {
sd_ble_gap_disconnect(self->conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
}
mp_obj_tuple_t *common_hal_bleio_peripheral_discover_remote_services(bleio_peripheral_obj_t *self, mp_obj_t service_uuids_whitelist) {
common_hal_bleio_device_discover_remote_services(MP_OBJ_FROM_PTR(self), service_uuids_whitelist);
// Convert to a tuple and then clear the list so the callee will take ownership.
mp_obj_tuple_t *services_tuple = mp_obj_new_tuple(self->remote_service_list->len,
self->remote_service_list->items);
mp_obj_list_clear(self->remote_service_list);
return services_tuple;
}
void common_hal_bleio_peripheral_pair(bleio_peripheral_obj_t *self) {
self->pair_status = PAIR_WAITING;
uint32_t err_code = sd_ble_gap_authenticate(self->conn_handle, &pairing_sec_params);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to start pairing, error 0x%04x"), err_code);
}
while (self->pair_status == PAIR_WAITING) {
RUN_BACKGROUND_TASKS;
}
if (self->pair_status == PAIR_NOT_PAIRED) {
mp_raise_OSError_msg(translate("Failed to pair"));
}
}

View File

@ -1,105 +0,0 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Dan Halbert for Adafruit Industries
* Copyright (c) 2018 Artur Pacholec
* Copyright (c) 2016 Glenn Ruben Bakke
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <string.h>
#include "ble_drv.h"
#include "ble_gap.h"
#include "py/mphal.h"
#include "py/objlist.h"
#include "py/runtime.h"
#include "shared-bindings/_bleio/Adapter.h"
#include "shared-bindings/_bleio/ScanEntry.h"
#include "shared-bindings/_bleio/Scanner.h"
#include "shared-module/_bleio/ScanEntry.h"
static uint8_t m_scan_buffer_data[BLE_GAP_SCAN_BUFFER_MIN];
static ble_data_t m_scan_buffer = {
m_scan_buffer_data,
BLE_GAP_SCAN_BUFFER_MIN
};
STATIC void scanner_on_ble_evt(ble_evt_t *ble_evt, void *scanner_in) {
bleio_scanner_obj_t *scanner = (bleio_scanner_obj_t*)scanner_in;
ble_gap_evt_adv_report_t *report = &ble_evt->evt.gap_evt.params.adv_report;
if (ble_evt->header.evt_id != BLE_GAP_EVT_ADV_REPORT) {
return;
}
bleio_scanentry_obj_t *entry = m_new_obj(bleio_scanentry_obj_t);
entry->base.type = &bleio_scanentry_type;
entry->rssi = report->rssi;
bleio_address_obj_t *address = m_new_obj(bleio_address_obj_t);
address->base.type = &bleio_address_type;
common_hal_bleio_address_construct(MP_OBJ_TO_PTR(address),
report->peer_addr.addr, report->peer_addr.addr_type);
entry->address = address;
entry->data = mp_obj_new_bytes(report->data.p_data, report->data.len);
mp_obj_list_append(scanner->scan_entries, MP_OBJ_FROM_PTR(entry));
const uint32_t err_code = sd_ble_gap_scan_start(NULL, &m_scan_buffer);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to continue scanning, err 0x%04x"), err_code);
}
}
void common_hal_bleio_scanner_construct(bleio_scanner_obj_t *self) {
}
mp_obj_t common_hal_bleio_scanner_scan(bleio_scanner_obj_t *self, mp_float_t timeout, mp_float_t interval, mp_float_t window) {
common_hal_bleio_adapter_set_enabled(true);
ble_drv_add_event_handler(scanner_on_ble_evt, self);
ble_gap_scan_params_t scan_params = {
.interval = SEC_TO_UNITS(interval, UNIT_0_625_MS),
.window = SEC_TO_UNITS(window, UNIT_0_625_MS),
.scan_phys = BLE_GAP_PHY_1MBPS,
};
self->scan_entries = mp_obj_new_list(0, NULL);
uint32_t err_code;
err_code = sd_ble_gap_scan_start(&scan_params, &m_scan_buffer);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to start scanning, err 0x%04x"), err_code);
}
mp_hal_delay_ms(timeout * 1000);
sd_ble_gap_scan_stop();
// Return list, and don't hang on to it, so it can be GC'd.
mp_obj_t entries = self->scan_entries;
self->scan_entries = MP_OBJ_NULL;
return entries;
}

View File

@ -31,17 +31,44 @@
#include "common-hal/_bleio/__init__.h"
#include "shared-bindings/_bleio/Characteristic.h"
#include "shared-bindings/_bleio/Descriptor.h"
#include "shared-bindings/_bleio/Peripheral.h"
#include "shared-bindings/_bleio/Service.h"
#include "shared-bindings/_bleio/Adapter.h"
void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_peripheral_obj_t *peripheral, bleio_uuid_obj_t *uuid, bool is_secondary) {
self->device = MP_OBJ_FROM_PTR(peripheral);
uint32_t _common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary, mp_obj_list_t * characteristic_list) {
self->handle = 0xFFFF;
self->uuid = uuid;
self->characteristic_list = mp_obj_new_list(0, NULL);
self->characteristic_list = characteristic_list;
self->is_remote = false;
self->connection = NULL;
self->is_secondary = is_secondary;
ble_uuid_t nordic_uuid;
bleio_uuid_convert_to_nrf_ble_uuid(uuid, &nordic_uuid);
uint8_t service_type = BLE_GATTS_SRVC_TYPE_PRIMARY;
if (is_secondary) {
service_type = BLE_GATTS_SRVC_TYPE_SECONDARY;
}
vm_used_ble = true;
return sd_ble_gatts_service_add(service_type, &nordic_uuid, &self->handle);
}
void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary) {
const uint32_t err_code = _common_hal_bleio_service_construct(self, uuid, is_secondary, mp_obj_new_list(0, NULL));
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to create service, NRF_ERROR_%q"), MP_OBJ_QSTR_VALUE(base_error_messages[err_code - NRF_ERROR_BASE_NUM]));
}
}
void bleio_service_from_connection(bleio_service_obj_t *self, mp_obj_t connection) {
self->handle = 0xFFFF;
self->uuid = NULL;
self->characteristic_list = mp_obj_new_list(0, NULL);
self->is_remote = true;
self->is_secondary = false;
self->connection = connection;
}
bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self) {
@ -60,7 +87,9 @@ bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self) {
return self->is_secondary;
}
void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic) {
void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self,
bleio_characteristic_obj_t *characteristic,
mp_buffer_info_t *initial_value_bufinfo) {
ble_gatts_char_md_t char_md = {
.char_props.broadcast = (characteristic->props & CHAR_PROP_BROADCAST) ? 1 : 0,
.char_props.read = (characteristic->props & CHAR_PROP_READ) ? 1 : 0,
@ -93,14 +122,11 @@ void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, blei
bleio_attribute_gatts_set_security_mode(&char_attr_md.read_perm, characteristic->read_perm);
bleio_attribute_gatts_set_security_mode(&char_attr_md.write_perm, characteristic->write_perm);
mp_buffer_info_t char_value_bufinfo;
mp_get_buffer_raise(characteristic->value, &char_value_bufinfo, MP_BUFFER_READ);
ble_gatts_attr_t char_attr = {
.p_uuid = &char_uuid,
.p_attr_md = &char_attr_md,
.init_len = char_value_bufinfo.len,
.p_value = char_value_bufinfo.buf,
.init_len = 0,
.p_value = NULL,
.init_offs = 0,
.max_len = characteristic->max_length,
};
@ -110,7 +136,7 @@ void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, blei
uint32_t err_code;
err_code = sd_ble_gatts_characteristic_add(self->handle, &char_md, &char_attr, &char_handles);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to add characteristic, err 0x%04x"), err_code);
mp_raise_OSError_msg_varg(translate("Failed to add characteristic, NRF_ERROR_%q"), MP_OBJ_QSTR_VALUE(base_error_messages[err_code - NRF_ERROR_BASE_NUM]));
}
characteristic->user_desc_handle = char_handles.user_desc_handle;

View File

@ -31,20 +31,22 @@
#include "py/objlist.h"
#include "common-hal/_bleio/UUID.h"
typedef struct {
typedef struct bleio_service_obj {
mp_obj_base_t base;
// Handle for this service.
// Handle for the local service.
uint16_t handle;
// True if created during discovery.
bool is_remote;
bool is_secondary;
bleio_uuid_obj_t *uuid;
// May be a Peripheral, Central, etc.
mp_obj_t device;
mp_obj_t connection;
mp_obj_list_t *characteristic_list;
// Range of attribute handles of this service.
// Range of attribute handles of this remote service.
uint16_t start_handle;
uint16_t end_handle;
struct bleio_service_obj* next;
} bleio_service_obj_t;
void bleio_service_from_connection(bleio_service_obj_t *self, mp_obj_t connection);
#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_SERVICE_H

View File

@ -30,6 +30,7 @@
#include "py/runtime.h"
#include "common-hal/_bleio/UUID.h"
#include "shared-bindings/_bleio/__init__.h"
#include "shared-bindings/_bleio/Adapter.h"
#include "ble.h"
@ -39,9 +40,7 @@
// If uuid128 is NULL, this is a Bluetooth SIG 16-bit UUID.
// If uuid128 is not NULL, it's a 128-bit (16-byte) UUID, with bytes 12 and 13 zero'd out, where
// the 16-bit part goes. Those 16 bits are passed in uuid16.
void common_hal_bleio_uuid_construct(bleio_uuid_obj_t *self, uint32_t uuid16, uint8_t uuid128[]) {
common_hal_bleio_adapter_set_enabled(true);
void common_hal_bleio_uuid_construct(bleio_uuid_obj_t *self, uint32_t uuid16, const uint8_t uuid128[]) {
self->nrf_ble_uuid.uuid = uuid16;
if (uuid128 == NULL) {
self->nrf_ble_uuid.type = BLE_UUID_TYPE_BLE;
@ -54,6 +53,7 @@ void common_hal_bleio_uuid_construct(bleio_uuid_obj_t *self, uint32_t uuid16, ui
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to register Vendor-Specific UUID, err 0x%04x"), err_code);
}
vm_used_ble = true;
}
}
@ -65,25 +65,24 @@ uint32_t common_hal_bleio_uuid_get_uuid16(bleio_uuid_obj_t *self) {
return self->nrf_ble_uuid.uuid;
}
// True if uuid128 has been successfully filled in.
bool common_hal_bleio_uuid_get_uuid128(bleio_uuid_obj_t *self, uint8_t uuid128[16]) {
void common_hal_bleio_uuid_get_uuid128(bleio_uuid_obj_t *self, uint8_t uuid128[16]) {
uint8_t length;
const uint32_t err_code = sd_ble_uuid_encode(&self->nrf_ble_uuid, &length, uuid128);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Could not decode ble_uuid, err 0x%04x"), err_code);
}
// If not 16 bytes, this is not a 128-bit UUID, so return.
return length == 16;
}
// Returns 0 if this is a 16-bit UUID, otherwise returns a non-zero index
// into the 128-bit uuid registration table.
uint32_t common_hal_bleio_uuid_get_uuid128_reference(bleio_uuid_obj_t *self) {
return self->nrf_ble_uuid.type == BLE_UUID_TYPE_BLE ? 0 : self->nrf_ble_uuid.type;
void common_hal_bleio_uuid_pack_into(bleio_uuid_obj_t *self, uint8_t* buf) {
if (self->nrf_ble_uuid.type == BLE_UUID_TYPE_BLE) {
buf[0] = self->nrf_ble_uuid.uuid & 0xff;
buf[1] = self->nrf_ble_uuid.uuid >> 8;
} else {
common_hal_bleio_uuid_get_uuid128(self, buf);
}
}
void bleio_uuid_construct_from_nrf_ble_uuid(bleio_uuid_obj_t *self, ble_uuid_t *nrf_ble_uuid) {
if (nrf_ble_uuid->type == BLE_UUID_TYPE_UNKNOWN) {
mp_raise_RuntimeError(translate("Unexpected nrfx uuid type"));

View File

@ -26,34 +26,58 @@
* THE SOFTWARE.
*/
#include <string.h>
#include "py/runtime.h"
#include "shared-bindings/_bleio/__init__.h"
#include "shared-bindings/_bleio/Adapter.h"
#include "shared-bindings/_bleio/Central.h"
#include "shared-bindings/_bleio/Characteristic.h"
#include "shared-bindings/_bleio/Connection.h"
#include "shared-bindings/_bleio/Descriptor.h"
#include "shared-bindings/_bleio/Peripheral.h"
#include "shared-bindings/_bleio/Service.h"
#include "shared-bindings/_bleio/UUID.h"
#include "supervisor/shared/bluetooth.h"
#include "common-hal/_bleio/__init__.h"
static volatile bool m_discovery_in_process;
static volatile bool m_discovery_successful;
static bleio_service_obj_t *m_char_discovery_service;
static bleio_characteristic_obj_t *m_desc_discovery_characteristic;
const mp_obj_t base_error_messages[20] = {
MP_ROM_QSTR(MP_QSTR_SUCCESS),
MP_ROM_QSTR(MP_QSTR_SVC_HANDLER_MISSING),
MP_ROM_QSTR(MP_QSTR_SOFTDEVICE_NOT_ENABLED),
MP_ROM_QSTR(MP_QSTR_INTERNAL),
MP_ROM_QSTR(MP_QSTR_NO_MEM),
MP_ROM_QSTR(MP_QSTR_NOT_FOUND),
MP_ROM_QSTR(MP_QSTR_NOT_SUPPORTED),
MP_ROM_QSTR(MP_QSTR_INVALID_PARAM),
MP_ROM_QSTR(MP_QSTR_INVALID_STATE),
MP_ROM_QSTR(MP_QSTR_INVALID_LENGTH),
MP_ROM_QSTR(MP_QSTR_INVALID_FLAGS),
MP_ROM_QSTR(MP_QSTR_INVALID_DATA),
MP_ROM_QSTR(MP_QSTR_DATA_SIZE),
MP_ROM_QSTR(MP_QSTR_TIMEOUT),
MP_ROM_QSTR(MP_QSTR_NULL),
MP_ROM_QSTR(MP_QSTR_FORBIDDEN),
MP_ROM_QSTR(MP_QSTR_INVALID_ADDR),
MP_ROM_QSTR(MP_QSTR_BUSY),
MP_ROM_QSTR(MP_QSTR_CONN_COUNT),
MP_ROM_QSTR(MP_QSTR_RESOURCES),
};
// 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);
bleio_adapter_reset(&common_hal_bleio_adapter_obj);
if (!vm_used_ble) {
return;
}
if (common_hal_bleio_adapter_get_enabled(&common_hal_bleio_adapter_obj)) {
common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, false);
}
supervisor_start_bluetooth();
}
// 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 = {
bleio_adapter_obj_t common_hal_bleio_adapter_obj = {
.base = {
.type = &bleio_adapter_type,
},
@ -65,395 +89,22 @@ void common_hal_bleio_check_connected(uint16_t conn_handle) {
}
}
uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device) {
if (MP_OBJ_IS_TYPE(device, &bleio_peripheral_type)) {
return ((bleio_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle;
} else if (MP_OBJ_IS_TYPE(device, &bleio_central_type)) {
return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle;
} else {
return BLE_CONN_HANDLE_INVALID;
}
}
mp_obj_list_t *common_hal_bleio_device_get_remote_service_list(mp_obj_t device) {
if (MP_OBJ_IS_TYPE(device, &bleio_peripheral_type)) {
return ((bleio_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->remote_service_list;
} else if (MP_OBJ_IS_TYPE(device, &bleio_central_type)) {
return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->remote_service_list;
} else {
return NULL;
}
}
// service_uuid may be NULL, to discover all services.
STATIC bool discover_next_services(mp_obj_t device, uint16_t start_handle, ble_uuid_t *service_uuid) {
m_discovery_successful = false;
m_discovery_in_process = true;
uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(device);
uint32_t err_code = sd_ble_gattc_primary_services_discover(conn_handle, start_handle, service_uuid);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg(translate("Failed to discover services"));
}
// Wait for a discovery event.
while (m_discovery_in_process) {
MICROPY_VM_HOOK_LOOP;
}
return m_discovery_successful;
}
STATIC bool discover_next_characteristics(mp_obj_t device, bleio_service_obj_t *service, uint16_t start_handle) {
m_char_discovery_service = service;
ble_gattc_handle_range_t handle_range;
handle_range.start_handle = start_handle;
handle_range.end_handle = service->end_handle;
m_discovery_successful = false;
m_discovery_in_process = true;
uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(device);
uint32_t err_code = sd_ble_gattc_characteristics_discover(conn_handle, &handle_range);
if (err_code != NRF_SUCCESS) {
return false;
}
// Wait for a discovery event.
while (m_discovery_in_process) {
MICROPY_VM_HOOK_LOOP;
}
return m_discovery_successful;
}
STATIC bool discover_next_descriptors(mp_obj_t device, bleio_characteristic_obj_t *characteristic, uint16_t start_handle, uint16_t end_handle) {
m_desc_discovery_characteristic = characteristic;
ble_gattc_handle_range_t handle_range;
handle_range.start_handle = start_handle;
handle_range.end_handle = end_handle;
m_discovery_successful = false;
m_discovery_in_process = true;
uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(device);
uint32_t err_code = sd_ble_gattc_descriptors_discover(conn_handle, &handle_range);
if (err_code != NRF_SUCCESS) {
return false;
}
// Wait for a discovery event.
while (m_discovery_in_process) {
MICROPY_VM_HOOK_LOOP;
}
return m_discovery_successful;
}
STATIC void on_primary_srv_discovery_rsp(ble_gattc_evt_prim_srvc_disc_rsp_t *response, mp_obj_t device) {
for (size_t i = 0; i < response->count; ++i) {
ble_gattc_service_t *gattc_service = &response->services[i];
bleio_service_obj_t *service = m_new_obj(bleio_service_obj_t);
service->base.type = &bleio_service_type;
// Initialize several fields at once.
common_hal_bleio_service_construct(service, device, NULL, false);
service->is_remote = true;
service->start_handle = gattc_service->handle_range.start_handle;
service->end_handle = gattc_service->handle_range.end_handle;
service->handle = gattc_service->handle_range.start_handle;
if (gattc_service->uuid.type != BLE_UUID_TYPE_UNKNOWN) {
// Known service UUID.
bleio_uuid_obj_t *uuid = m_new_obj(bleio_uuid_obj_t);
uuid->base.type = &bleio_uuid_type;
bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_service->uuid);
service->uuid = uuid;
} else {
// The discovery response contained a 128-bit UUID that has not yet been registered with the
// softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it.
// For now, just set the UUID to NULL.
service->uuid = NULL;
}
mp_obj_list_append(common_hal_bleio_device_get_remote_service_list(device), service);
}
if (response->count > 0) {
m_discovery_successful = true;
}
m_discovery_in_process = false;
}
STATIC void on_char_discovery_rsp(ble_gattc_evt_char_disc_rsp_t *response, mp_obj_t device) {
for (size_t i = 0; i < response->count; ++i) {
ble_gattc_char_t *gattc_char = &response->chars[i];
bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t);
characteristic->base.type = &bleio_characteristic_type;
bleio_uuid_obj_t *uuid = NULL;
if (gattc_char->uuid.type != BLE_UUID_TYPE_UNKNOWN) {
// Known characteristic UUID.
uuid = m_new_obj(bleio_uuid_obj_t);
uuid->base.type = &bleio_uuid_type;
bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_char->uuid);
} else {
// The discovery response contained a 128-bit UUID that has not yet been registered with the
// softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it.
// For now, just leave the UUID as NULL.
}
bleio_characteristic_properties_t props =
(gattc_char->char_props.broadcast ? CHAR_PROP_BROADCAST : 0) |
(gattc_char->char_props.indicate ? CHAR_PROP_INDICATE : 0) |
(gattc_char->char_props.notify ? CHAR_PROP_NOTIFY : 0) |
(gattc_char->char_props.read ? CHAR_PROP_READ : 0) |
(gattc_char->char_props.write ? CHAR_PROP_WRITE : 0) |
(gattc_char->char_props.write_wo_resp ? CHAR_PROP_WRITE_NO_RESPONSE : 0);
// Call common_hal_bleio_characteristic_construct() to initalize some fields and set up evt handler.
common_hal_bleio_characteristic_construct(
characteristic, m_char_discovery_service, uuid, props, SECURITY_MODE_OPEN, SECURITY_MODE_OPEN,
GATT_MAX_DATA_LENGTH, false, // max_length, fixed_length: values may not matter for gattc
mp_obj_new_list(0, NULL));
characteristic->handle = gattc_char->handle_value;
mp_obj_list_append(m_char_discovery_service->characteristic_list, MP_OBJ_FROM_PTR(characteristic));
}
if (response->count > 0) {
m_discovery_successful = true;
}
m_discovery_in_process = false;
}
STATIC void on_desc_discovery_rsp(ble_gattc_evt_desc_disc_rsp_t *response, mp_obj_t device) {
for (size_t i = 0; i < response->count; ++i) {
ble_gattc_desc_t *gattc_desc = &response->descs[i];
// Remember handles for certain well-known descriptors.
switch (gattc_desc->uuid.uuid) {
case BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG:
m_desc_discovery_characteristic->cccd_handle = gattc_desc->handle;
break;
case BLE_UUID_DESCRIPTOR_SERVER_CHAR_CONFIG:
m_desc_discovery_characteristic->sccd_handle = gattc_desc->handle;
break;
case BLE_UUID_DESCRIPTOR_CHAR_USER_DESC:
m_desc_discovery_characteristic->user_desc_handle = gattc_desc->handle;
break;
default:
// TODO: sd_ble_gattc_descriptors_discover() can return things that are not descriptors,
// so ignore those.
// https://devzone.nordicsemi.com/f/nordic-q-a/49500/sd_ble_gattc_descriptors_discover-is-returning-attributes-that-are-not-descriptors
break;
}
bleio_descriptor_obj_t *descriptor = m_new_obj(bleio_descriptor_obj_t);
descriptor->base.type = &bleio_descriptor_type;
bleio_uuid_obj_t *uuid = NULL;
if (gattc_desc->uuid.type != BLE_UUID_TYPE_UNKNOWN) {
// Known descriptor UUID.
uuid = m_new_obj(bleio_uuid_obj_t);
uuid->base.type = &bleio_uuid_type;
bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_desc->uuid);
} else {
// The discovery response contained a 128-bit UUID that has not yet been registered with the
// softdevice via sd_ble_uuid_vs_add(). We need to fetch the 128-bit value and register it.
// For now, just leave the UUID as NULL.
}
common_hal_bleio_descriptor_construct(
descriptor, m_desc_discovery_characteristic, uuid,
SECURITY_MODE_OPEN, SECURITY_MODE_OPEN,
GATT_MAX_DATA_LENGTH, false, mp_const_empty_bytes);
descriptor->handle = gattc_desc->handle;
mp_obj_list_append(m_desc_discovery_characteristic->descriptor_list, MP_OBJ_FROM_PTR(descriptor));
}
if (response->count > 0) {
m_discovery_successful = true;
}
m_discovery_in_process = false;
}
STATIC void discovery_on_ble_evt(ble_evt_t *ble_evt, mp_obj_t device) {
switch (ble_evt->header.evt_id) {
case BLE_GAP_EVT_DISCONNECTED:
m_discovery_successful = false;
m_discovery_in_process = false;
break;
case BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP:
on_primary_srv_discovery_rsp(&ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp, device);
break;
case BLE_GATTC_EVT_CHAR_DISC_RSP:
on_char_discovery_rsp(&ble_evt->evt.gattc_evt.params.char_disc_rsp, device);
break;
case BLE_GATTC_EVT_DESC_DISC_RSP:
on_desc_discovery_rsp(&ble_evt->evt.gattc_evt.params.desc_disc_rsp, device);
break;
default:
// For debugging.
// mp_printf(&mp_plat_print, "Unhandled discovery event: 0x%04x\n", ble_evt->header.evt_id);
break;
}
}
void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist) {
mp_obj_list_t *remote_services_list = common_hal_bleio_device_get_remote_service_list(device);
ble_drv_add_event_handler(discovery_on_ble_evt, device);
// Start over with an empty list.
mp_obj_list_clear(MP_OBJ_FROM_PTR(common_hal_bleio_device_get_remote_service_list(device)));
if (service_uuids_whitelist == mp_const_none) {
// List of service UUID's not given, so discover all available services.
uint16_t next_service_start_handle = BLE_GATT_HANDLE_START;
while (discover_next_services(device, next_service_start_handle, MP_OBJ_NULL)) {
// discover_next_services() appends to remote_services_list.
// Get the most recently discovered service, and then ask for services
// whose handles start after the last attribute handle inside that service.
const bleio_service_obj_t *service =
MP_OBJ_TO_PTR(remote_services_list->items[remote_services_list->len - 1]);
next_service_start_handle = service->end_handle + 1;
}
} else {
mp_obj_iter_buf_t iter_buf;
mp_obj_t iterable = mp_getiter(service_uuids_whitelist, &iter_buf);
mp_obj_t uuid_obj;
while ((uuid_obj = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) {
mp_raise_ValueError(translate("non-UUID found in service_uuids_whitelist"));
}
bleio_uuid_obj_t *uuid = MP_OBJ_TO_PTR(uuid_obj);
ble_uuid_t nrf_uuid;
bleio_uuid_convert_to_nrf_ble_uuid(uuid, &nrf_uuid);
// Service might or might not be discovered; that's ok. Caller has to check
// Central.remote_services to find out.
// We only need to call this once for each service to discover.
discover_next_services(device, BLE_GATT_HANDLE_START, &nrf_uuid);
}
}
for (size_t service_idx = 0; service_idx < remote_services_list->len; ++service_idx) {
bleio_service_obj_t *service = MP_OBJ_TO_PTR(remote_services_list->items[service_idx]);
// Skip the service if it had an unknown (unregistered) UUID.
if (service->uuid == NULL) {
continue;
}
uint16_t next_char_start_handle = service->start_handle;
// Stop when we go past the end of the range of handles for this service or
// discovery call returns nothing.
// discover_next_characteristics() appends to the characteristic_list.
while (next_char_start_handle <= service->end_handle &&
discover_next_characteristics(device, service, next_char_start_handle)) {
// Get the most recently discovered characteristic, and then ask for characteristics
// whose handles start after the last attribute handle inside that characteristic.
const bleio_characteristic_obj_t *characteristic =
MP_OBJ_TO_PTR(service->characteristic_list->items[service->characteristic_list->len - 1]);
next_char_start_handle = characteristic->handle + 1;
}
// Got characteristics for this service. Now discover descriptors for each characteristic.
size_t char_list_len = service->characteristic_list->len;
for (size_t char_idx = 0; char_idx < char_list_len; ++char_idx) {
bleio_characteristic_obj_t *characteristic =
MP_OBJ_TO_PTR(service->characteristic_list->items[char_idx]);
const bool last_characteristic = char_idx == char_list_len - 1;
bleio_characteristic_obj_t *next_characteristic = last_characteristic
? NULL
: MP_OBJ_TO_PTR(service->characteristic_list->items[char_idx + 1]);
// Skip the characteristic if it had an unknown (unregistered) UUID.
if (characteristic->uuid == NULL) {
continue;
}
uint16_t next_desc_start_handle = characteristic->handle + 1;
// Don't run past the end of this service or the beginning of the next characteristic.
uint16_t next_desc_end_handle = next_characteristic == NULL
? service->end_handle
: next_characteristic->handle - 1;
// Stop when we go past the end of the range of handles for this service or
// discovery call returns nothing.
// discover_next_descriptors() appends to the descriptor_list.
while (next_desc_start_handle <= service->end_handle &&
next_desc_start_handle < next_desc_end_handle &&
discover_next_descriptors(device, characteristic,
next_desc_start_handle, next_desc_end_handle)) {
// Get the most recently discovered descriptor, and then ask for descriptors
// whose handles start after that descriptor's handle.
const bleio_descriptor_obj_t *descriptor =
MP_OBJ_TO_PTR(characteristic->descriptor_list->items[characteristic->descriptor_list->len - 1]);
next_desc_start_handle = descriptor->handle + 1;
}
}
}
// This event handler is no longer needed.
ble_drv_remove_event_handler(discovery_on_ble_evt, device);
}
// GATTS read of a Characteristic or Descriptor.
mp_obj_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle) {
size_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle, uint8_t* buf, size_t len) {
// conn_handle might be BLE_CONN_HANDLE_INVALID if we're not connected, but that's OK, because
// we can still read and write the local value.
mp_buffer_info_t bufinfo;
ble_gatts_value_t gatts_value = {
.p_value = NULL,
.len = 0,
.p_value = buf,
.len = len,
};
// Read once to find out what size buffer we need, then read again to fill buffer.
mp_obj_t value = mp_const_none;
uint32_t err_code = sd_ble_gatts_value_get(conn_handle, handle, &gatts_value);
if (err_code == NRF_SUCCESS) {
value = mp_obj_new_bytearray_of_zeros(gatts_value.len);
mp_get_buffer_raise(value, &bufinfo, MP_BUFFER_WRITE);
gatts_value.p_value = bufinfo.buf;
// Read again, with the correct size of buffer.
err_code = sd_ble_gatts_value_get(conn_handle, handle, &gatts_value);
}
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to read gatts value, err 0x%04x"), err_code);
}
return value;
return gatts_value.len;
}
void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo) {
@ -471,6 +122,72 @@ void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buff
}
}
typedef struct {
uint8_t* buf;
size_t len;
size_t final_len;
uint16_t conn_handle;
volatile uint16_t status;
volatile bool done;
} read_info_t;
STATIC bool _on_gattc_read_rsp_evt(ble_evt_t *ble_evt, void *param) {
read_info_t* read = param;
switch (ble_evt->header.evt_id) {
// More events may be handled later, so keep this as a switch.
case BLE_GATTC_EVT_READ_RSP: {
ble_gattc_evt_t* evt = &ble_evt->evt.gattc_evt;
ble_gattc_evt_read_rsp_t *response = &evt->params.read_rsp;
if (read && evt->conn_handle == read->conn_handle) {
read->status = evt->gatt_status;
size_t len = MIN(read->len, response->len);
memcpy(read->buf, response->data, len);
read->final_len = len;
// Indicate to busy-wait loop that we've read the attribute value.
read->done = true;
}
break;
}
default:
// For debugging.
// mp_printf(&mp_plat_print, "Unhandled characteristic event: 0x%04x\n", ble_evt->header.evt_id);
return false;
break;
}
return true;
}
size_t common_hal_bleio_gattc_read(uint16_t handle, uint16_t conn_handle, uint8_t* buf, size_t len) {
common_hal_bleio_check_connected(conn_handle);
read_info_t read_info;
read_info.buf = buf;
read_info.len = len;
read_info.final_len = 0;
read_info.conn_handle = conn_handle;
// Set to true by the event handler.
read_info.done = false;
ble_drv_add_event_handler(_on_gattc_read_rsp_evt, &read_info);
const uint32_t err_code = sd_ble_gattc_read(conn_handle, handle, 0);
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed initiate attribute read, err 0x%04x"), err_code);
}
while (!read_info.done) {
RUN_BACKGROUND_TASKS;
}
if (read_info.status != BLE_GATT_STATUS_SUCCESS) {
mp_raise_OSError_msg_varg(translate("Failed to read attribute value, err 0x%04x"), read_info.status);
}
ble_drv_remove_event_handler(_on_gattc_read_rsp_evt, &read_info);
return read_info.final_len;
}
void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response) {
common_hal_bleio_check_connected(conn_handle);
@ -500,3 +217,7 @@ void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buff
}
}
void common_hal_bleio_gc_collect(void) {
bleio_adapter_gc_collect(&common_hal_bleio_adapter_obj);
}

View File

@ -39,4 +39,9 @@ typedef struct {
// 20 bytes max (23 - 3).
#define GATT_MAX_DATA_LENGTH (BLE_GATT_ATT_MTU_DEFAULT - 3)
const mp_obj_t base_error_messages[20];
// Track if the user code modified the BLE state to know if we need to undo it on reload.
bool vm_used_ble;
#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_INIT_H

View File

@ -229,12 +229,10 @@ SRC_COMMON_HAL_ALL = \
_bleio/__init__.c \
_bleio/Adapter.c \
_bleio/Attribute.c \
_bleio/Central.c \
_bleio/Characteristic.c \
_bleio/CharacteristicBuffer.c \
_bleio/Connection.c \
_bleio/Descriptor.c \
_bleio/Peripheral.c \
_bleio/Scanner.c \
_bleio/Service.c \
_bleio/UUID.c \
analogio/AnalogIn.c \
@ -307,6 +305,7 @@ SRC_SHARED_MODULE_ALL = \
_bleio/Address.c \
_bleio/Attribute.c \
_bleio/ScanEntry.c \
_bleio/ScanResults.c \
_pixelbuf/PixelBuf.c \
_pixelbuf/__init__.c \
_stage/Layer.c \

View File

@ -99,4 +99,11 @@ static inline void ringbuf_put_n(ringbuf_t* r, uint8_t* buf, uint8_t bufsize)
}
}
}
static inline void ringbuf_get_n(ringbuf_t* r, uint8_t* buf, uint8_t bufsize)
{
for(uint8_t i=0; i < bufsize; i++) {
buf[i] = ringbuf_get(r);
}
}
#endif // MICROPY_INCLUDED_PY_RINGBUF_H

View File

@ -758,7 +758,7 @@ unwind_jump:;
} else {
PUSH(value); // push the next iteration value
}
DISPATCH();
DISPATCH_WITH_PEND_EXC_CHECK();
}
// matched against: SETUP_EXCEPT, SETUP_FINALLY, SETUP_WITH

View File

@ -25,22 +25,45 @@
* THE SOFTWARE.
*/
#include <string.h>
#include "py/objproperty.h"
#include "py/runtime.h"
#include "shared-bindings/_bleio/Address.h"
#include "shared-bindings/_bleio/Adapter.h"
#define ADV_INTERVAL_MIN (0.0020f)
#define ADV_INTERVAL_MIN_STRING "0.0020"
#define ADV_INTERVAL_MAX (10.24f)
#define ADV_INTERVAL_MAX_STRING "10.24"
// 20ms is recommended by Apple
#define ADV_INTERVAL_DEFAULT (0.1f)
#define INTERVAL_DEFAULT (0.1f)
#define INTERVAL_MIN (0.0025f)
#define INTERVAL_MIN_STRING "0.0025"
#define INTERVAL_MAX (40.959375f)
#define INTERVAL_MAX_STRING "40.959375"
#define WINDOW_DEFAULT (0.1f)
//| .. currentmodule:: _bleio
//|
//| :class:`Adapter` --- BLE adapter information
//| :class:`Adapter` --- BLE adapter
//| ----------------------------------------------------
//|
//| Get current status of the BLE adapter
//| The Adapter manages the discovery and connection to other nearby Bluetooth Low Energy devices.
//| This part of the Bluetooth Low Energy Specification is known as Generic Access Profile (GAP).
//|
//| Usage::
//| Discovery of other devices happens during a scanning process that listens for small packets of
//| information, known as advertisements, that are broadcast unencrypted. The advertising packets
//| have two different uses. The first is to broadcast a small piece of data to anyone who cares and
//| and nothing more. These are known as Beacons. The second class of advertisement is to promote
//| additional functionality available after the devices establish a connection. For example, a
//| BLE keyboard may advertise that it can provide key information, but not what the key info is.
//|
//| import _bleio
//| _bleio.adapter.enabled = True
//| print(_bleio.adapter.address)
//| The built-in BLE adapter can do both parts of this process, it can scan for other device
//| advertisements and it can advertise it's own data. Furthermore, Adapters can accept incoming
//| connections and also initiate connections.
//|
//| .. class:: Adapter()
@ -49,19 +72,19 @@
//| Use `_bleio.adapter` to access the sole instance available.
//|
//| .. attribute:: adapter.enabled
//| .. attribute:: enabled
//|
//| State of the BLE adapter.
//| State of the BLE adapter.
//|
STATIC mp_obj_t bleio_adapter_get_enabled(mp_obj_t self) {
return mp_obj_new_bool(common_hal_bleio_adapter_get_enabled());
return mp_obj_new_bool(common_hal_bleio_adapter_get_enabled(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_enabled_obj, bleio_adapter_get_enabled);
static mp_obj_t bleio_adapter_set_enabled(mp_obj_t self, mp_obj_t value) {
const bool enabled = mp_obj_is_true(value);
common_hal_bleio_adapter_set_enabled(enabled);
common_hal_bleio_adapter_set_enabled(self, enabled);
return mp_const_none;
}
@ -74,12 +97,12 @@ const mp_obj_property_t bleio_adapter_enabled_obj = {
(mp_obj_t)&mp_const_none_obj },
};
//| .. attribute:: adapter.address
//| .. attribute:: address
//|
//| MAC address of the BLE adapter. (read-only)
//| MAC address of the BLE adapter. (read-only)
//|
STATIC mp_obj_t bleio_adapter_get_address(mp_obj_t self) {
return MP_OBJ_FROM_PTR(common_hal_bleio_adapter_get_address());
return MP_OBJ_FROM_PTR(common_hal_bleio_adapter_get_address(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_address_obj, bleio_adapter_get_address);
@ -91,29 +114,260 @@ const mp_obj_property_t bleio_adapter_address_obj = {
(mp_obj_t)&mp_const_none_obj },
};
//| .. attribute:: adapter.default_name
//| .. attribute:: name
//|
//| default_name of the BLE adapter. (read-only)
//| The name is "CIRCUITPY" + the last four hex digits of ``adapter.address``,
//| to make it easy to distinguish multiple CircuitPython boards.
//| name of the BLE adapter used once connected. Not used in advertisements.
//| The name is "CIRCUITPY" + the last four hex digits of ``adapter.address``,
//| to make it easy to distinguish multiple CircuitPython boards.
//|
STATIC mp_obj_t bleio_adapter_get_default_name(mp_obj_t self) {
return common_hal_bleio_adapter_get_default_name();
STATIC mp_obj_t bleio_adapter_get_name(mp_obj_t self) {
return MP_OBJ_FROM_PTR(common_hal_bleio_adapter_get_name(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_name_obj, bleio_adapter_get_name);
STATIC mp_obj_t bleio_adapter_set_name(mp_obj_t self, mp_obj_t new_name) {
common_hal_bleio_adapter_set_name(self, mp_obj_str_get_str(new_name));
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(bleio_adapter_set_name_obj, bleio_adapter_set_name);
const mp_obj_property_t bleio_adapter_name_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_adapter_get_name_obj,
(mp_obj_t)&bleio_adapter_set_name_obj,
(mp_obj_t)&mp_const_none_obj },
};
//| .. method:: start_advertising(data, *, scan_response=None, connectable=True, interval=0.1)
//|
//| Starts advertising until `stop_advertising` is called or if connectable, another device
//| connects to us.
//|
//| :param buf data: advertising data packet bytes
//| :param buf scan_response: scan response data packet bytes. ``None`` if no scan response is needed.
//| :param bool connectable: If `True` then other devices are allowed to connect to this peripheral.
//| :param float interval: advertising interval, in seconds
//|
STATIC mp_obj_t bleio_adapter_start_advertising(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
bleio_adapter_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
enum { ARG_data, ARG_scan_response, ARG_connectable, ARG_interval };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_scan_response, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_connectable, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
{ MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
};
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 data_bufinfo;
mp_get_buffer_raise(args[ARG_data].u_obj, &data_bufinfo, MP_BUFFER_READ);
// Pass an empty buffer if scan_response not provided.
mp_buffer_info_t scan_response_bufinfo = { 0 };
if (args[ARG_scan_response].u_obj != mp_const_none) {
mp_get_buffer_raise(args[ARG_scan_response].u_obj, &scan_response_bufinfo, MP_BUFFER_READ);
}
if (args[ARG_interval].u_obj == MP_OBJ_NULL) {
args[ARG_interval].u_obj = mp_obj_new_float(ADV_INTERVAL_DEFAULT);
}
const mp_float_t interval = mp_obj_float_get(args[ARG_interval].u_obj);
if (interval < ADV_INTERVAL_MIN || interval > ADV_INTERVAL_MAX) {
mp_raise_ValueError_varg(translate("interval must be in range %s-%s"),
ADV_INTERVAL_MIN_STRING, ADV_INTERVAL_MAX_STRING);
}
common_hal_bleio_adapter_start_advertising(self, args[ARG_connectable].u_bool, interval,
&data_bufinfo, &scan_response_bufinfo);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_adapter_start_advertising_obj, 2, bleio_adapter_start_advertising);
//| .. method:: stop_advertising()
//|
//| Stop sending advertising packets.
STATIC mp_obj_t bleio_adapter_stop_advertising(mp_obj_t self_in) {
bleio_adapter_obj_t *self = MP_OBJ_TO_PTR(self_in);
common_hal_bleio_adapter_stop_advertising(self);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_stop_advertising_obj, bleio_adapter_stop_advertising);
//| .. method:: start_scan(prefixes=b"", \*, buffer_size=512, extended=False, timeout=None, interval=0.1, window=0.1, minimum_rssi=-80)
//|
//| Starts a BLE scan and returns an iterator of results. Advertisements and scan responses are
//| filtered and returned separately.
//|
//| :param sequence prefixes: Sequence of byte string prefixes to filter advertising packets
//| with. A packets without an advertising structure that matches one of the prefixes are
//| ignored. Format is one byte for length (n) and n bytes of prefix and can be repeated.
//| :param int buffer_size: the maximum number of advertising bytes to buffer.
//| :param bool extended: When True, support extended advertising packets. Increasing buffer_size is recommended when this is set.
//| :param float timeout: the scan timeout in seconds. If None, will scan until `stop_scan` is called.
//| :param float interval: the interval (in seconds) between the start of two consecutive scan windows
//| Must be in the range 0.0025 - 40.959375 seconds.
//| :param float window: the duration (in seconds) to scan a single BLE channel.
//| window must be <= interval.
//| :param int minimum_rssi: the minimum rssi of entries to return.
//| :param bool active: retrieve scan responses for scannable advertisements.
//| :returns: an iterable of `_bleio.ScanEntry` objects
//| :rtype: iterable
//|
STATIC mp_obj_t bleio_adapter_start_scan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_prefixes, ARG_buffer_size, ARG_extended, ARG_timeout, ARG_interval, ARG_window, ARG_minimum_rssi, ARG_active };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_prefixes, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_buffer_size, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 512} },
{ MP_QSTR_extended, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_window, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_minimum_rssi, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -80} },
{ MP_QSTR_active, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
};
bleio_adapter_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
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_float_t timeout = 0;
if (args[ARG_timeout].u_obj != MP_OBJ_NULL) {
timeout = mp_obj_get_float(args[ARG_timeout].u_obj);
}
if (args[ARG_interval].u_obj == MP_OBJ_NULL) {
args[ARG_interval].u_obj = mp_obj_new_float(INTERVAL_DEFAULT);
}
if (args[ARG_window].u_obj == MP_OBJ_NULL) {
args[ARG_window].u_obj = mp_obj_new_float(WINDOW_DEFAULT);
}
const mp_float_t interval = mp_obj_float_get(args[ARG_interval].u_obj);
if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) {
mp_raise_ValueError_varg(translate("interval must be in range %s-%s"), INTERVAL_MIN_STRING, INTERVAL_MAX_STRING);
}
const mp_float_t window = mp_obj_float_get(args[ARG_window].u_obj);
if (window > interval) {
mp_raise_ValueError(translate("window must be <= interval"));
}
mp_buffer_info_t prefix_bufinfo;
prefix_bufinfo.len = 0;
if (args[ARG_prefixes].u_obj != MP_OBJ_NULL) {
mp_get_buffer_raise(args[ARG_prefixes].u_obj, &prefix_bufinfo, MP_BUFFER_READ);
if (gc_nbytes(prefix_bufinfo.buf) == 0) {
mp_raise_ValueError(translate("Prefix buffer must be on the heap"));
}
}
return common_hal_bleio_adapter_start_scan(self, prefix_bufinfo.buf, prefix_bufinfo.len, args[ARG_extended].u_bool, args[ARG_buffer_size].u_int, timeout, interval, window, args[ARG_minimum_rssi].u_int, args[ARG_active].u_bool);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_adapter_start_scan_obj, 1, bleio_adapter_start_scan);
//| .. method:: stop_scan()
//|
//| Stop the current scan.
STATIC mp_obj_t bleio_adapter_stop_scan(mp_obj_t self_in) {
bleio_adapter_obj_t *self = MP_OBJ_TO_PTR(self_in);
common_hal_bleio_adapter_stop_scan(self);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_stop_scan_obj, bleio_adapter_stop_scan);
//| .. attribute:: connected
//|
//| True when the adapter is connected to another device regardless of who initiated the
//| connection. (read-only)
//|
STATIC mp_obj_t bleio_adapter_get_connected(mp_obj_t self) {
return mp_obj_new_bool(common_hal_bleio_adapter_get_connected(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_default_name_obj, bleio_adapter_get_default_name);
MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_connected_obj, bleio_adapter_get_connected);
const mp_obj_property_t bleio_adapter_default_name_obj = {
const mp_obj_property_t bleio_adapter_connected_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_adapter_get_default_name_obj,
.proxy = { (mp_obj_t)&bleio_adapter_get_connected_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj },
};
//| .. attribute:: connections
//|
//| Tuple of active connections including those initiated through
//| :py:meth:`_bleio.Adapter.connect`. (read-only)
//|
STATIC mp_obj_t bleio_adapter_get_connections(mp_obj_t self) {
return common_hal_bleio_adapter_get_connections(self);
}
MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_connections_obj, bleio_adapter_get_connections);
const mp_obj_property_t bleio_adapter_connections_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_adapter_get_connections_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj },
};
//| .. method:: connect(address, *, timeout, pair=False)
//|
//| Attempts a connection to the device with the given address.
//|
//| :param Address address: The address of the peripheral to connect to
//| :param float/int timeout: Try to connect for timeout seconds.
//|
STATIC mp_obj_t bleio_adapter_connect(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
bleio_adapter_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
enum { ARG_address, ARG_timeout, ARG_pair };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_pair, MP_ARG_KW_ONLY | MP_ARG_BOOL, { .u_bool = false } },
};
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);
if (!MP_OBJ_IS_TYPE(args[ARG_address].u_obj, &bleio_address_type)) {
mp_raise_ValueError(translate("Expected an Address"));
}
bleio_address_obj_t *address = MP_OBJ_TO_PTR(args[ARG_address].u_obj);
mp_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj);
return common_hal_bleio_adapter_connect(self, address, timeout, args[ARG_pair].u_bool);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_adapter_connect_obj, 2, bleio_adapter_connect);
STATIC const mp_rom_map_elem_t bleio_adapter_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_enabled), MP_ROM_PTR(&bleio_adapter_enabled_obj) },
{ MP_ROM_QSTR(MP_QSTR_address), MP_ROM_PTR(&bleio_adapter_address_obj) },
{ MP_ROM_QSTR(MP_QSTR_default_name), MP_ROM_PTR(&bleio_adapter_default_name_obj) },
{ MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&bleio_adapter_name_obj) },
{ MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_adapter_start_advertising_obj) },
{ MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_adapter_stop_advertising_obj) },
{ MP_ROM_QSTR(MP_QSTR_start_scan), MP_ROM_PTR(&bleio_adapter_start_scan_obj) },
{ MP_ROM_QSTR(MP_QSTR_stop_scan), MP_ROM_PTR(&bleio_adapter_stop_scan_obj) },
{ MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&bleio_adapter_connect_obj) },
{ MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_adapter_connected_obj) },
{ MP_ROM_QSTR(MP_QSTR_connections), MP_ROM_PTR(&bleio_adapter_connections_obj) },
};
STATIC MP_DEFINE_CONST_DICT(bleio_adapter_locals_dict, bleio_adapter_locals_dict_table);

View File

@ -28,13 +28,33 @@
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADAPTER_H
#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADAPTER_H
#include <stdint.h>
#include "common-hal/_bleio/Adapter.h"
#include "py/objstr.h"
#include "shared-module/_bleio/Address.h"
const mp_obj_type_t bleio_adapter_type;
extern bool common_hal_bleio_adapter_get_enabled(void);
extern void common_hal_bleio_adapter_set_enabled(bool enabled);
extern bleio_address_obj_t *common_hal_bleio_adapter_get_address(void);
extern mp_obj_t common_hal_bleio_adapter_get_default_name(void);
extern bool common_hal_bleio_adapter_get_enabled(bleio_adapter_obj_t *self);
extern void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enabled);
extern bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self);
extern bleio_address_obj_t *common_hal_bleio_adapter_get_address(bleio_adapter_obj_t *self);
extern mp_obj_str_t* common_hal_bleio_adapter_get_name(bleio_adapter_obj_t *self);
extern void common_hal_bleio_adapter_set_name(bleio_adapter_obj_t *self, const char* name);
extern uint32_t _common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool connectable, float interval, uint8_t *advertising_data, uint16_t advertising_data_len, uint8_t *scan_response_data, uint16_t scan_response_data_len);
extern void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, bool connectable, mp_float_t interval, mp_buffer_info_t *advertising_data_bufinfo, mp_buffer_info_t *scan_response_data_bufinfo);
void common_hal_bleio_adapter_stop_advertising(bleio_adapter_obj_t *self);
mp_obj_t common_hal_bleio_adapter_start_scan(bleio_adapter_obj_t *self, uint8_t* prefixes, uint8_t prefix_length, bool extended, mp_int_t buffer_size, mp_float_t timeout, mp_float_t interval, mp_float_t window, mp_int_t minimum_rssi, bool active);
void common_hal_bleio_adapter_stop_scan(bleio_adapter_obj_t *self);
bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self);
mp_obj_t common_hal_bleio_adapter_get_connections(bleio_adapter_obj_t *self);
mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout, bool pair);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_ADAPTER_H

View File

@ -114,10 +114,11 @@ const mp_obj_property_t bleio_address_address_bytes_obj = {
};
//| .. attribute:: type
//|
//|
//| The address type (read-only).
//| One of the integer values: `PUBLIC`, `RANDOM_STATIC`,
//| `RANDOM_PRIVATE_RESOLVABLE`, or `RANDOM_PRIVATE_NON_RESOLVABLE`.
//|
//| One of the integer values: `PUBLIC`, `RANDOM_STATIC`, `RANDOM_PRIVATE_RESOLVABLE`,
//| or `RANDOM_PRIVATE_NON_RESOLVABLE`.
//|
STATIC mp_obj_t bleio_address_get_type(mp_obj_t self_in) {
bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in);
@ -159,6 +160,28 @@ STATIC mp_obj_t bleio_address_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_o
}
}
//| .. method:: __hash__()
//|
//| Returns a hash for the Address data.
//|
STATIC mp_obj_t bleio_address_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
switch (op) {
// Two Addresses are equal if their address bytes and address_type are equal
case MP_UNARY_OP_HASH: {
mp_obj_t bytes = common_hal_bleio_address_get_address_bytes(MP_OBJ_TO_PTR(self_in));
GET_STR_HASH(bytes, h);
if (h == 0) {
GET_STR_DATA_LEN(bytes, data, len);
h = qstr_compute_hash(data, len);
}
h ^= common_hal_bleio_address_get_type(MP_OBJ_TO_PTR(self_in));
return MP_OBJ_NEW_SMALL_INT(h);
}
default:
return MP_OBJ_NULL; // op not supported
}
}
STATIC void bleio_address_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
bleio_address_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_buffer_info_t buf_info;
@ -205,6 +228,7 @@ const mp_obj_type_t bleio_address_type = {
.name = MP_QSTR_Address,
.make_new = bleio_address_make_new,
.print = bleio_address_print,
.unary_op = bleio_address_unary_op,
.binary_op = bleio_address_binary_op,
.locals_dict = (mp_obj_dict_t*)&bleio_address_locals_dict
};

View File

@ -1,207 +0,0 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Dan Halbert for Adafruit Industries
* Copyright (c) 2018 Artur Pacholec
* Copyright (c) 2016 Glenn Ruben Bakke
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#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/Address.h"
#include "shared-bindings/_bleio/Characteristic.h"
#include "shared-bindings/_bleio/Central.h"
#include "shared-bindings/_bleio/Service.h"
//| .. currentmodule:: _bleio
//|
//| :class:`Central` -- A BLE central device
//| =========================================================
//|
//| Implement a BLE central, which runs locally. Can connect to a given address.
//|
//| Usage::
//|
//| import _bleio
//|
//| scanner = _bleio.Scanner()
//| entries = scanner.scan(2.5)
//|
//| my_entry = None
//| for entry in entries:
//| if entry.name is not None and entry.name == 'InterestingPeripheral':
//| my_entry = entry
//| break
//|
//| if not my_entry:
//| raise Exception("'InterestingPeripheral' not found")
//|
//| central = _bleio.Central()
//| central.connect(my_entry.address, 10) # timeout after 10 seconds
//| remote_services = central.discover_remote_services()
//|
//| .. class:: Central()
//|
//| Create a new Central object.
//|
STATIC mp_obj_t bleio_central_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
mp_arg_check_num(n_args, kw_args, 0, 0, false);
bleio_central_obj_t *self = m_new_obj(bleio_central_obj_t);
self->base.type = &bleio_central_type;
common_hal_bleio_central_construct(self);
return MP_OBJ_FROM_PTR(self);
}
//| .. method:: connect(address, timeout, *, service_uuids_whitelist=None)
//| Attempts a connection to the remote peripheral.
//|
//| :param Address address: The address of the peripheral to connect to
//| :param float/int timeout: Try to connect for timeout seconds.
//|
STATIC mp_obj_t bleio_central_connect(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
bleio_central_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
enum { ARG_address, ARG_timeout, ARG_service_uuids_whitelist };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_timeout, 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);
if (!MP_OBJ_IS_TYPE(args[ARG_address].u_obj, &bleio_address_type)) {
mp_raise_ValueError(translate("Expected an Address"));
}
bleio_address_obj_t *address = MP_OBJ_TO_PTR(args[ARG_address].u_obj);
mp_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj);
// common_hal_bleio_central_connect() will validate that services is an iterable or None.
common_hal_bleio_central_connect(self, address, timeout);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_central_connect_obj, 3, bleio_central_connect);
//| .. method:: disconnect()
//|
//| Disconnects from the remote peripheral.
//|
STATIC mp_obj_t bleio_central_disconnect(mp_obj_t self_in) {
bleio_central_obj_t *self = MP_OBJ_TO_PTR(self_in);
common_hal_bleio_central_disconnect(self);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_central_disconnect_obj, bleio_central_disconnect);
//| .. method:: discover_remote_services(service_uuids_whitelist=None)
//| Do BLE discovery for all services or for the given service UUIDS,
//| to find their handles and characteristics, and return the discovered services.
//| `Peripheral.connected` must be True.
//|
//| :param iterable service_uuids_whitelist: an iterable of :py:class:~`UUID` objects for the services
//| provided by the peripheral that you want to use.
//| The peripheral may provide more services, but services not listed are ignored
//| and will not be returned.
//|
//| If service_uuids_whitelist is None, then all services will undergo discovery, which can be slow.
//|
//| If the service UUID is 128-bit, or its characteristic UUID's are 128-bit, you
//| you must have already created a :py:class:~`UUID` object for that UUID in order for the
//| service or characteristic to be discovered. Creating the UUID causes the UUID to be registered
//| for use. (This restriction may be lifted in the future.)
//|
//| :return: A tuple of services provided by the remote peripheral.
//|
STATIC mp_obj_t bleio_central_discover_remote_services(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
bleio_central_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
enum { ARG_service_uuids_whitelist };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_service_uuids_whitelist, 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);
if (!common_hal_bleio_central_get_connected(self)) {
mp_raise_ValueError(translate("Not connected"));
}
return MP_OBJ_FROM_PTR(common_hal_bleio_central_discover_remote_services(
MP_OBJ_FROM_PTR(self),
args[ARG_service_uuids_whitelist].u_obj));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_central_discover_remote_services_obj, 1, bleio_central_discover_remote_services);
//| .. attribute:: connected
//|
//| True if connected to a remove peripheral.
//|
STATIC mp_obj_t bleio_central_get_connected(mp_obj_t self_in) {
bleio_central_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_bool(common_hal_bleio_central_get_connected(self));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_central_get_connected_obj, bleio_central_get_connected);
const mp_obj_property_t bleio_central_connected_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_central_get_connected_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj },
};
STATIC const mp_rom_map_elem_t bleio_central_locals_dict_table[] = {
// Methods
{ MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&bleio_central_connect_obj) },
{ MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_central_disconnect_obj) },
{ MP_ROM_QSTR(MP_QSTR_discover_remote_services), MP_ROM_PTR(&bleio_central_discover_remote_services_obj) },
// Properties
{ MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_central_connected_obj) },
};
STATIC MP_DEFINE_CONST_DICT(bleio_central_locals_dict, bleio_central_locals_dict_table);
const mp_obj_type_t bleio_central_type = {
{ &mp_type_type },
.name = MP_QSTR_Central,
.make_new = bleio_central_make_new,
.locals_dict = (mp_obj_dict_t*)&bleio_central_locals_dict
};

View File

@ -1,43 +0,0 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Dan Halbert for Adafruit Industries
* 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_CENTRAL_H
#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CENTRAL_H
#include "py/objtuple.h"
#include "common-hal/_bleio/Central.h"
#include "common-hal/_bleio/Service.h"
extern const mp_obj_type_t bleio_central_type;
extern void common_hal_bleio_central_construct(bleio_central_obj_t *self);
extern void common_hal_bleio_central_connect(bleio_central_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout);
extern void common_hal_bleio_central_disconnect(bleio_central_obj_t *self);
extern bool common_hal_bleio_central_get_connected(bleio_central_obj_t *self);
extern mp_obj_tuple_t *common_hal_bleio_central_discover_remote_services(bleio_central_obj_t *self, mp_obj_t service_uuids_whitelist);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CENTRAL_H

View File

@ -45,8 +45,8 @@
//|
//| There is no regular constructor for a Characteristic. A new local Characteristic can be created
//| and attached to a Service by calling `add_to_service()`.
//| Remote Characteristic objects are created by `Central.discover_remote_services()`
//| or `Peripheral.discover_remote_services()` as part of remote Services.
//| Remote Characteristic objects are created by `Connection.discover_remote_services()`
//| as part of remote Services.
//|
//| .. method:: add_to_service(service, uuid, *, properties=0, read_perm=`Attribute.OPEN`, write_perm=`Attribute.OPEN`, max_length=20, fixed_length=False, initial_value=None)
@ -134,12 +134,10 @@ STATIC mp_obj_t bleio_characteristic_add_to_service(size_t n_args, const mp_obj_
// Range checking on max_length arg is done by the common_hal layer, because
// it may vary depending on underlying BLE implementation.
common_hal_bleio_characteristic_construct(
characteristic, MP_OBJ_TO_PTR(service_obj), MP_OBJ_TO_PTR(uuid_obj),
characteristic, MP_OBJ_TO_PTR(service_obj), 0, MP_OBJ_TO_PTR(uuid_obj),
properties, read_perm, write_perm,
max_length, fixed_length, &initial_value_bufinfo);
common_hal_bleio_service_add_characteristic(service_obj, characteristic);
return MP_OBJ_FROM_PTR(characteristic);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_characteristic_add_to_service_fun_obj, 3, bleio_characteristic_add_to_service);
@ -170,7 +168,8 @@ const mp_obj_property_t bleio_characteristic_properties_obj = {
//| .. attribute:: uuid
//|
//| The UUID of this characteristic. (read-only)
//| Will be ``None`` if the 128-bit UUID for this characteristic is not known.
//|
//| Will be ``None`` if the 128-bit UUID for this characteristic is not known.
//|
STATIC mp_obj_t bleio_characteristic_get_uuid(mp_obj_t self_in) {
bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in);
@ -194,7 +193,9 @@ const mp_obj_property_t bleio_characteristic_uuid_obj = {
STATIC mp_obj_t bleio_characteristic_get_value(mp_obj_t self_in) {
bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(self_in);
return common_hal_bleio_characteristic_get_value(self);
uint8_t temp[512];
size_t actual_len = common_hal_bleio_characteristic_get_value(self, temp, sizeof(temp));
return mp_obj_new_bytearray(actual_len, temp);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_value_obj, bleio_characteristic_get_value);
@ -224,8 +225,20 @@ const mp_obj_property_t bleio_characteristic_value_obj = {
STATIC mp_obj_t bleio_characteristic_get_descriptors(mp_obj_t self_in) {
bleio_characteristic_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 *char_list = common_hal_bleio_characteristic_get_descriptor_list(self);
return mp_obj_new_tuple(char_list->len, char_list->items);
bleio_descriptor_obj_t *descriptors = common_hal_bleio_characteristic_get_descriptor_list(self);
bleio_descriptor_obj_t *head = descriptors;
size_t len = 0;
while (head != NULL) {
len++;
head = head->next;
}
mp_obj_tuple_t * t = MP_OBJ_TO_PTR(mp_obj_new_tuple(len, NULL));
head = descriptors;
for (size_t i = len - 1; i >= 0; i--) {
t->items[i] = MP_OBJ_FROM_PTR(head);
head = head->next;
}
return MP_OBJ_FROM_PTR(t);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_characteristic_get_descriptors_obj, bleio_characteristic_get_descriptors);
@ -282,7 +295,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_characteristic_set_cccd_obj, 1, bleio_ch
STATIC const mp_rom_map_elem_t bleio_characteristic_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_add_to_service), MP_ROM_PTR(&bleio_characteristic_add_to_service_obj) },
{ MP_ROM_QSTR(MP_QSTR_properties), MP_ROM_PTR(&bleio_characteristic_get_properties_obj) },
{ MP_ROM_QSTR(MP_QSTR_properties), MP_ROM_PTR(&bleio_characteristic_properties_obj) },
{ MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_characteristic_uuid_obj) },
{ MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&bleio_characteristic_value_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_cccd), MP_ROM_PTR(&bleio_characteristic_set_cccd_obj) },

View File

@ -36,12 +36,12 @@
extern const mp_obj_type_t bleio_characteristic_type;
extern void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo);
extern mp_obj_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self);
extern void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, uint16_t handle, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo);
extern size_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self, uint8_t* buf, size_t len);
extern void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo);
extern bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties(bleio_characteristic_obj_t *self);
extern bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characteristic_obj_t *self);
extern mp_obj_list_t *common_hal_bleio_characteristic_get_descriptor_list(bleio_characteristic_obj_t *self);
extern bleio_descriptor_obj_t *common_hal_bleio_characteristic_get_descriptor_list(bleio_characteristic_obj_t *self);
extern bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_characteristic_obj_t *self);
extern void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *self, bleio_descriptor_obj_t *descriptor);
extern void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate);

View File

@ -0,0 +1,171 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Dan Halbert for Adafruit Industries
* Copyright (c) 2018 Artur Pacholec
* Copyright (c) 2016 Glenn Ruben Bakke
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "shared-bindings/_bleio/Connection.h"
#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/Address.h"
#include "shared-bindings/_bleio/Characteristic.h"
#include "shared-bindings/_bleio/Service.h"
//| .. currentmodule:: _bleio
//|
//| :class:`Connection` -- A BLE connection
//| =========================================================
//|
//| A BLE connection to another device. Used to discover and interact with services on the other
//| device.
//|
//| Usage::
//|
//| import _bleio
//|
//| my_entry = None
//| for entry in _bleio.adapter.scan(2.5):
//| if entry.name is not None and entry.name == 'InterestingPeripheral':
//| my_entry = entry
//| break
//|
//| if not my_entry:
//| raise Exception("'InterestingPeripheral' not found")
//|
//| connection = _bleio.adapter.connect(my_entry.address, timeout=10)
//|
STATIC void ensure_connected(bleio_connection_obj_t *self) {
if (!common_hal_bleio_connection_get_connected(self)) {
mp_raise_ValueError(translate("Connection has been disconnected and can no longer be used. Create a new connection."));
}
}
//| .. class:: Connection()
//|
//| Connections cannot be made directly. Instead, to initiate a connection use `Adapter.connect`.
//| Connections may also be made when another device initiates a connection. To use a Connection
//| created by a peer, read the `Adapter.connections` property.
//|
//| .. method:: disconnect()
//|
//| Disconnects from the remote peripheral.
//|
STATIC mp_obj_t bleio_connection_disconnect(mp_obj_t self_in) {
bleio_connection_obj_t *self = MP_OBJ_TO_PTR(self_in);
ensure_connected(self);
common_hal_bleio_connection_disconnect(self->connection);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_connection_disconnect_obj, bleio_connection_disconnect);
//| .. method:: discover_remote_services(service_uuids_whitelist=None)
//|
//| Do BLE discovery for all services or for the given service UUIDS,
//| to find their handles and characteristics, and return the discovered services.
//| `Connection.connected` must be True.
//|
//| :param iterable service_uuids_whitelist:
//|
//| an iterable of :py:class:~`UUID` objects for the services provided by the peripheral
//| that you want to use.
//|
//| The peripheral may provide more services, but services not listed are ignored
//| and will not be returned.
//|
//| If service_uuids_whitelist is None, then all services will undergo discovery, which can be
//| slow.
//|
//| If the service UUID is 128-bit, or its characteristic UUID's are 128-bit, you
//| you must have already created a :py:class:~`UUID` object for that UUID in order for the
//| service or characteristic to be discovered. Creating the UUID causes the UUID to be
//| registered for use. (This restriction may be lifted in the future.)
//|
//| :return: A tuple of `_bleio.Service` objects provided by the remote peripheral.
//|
STATIC mp_obj_t bleio_connection_discover_remote_services(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
bleio_connection_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
enum { ARG_service_uuids_whitelist };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_service_uuids_whitelist, 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);
ensure_connected(self);
return MP_OBJ_FROM_PTR(common_hal_bleio_connection_discover_remote_services(
self,
args[ARG_service_uuids_whitelist].u_obj));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_connection_discover_remote_services_obj, 1, bleio_connection_discover_remote_services);
//| .. attribute:: connected
//|
//| True if connected to a remote peer.
//|
STATIC mp_obj_t bleio_connection_get_connected(mp_obj_t self_in) {
bleio_connection_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_bool(common_hal_bleio_connection_get_connected(self));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_connection_get_connected_obj, bleio_connection_get_connected);
const mp_obj_property_t bleio_connection_connected_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_connection_get_connected_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj },
};
STATIC const mp_rom_map_elem_t bleio_connection_locals_dict_table[] = {
// Methods
{ MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_connection_disconnect_obj) },
{ MP_ROM_QSTR(MP_QSTR_discover_remote_services), MP_ROM_PTR(&bleio_connection_discover_remote_services_obj) },
// Properties
{ MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_connection_connected_obj) },
};
STATIC MP_DEFINE_CONST_DICT(bleio_connection_locals_dict, bleio_connection_locals_dict_table);
const mp_obj_type_t bleio_connection_type = {
{ &mp_type_type },
.name = MP_QSTR_Connection,
.locals_dict = (mp_obj_dict_t*)&bleio_connection_locals_dict,
.unary_op = mp_generic_unary_op,
};

View File

@ -25,16 +25,17 @@
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANNER_H
#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANNER_H
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CONNECTION_H
#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CONNECTION_H
#include "py/objtype.h"
#include "common-hal/_bleio/Scanner.h"
#include "py/objtuple.h"
#include "common-hal/_bleio/Connection.h"
#include "common-hal/_bleio/Service.h"
extern const mp_obj_type_t bleio_scanner_type;
extern const mp_obj_type_t bleio_connection_type;
extern void common_hal_bleio_scanner_construct(bleio_scanner_obj_t *self);
extern mp_obj_t common_hal_bleio_scanner_scan(bleio_scanner_obj_t *self, mp_float_t timeout, mp_float_t interval, mp_float_t window);
extern void common_hal_bleio_scanner_stop(bleio_scanner_obj_t *self);
extern void common_hal_bleio_connection_disconnect(bleio_connection_internal_t *self);
extern bool common_hal_bleio_connection_get_connected(bleio_connection_obj_t *self);
extern mp_obj_tuple_t *common_hal_bleio_connection_discover_remote_services(bleio_connection_obj_t *self, mp_obj_t service_uuids_whitelist);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANNER_H
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CONNECTION_H

View File

@ -46,9 +46,8 @@
//|
//| There is no regular constructor for a Descriptor. A new local Descriptor can be created
//| and attached to a Characteristic by calling `add_to_characteristic()`.
//| Remote Descriptor objects are created by `Central.discover_remote_services()`
//| or `Peripheral.discover_remote_services()` as part of remote Characteristics
//| in the remote Services that are discovered.
//| Remote Descriptor objects are created by `Connection.discover_remote_services()`
//| as part of remote Characteristics in the remote Services that are discovered.
//|
//| .. classmethod:: add_to_characteristic(characteristic, uuid, *, read_perm=`Attribute.OPEN`, write_perm=`Attribute.OPEN`, max_length=20, fixed_length=False, initial_value=b'')
//|
@ -180,7 +179,9 @@ const mp_obj_property_t bleio_descriptor_characteristic_obj = {
STATIC mp_obj_t bleio_descriptor_get_value(mp_obj_t self_in) {
bleio_descriptor_obj_t *self = MP_OBJ_TO_PTR(self_in);
return common_hal_bleio_descriptor_get_value(self);
uint8_t temp[512];
size_t actual_len = common_hal_bleio_descriptor_get_value(self, temp, sizeof(temp));
return mp_obj_new_bytearray(actual_len, temp);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_descriptor_get_value_obj, bleio_descriptor_get_value);

View File

@ -38,7 +38,7 @@ extern const mp_obj_type_t bleio_descriptor_type;
extern void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_characteristic_obj_t *characteristic, bleio_uuid_obj_t *uuid, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo);
extern bleio_uuid_obj_t *common_hal_bleio_descriptor_get_uuid(bleio_descriptor_obj_t *self);
extern bleio_characteristic_obj_t *common_hal_bleio_descriptor_get_characteristic(bleio_descriptor_obj_t *self);
extern mp_obj_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self);
extern size_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self, uint8_t* buf, size_t len);
extern void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buffer_info_t *bufinfo);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_DESCRIPTOR_H

View File

@ -1,325 +0,0 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Dan Halbert for Adafruit Industries
* Copyright (c) 2018 Artur Pacholec
* Copyright (c) 2016 Glenn Ruben Bakke
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#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/Characteristic.h"
#include "shared-bindings/_bleio/Peripheral.h"
#include "shared-bindings/_bleio/Service.h"
#include "shared-bindings/_bleio/UUID.h"
#include "shared-module/_bleio/ScanEntry.h"
#include "common-hal/_bleio/Peripheral.h"
#define ADV_INTERVAL_MIN (0.0020f)
#define ADV_INTERVAL_MIN_STRING "0.0020"
#define ADV_INTERVAL_MAX (10.24f)
#define ADV_INTERVAL_MAX_STRING "10.24"
// 20ms is recommended by Apple
#define ADV_INTERVAL_DEFAULT (0.1f)
//| .. currentmodule:: _bleio
//|
//| :class:`Peripheral` -- 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::
//|
//| from _bleio import Characteristic, Peripheral, Service
//| from adafruit_ble.advertising import ServerAdvertisement
//|
//| # Create a peripheral and start it up.
//| peripheral = _bleio.Peripheral()
//|
//| # Create a Service and add it to this Peripheral.
//| service = Service.add_to_peripheral(peripheral, _bleio.UUID(0x180f))
//|
//| # Create a Characteristic and add it to the Service.
//| characteristic = Characteristic.add_to_service(service,
//| _bleio.UUID(0x2919), properties=Characteristic.READ | Characteristic.NOTIFY)
//|
//| adv = ServerAdvertisement(peripheral)
//| peripheral.start_advertising(adv.advertising_data_bytes, scan_response=adv.scan_response_bytes)
//|
//| while not peripheral.connected:
//| # Wait for connection.
//| pass
//|
//| .. class:: Peripheral(name=None)
//|
//| Create a new Peripheral object.
//|
//| :param str name: The name used when advertising this peripheral. If name is None,
//| _bleio.adapter.default_name will be used.
//|
STATIC mp_obj_t bleio_peripheral_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_name };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_name, MP_ARG_OBJ, {.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);
bleio_peripheral_obj_t *self = m_new_obj(bleio_peripheral_obj_t);
self->base.type = &bleio_peripheral_type;
mp_obj_t name = args[ARG_name].u_obj;
if (name == mp_const_none) {
name = common_hal_bleio_adapter_get_default_name();
} else if (!MP_OBJ_IS_STR(name)) {
mp_raise_ValueError(translate("name must be a string"));
}
common_hal_bleio_peripheral_construct(self, name);
return MP_OBJ_FROM_PTR(self);
}
//| .. attribute:: connected (read-only)
//|
//| True if connected to a BLE Central device.
//|
STATIC mp_obj_t bleio_peripheral_get_connected(mp_obj_t self_in) {
bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_bool(common_hal_bleio_peripheral_get_connected(self));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_get_connected_obj, bleio_peripheral_get_connected);
const mp_obj_property_t bleio_peripheral_connected_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_peripheral_get_connected_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj },
};
//| .. attribute:: services
//|
//| A tuple of :py:class:`Service` objects offered by this peripheral. (read-only)
//|
STATIC mp_obj_t bleio_peripheral_get_services(mp_obj_t self_in) {
bleio_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 *services_list = common_hal_bleio_peripheral_get_services(self);
return mp_obj_new_tuple(services_list->len, services_list->items);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_get_services_obj, bleio_peripheral_get_services);
const mp_obj_property_t bleio_peripheral_services_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_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_peripheral_get_name(mp_obj_t self_in) {
bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in);
return common_hal_bleio_peripheral_get_name(self);
}
MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_get_name_obj, bleio_peripheral_get_name);
const mp_obj_property_t bleio_peripheral_name_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_peripheral_get_name_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj },
};
//| .. method:: start_advertising(data, *, scan_response=None, connectable=True, interval=0.1)
//|
//| Starts advertising the peripheral. The peripheral's name and
//| services are included in the advertisement packets.
//|
//| :param buf data: advertising data packet bytes
//| :param buf scan_response: scan response data packet bytes. ``None`` if no scan response is needed.
//| :param bool connectable: If `True` then other devices are allowed to connect to this peripheral.
//| :param float interval: advertising interval, in seconds
//|
STATIC mp_obj_t bleio_peripheral_start_advertising(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
enum { ARG_data, ARG_scan_response, ARG_connectable, ARG_interval };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_scan_response, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_connectable, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
{ MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
};
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 data_bufinfo;
mp_get_buffer_raise(args[ARG_data].u_obj, &data_bufinfo, MP_BUFFER_READ);
// Pass an empty buffer if scan_response not provided.
mp_buffer_info_t scan_response_bufinfo = { 0 };
if (args[ARG_scan_response].u_obj != mp_const_none) {
mp_get_buffer_raise(args[ARG_scan_response].u_obj, &scan_response_bufinfo, MP_BUFFER_READ);
}
if (args[ARG_interval].u_obj == MP_OBJ_NULL) {
args[ARG_interval].u_obj = mp_obj_new_float(ADV_INTERVAL_DEFAULT);
}
const mp_float_t interval = mp_obj_float_get(args[ARG_interval].u_obj);
if (interval < ADV_INTERVAL_MIN || interval > ADV_INTERVAL_MAX) {
mp_raise_ValueError_varg(translate("interval must be in range %s-%s"),
ADV_INTERVAL_MIN_STRING, ADV_INTERVAL_MAX_STRING);
}
common_hal_bleio_peripheral_start_advertising(self, args[ARG_connectable].u_bool, interval,
&data_bufinfo, &scan_response_bufinfo);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_peripheral_start_advertising_obj, 2, bleio_peripheral_start_advertising);
//| .. method:: stop_advertising()
//|
//| Stop sending advertising packets.
STATIC mp_obj_t bleio_peripheral_stop_advertising(mp_obj_t self_in) {
bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in);
common_hal_bleio_peripheral_stop_advertising(self);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_stop_advertising_obj, bleio_peripheral_stop_advertising);
//| .. method:: disconnect()
//|
//| Disconnects from the remote central.
//| Normally the central initiates a disconnection. Use this only
//| if necessary for your application.
//|
STATIC mp_obj_t bleio_peripheral_disconnect(mp_obj_t self_in) {
bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in);
common_hal_bleio_peripheral_disconnect(self);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_disconnect_obj, bleio_peripheral_disconnect);
//| .. method:: discover_remote_services(service_uuids_whitelist=None)
//| Do BLE discovery for all services or for the given service UUIDS,
//| to find their handles and characteristics, and return the discovered services.
//| `Peripheral.connected` must be True.
//|
//| :param iterable service_uuids_whitelist: an iterable of :py:class:~`UUID` objects for the services
//| provided by the peripheral that you want to use.
//| The peripheral may provide more services, but services not listed are ignored
//| and will not be returned.
//|
//| If service_uuids_whitelist is None, then all services will undergo discovery, which can be slow.
//|
//| If the service UUID is 128-bit, or its characteristic UUID's are 128-bit, you
//| you must have already created a :py:class:~`UUID` object for that UUID in order for the
//| service or characteristic to be discovered. Creating the UUID causes the UUID to be registered
//| for use. (This restriction may be lifted in the future.)
//|
//| Thought it is unusual for a peripheral to act as a BLE client, it can do so, and
//| needs to be able to do discovery on its peer (a central).
//| Examples include a peripheral accessing a central that provides Current Time Service,
//| Apple Notification Center Service, or Battery Service.
//|
//| :return: A tuple of services provided by the remote central.
//|
STATIC mp_obj_t bleio_peripheral_discover_remote_services(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
enum { ARG_service_uuids_whitelist };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_service_uuids_whitelist, 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);
if (!common_hal_bleio_peripheral_get_connected(self)) {
mp_raise_ValueError(translate("Not connected"));
}
return MP_OBJ_FROM_PTR(common_hal_bleio_peripheral_discover_remote_services(
MP_OBJ_FROM_PTR(self),
args[ARG_service_uuids_whitelist].u_obj));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_peripheral_discover_remote_services_obj, 1, bleio_peripheral_discover_remote_services);
//| .. method:: pair()
//|
//| Request pairing with connected central.
STATIC mp_obj_t bleio_peripheral_pair(mp_obj_t self_in) {
bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(self_in);
common_hal_bleio_peripheral_pair(self);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_pair_obj, bleio_peripheral_pair);
STATIC const mp_rom_map_elem_t bleio_peripheral_locals_dict_table[] = {
// Methods
{ MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_peripheral_start_advertising_obj) },
{ MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_peripheral_stop_advertising_obj) },
{ MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_peripheral_disconnect_obj) },
{ MP_ROM_QSTR(MP_QSTR_discover_remote_services), MP_ROM_PTR(&bleio_peripheral_discover_remote_services_obj) },
{ MP_ROM_QSTR(MP_QSTR_pair), MP_ROM_PTR(&bleio_peripheral_pair_obj) },
// Properties
{ MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&bleio_peripheral_connected_obj) },
{ MP_ROM_QSTR(MP_QSTR_name), MP_ROM_PTR(&bleio_peripheral_name_obj) },
{ MP_ROM_QSTR(MP_QSTR_services), MP_ROM_PTR(&bleio_peripheral_services_obj) },
};
STATIC MP_DEFINE_CONST_DICT(bleio_peripheral_locals_dict, bleio_peripheral_locals_dict_table);
const mp_obj_type_t bleio_peripheral_type = {
{ &mp_type_type },
.name = MP_QSTR_Peripheral,
.make_new = bleio_peripheral_make_new,
.locals_dict = (mp_obj_dict_t*)&bleio_peripheral_locals_dict
};

View File

@ -1,48 +0,0 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Dan Halbert for Adafruit Industries
* 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_PERIPHERAL_H
#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_PERIPHERAL_H
#include "py/objtuple.h"
#include "common-hal/_bleio/Peripheral.h"
#include "common-hal/_bleio/Service.h"
extern const mp_obj_type_t bleio_peripheral_type;
extern void common_hal_bleio_peripheral_construct(bleio_peripheral_obj_t *self, mp_obj_t name);
extern void common_hal_bleio_peripheral_add_service(bleio_peripheral_obj_t *self, bleio_service_obj_t *service);
extern mp_obj_list_t *common_hal_bleio_peripheral_get_services(bleio_peripheral_obj_t *self);
extern bool common_hal_bleio_peripheral_get_connected(bleio_peripheral_obj_t *self);
extern mp_obj_t common_hal_bleio_peripheral_get_name(bleio_peripheral_obj_t *self);
extern void common_hal_bleio_peripheral_start_advertising(bleio_peripheral_obj_t *device, bool connectable, float interval, mp_buffer_info_t *advertising_data_bufinfo, mp_buffer_info_t *scan_response_data_bufinfo);
extern void common_hal_bleio_peripheral_stop_advertising(bleio_peripheral_obj_t *device);
extern void common_hal_bleio_peripheral_disconnect(bleio_peripheral_obj_t *device);
extern mp_obj_tuple_t *common_hal_bleio_peripheral_discover_remote_services(bleio_peripheral_obj_t *self, mp_obj_t service_uuids_whitelist);
extern void common_hal_bleio_peripheral_pair(bleio_peripheral_obj_t *device);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_PERIPHERAL_H

View File

@ -29,6 +29,7 @@
#include <string.h>
#include "py/objproperty.h"
#include "py/runtime.h"
#include "shared-bindings/_bleio/Address.h"
#include "shared-bindings/_bleio/ScanEntry.h"
#include "shared-bindings/_bleio/UUID.h"
@ -36,14 +37,43 @@
//| .. currentmodule:: _bleio
//|
//| :class:`ScanEntry` -- BLE scan response entry
//| :class:`ScanEntry` -- BLE scan data
//| =========================================================
//|
//| Encapsulates information about a device that was received as a
//| response to a BLE scan request. This object may only be created
//| by a `_bleio.Scanner`: it has no user-visible constructor.
//| Encapsulates information about a device that was received during scanning. It can be
//| advertisement or scan response data. This object may only be created by a `_bleio.ScanResults`:
//| it has no user-visible constructor.
//|
//| .. class:: ScanEntry()
//|
//| Cannot be instantiated directly. Use `_bleio.Adapter.start_scan`.
//|
//| .. method:: matches(prefixes, *, all=True)
//|
//| Returns True if the ScanEntry matches all prefixes when ``all`` is True. This is stricter
//| than the scan filtering which accepts any advertisements that match any of the prefixes
//| where all is False.
//|
STATIC mp_obj_t bleio_scanentry_matches(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
enum { ARG_prefixes, ARG_all };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_prefixes, MP_ARG_OBJ | MP_ARG_REQUIRED },
{ MP_QSTR_all, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} },
};
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_prefixes].u_obj, &bufinfo, MP_BUFFER_READ);
return mp_obj_new_bool(common_hal_bleio_scanentry_matches(self, bufinfo.buf, bufinfo.len, args[ARG_all].u_bool));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_scanentry_matches_obj, 2, bleio_scanentry_matches);
//| .. attribute:: address
//|
//| The address of the device (read-only), of type `_bleio.Address`.
@ -95,11 +125,47 @@ const mp_obj_property_t bleio_scanentry_rssi_obj = {
(mp_obj_t)&mp_const_none_obj },
};
//| .. attribute:: connectable
//|
//| True if the device can be connected to. (read-only)
//|
STATIC mp_obj_t scanentry_get_connectable(mp_obj_t self_in) {
bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_bool(common_hal_bleio_scanentry_get_connectable(self));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanentry_get_connectable_obj, scanentry_get_connectable);
const mp_obj_property_t bleio_scanentry_connectable_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_scanentry_get_connectable_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj },
};
//| .. attribute:: scan_response
//|
//| True if the entry was a scan response. (read-only)
//|
STATIC mp_obj_t scanentry_get_scan_response(mp_obj_t self_in) {
bleio_scanentry_obj_t *self = MP_OBJ_TO_PTR(self_in);
return mp_obj_new_bool(common_hal_bleio_scanentry_get_scan_response(self));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_scanentry_get_scan_response_obj, scanentry_get_scan_response);
const mp_obj_property_t bleio_scanentry_scan_response_obj = {
.base.type = &mp_type_property,
.proxy = { (mp_obj_t)&bleio_scanentry_get_scan_response_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj },
};
STATIC const mp_rom_map_elem_t bleio_scanentry_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_address), MP_ROM_PTR(&bleio_scanentry_address_obj) },
{ MP_ROM_QSTR(MP_QSTR_advertisement_bytes), MP_ROM_PTR(&bleio_scanentry_advertisement_bytes_obj) },
{ MP_ROM_QSTR(MP_QSTR_rssi), MP_ROM_PTR(&bleio_scanentry_rssi_obj) },
{ MP_ROM_QSTR(MP_QSTR_connectable), MP_ROM_PTR(&bleio_scanentry_connectable_obj) },
{ MP_ROM_QSTR(MP_QSTR_scan_response), MP_ROM_PTR(&bleio_scanentry_scan_response_obj) },
{ MP_ROM_QSTR(MP_QSTR_matches), MP_ROM_PTR(&bleio_scanentry_matches_obj) },
};
STATIC MP_DEFINE_CONST_DICT(bleio_scanentry_locals_dict, bleio_scanentry_locals_dict_table);

View File

@ -37,5 +37,8 @@ extern const mp_obj_type_t bleio_scanentry_type;
mp_obj_t common_hal_bleio_scanentry_get_address(bleio_scanentry_obj_t *self);
mp_obj_t common_hal_bleio_scanentry_get_advertisement_bytes(bleio_scanentry_obj_t *self);
mp_int_t common_hal_bleio_scanentry_get_rssi(bleio_scanentry_obj_t *self);
bool common_hal_bleio_scanentry_get_connectable(bleio_scanentry_obj_t *self);
bool common_hal_bleio_scanentry_get_scan_response(bleio_scanentry_obj_t *self);
bool common_hal_bleio_scanentry_matches(bleio_scanentry_obj_t *self, uint8_t* prefixes, size_t prefixes_len, bool all);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANENTRY_H

View File

@ -0,0 +1,72 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Dan Halbert for Adafruit Industries
* Copyright (c) 2018 Artur Pacholec
* Copyright (c) 2017 Glenn Ruben Bakke
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <string.h>
#include "py/objproperty.h"
#include "py/runtime.h"
#include "shared-bindings/_bleio/ScanResults.h"
//| .. currentmodule:: _bleio
//|
//| :class:`ScanResults` -- An Iterator over BLE scanning results
//| ===============================================================
//|
//| Iterates over advertising data received while scanning. This object is always created
//| by a `_bleio.Adapter`: it has no user-visible constructor.
//|
STATIC mp_obj_t scanresults_iternext(mp_obj_t self_in) {
mp_check_self(MP_OBJ_IS_TYPE(self_in, &bleio_scanresults_type));
bleio_scanresults_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_obj_t scan_entry = common_hal_bleio_scanresults_next(self);
if (scan_entry != mp_const_none) {
return scan_entry;
}
return MP_OBJ_STOP_ITERATION;
}
//| .. class:: ScanResults()
//|
//| Cannot be instantiated directly. Use `_bleio.Adapter.start_scan`.
//|
//| .. method:: __iter__()
//|
//| Returns itself since it is the iterator.
//|
//| .. method:: __next__()
//|
//| Returns the next `_bleio.ScanEntry`. Blocks if none have been received and scanning is still
//| active. Raises `StopIteration` if scanning is finished and no other results are available.
//|
const mp_obj_type_t bleio_scanresults_type = {
{ &mp_type_type },
.name = MP_QSTR_ScanResults,
.getiter = mp_identity_getiter,
.iternext = scanresults_iternext,
};

View File

@ -5,6 +5,7 @@
*
* Copyright (c) 2019 Dan Halbert for Adafruit Industries
* Copyright (c) 2018 Artur Pacholec
* Copyright (c) 2017 Glenn Ruben Bakke
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -25,16 +26,14 @@
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_SCANNER_H
#define MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_SCANNER_H
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANRESULTS_H
#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANRESULTS_H
#include "py/obj.h"
#include "shared-module/_bleio/ScanResults.h"
typedef struct {
mp_obj_base_t base;
mp_obj_t scan_entries;
uint16_t interval;
uint16_t window;
} bleio_scanner_obj_t;
extern const mp_obj_type_t bleio_scanresults_type;
#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_BLEIO_SCANNER_H
mp_obj_t common_hal_bleio_scanresults_next(bleio_scanresults_obj_t *self);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SCANRESULTS_H

View File

@ -1,129 +0,0 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Dan Halbert for Adafruit Industries
* 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 "py/objproperty.h"
#include "py/runtime.h"
#include "shared-bindings/_bleio/ScanEntry.h"
#include "shared-bindings/_bleio/Scanner.h"
#define INTERVAL_DEFAULT (0.1f)
#define INTERVAL_MIN (0.0025f)
#define INTERVAL_MIN_STRING "0.0025"
#define INTERVAL_MAX (40.959375f)
#define INTERVAL_MAX_STRING "40.959375"
#define WINDOW_DEFAULT (0.1f)
//| .. currentmodule:: _bleio
//|
//| :class:`Scanner` -- scan for nearby BLE devices
//| =========================================================
//|
//| Scan for nearby BLE devices.
//|
//| Usage::
//|
//| import _bleio
//| scanner = _bleio.Scanner()
//| entries = scanner.scan(2.5) # Scan for 2.5 seconds
//|
//| .. class:: Scanner()
//|
//| Create a new Scanner object.
//|
STATIC mp_obj_t bleio_scanner_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *all_args, mp_map_t *kw_args) {
mp_arg_check_num(n_args, kw_args, 0, 0, false);
bleio_scanner_obj_t *self = m_new_obj(bleio_scanner_obj_t);
self->base.type = type;
common_hal_bleio_scanner_construct(self);
return MP_OBJ_FROM_PTR(self);
}
//| .. method:: scan(timeout, \*, interval=0.1, window=0.1)
//|
//| Performs a BLE scan.
//|
//| :param float timeout: the scan timeout in seconds
//| :param float interval: the interval (in seconds) between the start of two consecutive scan windows
//| Must be in the range 0.0025 - 40.959375 seconds.
//| :param float window: the duration (in seconds) to scan a single BLE channel.
//| window must be <= interval.
//| :returns: an iterable of `ScanEntry` objects
//| :rtype: iterable
//|
STATIC mp_obj_t bleio_scanner_scan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_timeout, ARG_interval, ARG_window };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_timeout, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_interval, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_window, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
};
bleio_scanner_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
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);
const mp_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj);
if (args[ARG_interval].u_obj == MP_OBJ_NULL) {
args[ARG_interval].u_obj = mp_obj_new_float(INTERVAL_DEFAULT);
}
if (args[ARG_window].u_obj == MP_OBJ_NULL) {
args[ARG_window].u_obj = mp_obj_new_float(WINDOW_DEFAULT);
}
const mp_float_t interval = mp_obj_float_get(args[ARG_interval].u_obj);
if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) {
mp_raise_ValueError_varg(translate("interval must be in range %s-%s"), INTERVAL_MIN_STRING, INTERVAL_MAX_STRING);
}
const mp_float_t window = mp_obj_float_get(args[ARG_window].u_obj);
if (window > interval) {
mp_raise_ValueError(translate("window must be <= interval"));
}
return common_hal_bleio_scanner_scan(self, timeout, interval, window);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_scanner_scan_obj, 2, bleio_scanner_scan);
STATIC const mp_rom_map_elem_t bleio_scanner_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_scan), MP_ROM_PTR(&bleio_scanner_scan_obj) },
};
STATIC MP_DEFINE_CONST_DICT(bleio_scanner_locals_dict, bleio_scanner_locals_dict_table);
const mp_obj_type_t bleio_scanner_type = {
{ &mp_type_type },
.name = MP_QSTR_Scanner,
.make_new = bleio_scanner_make_new,
.locals_dict = (mp_obj_dict_t*)&bleio_scanner_locals_dict
};

View File

@ -29,54 +29,38 @@
#include "py/objproperty.h"
#include "py/runtime.h"
#include "shared-bindings/_bleio/Characteristic.h"
#include "shared-bindings/_bleio/Peripheral.h"
#include "shared-bindings/_bleio/Service.h"
#include "shared-bindings/_bleio/UUID.h"
//| .. currentmodule:: _bleio
//|
//| :class:`Service` -- BLE service
//| :class:`Service` -- BLE GATT Service
//| =========================================================
//|
//| Stores information about a BLE service and its characteristics.
//|
//| .. class:: Service
//| .. class:: Service(uuid, *, secondary=False)
//|
//| There is no regular constructor for a Service. A new local Service can be created
//| and attached to a Peripheral by calling `add_to_peripheral()`.
//| Remote Service objects are created by `Central.discover_remote_services()`
//| or `Peripheral.discover_remote_services()`.
//| Create a new Service identitied by the specified UUID. It can be accessed by all
//| connections. This is known as a Service server. Client Service objects are created via
//| `Connection.discover_remote_services`.
//|
//| .. classmethod:: add_to_peripheral(peripheral, uuid, *, secondary=False)
//| To mark the Server as secondary, pass `True` as :py:data:`secondary`.
//|
//| Create a new Service object, identitied by the specified UUID, and add it
//| to the given Peripheral.
//|
//| To mark the service as secondary, pass `True` as :py:data:`secondary`.
//|
//| :param Peripheral peripheral: The peripheral that will provide this service
//| :param UUID uuid: The uuid of the service
//| :param bool secondary: If the service is a secondary one
//| :param UUID uuid: The uuid of the server
//| :param bool secondary: If the server is a secondary one
//
//| :return: the new Service
//| :return: the new Service
//|
STATIC mp_obj_t bleio_service_add_to_peripheral(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
// class is arg[0], which we can ignore.
enum { ARG_peripheral, ARG_uuid, ARG_secondary };
STATIC mp_obj_t bleio_service_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_uuid, ARG_secondary };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_peripheral, MP_ARG_REQUIRED | MP_ARG_OBJ,},
{ MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_secondary, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
};
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);
const mp_obj_t peripheral_obj = args[ARG_peripheral].u_obj;
if (!MP_OBJ_IS_TYPE(peripheral_obj, &bleio_peripheral_type)) {
mp_raise_ValueError(translate("Expected a Peripheral"));
}
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
const mp_obj_t uuid_obj = args[ARG_uuid].u_obj;
if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) {
@ -88,19 +72,15 @@ STATIC mp_obj_t bleio_service_add_to_peripheral(size_t n_args, const mp_obj_t *p
bleio_service_obj_t *service = m_new_obj(bleio_service_obj_t);
service->base.type = &bleio_service_type;
common_hal_bleio_service_construct(
service, MP_OBJ_TO_PTR(peripheral_obj), MP_OBJ_TO_PTR(uuid_obj), is_secondary);
common_hal_bleio_peripheral_add_service(peripheral_obj, service);
common_hal_bleio_service_construct(service, MP_OBJ_TO_PTR(uuid_obj), is_secondary);
return MP_OBJ_FROM_PTR(service);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_service_add_to_peripheral_fun_obj, 3, bleio_service_add_to_peripheral);
STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(bleio_service_add_to_peripheral_obj, MP_ROM_PTR(&bleio_service_add_to_peripheral_fun_obj));
//| .. attribute:: characteristics
//|
//| A tuple of :py:class:`Characteristic` designating the characteristics that are offered by this service. (read-only)
//| A tuple of :py:class:`Characteristic` designating the characteristics that are offered by
//| this service. (read-only)
//|
STATIC mp_obj_t bleio_service_get_characteristics(mp_obj_t self_in) {
bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in);
@ -156,7 +136,8 @@ const mp_obj_property_t bleio_service_secondary_obj = {
//| .. attribute:: uuid
//|
//| The UUID of this service. (read-only)
//| Will be ``None`` if the 128-bit UUID for this service is not known.
//|
//| Will be ``None`` if the 128-bit UUID for this service is not known.
//|
STATIC mp_obj_t bleio_service_get_uuid(mp_obj_t self_in) {
bleio_service_obj_t *self = MP_OBJ_TO_PTR(self_in);
@ -175,10 +156,10 @@ const mp_obj_property_t bleio_service_uuid_obj = {
STATIC const mp_rom_map_elem_t bleio_service_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_add_to_peripheral), MP_ROM_PTR(&bleio_service_add_to_peripheral_obj) },
{ MP_ROM_QSTR(MP_QSTR_characteristics), MP_ROM_PTR(&bleio_service_characteristics_obj) },
{ MP_ROM_QSTR(MP_QSTR_secondary), MP_ROM_PTR(&bleio_service_secondary_obj) },
{ MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_service_uuid_obj) },
{ MP_ROM_QSTR(MP_QSTR_remote), MP_ROM_PTR(&bleio_service_remote_obj) },
};
STATIC MP_DEFINE_CONST_DICT(bleio_service_locals_dict, bleio_service_locals_dict_table);
@ -196,6 +177,25 @@ STATIC void bleio_service_print(const mp_print_t *print, mp_obj_t self_in, mp_pr
const mp_obj_type_t bleio_service_type = {
{ &mp_type_type },
.name = MP_QSTR_Service,
.make_new = bleio_service_make_new,
.print = bleio_service_print,
.locals_dict = (mp_obj_dict_t*)&bleio_service_locals_dict
};
// Helper for classes that store lists of services.
mp_obj_tuple_t* service_linked_list_to_tuple(bleio_service_obj_t * services) {
// Return list as a tuple so user won't be able to change it.
bleio_service_obj_t *head = services;
size_t len = 0;
while (head != NULL) {
len++;
head = head->next;
}
mp_obj_tuple_t * t = MP_OBJ_TO_PTR(mp_obj_new_tuple(len, NULL));
head = services;
for (int32_t i = len - 1; i >= 0; i--) {
t->items[i] = MP_OBJ_FROM_PTR(head);
head = head->next;
}
return t;
}

View File

@ -28,16 +28,24 @@
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SERVICE_H
#define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SERVICE_H
#include "common-hal/_bleio/Peripheral.h"
#include "common-hal/_bleio/Characteristic.h"
#include "common-hal/_bleio/Connection.h"
#include "common-hal/_bleio/Service.h"
#include "py/objtuple.h"
const mp_obj_type_t bleio_service_type;
extern void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_peripheral_obj_t *peripheral, bleio_uuid_obj_t *uuid, bool is_secondary);
// Private version that doesn't allocate on the heap
extern uint32_t _common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary, mp_obj_list_t * characteristic_list);
extern void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary);
extern void common_hal_bleio_service_from_remote_service(bleio_service_obj_t *self, bleio_connection_obj_t* connection, bleio_uuid_obj_t *uuid, bool is_secondary);
extern bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self);
extern mp_obj_list_t *common_hal_bleio_service_get_characteristic_list(bleio_service_obj_t *self);
extern bool common_hal_bleio_service_get_is_remote(bleio_service_obj_t *self);
extern bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self);
extern void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic);
extern void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *initial_value_bufinfo);
mp_obj_tuple_t* service_linked_list_to_tuple(bleio_service_obj_t * services);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SERVICE_H

View File

@ -156,9 +156,10 @@ STATIC mp_obj_t bleio_uuid_get_uuid128(mp_obj_t self_in) {
bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(self_in);
uint8_t uuid128[16];
if (!common_hal_bleio_uuid_get_uuid128(self, uuid128)) {
if (common_hal_bleio_uuid_get_size(self) != 128) {
mp_raise_AttributeError(translate("not a 128-bit UUID"));
}
common_hal_bleio_uuid_get_uuid128(self, uuid128);
return mp_obj_new_bytes(uuid128, 16);
}
@ -192,10 +193,42 @@ const mp_obj_property_t bleio_uuid_size_obj = {
(mp_obj_t)&mp_const_none_obj},
};
//| .. method:: pack_into(buffer, offset=0)
//|
//| Packs the UUID into the given buffer at the given offset.
//|
STATIC mp_obj_t bleio_uuid_pack_into(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
bleio_uuid_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
enum { ARG_buffer, ARG_offset };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_buffer, MP_ARG_OBJ | MP_ARG_REQUIRED },
{ MP_QSTR_offset, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
};
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_buffer].u_obj, &bufinfo, MP_BUFFER_WRITE);
size_t offset = args[ARG_offset].u_int;
if (offset + common_hal_bleio_uuid_get_size(self) / 8 > bufinfo.len) {
mp_raise_ValueError(translate("Buffer + offset too small %d %d %d"));
}
common_hal_bleio_uuid_pack_into(self, bufinfo.buf + offset);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_uuid_pack_into_obj, 2, bleio_uuid_pack_into);
STATIC const mp_rom_map_elem_t bleio_uuid_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_uuid16), MP_ROM_PTR(&bleio_uuid_uuid16_obj) },
{ MP_ROM_QSTR(MP_QSTR_uuid128), MP_ROM_PTR(&bleio_uuid_uuid128_obj) },
{ MP_ROM_QSTR(MP_QSTR_size), MP_ROM_PTR(&bleio_uuid_size_obj) },
{ MP_ROM_QSTR(MP_QSTR_pack_into), MP_ROM_PTR(&bleio_uuid_pack_into_obj) },
};
STATIC MP_DEFINE_CONST_DICT(bleio_uuid_locals_dict, bleio_uuid_locals_dict_table);
@ -231,13 +264,19 @@ STATIC mp_obj_t bleio_uuid_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
//|
STATIC mp_obj_t bleio_uuid_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
switch (op) {
// Two UUID's are equal if their uuid16 values and uuid128 references match.
// Two UUID's are equal if their uuid16 values match or their uuid128 values match.
case MP_BINARY_OP_EQUAL:
if (MP_OBJ_IS_TYPE(rhs_in, &bleio_uuid_type)) {
return mp_obj_new_bool(
common_hal_bleio_uuid_get_uuid16(lhs_in) == common_hal_bleio_uuid_get_uuid16(rhs_in) &&
common_hal_bleio_uuid_get_uuid128_reference(lhs_in) ==
common_hal_bleio_uuid_get_uuid128_reference(rhs_in));
if (common_hal_bleio_uuid_get_size(lhs_in) == 16 &&
common_hal_bleio_uuid_get_size(rhs_in) == 16) {
return mp_obj_new_bool(common_hal_bleio_uuid_get_uuid16(lhs_in) ==
common_hal_bleio_uuid_get_uuid16(rhs_in));
}
uint8_t lhs[16];
uint8_t rhs[16];
common_hal_bleio_uuid_get_uuid128(lhs_in, lhs);
common_hal_bleio_uuid_get_uuid128(rhs_in, rhs);
return mp_obj_new_bool(memcmp(lhs, rhs, sizeof(lhs)) == 0);
} else {
return mp_const_false;
}

View File

@ -34,10 +34,11 @@ void bleio_uuid_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t
extern const mp_obj_type_t bleio_uuid_type;
extern void common_hal_bleio_uuid_construct(bleio_uuid_obj_t *self, mp_int_t uuid16, uint8_t uuid128[]);
extern void common_hal_bleio_uuid_construct(bleio_uuid_obj_t *self, mp_int_t uuid16, const uint8_t uuid128[]);
extern uint32_t common_hal_bleio_uuid_get_uuid16(bleio_uuid_obj_t *self);
extern bool common_hal_bleio_uuid_get_uuid128(bleio_uuid_obj_t *self, uint8_t uuid128[16]);
extern uint32_t common_hal_bleio_uuid_get_uuid128_reference(bleio_uuid_obj_t *self);
extern uint32_t common_hal_bleio_uuid_get_size(bleio_uuid_obj_t *self);
void common_hal_bleio_uuid_pack_into(bleio_uuid_obj_t *self, uint8_t* buf);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_UUID_H

View File

@ -29,13 +29,12 @@
#include "shared-bindings/_bleio/__init__.h"
#include "shared-bindings/_bleio/Address.h"
#include "shared-bindings/_bleio/Attribute.h"
#include "shared-bindings/_bleio/Central.h"
#include "shared-bindings/_bleio/Characteristic.h"
#include "shared-bindings/_bleio/CharacteristicBuffer.h"
#include "shared-bindings/_bleio/Connection.h"
#include "shared-bindings/_bleio/Descriptor.h"
#include "shared-bindings/_bleio/Peripheral.h"
#include "shared-bindings/_bleio/ScanEntry.h"
#include "shared-bindings/_bleio/Scanner.h"
#include "shared-bindings/_bleio/ScanResults.h"
#include "shared-bindings/_bleio/Service.h"
#include "shared-bindings/_bleio/UUID.h"
@ -64,34 +63,32 @@
//| Address
//| Adapter
//| Attribute
//| Central
//| Characteristic
//| CharacteristicBuffer
//| Connection
//| Descriptor
//| Peripheral
//| ScanEntry
//| Scanner
//| ScanResults
//| Service
//| UUID
//|
//| .. attribute:: adapter
//|
//| BLE Adapter information, such as enabled state as well as MAC
//| address.
//| BLE Adapter used to manage device discovery and connections.
//| This object is the sole instance of `_bleio.Adapter`.
//|
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_Adapter), MP_ROM_PTR(&bleio_adapter_type) },
{ MP_ROM_QSTR(MP_QSTR_Address), MP_ROM_PTR(&bleio_address_type) },
{ MP_ROM_QSTR(MP_QSTR_Attribute), MP_ROM_PTR(&bleio_attribute_type) },
{ MP_ROM_QSTR(MP_QSTR_Central), MP_ROM_PTR(&bleio_central_type) },
{ MP_ROM_QSTR(MP_QSTR_Connection), MP_ROM_PTR(&bleio_connection_type) },
{ MP_ROM_QSTR(MP_QSTR_Characteristic), MP_ROM_PTR(&bleio_characteristic_type) },
{ MP_ROM_QSTR(MP_QSTR_CharacteristicBuffer), MP_ROM_PTR(&bleio_characteristic_buffer_type) },
{ MP_ROM_QSTR(MP_QSTR_Descriptor), MP_ROM_PTR(&bleio_descriptor_type) },
{ MP_ROM_QSTR(MP_QSTR_Peripheral), MP_ROM_PTR(&bleio_peripheral_type) },
{ MP_ROM_QSTR(MP_QSTR_ScanEntry), MP_ROM_PTR(&bleio_scanentry_type) },
{ MP_ROM_QSTR(MP_QSTR_Scanner), MP_ROM_PTR(&bleio_scanner_type) },
{ MP_ROM_QSTR(MP_QSTR_ScanResults), MP_ROM_PTR(&bleio_scanresults_type) },
{ MP_ROM_QSTR(MP_QSTR_Service), MP_ROM_PTR(&bleio_service_type) },
{ MP_ROM_QSTR(MP_QSTR_UUID), MP_ROM_PTR(&bleio_uuid_type) },

View File

@ -36,17 +36,19 @@
#include "common-hal/_bleio/__init__.h"
#include "common-hal/_bleio/Adapter.h"
extern const super_adapter_obj_t common_hal_bleio_adapter_obj;
extern bleio_adapter_obj_t common_hal_bleio_adapter_obj;
extern void common_hal_bleio_check_connected(uint16_t conn_handle);
void common_hal_bleio_check_connected(uint16_t conn_handle);
extern uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device);
extern mp_obj_list_t *common_hal_bleio_device_get_remote_service_list(mp_obj_t device);
extern void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist);
uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device);
mp_obj_list_t *common_hal_bleio_device_get_remote_service_list(mp_obj_t device);
void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist);
extern mp_obj_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle);
extern void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo);
extern void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response);
size_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle, uint8_t* buf, size_t len);
void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo);
size_t common_hal_bleio_gattc_read(uint16_t handle, uint16_t conn_handle, uint8_t* buf, size_t len);
void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response);
void common_hal_bleio_gc_collect(void);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO___INIT___H

View File

@ -37,9 +37,50 @@ mp_obj_t common_hal_bleio_scanentry_get_address(bleio_scanentry_obj_t *self) {
}
mp_obj_t common_hal_bleio_scanentry_get_advertisement_bytes(bleio_scanentry_obj_t *self) {
return self->data;
return MP_OBJ_FROM_PTR(self->data);
}
mp_int_t common_hal_bleio_scanentry_get_rssi(bleio_scanentry_obj_t *self) {
return self->rssi;
}
bool common_hal_bleio_scanentry_get_connectable(bleio_scanentry_obj_t *self) {
return self->connectable;
}
bool common_hal_bleio_scanentry_get_scan_response(bleio_scanentry_obj_t *self) {
return self->scan_response;
}
bool bleio_scanentry_data_matches(const uint8_t* data, size_t len, const uint8_t* prefixes, size_t prefixes_length, bool any) {
if (prefixes_length == 0) {
return true;
}
size_t i = 0;
while(i < prefixes_length) {
uint8_t prefix_length = prefixes[i];
i += 1;
size_t j = 0;
while (j < len) {
uint8_t structure_length = data[j];
j += 1;
if (structure_length == 0) {
break;
}
if (structure_length >= prefix_length && memcmp(data + j, prefixes + i, prefix_length) == 0) {
if (any) {
return true;
}
} else if (!any) {
return false;
}
j += structure_length;
}
i += prefix_length;
}
return !any;
}
bool common_hal_bleio_scanentry_matches(bleio_scanentry_obj_t *self, const uint8_t* prefixes, size_t prefixes_len, bool all) {
return bleio_scanentry_data_matches(self->data->data, self->data->len, prefixes, prefixes_len, !all);
}

View File

@ -29,14 +29,19 @@
#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANENTRY_H
#include "py/obj.h"
#include "py/objstr.h"
#include "shared-bindings/_bleio/Address.h"
typedef struct {
mp_obj_base_t base;
bool connectable;
bool scan_response;
int8_t rssi;
bleio_address_obj_t *address;
mp_obj_t data;
mp_obj_str_t *data;
uint64_t time_received;
} bleio_scanentry_obj_t;
bool bleio_scanentry_data_matches(const uint8_t* data, size_t len, const uint8_t* prefixes, size_t prefix_length, bool any);
#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANENTRY_H

View File

@ -0,0 +1,138 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Dan Halbert for Adafruit Industries
* Copyright (c) 2018 Artur Pacholec
* Copyright (c) 2017 Glenn Ruben Bakke
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <string.h>
#include "py/objstr.h"
#include "py/runtime.h"
#include "shared-bindings/_bleio/ScanEntry.h"
#include "shared-bindings/_bleio/ScanResults.h"
bleio_scanresults_obj_t* shared_module_bleio_new_scanresults(size_t buffer_size, uint8_t* prefixes, size_t prefixes_len, mp_int_t minimum_rssi) {
bleio_scanresults_obj_t* self = m_new_obj(bleio_scanresults_obj_t);
self->base.type = &bleio_scanresults_type;
ringbuf_alloc(&self->buf, buffer_size, false);
self->prefixes = prefixes;
self->prefix_length = prefixes_len;
self->minimum_rssi = minimum_rssi;
return self;
}
mp_obj_t common_hal_bleio_scanresults_next(bleio_scanresults_obj_t *self) {
while (ringbuf_count(&self->buf) == 0 && !self->done && !mp_exception_pending()) {
RUN_BACKGROUND_TASKS;
}
if (ringbuf_count(&self->buf) == 0 || mp_exception_pending()) {
return mp_const_none;
}
// Create a ScanEntry out of the data on the buffer.
uint8_t type = ringbuf_get(&self->buf);
bool connectable = (type & (1 << 0)) != 0;
bool scan_response = (type & (1 << 1)) != 0;
uint64_t ticks_ms;
ringbuf_get_n(&self->buf, (uint8_t*) &ticks_ms, sizeof(ticks_ms));
uint8_t rssi = ringbuf_get(&self->buf);
uint8_t peer_addr[NUM_BLEIO_ADDRESS_BYTES];
ringbuf_get_n(&self->buf, peer_addr, sizeof(peer_addr));
uint8_t addr_type = ringbuf_get(&self->buf);
uint16_t len;
ringbuf_get_n(&self->buf, (uint8_t*) &len, sizeof(len));
mp_obj_str_t *o = MP_OBJ_TO_PTR(mp_obj_new_bytes_of_zeros(len));
ringbuf_get_n(&self->buf, (uint8_t*) o->data, len);
bleio_scanentry_obj_t *entry = m_new_obj(bleio_scanentry_obj_t);
entry->base.type = &bleio_scanentry_type;
entry->rssi = rssi;
bleio_address_obj_t *address = m_new_obj(bleio_address_obj_t);
address->base.type = &bleio_address_type;
common_hal_bleio_address_construct(MP_OBJ_TO_PTR(address), peer_addr, addr_type);
entry->address = address;
entry->data = o;
entry->time_received = ticks_ms;
entry->connectable = connectable;
entry->scan_response = scan_response;
return MP_OBJ_FROM_PTR(entry);
}
void shared_module_bleio_scanresults_append(bleio_scanresults_obj_t* self,
uint64_t ticks_ms,
bool connectable,
bool scan_response,
int8_t rssi,
uint8_t *peer_addr,
uint8_t addr_type,
uint8_t *data,
uint16_t len) {
int32_t packet_size = sizeof(uint8_t) + sizeof(ticks_ms) + sizeof(rssi) + NUM_BLEIO_ADDRESS_BYTES +
sizeof(addr_type) + sizeof(len) + len;
int32_t empty_space = self->buf.size - ringbuf_count(&self->buf);
if (packet_size >= empty_space) {
// We can't fit the packet so skip it.
return;
}
// Filter the packet.
if (rssi < self->minimum_rssi) {
return;
}
// If any prefixes are provided, then only include packets that include at least one of them.
if (!bleio_scanentry_data_matches(data, len, self->prefixes, self->prefix_length, true)) {
return;
}
uint8_t type = 0;
if (connectable) {
type |= 1 << 0;
}
if (scan_response) {
type |= 1 << 1;
}
// Add the packet to the buffer.
ringbuf_put(&self->buf, type);
ringbuf_put_n(&self->buf, (uint8_t*) &ticks_ms, sizeof(ticks_ms));
ringbuf_put(&self->buf, rssi);
ringbuf_put_n(&self->buf, peer_addr, NUM_BLEIO_ADDRESS_BYTES);
ringbuf_put(&self->buf, addr_type);
ringbuf_put_n(&self->buf, (uint8_t*) &len, sizeof(len));
ringbuf_put_n(&self->buf, data, len);
}
bool shared_module_bleio_scanresults_get_done(bleio_scanresults_obj_t* self) {
return self->done;
}
void shared_module_bleio_scanresults_set_done(bleio_scanresults_obj_t* self, bool done) {
self->done = done;
self->common_hal_data = NULL;
}

View File

@ -0,0 +1,64 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Dan Halbert for Adafruit Industries
* 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_MODULE_BLEIO_SCANRESULTS_H
#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANRESULTS_H
#include <stdint.h>
#include "py/obj.h"
#include "py/ringbuf.h"
typedef struct {
mp_obj_base_t base;
// Pointers that needs to live until the scan is done.
void* common_hal_data;
ringbuf_t buf;
// Prefixes is a length encoded array of prefixes.
uint8_t* prefixes;
size_t prefix_length;
mp_int_t minimum_rssi;
bool active;
bool done;
} bleio_scanresults_obj_t;
bleio_scanresults_obj_t* shared_module_bleio_new_scanresults(size_t buffer_size, uint8_t* prefixes, size_t prefixes_len, mp_int_t minimum_rssi);
bool shared_module_bleio_scanresults_get_done(bleio_scanresults_obj_t* self);
void shared_module_bleio_scanresults_set_done(bleio_scanresults_obj_t* self, bool done);
void shared_module_bleio_scanresults_append(bleio_scanresults_obj_t* self,
uint64_t ticks_ms,
bool connectable,
bool scan_result,
int8_t rssi,
uint8_t *peer_addr,
uint8_t addr_type,
uint8_t* data,
uint16_t len);
#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_SCANRESULTS_H

View File

@ -76,3 +76,11 @@ void autoreload_stop() {
autoreload_delay_ms = 0;
reload_requested = false;
}
void autoreload_now() {
if (!autoreload_enabled || autoreload_suspended || reload_requested) {
return;
}
mp_raise_reload_exception();
reload_requested = true;
}

View File

@ -43,4 +43,6 @@ bool autoreload_is_enabled(void);
void autoreload_suspend(void);
void autoreload_resume(void);
void autoreload_now(void);
#endif // MICROPY_INCLUDED_SUPERVISOR_AUTORELOAD_H

View File

@ -0,0 +1,341 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Scott Shawcroft 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 "extmod/vfs.h"
#include "extmod/vfs_fat.h"
#include "shared-bindings/_bleio/__init__.h"
#include "shared-bindings/_bleio/Adapter.h"
#include "shared-bindings/_bleio/Characteristic.h"
#include "shared-bindings/_bleio/Service.h"
#include "shared-bindings/_bleio/UUID.h"
#include "common-hal/_bleio/__init__.h"
#include "supervisor/shared/autoreload.h"
#include "py/mpstate.h"
bleio_service_obj_t supervisor_ble_service;
bleio_uuid_obj_t supervisor_ble_service_uuid;
bleio_characteristic_obj_t supervisor_ble_version_characteristic;
bleio_uuid_obj_t supervisor_ble_version_uuid;
bleio_characteristic_obj_t supervisor_ble_filename_characteristic;
bleio_uuid_obj_t supervisor_ble_filename_uuid;
bleio_characteristic_obj_t supervisor_ble_length_characteristic;
bleio_uuid_obj_t supervisor_ble_length_uuid;
bleio_characteristic_obj_t supervisor_ble_contents_characteristic;
bleio_uuid_obj_t supervisor_ble_contents_uuid;
const uint8_t circuitpython_base_uuid[16] = {0x6e, 0x68, 0x74, 0x79, 0x50, 0x74, 0x69, 0x75, 0x63, 0x72, 0x69, 0x43, 0x00, 0x00, 0xaf, 0xad };
uint8_t circuitpython_advertising_data[] = { 0x02, 0x01, 0x06, 0x02, 0x0a, 0x00, 0x11, 0x07, 0x6e, 0x68, 0x74, 0x79, 0x50, 0x74, 0x69, 0x75, 0x63, 0x72, 0x69, 0x43, 0x00, 0x01, 0xaf, 0xad, 0x06, 0x08, 0x43, 0x49, 0x52, 0x43, 0x55 };
uint8_t circuitpython_scan_response_data[15] = {0x0e, 0x09, 0x43, 0x49, 0x52, 0x43, 0x55, 0x49, 0x54, 0x50, 0x59, 0x00, 0x00, 0x00, 0x00};
mp_obj_list_t service_list;
mp_obj_t service_list_items[1];
mp_obj_list_t characteristic_list;
mp_obj_t characteristic_list_items[4];
void supervisor_bluetooth_start_advertising(void) {
bool is_connected = common_hal_bleio_adapter_get_connected(&common_hal_bleio_adapter_obj);
if (is_connected) {
return;
}
// TODO: switch to Adafruit short UUID for the advertisement and add manufacturing data to distinguish ourselves from arduino.
_common_hal_bleio_adapter_start_advertising(&common_hal_bleio_adapter_obj,
true,
1.0,
circuitpython_advertising_data,
sizeof(circuitpython_advertising_data),
circuitpython_scan_response_data,
sizeof(circuitpython_scan_response_data));
}
void supervisor_start_bluetooth(void) {
common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, true);
supervisor_ble_service_uuid.base.type = &bleio_uuid_type;
common_hal_bleio_uuid_construct(&supervisor_ble_service_uuid, 0x0100, circuitpython_base_uuid);
// We know we'll only be 1 characteristic so we can statically allocate it.
characteristic_list.base.type = &mp_type_list;
characteristic_list.alloc = sizeof(characteristic_list_items) / sizeof(characteristic_list_items[0]);
characteristic_list.len = 0;
characteristic_list.items = characteristic_list_items;
mp_seq_clear(characteristic_list.items, 0, characteristic_list.alloc, sizeof(*characteristic_list.items));
const uint32_t err_code = _common_hal_bleio_service_construct(&supervisor_ble_service, &supervisor_ble_service_uuid, false /* is secondary */, &characteristic_list);
// File length
supervisor_ble_version_uuid.base.type = &bleio_uuid_type;
common_hal_bleio_uuid_construct(&supervisor_ble_version_uuid, 0x0203, circuitpython_base_uuid);
common_hal_bleio_characteristic_construct(&supervisor_ble_version_characteristic,
&supervisor_ble_service,
0, // handle (for remote only)
&supervisor_ble_version_uuid,
CHAR_PROP_READ,
SECURITY_MODE_OPEN,
SECURITY_MODE_NO_ACCESS,
4, // max length
true, // fixed length
NULL); // no initial value
uint32_t version = 1;
mp_buffer_info_t bufinfo;
bufinfo.buf = &version;
bufinfo.len = sizeof(version);
common_hal_bleio_characteristic_set_value(&supervisor_ble_version_characteristic, &bufinfo);
// Active filename.
supervisor_ble_filename_uuid.base.type = &bleio_uuid_type;
common_hal_bleio_uuid_construct(&supervisor_ble_filename_uuid, 0x0200, circuitpython_base_uuid);
common_hal_bleio_characteristic_construct(&supervisor_ble_filename_characteristic,
&supervisor_ble_service,
0, // handle (for remote only)
&supervisor_ble_filename_uuid,
CHAR_PROP_READ | CHAR_PROP_WRITE,
SECURITY_MODE_OPEN,
SECURITY_MODE_OPEN,
500, // max length
false, // fixed length
NULL); // no initial value
char code_py[] = "/code.py";
bufinfo.buf = code_py;
bufinfo.len = sizeof(code_py);
common_hal_bleio_characteristic_set_value(&supervisor_ble_filename_characteristic, &bufinfo);
// File length
supervisor_ble_length_uuid.base.type = &bleio_uuid_type;
common_hal_bleio_uuid_construct(&supervisor_ble_length_uuid, 0x0202, circuitpython_base_uuid);
common_hal_bleio_characteristic_construct(&supervisor_ble_length_characteristic,
&supervisor_ble_service,
0, // handle (for remote only)
&supervisor_ble_length_uuid,
CHAR_PROP_NOTIFY | CHAR_PROP_READ,
SECURITY_MODE_OPEN,
SECURITY_MODE_NO_ACCESS,
4, // max length
true, // fixed length
NULL); // no initial value
// File actions
supervisor_ble_contents_uuid.base.type = &bleio_uuid_type;
common_hal_bleio_uuid_construct(&supervisor_ble_contents_uuid, 0x0201, circuitpython_base_uuid);
common_hal_bleio_characteristic_construct(&supervisor_ble_contents_characteristic,
&supervisor_ble_service,
0, // handle (for remote only)
&supervisor_ble_contents_uuid,
CHAR_PROP_NOTIFY | CHAR_PROP_WRITE_NO_RESPONSE | CHAR_PROP_WRITE,
SECURITY_MODE_OPEN,
SECURITY_MODE_OPEN,
500, // max length
false, // fixed length
NULL); // no initial value
supervisor_bluetooth_start_advertising();
vm_used_ble = false;
}
FIL active_file;
volatile bool new_filename;
volatile bool run_ble_background;
bool was_connected;
void update_file_length(void) {
int32_t file_length = -1;
mp_buffer_info_t bufinfo;
bufinfo.buf = &file_length;
bufinfo.len = sizeof(file_length);
if (active_file.obj.fs != 0) {
file_length = (int32_t) f_size(&active_file);
}
common_hal_bleio_characteristic_set_value(&supervisor_ble_length_characteristic, &bufinfo);
}
void open_current_file(void) {
if (active_file.obj.fs != 0) {
return;
}
uint16_t max_len = supervisor_ble_filename_characteristic.max_length;
uint8_t path[max_len];
size_t length = common_hal_bleio_characteristic_get_value(&supervisor_ble_filename_characteristic, path, max_len - 1);
path[length] = '\0';
FATFS *fs = &((fs_user_mount_t *) MP_STATE_VM(vfs_mount_table)->obj)->fatfs;
FRESULT result = f_open(fs, &active_file, (char*) path, FA_READ | FA_WRITE);
update_file_length();
}
void close_current_file(void) {
f_close(&active_file);
}
uint32_t current_command[1024 / sizeof(uint32_t)];
volatile size_t current_offset;
void supervisor_bluetooth_background(void) {
if (!run_ble_background) {
return;
}
bool is_connected = common_hal_bleio_adapter_get_connected(&common_hal_bleio_adapter_obj);
if (!was_connected && is_connected) {
open_current_file();
} else if (was_connected && !is_connected) {
close_current_file();
new_filename = false;
}
was_connected = is_connected;
run_ble_background = false;
if (!is_connected) {
supervisor_bluetooth_start_advertising();
return;
}
if (new_filename) {
close_current_file();
open_current_file();
new_filename = false;
// get length and set the characteristic for it
}
uint16_t current_length = ((uint16_t*) current_command)[0];
if (current_length > 0 && current_length == current_offset) {
uint16_t command = ((uint16_t *) current_command)[1];
if (command == 1) {
uint16_t max_len = 20; //supervisor_ble_contents_characteristic.max_length;
uint8_t buf[max_len];
mp_buffer_info_t bufinfo;
bufinfo.buf = buf;
f_lseek(&active_file, 0);
while (f_read(&active_file, buf, max_len, &bufinfo.len) == FR_OK) {
if (bufinfo.len == 0) {
break;
}
common_hal_bleio_characteristic_set_value(&supervisor_ble_contents_characteristic, &bufinfo);
}
} else if (command == 2) { // patch
uint32_t offset = current_command[1];
uint32_t remove_length = current_command[2];
uint32_t insert_length = current_command[3];
uint32_t file_length = (int32_t) f_size(&active_file);
//uint32_t data_shift_length = fileLength - offset - remove_length;
int32_t data_shift = insert_length - remove_length;
uint32_t new_length = file_length + data_shift;
FRESULT result;
// TODO: Make these loops smarter to read and write on sector boundaries.
if (data_shift < 0) {
for (uint32_t shift_offset = offset + insert_length; shift_offset < new_length; shift_offset++) {
uint8_t data;
UINT actual;
f_lseek(&active_file, shift_offset - data_shift);
result = f_read(&active_file, &data, 1, &actual);
f_lseek(&active_file, shift_offset);
result = f_write(&active_file, &data, 1, &actual);
}
f_truncate(&active_file);
} else if (data_shift > 0) {
result = f_lseek(&active_file, file_length);
// Fill end with 0xff so we don't need to erase.
uint8_t data = 0xff;
for (size_t i = 0; i < (size_t) data_shift; i++) {
UINT actual;
result = f_write(&active_file, &data, 1, &actual);
}
for (uint32_t shift_offset = new_length - 1; shift_offset >= offset + insert_length ; shift_offset--) {
UINT actual;
f_lseek(&active_file, shift_offset - data_shift);
result = f_read(&active_file, &data, 1, &actual);
f_lseek(&active_file, shift_offset);
result = f_write(&active_file, &data, 1, &actual);
}
}
f_lseek(&active_file, offset);
uint8_t* data = (uint8_t *) (current_command + 4);
UINT written;
result = f_write(&active_file, data, insert_length, &written);
f_sync(&active_file);
// Notify the new file length.
update_file_length();
// Trigger an autoreload
autoreload_now();
}
current_offset = 0;
}
}
// This happens in an interrupt so we need to be quick.
bool supervisor_bluetooth_hook(ble_evt_t *ble_evt) {
// Catch writes to filename or contents. Length is read-only.
bool done = false;
switch (ble_evt->header.evt_id) {
case BLE_GAP_EVT_CONNECTED:
// We run our background task even if it wasn't us connected to because we may want to
// advertise if the user code stopped advertising.
run_ble_background = true;
break;
case BLE_GAP_EVT_DISCONNECTED:
run_ble_background = true;
break;
case BLE_GATTS_EVT_WRITE: {
// A client wrote to a characteristic.
ble_gatts_evt_write_t *evt_write = &ble_evt->evt.gatts_evt.params.write;
// Event handle must match the handle for my characteristic.
if (evt_write->handle == supervisor_ble_contents_characteristic.handle) {
// Handle events
//write_to_ringbuf(self, evt_write->data, evt_write->len);
// First packet includes a uint16_t le for length at the start.
uint16_t current_length = ((uint16_t*) current_command)[0];
memcpy(((uint8_t*) current_command) + current_offset, evt_write->data, evt_write->len);
current_offset += evt_write->len;
current_length = ((uint16_t*) current_command)[0];
if (current_offset == current_length) {
run_ble_background = true;
done = true;
}
} else if (evt_write->handle == supervisor_ble_filename_characteristic.handle) {
new_filename = true;
run_ble_background = true;
done = true;
} else {
return done;
}
break;
}
default:
// For debugging.
// mp_printf(&mp_plat_print, "Unhandled peripheral event: 0x%04x\n", ble_evt->header.evt_id);
break;
}
return done;
}

View File

@ -3,8 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Dan Halbert for Adafruit Industries
* Copyright (c) 2018 Artur Pacholec
* Copyright (c) 2019 Scott Shawcroft 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
@ -25,20 +24,11 @@
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CENTRAL_H
#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CENTRAL_H
#ifndef MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_H
#define MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_H
#include <stdbool.h>
void supervisor_start_bluetooth(void);
bool supervisor_bluetooth_hook(ble_evt_t *ble_evt);
void supervisor_bluetooth_background(void);
#include "py/objlist.h"
#include "shared-module/_bleio/Address.h"
typedef struct {
mp_obj_base_t base;
volatile bool waiting_to_connect;
volatile uint16_t conn_handle;
// Services discovered after connecting to a remote peripheral.
mp_obj_list_t *remote_service_list;
} bleio_central_obj_t;
#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CENTRAL_H
#endif

View File

@ -37,7 +37,8 @@ typedef enum {
MICROPY_NLR_JUMP_FAIL,
MICROPY_FATAL_ERROR,
GC_ALLOC_OUTSIDE_VM,
PROGRAMMATIC_SAFE_MODE
PROGRAMMATIC_SAFE_MODE,
NORDIC_SOFT_DEVICE_ASSERT
} safe_mode_t;
safe_mode_t wait_for_safe_mode_reset(void);

View File

@ -28,6 +28,11 @@ ifneq ($(SPI_FLASH_FILESYSTEM),)
CFLAGS += -DSPI_FLASH_FILESYSTEM=$(SPI_FLASH_FILESYSTEM) -DEXPRESS_BOARD
endif
ifeq ($(CIRCUITPY_BLEIO),1)
SRC_SUPERVISOR += supervisor/shared/bluetooth.c
endif
# Choose which flash filesystem impl to use.
# (Right now INTERNAL_FLASH_FILESYSTEM and SPI_FLASH_FILESYSTEM are mutually exclusive.
# But that might not be true in the future.)