@@ -86,6 +86,7 @@ typedef enum {
86
86
static volatile BLEStatus bleStatus ;
87
87
88
88
#define BLE_SCAN_EVENT JS_EVENT_PREFIX"blescan"
89
+ #define BLE_WRITE_EVENT JS_EVENT_PREFIX"blew"
89
90
90
91
/**@brief Error handlers.
91
92
*
@@ -273,6 +274,12 @@ void jswrap_nrf_bluetooth_startAdvertise(void) {
273
274
APP_ERROR_CHECK (err_code );
274
275
}
275
276
277
+ /// Get the correct event name for a BLE write event to a characteristic (eventName should be 12 chars long)
278
+ void ble_handle_to_write_event_name (char * eventName , uint16_t handle ) {
279
+ strcpy (eventName , BLE_WRITE_EVENT );
280
+ itostr (handle , & eventName [strlen (eventName )], 16 );
281
+ }
282
+
276
283
/**@brief Function for the application's SoftDevice event handler.
277
284
*
278
285
* @param[in] p_ble_evt SoftDevice event.
@@ -334,7 +341,7 @@ static void on_ble_evt(ble_evt_t * p_ble_evt)
334
341
p_adv -> peer_addr .addr [0 ]));
335
342
JsVar * data = jsvNewStringOfLength (p_adv -> dlen );
336
343
if (data ) {
337
- jsvSetString (data , p_adv -> data , p_adv -> dlen );
344
+ jsvSetString (data , ( char * ) p_adv -> data , p_adv -> dlen );
338
345
JsVar * ab = jsvNewArrayBufferFromString (data , p_adv -> dlen );
339
346
jsvUnLock (data );
340
347
jsvObjectSetChildAndUnLock (evt , "data" , ab );
@@ -345,6 +352,26 @@ static void on_ble_evt(ble_evt_t * p_ble_evt)
345
352
break ;
346
353
}
347
354
355
+ case BLE_GATTS_EVT_WRITE : {
356
+ ble_gatts_evt_write_t * p_evt_write = & p_ble_evt -> evt .gatts_evt .params .write ;
357
+ // We got a param write event - add this to the object callback queue
358
+ JsVar * evt = jsvNewObject ();
359
+ if (evt ) {
360
+ JsVar * data = jsvNewStringOfLength (p_evt_write -> len );
361
+ if (data ) {
362
+ jsvSetString (data , (char * )p_evt_write -> data , p_evt_write -> len );
363
+ JsVar * ab = jsvNewArrayBufferFromString (data , p_evt_write -> len );
364
+ jsvUnLock (data );
365
+ jsvObjectSetChildAndUnLock (evt , "data" , ab );
366
+ }
367
+ char eventName [12 ];
368
+ ble_handle_to_write_event_name (eventName , p_evt_write -> handle );
369
+ jsiQueueObjectCallbacks (execInfo .root , eventName , & evt , 1 );
370
+ jsvUnLock (evt );
371
+ }
372
+ break ;
373
+ }
374
+
348
375
default :
349
376
// No implementation needed.
350
377
break ;
@@ -604,6 +631,171 @@ void jswrap_nrf_bluetooth_setAdvertising(JsVar *data) {
604
631
jsExceptionHere (JSET_ERROR , "Got BLE error code %d" , err_code );
605
632
}
606
633
634
+
635
+ /*JSON{
636
+ "type" : "staticmethod",
637
+ "class" : "NRF",
638
+ "name" : "setServices",
639
+ "generate" : "jswrap_nrf_bluetooth_setServices",
640
+ "params" : [
641
+ ["data","JsVar","The service (and characteristics) to advertise"]
642
+ ]
643
+ }
644
+ BETA: This only partially works at the moment
645
+
646
+ Change the services and characteristics Espruino advertises.
647
+
648
+ ```
649
+ NRF.setServices({
650
+ 0xBCDE : {
651
+ 0xABCD : {
652
+ value : "Hello", // optional
653
+ maxLen : 5, // optional (otherwise is length of initial value)
654
+ broadcast : false, // optional, default is false
655
+ readable : true, // optional, default is false
656
+ writable : true, // optional, default is false
657
+ onWrite : function(evt) { // optional
658
+ console.log("Got ", evt.data);
659
+ }
660
+ }
661
+ // more characteristics allowed
662
+ }
663
+ // more services allowed
664
+ });
665
+ ```
666
+ */
667
+ void jswrap_nrf_bluetooth_setServices (JsVar * data ) {
668
+ uint32_t err_code ;
669
+
670
+ // TODO: Reset services
671
+
672
+ if (jsvIsObject (data )) {
673
+ JsvObjectIterator it ;
674
+ jsvObjectIteratorNew (& it , data );
675
+ while (jsvObjectIteratorHasValue (& it )) {
676
+ ble_uuid_t ble_uuid ;
677
+ uint16_t service_handle ;
678
+
679
+ // Add the service
680
+ BLE_UUID_BLE_ASSIGN (ble_uuid , jsvGetIntegerAndUnLock (jsvObjectIteratorGetKey (& it )));
681
+ err_code = sd_ble_gatts_service_add (BLE_GATTS_SRVC_TYPE_PRIMARY ,
682
+ & ble_uuid ,
683
+ & service_handle );
684
+ if (err_code ) {
685
+ jsExceptionHere (JSET_ERROR , "Got BLE error code %d in gatts_service_add" , err_code );
686
+ break ;
687
+ }
688
+ // sd_ble_gatts_include_add ?
689
+
690
+ // Now add characteristics
691
+ JsVar * serviceVar = jsvObjectIteratorGetValue (& it );
692
+ JsvObjectIterator serviceit ;
693
+ jsvObjectIteratorNew (& serviceit , serviceVar );
694
+ while (jsvObjectIteratorHasValue (& serviceit )) {
695
+ ble_uuid_t char_uuid ;
696
+ ble_gatts_char_md_t char_md ;
697
+ ble_gatts_attr_t attr_char_value ;
698
+ ble_gatts_attr_md_t attr_md ;
699
+ ble_gatts_char_handles_t characteristic_handles ;
700
+
701
+ BLE_UUID_BLE_ASSIGN (char_uuid , jsvGetIntegerAndUnLock (jsvObjectIteratorGetKey (& serviceit )));
702
+ JsVar * charVar = jsvObjectIteratorGetValue (& serviceit );
703
+
704
+ memset (& char_md , 0 , sizeof (char_md ));
705
+ if (jsvGetBoolAndUnLock (jsvObjectGetChild (charVar , "broadcast" , 0 )))
706
+ char_md .char_props .broadcast = 1 ;
707
+ if (jsvGetBoolAndUnLock (jsvObjectGetChild (charVar , "readable" , 0 )))
708
+ char_md .char_props .read = 1 ;
709
+ if (jsvGetBoolAndUnLock (jsvObjectGetChild (charVar , "writable" , 0 ))) {
710
+ char_md .char_props .write = 1 ;
711
+ char_md .char_props .write_wo_resp = 1 ;
712
+ }
713
+ char_md .p_char_user_desc = NULL ;
714
+ char_md .p_char_pf = NULL ;
715
+ char_md .p_user_desc_md = NULL ;
716
+ char_md .p_cccd_md = NULL ;
717
+ char_md .p_sccd_md = NULL ;
718
+
719
+ memset (& attr_md , 0 , sizeof (attr_md ));
720
+ BLE_GAP_CONN_SEC_MODE_SET_OPEN (& attr_md .read_perm );
721
+ BLE_GAP_CONN_SEC_MODE_SET_OPEN (& attr_md .write_perm );
722
+ attr_md .vloc = BLE_GATTS_VLOC_STACK ;
723
+ attr_md .rd_auth = 0 ;
724
+ attr_md .wr_auth = 0 ;
725
+ attr_md .vlen = 1 ; // TODO: variable length?
726
+
727
+ memset (& attr_char_value , 0 , sizeof (attr_char_value ));
728
+ attr_char_value .p_uuid = & char_uuid ;
729
+ attr_char_value .p_attr_md = & attr_md ;
730
+ attr_char_value .init_len = 0 ;
731
+ attr_char_value .init_offs = 0 ;
732
+ attr_char_value .p_value = 0 ;
733
+ attr_char_value .max_len = (uint16_t )jsvGetIntegerAndUnLock (jsvObjectGetChild (charVar , "maxLen" , 0 ));
734
+ if (attr_char_value .max_len == 0 ) attr_char_value .max_len = 1 ;
735
+
736
+ // get initial data
737
+ JsVar * charValue = jsvObjectGetChild (charVar , "value" , 0 );
738
+ if (charValue ) {
739
+ JSV_GET_AS_CHAR_ARRAY (vPtr , vLen , charValue );
740
+ if (vPtr && vLen ) {
741
+ attr_char_value .p_value = (uint8_t * )vPtr ;
742
+ attr_char_value .init_len = vLen ;
743
+ if (attr_char_value .init_len > attr_char_value .max_len )
744
+ attr_char_value .max_len = attr_char_value .init_len ;
745
+ }
746
+ }
747
+
748
+ err_code = sd_ble_gatts_characteristic_add (service_handle ,
749
+ & char_md ,
750
+ & attr_char_value ,
751
+ & characteristic_handles );
752
+
753
+ jsvUnLock (charValue ); // unlock here in case we were storing data in a flat string
754
+ if (err_code ) {
755
+ jsExceptionHere (JSET_ERROR , "Got BLE error code %d in gatts_characteristic_add" , err_code );
756
+ break ;
757
+ }
758
+
759
+ // Add Write callback
760
+ JsVar * writeCb = jsvObjectGetChild (charVar , "onWrite" , 0 );
761
+ if (writeCb ) {
762
+ char eventName [12 ];
763
+ ble_handle_to_write_event_name (eventName , characteristic_handles .value_handle );
764
+ jsvObjectSetChildAndUnLock (execInfo .root , eventName , writeCb );
765
+ }
766
+
767
+ jsvUnLock (charVar );
768
+ /* We'd update the characteristic with:
769
+
770
+ memset(&hvx_params, 0, sizeof(hvx_params));
771
+ hvx_params.handle = characteristic_handle.value_handle;
772
+ hvx_params.p_data = p_string;
773
+ hvx_params.p_len = &length;
774
+ hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
775
+ return sd_ble_gatts_hvx(p_nus->conn_handle, &hvx_params);
776
+
777
+ Maybe we could find the handle out based on characteristic UUID, rather than having
778
+ to store it?
779
+
780
+ */
781
+
782
+ jsvObjectIteratorNext (& serviceit );
783
+ }
784
+ jsvObjectIteratorFree (& serviceit );
785
+ jsvUnLock (serviceVar );
786
+
787
+ jsvObjectIteratorNext (& it );
788
+ }
789
+ jsvObjectIteratorFree (& it );
790
+
791
+
792
+
793
+ } else if (!jsvIsUndefined (data )) {
794
+ jsExceptionHere (JSET_TYPEERROR , "Expecting object or undefined, got %t" , data );
795
+ }
796
+ }
797
+
798
+
607
799
/*JSON{
608
800
"type" : "staticmethod",
609
801
"class" : "NRF",
0 commit comments