/** * This file is part of Espruino, a JavaScript interpreter for Microcontrollers * * Copyright (C) 2013 Gordon Williams * * 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 /**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; extern uint16_t bleAdvertisingInterval; /**< The advertising interval (in units of 0.625 ms). */ 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