summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Halbert <halbert@halwitz.org>2019-06-19 21:54:28 -0400
committerDan Halbert <halbert@halwitz.org>2019-06-19 21:54:28 -0400
commit24ac1fdcabdcdb02f9807192d3cb2a4210b4afc8 (patch)
treea5e72ff005802c5138c522d3502f706fbae935da
parenta1b5d800f30418a08a39e73ba4b3e276ca6c7241 (diff)
WIP: backup only; not compiled
-rw-r--r--ports/nrf/bluetooth/ble_drv.h2
-rw-r--r--ports/nrf/common-hal/bleio/Central.c312
-rw-r--r--ports/nrf/common-hal/bleio/Central.h45
-rw-r--r--ports/nrf/common-hal/bleio/Characteristic.c86
-rw-r--r--ports/nrf/common-hal/bleio/Service.h2
-rw-r--r--ports/nrf/common-hal/bleio/__init__.c10
-rw-r--r--py/circuitpy_defns.mk1
-rw-r--r--shared-bindings/bleio/Central.c186
-rw-r--r--shared-bindings/bleio/Central.h39
-rw-r--r--shared-module/bleio/Characteristic.h40
10 files changed, 663 insertions, 60 deletions
diff --git a/ports/nrf/bluetooth/ble_drv.h b/ports/nrf/bluetooth/ble_drv.h
index d349fa4e0..a066f588f 100644
--- a/ports/nrf/bluetooth/ble_drv.h
+++ b/ports/nrf/bluetooth/ble_drv.h
@@ -43,7 +43,9 @@
#define SEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000000) / (RESOLUTION))
// 0.625 msecs (625 usecs)
#define ADV_INTERVAL_UNIT_FLOAT_SECS (0.000625)
+// Microseconds is the base unit. The macros above know that.
#define UNIT_0_625_MS (625)
+#define UNIT_1_25_MS (1250)
#define UNIT_10_MS (10000)
typedef void (*ble_drv_evt_handler_t)(ble_evt_t*, void*);
diff --git a/ports/nrf/common-hal/bleio/Central.c b/ports/nrf/common-hal/bleio/Central.c
new file mode 100644
index 000000000..af5a56b76
--- /dev/null
+++ b/ports/nrf/common-hal/bleio/Central.c
@@ -0,0 +1,312 @@
+/*
+ * 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/Adapter.h"
+#include "shared-bindings/bleio/Characteristic.h"
+#include "shared-bindings/bleio/Central.h"
+#include "shared-bindings/bleio/Service.h"
+#include "shared-bindings/bleio/UUID.h"
+
+static bleio_service_obj_t *m_char_discovery_service;
+static volatile bool m_discovery_successful;
+
+STATIC bool discover_next_services(bleio_central_obj_t *self, uint16_t start_handle) {
+ m_discovery_successful = false;
+
+ uint32_t err_code = sd_ble_gattc_primary_services_discover(self->conn_handle, start_handle, NULL);
+ if (err_code != NRF_SUCCESS) {
+ mp_raise_OSError_msg(translate("Failed to discover services"));
+ }
+
+ // Serialize discovery.
+ err_code = sd_mutex_acquire(&m_discovery_mutex);
+ if (err_code != NRF_SUCCESS) {
+ mp_raise_OSError_msg(translate("Failed to acquire mutex"));
+ }
+
+ // Wait for someone else to release m_discovery_mutex.
+ while (sd_mutex_acquire(&m_discovery_mutex) == NRF_ERROR_SOC_MUTEX_ALREADY_TAKEN) {
+#ifdef MICROPY_VM_HOOK_LOOP
+ MICROPY_VM_HOOK_LOOP
+#endif
+ }
+
+ err_code = sd_mutex_release(&m_discovery_mutex);
+ if (err_code != NRF_SUCCESS) {
+ mp_raise_OSError_msg(translate("Failed to release mutex"));
+ }
+
+ return m_discovery_successful;
+}
+
+STATIC bool discover_next_characteristics(bleio_central_obj_t *self, 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;
+
+ uint32_t err_code = sd_ble_gattc_characteristics_discover(self->conn_handle, &handle_range);
+ if (err_code != NRF_SUCCESS) {
+ return false;
+ }
+
+ err_code = sd_mutex_acquire(&m_discovery_mutex);
+ if (err_code != NRF_SUCCESS) {
+ mp_raise_OSError_msg(translate("Failed to acquire mutex"));
+ }
+
+ while (sd_mutex_acquire(&m_discovery_mutex) == NRF_ERROR_SOC_MUTEX_ALREADY_TAKEN) {
+ MICROPY_VM_HOOK_LOOP;
+ }
+
+ err_code = sd_mutex_release(&m_discovery_mutex);
+ if (err_code != NRF_SUCCESS) {
+ mp_raise_OSError_msg(translate("Failed to release mutex"));
+ }
+
+ return m_discovery_successful;
+}
+
+STATIC void on_primary_srv_discovery_rsp(ble_gattc_evt_prim_srvc_disc_rsp_t *response, bleio_central_obj_t *central) {
+ 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;
+ service->device = central;
+ service->characteristic_list = mp_obj_new_list(0, NULL);
+ 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;
+
+ bleio_uuid_obj_t *uuid = m_new_obj(bleio_uuid_obj_t);
+ bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_service->uuid);
+ service->uuid = uuid;
+
+ mp_obj_list_append(central->service_list, service);
+ }
+
+ if (response->count > 0) {
+ m_discovery_successful = true;
+ }
+
+ const uint32_t err_code = sd_mutex_release(&m_discovery_mutex);
+ if (err_code != NRF_SUCCESS) {
+ mp_raise_OSError_msg(translate("Failed to release mutex"));
+ }
+}
+
+STATIC void on_char_discovery_rsp(ble_gattc_evt_char_disc_rsp_t *response, bleio_central_obj_t *central) {
+ 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 = m_new_obj(bleio_uuid_obj_t);
+ uuid->base.type = &bleio_uuid_type;
+ bleio_uuid_construct_from_nrf_ble_uuid(uuid, &gattc_char->uuid);
+ characteristic->uuid = uuid;
+
+ characteristic->props.broadcast = gattc_char->char_props.broadcast;
+ characteristic->props.indicate = gattc_char->char_props.indicate;
+ characteristic->props.notify = gattc_char->char_props.notify;
+ characteristic->props.read = gattc_char->char_props.read;
+ characteristic->props.write = gattc_char->char_props.write;
+ characteristic->props.write_no_response = gattc_char->char_props.write_wo_resp;
+ characteristic->handle = gattc_char->handle_value;
+ characteristic->service = m_char_discovery_service;
+
+ mp_obj_list_append(m_char_discovery_service->characteristic_list, MP_OBJ_FROM_PTR(characteristic));
+ }
+
+ if (response->count > 0) {
+ m_discovery_successful = true;
+ }
+
+ const uint32_t err_code = sd_mutex_release(&m_discovery_mutex);
+ if (err_code != NRF_SUCCESS) {
+ mp_raise_OSError_msg(translate("Failed to release mutex"));
+ }
+}
+
+STATIC void 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:
+ {
+ ble_gap_conn_params_t conn_params;
+ central->conn_handle = ble_evt->evt.gap_evt.conn_handle;
+
+ sd_ble_gap_ppcp_get(&conn_params);
+ sd_ble_gap_conn_param_update(ble_evt->evt.gap_evt.conn_handle, &conn_params);
+ break;
+ }
+
+ case BLE_GAP_EVT_TIMEOUT:
+ if (central->attempting_to_connect) {
+ // Signal that connection attempt has timed out.
+ central->attempting_to_connect = false;
+ }
+ break;
+
+ case BLE_GAP_EVT_DISCONNECTED:
+ central->conn_handle = BLE_CONN_HANDLE_INVALID;
+ break;
+
+ case BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP:
+ on_primary_srv_discovery_rsp(&ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp, central);
+ break;
+
+ case BLE_GATTC_EVT_CHAR_DISC_RSP:
+ on_char_discovery_rsp(&ble_evt->evt.gattc_evt.params.char_disc_rsp, central);
+ break;
+
+ case BLE_GATTS_EVT_SYS_ATTR_MISSING:
+ sd_ble_gatts_sys_attr_set(ble_evt->evt.gatts_evt.conn_handle, NULL, 0, 0);
+ break;
+
+ case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST:
+ sd_ble_gatts_exchange_mtu_reply(central->conn_handle, BLE_GATT_ATT_MTU_DEFAULT);
+ 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;
+ }
+ }
+}
+
+void common_hal_bleio_central_connect(bleio_central_obj_t *self, mp_float_t timeout) {
+ common_hal_bleio_adapter_set_enabled(true);
+ ble_drv_add_event_handler(on_ble_evt, self);
+
+ 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->attempting_to_connect = true;
+
+ uint32_t err_code = sd_ble_gap_connect(&scan_params, &m_scan_buffer);
+
+ if (err_code != NRF_SUCCESS) {
+ mp_raise_OSError_msg_varg(translate("Failed to start connecting, error 0x%04x"), err_code);
+ }
+
+ while (self->conn_handle == BLE_CONN_HANDLE_INVALID && self->attempting_to_connect) {
+ #ifdef MICROPY_VM_HOOK_LOOP
+ MICROPY_VM_HOOK_LOOP
+ #endif
+ }
+
+ if (!self->attempting_to_connect) {
+ mp_raise_OSError_msg(translate("Failed to connect: timeout"));
+ }
+
+ // Conenction successful.
+ // Now discover all services on the remote peripheral. Ask for services repeatedly
+ // until no more are left.
+
+ uint16_t next_start_handle;
+
+ next_start_handle = BLE_GATT_HANDLE_START;
+
+ while (1) {
+ if(!discover_next_services(self, discovery_start_handle)) {
+ break;
+ }
+
+ // discover_next_services() appends to service_list.
+ const mp_obj_list_t *service_list = MP_OBJ_TO_PTR(self->service_list);
+
+ // Get the most recently discovered service, and ask for services with handles
+ // starting after the last attribute handle of t
+ const bleio_service_obj_t *service = service_list->items[service_list->len - 1];
+ next_start_handle = service->end_handle + 1;
+ }
+
+ // Now, for each service, discover its characteristics.
+ // find characteristics in each service
+ const mp_obj_list_t *service_list = MP_OBJ_TO_PTR(self->service_list);
+ for (size_t i = 0; i < service_list->len; ++i) {
+ bleio_service_obj_t *service = service_list->items[i];
+
+ next_start_handle = service->start_handle;
+
+ while (1) {
+ if (!discover_next_characteristics(self, service, service->start_handle)) {
+ break;
+ }
+
+ // discover_next_characteristics() appends to the characteristic_list.
+ const mp_obj_list_t *characteristic_list = MP_OBJ_TO_PTR(service->characteristic_list);
+
+ // Get the most recently discovered characteristic.
+ const bleio_characteristic_obj_t *characteristic =
+ characteristic_list->items[characteristic_list->len - 1];
+ next_start_handle = characteristic->handle + 1;
+ if (next_start_handle >= service->end_handle) {
+ // Went past the end of the range of handles for this service.
+ break;
+ }
+ }
+ }
+}
+
+void common_hal_bleio_central_disconnect(bleio_central_obj_t *self) {
+ sd_ble_gap_disconnect(self->conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
+}
diff --git a/ports/nrf/common-hal/bleio/Central.h b/ports/nrf/common-hal/bleio/Central.h
new file mode 100644
index 000000000..c44cf3b77
--- /dev/null
+++ b/ports/nrf/common-hal/bleio/Central.h
@@ -0,0 +1,45 @@
+/*
+ * 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_CENTRAL_H
+#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CENTRAL_H
+
+#include <stdbool.h>
+
+#include "shared-module/bleio/Address.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ mp_obj_t remote_name;
+ bleio_address_obj_t address;
+ volatile bool attempting_to_connect;
+ volatile uint16_t conn_handle;
+ mp_obj_t service_list;
+ mp_obj_t conn_handler;
+} bleio_central_obj_t;
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CENTRAL_H
diff --git a/ports/nrf/common-hal/bleio/Characteristic.c b/ports/nrf/common-hal/bleio/Characteristic.c
index 346cfefbc..70ebd7aba 100644
--- a/ports/nrf/common-hal/bleio/Characteristic.c
+++ b/ports/nrf/common-hal/bleio/Characteristic.c
@@ -37,9 +37,6 @@
#include "common-hal/bleio/Characteristic.h"
STATIC volatile bleio_characteristic_obj_t *m_read_characteristic;
-STATIC volatile uint8_t m_tx_in_progress;
-// Serialize gattc writes that send a response. This might be done per object?
-STATIC nrf_mutex_t *m_write_mutex;
STATIC uint16_t get_cccd(bleio_characteristic_obj_t *characteristic) {
const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device);
@@ -118,17 +115,21 @@ STATIC void gatts_notify_indicate(bleio_characteristic_obj_t *characteristic, mp
.p_data = bufinfo->buf,
};
- while (m_tx_in_progress >= MAX_TX_IN_PROGRESS) {
-#ifdef MICROPY_VM_HOOK_LOOP
- MICROPY_VM_HOOK_LOOP
-#endif
- }
-
const uint16_t conn_handle = common_hal_bleio_device_get_conn_handle(characteristic->service->device);
- m_tx_in_progress++;
- const uint32_t err_code = sd_ble_gatts_hvx(conn_handle, &hvx_params);
- if (err_code != NRF_SUCCESS) {
- m_tx_in_progress--;
+
+ while (1) {
+ const uint32_t err_code = sd_ble_gatts_hvx(conn_handle, &hvx_params);
+ if (err_code == NRF_SUCCESS) {
+ break;
+ }
+ // TX buffer is full
+ // We could wait for an event indicating the write is complete, but just retrying is easier.
+ if (err_code == NRF_ERROR_RESOURCES) {
+ MICROPY_VM_HOOK_LOOP;
+ continue;
+ }
+
+ // Some real error has occurred.
mp_raise_OSError_msg_varg(translate("Failed to notify or indicate attribute value, err 0x%04x"), err_code);
}
@@ -144,11 +145,8 @@ STATIC void gattc_read(bleio_characteristic_obj_t *characteristic) {
mp_raise_OSError_msg_varg(translate("Failed to read attribute value, err 0x%04x"), err_code);
}
-//
while (m_read_characteristic != NULL) {
-#ifdef MICROPY_VM_HOOK_LOOP
- MICROPY_VM_HOOK_LOOP
-#endif
+ MICROPY_VM_HOOK_LOOP;
}
}
@@ -158,67 +156,47 @@ STATIC void gattc_write(bleio_characteristic_obj_t *characteristic, mp_buffer_in
ble_gattc_write_params_t write_params = {
.flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_CANCEL,
- .write_op = BLE_GATT_OP_WRITE_REQ,
+ .write_op = characteristic->props.write_no_response ? BLE_GATT_OP_WRITE_CMD : BLE_GATT_OP_WRITE_REQ,
.handle = characteristic->handle,
.p_value = bufinfo->buf,
.len = bufinfo->len,
};
- if (characteristic->props.write_no_response) {
- write_params.write_op = BLE_GATT_OP_WRITE_CMD;
+ while (1) {
+ uint32 err_code = sd_ble_gattc_write(conn_handle, &write_params);
+ if (err_code == NRF_SUCCESS) {
+ break;
+ }
- err_code = sd_mutex_acquire(m_write_mutex);
- if (err_code != NRF_SUCCESS) {
- mp_raise_OSError_msg_varg(translate("Failed to acquire mutex, err 0x%04x"), err_code);
+ // Write with response will return NRF_ERROR_BUSY if the response has not been received.
+ // Write without reponse will return NRF_ERROR_RESOURCES if too many writes are pending.
+ if (err_code == NRF_ERROR_BUSY || err_code == NRF_ERROR_RESOURCES) {
+ // We could wait for an event indicating the write is complete, but just retrying is easier.
+ MICROPY_VM_HOOK_LOOP;
+ continue;
}
- }
- err_code = sd_ble_gattc_write(conn_handle, &write_params);
- if (err_code != NRF_SUCCESS) {
+ // Some real error occurred.
mp_raise_OSError_msg_varg(translate("Failed to write attribute value, err 0x%04x"), err_code);
}
- while (sd_mutex_acquire(m_write_mutex) == NRF_ERROR_SOC_MUTEX_ALREADY_TAKEN) {
-#ifdef MICROPY_VM_HOOK_LOOP
- MICROPY_VM_HOOK_LOOP
-#endif
- }
-
- err_code = sd_mutex_release(m_write_mutex);
- if (err_code != NRF_SUCCESS) {
- mp_raise_OSError_msg_varg(translate("Failed to release mutex, err 0x%04x"), err_code);
- }
}
STATIC void characteristic_on_ble_evt(ble_evt_t *ble_evt, void *param) {
switch (ble_evt->header.evt_id) {
- case BLE_GATTS_EVT_HVN_TX_COMPLETE:
- {
- uint8_t count = ble_evt->evt.gatts_evt.params.hvn_tx_complete.count;
- // Don't underflow the count.
- if (count >= m_tx_in_progress) {
- m_tx_in_progress = 0;
- } else {
- m_tx_in_progress -= count;
- }
- break;
- }
+
+ // 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;
m_read_characteristic->value_data = mp_obj_new_bytearray(response->len, response->data);
- // Flag to busy-wait loop that we've read the characteristic.
+ // Indicate to busy-wait loop that we've read the characteristic.
m_read_characteristic = NULL;
break;
}
- case BLE_GATTC_EVT_WRITE_RSP:
- // Someone else can write now.
- sd_mutex_release(m_write_mutex);
- break;
-
- // For debugging.
+ // For debugging.
default:
// mp_printf(&mp_plat_print, "Unhandled characteristic event: 0x%04x\n", ble_evt->header.evt_id);
break;
diff --git a/ports/nrf/common-hal/bleio/Service.h b/ports/nrf/common-hal/bleio/Service.h
index b5acf39ae..dc794ff28 100644
--- a/ports/nrf/common-hal/bleio/Service.h
+++ b/ports/nrf/common-hal/bleio/Service.h
@@ -32,12 +32,14 @@
typedef struct {
mp_obj_base_t base;
+ // Handle for this service.
uint16_t handle;
bool is_secondary;
bleio_uuid_obj_t *uuid;
// May be a Peripheral, Central, etc.
mp_obj_t *device;
mp_obj_t characteristic_list;
+ // Range of attribute handles of this service.
uint16_t start_handle;
uint16_t end_handle;
} bleio_service_obj_t;
diff --git a/ports/nrf/common-hal/bleio/__init__.c b/ports/nrf/common-hal/bleio/__init__.c
index 58b81f066..699dcc57f 100644
--- a/ports/nrf/common-hal/bleio/__init__.c
+++ b/ports/nrf/common-hal/bleio/__init__.c
@@ -49,9 +49,8 @@ const super_adapter_obj_t common_hal_bleio_adapter_obj = {
gatt_role_t common_hal_bleio_device_get_gatt_role(mp_obj_t device) {
if (MP_OBJ_IS_TYPE(device, &bleio_peripheral_type)) {
return ((bleio_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->gatt_role;
-// Does not exist yet.
-// } else if (MP_OBJ_IS_TYPE(device, &bleio_central_type)) {
-// return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->gatt_role;
+ } else if (MP_OBJ_IS_TYPE(device, &bleio_central_type)) {
+ return ((bleio_central_obj_t*) MP_OBJ_TO_PTR(device))->gatt_role;
} else {
return GATT_ROLE_NONE;
}
@@ -60,9 +59,8 @@ gatt_role_t common_hal_bleio_device_get_gatt_role(mp_obj_t device) {
uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device) {
if (MP_OBJ_IS_TYPE(device, &bleio_peripheral_type)) {
return ((bleio_peripheral_obj_t*) MP_OBJ_TO_PTR(device))->conn_handle;
-// Does not exist yet.
-// } else if (MP_OBJ_IS_TYPE(device, &bleio_central_type)) {
-// return ((bleio_central_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 0;
}
diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk
index 435cf2c33..782314fc7 100644
--- a/py/circuitpy_defns.mk
+++ b/py/circuitpy_defns.mk
@@ -224,6 +224,7 @@ $(filter $(SRC_PATTERNS), \
audioio/AudioOut.c \
bleio/__init__.c \
bleio/Adapter.c \
+ bleio/Central.c \
bleio/Characteristic.c \
bleio/CharacteristicBuffer.c \
bleio/Descriptor.c \
diff --git a/shared-bindings/bleio/Central.c b/shared-bindings/bleio/Central.c
new file mode 100644
index 000000000..e5fd66348
--- /dev/null
+++ b/shared-bindings/bleio/Central.c
@@ -0,0 +1,186 @@
+/*
+ * 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/AddressType.h"
+#include "shared-bindings/bleio/Characteristic.h"
+#include "shared-bindings/bleio/Central.h"
+#include "shared-bindings/bleio/Service.h"
+#include "shared-bindings/bleio/UUID.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 == 'MyCentral':
+//| my_entry = entry
+//| break
+//|
+//| central = bleio.Central(my_entry.address)
+//| central.connect()
+//|
+
+//| .. class:: Central(address=None, scan_entry=None)
+//|
+//| Create a new Central object. If the `address` or :py:data:`scan_entry` parameters are not `None`,
+//| the role is set to Central, otherwise it's set to Peripheral.
+//|
+//| :param bleio.Address address: The address of the central to connect to
+//| :param bleio.ScanEntry scan_entry: The scan entry returned from `bleio.Scanner`
+//|
+STATIC mp_obj_t bleio_central_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *pos_args) {
+ mp_arg_check_num(n_args, n_kw, 0, 1, true);
+ bleio_central_obj_t *self = m_new_obj(bleio_central_obj_t);
+ self->base.type = &bleio_central_type;
+ self->service_list = mp_obj_new_list(0, NULL);
+ self->conn_handler = mp_const_none;
+ self->conn_handle = 0xFFFF;
+
+ mp_map_t kw_args;
+ mp_map_init_fixed_table(&kw_args, n_kw, pos_args + n_args);
+
+ enum { ARG_address, ARG_scan_entry };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_address, MP_ARG_OBJ, {.u_obj = mp_const_none} },
+ { MP_QSTR_scan_entry, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ const mp_obj_t address_obj = args[ARG_address].u_obj;
+ const mp_obj_t scan_entry_obj = args[ARG_scan_entry].u_obj;
+
+ if (!MP_OBJ_IS_TYPE(address_obj, &bleio_address_type)) {
+ mp_raise_ValueError("Expected an Address");
+ }
+
+ return MP_OBJ_FROM_PTR(self);
+}
+
+
+
+//| .. method:: connect()
+//|
+//| Attempts a connection to the remote peripheral. If the connection is successful,
+//|
+STATIC mp_obj_t bleio_central_connect(mp_obj_t self_in) {
+ bleio_central_obj_t *self = MP_OBJ_TO_PTR(self_in);
+
+ if (self->is_peripheral) {
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
+ translate("Can't connect in Peripheral mode")));
+ }
+
+ common_hal_bleio_central_connect(self);
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_central_connect_obj, 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);
+
+//| .. attribute:: remote_name (read-only)
+//|
+//| The name of the remote peripheral, if connected. May be None if no name was advertised.
+//|
+STATIC mp_obj_t bleio_central_get_remote_name(mp_obj_t self_in) {
+ bleio_central_obj_t *self = MP_OBJ_TO_PTR(self_in);
+
+ return self->remote_name;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(bleio_central_get_remote_name_obj, bleio_central_get_remote_name);
+
+//| .. attribute:: remote_services (read-only)
+//|
+//| Empty until connected, then a list of services provided by the remote peripheral.
+//|
+STATIC mp_obj_t bleio_central_get_remote_services(mp_obj_t self_in) {
+ bleio_central_obj_t *self = MP_OBJ_TO_PTR(self_in);
+
+ return self->service_list;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_central_get_remote_services_obj, bleio_central_get_remote_services);
+
+const mp_obj_property_t bleio_central_remote_services_obj = {
+ .base.type = &mp_type_property,
+ .proxy = { (mp_obj_t)&bleio_central_get_remote_services_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) },
+
+ // Properties
+ { MP_ROM_QSTR(MP_QSTR_remote_name), MP_ROM_PTR(&bleio_central_remote_name_obj) },
+ { MP_ROM_QSTR(MP_QSTR_remote_services), MP_ROM_PTR(&bleio_central_remote_services_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
+};
diff --git a/shared-bindings/bleio/Central.h b/shared-bindings/bleio/Central.h
new file mode 100644
index 000000000..5ea2119af
--- /dev/null
+++ b/shared-bindings/bleio/Central.h
@@ -0,0 +1,39 @@
+/*
+ * 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 "common-hal/bleio/Central.h"
+#include "common-hal/bleio/Service.h"
+
+extern const mp_obj_type_t bleio_device_type;
+
+extern void common_hal_bleio_device_connect(bleio_device_obj_t *device);
+extern void common_hal_bleio_device_disconnect(bleio_device_obj_t *device);
+
+#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_CENTRAL_H
diff --git a/shared-module/bleio/Characteristic.h b/shared-module/bleio/Characteristic.h
new file mode 100644
index 000000000..27b43588f
--- /dev/null
+++ b/shared-module/bleio/Characteristic.h
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Dan Halbert for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H
+#define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H
+
+// Flags for each characteristic property. Common across ports.
+typedef struct {
+ bool broadcast : 1;
+ bool read : 1;
+ bool write_no_response : 1;
+ bool write : 1;
+ bool notify : 1;
+ bool indicate : 1;
+} bleio_characteristic_properties_t;
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H