mirror of
https://github.com/espruino/Espruino.git
synced 2025-12-08 19:06:15 +00:00
313 lines
11 KiB
C
313 lines
11 KiB
C
/**
|
|
* 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/.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
* Utilities for converting Nordic datastructures to Espruino and vice versa
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
#include <stdio.h>
|
|
|
|
#include "esp_bt.h"
|
|
#include "esp_bt_main.h"
|
|
#include "esp_gatt_common_api.h"
|
|
#include "esp_log.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
|
|
#include "jswrap_bluetooth.h"
|
|
#include "bluetooth.h"
|
|
#include "bluetooth_utils.h"
|
|
#include "jsutils.h"
|
|
#include "jsparse.h"
|
|
#include "jsinteractive.h"
|
|
|
|
#include "BLE/esp32_gap_func.h"
|
|
#include "BLE/esp32_gatts_func.h"
|
|
#include "BLE/esp32_gattc_func.h"
|
|
#include "BLE/esp32_bluetooth_utils.h"
|
|
#include "jshardwareESP32.h"
|
|
|
|
volatile BLEStatus bleStatus;
|
|
ble_uuid_t bleUUIDFilter;
|
|
uint16_t bleAdvertisingInterval; /**< The advertising interval (in units of 0.625 ms). */
|
|
volatile uint16_t m_peripheral_conn_handle; /**< Handle of the current connection. */
|
|
volatile uint16_t m_central_conn_handles[1]; /**< Handle of central mode connection */
|
|
|
|
/** Initialise the BLE stack - called before Espruino is ready */
|
|
void jsble_init(){
|
|
esp_err_t ret;
|
|
if(ESP32_Get_NVS_Status(ESP_NETWORK_BLE)) {
|
|
ret = esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
|
|
if(ret) {
|
|
jsExceptionHere(JSET_ERROR,"mem release failed:%x",ret);
|
|
return;
|
|
}
|
|
|
|
if(initController()) return;
|
|
if(initBluedroid()) return;
|
|
if(registerCallbacks()) return;
|
|
setMtu();
|
|
gap_init_security();
|
|
// force advertising with the right info
|
|
bleStatus |= BLE_IS_ADVERTISING;
|
|
}
|
|
else{
|
|
ret = esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
|
|
jsWarn("Bluetooth is disabled per ESP32.enableBLE(false)\n");
|
|
}
|
|
}
|
|
/** Completely deinitialise the BLE stack. Return true on success */
|
|
bool jsble_kill(){
|
|
jsWarn("jsble_kill not implemented yet\n");
|
|
return true;
|
|
}
|
|
|
|
/// 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) {
|
|
if (!err_code) return 0;
|
|
return jsvVarPrintf("ERR 0x%x", err_code);
|
|
}
|
|
|
|
/// Executes a pending BLE event - returns the number of event bytes handled. sizeof(buffer)==IOEVENT_MAX_LEN
|
|
int jsble_exec_pending(uint8_t *buffer, int bufferLen) {
|
|
assert(IOEVENT_MAX_LEN >= sizeof(BLEAdvReportData));
|
|
int eventBytesHandled = 2+bufferLen;
|
|
// Now handle the actual event
|
|
if (bufferLen<3) return;
|
|
BLEPending blep = (BLEPending)buffer[0];
|
|
uint16_t data = (uint16_t)(buffer[1] | (buffer[2]<<8));
|
|
// skip first 3 bytes
|
|
buffer += 3;
|
|
bufferLen -= 3;
|
|
/* jsble_exec_pending_common handles 'common' events between nRF52/ESP32, then
|
|
* we handle nRF52-specific events below */
|
|
if (!jsble_exec_pending_common(blep, data, buffer, bufferLen)) switch (blep) {
|
|
// ESP32 specific handlers go here ...
|
|
default:
|
|
jsWarn("jsble_exec_pending: Unknown enum type %d",(int)blep);
|
|
}
|
|
return eventBytesHandled;
|
|
}
|
|
|
|
void jsble_restart_softdevice(JsVar *jsFunction){
|
|
bleStatus &= ~(BLE_NEEDS_SOFTDEVICE_RESTART | BLE_SERVICES_WERE_SET);
|
|
if (bleStatus & BLE_IS_SCANNING) {
|
|
bluetooth_gap_setScan(false, false);
|
|
}
|
|
if (jsvIsFunction(jsFunction))
|
|
jspExecuteFunction(jsFunction,NULL,0,NULL);
|
|
jswrap_ble_reconfigure_softdevice();
|
|
}
|
|
|
|
uint32_t jsble_advertising_start() {
|
|
if(!ESP32_Get_NVS_Status(ESP_NETWORK_BLE))
|
|
return ESP_ERR_INVALID_STATE; // ESP32.enableBLE(false)
|
|
esp_err_t status;
|
|
if (bleStatus & BLE_IS_ADVERTISING) return;
|
|
status = bluetooth_gap_startAdvertising(true);
|
|
bleStatus |= BLE_IS_ADVERTISING;
|
|
return status;
|
|
}
|
|
void jsble_advertising_stop() {
|
|
if(!ESP32_Get_NVS_Status(ESP_NETWORK_BLE))
|
|
return ESP_ERR_INVALID_STATE; // ESP32.enableBLE(false)
|
|
|
|
esp_err_t status;
|
|
if (!(bleStatus & BLE_IS_ADVERTISING)) return;
|
|
status = bluetooth_gap_startAdvertising(false);
|
|
bleStatus &= ~BLE_IS_ADVERTISING;
|
|
if(status){
|
|
jsExceptionHere(JSET_ERROR,"error in stop advertising:0X%x",status);
|
|
}
|
|
}
|
|
/** Is BLE connected to any device at all? */
|
|
bool jsble_has_connection(){
|
|
if(!ESP32_Get_NVS_Status(ESP_NETWORK_BLE))
|
|
return false; // ESP32.enableBLE(false)
|
|
#if CENTRAL_LINK_COUNT>0
|
|
return (m_central_conn_handles[0] != BLE_GATT_HANDLE_INVALID) ||
|
|
(m_peripheral_conn_handle != BLE_GATT_HANDLE_INVALID);
|
|
#else
|
|
return m_peripheral_conn_handle != BLE_GATT_HANDLE_INVALID;
|
|
#endif
|
|
}
|
|
|
|
/** Is BLE connected to a central device at all? */
|
|
bool jsble_has_central_connection(){
|
|
if(!ESP32_Get_NVS_Status(ESP_NETWORK_BLE))
|
|
return false; // ESP32.enableBLE(false)
|
|
#if CENTRAL_LINK_COUNT>0
|
|
return (m_central_conn_handles[0] != BLE_GATT_HANDLE_INVALID);
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
/** Return the index of the central connection in m_central_conn_handles, or -1 */
|
|
int jsble_get_central_connection_idx(uint16_t handle) {
|
|
return 0; // only one central connection!
|
|
}
|
|
|
|
/** Is BLE connected to a server device at all (eg, the simple, 'slave' mode)? */
|
|
bool jsble_has_peripheral_connection(){
|
|
if(!ESP32_Get_NVS_Status(ESP_NETWORK_BLE))
|
|
return false;
|
|
return (m_peripheral_conn_handle != BLE_GATT_HANDLE_INVALID);
|
|
}
|
|
|
|
/** Call this when something happens on BLE with this as
|
|
* a peripheral - used with Dynamic Interval Adjustment */
|
|
void jsble_peripheral_activity() {
|
|
}
|
|
|
|
/// 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) {
|
|
if (err_code != ESP_OK) {
|
|
const char *n = esp_err_to_name(err_code);
|
|
if (!n || !strcmp(n,"ERROR"))
|
|
jsExceptionHere(JSET_ERROR, "BLE: ERROR 0x%x (:%d)", err_code, lineNumber);
|
|
else
|
|
jsExceptionHere(JSET_ERROR, "BLE: %s (:%d)", n, lineNumber);
|
|
return true;
|
|
}
|
|
NOT_USED(err_code);
|
|
NOT_USED(lineNumber);
|
|
return false;
|
|
}
|
|
/// Scanning for advertising packets
|
|
uint32_t jsble_set_scanning(bool enabled, JsVar *options){
|
|
if(!ESP32_Get_NVS_Status(ESP_NETWORK_BLE))
|
|
return ESP_ERR_INVALID_STATE; // ESP32.enableBLE(false)
|
|
|
|
if (enabled) {
|
|
if (bleStatus & BLE_IS_SCANNING) return 0;
|
|
bleStatus |= BLE_IS_SCANNING;
|
|
bool activeScan = false;
|
|
if (enabled && jsvIsObject(options)) {
|
|
activeScan = jsvObjectGetBoolChild(options, "active");
|
|
}
|
|
bluetooth_gap_setScan(enabled, activeScan);
|
|
} else { // !enabled
|
|
if (!(bleStatus & BLE_IS_SCANNING)) return 0;
|
|
bleStatus &= ~BLE_IS_SCANNING;
|
|
bluetooth_gap_setScan(false, false);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/// returning RSSI values for current connection
|
|
uint32_t jsble_set_rssi_scan(bool enabled){
|
|
if (enabled)
|
|
jsWarn("set rssi scan not implemeted yet\n");
|
|
NOT_USED(enabled);
|
|
return 0;
|
|
}
|
|
|
|
/** 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){
|
|
if(!ESP32_Get_NVS_Status(ESP_NETWORK_BLE))
|
|
return; // ESP32.enableBLE(false)
|
|
if (jsble_has_peripheral_connection()) {
|
|
jsWarn("Skip jsble_set_services as connected");
|
|
return;
|
|
}
|
|
gatts_set_services(data);
|
|
}
|
|
|
|
/// Disconnect from the given connection
|
|
uint32_t jsble_disconnect(uint16_t conn_handle){
|
|
if(!ESP32_Get_NVS_Status(ESP_NETWORK_BLE))
|
|
return ESP_ERR_INVALID_STATE; // ESP32.enableBLE(false)
|
|
|
|
return gattc_disconnect(conn_handle);
|
|
}
|
|
|
|
/// 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){
|
|
jsWarn("send hid input report not implemented yet\n");
|
|
NOT_USED(data);
|
|
NOT_USED(length);
|
|
}
|
|
|
|
/// Connect to the given peer address. When done call bleCompleteTask
|
|
void jsble_central_connect(ble_gap_addr_t peer_addr, JsVar *options){
|
|
if(!ESP32_Get_NVS_Status(ESP_NETWORK_BLE))
|
|
return; // ESP32.enableBLE(false)
|
|
// Ignore options for now
|
|
gattc_connect(peer_addr, 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){
|
|
NOT_USED(central_conn_handle);
|
|
bleUUIDFilter = uuid;
|
|
gattc_searchService(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){
|
|
NOT_USED(central_conn_handle);
|
|
gattc_getCharacteristics(service, 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){
|
|
uint16_t handle = jsvObjectGetIntegerChild(characteristic, "handle_value");
|
|
gattc_writeValue(handle, dataPtr, dataLen);
|
|
}
|
|
// Read data from the given characteristic. When done call bleCompleteTask
|
|
void jsble_central_characteristicRead(uint16_t central_conn_handle, JsVar *characteristic){
|
|
uint16_t handle = jsvObjectGetIntegerChild(characteristic, "handle_value");
|
|
gattc_readValue(handle);
|
|
}
|
|
// Discover descriptors of characteristic
|
|
void jsble_central_characteristicDescDiscover(uint16_t central_conn_handle, JsVar *characteristic){
|
|
jsWarn("Central characteristicDescDiscover not implemented yet\n");
|
|
NOT_USED(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){
|
|
uint16_t handle = jsvObjectGetIntegerChild(characteristic, "handle_value");
|
|
uint16_t handle_cccd = jsvObjectGetIntegerChild(characteristic, "handle_cccd");
|
|
if (!handle_cccd)
|
|
return bleCompleteTaskFailAndUnLock(BLETASK_CHARACTERISTIC_NOTIFY, jsvNewFromString("No CCCD handle found"));
|
|
gattc_characteristicNotify(handle, handle_cccd, enable);
|
|
// see https://github.com/espressif/esp-idf/blob/master/examples/bluetooth/bluedroid/ble/gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md#registering-for-notifications
|
|
}
|
|
/// Start bonding on the current central connection
|
|
void jsble_central_startBonding(uint16_t central_conn_handle, bool forceRePair){
|
|
jsWarn("central start bonding not implemented yet\n");
|
|
NOT_USED(forceRePair);
|
|
}
|
|
/// RSSI monitoring in central mode
|
|
uint32_t jsble_set_central_rssi_scan(uint16_t central_conn_handle, bool enabled){
|
|
jsWarn("central set rssi scan not implemented yet\n");
|
|
return 0;
|
|
}
|
|
// Set whether or not the whitelist is enabled
|
|
void jsble_central_setWhitelist(uint16_t central_conn_handle, bool whitelist){
|
|
jsWarn("central set Whitelist not implemented yet\n");
|
|
}
|
|
|
|
void jsble_update_security() {
|
|
}
|
|
|
|
/// Return an object showing the security status of the given connection
|
|
JsVar *jsble_get_security_status(uint16_t conn_handle) {
|
|
return 0;
|
|
}
|
|
|
|
/// Set the transmit power of the current (and future) connections
|
|
void jsble_set_tx_power(int8_t pwr) {
|
|
jsWarn("jsble_set_tx_power not implemented yet\n");
|
|
}
|
|
|
|
uint32_t jsble_central_send_passkey(uint16_t central_conn_handle, char *passkey) {
|
|
jsWarn("central set Whitelist not implemented yet\n");
|
|
return 0;
|
|
}
|