mirror of
https://github.com/espruino/Espruino.git
synced 2025-12-08 19:06:15 +00:00
Added ability to add BLE characteristics
This commit is contained in:
parent
2b1bfdb09b
commit
79d26a9f57
@ -86,6 +86,7 @@ typedef enum {
|
||||
static volatile BLEStatus bleStatus;
|
||||
|
||||
#define BLE_SCAN_EVENT JS_EVENT_PREFIX"blescan"
|
||||
#define BLE_WRITE_EVENT JS_EVENT_PREFIX"blew"
|
||||
|
||||
/**@brief Error handlers.
|
||||
*
|
||||
@ -273,6 +274,12 @@ void jswrap_nrf_bluetooth_startAdvertise(void) {
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
/// Get the correct event name for a BLE write event to a characteristic (eventName should be 12 chars long)
|
||||
void ble_handle_to_write_event_name(char *eventName, uint16_t handle) {
|
||||
strcpy(eventName, BLE_WRITE_EVENT);
|
||||
itostr(handle, &eventName[strlen(eventName)], 16);
|
||||
}
|
||||
|
||||
/**@brief Function for the application's SoftDevice event handler.
|
||||
*
|
||||
* @param[in] p_ble_evt SoftDevice event.
|
||||
@ -334,7 +341,7 @@ static void on_ble_evt(ble_evt_t * p_ble_evt)
|
||||
p_adv->peer_addr.addr[0]));
|
||||
JsVar *data = jsvNewStringOfLength(p_adv->dlen);
|
||||
if (data) {
|
||||
jsvSetString(data, p_adv->data, p_adv->dlen);
|
||||
jsvSetString(data, (char*)p_adv->data, p_adv->dlen);
|
||||
JsVar *ab = jsvNewArrayBufferFromString(data, p_adv->dlen);
|
||||
jsvUnLock(data);
|
||||
jsvObjectSetChildAndUnLock(evt, "data", ab);
|
||||
@ -345,6 +352,26 @@ static void on_ble_evt(ble_evt_t * p_ble_evt)
|
||||
break;
|
||||
}
|
||||
|
||||
case BLE_GATTS_EVT_WRITE: {
|
||||
ble_gatts_evt_write_t * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
|
||||
// We got a param write event - add this to the object callback queue
|
||||
JsVar *evt = jsvNewObject();
|
||||
if (evt) {
|
||||
JsVar *data = jsvNewStringOfLength(p_evt_write->len);
|
||||
if (data) {
|
||||
jsvSetString(data, (char*)p_evt_write->data, p_evt_write->len);
|
||||
JsVar *ab = jsvNewArrayBufferFromString(data, p_evt_write->len);
|
||||
jsvUnLock(data);
|
||||
jsvObjectSetChildAndUnLock(evt, "data", ab);
|
||||
}
|
||||
char eventName[12];
|
||||
ble_handle_to_write_event_name(eventName, p_evt_write->handle);
|
||||
jsiQueueObjectCallbacks(execInfo.root, eventName, &evt, 1);
|
||||
jsvUnLock(evt);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// No implementation needed.
|
||||
break;
|
||||
@ -604,6 +631,171 @@ void jswrap_nrf_bluetooth_setAdvertising(JsVar *data) {
|
||||
jsExceptionHere(JSET_ERROR, "Got BLE error code %d", err_code);
|
||||
}
|
||||
|
||||
|
||||
/*JSON{
|
||||
"type" : "staticmethod",
|
||||
"class" : "NRF",
|
||||
"name" : "setServices",
|
||||
"generate" : "jswrap_nrf_bluetooth_setServices",
|
||||
"params" : [
|
||||
["data","JsVar","The service (and characteristics) to advertise"]
|
||||
]
|
||||
}
|
||||
BETA: This only partially works at the moment
|
||||
|
||||
Change the services and characteristics Espruino advertises.
|
||||
|
||||
```
|
||||
NRF.setServices({
|
||||
0xBCDE : {
|
||||
0xABCD : {
|
||||
value : "Hello", // optional
|
||||
maxLen : 5, // optional (otherwise is length of initial value)
|
||||
broadcast : false, // optional, default is false
|
||||
readable : true, // optional, default is false
|
||||
writable : true, // optional, default is false
|
||||
onWrite : function(evt) { // optional
|
||||
console.log("Got ", evt.data);
|
||||
}
|
||||
}
|
||||
// more characteristics allowed
|
||||
}
|
||||
// more services allowed
|
||||
});
|
||||
```
|
||||
*/
|
||||
void jswrap_nrf_bluetooth_setServices(JsVar *data) {
|
||||
uint32_t err_code;
|
||||
|
||||
// TODO: Reset services
|
||||
|
||||
if (jsvIsObject(data)) {
|
||||
JsvObjectIterator it;
|
||||
jsvObjectIteratorNew(&it, data);
|
||||
while (jsvObjectIteratorHasValue(&it)) {
|
||||
ble_uuid_t ble_uuid;
|
||||
uint16_t service_handle;
|
||||
|
||||
// Add the service
|
||||
BLE_UUID_BLE_ASSIGN(ble_uuid, jsvGetIntegerAndUnLock(jsvObjectIteratorGetKey(&it)));
|
||||
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
|
||||
&ble_uuid,
|
||||
&service_handle);
|
||||
if (err_code) {
|
||||
jsExceptionHere(JSET_ERROR, "Got BLE error code %d in gatts_service_add", err_code);
|
||||
break;
|
||||
}
|
||||
// sd_ble_gatts_include_add ?
|
||||
|
||||
// Now add characteristics
|
||||
JsVar *serviceVar = jsvObjectIteratorGetValue(&it);
|
||||
JsvObjectIterator serviceit;
|
||||
jsvObjectIteratorNew(&serviceit, serviceVar);
|
||||
while (jsvObjectIteratorHasValue(&serviceit)) {
|
||||
ble_uuid_t char_uuid;
|
||||
ble_gatts_char_md_t char_md;
|
||||
ble_gatts_attr_t attr_char_value;
|
||||
ble_gatts_attr_md_t attr_md;
|
||||
ble_gatts_char_handles_t characteristic_handles;
|
||||
|
||||
BLE_UUID_BLE_ASSIGN(char_uuid, jsvGetIntegerAndUnLock(jsvObjectIteratorGetKey(&serviceit)));
|
||||
JsVar *charVar = jsvObjectIteratorGetValue(&serviceit);
|
||||
|
||||
memset(&char_md, 0, sizeof(char_md));
|
||||
if (jsvGetBoolAndUnLock(jsvObjectGetChild(charVar, "broadcast", 0)))
|
||||
char_md.char_props.broadcast = 1;
|
||||
if (jsvGetBoolAndUnLock(jsvObjectGetChild(charVar, "readable", 0)))
|
||||
char_md.char_props.read = 1;
|
||||
if (jsvGetBoolAndUnLock(jsvObjectGetChild(charVar, "writable", 0))) {
|
||||
char_md.char_props.write = 1;
|
||||
char_md.char_props.write_wo_resp = 1;
|
||||
}
|
||||
char_md.p_char_user_desc = NULL;
|
||||
char_md.p_char_pf = NULL;
|
||||
char_md.p_user_desc_md = NULL;
|
||||
char_md.p_cccd_md = NULL;
|
||||
char_md.p_sccd_md = NULL;
|
||||
|
||||
memset(&attr_md, 0, sizeof(attr_md));
|
||||
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
|
||||
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
|
||||
attr_md.vloc = BLE_GATTS_VLOC_STACK;
|
||||
attr_md.rd_auth = 0;
|
||||
attr_md.wr_auth = 0;
|
||||
attr_md.vlen = 1; // TODO: variable length?
|
||||
|
||||
memset(&attr_char_value, 0, sizeof(attr_char_value));
|
||||
attr_char_value.p_uuid = &char_uuid;
|
||||
attr_char_value.p_attr_md = &attr_md;
|
||||
attr_char_value.init_len = 0;
|
||||
attr_char_value.init_offs = 0;
|
||||
attr_char_value.p_value = 0;
|
||||
attr_char_value.max_len = (uint16_t)jsvGetIntegerAndUnLock(jsvObjectGetChild(charVar, "maxLen", 0));
|
||||
if (attr_char_value.max_len==0) attr_char_value.max_len=1;
|
||||
|
||||
// get initial data
|
||||
JsVar *charValue = jsvObjectGetChild(charVar, "value", 0);
|
||||
if (charValue) {
|
||||
JSV_GET_AS_CHAR_ARRAY(vPtr, vLen, charValue);
|
||||
if (vPtr && vLen) {
|
||||
attr_char_value.p_value = (uint8_t*)vPtr;
|
||||
attr_char_value.init_len = vLen;
|
||||
if (attr_char_value.init_len > attr_char_value.max_len)
|
||||
attr_char_value.max_len = attr_char_value.init_len;
|
||||
}
|
||||
}
|
||||
|
||||
err_code = sd_ble_gatts_characteristic_add(service_handle,
|
||||
&char_md,
|
||||
&attr_char_value,
|
||||
&characteristic_handles);
|
||||
|
||||
jsvUnLock(charValue); // unlock here in case we were storing data in a flat string
|
||||
if (err_code) {
|
||||
jsExceptionHere(JSET_ERROR, "Got BLE error code %d in gatts_characteristic_add", err_code);
|
||||
break;
|
||||
}
|
||||
|
||||
// Add Write callback
|
||||
JsVar *writeCb = jsvObjectGetChild(charVar, "onWrite", 0);
|
||||
if (writeCb) {
|
||||
char eventName[12];
|
||||
ble_handle_to_write_event_name(eventName, characteristic_handles.value_handle);
|
||||
jsvObjectSetChildAndUnLock(execInfo.root, eventName, writeCb);
|
||||
}
|
||||
|
||||
jsvUnLock(charVar);
|
||||
/* We'd update the characteristic with:
|
||||
|
||||
memset(&hvx_params, 0, sizeof(hvx_params));
|
||||
hvx_params.handle = characteristic_handle.value_handle;
|
||||
hvx_params.p_data = p_string;
|
||||
hvx_params.p_len = &length;
|
||||
hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
|
||||
return sd_ble_gatts_hvx(p_nus->conn_handle, &hvx_params);
|
||||
|
||||
Maybe we could find the handle out based on characteristic UUID, rather than having
|
||||
to store it?
|
||||
|
||||
*/
|
||||
|
||||
jsvObjectIteratorNext(&serviceit);
|
||||
}
|
||||
jsvObjectIteratorFree(&serviceit);
|
||||
jsvUnLock(serviceVar);
|
||||
|
||||
jsvObjectIteratorNext(&it);
|
||||
}
|
||||
jsvObjectIteratorFree(&it);
|
||||
|
||||
|
||||
|
||||
} else if (!jsvIsUndefined(data)) {
|
||||
jsExceptionHere(JSET_TYPEERROR, "Expecting object or undefined, got %t", data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*JSON{
|
||||
"type" : "staticmethod",
|
||||
"class" : "NRF",
|
||||
|
||||
@ -21,6 +21,7 @@ void jswrap_nrf_bluetooth_wake(void);
|
||||
|
||||
JsVarFloat jswrap_nrf_bluetooth_getBattery(void);
|
||||
void jswrap_nrf_bluetooth_setAdvertising(JsVar *data);
|
||||
void jswrap_nrf_bluetooth_setServices(JsVar *data);
|
||||
void jswrap_nrf_bluetooth_setScan(JsVar *callback);
|
||||
void jswrap_nrf_bluetooth_setTxPower(JsVarInt pwr);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user