/
bluetooth.h
403 lines (346 loc) · 20 KB
/
bluetooth.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
/**
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
*
* Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* ----------------------------------------------------------------------------
* Platform Specific Bluetooth Functionality
* ----------------------------------------------------------------------------
*/
#ifndef BLUETOOTH_H
#define BLUETOOTH_H
#ifdef NRF5X
#include "app_config.h"
#endif
#include "jsdevices.h"
#ifdef NRF5X
#if NRF_SD_BLE_API_VERSION>5
#include "nrf_sdh_ble.h"
#define BLE_GAP_ADV_MAX_SIZE BLE_GAP_ADV_SET_DATA_SIZE_MAX
#else
#include "ble.h"
#endif
#include "ble_advdata.h"
/* Check for errors when in an IRQ, when we're pretty sure an error won't
* cause a hard reset. Error is then reported outside of the IRQ without
* rebooting Espruino. */
#define APP_ERROR_CHECK_NOT_URGENT(ERR_CODE) if (ERR_CODE) { uint32_t line = __LINE__; jsble_queue_pending_buf(BLEP_ERROR, ERR_CODE, (char*)&line, 4); }
#else // !NRF5X
typedef struct {
uint16_t uuid;
uint8_t type; //see BLE_UUID_TYPE_... definitions
uint8_t uuid128[16]; //BLE knows 16/32/128 bit uuids. Espruino supports 16/128.
} PACKED_FLAGS ble_uuid_t;
typedef struct {
//uint8_t addr_id_peer;
uint8_t addr_type;
uint8_t addr[6];
} ble_gap_addr_t;
#define BLE_GATT_HANDLE_INVALID (0)
#define BLE_GAP_ADDR_TYPE_PUBLIC (0)
#define BLE_GAP_ADDR_TYPE_RANDOM_STATIC (1)
#define BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE (2)
#define BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE (3)
#define BLE_GAP_ADV_MAX_SIZE (31)
#define BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE 0x02
#define BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE 0x03
#define BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE 0x06
#define BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE 0x07
#define BLE_GAP_AD_TYPE_SERVICE_DATA 0x16
#define BLE_GAP_AD_TYPE_SERVICE_DATA_128BIT_UUID 0x21
#define BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME 0x08
#define BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME 0x09
#define BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA 0xFF
#define BLE_UUID_TYPE_UNKNOWN (0)
#define BLE_UUID_TYPE_BLE (1)
#define BLE_UUID_TYPE_128 2
#define MSEC_TO_UNITS(MS,MEH) MS
#define GATT_MTU_SIZE_DEFAULT 23
#define BLE_NUS_MAX_DATA_LEN 20 //GATT_MTU_SIZE_DEFAULT - 3
#define BLE_CCCD_VALUE_LEN 2
#define BLE_GATT_HVX_NOTIFICATION 1 // flag in CCCD
#define BLE_GATT_HVX_INDICATION 2 // flag in CCCD
#endif //!NRF5X (fudge NRF5X API for ESP32)
#ifndef CENTRAL_LINK_COUNT /**<number of central links used by the application. When changing this number remember to adjust the RAM settings*/
#if defined(NRF52_SERIES) || defined(ESP32) // nRF52 gets the ability to connect to other devices
#define CENTRAL_LINK_COUNT 1
#else
#define CENTRAL_LINK_COUNT 0 /**<number of central links used by the application. When changing this number remember to adjust the RAM settings*/
#endif
#endif
#ifndef PERIPHERAL_LINK_COUNT /**<number of peripheral links used by the application. When changing this number remember to adjust the RAM settings*/
#if defined(NRF52_SERIES) || defined(ESP32) // nRF52 gets the ability to connect to other devices
#define PERIPHERAL_LINK_COUNT 1
#else
#define PERIPHERAL_LINK_COUNT 1 /**<number of peripheral links used by the application. When changing this number remember to adjust the RAM settings*/
#endif
#endif
#ifndef APP_TIMER_OP_QUEUE_SIZE
#define APP_TIMER_OP_QUEUE_SIZE 2 /**< Size of timer operation queues. */
#endif
#define APP_TIMER_PRESCALER 0 /**< Value of the RTC1 PRESCALER register. */
#define APP_BLE_CONN_CFG_TAG 1 /**< A tag identifying the SoftDevice BLE configuration. */
// BLE HID stuff
#define HID_KEYS_MAX_LEN 16 /**< Maximum length of the Input Report characteristic. */
#define HID_MODIFIER_KEY_POS 0 /**< Position of the modifier byte in the Input Report. */
#define HID_SCAN_CODE_POS 2 /**< This macro indicates the start position of the key scan code in a HID Report. As per the document titled 'Device Class Definition for Human Interface Devices (HID) V1.11, each report shall have one modifier byte followed by a reserved constant byte and then the key scan code. */
#ifndef BLUETOOTH_ADVERTISING_INTERVAL
#define BLUETOOTH_ADVERTISING_INTERVAL 375
#endif
typedef enum {
BLE_NONE = 0,
BLE_IS_SENDING = 1, //< sending data with jswrap_ble_transmit_string?
BLE_IS_SCANNING = 2, //< scanning for BLE devices?
BLE_IS_ADVERTISING = 4, //< currently advertising info? stops when connected
BLE_NEEDS_SOFTDEVICE_RESTART = 8, //< We need to reset the services we're reporting, but we can't because we're connected
BLE_SERVICES_WERE_SET = 16, //< setServices was called already, so we need to restart softdevice before we can call it again
BLE_NUS_INITED = 32, //< Has the Nordic UART service been initialised?
BLE_HID_INITED = 64, //< Has the BLE HID service been initialised?
BLE_IS_SENDING_HID = 128, //< Are we waiting to send data for USB HID?
BLE_IS_RSSI_SCANNING = 256, //< Are we scanning for RSSI values
BLE_IS_SLEEPING = 512, //< NRF.sleep has been called
BLE_PM_INITIALISED = 1<<10, //< Set when the Peer Manager has been initialised (only needs doing once, even after SD restart)
BLE_IS_NOT_CONNECTABLE = 1<<11, //< Is the device connectable?
BLE_IS_NOT_SCANNABLE = 1<<12, //< Is the device scannable? eg, scan response
BLE_IS_NOT_PAIRABLE = 1<<13, //< Is the device pairable?
BLE_WHITELIST_ON_BOND = 1<<14, //< Should we write to the whitelist whenever we bond to a device?
BLE_DISABLE_DYNAMIC_INTERVAL = 1<<15, //< Disable automatically changing interval based on BLE peripheral activity
BLE_ENCRYPT_UART = 1<<16, //< Has security with encryption been requested (if so UART must require it)
BLE_SECURITY_MITM = 1 << 17, //< Has security with mitm protection been requested (if so UART must require it)
#ifdef ESPR_BLUETOOTH_ANCS
BLE_ANCS_INITED = 1<<18, //< Apple Notification Centre enabled
BLE_AMS_INITED = 1<<19, //< Apple Media Service enabled
BLE_CTS_INITED = 1<<20, //< Apple Notification Centre enabled
BLE_ANCS_AMS_OR_CTS_INITED = BLE_ANCS_INITED|BLE_AMS_INITED|BLE_CTS_INITED, //< Apple Notifications or Media Service enabled
#endif
#ifndef SAVE_ON_FLASH
BLE_ADVERTISE_WHEN_CONNECTED = 1<<21, // Do we keep advertising when we're connected?
#endif
BLE_IS_ADVERTISING_MULTIPLE = 1<<22, // We have multiple different advertising packets
BLE_ADVERTISING_MULTIPLE_SHIFT = 23,//GET_BIT_NUMBER(BLE_ADVERTISING_MULTIPLE_ONE),
BLE_ADVERTISING_MULTIPLE_ONE = 1 << BLE_ADVERTISING_MULTIPLE_SHIFT,
BLE_ADVERTISING_MULTIPLE_MASK = 255 << BLE_ADVERTISING_MULTIPLE_SHIFT,
/// These are flags that should be reset when the softdevice starts up
BLE_RESET_ON_SOFTDEVICE_START = BLE_IS_SENDING|BLE_IS_SCANNING|BLE_IS_ADVERTISING
} BLEStatus;
typedef enum {
BLE_BOND_REQUEST,
BLE_BOND_START,
BLE_BOND_SUCCESS,
BLE_BOND_FAIL
} BLEBondingStatus;
typedef enum {
BLEP_NONE,
BLEP_ERROR, //< Softdevice threw some error (code in data)
BLEP_CONNECTED, //< Peripheral connected (address as buffer)
BLEP_DISCONNECTED, //< Peripheral disconnected
BLEP_ADVERTISING_START, //< Start advertising - do it outside of IRQ because we may need to allocate JsVars
BLEP_ADVERTISING_STOP, //< Stop advertising - do it outside of IRQ
BLEP_RESTART_SOFTDEVICE, //< Perform a softdevice restart (again, we don't want to do this in an IRQ!)
BLEP_RSSI_PERIPH, //< RSSI data from peripheral connection (rssi as data)
BLEP_ADV_REPORT, //< Advertising received (as buffer)
#if CENTRAL_LINK_COUNT>0
BLEP_RSSI_CENTRAL, //< RSSI data from central connection (rssi as data low byte, index in m_central_conn_handles as high byte )
BLEP_TASK_FAIL, //< Task failed because unknown
BLEP_TASK_FAIL_CONN_TIMEOUT, //< Task failed becauseConnection timeout
BLEP_TASK_FAIL_DISCONNECTED, //< Task failed because disconnected
BLEP_TASK_CENTRAL_CONNECTED, //< Central: Connected, (m_central_conn_handles index as data)
BLEP_TASK_DISCOVER_SERVICE, //< New service discovered (as buffer)
BLEP_TASK_DISCOVER_SERVICE_COMPLETE, //< Service discovery complete
BLEP_TASK_DISCOVER_CHARACTERISTIC, //< New characteristic discovered (as buffer)
BLEP_TASK_DISCOVER_CHARACTERISTIC_COMPLETE, //< Characteristic discovery complete
BLEP_TASK_DISCOVER_CCCD, //< Discovery of CCCD for characteristic finished (cccd in data)
BLEP_TASK_CHARACTERISTIC_READ, //< Central: Characteristic read finished (as buffer)
BLEP_TASK_CHARACTERISTIC_WRITE, //< Central: Characteristic write finished
BLEP_TASK_CHARACTERISTIC_NOTIFY, //< Central: Started requesting notifications
BLEP_CENTRAL_NOTIFICATION, //< A characteristic we were watching has changed
BLEP_CENTRAL_DISCONNECTED, //< Central: Disconnected (reason as data low byte, index in m_central_conn_handles as high byte )
#endif
#if PEER_MANAGER_ENABLED
BLEP_BONDING_STATUS, //< Bonding negotiation status (data is one of BLEBondingStatus)
#endif
BLEP_WRITE, //< One of our characteristics written by someone else
BLEP_TASK_PASSKEY_DISPLAY, //< We're pairing and have been provided with a passkey to display (data = conn_handle)
BLEP_TASK_AUTH_KEY_REQUEST, //< We're pairing and the device wants a passkey from us (data = conn_handle)
BLEP_TASK_AUTH_STATUS, //< Data on how authentication was going has been received
#ifdef USE_NFC
BLEP_NFC_STATUS, //< NFC changed state
BLEP_NFC_RX, //< NFC data received (as buffer)
BLEP_NFC_TX, //< NFC data sent
#endif
#if BLE_HIDS_ENABLED
BLEP_HID_SENT, //< A HID report has been sent
BLEP_HID_VALUE, //< A HID value was received (eg caps lock)
#endif
#ifdef ESPR_BLUETOOTH_ANCS
BLEP_ANCS_DISCOVERED, //< Apple ANCS discovered (need to request notifications)
BLEP_ANCS_NOTIF, //< Apple Notification Centre notification received
BLEP_ANCS_NOTIF_ATTR, //< Apple Notification Centre notification attributes received
BLEP_ANCS_APP_ATTR, //< Apple Notification Centre app attributes received
BLEP_ANCS_ERROR, //< Apple Notification Centre error - cancel any active tasks
BLEP_AMS_DISCOVERED, //< Apple AMS discovered (need to request notifications)
BLEP_AMS_TRACK_UPDATE, //< Apple Media Service Track info updated
BLEP_AMS_PLAYER_UPDATE, //< Apple Media Service Player info updated
BLEP_AMS_ATTRIBUTE, //< Apple Media Service Track or Player info read response
BLEP_CTS_DISCOVERED, //< Apple Current Time Service discovered (need to request notifications)
BLEP_CTS_TIME //< Apple Current Time Service data (data = current_time_char_t + optional local_time_char_t)
#endif
} BLEPending;
extern volatile BLEStatus bleStatus;
/// Filter to use when discovering BLE Services/Characteristics
extern ble_uuid_t bleUUIDFilter;
/// The advertising interval (in units of 0.625 ms)
extern uint16_t bleAdvertisingInterval;
/// The interval for the current peripheral connection (in units of 1.25 ms)
extern uint16_t blePeriphConnectionInterval;
extern volatile uint16_t m_peripheral_conn_handle; /**< Handle of the current connection. */
#if CENTRAL_LINK_COUNT>0
extern volatile uint16_t m_central_conn_handles[CENTRAL_LINK_COUNT]; /**< Handle for central mode connection */
#endif
/// for BLEP_ADV_REPORT
typedef struct {
ble_gap_addr_t peer_addr;
int8_t rssi; /**< Received Signal Strength Indication in dBm of the last packet received. */
uint8_t dlen; /**< Advertising or scan response data length. */
uint8_t data[BLE_GAP_ADV_MAX_SIZE]; /**< Advertising or scan response data. */
} BLEAdvReportData;
/** Initialise the BLE stack */
void jsble_init();
/** Completely deinitialise the BLE stack. Return true on success */
bool jsble_kill();
/// Checks for error and reports an exception string if there was one, else 0 if no error
JsVar *jsble_get_error_string(uint32_t err_code);
/** Execute a task that was added by jsble_queue_pending - this is done outside of IRQ land. Returns number of events handled */
int jsble_exec_pending(IOEvent *event);
/** Stop and restart the softdevice so that we can update the services in it -
* both user-defined as well as UART/HID. If jsFunction is a function it is
* called when the softdevice is uninitialised. */
void jsble_restart_softdevice(JsVar *jsFunction);
uint32_t jsble_advertising_start();
uint32_t jsble_advertising_update_advdata(char *dPtr, unsigned int dLen);
uint32_t jsble_advertising_update_scanresponse(char *dPtr, unsigned int dLen);
void jsble_advertising_stop();
/** Is BLE connected to any device at all? */
bool jsble_has_connection();
/** Is BLE connected to a central device at all? */
bool jsble_has_central_connection();
/** Return the index of the central connection in m_central_conn_handles, or -1 */
int jsble_get_central_connection_idx(uint16_t handle);
/** Is BLE connected to a server device at all (eg, the simple, 'slave' mode)? */
bool jsble_has_peripheral_connection();
/** Call this when something happens on BLE with this as
* a peripheral - used with Dynamic Interval Adjustment */
void jsble_peripheral_activity();
#ifndef SAVE_ON_FLASH_EXTREME
#define jsble_check_error(X) jsble_check_error_line(X, __LINE__)
/// Checks for error and reports an exception if there was one. Return true on error
bool jsble_check_error_line(uint32_t err_code, int lineNumber);
#else
/// Checks for error and reports an exception if there was one. Return true on error
bool jsble_check_error(uint32_t err_code);
#endif
/** Set the connection interval of the peripheral connection. Returns an error code */
uint32_t jsble_set_periph_connection_interval(JsVarFloat min, JsVarFloat max);
/// Scanning for advertising packets. options can be an object with optional {active:bool, phy:"1mbps/2mbps/coded"}
uint32_t jsble_set_scanning(bool enabled, JsVar *options);
/// returning RSSI values for current connection
uint32_t jsble_set_rssi_scan(bool enabled);
/** Actually set the services defined in the 'data' object. Note: we can
* only do this *once* - so to change it we must reset the softdevice and
* then call this again */
void jsble_set_services(JsVar *data);
/// Disconnect from the given connection
uint32_t jsble_disconnect(uint16_t conn_handle);
/// Start bonding on the peripheral connection, returns a promise
void jsble_startBonding(bool forceRePair);
/// For BLE HID, send an input report to the receiver. Must be <= HID_KEYS_MAX_LEN
void jsble_send_hid_input_report(uint8_t *data, int length);
/// Update the current security settings from the info in hiddenRoot.BLE_NAME_SECURITY
void jsble_update_security();
/// Return an object showing the security status of the given connection
JsVar *jsble_get_security_status(uint16_t conn_handle);
/// Set the transmit power of the current (and future) connections
void jsble_set_tx_power(int8_t pwr);
// ------------------------------------------------- lower-level utility fns
#ifdef NRF5X
/// Build advertising data struct to pass into @ref ble_advertising_init.
void jsble_setup_advdata(ble_advdata_t *advdata);
#endif
#ifdef USE_NFC
#define TAG_HEADER_LEN 0x0A
/*
TT = Tag Type
ML = NDEF Message Length
RT = Record Type
TF = TNF and Flags
TL = Type Legnth
IC = URI Identifier Code
*/
#define NDEF_HEADER "\x00\x00\x00\x00" /* | UID/BCC | */ \
"\x00\x00\x00\x00" /* | UID/BCC | */ \
"\x00\x00\xFF\xFF" /* | UID/BCC | LOCK | */ \
"\xE1\x11\x7C\x0F" /* | Cap. Container | */ \
"\x03\x00\x00\x00" /* | TT | ML (1 or 3 bytes)| */
#define NDEF_HEADER_LEN_SHORT 0x12 /* with 1 byte length */
#define NDEF_HEADER_LEN_LONG 0x14 /* with 3 byte length */
#define NDEF_HEADER_MSG_LEN_OFFSET 0x11
#define NDEF_URL_RECORD_HEADER \
"\xC1\x01" /* | TF | TL | */ \
"\x00\x00\x00\x00" /* | Payload Length | */ \
"\x55\x00" /* | RT | IC | 0x00: No prepending */
#define NDEF_URL_RECORD_HEADER_LEN 0x08 /* record header (TF, TL, PL, RT, IC ) */
#define NDEF_IC_LEN 0x01
#define NDEF_MSG_IC_OFFSET 7
#define NDEF_MSG_PL_LEN_MSB_OFFSET 4
#define NDEF_TERM_TLV 0xfe /* last TLV block / byte */
#define NDEF_TERM_TLV_LEN 0x01
#define NDEF_TAG2_VALUE_MAXLEN (992 - 4 - NDEF_TERM_TLV_LEN) /* max NDEF data size for 0x7C size in cap. container (*8=992)*/
void jsble_nfc_stop();
void jsble_nfc_start(const uint8_t *data, size_t len);
void jsble_nfc_get_internal(uint8_t *data, size_t *max_len);
void jsble_nfc_send(const uint8_t *data, size_t len);
void jsble_nfc_send_rsp(const uint8_t data, size_t len);
#endif
#if CENTRAL_LINK_COUNT>0
/** Connect to the given peer address. When done call bleCompleteTask.
options is an optional object containing optional fields:
{
minInterval // min connection interval in milliseconds, 7.5 ms to 4 s
maxInterval // max connection interval in milliseconds, 7.5 ms to 4 s
}
See BluetoothRemoteGATTServer.connect docs for more docs */
void jsble_central_connect(ble_gap_addr_t peer_addr, JsVar *options);
/// Get primary services. Filter by UUID unless UUID is invalid, in which case return all. When done call bleCompleteTask
void jsble_central_getPrimaryServices(uint16_t central_conn_handle, ble_uuid_t uuid);
/// Get characteristics. Filter by UUID unless UUID is invalid, in which case return all. When done call bleCompleteTask
void jsble_central_getCharacteristics(uint16_t central_conn_handle, JsVar *service, ble_uuid_t uuid);
// Write data to the given characteristic. When done call bleCompleteTask
void jsble_central_characteristicWrite(uint16_t central_conn_handle, JsVar *characteristic, char *dataPtr, size_t dataLen);
// Read data from the given characteristic. When done call bleCompleteTask
void jsble_central_characteristicRead(uint16_t central_conn_handle, JsVar *characteristic);
// Discover descriptors of characteristic
void jsble_central_characteristicDescDiscover(uint16_t central_conn_handle, JsVar *characteristic);
// Set whether to notify on the given characteristic. When done call bleCompleteTask
void jsble_central_characteristicNotify(uint16_t central_conn_handle, JsVar *characteristic, bool enable);
/// Start bonding on the current central connection
void jsble_central_startBonding(uint16_t central_conn_handle, bool forceRePair);
/// Get the security status of the current link
JsVar *jsble_central_getSecurityStatus(uint16_t central_conn_handle);
/// RSSI monitoring in central mode
uint32_t jsble_set_central_rssi_scan(uint16_t central_conn_handle, bool enabled);
/// Send a passkey if one was requested (passkey = 6 bytes long)
uint32_t jsble_central_send_passkey(uint16_t central_conn_handle, char *passkey);
#endif
#if PEER_MANAGER_ENABLED
/// Set whether or not the whitelist is enabled
void jsble_central_setWhitelist(bool whitelist);
/// Erase any saved bonding info for peers
void jsble_central_eraseBonds();
/// Try to resolve a bonded peer's address from a random private resolvable address
JsVar *jsble_resolveAddress(JsVar *address);
#endif
#endif // BLUETOOTH_H