/* * This file is part of Espruino, a JavaScript interpreter for Microcontrollers * * Copyright (C) 2017 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/. * * ---------------------------------------------------------------------------- * ESP32 specific GATT client functions * ---------------------------------------------------------------------------- */ #include #include "esp_log.h" #include "BLE/esp32_gattc_func.h" #include "BLE/esp32_bluetooth_utils.h" #include "bluetooth_utils.h" #include "jswrap_bluetooth.h" #include "jsvar.h" #include "jsutils.h" #include "jsparse.h" #include "jsinteractive.h" #define GATTC_PROFILE 0 #define INVALID_HANDLE 0 static struct gattc_profile_inst gattc_apps[1] = { [GATTC_PROFILE] = { .gattc_cb = gattc_event_handler, .gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */ } }; static ble_uuid_t serviceFilter; static ble_uuid_t charFilter; static esp_gattc_char_elem_t *char_elem_result = NULL; //static esp_gattc_descr_elem_t *descr_elem_result = NULL; void gattc_evt_reg(esp_gatt_if_t gattc_if,esp_ble_gattc_cb_param_t *param) { gattc_apps[param->reg.app_id].gattc_if = gattc_if; } void gattc_evt_connect(esp_gatt_if_t gattc_if,esp_ble_gattc_cb_param_t *param) { esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param; gattc_apps[GATTC_PROFILE].conn_id = p_data->connect.conn_id; m_central_conn_handles[0] = 0x01; memcpy(gattc_apps[GATTC_PROFILE].remote_bda, p_data->connect.remote_bda, sizeof(esp_bd_addr_t)); esp_err_t mtu_ret = esp_ble_gattc_send_mtu_req (gattc_if, p_data->connect.conn_id); if (mtu_ret){jsWarn("config MTU error, error code = %x", mtu_ret);} } void gattc_evt_disconnect(esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { NOT_USED(gattc_if); NOT_USED(param); m_central_conn_handles[0] = BLE_GATT_HANDLE_INVALID; } void gattc_evt_cfg_mtu(esp_gatt_if_t gattc_if,esp_ble_gattc_cb_param_t *param) { NOT_USED(gattc_if); NOT_USED(param); if (!bleTaskInfo) bleTaskInfo = jsvNewEmptyArray(); jsvObjectSetChildAndUnLock(bleTaskInfo,"connected", jsvNewFromBool(true)); bleCompleteTaskSuccess(BLETASK_CONNECT, bleTaskInfo); } void gattc_evt_search_cmpl() { JsVar *t = jsvSkipNameAndUnLock(jsvArrayPopFirst(bleTaskInfo)); jsvUnLock(bleTaskInfo); bleTaskInfo = t; if(bleTaskInfo) bleCompleteTaskSuccess(BLETASK_PRIMARYSERVICE,bleTaskInfo); else bleCompleteTaskFailAndUnLock(BLETASK_PRIMARYSERVICE,jsvNewFromString("No Services found")); } void gattc_evt_search_res(esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { NOT_USED(gattc_if); esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param; esp_gatt_srvc_id_t *srvc_id =(esp_gatt_srvc_id_t *)&p_data->search_res.srvc_id; ble_uuid_t ble_uuid; espbtuuid_TO_bleuuid(srvc_id->id.uuid, &ble_uuid); // no need to check since esp_ble_gattc_search_service accepts a filter if (!bleTaskInfo) bleTaskInfo = jsvNewEmptyArray(); JsVar *o = jspNewObject(0,"BluetoothRemoteGATTService"); if (o) { jsvObjectSetChildAndUnLock(o,"uuid",bleUUIDToStr(ble_uuid)); jsvObjectSetChildAndUnLock(o,"isPrimary",jsvNewFromBool(true)); jsvObjectSetChildAndUnLock(o,"start_handle",jsvNewFromInteger(p_data->search_res.start_handle)); jsvObjectSetChildAndUnLock(o,"end_handle",jsvNewFromInteger(p_data->search_res.end_handle)); jsvArrayPushAndUnLock(bleTaskInfo,o); } } void gattc_evt_read_char(esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { NOT_USED(gattc_if); esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param; JsVar *data = jsvNewDataViewWithData(p_data->read.value_len,(unsigned char*)p_data->read.value); jsvObjectSetChild(bleTaskInfo,"value",data); bleCompleteTaskSuccessAndUnLock(BLETASK_CHARACTERISTIC_READ,data); } void gattc_evt_write_char(esp_gatt_if_t gattc_if,esp_ble_gattc_cb_param_t *param) { NOT_USED(gattc_if); NOT_USED(param); bleCompleteTaskSuccess(BLETASK_CHARACTERISTIC_WRITE,0); } void gattc_init() { esp_err_t ret; ret = esp_ble_gattc_app_register(GATTC_PROFILE);if(ret){jsWarn("gattc register app error:%x\n",ret);return;} } void gattc_reset() { esp_err_t ret; if(gattc_apps[GATTC_PROFILE].gattc_if != ESP_GATT_IF_NONE){ ret = esp_ble_gattc_app_unregister((esp_gatt_if_t)gattc_apps[GATTC_PROFILE].gattc_if); if(ret) jsWarn("could not unregister GATTC(%d)\n",ret); } m_central_conn_handles[0] = BLE_GATT_HANDLE_INVALID; } void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { jsWarnGattcEvent(event,gattc_if); //esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param; //jsiConsolePrintf("gattc_event_handler: %d\n", event); switch (event) { case ESP_GATTC_REG_EVT: gattc_evt_reg(gattc_if,param);break; case ESP_GATTC_CONNECT_EVT: gattc_evt_connect(gattc_if,param);break; case ESP_GATTC_CFG_MTU_EVT: gattc_evt_cfg_mtu(gattc_if,param);break; case ESP_GATTC_SEARCH_CMPL_EVT: gattc_evt_search_cmpl(gattc_if,param);break; case ESP_GATTC_SEARCH_RES_EVT: gattc_evt_search_res(gattc_if,param);break; case ESP_GATTC_READ_CHAR_EVT: gattc_evt_read_char(gattc_if,param);break; case ESP_GATTC_WRITE_CHAR_EVT: gattc_evt_write_char(gattc_if,param);break; case ESP_GATTC_UNREG_EVT: break; case ESP_GATTC_OPEN_EVT: break; case ESP_GATTC_CLOSE_EVT: break; case ESP_GATTC_READ_DESCR_EVT: break; case ESP_GATTC_WRITE_DESCR_EVT: break; case ESP_GATTC_NOTIFY_EVT: break; case ESP_GATTC_PREP_WRITE_EVT: break; case ESP_GATTC_EXEC_EVT: break; case ESP_GATTC_ACL_EVT: break; case ESP_GATTC_CANCEL_OPEN_EVT: break; case ESP_GATTC_SRVC_CHG_EVT: break; case ESP_GATTC_ENC_CMPL_CB_EVT: break; case ESP_GATTC_ADV_DATA_EVT: break; case ESP_GATTC_MULT_ADV_ENB_EVT: break; case ESP_GATTC_MULT_ADV_UPD_EVT: break; case ESP_GATTC_MULT_ADV_DATA_EVT: break; case ESP_GATTC_MULT_ADV_DIS_EVT: break; case ESP_GATTC_CONGEST_EVT: break; case ESP_GATTC_BTH_SCAN_ENB_EVT: break; case ESP_GATTC_BTH_SCAN_CFG_EVT: break; case ESP_GATTC_BTH_SCAN_RD_EVT: break; case ESP_GATTC_BTH_SCAN_THR_EVT: break; case ESP_GATTC_BTH_SCAN_PARAM_EVT: break; case ESP_GATTC_BTH_SCAN_DIS_EVT: break; case ESP_GATTC_SCAN_FLT_CFG_EVT: break; case ESP_GATTC_SCAN_FLT_PARAM_EVT: break; case ESP_GATTC_SCAN_FLT_STATUS_EVT: break; case ESP_GATTC_ADV_VSC_EVT: break; case ESP_GATTC_REG_FOR_NOTIFY_EVT: break; case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: break; case ESP_GATTC_DISCONNECT_EVT: break; default: break; } } void gattc_connect(ble_gap_addr_t peer_addr, JsVar *options) { NOT_USED(options); esp_bd_addr_t remote_bda; esp_ble_addr_type_t remote_bda_type; bleaddr_TO_espbtaddr(peer_addr, remote_bda, &remote_bda_type); esp_err_t ret; ret = esp_ble_gattc_open((esp_gatt_if_t)gattc_apps[GATTC_PROFILE].gattc_if, remote_bda, remote_bda_type, true); jsble_check_error((uint32_t)ret); } uint32_t gattc_disconnect(uint16_t conn_handle) { NOT_USED(conn_handle); esp_err_t ret; ret = esp_ble_gattc_close((esp_gatt_if_t)gattc_apps[GATTC_PROFILE].gattc_if,gattc_apps[GATTC_PROFILE].conn_id); jsble_check_error((uint32_t)ret); return (uint32_t)ret; } void gattc_searchService(ble_uuid_t uuid){ serviceFilter = uuid; esp_bt_uuid_t espServiceFilter; bleuuid_TO_espbtuuid(uuid, &espServiceFilter); esp_err_t ret = esp_ble_gattc_search_service((esp_gatt_if_t)gattc_apps[GATTC_PROFILE].gattc_if, gattc_apps[GATTC_PROFILE].conn_id, &espServiceFilter); jsble_check_error((uint32_t)ret); // followed by ESP_GATTC_SEARCH_RES_EVT // and ESP_GATTC_SEARCH_CMPL_EVT } void gattc_getCharacteristic(ble_uuid_t char_uuid){ uint16_t count = 0; charFilter = char_uuid; esp_ble_gattc_get_attr_count( (esp_gatt_if_t)gattc_apps[GATTC_PROFILE].gattc_if, gattc_apps[GATTC_PROFILE].conn_id,ESP_GATT_DB_CHARACTERISTIC, gattc_apps[GATTC_PROFILE].service_start_handle,gattc_apps[GATTC_PROFILE].service_end_handle, INVALID_HANDLE,&count); if(count > 0) { if (!bleTaskInfo) bleTaskInfo = jsvNewEmptyArray(); char_elem_result = (esp_gattc_char_elem_t *)malloc(sizeof(esp_gattc_char_elem_t) * count); esp_bt_uuid_t espCharFilter; bleuuid_TO_espbtuuid(char_uuid,&espCharFilter); esp_ble_gattc_get_char_by_uuid( (esp_gatt_if_t)gattc_apps[GATTC_PROFILE].gattc_if, gattc_apps[GATTC_PROFILE].conn_id, gattc_apps[GATTC_PROFILE].service_start_handle,gattc_apps[GATTC_PROFILE].service_end_handle, espCharFilter,char_elem_result,&count); if(count > 0){ //check with more than one character in service gattc_apps[GATTC_PROFILE].char_handle = char_elem_result[0].char_handle; JsVar *o = jspNewObject(0,"BluetoothRemoteGATTCharacteristic"); if(o) { jsvObjectSetChildAndUnLock(o,"uuid",jsvVarPrintf("%04x",char_elem_result[0].uuid.uuid.uuid16)); jsvObjectSetChildAndUnLock(o,"handle_value",jsvNewFromInteger(char_elem_result[0].char_handle)); JsVar *p = jsvNewObject(); if(p){ jsvObjectSetChildAndUnLock(p,"broadcast",jsvNewFromBool(char_elem_result[0].properties & ESP_GATT_CHAR_PROP_BIT_BROADCAST)); jsvObjectSetChildAndUnLock(p,"read",jsvNewFromBool(char_elem_result[0].properties & ESP_GATT_CHAR_PROP_BIT_READ)); jsvObjectSetChildAndUnLock(p,"writeWithoutResponse",jsvNewFromBool(char_elem_result[0].properties & ESP_GATT_CHAR_PROP_BIT_WRITE_NR)); jsvObjectSetChildAndUnLock(p,"write",jsvNewFromBool(char_elem_result[0].properties & ESP_GATT_CHAR_PROP_BIT_WRITE)); jsvObjectSetChildAndUnLock(p,"notify",jsvNewFromBool(char_elem_result[0].properties & ESP_GATT_CHAR_PROP_BIT_NOTIFY)); jsvObjectSetChildAndUnLock(p,"indicate",jsvNewFromBool(char_elem_result[0].properties & ESP_GATT_CHAR_PROP_BIT_INDICATE)); jsvObjectSetChildAndUnLock(p,"authenticatedSignedWrites",jsvNewFromBool(char_elem_result[0].properties & ESP_GATT_CHAR_PROP_BIT_AUTH)); jsvObjectSetChildAndUnLock(o,"properties",p); } } jsvArrayPushAndUnLock(bleTaskInfo,o); } free(char_elem_result); } JsVar *t = jsvSkipNameAndUnLock(jsvArrayPopFirst(bleTaskInfo)); jsvUnLock(bleTaskInfo); bleTaskInfo = t; if (bleTaskInfo) bleCompleteTaskSuccess(BLETASK_CHARACTERISTIC, bleTaskInfo); else bleCompleteTaskFailAndUnLock(BLETASK_CHARACTERISTIC, jsvNewFromString("No Characteristics found")); } void gattc_readValue(uint16_t charHandle) { // check for connected esp_ble_gattc_read_char((esp_gatt_if_t)gattc_apps[GATTC_PROFILE].gattc_if,gattc_apps[GATTC_PROFILE].conn_id, charHandle,ESP_GATT_AUTH_REQ_NONE); } void gattc_writeValue(uint16_t charHandle, char *data, size_t dataLen) { esp_ble_gattc_write_char( (esp_gatt_if_t)gattc_apps[GATTC_PROFILE].gattc_if, gattc_apps[GATTC_PROFILE].conn_id, charHandle, (uint16_t)dataLen, (uint8_t*)data, ESP_GATT_WRITE_TYPE_RSP,ESP_GATT_AUTH_REQ_NONE); } void gattc_readDesc(uint16_t charHandle) { esp_ble_gattc_read_char_descr ((esp_gatt_if_t)gattc_apps[GATTC_PROFILE].gattc_if,gattc_apps[GATTC_PROFILE].conn_id, charHandle,ESP_GATT_AUTH_REQ_NONE); }