-
-
Notifications
You must be signed in to change notification settings - Fork 754
/
Copy pathbluetooth.c
3790 lines (3422 loc) · 151 KB
/
bluetooth.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
*
* Copyright (C) 2013 Gordon Williams <[email protected]>
*
* 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
* ----------------------------------------------------------------------------
*/
#ifdef BLUETOOTH
#include "jswrap_bluetooth.h"
#ifdef USE_TERMINAL
#include "jswrap_terminal.h"
#endif
#include "jsinteractive.h"
#include "jsdevices.h"
#include "jshardware.h"
#include "jstimer.h"
#include "nrf5x_utils.h"
#include "bluetooth.h"
#include "bluetooth_utils.h"
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include "nordic_common.h"
#include "nrf.h"
#if NRF_SD_BLE_API_VERSION<5
#include "softdevice_handler.h"
#else
#include "nrf_sdm.h" // for softdevice_disable
#include "nrf_sdh.h"
#include "nrf_sdh_ble.h"
#include "nrf_sdh_soc.h"
#include "nrf_drv_clock.h" // for nrf_drv_clock_lfclk_request workaround
#endif
#include "nrf_log.h"
#include "ble_hci.h"
#include "ble_advdata.h"
#include "ble_conn_params.h"
#include "app_timer.h"
#include "ble_nus.h"
#include "app_util_platform.h"
#include "nrf_delay.h"
#ifdef USE_NFC
#include "hal_t2t/hal_nfc_t2t.h"
#endif
#if BLE_HIDS_ENABLED
#include "ble_hids.h"
#endif
#if ESPR_BLUETOOTH_ANCS
#include "bluetooth_ancs.h"
#endif
#if PEER_MANAGER_ENABLED
#include "peer_manager.h"
#include "fds.h"
#include "id_manager.h"
#if NRF_SD_BLE_API_VERSION<5
#include "fstorage.h"
#include "fstorage_internal_defs.h"
#endif
#include "ble_conn_state.h"
static pm_peer_id_t m_peer_id; /**< Device reference handle to the current bonded central. */
static pm_peer_id_t m_whitelist_peers[BLE_GAP_WHITELIST_ADDR_MAX_COUNT]; /**< List of peers currently in the whitelist. */
static uint32_t m_whitelist_peer_cnt; /**< Number of peers currently in the whitelist. */
static bool m_is_wl_changed; /**< Indicates if the whitelist has been changed since last time it has been updated in the Peer Manager. */
static volatile ble_gap_sec_params_t m_sec_params; /**< Current security parameters. */
#ifdef ESPR_BLE_PRIVATE_ADDRESS_SUPPORT
static pm_privacy_params_t m_privacy_params; /**< Current privacy parameters */
#endif // ESPR_BLE_PRIVATE_ADDRESS_SUPPORT
// needed for peer_manager_init so we can smoothly upgrade from pre 1v92 firmwares
#include "fds_internal_defs.h"
// If we have peer manager we have central mode and NRF52
// So just enable link security
#define LINK_SECURITY
#endif
#ifdef LINK_SECURITY
// Code to handle secure Bluetooth links
#include "ecc.h"
#define BLE_GAP_LESC_P256_SK_LEN 32
/**@brief GAP LE Secure Connections P-256 Private Key. */
typedef struct
{
uint8_t sk[BLE_GAP_LESC_P256_SK_LEN]; /**< LE Secure Connections Elliptic Curve Diffie-Hellman P-256 Private Key in little-endian. */
} ble_gap_lesc_p256_sk_t;
__ALIGN(4) static ble_gap_lesc_p256_sk_t m_lesc_sk; /**< LESC ECC Private Key */
__ALIGN(4) static ble_gap_lesc_p256_pk_t m_lesc_pk; /**< LESC ECC Public Key */
__ALIGN(4) static ble_gap_lesc_dhkey_t m_lesc_dhkey; /**< LESC ECC DH Key*/
#endif
// -----------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------
#if NRF_SD_BLE_API_VERSION < 5
#ifndef NRF_BLE_MAX_MTU_SIZE
#define NRF_BLE_MAX_MTU_SIZE GATT_MTU_SIZE_DEFAULT /**< MTU size used in the softdevice enabling and to reply to a BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST event. */
#endif
#define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(100, APP_TIMER_PRESCALER) /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */
#define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(10000, APP_TIMER_PRESCALER) /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
#define MAX_CONN_PARAMS_UPDATE_COUNT 3 /**< Number of attempts before giving up the connection parameter negotiation. */
#else
#ifndef GATT_MTU_SIZE_DEFAULT
#define GATT_MTU_SIZE_DEFAULT BLE_GATT_ATT_MTU_DEFAULT
#endif
#define NRF_BLE_MAX_MTU_SIZE NRF_SDH_BLE_GATT_MAX_MTU_SIZE /**< MTU size used in the softdevice enabling and to reply to a BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST event. */
#define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(100) /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */
#define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(10000) /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
#define MAX_CONN_PARAMS_UPDATE_COUNT 3 /**< Number of attempts before giving up the connection parameter negotiation. */
#endif
#define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) /**< Connection Supervision Timeout in 10 ms units, see @ref BLE_GAP_CP_LIMITS.*/
// Slave latency - the number of missed responses to BLE requests we're happy to put up with - see BLE_GAP_CP_LIMITS
#define SLAVE_LATENCY 0 // latency for *us* - we want to respond on every event
#define SLAVE_LATENCY_CENTRAL 2 // when connecting to something else, be willing to put up with some lack of response
#if NRF_BLE_MAX_MTU_SIZE != GATT_MTU_SIZE_DEFAULT
#define EXTENSIBLE_MTU // The MTU can be extended past the default of 23
#endif
#define APP_BLE_OBSERVER_PRIO 2 /**< Application's BLE observer priority. You shouldn't need to modify this value. */
#define APP_SOC_OBSERVER_PRIO 1 /**< Applications' SoC observer priority. You shoulnd't need to modify this value. */
/* We want to listen as much of the time as possible. Not sure if 100/100 is feasible (50/100 is what's used in examples), but it seems to work fine like this. */
#define SCAN_INTERVAL MSEC_TO_UNITS(100, UNIT_0_625_MS) /**< Scan interval in units of 0.625 millisecond - 100 msec */
#define SCAN_WINDOW MSEC_TO_UNITS(100, UNIT_0_625_MS) /**< Scan window in units of 0.625 millisecond - 100 msec */
#define APP_ADV_TIMEOUT_IN_SECONDS 180 /**< The advertising timeout (in units of seconds). */
// BLE HID stuff
#define BASE_USB_HID_SPEC_VERSION 0x0101 /**< Version number of base USB HID Specification implemented by this application. */
#define HID_OUTPUT_REPORT_INDEX 0 /**< Index of Output Report. */
#define HID_OUTPUT_REPORT_MAX_LEN 1 /**< Maximum length of Output Report. */
#define HID_INPUT_REPORT_KEYS_INDEX 0 /**< Index of Input Report. */
#define HID_INPUT_REP_REF_ID 0 /**< Id of reference to Keyboard Input Report. */
#define HID_OUTPUT_REP_REF_ID 0 /**< Id of reference to Keyboard Output Report. */
#define HID_INPUT_REPORT_KEYS_MAX_LEN 8
#define APP_FEATURE_NOT_SUPPORTED BLE_GATT_STATUS_ATTERR_APP_BEGIN + 2 /**< Reply when unsupported features are requested. */
// -----------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------
#define ADVERTISE_MAX_UUIDS 4 ///< maximum custom UUIDs to advertise
#if NRF_SD_BLE_API_VERSION < 5
static ble_nus_t m_nus; /**< Structure to identify the Nordic UART Service. */
#elif NRF_SD_BLE_API_VERSION < 6
BLE_NUS_DEF(m_nus); /**< Structure to identify the Nordic UART Service. */
#else
BLE_NUS_DEF(m_nus, NRF_SDH_BLE_TOTAL_LINK_COUNT); /**< Structure to identify the Nordic UART Service. */
#endif
#if BLE_HIDS_ENABLED
#if NRF_SD_BLE_API_VERSION < 5
static ble_hids_t m_hids; /**< Structure used to identify the HID service. */
#elif NRF_SD_BLE_API_VERSION < 6
BLE_HIDS_DEF(m_hids);
#else
BLE_HIDS_DEF(m_hids,
NRF_SDH_BLE_TOTAL_LINK_COUNT,
HID_INPUT_REPORT_KEYS_MAX_LEN,
HID_OUTPUT_REPORT_MAX_LEN);
#endif
static bool m_in_boot_mode = false;
#endif
#if NRF_SD_BLE_API_VERSION > 5
uint8_t m_adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET; /**< Advertising handle used to identify an advertising set. */
int8_t m_tx_power = 0;
#endif
volatile uint16_t m_peripheral_conn_handle; /**< Handle of the current connection. */
#ifndef SAVE_ON_FLASH
ble_gap_addr_t m_peripheral_addr;
#endif
#ifdef EXTENSIBLE_MTU
volatile uint16_t m_peripheral_effective_mtu;
#else
const uint16_t m_peripheral_effective_mtu = GATT_MTU_SIZE_DEFAULT;
#endif
#if CENTRAL_LINK_COUNT>0
volatile uint16_t m_central_conn_handles[CENTRAL_LINK_COUNT]; /**< Handle for central mode connection */
#ifdef EXTENSIBLE_MTU
volatile uint16_t m_central_effective_mtu;
#else
const uint16_t m_central_effective_mtu = GATT_MTU_SIZE_DEFAULT;
#endif
#endif
#ifdef USE_NFC
volatile bool nfcEnabled = false;
#endif
/// The advertising interval (in units of 0.625 ms)
uint16_t bleAdvertisingInterval = MSEC_TO_UNITS(BLUETOOTH_ADVERTISING_INTERVAL, UNIT_0_625_MS);
volatile BLEStatus bleStatus = 0;
ble_uuid_t bleUUIDFilter;
/// When doing service discovery, this is the last handle we'll need to discover with
uint16_t bleFinalHandle;
/// Array of data waiting to be sent over Bluetooth NUS
uint8_t nusTxBuf[BLE_NUS_MAX_DATA_LEN];
/// Number of bytes ready to send inside nusTxBuf
uint16_t nuxTxBufLength = 0;
#ifdef NRF52_SERIES
#define DYNAMIC_INTERVAL_ADJUSTMENT
#endif
/* Dynamic interval adjustment kicks Espruino into a low power mode after
* a certain amount of time of being connected with nothing happening. The next time
* stuff happens it renegotiates back to the high rate, but this could take a second
* or two.
*/
#ifdef DYNAMIC_INTERVAL_ADJUSTMENT
#define BLE_DYNAMIC_INTERVAL_LOW_RATE 200 // connection interval when idle (milliseconds)
#define BLE_DYNAMIC_INTERVAL_HIGH_RATE 7.5 // connection interval when not idle (milliseconds) - 7.5ms is fastest possible
#define BLE_DEFAULT_HIGH_INTERVAL true
#define DEFAULT_PERIPH_MAX_CONN_INTERVAL BLE_DYNAMIC_INTERVAL_HIGH_RATE // highest possible on connect
#define BLE_DYNAMIC_INTERVAL_IDLE_TIME (int)(120000 / BLE_DYNAMIC_INTERVAL_HIGH_RATE) // time in milliseconds at which we enter idle
/// How many connection intervals has BLE been idle for? Use for dynamic interval adjustment
uint16_t bleIdleCounter = 0;
/// Are we using a high speed or low speed interval at the moment?
bool bleHighInterval;
#else
// No interval adjustment - allow us to enter a slightly lower power connection state
#define DEFAULT_PERIPH_MAX_CONN_INTERVAL 20
#endif
/// The interval for the current connection (periph/central may be mixed) (in units of 1.25 ms)
uint16_t blePeriphConnectionInterval = 6;
static ble_gap_sec_params_t get_gap_sec_params();
#if PEER_MANAGER_ENABLED
static bool jsble_can_pair_with_peer(const ble_gap_sec_params_t *own_params, const ble_gap_sec_params_t *peer_params);
#endif
#if NRF_SD_BLE_API_VERSION>5
// if m_scan_param.extended=0, use BLE_GAP_SCAN_BUFFER_MIN
// if m_scan_param.extended=1, use BLE_GAP_SCAN_BUFFER_EXTENDED_MIN
uint8_t m_scan_buffer_data[BLE_GAP_SCAN_BUFFER_EXTENDED_MIN]; /**< buffer where advertising reports will be stored by the SoftDevice. */
ble_data_t m_scan_buffer = {
m_scan_buffer_data,
sizeof(m_scan_buffer_data)
};
// TODO: this is 255 bytes to allow extended advertising. Maybe we don't need that all the time?
#endif
// -----------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------
/// 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;
#ifndef ESPR_NO_BLUETOOTH_MESSAGES
const char *name = 0;
switch (err_code) {
case NRF_ERROR_NO_MEM : name="NO_MEM"; break;
case NRF_ERROR_INVALID_PARAM : name="INVALID_PARAM"; break;
case NRF_ERROR_INVALID_STATE : name="INVALID_STATE"; break;
case NRF_ERROR_INVALID_LENGTH: name="INVALID_LENGTH"; break;
case NRF_ERROR_INVALID_FLAGS : name="INVALID_FLAGS"; break;
case NRF_ERROR_DATA_SIZE : name="DATA_SIZE"; break;
case NRF_ERROR_FORBIDDEN : name="FORBIDDEN"; break;
case NRF_ERROR_BUSY : name="BUSY"; break;
case NRF_ERROR_INVALID_ADDR : name="INVALID ADDR"; break;
case BLE_ERROR_INVALID_CONN_HANDLE
: name="INVALID_CONN_HANDLE"; break;
case BLE_ERROR_GAP_INVALID_BLE_ADDR
: name="INVALID_BLE_ADDR"; break;
case NRF_ERROR_CONN_COUNT : name="CONN_COUNT"; break;
case BLE_ERROR_NOT_ENABLED : name="NOT_ENABLED"; break;
#if NRF_SD_BLE_API_VERSION<5
case BLE_ERROR_NO_TX_PACKETS : name="NO_TX_PACKETS"; break;
#endif
}
if (name)
return jsvVarPrintf("ERR 0x%x (%s)", err_code, name);
else
#endif // ESPR_NO_BLUETOOTH_MESSAGES
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));
assert(IOEVENT_MAX_LEN >= NRF_BLE_MAX_MTU_SIZE);
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) {
case BLEP_CONNECTED: {
assert(bufferLen == sizeof(ble_gap_addr_t));
ble_gap_addr_t *peer_addr = (ble_gap_addr_t*)buffer;
bleQueueEventAndUnLock(JS_EVENT_PREFIX"connect", bleAddrToStr(*peer_addr));
jshHadEvent();
break;
}
case BLEP_DISCONNECTED: {
JsVar *reason = jsvNewFromInteger(data);
bleQueueEventAndUnLock(JS_EVENT_PREFIX"disconnect", reason);
break;
}
case BLEP_ADVERTISING_START: {
if (bleStatus & BLE_IS_ADVERTISING) jsble_advertising_stop(); // if we were advertising, stop
jsble_advertising_start(); // start advertising - we ignore the return code here
break;
}
case BLEP_ADVERTISING_STOP: {
jsble_advertising_stop();
break;
}
case BLEP_RESTART_SOFTDEVICE: {
jsble_restart_softdevice(NULL);
break;
}
case BLEP_RSSI_PERIPH: {
JsVar *evt = jsvNewFromInteger((signed char)data);
if (evt) jsiQueueObjectCallbacks(execInfo.root, BLE_RSSI_EVENT, &evt, 1);
jsvUnLock(evt);
break;
}
#if CENTRAL_LINK_COUNT>0
case BLEP_RSSI_CENTRAL: { // rssi as data low byte, index in m_central_conn_handles as high byte
int centralIdx = data>>8; // index in m_central_conn_handles
JsVar *gattServer = bleGetActiveBluetoothGattServer(centralIdx);
if (gattServer) {
JsVar *rssi = jsvNewFromInteger((signed char)(data & 255));
JsVar *bluetoothDevice = jsvObjectGetChildIfExists(gattServer, "device");
if (bluetoothDevice) {
jsvObjectSetChild(bluetoothDevice, "rssi", rssi);
}
jsiQueueObjectCallbacks(gattServer, BLE_RSSI_EVENT, &rssi, 1);
jsvUnLock3(rssi, gattServer, bluetoothDevice);
}
break;
}
case BLEP_TASK_DISCOVER_CHARACTERISTIC: { /* bleTaskInfo = BluetoothRemoteGATTService, bleTaskInfo2 = an array of BluetoothRemoteGATTCharacteristic, or 0 */
if (!bleInTask(BLETASK_CHARACTERISTIC)) {
jsExceptionHere(JSET_INTERNALERROR,"Wrong task: %d vs %d", bleGetCurrentTask(), BLETASK_PRIMARYSERVICE);
break;
}
ble_gattc_char_t *p_chr = (ble_gattc_char_t*)buffer;
if (!bleTaskInfo2) bleTaskInfo2 = jsvNewEmptyArray();
if (!bleTaskInfo2) break;
JsVar *o = jspNewObject(0, "BluetoothRemoteGATTCharacteristic");
if (o) {
jsvObjectSetChild(o,"service", bleTaskInfo);
jsvObjectSetChildAndUnLock(o,"uuid", bleUUIDToStr(p_chr->uuid));
jsvObjectSetChildAndUnLock(o,"handle_value", jsvNewFromInteger(p_chr->handle_value));
jsvObjectSetChildAndUnLock(o,"handle_decl", jsvNewFromInteger(p_chr->handle_decl));
JsVar *p = jsvNewObject();
if (p) {
jsvObjectSetChildAndUnLock(p,"broadcast",jsvNewFromBool(p_chr->char_props.broadcast));
jsvObjectSetChildAndUnLock(p,"read",jsvNewFromBool(p_chr->char_props.read));
jsvObjectSetChildAndUnLock(p,"writeWithoutResponse",jsvNewFromBool(p_chr->char_props.write_wo_resp));
jsvObjectSetChildAndUnLock(p,"write",jsvNewFromBool(p_chr->char_props.write));
jsvObjectSetChildAndUnLock(p,"notify",jsvNewFromBool(p_chr->char_props.notify));
jsvObjectSetChildAndUnLock(p,"indicate",jsvNewFromBool(p_chr->char_props.indicate));
jsvObjectSetChildAndUnLock(p,"authenticatedSignedWrites",jsvNewFromBool(p_chr->char_props.auth_signed_wr));
jsvObjectSetChildAndUnLock(o,"properties", p);
}
// char_props?
jsvArrayPushAndUnLock(bleTaskInfo2, o);
}
break;
}
case BLEP_TASK_DISCOVER_CHARACTERISTIC_COMPLETE: { /* bleTaskInfo = BluetoothRemoteGATTService, bleTaskInfo2 = an array of BluetoothRemoteGATTCharacteristic, or 0 */
// When done, send the result to the handler
if (bleTaskInfo2 && bleUUIDFilter.type != BLE_UUID_TYPE_UNKNOWN) {
// single item because filtering
JsVar *t = jsvSkipNameAndUnLock(jsvArrayPopFirst(bleTaskInfo2));
jsvUnLock(bleTaskInfo2);
bleTaskInfo2 = t;
}
if (bleTaskInfo) bleCompleteTaskSuccess(BLETASK_CHARACTERISTIC, bleTaskInfo2);
else bleCompleteTaskFailAndUnLock(BLETASK_CHARACTERISTIC, jsvNewFromString("No Characteristics found"));
break;
}
case BLEP_TASK_DISCOVER_CCCD: { /* bleTaskInfo = BluetoothRemoteGATTCharacteristic */
uint16_t cccd_handle = data;
if (cccd_handle) {
if(bleTaskInfo)
jsvObjectSetChildAndUnLock(bleTaskInfo, "handle_cccd", jsvNewFromInteger(cccd_handle));
// Switch task here rather than completing...
bleSwitchTask(BLETASK_CHARACTERISTIC_NOTIFY);
jsble_central_characteristicNotify(jswrap_ble_BluetoothRemoteGATTCharacteristic_getHandle(bleTaskInfo), bleTaskInfo, true);
} else {
// Couldn't find anything - just report error
bleCompleteTaskFailAndUnLock(BLETASK_CHARACTERISTIC_DESC_AND_STARTNOTIFY, jsvNewFromString("CCCD Handle not found"));
}
break;
}
#endif // CENTRAL_LINK_COUNT>0
#if PEER_MANAGER_ENABLED
case BLEP_BONDING_STATUS: {
BLEBondingStatus bondStatus = (BLEBondingStatus)data;
const char *bondString = 0;
switch (bondStatus) {
case BLE_BOND_REQUEST:
bondString="request";
break;
case BLE_BOND_START:
bondString="start";
break;
case BLE_BOND_SUCCESS:
bondString="success";
if (bleInTask(BLETASK_BONDING))
bleCompleteTaskSuccess(BLETASK_BONDING, 0);
break;
case BLE_BOND_FAIL:
bondString="fail";
if (bleInTask(BLETASK_BONDING))
bleCompleteTaskFailAndUnLock(BLETASK_BONDING, jsvNewFromString("Bonding failed"));
break;
}
if (bondString)
bleQueueEventAndUnLock(JS_EVENT_PREFIX"bond", jsvNewFromString(bondString));
break;
}
#endif
#ifdef USE_NFC
case BLEP_NFC_STATUS:
bleQueueEventAndUnLock(data ? JS_EVENT_PREFIX"NFCon" : JS_EVENT_PREFIX"NFCoff", 0);
break;
case BLEP_NFC_TX:
bleQueueEventAndUnLock(JS_EVENT_PREFIX"NFCtx", 0);
break;
case BLEP_NFC_RX: {
/* try to fetch NfcData data */
JsVar *nfcData = jsvObjectGetChildIfExists(execInfo.hiddenRoot, "NfcData");
if(nfcData) {
/* success - handle request internally */
JSV_GET_AS_CHAR_ARRAY(nfcDataPtr, nfcDataLen, nfcData);
jsvUnLock(nfcData);
/* check data, on error let request go into timeout - reader will retry. */
if (!nfcDataPtr || !nfcDataLen) {
break;
}
/* check rx data length and read block command code (0x30) */
if(bufferLen < 2 || buffer[0] != 0x30) {
jsble_nfc_send_rsp(0, 0); /* switch to rx */
break;
}
/* fetch block index (addressing is done in multiples of 4 byte */
size_t idx = buffer[1] * 4;
/* assemble 16 byte block */
uint8_t buf[16]; memset(buf, '\0', 16);
if(idx + 16 < nfcDataLen) {
memcpy(buf, nfcDataPtr + idx, 16);
} else
if(idx < nfcDataLen) {
memcpy(buf, nfcDataPtr + idx, nfcDataLen - idx);
}
/* send response */
jsble_nfc_send(buf, 16);
} else {
/* no NfcData available, fire js-event */
bleQueueEventAndUnLock(JS_EVENT_PREFIX"NFCrx",
jsvNewArrayBufferWithData(bufferLen, buffer));
}
break;
}
#endif
#if BLE_HIDS_ENABLED
case BLEP_HID_SENT:
jsiQueueObjectCallbacks(execInfo.root, BLE_HID_SENT_EVENT, 0, 0);
jsvObjectSetChild(execInfo.root, BLE_HID_SENT_EVENT, 0); // fire only once
jshHadEvent();
break;
case BLEP_HID_VALUE:
bleQueueEventAndUnLock(JS_EVENT_PREFIX"HID", jsvNewFromInteger(data));
break;
#endif
#ifdef LINK_SECURITY
case BLEP_TASK_PASSKEY_DISPLAY: { // data = connection handle
uint16_t conn_handle = data;
#if CENTRAL_LINK_COUNT>0
/* TODO: yes/no passkey
uint8_t match_request : 1; If 1 requires the application to report the match using @ref sd_ble_gap_auth_key_reply
with either @ref BLE_GAP_AUTH_KEY_TYPE_NONE if there is no match or
@ref BLE_GAP_AUTH_KEY_TYPE_PASSKEY if there is a match. */
int centralIdx = jsble_get_central_connection_idx(conn_handle);
#endif
if (bufferLen==BLE_GAP_PASSKEY_LEN) {
buffer[BLE_GAP_PASSKEY_LEN] = 0;
JsVar *passkey = jsvNewFromString((char*)buffer);
#if CENTRAL_LINK_COUNT>0
if (centralIdx<0) { // it's on the peripheral connection
#endif
bleQueueEventAndUnLock(JS_EVENT_PREFIX"passkey", passkey);
#if CENTRAL_LINK_COUNT>0
} else { // it's on a central connection
JsVar *gattServer = bleGetActiveBluetoothGattServer(centralIdx);
if (gattServer) {
JsVar *bluetoothDevice = jsvObjectGetChildIfExists(gattServer, "device");
if (bluetoothDevice) {
jsiQueueObjectCallbacks(bluetoothDevice, JS_EVENT_PREFIX"passkey", &passkey, 1);
jshHadEvent();
}
jsvUnLock2(bluetoothDevice, gattServer);
}
}
#endif
jsvUnLock(passkey);
}
break;
}
case BLEP_TASK_AUTH_KEY_REQUEST: { // data = connection handle
//jsiConsolePrintf("BLEP_TASK_AUTH_KEY_REQUEST\n");
uint16_t conn_handle = data;
#if CENTRAL_LINK_COUNT>0
int centralIdx = jsble_get_central_connection_idx(conn_handle);
JsVar *gattServer = bleGetActiveBluetoothGattServer(centralIdx);
if (gattServer) {
jsvObjectSetChildAndUnLock(gattServer, "connected", jsvNewFromBool(false));
JsVar *bluetoothDevice = jsvObjectGetChildIfExists(gattServer, "device");
if (bluetoothDevice) {
// HCI error code, see BLE_HCI_STATUS_CODES in ble_hci.h
jsiQueueObjectCallbacks(bluetoothDevice, JS_EVENT_PREFIX"passkeyRequest", 0, 0);
jshHadEvent();
}
jsvUnLock2(gattServer, bluetoothDevice);
}
#endif
if (conn_handle == m_peripheral_conn_handle) {
bool ok = false;
JsVar *options = jsvObjectGetChildIfExists(execInfo.hiddenRoot, BLE_NAME_SECURITY);
if (jsvIsObject(options)) {
JsVar *oobKey = jsvObjectGetChildIfExists(options, "oob");
JSV_GET_AS_CHAR_ARRAY(keyPtr, keyLen, oobKey);
if (keyPtr && keyLen==BLE_GAP_SEC_KEY_LEN) {
ok = true;
//jsiConsolePrintf("Replying with Auth key %d,%d,%d,%d...\n",keyPtr[0],keyPtr[1],keyPtr[2],keyPtr[3]);
jsble_check_error(sd_ble_gap_auth_key_reply(conn_handle,
BLE_GAP_AUTH_KEY_TYPE_OOB,
(uint8_t *)keyPtr));
}
jsvUnLock(oobKey);
}
jsvUnLock(options);
if (!ok) jsExceptionHere(JSET_INTERNALERROR, "Auth key requested, but NRF.setSecurity({oob}) not valid");
// TODO: this could be because we have keyboard:1 and the connecting device is displaying a passkey and wants US to send it back (we only implement that for central at the moment)
}
break;
}
case BLEP_TASK_AUTH_STATUS: {
//uint16_t conn_handle = data;
ble_gap_evt_auth_status_t *auth_status = (ble_gap_evt_auth_status_t*)buffer;
JsVar *o = jsvNewObject();
if (o) {
const char *str=NULL;
#ifndef ESPR_NO_BLUETOOTH_MESSAGES
switch(auth_status->auth_status) {
case BLE_GAP_SEC_STATUS_SUCCESS : str="SUCCESS";break;
case BLE_GAP_SEC_STATUS_TIMEOUT : str="TIMEOUT";break;
case BLE_GAP_SEC_STATUS_PDU_INVALID : str="PDU_INVALID";break;
case BLE_GAP_SEC_STATUS_RFU_RANGE1_BEGIN : str="RFU_RANGE1_BEGIN";break;
case BLE_GAP_SEC_STATUS_RFU_RANGE1_END : str="RFU_RANGE1_END";break;
case BLE_GAP_SEC_STATUS_PASSKEY_ENTRY_FAILED : str="PASSKEY_ENTRY_FAILED";break;
case BLE_GAP_SEC_STATUS_OOB_NOT_AVAILABLE : str="OOB_NOT_AVAILABLE";break;
case BLE_GAP_SEC_STATUS_AUTH_REQ : str="AUTH_REQ";break;
case BLE_GAP_SEC_STATUS_CONFIRM_VALUE : str="CONFIRM_VALUE";break;
case BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP : str="PAIRING_NOT_SUPP";break;
case BLE_GAP_SEC_STATUS_ENC_KEY_SIZE : str="ENC_KEY_SIZE";break;
case BLE_GAP_SEC_STATUS_SMP_CMD_UNSUPPORTED : str="SMP_CMD_UNSUPPORTED";break;
case BLE_GAP_SEC_STATUS_UNSPECIFIED : str="UNSPECIFIED";break;
case BLE_GAP_SEC_STATUS_REPEATED_ATTEMPTS : str="REPEATED_ATTEMPTS";break;
case BLE_GAP_SEC_STATUS_INVALID_PARAMS : str="INVALID_PARAMS";break;
case BLE_GAP_SEC_STATUS_DHKEY_FAILURE : str="DHKEY_FAILURE";break;
case BLE_GAP_SEC_STATUS_NUM_COMP_FAILURE : str="NUM_COMP_FAILURE";break;
case BLE_GAP_SEC_STATUS_BR_EDR_IN_PROG : str="BR_EDR_IN_PROG";break;
case BLE_GAP_SEC_STATUS_X_TRANS_KEY_DISALLOWED : str="X_TRANS_KEY_DISALLOWED";break;
}
#endif // ESPR_NO_BLUETOOTH_MESSAGES
jsvObjectSetChildAndUnLock(o,"auth_status",str?jsvNewFromString(str):jsvNewFromInteger(auth_status->auth_status));
jsvObjectSetChildAndUnLock(o, "bonded", jsvNewFromBool(auth_status->bonded));
jsvObjectSetChildAndUnLock(o, "lv4", jsvNewFromInteger(auth_status->sm1_levels.lv4));
jsvObjectSetChildAndUnLock(o, "kdist_own", jsvNewFromInteger(*((uint8_t *)&auth_status->kdist_own)));
jsvObjectSetChildAndUnLock(o, "kdist_peer", jsvNewFromInteger(*((uint8_t *)&auth_status->kdist_peer)));
bleQueueEventAndUnLock(JS_EVENT_PREFIX"security",o);
}
break;
}
#endif
#ifdef ESPR_BLUETOOTH_ANCS
case BLEP_ANCS_DISCOVERED:
ble_ancs_handle_discovered();
break;
case BLEP_ANCS_NOTIF:
ble_ancs_handle_notif(blep, (ble_ancs_c_evt_notif_t*)buffer);
break;
case BLEP_ANCS_NOTIF_ATTR:
ble_ancs_handle_notif_attr(blep, (ble_ancs_c_evt_notif_t*)buffer);
break;
case BLEP_ANCS_APP_ATTR:
ble_ancs_handle_app_attr(blep, (char *)buffer, bufferLen);
break;
case BLEP_ANCS_ERROR:
if (BLETASK_IS_ANCS(bleGetCurrentTask()))
bleCompleteTaskFailAndUnLock(bleGetCurrentTask(), jsvNewFromString("ANCS Error"));
break;
case BLEP_AMS_DISCOVERED:
ble_ams_handle_discovered();
break;
case BLEP_AMS_TRACK_UPDATE:
ble_ams_handle_track_update(blep, data, (char *)buffer, bufferLen);
break;
case BLEP_AMS_PLAYER_UPDATE:
ble_ams_handle_player_update(blep, data, (char *)buffer, bufferLen);
break;
case BLEP_AMS_ATTRIBUTE:
ble_ams_handle_attribute(blep, (char *)buffer, bufferLen);
break;
case BLEP_CTS_DISCOVERED:
ble_cts_handle_discovered();
break;
case BLEP_CTS_TIME:
ble_cts_handle_time(blep, (char *)buffer, bufferLen);
break;
#endif
#ifndef SAVE_ON_FLASH
default:
jsWarn("jsble_exec_pending: Unknown enum type %d",(int)blep);
#endif
}
if (jspIsInterrupted())
jsWarn("Interrupted processing event %d",(int)blep);
return eventBytesHandled;
}
// -----------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------
/** Set the connection interval of the peripheral connection. Returns an error code. */
uint32_t jsble_set_periph_connection_interval(JsVarFloat min, JsVarFloat max) {
ble_gap_conn_params_t gap_conn_params;
memset(&gap_conn_params, 0, sizeof(gap_conn_params));
gap_conn_params.min_conn_interval = (uint16_t)(0.5+MSEC_TO_UNITS(min, UNIT_1_25_MS)); // Minimum acceptable connection interval
gap_conn_params.max_conn_interval = (uint16_t)(0.5+MSEC_TO_UNITS(max, UNIT_1_25_MS)); // Maximum acceptable connection interval
gap_conn_params.slave_latency = SLAVE_LATENCY;
gap_conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT;
if (jsble_has_peripheral_connection()) {
// Connected - initiate dynamic change
return ble_conn_params_change_conn_params(
#if NRF_SD_BLE_API_VERSION>=5
m_peripheral_conn_handle,
#endif
&gap_conn_params);
} else {
// Not connected, just tell the stack
return sd_ble_gap_ppcp_set(&gap_conn_params);
}
}
/** Is BLE connected to any device at all? */
bool jsble_has_connection() {
if (jsble_has_central_connection())
return true;
return m_peripheral_conn_handle != BLE_CONN_HANDLE_INVALID;
}
/** Is BLE connected to a central device at all? */
bool jsble_has_central_connection() {
#if CENTRAL_LINK_COUNT>0
for (int i=0;i<CENTRAL_LINK_COUNT;i++)
if (m_central_conn_handles[i] != BLE_CONN_HANDLE_INVALID)
return true;
#endif
return false;
}
/** Return the index of the central connection in m_central_conn_handles, or -1 */
int jsble_get_central_connection_idx(uint16_t handle) {
#if CENTRAL_LINK_COUNT>0
for (int i=0;i<CENTRAL_LINK_COUNT;i++)
if (m_central_conn_handles[i] == handle)
return i;
#endif
return -1;
}
/** Is BLE connected to a server device at all (eg, the simple, 'slave' mode)? */
bool jsble_has_peripheral_connection() {
return (m_peripheral_conn_handle != BLE_CONN_HANDLE_INVALID);
}
/** Call this when something happens on BLE with this as
* a peripheral - used with Dynamic Interval Adjustment */
void jsble_peripheral_activity() {
#ifdef DYNAMIC_INTERVAL_ADJUSTMENT
if (jsble_has_peripheral_connection() &&
!(bleStatus & BLE_DISABLE_DYNAMIC_INTERVAL) &&
bleIdleCounter < 10) {
// so we must have been called once before
if (!bleHighInterval) {
bleHighInterval = true;
jsble_set_periph_connection_interval(BLE_DYNAMIC_INTERVAL_HIGH_RATE, BLE_DYNAMIC_INTERVAL_HIGH_RATE);
}
}
bleIdleCounter = 0;
#endif
}
/// Checks for error and reports an exception if there was one. Return true on error
#ifndef SAVE_ON_FLASH_EXTREME
bool jsble_check_error_line(uint32_t err_code, int lineNumber) {
JsVar *v = jsble_get_error_string(err_code);
if (!v) return 0;
jsExceptionHere(JSET_ERROR, "%v (:%d)", v, lineNumber);
bleQueueEventAndUnLock(JS_EVENT_PREFIX"error", v);
return true;
}
#else
bool jsble_check_error(uint32_t err_code) {
JsVar *v = jsble_get_error_string(err_code);
if (!v) return 0;
jsExceptionHere(JSET_ERROR, "%v", v);
jsvUnLock(v);
return true;
}
#endif
// -----------------------------------------------------------------------------------
// --------------------------------------------------------------------------- ERRORS
void ble_app_error_handler(uint32_t error_code, uint32_t line_num, const uint8_t * p_file_name) {
#ifdef LED1_PININDEX
jshPinOutput(LED1_PININDEX, LED1_ONSTATE);
#endif
#ifdef LED2_PININDEX
jshPinOutput(LED2_PININDEX, LED2_ONSTATE);
#endif
#ifdef LED3_PININDEX
jshPinOutput(LED3_PININDEX, LED3_ONSTATE);
#endif
jsiConsolePrintf("NRF ERROR 0x%x\n at %s:%d\nREBOOTING.\n", error_code, p_file_name?(const char *)p_file_name:"?", line_num);
#ifdef USE_TERMINAL
// If we have a terminal, try and write to that!
jsiStatus |= JSIS_ECHO_OFF;
jsiSetConsoleDevice(EV_TERMINAL, 1);
jsiConsolePrintf("NRF ERROR 0x%x\n at %s:%d\nREBOOTING.\n", error_code, p_file_name?(const char *)p_file_name:"?", line_num);
jswrap_terminal_idle();
#endif
/* don't flush - just delay. If this happened in an IRQ, waiting to flush
* will result in the device locking up. */
#ifdef USE_TERMINAL
nrf_delay_ms(10000);
#else
nrf_delay_ms(1000);
#endif
NVIC_SystemReset();
}
void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name) {
ble_app_error_handler(0xDEADBEEF, line_num, p_file_name);
}
void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info) {
if (id == NRF_FAULT_ID_SDK_ERROR) {
error_info_t *error_info = (error_info_t *)info;
ble_app_error_handler(error_info->err_code, error_info->line_num, error_info->p_file_name);
} else
ble_app_error_handler(id, pc, 0);
}
/// Function for handling errors from the Connection Parameters module.
static void conn_params_error_handler(uint32_t nrf_error) {
/* connection parameters module can produce this if the connection
* is disconnected at just the right point while it is trying to
* negotiate connection parameters. Ignore it, since we don't
* want it to be able to reboot the device!
*/
if (nrf_error == NRF_ERROR_INVALID_STATE)
return;
APP_ERROR_CHECK_NOT_URGENT(nrf_error);
}
#if BLE_HIDS_ENABLED
static void service_error_handler(uint32_t nrf_error) {
APP_ERROR_CHECK_NOT_URGENT(nrf_error);
}
#endif
#if NRF_LOG_ENABLED
void nrf_log_frontend_std_0(uint32_t severity_mid, char const * const p_str) {
nrf_log_frontend_std_6(severity_mid, p_str,0,0,0,0,0,0);
}
void nrf_log_frontend_std_1(uint32_t severity_mid,
char const * const p_str,
uint32_t val0) {
nrf_log_frontend_std_6(severity_mid, p_str,val0,0,0,0,0,0);
}
void nrf_log_frontend_std_2(uint32_t severity_mid,
char const * const p_str,
uint32_t val0,
uint32_t val1) {
nrf_log_frontend_std_6(severity_mid, p_str,val0,val1,0,0,0,0);
}
void nrf_log_frontend_std_3(uint32_t severity_mid,
char const * const p_str,
uint32_t val0,
uint32_t val1,
uint32_t val2) {
nrf_log_frontend_std_6(severity_mid, p_str,val0,val1,val2,0,0,0);
}
void nrf_log_frontend_std_4(uint32_t severity_mid,
char const * const p_str,
uint32_t val0,
uint32_t val1,
uint32_t val2,
uint32_t val3) {
nrf_log_frontend_std_6(severity_mid, p_str,val0,val1,val2,val3,0,0);
}
void nrf_log_frontend_std_5(uint32_t severity_mid,
char const * const p_str,
uint32_t val0,
uint32_t val1,
uint32_t val2,
uint32_t val3,
uint32_t val4) {
nrf_log_frontend_std_6(severity_mid, p_str,val0,val1,val2,val3,val4,0);
}
void nrf_log_frontend_std_6(uint32_t severity_mid,
char const * const p_str,
uint32_t val0,
uint32_t val1,
uint32_t val2,
uint32_t val3,
uint32_t val4,
uint32_t val5) {
#ifdef DEFAULT_CONSOLE_DEVICE
jshTransmitPrintf(DEFAULT_CONSOLE_DEVICE, p_str, val0, val1, val2, val3, val4, val5);
jshTransmit(DEFAULT_CONSOLE_DEVICE, '\r');
jshTransmit(DEFAULT_CONSOLE_DEVICE, '\n');
#endif
}
void nrf_log_frontend_hexdump(uint32_t severity_mid,
const void * const p_data,
uint16_t length) {
uint8_t *u8_data = (uint8_t *)p_data;
unsigned int i;
for (i=0;i<length;i++) {
jshTransmitPrintf(DEFAULT_CONSOLE_DEVICE, "%02x ", u8_data[i]);
if ((i&7) == 7) {
jshTransmit(DEFAULT_CONSOLE_DEVICE, '\r');
jshTransmit(DEFAULT_CONSOLE_DEVICE, '\n');
}
}
jshTransmit(DEFAULT_CONSOLE_DEVICE, '\r');
jshTransmit(DEFAULT_CONSOLE_DEVICE, '\n');
}
#ifdef NRF5X_SDK_15_3
const nrf_log_module_const_data_t m_nrf_log_app_logs_data_const = {
.p_module_name = ""
};
#else
nrf_log_module_dynamic_data_t NRF_LOG_MODULE_DATA_DYNAMIC = {
.module_id = 0
};
#endif
#endif
/// Function for handling an event from the Connection Parameters Module.
static void on_conn_params_evt(ble_conn_params_evt_t * p_evt) {
// Either BLE_CONN_PARAMS_EVT_FAILED or BLE_CONN_PARAMS_EVT_SUCCEEDED - that's it
}
/// Sigh - NFC has lots of these, so we need to define it to build
void log_uart_printf(const char * format_msg, ...) {
// jsiConsolePrintf("NFC: %s\n", format_msg);
}
// -----------------------------------------------------------------------------------
// -------------------------------------------------------------------------- HANDLERS
#if NRF_SD_BLE_API_VERSION<5
static void nus_data_handler(ble_nus_t * p_nus, uint8_t * p_data, uint16_t length) {
jsble_peripheral_activity(); // flag that we've been busy
jshPushIOCharEvents(EV_BLUETOOTH, (char*)p_data, length);
jshHadEvent();
}
#else
static void nus_data_handler(ble_nus_evt_t * p_evt) {
if (p_evt->type == BLE_NUS_EVT_RX_DATA) {
jsble_peripheral_activity(); // flag that we've been busy
jshPushIOCharEvents(EV_BLUETOOTH, (char*)p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);
jshHadEvent();
}
}
#endif
void nus_transmit_string() {
if (!jsble_has_peripheral_connection() ||
!(bleStatus & BLE_NUS_INITED) ||
(bleStatus & BLE_IS_SLEEPING)) {
// If no connection, drain the output buffer
nuxTxBufLength = 0;
jshTransmitClearDevice(EV_BLUETOOTH);
return;
}
/* 6 is the max number of packets we can send
* in one connection interval on nRF52. We could
* do 5, but it seems some things have issues with
* this (eg nRF cloud gateways) so only send 1 packet
* for now. */
int max_data_len = MIN((m_peripheral_effective_mtu-3),BLE_NUS_MAX_DATA_LEN);
for (int packet=0;packet<1;packet++) {
// No data? try and get some from our queue
if (!nuxTxBufLength) {
nuxTxBufLength = 0;
int ch = jshGetCharToTransmit(EV_BLUETOOTH);
while (ch>=0) {
nusTxBuf[nuxTxBufLength++] = ch;
if (nuxTxBufLength>=max_data_len) break;
ch = jshGetCharToTransmit(EV_BLUETOOTH);
}
}
// If there's no data in the queue, nothing to do - leave
if (!nuxTxBufLength) return;
jsble_peripheral_activity(); // flag that we've been busy
// We have data - try and send it
#if NRF_SD_BLE_API_VERSION>5
uint32_t err_code = ble_nus_data_send(&m_nus, nusTxBuf, &nuxTxBufLength, m_peripheral_conn_handle);
#elif NRF_SD_BLE_API_VERSION<5
uint32_t err_code = ble_nus_string_send(&m_nus, nusTxBuf, nuxTxBufLength);
#else // NRF_SD_BLE_API_VERSION==5
uint32_t err_code = ble_nus_string_send(&m_nus, nusTxBuf, &nuxTxBufLength);
#endif
if (err_code == NRF_SUCCESS) {
nuxTxBufLength=0; // everything sent Ok
bleStatus |= BLE_IS_SENDING;
} else if (err_code==NRF_ERROR_INVALID_STATE) {
// If no notifications we are connected but the central isn't reading, so sends will fail.
// Ideally we check m_nus.is_notification_enabled but SDK15 changed this, so lets just see if
// the send creates a NRF_ERROR_INVALID_STATE error
nuxTxBufLength = 0; // clear tx buffer
jshTransmitClearDevice(EV_BLUETOOTH); // clear all tx data in queue
}
/* if it failed to send all or any data we keep it around in
* nusTxBuf (with count in nuxTxBufLength) so next time around
* we can try again. */
}
}
/// Radio Notification handler
void SWI1_IRQHandler(bool radio_evt) {
if (bleStatus & BLE_NUS_INITED)
nus_transmit_string();
// If we're doing multiple advertising, iterate through advertising options
if ((bleStatus & BLE_IS_ADVERTISING) && (bleStatus & BLE_IS_ADVERTISING_MULTIPLE)) {