Преглед изворни кода

more work on ble scan. still not finding target device name, even with gap inquiry.

Thomas Buck пре 1 година
родитељ
комит
8e13f7b0d8
6 измењених фајлова са 324 додато и 12 уклоњено
  1. 4
    0
      CMakeLists.txt
  2. 28
    1
      include/ble.h
  3. 1
    1
      include/util.h
  4. 274
    9
      src/ble.c
  5. 16
    0
      src/console.c
  6. 1
    1
      src/util.c

+ 4
- 0
CMakeLists.txt Прегледај датотеку

@@ -94,6 +94,9 @@ target_compile_options(gadget PUBLIC
94 94
 set_source_files_properties(pico-sdk/lib/btstack/src/ble/sm.c PROPERTIES COMPILE_FLAGS -Wno-unused-parameter)
95 95
 set_source_files_properties(pico-sdk/lib/btstack/src/btstack_hid_parser.c PROPERTIES COMPILE_FLAGS -Wno-maybe-uninitialized)
96 96
 set_source_files_properties(pico-sdk/src/rp2_common/pico_cyw43_driver/cyw43_driver.c PROPERTIES COMPILE_FLAGS -Wno-unused-parameter)
97
+set_source_files_properties(pico-sdk/lib/btstack/src/classic/avdtp_util.c PROPERTIES COMPILE_FLAGS -Wno-unused-parameter)
98
+set_source_files_properties(pico-sdk/lib/btstack/src/classic/goep_client.c PROPERTIES COMPILE_FLAGS -Wno-unused-parameter)
99
+set_source_files_properties(pico-sdk/lib/btstack/src/classic/goep_server.c PROPERTIES COMPILE_FLAGS -Wno-unused-parameter)
97 100
 
98 101
 # pull in common dependencies
99 102
 target_link_libraries(gadget
@@ -106,6 +109,7 @@ target_link_libraries(gadget
106 109
     hardware_gpio
107 110
     hardware_pwm
108 111
     pico_btstack_ble
112
+    pico_btstack_classic
109 113
     pico_btstack_cyw43
110 114
     pico_cyw43_arch_threadsafe_background
111 115
 )

+ 28
- 1
include/ble.h Прегледај датотеку

@@ -19,13 +19,40 @@
19 19
 #ifndef __BLE_H__
20 20
 #define __BLE_H__
21 21
 
22
-void ble_init(void);
22
+#include "btstack.h"
23
+
24
+#define BLE_MAX_NAME_LENGTH 32
25
+#define BLE_MAX_SCAN_RESULTS 32
23 26
 
24 27
 enum ble_scan_mode {
25 28
     BLE_SCAN_OFF    = 0,
26 29
     BLE_SCAN_ON     = 1,
27 30
     BLE_SCAN_TOGGLE = 2,
28 31
 };
32
+
33
+enum ble_remote_name_state {
34
+    BLE_NAME_REQUEST = 0,
35
+    BLE_NAME_INQUIRED,
36
+    BLE_NAME_FETCHED,
37
+};
38
+
39
+struct ble_scan_result {
40
+    bool set;
41
+    uint32_t time;
42
+    enum ble_remote_name_state state;
43
+
44
+    bd_addr_t addr;
45
+    bd_addr_type_t type;
46
+    int8_t rssi;
47
+    uint8_t page_scan_repetition_mode;
48
+    uint16_t clock_offset;
49
+    uint32_t class;
50
+
51
+    char name[BLE_MAX_NAME_LENGTH + 1];
52
+};
53
+
54
+void ble_init(void);
29 55
 void ble_scan(enum ble_scan_mode mode);
56
+int ble_get_scan_results(struct ble_scan_result *buf, uint len);
30 57
 
31 58
 #endif // __BLE_H__

+ 1
- 1
include/util.h Прегледај датотеку

@@ -29,7 +29,7 @@ bool str_startswith(const char *str, const char *start);
29 29
 void reset_to_bootloader(void);
30 30
 void reset_to_main(void);
31 31
 
32
-void hexdump(uint8_t *buff, size_t len);
32
+void hexdump(const uint8_t *buff, size_t len);
33 33
 
34 34
 float map(float value, float leftMin, float leftMax, float rightMin, float rightMax);
35 35
 

+ 274
- 9
src/ble.c Прегледај датотеку

@@ -2,6 +2,8 @@
2 2
  * ble.c
3 3
  *
4 4
  * https://github.com/raspberrypi/pico-examples/blob/master/pico_w/bt/standalone/client.c
5
+ * https://vanhunteradams.com/Pico/BLE/BTStack_HCI.html
6
+ * https://github.com/bluekitchen/btstack/blob/master/example/gap_inquiry.c
5 7
  *
6 8
  * Copyright (c) 2023 Thomas Buck (thomas@xythobuz.de)
7 9
  *
@@ -18,13 +20,15 @@
18 20
  * See <http://www.gnu.org/licenses/>.
19 21
  */
20 22
 
21
-#include "btstack.h"
22 23
 #include "pico/cyw43_arch.h"
23 24
 
24 25
 #include "config.h"
25 26
 #include "log.h"
27
+#include "util.h"
26 28
 #include "ble.h"
27 29
 
30
+#define GAP_INQUIRY_INTERVAL 5 // *1.28s
31
+
28 32
 enum ble_state {
29 33
     TC_OFF = 0,
30 34
     TC_IDLE,
@@ -38,9 +42,105 @@ enum ble_state {
38 42
 
39 43
 static btstack_packet_callback_registration_t hci_event_callback_registration;
40 44
 static enum ble_state state = TC_OFF;
45
+static struct ble_scan_result scans[BLE_MAX_SCAN_RESULTS] = {0};
46
+
47
+// TODO scan result entries are not aging out
41 48
 
42
-static bd_addr_t server_addr;
43
-static bd_addr_type_t server_addr_type;
49
+static void hci_add_scan_result(bd_addr_t addr, bd_addr_type_t type,
50
+                                int8_t rssi, uint8_t scan_mode,
51
+                                uint16_t clock_offset, uint32_t class) {
52
+    int unused = -1;
53
+
54
+    for (uint i = 0; i < BLE_MAX_SCAN_RESULTS; i++) {
55
+        if (!scans[i].set) {
56
+            if (unused < 0) {
57
+                unused = i;
58
+            }
59
+            continue;
60
+        }
61
+
62
+        if (memcmp(addr, scans[i].addr, sizeof(bd_addr_t)) == 0) {
63
+            // already in list, just update changed values
64
+            scans[i].time = to_ms_since_boot(get_absolute_time());
65
+            if (scans[i].type == 0) {
66
+                scans[i].type = type;
67
+            }
68
+            if (scans[i].rssi == 0) {
69
+                scans[i].rssi = rssi;
70
+            }
71
+            if (scans[i].page_scan_repetition_mode == 0) {
72
+                scans[i].page_scan_repetition_mode = scan_mode;
73
+            }
74
+            if (scans[i].clock_offset == 0) {
75
+                scans[i].clock_offset = clock_offset;
76
+            }
77
+            if (scans[i].class == 0) {
78
+                scans[i].class = class;
79
+            }
80
+            return;
81
+        }
82
+    }
83
+
84
+    if (unused < 0) {
85
+        debug("no space in scan results for %s", bd_addr_to_str(addr));
86
+        return;
87
+    }
88
+
89
+    debug("new device with addr %s", bd_addr_to_str(addr));
90
+    scans[unused].set = true;
91
+    scans[unused].time = to_ms_since_boot(get_absolute_time());
92
+    scans[unused].state = BLE_NAME_REQUEST;
93
+    memcpy(scans[unused].addr, addr, sizeof(bd_addr_t));
94
+    scans[unused].type = type;
95
+    scans[unused].rssi = rssi;
96
+    scans[unused].page_scan_repetition_mode = scan_mode;
97
+    scans[unused].clock_offset = clock_offset;
98
+    scans[unused].class = class;
99
+    scans[unused].name[0] = '\0';
100
+}
101
+
102
+static void hci_scan_result_add_name(bd_addr_t addr, const uint8_t *data, uint8_t data_size) {
103
+    for (uint i = 0; i < BLE_MAX_SCAN_RESULTS; i++) {
104
+        if (!scans[i].set) {
105
+            continue;
106
+        }
107
+        if (memcmp(addr, scans[i].addr, sizeof(bd_addr_t)) != 0) {
108
+            continue;
109
+        }
110
+
111
+        uint8_t len = data_size;
112
+        if (len > BLE_MAX_NAME_LENGTH) {
113
+            len = BLE_MAX_NAME_LENGTH;
114
+        }
115
+        memcpy(scans[i].name, data, len);
116
+        scans[i].name[len] = '\0';
117
+        scans[i].time = to_ms_since_boot(get_absolute_time());
118
+        return;
119
+    }
120
+
121
+    debug("no matching entry for %s to add name '%.*s' to", bd_addr_to_str(addr), data_size, data);
122
+}
123
+
124
+static void hci_continue_name_requests(void) {
125
+    for (uint i = 0; i < BLE_MAX_SCAN_RESULTS; i++) {
126
+        if (!scans[i].set) {
127
+            continue;
128
+        }
129
+
130
+        if (scans[i].state == BLE_NAME_REQUEST) {
131
+            scans[i].state = BLE_NAME_INQUIRED;
132
+            debug("Inquire remote name of %s", bd_addr_to_str(scans[i].addr));
133
+            gap_remote_name_request(scans[i].addr,
134
+                                    scans[i].page_scan_repetition_mode,
135
+                                    scans[i].clock_offset | 0x8000);
136
+            return;
137
+        }
138
+    }
139
+
140
+    if (state == TC_W4_SCAN_RESULT) {
141
+        gap_inquiry_start(GAP_INQUIRY_INTERVAL);
142
+    }
143
+}
44 144
 
45 145
 static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
46 146
     UNUSED(size);
@@ -64,24 +164,147 @@ static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *pa
64 164
             }
65 165
         break;
66 166
 
167
+    case HCI_EVENT_INQUIRY_COMPLETE:
168
+    case HCI_EVENT_COMMAND_STATUS:
169
+    case HCI_EVENT_REMOTE_HOST_SUPPORTED_FEATURES:
170
+    case BTSTACK_EVENT_SCAN_MODE_CHANGED:
67 171
     case HCI_EVENT_COMMAND_COMPLETE:
68 172
     case HCI_EVENT_TRANSPORT_PACKET_SENT:
69 173
         break;
70 174
 
71
-    case GAP_EVENT_ADVERTISING_REPORT:
175
+    case GAP_EVENT_ADVERTISING_REPORT: {
72 176
         if (state != TC_W4_SCAN_RESULT) {
73 177
             debug("scan result in invalid state %d", state);
74 178
             return;
75 179
         }
76 180
 
77
-        gap_event_advertising_report_get_address(packet, server_addr);
78
-        server_addr_type = gap_event_advertising_report_get_address_type(packet);
79
-        debug("Found device with addr %s", bd_addr_to_str(server_addr));
181
+        bd_addr_t addr;
182
+        gap_event_advertising_report_get_address(packet, addr);
183
+
184
+        bd_addr_type_t type;
185
+        type = gap_event_advertising_report_get_address_type(packet);
186
+
187
+        int8_t rssi;
188
+        rssi = (int8_t)gap_event_advertising_report_get_rssi(packet);
189
+
190
+        // add data received so far
191
+        hci_add_scan_result(addr, type, rssi, 0, 0, 0);
192
+
193
+        // get advertisement from report event
194
+        const uint8_t *adv_data = gap_event_advertising_report_get_data(packet);
195
+        uint8_t adv_len = gap_event_advertising_report_get_data_length(packet);
196
+
197
+        // iterate over advertisement data
198
+        ad_context_t context;
199
+        for (ad_iterator_init(&context, adv_len, adv_data);
200
+             ad_iterator_has_more(&context);
201
+             ad_iterator_next(&context)) {
202
+            uint8_t data_type = ad_iterator_get_data_type(&context);
203
+            uint8_t data_size = ad_iterator_get_data_len(&context);
204
+            const uint8_t *data = ad_iterator_get_data(&context);
205
+            switch (data_type) {
206
+            case BLUETOOTH_DATA_TYPE_SHORTENED_LOCAL_NAME:
207
+            case BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME:
208
+                // unfortunately not the name we're interested in for our targets...
209
+                hci_scan_result_add_name(addr, data, data_size);
210
+                break;
211
+
212
+            case BLUETOOTH_DATA_TYPE_FLAGS:
213
+            case BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS:
214
+            case BLUETOOTH_DATA_TYPE_SERVICE_DATA_16_BIT_UUID:
215
+            case BLUETOOTH_DATA_TYPE_MANUFACTURER_SPECIFIC_DATA:
216
+            case BLUETOOTH_DATA_TYPE_SLAVE_CONNECTION_INTERVAL_RANGE:
217
+                break;
218
+
219
+            default:
220
+                debug("Unexpected advertisement type 0x%02X from %s", data_type, bd_addr_to_str(addr));
221
+                hexdump(data, data_size);
222
+                break;
223
+            }
224
+        }
225
+        break;
226
+    }
227
+
228
+    case GAP_EVENT_INQUIRY_RESULT: {
229
+        bd_addr_t addr;
230
+        gap_event_inquiry_result_get_bd_addr(packet, addr);
231
+
232
+        uint8_t scan_mode;
233
+        scan_mode = gap_event_inquiry_result_get_page_scan_repetition_mode(packet);
234
+
235
+        uint16_t clock_offset;
236
+        clock_offset = gap_event_inquiry_result_get_clock_offset(packet);
237
+
238
+        uint32_t class;
239
+        class = gap_event_inquiry_result_get_class_of_device(packet);
240
+
241
+        int8_t rssi = 0;
242
+        if (gap_event_inquiry_result_get_rssi_available(packet)) {
243
+            rssi = (int8_t)gap_event_inquiry_result_get_rssi(packet);
244
+        }
245
+
246
+        // add data received so far
247
+        hci_add_scan_result(addr, 0, rssi, scan_mode, clock_offset, class);
248
+
249
+        if (gap_event_inquiry_result_get_name_available(packet)) {
250
+            const uint8_t *data = gap_event_inquiry_result_get_name(packet);
251
+            uint8_t data_size = gap_event_inquiry_result_get_name_len(packet);
252
+            // still not the name we need
253
+            hci_scan_result_add_name(addr, data, data_size);
254
+        } else {
255
+            for (uint i = 0; i < BLE_MAX_SCAN_RESULTS; i++) {
256
+                if (!scans[i].set) {
257
+                    continue;
258
+                }
259
+                if (memcmp(addr, scans[i].addr, sizeof(bd_addr_t)) == 0) {
260
+                    scans[i].state = BLE_NAME_REQUEST;
261
+                    break;
262
+                }
263
+            }
264
+        }
265
+        break;
266
+    }
267
+
268
+    case HCI_EVENT_EXTENDED_INQUIRY_RESPONSE:
269
+        // TODO ?
270
+        break;
271
+
272
+    case GAP_EVENT_INQUIRY_COMPLETE:
273
+        // trigger re-read of all names
274
+        for (uint i = 0; i < BLE_MAX_SCAN_RESULTS; i++) {
275
+            if (scans[i].state == BLE_NAME_INQUIRED) {
276
+                scans[i].state = BLE_NAME_REQUEST;
277
+            }
278
+        }
279
+        hci_continue_name_requests();
280
+        break;
281
+
282
+    case HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE: {
283
+        bd_addr_t addr;
284
+        reverse_bd_addr(&packet[3], addr);
285
+        if (packet[2] != 0) {
286
+            debug("page timeout receiving name from %s", bd_addr_to_str(addr));
287
+        } else {
288
+            for (uint i = 0; i < BLE_MAX_SCAN_RESULTS; i++) {
289
+                if (!scans[i].set) {
290
+                    continue;
291
+                }
292
+                if (memcmp(addr, scans[i].addr, sizeof(bd_addr_t)) == 0) {
293
+                    scans[i].state = BLE_NAME_FETCHED;
294
+                    // also not the name we are looking for
295
+                    hci_scan_result_add_name(addr, &packet[9], strlen((char *)&packet[9]));
296
+                    break;
297
+                }
298
+            }
299
+        }
300
+        hci_continue_name_requests();
80 301
         break;
302
+    }
81 303
 
82 304
     case HCI_EVENT_LE_META:
83 305
         switch (hci_event_le_meta_get_subevent_code(packet)) {
84 306
             case HCI_SUBEVENT_LE_ADVERTISING_REPORT:
307
+                // handled internally by BTstack
85 308
                 break;
86 309
 
87 310
             case HCI_SUBEVENT_LE_CONNECTION_COMPLETE:
@@ -101,12 +324,18 @@ static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *pa
101 324
 }
102 325
 
103 326
 void ble_init(void) {
327
+    for (uint i = 0; i < BLE_MAX_SCAN_RESULTS; i++) {
328
+        scans[i].set = false;
329
+    }
330
+
104 331
     l2cap_init();
105 332
     sm_init();
106 333
     sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
107 334
 
108 335
     gatt_client_init();
109 336
 
337
+    hci_set_inquiry_mode(INQUIRY_MODE_RSSI_AND_EIR);
338
+
110 339
     hci_event_callback_registration.callback = &hci_event_handler;
111 340
     hci_add_event_handler(&hci_event_callback_registration);
112 341
 
@@ -114,29 +343,38 @@ void ble_init(void) {
114 343
 }
115 344
 
116 345
 void ble_scan(enum ble_scan_mode mode) {
346
+    cyw43_thread_enter();
347
+
117 348
     switch (mode) {
118 349
     case BLE_SCAN_OFF:
119 350
         debug("stopping BLE scan");
120 351
         state = TC_IDLE;
121 352
         gap_stop_scan();
353
+        gap_inquiry_stop();
122 354
         break;
123 355
 
124 356
     case BLE_SCAN_ON:
125 357
         debug("starting BLE scan");
126 358
         state = TC_W4_SCAN_RESULT;
359
+
127 360
         gap_set_scan_parameters(0,0x0030, 0x0030);
128 361
         gap_start_scan();
362
+
363
+        // also start an inquiry scan
364
+        gap_inquiry_start(GAP_INQUIRY_INTERVAL);
129 365
         break;
130 366
 
131 367
     case BLE_SCAN_TOGGLE:
132 368
         switch (state) {
133 369
         case TC_W4_SCAN_RESULT:
370
+            cyw43_thread_exit();
134 371
             ble_scan(0);
135
-            break;
372
+            return;
136 373
 
137 374
         case TC_IDLE:
375
+            cyw43_thread_exit();
138 376
             ble_scan(1);
139
-            break;
377
+            return;
140 378
 
141 379
         default:
142 380
             debug("invalid state %d", state);
@@ -148,4 +386,31 @@ void ble_scan(enum ble_scan_mode mode) {
148 386
         debug("invalid mode %d", mode);
149 387
         break;
150 388
     }
389
+
390
+    cyw43_thread_exit();
391
+}
392
+
393
+int ble_get_scan_results(struct ble_scan_result *buf, uint len) {
394
+    if (!buf || (len <= 0)) {
395
+        return -1;
396
+    }
397
+
398
+    cyw43_thread_enter();
399
+
400
+    uint pos = 0;
401
+    for (uint i = 0; i < BLE_MAX_SCAN_RESULTS; i++) {
402
+        if (!scans[i].set) {
403
+            continue;
404
+        }
405
+
406
+        memcpy(buf + pos, scans + i, sizeof(struct ble_scan_result));
407
+        pos++;
408
+
409
+        if (pos >= len) {
410
+            break;
411
+        }
412
+    }
413
+
414
+    cyw43_thread_exit();
415
+    return pos;
151 416
 }

+ 16
- 0
src/console.c Прегледај датотеку

@@ -78,6 +78,7 @@ static void cnsl_interpret(const char *line) {
78 78
         println("  mount - make mass storage medium (un)available");
79 79
         println("  power - show Lipo battery status");
80 80
         println("   scan - start or stop BLE scan");
81
+        println("scanres - print list of found BLE devices");
81 82
         println("  clear - blank screen");
82 83
         println(" splash - draw image on screen");
83 84
         println("  fonts - show font list");
@@ -101,6 +102,21 @@ static void cnsl_interpret(const char *line) {
101 102
                 lipo_charging() ? "charging" : "draining");
102 103
     } else if (strcmp(line, "scan") == 0) {
103 104
         ble_scan(2);
105
+    } else if (strcmp(line, "scanres") == 0) {
106
+        struct ble_scan_result results[BLE_MAX_SCAN_RESULTS] = {0};
107
+        int n = ble_get_scan_results(results, BLE_MAX_SCAN_RESULTS);
108
+        if (n < 0) {
109
+            println("Error reading results (%d)", n);
110
+        } else {
111
+            println("%d results", n);
112
+            for (int i = 0; i < n; i++) {
113
+                uint32_t age = to_ms_since_boot(get_absolute_time()) - results[i].time;
114
+                println("addr=%s type=%d rssi=%d age=%.1fs name='%s'",
115
+                        bd_addr_to_str(results[i].addr),
116
+                        results[i].type, results[i].rssi,
117
+                        age / 1000.0, results[i].name);
118
+            }
119
+        }
104 120
     } else if (strcmp(line, "clear") == 0) {
105 121
         lcd_clear();
106 122
     } else if (strcmp(line, "splash") == 0) {

+ 1
- 1
src/util.c Прегледај датотеку

@@ -89,7 +89,7 @@ void reset_to_main(void) {
89 89
     }
90 90
 }
91 91
 
92
-void hexdump(uint8_t *buff, size_t len) {
92
+void hexdump(const uint8_t *buff, size_t len) {
93 93
     for (size_t i = 0; i < len; i += 16) {
94 94
         for (size_t j = 0; (j < 16) && ((i + j) < len); j++) {
95 95
             print("0x%02X", buff[i + j]);

Loading…
Откажи
Сачувај