|
4 | 4 | * The MIT License (MIT)
|
5 | 5 | *
|
6 | 6 | * Copyright (c) 2017 Glenn Ruben Bakke
|
| 7 | + * Copyright (c) 2018 Artur Pacholec |
7 | 8 | *
|
8 | 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
9 | 10 | * of this software and associated documentation files (the "Software"), to deal
|
|
24 | 25 | * THE SOFTWARE.
|
25 | 26 | */
|
26 | 27 |
|
27 |
| -#if BLUETOOTH_SD |
28 |
| - |
29 | 28 | #include <string.h>
|
| 29 | + |
| 30 | +#include "ble.h" |
30 | 31 | #include "ble_uart.h"
|
31 | 32 | #include "ringbuffer.h"
|
32 | 33 | #include "py/mphal.h"
|
| 34 | +#include "py/runtime.h" |
33 | 35 | #include "lib/utils/interrupt_char.h"
|
| 36 | +#include "shared-bindings/bleio/Adapter.h" |
| 37 | +#include "shared-bindings/bleio/Characteristic.h" |
| 38 | +#include "shared-bindings/bleio/Device.h" |
| 39 | +#include "shared-bindings/bleio/Service.h" |
| 40 | +#include "shared-bindings/bleio/UUID.h" |
34 | 41 |
|
35 |
| -#if MICROPY_PY_BLE_NUS |
| 42 | +#if (MICROPY_PY_BLE_NUS == 1) |
36 | 43 |
|
37 |
| -static bleio_uuid_obj_t uuid_obj_service = { |
38 |
| - .base.type = &bleio_uuid_type, |
39 |
| - .type = UUID_128_BIT, |
40 |
| - .value = {0x01, 0x00} |
41 |
| -}; |
| 44 | +static const char default_name[] = "CP-REPL"; // max 8 chars or uuid won't fit in adv data |
| 45 | +static const char NUS_UUID[] = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"; |
42 | 46 |
|
43 |
| -static bleio_uuid_obj_t uuid_obj_char_tx = { |
44 |
| - .base.type = &bleio_uuid_type, |
45 |
| - .type = UUID_128_BIT, |
46 |
| - .value = {0x03, 0x00} |
47 |
| -}; |
| 47 | +#define NUS_RX_UUID 0x0002 |
| 48 | +#define NUS_TX_UUID 0x0003 |
| 49 | +#define BUFFER_SIZE 128 |
48 | 50 |
|
49 |
| -static bleio_uuid_obj_t uuid_obj_char_rx = { |
50 |
| - .base.type = &bleio_uuid_type, |
51 |
| - .type = UUID_128_BIT, |
52 |
| - .value = {0x02, 0x00} |
53 |
| -}; |
| 51 | +ringBuffer_typedef(uint8_t, ringbuffer_t); |
54 | 52 |
|
55 |
| -static ubluepy_service_obj_t ble_uart_service = { |
56 |
| - .base.type = &ubluepy_service_type, |
57 |
| - .p_uuid = &uuid_obj_service, |
58 |
| - .type = UBLUEPY_SERVICE_PRIMARY |
59 |
| -}; |
| 53 | +static bleio_device_obj_t m_device; |
| 54 | +static bleio_service_obj_t *m_nus; |
| 55 | +static bleio_characteristic_obj_t *m_tx_chara; |
| 56 | +static bleio_characteristic_obj_t *m_rx_chara; |
60 | 57 |
|
61 |
| -static ubluepy_characteristic_obj_t ble_uart_char_rx = { |
62 |
| - .base.type = &ubluepy_characteristic_type, |
63 |
| - .p_uuid = &uuid_obj_char_rx, |
64 |
| - .props = UBLUEPY_PROP_WRITE | UBLUEPY_PROP_WRITE_WO_RESP, |
65 |
| - .attrs = 0, |
66 |
| -}; |
| 58 | +static volatile bool m_cccd_enabled; |
67 | 59 |
|
68 |
| -static ubluepy_characteristic_obj_t ble_uart_char_tx = { |
69 |
| - .base.type = &ubluepy_characteristic_type, |
70 |
| - .p_uuid = &uuid_obj_char_tx, |
71 |
| - .props = UBLUEPY_PROP_NOTIFY, |
72 |
| - .attrs = UBLUEPY_ATTR_CCCD, |
| 60 | +static uint8_t m_rx_ring_buffer_data[BUFFER_SIZE]; |
| 61 | +static ringbuffer_t m_rx_ring_buffer = { |
| 62 | + .size = sizeof(m_rx_ring_buffer_data) + 1, |
| 63 | + .elems = m_rx_ring_buffer_data, |
73 | 64 | };
|
74 | 65 |
|
75 |
| -static ubluepy_peripheral_obj_t ble_uart_peripheral = { |
76 |
| - .base.type = &ubluepy_peripheral_type, |
77 |
| - .conn_handle = 0xFFFF, |
78 |
| -}; |
| 66 | +STATIC void on_ble_evt(ble_evt_t *ble_evt, void *param) { |
| 67 | + switch (ble_evt->header.evt_id) { |
| 68 | + case BLE_GAP_EVT_DISCONNECTED: |
| 69 | + { |
| 70 | + mp_obj_t device_obj = MP_OBJ_FROM_PTR(&m_device); |
| 71 | + mp_call_function_0(mp_load_attr(device_obj, qstr_from_str("start_advertising"))); |
| 72 | + break; |
| 73 | + } |
79 | 74 |
|
80 |
| -static volatile bool m_cccd_enabled; |
81 |
| -static volatile bool m_connected; |
| 75 | + case BLE_GATTS_EVT_WRITE: |
| 76 | + { |
| 77 | + ble_gatts_evt_write_t *write = &ble_evt->evt.gatts_evt.params.write; |
| 78 | + |
| 79 | + if (write->handle == m_tx_chara->cccd_handle) { |
| 80 | + m_cccd_enabled = true; |
| 81 | + } else if (write->handle == m_rx_chara->handle) { |
| 82 | + for (size_t i = 0; i < write->len; ++i) { |
| 83 | +#if MICROPY_KBD_EXCEPTION |
| 84 | + if (write->data[i] == mp_interrupt_char) { |
| 85 | + mp_keyboard_interrupt(); |
| 86 | + } else |
| 87 | +#endif |
| 88 | + { |
| 89 | + bufferWrite(&m_rx_ring_buffer, write->data[i]); |
| 90 | + } |
| 91 | + } |
| 92 | + } |
| 93 | + } |
| 94 | + } |
| 95 | +} |
82 | 96 |
|
83 |
| -ringBuffer_typedef(uint8_t, ringbuffer_t); |
| 97 | +void ble_uart_init(void) { |
| 98 | + mp_obj_t device_obj = MP_OBJ_FROM_PTR(&m_device); |
| 99 | + m_device.base.type = &bleio_device_type; |
| 100 | + m_device.service_list = mp_obj_new_list(0, NULL); |
| 101 | + m_device.notif_handler = mp_const_none; |
| 102 | + m_device.conn_handler = mp_const_none; |
| 103 | + m_device.conn_handle = 0xFFFF; |
| 104 | + m_device.is_peripheral = true; |
| 105 | + m_device.name = mp_obj_new_str(default_name, strlen(default_name), false); |
| 106 | + common_hal_bleio_adapter_get_address(&m_device.address); |
| 107 | + |
| 108 | + mp_obj_t nus_uuid_str = mp_obj_new_str(NUS_UUID, strlen(NUS_UUID), false); |
| 109 | + mp_obj_t nus_uuid_obj = bleio_uuid_type.make_new(&bleio_uuid_type, 1, 0, &nus_uuid_str); |
| 110 | + mp_obj_t nus_obj = bleio_service_type.make_new(&bleio_service_type, 1, 0, &nus_uuid_obj); |
| 111 | + m_nus = MP_OBJ_TO_PTR(nus_obj); |
| 112 | + mp_call_function_1(mp_load_attr(device_obj, qstr_from_str("add_service")), nus_obj); |
| 113 | + |
| 114 | + mp_obj_t tx_uuid_int = mp_obj_new_int(NUS_TX_UUID); |
| 115 | + mp_obj_t tx_uuid_obj = bleio_uuid_type.make_new(&bleio_uuid_type, 1, 0, &tx_uuid_int); |
| 116 | + mp_obj_t tx_obj = bleio_characteristic_type.make_new(&bleio_characteristic_type, 1, 0, &tx_uuid_obj); |
| 117 | + m_tx_chara = MP_OBJ_TO_PTR(tx_obj); |
| 118 | + m_tx_chara->uuid->type = UUID_TYPE_128BIT; |
| 119 | + m_tx_chara->uuid->uuid_vs_idx = m_nus->uuid->uuid_vs_idx; |
| 120 | + m_tx_chara->props.notify = true; |
| 121 | + mp_call_function_1(mp_load_attr(nus_obj, qstr_from_str("add_characteristic")), tx_obj); |
| 122 | + |
| 123 | + mp_obj_t rx_uuid_int = mp_obj_new_int(NUS_RX_UUID); |
| 124 | + mp_obj_t rx_uuid_obj = bleio_uuid_type.make_new(&bleio_uuid_type, 1, 0, &rx_uuid_int); |
| 125 | + mp_obj_t rx_obj = bleio_characteristic_type.make_new(&bleio_characteristic_type, 1, 0, &rx_uuid_obj); |
| 126 | + m_rx_chara = MP_OBJ_TO_PTR(rx_obj); |
| 127 | + m_rx_chara->uuid->type = UUID_TYPE_128BIT; |
| 128 | + m_rx_chara->uuid->uuid_vs_idx = m_nus->uuid->uuid_vs_idx; |
| 129 | + m_rx_chara->props.write = true; |
| 130 | + m_rx_chara->props.write_wo_resp = true; |
| 131 | + mp_call_function_1(mp_load_attr(nus_obj, qstr_from_str("add_characteristic")), rx_obj); |
| 132 | + |
| 133 | + mp_call_function_0(mp_load_attr(device_obj, qstr_from_str("start_advertising"))); |
| 134 | + |
| 135 | + ble_drv_add_event_handler(on_ble_evt, &m_device); |
84 | 136 |
|
85 |
| -static ringbuffer_t m_rx_ring_buffer; |
86 |
| -static ringbuffer_t * mp_rx_ring_buffer = &m_rx_ring_buffer; |
87 |
| -static uint8_t m_rx_ring_buffer_data[128]; |
| 137 | + m_cccd_enabled = false; |
88 | 138 |
|
89 |
| -static ubluepy_advertise_data_t m_adv_data_uart_service; |
| 139 | + while (!m_cccd_enabled) { |
| 140 | +#ifdef MICROPY_VM_HOOK_LOOP |
| 141 | + MICROPY_VM_HOOK_LOOP |
| 142 | +#endif |
| 143 | + } |
| 144 | +} |
90 | 145 |
|
91 |
| -#if BLUETOOTH_WEBBLUETOOTH_REPL |
92 |
| -static ubluepy_advertise_data_t m_adv_data_eddystone_url; |
93 |
| -#endif // BLUETOOTH_WEBBLUETOOTH_REPL |
| 146 | +bool ble_uart_connected(void) { |
| 147 | + return (m_device.conn_handle != BLE_CONN_HANDLE_INVALID); |
| 148 | +} |
94 | 149 |
|
95 |
| -int mp_hal_stdin_rx_chr(void) { |
96 |
| - while (isBufferEmpty(mp_rx_ring_buffer)) { |
97 |
| - ; |
| 150 | +char ble_uart_rx_chr(void) { |
| 151 | + while (isBufferEmpty(&m_rx_ring_buffer)) { |
| 152 | +#ifdef MICROPY_VM_HOOK_LOOP |
| 153 | + MICROPY_VM_HOOK_LOOP |
| 154 | +#endif |
98 | 155 | }
|
99 | 156 |
|
100 | 157 | uint8_t byte;
|
101 |
| - bufferRead(mp_rx_ring_buffer, byte); |
| 158 | + bufferRead(&m_rx_ring_buffer, byte); |
102 | 159 | return (int)byte;
|
103 | 160 | }
|
104 | 161 |
|
105 |
| -bool mp_hal_stdin_any(void) { |
106 |
| - return !isBufferEmpty(mp_rx_ring_buffer); |
| 162 | +bool ble_uart_stdin_any(void) { |
| 163 | + return !isBufferEmpty(&m_rx_ring_buffer); |
| 164 | +} |
| 165 | + |
| 166 | +void ble_uart_stdout_tx_str(const char *text) { |
| 167 | + mp_hal_stdout_tx_strn(text, strlen(text)); |
| 168 | +} |
| 169 | + |
| 170 | +int mp_hal_stdin_rx_chr(void) { |
| 171 | + return ble_uart_rx_chr(); |
107 | 172 | }
|
108 | 173 |
|
109 | 174 | void mp_hal_stdout_tx_strn(const char *str, size_t len) {
|
110 |
| - uint8_t *buf = (uint8_t *)str; |
111 | 175 | size_t send_len;
|
112 | 176 |
|
113 | 177 | while (len > 0) {
|
114 |
| - if (len >= 20) { |
115 |
| - send_len = 20; // (GATT_MTU_SIZE_DEFAULT - 3) |
| 178 | + if (len >= BLE_GATT_ATT_MTU_DEFAULT - 3) { |
| 179 | + send_len = (BLE_GATT_ATT_MTU_DEFAULT - 3); |
116 | 180 | } else {
|
117 | 181 | send_len = len;
|
118 | 182 | }
|
119 | 183 |
|
120 |
| - ubluepy_characteristic_obj_t * p_char = &ble_uart_char_tx; |
| 184 | + mp_buffer_info_t bufinfo = { |
| 185 | + .buf = (uint8_t*)str, |
| 186 | + .len = send_len, |
| 187 | + }; |
121 | 188 |
|
122 |
| - ble_drv_attr_s_notify(p_char->p_service->p_periph->conn_handle, |
123 |
| - p_char->handle, |
124 |
| - send_len, |
125 |
| - buf); |
| 189 | + common_hal_bleio_characteristic_write_value(m_tx_chara, &bufinfo); |
126 | 190 |
|
127 | 191 | len -= send_len;
|
128 |
| - buf += send_len; |
| 192 | + str += send_len; |
129 | 193 | }
|
130 | 194 | }
|
131 | 195 |
|
132 |
| -void mp_hal_stdout_tx_strn_cooked(const char *str, mp_uint_t len) { |
133 |
| - mp_hal_stdout_tx_strn(str, len); |
134 |
| -} |
135 |
| - |
136 |
| -STATIC void gap_event_handler(mp_obj_t self_in, uint16_t event_id, uint16_t conn_handle, uint16_t length, uint8_t * data) { |
137 |
| - ubluepy_peripheral_obj_t * self = MP_OBJ_TO_PTR(self_in); |
138 |
| - |
139 |
| - if (event_id == 16) { // connect event |
140 |
| - self->conn_handle = conn_handle; |
141 |
| - m_connected = true; |
142 |
| - } else if (event_id == 17) { // disconnect event |
143 |
| - self->conn_handle = 0xFFFF; // invalid connection handle |
144 |
| - m_connected = false; |
145 |
| - ble_uart_advertise(); |
146 |
| - } |
147 |
| -} |
148 |
| - |
149 |
| -STATIC void gatts_event_handler(mp_obj_t self_in, uint16_t event_id, uint16_t attr_handle, uint16_t length, uint8_t * data) { |
150 |
| - ubluepy_peripheral_obj_t * self = MP_OBJ_TO_PTR(self_in); |
151 |
| - (void)self; |
152 |
| - |
153 |
| - if (event_id == 80) { // gatts write |
154 |
| - if (ble_uart_char_tx.cccd_handle == attr_handle) { |
155 |
| - m_cccd_enabled = true; |
156 |
| - } else if (ble_uart_char_rx.handle == attr_handle) { |
157 |
| - for (uint16_t i = 0; i < length; i++) { |
158 |
| - #if MICROPY_KBD_EXCEPTION |
159 |
| - if (data[i] == mp_interrupt_char) { |
160 |
| - mp_keyboard_interrupt(); |
161 |
| - } else |
162 |
| - #endif |
163 |
| - { |
164 |
| - bufferWrite(mp_rx_ring_buffer, data[i]); |
165 |
| - } |
166 |
| - } |
167 |
| - } |
168 |
| - } |
169 |
| -} |
170 |
| - |
171 |
| -void ble_uart_init0(void) { |
172 |
| - uint8_t base_uuid[] = {0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x00, 0x00, 0x40, 0x6E}; |
173 |
| - uint8_t uuid_vs_idx; |
174 |
| - |
175 |
| - (void)ble_drv_uuid_add_vs(base_uuid, &uuid_vs_idx); |
176 |
| - |
177 |
| - uuid_obj_service.uuid_vs_idx = uuid_vs_idx; |
178 |
| - uuid_obj_char_tx.uuid_vs_idx = uuid_vs_idx; |
179 |
| - uuid_obj_char_rx.uuid_vs_idx = uuid_vs_idx; |
180 |
| - |
181 |
| - (void)ble_drv_service_add(&ble_uart_service); |
182 |
| - ble_uart_service.char_list = mp_obj_new_list(0, NULL); |
183 |
| - |
184 |
| - // add TX characteristic |
185 |
| - ble_uart_char_tx.service_handle = ble_uart_service.handle; |
186 |
| - bool retval = ble_drv_characteristic_add(&ble_uart_char_tx); |
187 |
| - if (retval) { |
188 |
| - ble_uart_char_tx.p_service = &ble_uart_service; |
189 |
| - } |
190 |
| - mp_obj_list_append(ble_uart_service.char_list, MP_OBJ_FROM_PTR(&ble_uart_char_tx)); |
191 |
| - |
192 |
| - // add RX characteristic |
193 |
| - ble_uart_char_rx.service_handle = ble_uart_service.handle; |
194 |
| - retval = ble_drv_characteristic_add(&ble_uart_char_rx); |
195 |
| - if (retval) { |
196 |
| - ble_uart_char_rx.p_service = &ble_uart_service; |
197 |
| - } |
198 |
| - mp_obj_list_append(ble_uart_service.char_list, MP_OBJ_FROM_PTR(&ble_uart_char_rx)); |
199 |
| - |
200 |
| - // setup the peripheral |
201 |
| - ble_uart_peripheral.service_list = mp_obj_new_list(0, NULL); |
202 |
| - mp_obj_list_append(ble_uart_peripheral.service_list, MP_OBJ_FROM_PTR(&ble_uart_service)); |
203 |
| - ble_uart_service.p_periph = &ble_uart_peripheral; |
204 |
| - |
205 |
| - ble_drv_gap_event_handler_set(MP_OBJ_FROM_PTR(&ble_uart_peripheral), gap_event_handler); |
206 |
| - ble_drv_gatts_event_handler_set(MP_OBJ_FROM_PTR(&ble_uart_peripheral), gatts_event_handler); |
207 |
| - |
208 |
| - ble_uart_peripheral.conn_handle = 0xFFFF; |
209 |
| - |
210 |
| - char device_name[] = "mpus"; |
211 |
| - |
212 |
| - mp_obj_t service_list = mp_obj_new_list(0, NULL); |
213 |
| - mp_obj_list_append(service_list, MP_OBJ_FROM_PTR(&ble_uart_service)); |
214 |
| - |
215 |
| - mp_obj_t * services = NULL; |
216 |
| - mp_uint_t num_services; |
217 |
| - mp_obj_get_array(service_list, &num_services, &services); |
218 |
| - |
219 |
| - m_adv_data_uart_service.p_services = services; |
220 |
| - m_adv_data_uart_service.num_of_services = num_services; |
221 |
| - m_adv_data_uart_service.p_device_name = (uint8_t *)device_name; |
222 |
| - m_adv_data_uart_service.device_name_len = strlen(device_name); |
223 |
| - m_adv_data_uart_service.connectable = true; |
224 |
| - m_adv_data_uart_service.p_data = NULL; |
225 |
| - |
226 |
| -#if BLUETOOTH_WEBBLUETOOTH_REPL |
227 |
| - // for now point eddystone URL to https://goo.gl/x46FES => https://glennrub.github.io/webbluetooth/micropython/repl/ |
228 |
| - static uint8_t eddystone_url_data[27] = {0x2, 0x1, 0x6, |
229 |
| - 0x3, 0x3, 0xaa, 0xfe, |
230 |
| - 19, 0x16, 0xaa, 0xfe, 0x10, 0xee, 0x3, 'g', 'o', 'o', '.', 'g', 'l', '/', 'x', '4', '6', 'F', 'E', 'S'}; |
231 |
| - // eddystone url adv data |
232 |
| - m_adv_data_eddystone_url.p_data = eddystone_url_data; |
233 |
| - m_adv_data_eddystone_url.data_len = sizeof(eddystone_url_data); |
234 |
| - m_adv_data_eddystone_url.connectable = false; |
235 |
| -#endif |
236 |
| - |
237 |
| - m_cccd_enabled = false; |
238 |
| - |
239 |
| - // initialize ring buffer |
240 |
| - m_rx_ring_buffer.size = sizeof(m_rx_ring_buffer_data) + 1; |
241 |
| - m_rx_ring_buffer.start = 0; |
242 |
| - m_rx_ring_buffer.end = 0; |
243 |
| - m_rx_ring_buffer.elems = m_rx_ring_buffer_data; |
244 |
| - |
245 |
| - m_connected = false; |
246 |
| - |
247 |
| - ble_uart_advertise(); |
248 |
| -} |
249 |
| - |
250 |
| -void ble_uart_advertise(void) { |
251 |
| -#if BLUETOOTH_WEBBLUETOOTH_REPL |
252 |
| - while (!m_connected) { |
253 |
| - (void)ble_drv_advertise_data(&m_adv_data_uart_service); |
254 |
| - mp_hal_delay_ms(500); |
255 |
| - (void)ble_drv_advertise_data(&m_adv_data_eddystone_url); |
256 |
| - mp_hal_delay_ms(500); |
257 |
| - } |
258 |
| - |
259 |
| - ble_drv_advertise_stop(); |
260 |
| -#else |
261 |
| - (void)ble_drv_advertise_data(&m_adv_data_uart_service); |
262 |
| -#endif // BLUETOOTH_WEBBLUETOOTH_REPL |
263 |
| -} |
264 |
| - |
265 |
| -bool ble_uart_connected(void) { |
266 |
| - return (m_connected); |
267 |
| -} |
268 |
| - |
269 |
| -bool ble_uart_enabled(void) { |
270 |
| - return (m_cccd_enabled); |
271 |
| -} |
272 |
| - |
273 | 196 | #endif // MICROPY_PY_BLE_NUS
|
274 |
| - |
275 |
| -#endif // BLUETOOTH_SD |
0 commit comments