Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
dbt_labpadclient01.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2020-2023 Gothel Software e.K.
4 * Copyright (c) 2020-2023 ZAFENA AB
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26#include <cstring>
27#include <string>
28#include <memory>
29#include <cstdint>
30#include <fstream>
31#include <iostream>
32
33#include <cinttypes>
34
35#include <pthread.h>
36#include <csignal>
37
38#include <jau/cpp_lang_util.hpp>
40#include <jau/basic_algos.hpp>
41#include <jau/darray.hpp>
42
44
45extern "C" {
46 #include <unistd.h>
47}
48
49#include "dbt_constants.hpp"
50
51using namespace direct_bt;
52using namespace jau;
53using namespace jau::fractions_i64_literals;
54
55/** \file
56 * The example demonstrates a client connecting to `Avalun's LabPad device`.
57 *
58 * It differs from _dbt_scanner10_ as follows:
59 *
60 * * Employs a minimal GattServer supplying `Generic Access` service
61 *
62 * * Performing one simple Gatt write and indication listener test
63 *
64 * * Uses pre-set `-dev LabPad` device name and SMPIOCapability::KEYBOARD_ONLY + BTSecurityLevel::ENC_AUTH
65 *
66 * * Commandline `-passkey <int>` uses `LabPad` implicitly, i.e. user only needs to pass the integer w/o device name.
67 *
68 * Other than that, please refer to _dbt_scanner10_ as a general example.
69 */
70
71static uint64_t timestamp_t0;
72
73static EUI48 useAdapter = EUI48::ALL_DEVICE;
74static BTMode btMode = BTMode::DUAL;
75static bool use_privacy = false;
76
77static DiscoveryPolicy discoveryPolicy = DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_READY; // default value
78static bool le_scan_active = true; // default value
79static const uint16_t le_scan_interval = 24; // default value
80static const uint16_t le_scan_window = 24; // default value
81static const uint8_t filter_policy = 0; // default value
82static const bool filter_dup = true; // default value
83
84static std::shared_ptr<BTAdapter> chosenAdapter = nullptr;
85
87static std::atomic<int> deviceReadyCount = 0;
88
89static std::atomic<int> MULTI_MEASUREMENTS = 8;
90
91static bool KEEP_CONNECTED = true;
92static bool GATT_PING_ENABLED = false;
93static bool REMOVE_DEVICE = true;
94
95// Avalun's LabPad Comman + Event UUID
96static std::unique_ptr<uuid_t> cmd_req_uuid = jau::uuid_t::create(std::string("2c1b2472-4a5f-11e5-9595-0002a5d5c51b"));
97static std::unique_ptr<uuid_t> cmd_rsp_uuid = jau::uuid_t::create(std::string("2c1b2473-4a5f-11e5-9595-0002a5d5c51b"));
98
99static const POctets cmd_data1( { 0x00 /* cmd-idx-0 */,
100 0x14, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
101 0x01, 0x5E, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
102 0x9B, 0x23, 0x84 }, lb_endian::little);
103
104static const POctets cmd_data2( { 0x01 /* cmd-idx-1 */,
105 0xB8 }, lb_endian::little);
106
107static const POctets resp_exp( { 0x00 /* rsp-idx-0 */,
108 0x14, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00,
109 0x01, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110 0xf6, 0x64, 0x17,
111 0x01 /* rsp-idx-1 */, 0xed }, lb_endian::little );
112
113static bool SHOW_UPDATE_EVENTS = false;
114static bool QUIET = false;
115
116static void connectDiscoveredDevice(BTDeviceRef device);
117
118static void processReadyDevice(const BTDeviceRef& device);
119
120static void removeDevice(BTDeviceRef device);
121static void resetAdapter(BTAdapter *a, int mode);
122static bool startDiscovery(BTAdapter *a, const std::string& msg);
123
125 /* services: */
126 jau::make_darray( // DBGattService
127 std::make_shared<DBGattService> ( true /* primary */,
128 std::make_unique<const jau::uuid16_t>(GattServiceType::GENERIC_ACCESS) /* type_ */,
129 jau::make_darray ( // DBGattChar
130 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::DEVICE_NAME) /* value_type_ */,
132 jau::darray<DBGattDescRef>() /* intentionally empty */,
133 make_gvalue("Jausoft_Dev", 128) /* value */, true /* variable_length */ ),
134 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::APPEARANCE) /* value_type_ */,
136 jau::darray<DBGattDescRef>() /* intentionally empty */,
137 make_gvalue((uint16_t)0) /* value */ )
138 ) ),
139 std::make_shared<DBGattService> ( true /* primary */,
140 std::make_unique<const jau::uuid16_t>(GattServiceType::DEVICE_INFORMATION) /* type_ */,
141 jau::make_darray ( // DBGattChar
142 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::MANUFACTURER_NAME_STRING) /* value_type_ */,
144 jau::darray<DBGattDescRef>() /* intentionally empty */,
145 make_gvalue("Gothel Software") /* value */ ),
146 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::MODEL_NUMBER_STRING) /* value_type_ */,
148 jau::darray<DBGattDescRef>() /* intentionally empty */,
149 make_gvalue("2.4.0-pre") /* value */ ),
150 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::SERIAL_NUMBER_STRING) /* value_type_ */,
152 jau::darray<DBGattDescRef>() /* intentionally empty */,
153 make_gvalue("sn:0123456789") /* value */ ),
154 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::HARDWARE_REVISION_STRING) /* value_type_ */,
156 jau::darray<DBGattDescRef>() /* intentionally empty */,
157 make_gvalue("hw:0123456789") /* value */ ),
158 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::FIRMWARE_REVISION_STRING) /* value_type_ */,
160 jau::darray<DBGattDescRef>() /* intentionally empty */,
161 make_gvalue("fw:0123456789") /* value */ ),
162 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::SOFTWARE_REVISION_STRING) /* value_type_ */,
164 jau::darray<DBGattDescRef>() /* intentionally empty */,
165 make_gvalue("sw:0123456789") /* value */ )
166 ) )
167 ) ) );
168
170
171 void adapterSettingsChanged(BTAdapter &a, const AdapterSetting oldmask, const AdapterSetting newmask,
172 const AdapterSetting changedmask, const uint64_t timestamp) override {
173 const bool initialSetting = AdapterSetting::NONE == oldmask;
174 if( initialSetting ) {
175 fprintf_td(stderr, "****** SETTINGS_INITIAL: %s -> %s, changed %s\n", to_string(oldmask).c_str(),
176 to_string(newmask).c_str(), to_string(changedmask).c_str());
177 } else {
178 fprintf_td(stderr, "****** SETTINGS_CHANGED: %s -> %s, changed %s\n", to_string(oldmask).c_str(),
179 to_string(newmask).c_str(), to_string(changedmask).c_str());
180 }
181 fprintf_td(stderr, "Status BTAdapter:\n");
182 fprintf_td(stderr, "%s\n", a.toString().c_str());
183 (void)timestamp;
184
185 if( !initialSetting &&
186 isAdapterSettingBitSet(changedmask, AdapterSetting::POWERED) &&
187 isAdapterSettingBitSet(newmask, AdapterSetting::POWERED) )
188 {
189 std::thread sd(::startDiscovery, &a, "powered-on"); // @suppress("Invalid arguments")
190 sd.detach();
191 }
192 }
193
194 void discoveringChanged(BTAdapter &a, const ScanType currentMeta, const ScanType changedType, const bool changedEnabled, const DiscoveryPolicy policy, const uint64_t timestamp) override {
195 fprintf_td(stderr, "****** DISCOVERING: meta %s, changed[%s, enabled %d, policy %s]: %s\n",
196 to_string(currentMeta).c_str(), to_string(changedType).c_str(), changedEnabled, to_string(policy).c_str(), a.toString().c_str());
197 (void)timestamp;
198 }
199
200 bool deviceFound(const BTDeviceRef& device, const uint64_t timestamp) override {
201 (void)timestamp;
202
204 ( BTDeviceRegistry::isWaitingForDevice(device->getAddressAndType().address, device->getName()) &&
205 ( 0 < MULTI_MEASUREMENTS || !BTDeviceRegistry::isDeviceProcessed(device->getAddressAndType()) )
206 )
207 )
208 {
209 fprintf_td(stderr, "****** FOUND__-0: Connecting %s\n", device->toString(true).c_str());
210 {
211 const uint64_t td = jau::getCurrentMilliseconds() - timestamp_t0; // adapter-init -> now
212 fprintf_td(stderr, "PERF: adapter-init -> FOUND__-0 %" PRIu64 " ms\n", td);
213 }
214 std::thread dc(::connectDiscoveredDevice, device); // @suppress("Invalid arguments")
215 dc.detach();
216 return true;
217 } else {
218 if( !QUIET ) {
219 fprintf_td(stderr, "****** FOUND__-1: NOP %s\n", device->toString(true).c_str());
220 }
221 return false;
222 }
223 }
224
225 void deviceUpdated(const BTDeviceRef& device, const EIRDataType updateMask, const uint64_t timestamp) override {
226 if( !QUIET && SHOW_UPDATE_EVENTS ) {
227 fprintf_td(stderr, "****** UPDATED: %s of %s\n", to_string(updateMask).c_str(), device->toString(true).c_str());
228 }
229 (void)timestamp;
230 }
231
232 void deviceConnected(const BTDeviceRef& device, const bool discovered, const uint64_t timestamp) override {
233 fprintf_td(stderr, "****** CONNECTED (discovered %d): %s\n", discovered, device->toString(true).c_str());
234 (void)discovered;
235 (void)timestamp;
236 }
237
238 void devicePairingState(const BTDeviceRef& device, const SMPPairingState state, const PairingMode mode, const uint64_t timestamp) override {
239 fprintf_td(stderr, "****** PAIRING STATE: state %s, mode %s, %s\n",
240 to_string(state).c_str(), to_string(mode).c_str(), device->toString().c_str());
241 (void)timestamp;
242 switch( state ) {
243 case SMPPairingState::NONE:
244 // next: deviceReady(..)
245 break;
246 case SMPPairingState::FAILED: {
247 const bool res = SMPKeyBin::remove(CLIENT_KEY_PATH, *device);
248 fprintf_td(stderr, "****** PAIRING_STATE: state %s; Remove key file %s, res %d\n",
249 to_string(state).c_str(), SMPKeyBin::getFilename(CLIENT_KEY_PATH, *device).c_str(), res);
250 // next: deviceReady() or deviceDisconnected(..)
251 } break;
252 case SMPPairingState::REQUESTED_BY_RESPONDER:
253 // next: FEATURE_EXCHANGE_STARTED
254 break;
255 case SMPPairingState::FEATURE_EXCHANGE_STARTED:
256 // next: FEATURE_EXCHANGE_COMPLETED
257 break;
258 case SMPPairingState::FEATURE_EXCHANGE_COMPLETED:
259 // next: PASSKEY_EXPECTED... or KEY_DISTRIBUTION
260 break;
261 case SMPPairingState::PASSKEY_EXPECTED: {
262 const BTSecurityRegistry::Entry* sec = BTSecurityRegistry::getStartOf(device->getAddressAndType().address, device->getName());
263 if( nullptr != sec && sec->getPairingPasskey() != BTSecurityRegistry::Entry::NO_PASSKEY ) {
264 std::thread dc(&BTDevice::setPairingPasskey, device, static_cast<uint32_t>( sec->getPairingPasskey() ));
265 dc.detach();
266 } else {
267 // std::thread dc(&BTDevice::setPairingPasskey, device, 0);
268 // 3s disconnect:
269 std::thread dc(&BTDevice::setPairingPasskeyNegative, device);
270 dc.detach();
271 }
272 // next: KEY_DISTRIBUTION or FAILED
273 } break;
274 case SMPPairingState::NUMERIC_COMPARE_EXPECTED: {
275 const BTSecurityRegistry::Entry* sec = BTSecurityRegistry::getStartOf(device->getAddressAndType().address, device->getName());
276 if( nullptr != sec ) {
277 std::thread dc(&BTDevice::setPairingNumericComparison, device, sec->getPairingNumericComparison());
278 dc.detach();
279 } else {
280 std::thread dc(&BTDevice::setPairingNumericComparison, device, false);
281 dc.detach();
282 }
283 // next: KEY_DISTRIBUTION or FAILED
284 } break;
285 case SMPPairingState::OOB_EXPECTED:
286 // FIXME: ABORT
287 break;
288 case SMPPairingState::KEY_DISTRIBUTION:
289 // next: COMPLETED or FAILED
290 break;
291 case SMPPairingState::COMPLETED:
292 // next: deviceReady(..)
293 break;
294 default: // nop
295 break;
296 }
297 }
298
299 void deviceReady(const BTDeviceRef& device, const uint64_t timestamp) override {
300 (void)timestamp;
302 fprintf_td(stderr, "****** READY-0: Processing[%d] %s\n", deviceReadyCount.load(), device->toString(true).c_str());
303 processReadyDevice(device); // AdapterStatusListener::deviceReady() explicitly allows prolonged and complex code execution!
304 }
305
306 void deviceDisconnected(const BTDeviceRef& device, const HCIStatusCode reason, const uint16_t handle, const uint64_t timestamp) override {
307 fprintf_td(stderr, "****** DISCONNECTED: Reason 0x%X (%s), old handle %s: %s\n",
308 static_cast<uint8_t>(reason), to_string(reason).c_str(),
309 to_hexstring(handle).c_str(), device->toString(true).c_str());
310 (void)timestamp;
311
312 if( REMOVE_DEVICE ) {
313 std::thread dc(::removeDevice, device); // @suppress("Invalid arguments")
314 dc.detach();
315 }
317 std::thread dc(::resetAdapter, &device->getAdapter(), 1); // @suppress("Invalid arguments")
318 dc.detach();
319 }
320 }
321
322 std::string toString() const noexcept override {
323 return "MyAdapterStatusListener[this "+to_hexstring(this)+"]";
324 }
325
326};
327
329
331 private:
332 int i, j;
333
334 public:
335
336 MyGATTEventListener(int i_, int j_) : i(i_), j(j_) {}
337
338 void notificationReceived(BTGattCharRef charDecl, const TROOctets& char_value, const uint64_t timestamp) override {
339 const uint64_t tR = jau::getCurrentMilliseconds();
340 fprintf_td(stderr, "**[%2.2d.%2.2d] Characteristic-Notify: UUID %s, td %" PRIu64 " ******\n",
341 i, j, charDecl->value_type->toUUID128String().c_str(), (tR-timestamp));
342 fprintf_td(stderr, "**[%2.2d.%2.2d] Characteristic: %s ******\n", i, j, charDecl->toString().c_str());
343 if( _TEMPERATURE_MEASUREMENT == *charDecl->value_type ) {
344 std::shared_ptr<GattTemperatureMeasurement> temp = GattTemperatureMeasurement::get(char_value);
345 if( nullptr != temp ) {
346 fprintf_td(stderr, "**[%2.2d.%2.2d] Value T: %s ******\n", i, j, temp->toString().c_str());
347 }
348 fprintf_td(stderr, "**[%2.2d.%2.2d] Value R: %s ******\n", i, j, char_value.toString().c_str());
349 } else {
350 fprintf_td(stderr, "**[%2.2d.%2.2d] Value R: %s ******\n", i, j, char_value.toString().c_str());
351 fprintf_td(stderr, "**[%2.2d.%2.2d] Value S: %s ******\n", i, j, jau::dfa_utf8_decode(char_value.get_ptr(), char_value.size()).c_str());
352 }
353 }
354
356 const TROOctets& char_value, const uint64_t timestamp,
357 const bool confirmationSent) override
358 {
359 const uint64_t tR = jau::getCurrentMilliseconds();
360 fprintf_td(stderr, "**[%2.2d.%2.2d] Characteristic-Indication: UUID %s, td %" PRIu64 ", confirmed %d ******\n",
361 i, j, charDecl->value_type->toUUID128String().c_str(), (tR-timestamp), confirmationSent);
362 fprintf_td(stderr, "**[%2.2d.%2.2d] Characteristic: %s ******\n", i, j, charDecl->toString().c_str());
363 if( _TEMPERATURE_MEASUREMENT == *charDecl->value_type ) {
364 std::shared_ptr<GattTemperatureMeasurement> temp = GattTemperatureMeasurement::get(char_value);
365 if( nullptr != temp ) {
366 fprintf_td(stderr, "**[%2.2d.%2.2d] Value T: %s ******\n", i, j, temp->toString().c_str());
367 }
368 fprintf_td(stderr, "**[%2.2d.%2.2d] Value R: %s ******\n", i, j, char_value.toString().c_str());
369 } else {
370 fprintf_td(stderr, "**[%2.2d.%2.2d] Value R: %s ******\n", i, j, char_value.toString().c_str());
371 fprintf_td(stderr, "**[%2.2d.%2.2d] Value S: %s ******\n", i, j, jau::dfa_utf8_decode(char_value.get_ptr(), char_value.size()).c_str());
372 }
373 }
374};
375
376static void connectDiscoveredDevice(BTDeviceRef device) { // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
377 fprintf_td(stderr, "****** Connecting Device: Start %s\n", device->toString().c_str());
378
379 const BTSecurityRegistry::Entry* sec = BTSecurityRegistry::getStartOf(device->getAddressAndType().address, device->getName());
380 if( nullptr != sec ) {
381 fprintf_td(stderr, "****** Connecting Device: Found SecurityDetail %s for %s\n", sec->toString().c_str(), device->toString().c_str());
382 } else {
383 fprintf_td(stderr, "****** Connecting Device: No SecurityDetail for %s\n", device->toString().c_str());
384 }
385 const BTSecurityLevel req_sec_level = nullptr != sec ? sec->getSecLevel() : BTSecurityLevel::UNSET;
386 HCIStatusCode res = device->uploadKeys(CLIENT_KEY_PATH, req_sec_level, true /* verbose_ */);
387 fprintf_td(stderr, "****** Connecting Device: BTDevice::uploadKeys(...) result %s\n", to_string(res).c_str());
388 if( HCIStatusCode::SUCCESS != res ) {
389 if( nullptr != sec ) {
390 if( sec->isSecurityAutoEnabled() ) {
391 bool r = device->setConnSecurityAuto( sec->getSecurityAutoIOCap() );
392 fprintf_td(stderr, "****** Connecting Device: Using SecurityDetail.SEC AUTO %s, set OK %d\n", sec->toString().c_str(), r);
393 } else if( sec->isSecLevelOrIOCapSet() ) {
394 bool r = device->setConnSecurity( sec->getSecLevel(), sec->getIOCap() );
395 fprintf_td(stderr, "****** Connecting Device: Using SecurityDetail.Level+IOCap %s, set OK %d\n", sec->toString().c_str(), r);
396 } else {
397 bool r = device->setConnSecurityAuto( SMPIOCapability::KEYBOARD_ONLY );
398 fprintf_td(stderr, "****** Connecting Device: Setting SEC AUTO security detail w/ KEYBOARD_ONLY (%s) -> set OK %d\n", sec->toString().c_str(), r);
399 }
400 } else {
401 bool r = device->setConnSecurityAuto( SMPIOCapability::KEYBOARD_ONLY );
402 fprintf_td(stderr, "****** Connecting Device: Setting SEC AUTO security detail w/ KEYBOARD_ONLY -> set OK %d\n", r);
403 }
404 }
405 std::shared_ptr<const EInfoReport> eir = device->getEIR();
406 fprintf_td(stderr, "EIR-1 %s\n", device->getEIRInd()->toString().c_str());
407 fprintf_td(stderr, "EIR-2 %s\n", device->getEIRScanRsp()->toString().c_str());
408 fprintf_td(stderr, "EIR-+ %s\n", eir->toString().c_str());
409
410 uint16_t conn_interval_min = (uint16_t)8; // 10ms
411 uint16_t conn_interval_max = (uint16_t)12; // 15ms
412 const uint16_t conn_latency = (uint16_t)0;
413 if( eir->isSet(EIRDataType::CONN_IVAL) ) {
414 eir->getConnInterval(conn_interval_min, conn_interval_max);
415 }
416 const uint16_t supervision_timeout = (uint16_t) getHCIConnSupervisorTimeout(conn_latency, (int) ( conn_interval_max * 1.25 ) /* ms */);
417 res = device->connectLE(le_scan_interval, le_scan_window, conn_interval_min, conn_interval_max, conn_latency, supervision_timeout);
418 fprintf_td(stderr, "****** Connecting Device: End result %s of %s\n", to_string(res).c_str(), device->toString().c_str());
419}
420
421static void processReadyDevice(const BTDeviceRef& device) {
422 fprintf_td(stderr, "****** Processing Ready Device: Start %s\n", device->toString().c_str());
423
424 const uint64_t t1 = jau::getCurrentMilliseconds();
425
426 SMPKeyBin::createAndWrite(*device, CLIENT_KEY_PATH, true /* verbose */);
427
428 const uint64_t t2 = jau::getCurrentMilliseconds();
429
430 bool success = false;
431
432 if( device->getAdapter().getBTMajorVersion() > 4 ) {
433 LE_PHYs Tx { LE_PHYs::LE_2M }, Rx { LE_PHYs::LE_2M };
434 HCIStatusCode res = device->setConnectedLE_PHY(Tx, Rx);
435 fprintf_td(stderr, "****** Set Connected LE PHY: status %s: Tx %s, Rx %s\n",
436 to_string(res).c_str(), to_string(Tx).c_str(), to_string(Rx).c_str());
437 }
438 {
439 LE_PHYs resTx, resRx;
440 HCIStatusCode res = device->getConnectedLE_PHY(resTx, resRx);
441 fprintf_td(stderr, "****** Got Connected LE PHY: status %s: Tx %s, Rx %s\n",
442 to_string(res).c_str(), to_string(resTx).c_str(), to_string(resRx).c_str());
443 }
444
445 //
446 // GATT Service Processing
447 //
448 fprintf_td(stderr, "****** Processing Ready Device: GATT start: %s\n", device->getAddressAndType().toString().c_str());
449 if( !QUIET ) {
450 device->getAdapter().printDeviceLists();
451 }
452 const uint64_t t3 = jau::getCurrentMilliseconds();
453
454 try {
455 jau::darray<BTGattServiceRef> primServices = device->getGattServices();
456 if( 0 == primServices.size() ) {
457 fprintf_td(stderr, "****** Processing Ready Device: getServices() failed %s\n", device->toString().c_str());
458 goto exit;
459 }
460
461 const uint64_t t5 = jau::getCurrentMilliseconds();
462 {
463 const uint64_t td00 = device->getLastDiscoveryTimestamp() - timestamp_t0; // adapter-init to discovered
464 const uint64_t td01 = t1 - timestamp_t0; // adapter-init to processing-start
465 const uint64_t td05 = t5 - timestamp_t0; // adapter-init -> gatt-complete
466 const uint64_t tdc1 = t1 - device->getLastDiscoveryTimestamp(); // discovered to processing-start
467 const uint64_t tdc5 = t5 - device->getLastDiscoveryTimestamp(); // discovered to gatt-complete
468 const uint64_t td12 = t2 - t1; // SMPKeyBin
469 const uint64_t td23 = t3 - t2; // LE_PHY
470 const uint64_t td13 = t3 - t1; // SMPKeyBin + LE_PHY
471 const uint64_t td35 = t5 - t3; // get-gatt-services
472 fprintf_td(stderr, "\n\n\n");
473 fprintf_td(stderr, "PERF: GATT primary-services completed\n"
474 "PERF: adapter-init to discovered %" PRIu64 " ms,\n"
475 "PERF: adapter-init to processing-start %" PRIu64 " ms,\n"
476 "PERF: adapter-init to gatt-complete %" PRIu64 " ms\n"
477 "PERF: discovered to processing-start %" PRIu64 " ms,\n"
478 "PERF: discovered to gatt-complete %" PRIu64 " ms,\n"
479 "PERF: SMPKeyBin + LE_PHY %" PRIu64 " ms (SMPKeyBin %" PRIu64 " ms, LE_PHY %" PRIu64 " ms),\n"
480 "PERF: get-gatt-services %" PRIu64 " ms,\n\n",
481 td00, td01, td05,
482 tdc1, tdc5,
483 td13, td12, td23, td35);
484 }
485
486 {
487 const jau::nsize_t response_size = resp_exp.size();
488 int fail_point = 0;
489 BTGattCmd cmd = BTGattCmd(*device, "TestCmd", *cmd_req_uuid, *cmd_rsp_uuid, 256);
490 cmd.setVerbose(true);
491 HCIStatusCode cmd_res = cmd.isResolved() ? HCIStatusCode::SUCCESS : HCIStatusCode::INTERNAL_FAILURE;
492 if( HCIStatusCode::SUCCESS != cmd_res ) {
493 fail_point = 1;
494 } else {
495 cmd.setResponseMinSize(response_size);
496 cmd.setDataCallback( [](BTGattCharRef, const jau::TROOctets& char_value, const uint64_t) { // NOLINT(performance-unnecessary-value-param)
497 fprintf_td(stderr, "Received: %s\n", char_value.toString().c_str());
498 });
499 fprintf_td(stderr, "Command test: %s\n", cmd.toString().c_str());
500 cmd_res = cmd.sendOnly(true, cmd_data1);
501 }
502 if( HCIStatusCode::SUCCESS != cmd_res ) {
503 fail_point = 2;
504 } else {
505 cmd_res = cmd.send(true, cmd_data2, 3_s);
506 }
507 if( HCIStatusCode::SUCCESS != cmd_res ) {
508 fail_point = 3;
509 } else {
510 if( !cmd.hasResponseSet() ) {
511 fail_point = 4;
512 } else {
513 const jau::TROOctets& resp = cmd.getResponse();
514 if( response_size != resp.size() ) {
515 fail_point = 5;
516 fprintf_td(stderr, "Failure: %s -> %s (response size)\n", cmd.toString().c_str(), resp.toString().c_str());
517 } else if( resp_exp != resp ) {
518 fail_point = 6;
519 fprintf_td(stderr, "Failure: %s (response content)\n", cmd.toString().c_str());
520 fprintf_td(stderr, "- exp %s\n", resp_exp.toString().c_str());
521 fprintf_td(stderr, "- has %s\n", resp.toString().c_str());
522 } else {
523 fprintf_td(stderr, "Success: %s -> %s\n", cmd.toString().c_str(), resp.toString().c_str());
524 }
525 }
526 }
527 if( 0 != fail_point ) {
528 fprintf_td(stderr, "Failure: point %u: %s -> %s\n", fail_point, cmd.toString().c_str(), to_string(cmd_res).c_str());
529 }
530 }
531
532 std::shared_ptr<GattGenericAccessSvc> ga = device->getGattGenericAccess();
533 if( nullptr != ga && !QUIET ) {
534 fprintf_td(stderr, " GenericAccess: %s\n\n", ga->toString().c_str());
535 }
536 {
537 std::shared_ptr<BTGattHandler> gatt = device->getGattHandler();
538 if( nullptr != gatt && gatt->isConnected() ) {
539 std::shared_ptr<GattDeviceInformationSvc> di = gatt->getDeviceInformation(primServices);
540 if( nullptr != di && !QUIET ) {
541 fprintf_td(stderr, " DeviceInformation: %s\n\n", di->toString().c_str());
542 }
543 }
544 }
545
546
547 for(size_t i=0; i<primServices.size(); i++) {
548 BTGattService & primService = *primServices.at(i);
549 {
550 fprintf_td(stderr, " [%2.2d] Service UUID %s (%s)\n", i,
551 primService.type->toUUID128String().c_str(),
552 primService.type->getTypeSizeString().c_str());
553 fprintf_td(stderr, " [%2.2d] %s\n", i, primService.toString().c_str());
554 }
555 jau::darray<BTGattCharRef> & serviceCharacteristics = primService.characteristicList;
556 for(size_t j=0; j<serviceCharacteristics.size(); j++) {
557 BTGattCharRef & serviceChar = serviceCharacteristics.at(j);
558 {
559 fprintf_td(stderr, " [%2.2d.%2.2d] Characteristic: UUID %s (%s)\n", i, j,
560 serviceChar->value_type->toUUID128String().c_str(),
561 serviceChar->value_type->getTypeSizeString().c_str());
562 fprintf_td(stderr, " [%2.2d.%2.2d] %s\n", i, j, serviceChar->toString().c_str());
563 }
564 if( serviceChar->hasProperties(BTGattChar::PropertyBitVal::Read) ) {
565 POctets value(BTGattHandler::number(BTGattHandler::Defaults::MAX_ATT_MTU), 0, jau::lb_endian::little);
566 if( serviceChar->readValue(value) ) {
567 std::string sval = dfa_utf8_decode(value.get_ptr(), value.size());
568 {
569 fprintf_td(stderr, " [%2.2d.%2.2d] value: %s ('%s')\n", (int)i, (int)j, value.toString().c_str(), sval.c_str());
570 }
571 }
572 }
573 jau::darray<BTGattDescRef> & charDescList = serviceChar->descriptorList;
574 for(size_t k=0; k<charDescList.size(); k++) {
575 BTGattDesc & charDesc = *charDescList.at(k);
576 {
577 fprintf_td(stderr, " [%2.2d.%2.2d.%2.2d] Descriptor: UUID %s (%s)\n", i, j, k,
578 charDesc.type->toUUID128String().c_str(),
579 charDesc.type->getTypeSizeString().c_str());
580 fprintf_td(stderr, " [%2.2d.%2.2d.%2.2d] %s\n", i, j, k, charDesc.toString().c_str());
581 }
582 }
583 bool cccdEnableResult[2];
584 if( serviceChar->enableNotificationOrIndication( cccdEnableResult ) ) {
585 // ClientCharConfigDescriptor (CCD) is available
586 bool clAdded = serviceChar->addCharListener( std::make_shared<MyGATTEventListener>(i, j) );
587 {
588 fprintf_td(stderr, " [%2.2d.%2.2d] Characteristic-Listener: Notification(%d), Indication(%d): Added %d\n",
589 (int)i, (int)j, cccdEnableResult[0], cccdEnableResult[1], clAdded);
590 fprintf_td(stderr, "\n");
591 }
592 }
593 }
594 fprintf_td(stderr, "\n");
595 }
596 // FIXME sleep 1s for potential callbacks ..
597 jau::sleep_for( 1_s );
598 success = true;
599 } catch ( std::exception & e ) {
600 fprintf_td(stderr, "****** Processing Ready Device: Exception caught for %s: %s\n", device->toString().c_str(), e.what());
601 }
602
603exit:
604 fprintf_td(stderr, "****** Processing Ready Device: End-1: Success %d on %s\n", success, device->toString().c_str());
605
606 if( DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_DISCONNECTED == discoveryPolicy ) {
607 device->getAdapter().removeDevicePausingDiscovery(*device);
608 }
609
610 if( KEEP_CONNECTED && GATT_PING_ENABLED && success ) {
611 while( device->pingGATT() ) {
612 fprintf_td(stderr, "****** Processing Ready Device: pingGATT OK: %s\n", device->getAddressAndType().toString().c_str());
613 jau::sleep_for( 1_s );
614 }
615 fprintf_td(stderr, "****** Processing Ready Device: pingGATT failed, waiting for disconnect: %s\n", device->getAddressAndType().toString().c_str());
616 // Even w/ GATT_PING_ENABLED, we utilize disconnect event to clean up -> remove
617 }
618
619 if( !QUIET ) {
620 device->getAdapter().printDeviceLists();
621 }
622
623 fprintf_td(stderr, "****** Processing Ready Device: End-2: Success %d on %s\n", success, device->toString().c_str());
624
625 if( success ) {
626 BTDeviceRegistry::addToProcessedDevices(device->getAddressAndType(), device->getName());
627 }
628
629 if( !KEEP_CONNECTED ) {
630 device->removeAllCharListener();
631
632 device->remove();
633
635 resetAdapter(&device->getAdapter(), 2);
636 }
637 }
638
639 if( 0 < MULTI_MEASUREMENTS ) {
641 fprintf_td(stderr, "****** Processing Ready Device: MULTI_MEASUREMENTS left %d: %s\n", MULTI_MEASUREMENTS.load(), device->getAddressAndType().toString().c_str());
642 }
643}
644
645static void removeDevice(BTDeviceRef device) { // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
646 fprintf_td(stderr, "****** Remove Device: removing: %s\n", device->getAddressAndType().toString().c_str());
647
648 device->remove();
649}
650
651static void resetAdapter(BTAdapter *a, int mode) {
652 fprintf_td(stderr, "****** Reset Adapter: reset[%d] start: %s\n", mode, a->toString().c_str());
653 HCIStatusCode res = a->reset();
654 fprintf_td(stderr, "****** Reset Adapter: reset[%d] end: %s, %s\n", mode, to_string(res).c_str(), a->toString().c_str());
655}
656
657static bool startDiscovery(BTAdapter *a, const std::string& msg) {
658 if( useAdapter != EUI48::ALL_DEVICE && useAdapter != a->getAddressAndType().address ) {
659 fprintf_td(stderr, "****** Start discovery (%s): Adapter not selected: %s\n", msg.c_str(), a->toString().c_str());
660 return false;
661 }
662
663 if( false ) {
666 if( nullptr != gattDevNameChar ) {
667 std::string aname = a->getName();
668 gattDevNameChar->setValue(reinterpret_cast<uint8_t*>(aname.data()), aname.size(), 0);
669 }
670 }
671
673 fprintf_td(stderr, "****** Start discovery (%s) result: %s: %s\n", msg.c_str(), to_string(status).c_str(), a->toString().c_str());
674 fprintf_td(stderr, "%s", dbGattServer->toFullString().c_str());
675 return HCIStatusCode::SUCCESS == status;
676}
677
678static bool initAdapter(std::shared_ptr<BTAdapter>& adapter) {
679 if( useAdapter != EUI48::ALL_DEVICE && useAdapter != adapter->getAddressAndType().address ) {
680 fprintf_td(stderr, "initAdapter: Adapter not selected: %s\n", adapter->toString().c_str());
681 return false;
682 }
683 // Initialize with defaults and power-on
684 if( !adapter->isInitialized() ) {
685 HCIStatusCode status = adapter->initialize( btMode, false );
686 if( HCIStatusCode::SUCCESS != status ) {
687 fprintf_td(stderr, "initAdapter: Adapter initialization failed: %s: %s\n",
688 to_string(status).c_str(), adapter->toString().c_str());
689 return false;
690 }
691 adapter->setPrivacy(use_privacy);
692 }
693 if( !adapter->setPowered( true ) ) {
694 fprintf_td(stderr, "initAdapter: Adapter power-on failed:: %s\n", adapter->toString().c_str());
695 return false;
696 }
697 // adapter is powered-on
698 fprintf_td(stderr, "initAdapter: %s\n", adapter->toString().c_str());
699 {
700 const LE_Features le_feats = adapter->getLEFeatures();
701 fprintf_td(stderr, "initAdapter: LE_Features %s\n", to_string(le_feats).c_str());
702 }
703 if( adapter->getBTMajorVersion() > 4 ) {
704 LE_PHYs Tx { LE_PHYs::LE_2M }, Rx { LE_PHYs::LE_2M };
705 HCIStatusCode res = adapter->setDefaultLE_PHY(Tx, Rx);
706 fprintf_td(stderr, "initAdapter: Set Default LE PHY: status %s: Tx %s, Rx %s\n",
707 to_string(res).c_str(), to_string(Tx).c_str(), to_string(Rx).c_str());
708 }
709 std::shared_ptr<AdapterStatusListener> asl(new MyAdapterStatusListener());
710 adapter->addStatusListener( asl );
711
712 if( !startDiscovery(adapter.get(), "initAdapter") ) {
713 adapter->removeStatusListener( asl );
714 return false;
715 }
716 return true;
717}
718
719static void myChangedAdapterSetFunc(const bool added, std::shared_ptr<BTAdapter>& adapter) {
720 if( added ) {
721 if( nullptr == chosenAdapter ) {
722 if( initAdapter( adapter ) ) {
723 chosenAdapter = adapter;
724 fprintf_td(stderr, "****** Adapter ADDED__: InitOK: %s\n", adapter->toString().c_str());
725 } else {
726 fprintf_td(stderr, "****** Adapter ADDED__: Ignored: %s\n", adapter->toString().c_str());
727 }
728 } else {
729 fprintf_td(stderr, "****** Adapter ADDED__: Ignored (other): %s\n", adapter->toString().c_str());
730 }
731 } else {
732 if( nullptr != chosenAdapter && adapter == chosenAdapter ) {
733 chosenAdapter = nullptr;
734 fprintf_td(stderr, "****** Adapter REMOVED: %s\n", adapter->toString().c_str());
735 } else {
736 fprintf_td(stderr, "****** Adapter REMOVED (other): %s\n", adapter->toString().c_str());
737 }
738 }
739}
740
741void test() {
742 bool done = false;
743
745
746 std::shared_ptr<BTManager> mngr = BTManager::get();
747 mngr->addChangedAdapterSetCallback(myChangedAdapterSetFunc);
748
749 while( !done ) {
750 if( 0 == MULTI_MEASUREMENTS ||
752 )
753 {
754 fprintf_td(stderr, "****** EOL Test MULTI_MEASUREMENTS left %d, processed %zu/%zu\n",
756 fprintf_td(stderr, "****** WaitForDevice %s\n", BTDeviceRegistry::getWaitForDevicesString().c_str());
757 fprintf_td(stderr, "****** DevicesProcessed %s\n", BTDeviceRegistry::getProcessedDevicesString().c_str());
758 done = true;
759 } else {
760 jau::sleep_for( 2_s );
761 }
762 }
763 chosenAdapter = nullptr;
764
765 //
766 // just a manually controlled pull down to show status, not required
767 //
768 jau::darray<std::shared_ptr<BTAdapter>> adapterList = mngr->getAdapters();
769
770 jau::for_each_const(adapterList, [](const std::shared_ptr<BTAdapter>& adapter) {
771 fprintf_td(stderr, "****** EOL Adapter's Devices - pre close: %s\n", adapter->toString().c_str());
772 adapter->printDeviceLists();
773 });
774 {
775 BTManager::size_type count = mngr->removeChangedAdapterSetCallback(myChangedAdapterSetFunc);
776 fprintf_td(stderr, "****** EOL Removed ChangedAdapterSetCallback %zu\n", (size_t)count);
777
778 mngr->close();
779 }
780 jau::for_each_const(adapterList, [](const std::shared_ptr<BTAdapter>& adapter) {
781 fprintf_td(stderr, "****** EOL Adapter's Devices - post close: %s\n", adapter->toString().c_str());
782 adapter->printDeviceLists();
783 });
784}
785
786#include <cstdio>
787
788int main(int argc, char *argv[])
789{
790 bool waitForEnter=false;
791
792 fprintf_td(stderr, "Direct-BT Native Version %s (API %s)\n", DIRECT_BT_VERSION, DIRECT_BT_VERSION_API);
793
794 // Add defaults for Avalun's LabPad device, announcing its device name as 'LabPad[0-9]+'
795 std::string dev_name_prefix = "LabPad";
796 {
797 BTDeviceRegistry::addToWaitForDevices( dev_name_prefix );
798
800 sec->io_cap = SMPIOCapability::KEYBOARD_ONLY;
801 sec->sec_level = BTSecurityLevel::ENC_AUTH;
802 }
803
804 for(int i=1; i<argc; i++) {
805 fprintf(stderr, "arg[%d/%d]: '%s'\n", i, argc, argv[i]);
806
807 if( !strcmp("-dbt_debug", argv[i]) && argc > (i+1) ) {
808 setenv("direct_bt.debug", argv[++i], 1 /* overwrite */);
809 } else if( !strcmp("-dbt_verbose", argv[i]) && argc > (i+1) ) {
810 setenv("direct_bt.verbose", argv[++i], 1 /* overwrite */);
811 } else if( !strcmp("-dbt_gatt", argv[i]) && argc > (i+1) ) {
812 setenv("direct_bt.gatt", argv[++i], 1 /* overwrite */);
813 } else if( !strcmp("-dbt_l2cap", argv[i]) && argc > (i+1) ) {
814 setenv("direct_bt.l2cap", argv[++i], 1 /* overwrite */);
815 } else if( !strcmp("-dbt_hci", argv[i]) && argc > (i+1) ) {
816 setenv("direct_bt.hci", argv[++i], 1 /* overwrite */);
817 } else if( !strcmp("-dbt_mgmt", argv[i]) && argc > (i+1) ) {
818 setenv("direct_bt.mgmt", argv[++i], 1 /* overwrite */);
819 } else if( !strcmp("-wait", argv[i]) ) {
820 waitForEnter = true;
821 } else if( !strcmp("-show_update_events", argv[i]) ) {
822 SHOW_UPDATE_EVENTS = true;
823 } else if( !strcmp("-quiet", argv[i]) ) {
824 QUIET = true;
825 } else if( !strcmp("-discoveryPolicy", argv[i]) ) {
826 discoveryPolicy = to_DiscoveryPolicy(atoi(argv[++i]));
827 } else if( !strcmp("-scanPassive", argv[i]) ) {
828 le_scan_active = false;
829 } else if( !strcmp("-btmode", argv[i]) && argc > (i+1) ) {
830 btMode = to_BTMode(argv[++i]);
831 } else if( !strcmp("-adapter", argv[i]) && argc > (i+1) ) {
832 useAdapter = EUI48( std::string(argv[++i]) );
833 } else if( !strcmp("-passkey", argv[i]) && argc > (i+1) ) {
835 sec->passkey = atoi(argv[++i]);
836 fprintf(stderr, "Set passkey in %s\n", sec->toString().c_str());
837 }
838 }
839 fprintf_td(stderr, "pid %d\n", getpid());
840
841 fprintf_td(stderr, "Run with '[-btmode LE|BREDR|DUAL] "
842 "[-disconnect] [-show_update_events] [-quiet] "
843 "[-discoveryPolicy <0-4>] "
844 "[-scanPassive] "
845 "[-adapter <adapter_address>] "
846 "(-passkey <digits>)* "
847 "[-dbt_verbose true|false] "
848 "[-dbt_debug true|false|adapter.event,gatt.data,hci.event,hci.scan_ad_eir,mgmt.event] "
849 "[-dbt_mgmt cmd.timeout=3000,ringsize=64,...] "
850 "[-dbt_hci cmd.complete.timeout=10000,cmd.status.timeout=3000,ringsize=64,...] "
851 "[-dbt_gatt cmd.read.timeout=500,cmd.write.timeout=500,cmd.init.timeout=2500,ringsize=128,...] "
852 "[-dbt_l2cap reader.timeout=10000,restart.count=0,...] "
853 "\n");
854
855 fprintf_td(stderr, "MULTI_MEASUREMENTS %d\n", MULTI_MEASUREMENTS.load());
856 fprintf_td(stderr, "KEEP_CONNECTED %d\n", KEEP_CONNECTED);
857 fprintf_td(stderr, "RESET_ADAPTER_EACH_CONN %d\n", RESET_ADAPTER_EACH_CONN);
858 fprintf_td(stderr, "GATT_PING_ENABLED %d\n", GATT_PING_ENABLED);
859 fprintf_td(stderr, "REMOVE_DEVICE %d\n", REMOVE_DEVICE);
860 fprintf_td(stderr, "SHOW_UPDATE_EVENTS %d\n", SHOW_UPDATE_EVENTS);
861 fprintf_td(stderr, "QUIET %d\n", QUIET);
862 fprintf_td(stderr, "adapter %s, privacy %d\n", useAdapter.toString().c_str(), use_privacy);
863 fprintf_td(stderr, "btmode %s\n", to_string(btMode).c_str());
864 fprintf_td(stderr, "discoveryPolicy %s\n", to_string(discoveryPolicy).c_str());
865 fprintf_td(stderr, "scanActive %s\n", to_string(le_scan_active).c_str());
866 fprintf_td(stderr, "security-details: %s\n", BTSecurityRegistry::allToString().c_str());
867 fprintf_td(stderr, "waitForDevice: %s\n", BTDeviceRegistry::getWaitForDevicesString().c_str());
868
869 if( waitForEnter ) {
870 fprintf_td(stderr, "Press ENTER to continue\n");
871 getchar();
872 }
873 fprintf_td(stderr, "****** TEST start\n");
874 test();
875 fprintf_td(stderr, "****** TEST end\n");
876 if( true ) {
877 // Just for testing purpose, i.e. triggering BTManager::close() within the test controlled app,
878 // instead of program shutdown.
879 fprintf_td(stderr, "****** Manager close start\n");
880 std::shared_ptr<BTManager> mngr = BTManager::get(); // already existing
881 mngr->close();
882 fprintf_td(stderr, "****** Manager close end\n");
883 }
884}
void indicationReceived(BTGattCharRef charDecl, const TROOctets &char_value, const uint64_t timestamp, const bool confirmationSent) override
Called from native BLE stack, initiated by a received indication associated with the given BTGattChar...
void notificationReceived(BTGattCharRef charDecl, const TROOctets &char_value, const uint64_t timestamp) override
Called from native BLE stack, initiated by a received notification associated with the given BTGattCh...
MyGATTEventListener(int i_, int j_)
BTAdapter status listener for remote BTDevice discovery events: Added, updated and removed; as well a...
Definition: BTAdapter.hpp:127
BTAdapter represents one local Bluetooth Controller.
Definition: BTAdapter.hpp:324
std::string toString() const noexcept override
Definition: BTAdapter.hpp:1331
HCIStatusCode reset() noexcept
Reset the adapter.
Definition: BTAdapter.cpp:943
HCIStatusCode startDiscovery(const DBGattServerRef &gattServerData_=nullptr, const DiscoveryPolicy policy=DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_READY, const bool le_scan_active=true, const uint16_t le_scan_interval=24, const uint16_t le_scan_window=24, const uint8_t filter_policy=0x00, const bool filter_dup=true) noexcept
Starts discovery.
Definition: BTAdapter.cpp:1150
BDAddressAndType const & getAddressAndType() const noexcept
Returns the adapter's public BDAddressAndType, i.e.
Definition: BTAdapter.hpp:691
std::string getName() const noexcept
Returns the name.
Definition: BTAdapter.hpp:714
BTGattChar event listener for notification and indication events.
Definition: BTGattChar.hpp:450
Class maps a GATT command and optionally its asynchronous response to a synchronous atomic operation.
Definition: BTGattCmd.hpp:67
void setVerbose(const bool v) noexcept
Set verbosity for UUID resolution.
Definition: BTGattCmd.hpp:274
bool hasResponseSet() const noexcept
Return true if a notification or indication response has been set via constructor,...
Definition: BTGattCmd.hpp:268
HCIStatusCode send(const bool prefNoAck, const jau::TROOctets &cmd_data, const jau::fraction_i64 &timeout) noexcept
Send the command to the remote BTDevice.
Definition: BTGattCmd.cpp:176
HCIStatusCode sendOnly(const bool prefNoAck, const jau::TROOctets &cmd_data) noexcept
Send the command to the remote BTDevice, only.
Definition: BTGattCmd.cpp:179
bool isResolved() noexcept
Query whether all UUIDs of this commands have been resolved.
Definition: BTGattCmd.cpp:167
const jau::TROOctets & getResponse() const noexcept
Returns the read-only response data object for configured commands with response notification or indi...
Definition: BTGattCmd.hpp:283
void setResponseMinSize(jau::nsize_t v) noexcept
Definition: BTGattCmd.hpp:255
void setDataCallback(const DataCallback &dcb) noexcept
Definition: BTGattCmd.hpp:256
std::string toString() const noexcept
Definition: BTGattCmd.cpp:262
Representing a Gatt Characteristic Descriptor object from the GATTRole::Client perspective.
Definition: BTGattDesc.hpp:74
std::unique_ptr< const jau::uuid_t > type
Type of descriptor.
Definition: BTGattDesc.hpp:110
std::string toString() const noexcept override
Definition: BTGattDesc.cpp:99
Representing a Gatt Service object from the GATTRole::Client perspective.
std::unique_ptr< const jau::uuid_t > type
Service type UUID.
std::string toString() const noexcept override
jau::darray< BTGattCharRef > characteristicList
List of Characteristic Declarations as shared reference.
jau::nsize_t size_type
Definition: BTManager.hpp:211
Representing a complete list of Gatt Service objects from the GATTRole::Server perspective,...
Persistent endian aware octet data, i.e.
Definition: octets.hpp:560
std::string toString() const
Definition: octets.hpp:932
Transient read only and endian aware octet data, i.e.
Definition: octets.hpp:67
std::string toString() const noexcept
Definition: octets.hpp:288
constexpr nsize_t size() const noexcept
Returns the used memory size for read and write operations, may be zero.
Definition: octets.hpp:162
constexpr uint8_t const * get_ptr() const noexcept
Definition: octets.hpp:272
constexpr size_type size() const noexcept
Like std::vector::size().
Definition: darray.hpp:763
const_reference at(size_type i) const
Like std::vector::at(size_type), immutable reference.
Definition: darray.hpp:814
static std::unique_ptr< uuid_t > create(TypeSize const t, uint8_t const *const buffer, lb_endian_t const le_or_be)
Definition: uuid.cpp:56
static bool REMOVE_DEVICE
static const uint8_t filter_policy
static std::atomic< int > deviceReadyCount
int main(int argc, char *argv[])
static const uint16_t le_scan_interval
static const POctets cmd_data2({ 0x01, 0xB8 }, lb_endian::little)
static uint64_t timestamp_t0
static bool use_privacy
static bool SHOW_UPDATE_EVENTS
static const uint16_t le_scan_window
static void connectDiscoveredDevice(BTDeviceRef device)
static void processReadyDevice(const BTDeviceRef &device)
static bool QUIET
static const bool filter_dup
static bool KEEP_CONNECTED
static bool le_scan_active
static std::shared_ptr< BTAdapter > chosenAdapter
static void myChangedAdapterSetFunc(const bool added, std::shared_ptr< BTAdapter > &adapter)
static bool initAdapter(std::shared_ptr< BTAdapter > &adapter)
static bool startDiscovery(BTAdapter *a, const std::string &msg)
static std::atomic< int > MULTI_MEASUREMENTS
static void resetAdapter(BTAdapter *a, int mode)
static DiscoveryPolicy discoveryPolicy
static std::unique_ptr< uuid_t > cmd_req_uuid
static BTMode btMode
static bool GATT_PING_ENABLED
DBGattServerRef dbGattServer(new DBGattServer(jau::make_darray(std::make_shared< DBGattService >(true, std::make_unique< const jau::uuid16_t >(GattServiceType::GENERIC_ACCESS), jau::make_darray(std::make_shared< DBGattChar >(std::make_unique< const jau::uuid16_t >(GattCharacteristicType::DEVICE_NAME), BTGattChar::PropertyBitVal::Read, jau::darray< DBGattDescRef >(), make_gvalue("Jausoft_Dev", 128), true), std::make_shared< DBGattChar >(std::make_unique< const jau::uuid16_t >(GattCharacteristicType::APPEARANCE), BTGattChar::PropertyBitVal::Read, jau::darray< DBGattDescRef >(), make_gvalue((uint16_t) 0)))), std::make_shared< DBGattService >(true, std::make_unique< const jau::uuid16_t >(GattServiceType::DEVICE_INFORMATION), jau::make_darray(std::make_shared< DBGattChar >(std::make_unique< const jau::uuid16_t >(GattCharacteristicType::MANUFACTURER_NAME_STRING), BTGattChar::PropertyBitVal::Read, jau::darray< DBGattDescRef >(), make_gvalue("Gothel Software")), std::make_shared< DBGattChar >(std::make_unique< const jau::uuid16_t >(GattCharacteristicType::MODEL_NUMBER_STRING), BTGattChar::PropertyBitVal::Read, jau::darray< DBGattDescRef >(), make_gvalue("2.4.0-pre")), std::make_shared< DBGattChar >(std::make_unique< const jau::uuid16_t >(GattCharacteristicType::SERIAL_NUMBER_STRING), BTGattChar::PropertyBitVal::Read, jau::darray< DBGattDescRef >(), make_gvalue("sn:0123456789")), std::make_shared< DBGattChar >(std::make_unique< const jau::uuid16_t >(GattCharacteristicType::HARDWARE_REVISION_STRING), BTGattChar::PropertyBitVal::Read, jau::darray< DBGattDescRef >(), make_gvalue("hw:0123456789")), std::make_shared< DBGattChar >(std::make_unique< const jau::uuid16_t >(GattCharacteristicType::FIRMWARE_REVISION_STRING), BTGattChar::PropertyBitVal::Read, jau::darray< DBGattDescRef >(), make_gvalue("fw:0123456789")), std::make_shared< DBGattChar >(std::make_unique< const jau::uuid16_t >(GattCharacteristicType::SOFTWARE_REVISION_STRING), BTGattChar::PropertyBitVal::Read, jau::darray< DBGattDescRef >(), make_gvalue("sw:0123456789")))))))
static const POctets cmd_data1({ 0x00, 0x14, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x5E, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x9B, 0x23, 0x84 }, lb_endian::little)
static int RESET_ADAPTER_EACH_CONN
static EUI48 useAdapter
void test()
static void removeDevice(BTDeviceRef device)
static const POctets resp_exp({ 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x01, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0x64, 0x17, 0x01, 0xed }, lb_endian::little)
static std::unique_ptr< uuid_t > cmd_rsp_uuid
static const uuid16_t _TEMPERATURE_MEASUREMENT(GattCharacteristicType::TEMPERATURE_MEASUREMENT)
constexpr const char CLIENT_KEY_PATH[]
C++20 we could use constexpr std::string
constexpr UnaryFunction for_each_const(T &data, UnaryFunction f, std::enable_if_t< is_cow_type< T >::value, bool >=true) noexcept
uint32_t dfa_utf8_decode(uint32_t &state, uint32_t &codep, const uint32_t byte_value)
std::string to_string(const alphabet &v) noexcept
Definition: base_codec.hpp:97
SMPPairingState
SMP Pairing Process state definition.
Definition: SMPTypes.hpp:107
Entry * getStartOf(const EUI48 &addr, const std::string &name) noexcept
Returns a matching Entry,.
Entry * getOrCreate(const std::string &addrOrNameSub) noexcept
Determines whether the given addrOrNameSub is a EUI48Sub or just a name and retrieves an entry.
BTMode
Bluetooth adapter operating mode.
Definition: BTTypes0.hpp:112
LE_Features
HCI Supported Commands.
Definition: BTTypes0.hpp:162
DiscoveryPolicy
Discovery policy defines the BTAdapter discovery mode after connecting a remote BTDevice:
Definition: BTAdapter.hpp:82
std::shared_ptr< BTDevice > BTDeviceRef
Definition: BTDevice.hpp:1347
LE_PHYs
LE Transport PHY bit values.
Definition: BTTypes0.hpp:231
Entry * get(const EUI48 &addr, const std::string &name, AddressNameEntryMatchFunc m) noexcept
Returns a matching BTSecurityRegistry::Entry with the given addr and/or name.
ScanType
Meta ScanType as derived from BTMode, with defined value mask consisting of BDAddressType bits.
Definition: BTTypes0.hpp:350
AdapterSetting
Adapter Setting Bits.
Definition: BTTypes1.hpp:144
constexpr bool isAdapterSettingBitSet(const AdapterSetting mask, const AdapterSetting bit) noexcept
Definition: BTTypes1.hpp:185
BTMode to_BTMode(const std::string &value) noexcept
Maps the specified name to a constant of BTMode.
Definition: BTTypes0.cpp:226
std::string allToString() noexcept
BTSecurityLevel
Bluetooth Security Level.
Definition: BTTypes0.hpp:267
PairingMode
Bluetooth secure pairing mode.
Definition: BTTypes0.hpp:317
constexpr DiscoveryPolicy to_DiscoveryPolicy(const uint8_t v) noexcept
Definition: BTAdapter.hpp:103
constexpr uint16_t getHCIConnSupervisorTimeout(const uint16_t conn_latency, const uint16_t conn_interval_max_ms, const uint16_t min_result_ms=number(HCIConstInt::LE_CONN_MIN_TIMEOUT_MS), const uint16_t multiplier=10) noexcept
Defining the supervising timeout for LE connections to be a multiple of the maximum connection interv...
Definition: HCITypes.hpp:102
HCIStatusCode
BT Core Spec v5.2: Vol 1, Part F Controller Error Codes: 1.3 List of Error Codes.
Definition: HCITypes.hpp:138
EIRDataType
Bit mask of 'Extended Inquiry Response' (EIR) data fields, indicating a set of related data.
Definition: BTTypes0.hpp:838
std::shared_ptr< BTGattChar > BTGattCharRef
Definition: BTGattChar.hpp:410
jau::POctets make_gvalue(const char *name)
Convenience jau::POctets ctor function to create DBGattChar or DBGattDesc values.
std::shared_ptr< DBGattServer > DBGattServerRef
std::shared_ptr< DBGattChar > DBGattCharRef
constexpr darray< First > make_darray(First &&arg1, Next &&... argsN)
Construct a darray<T> instance, initialized by move semantics from the variadic (template pack) argum...
Definition: darray.hpp:1373
bool remove(const std::string &path, const traverse_options topts=traverse_options::none) noexcept
Remove the given path.
Definition: file_util.cpp:1173
constexpr uint32_t number(const iostate rhs) noexcept
Definition: byte_stream.hpp:72
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
Definition: int_types.hpp:53
std::string to_hexstring(value_type const &v) noexcept
Produce a lower-case hexadecimal string representation of the given pointer.
std::string getProcessedDevicesString() noexcept
void addToWaitForDevices(const std::string &addrOrNameSub) noexcept
size_t getProcessedDeviceCount() noexcept
std::string getWaitForDevicesString() noexcept
bool isDeviceProcessed(const BDAddressAndType &a) noexcept
bool areAllDevicesProcessed(DeviceQueryMatchFunc m) noexcept
Returns true if all addToWaitForDevices() awaited devices have been addToProcessedDevices() processed...
bool isWaitingForAnyDevice() noexcept
size_t getWaitForDevicesCount() noexcept
void addToProcessedDevices(const BDAddressAndType &a, const std::string &n) noexcept
bool isWaitingForDevice(const EUI48 &address, const std::string &name, DeviceQueryMatchFunc m) noexcept
Returns true if the given address and/or name matches any of the BTDeviceRegistry::addToWaitForDevice...
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition: backtrace.hpp:32
int fprintf_td(const uint64_t elapsed_ms, FILE *stream, const char *format,...) noexcept
Convenient fprintf() invocation, prepending the given elapsed_ms timestamp.
Definition: debug.cpp:270
uint64_t getCurrentMilliseconds() noexcept
Returns current monotonic time in milliseconds.
Definition: basic_types.cpp:64
bool sleep_for(const fraction_timespec &relative_time, const bool monotonic=true, const bool ignore_irq=true) noexcept
sleep_for causes the current thread to block until a specific amount of time has passed.
constexpr bool isSecLevelOrIOCapSet() const noexcept
constexpr const BTSecurityLevel & getSecLevel() const noexcept
constexpr int getPairingPasskey() const noexcept
constexpr bool getPairingNumericComparison() const noexcept
constexpr const SMPIOCapability & getSecurityAutoIOCap() const noexcept
constexpr const SMPIOCapability & getIOCap() const noexcept
constexpr bool isSecurityAutoEnabled() const noexcept
std::string toString() const noexcept
A packed 48 bit EUI-48 identifier, formerly known as MAC-48 or simply network device MAC address (Med...
Definition: eui48.hpp:324
std::string toString() const noexcept
Definition: eui48.cpp:167
@ GENERIC_ACCESS
This service contains generic information about the device.
@ DEVICE_INFORMATION
This service exposes manufacturer and/or vendor information about a device.
@ Read
@ SOFTWARE_REVISION_STRING
@ SERIAL_NUMBER_STRING
@ MODEL_NUMBER_STRING
@ DEVICE_NAME
@ APPEARANCE
@ MANUFACTURER_NAME_STRING
@ TEMPERATURE_MEASUREMENT
@ FIRMWARE_REVISION_STRING
@ HARDWARE_REVISION_STRING