|
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