Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
dbt_peripheral00.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2021 Gothel Software e.K.
4 * Copyright (c) 2021 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;
53
54
55/** \file
56 * This _dbt_peripheral00__ C++ peripheral ::BTRole::Slave GATT server example uses an event driven workflow.
57 *
58 * ### dbt_peripheral00 Invocation Examples:
59 * Using `scripts/run-dbt_peripheral00.sh` from `dist` directory:
60 *
61 * * Serving clients as `TestDevice001` using adapter 00:1A:7D:DA:71:03; Using ENC_ONLY (JUST_WORKS) encryption.
62 * ~~~
63 * ../scripts/run-dbt_peripheral00.sh -adapter 00:1A:7D:DA:71:03 -name TestDevice001 -seclevel 2
64 * ~~~
65 */
66
67static uint64_t timestamp_t0;
68
69static EUI48 useAdapter = EUI48::ALL_DEVICE;
70static BTMode btMode = BTMode::DUAL;
71static bool use_SC = true;
72static std::string adapter_name = "TestDev001_N";
73static std::string adapter_short_name = "TDev001N";
74static std::shared_ptr<BTAdapter> chosenAdapter = nullptr;
75static BTSecurityLevel adapter_sec_level = BTSecurityLevel::UNSET;
76static SMPIOCapability adapter_sec_io_cap = SMPIOCapability::UNSET;
77static bool SHOW_UPDATE_EVENTS = false;
78static bool RUN_ONLY_ONCE = false;
81
83
84static bool startAdvertising(BTAdapter *a, std::string msg); // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
85static bool stopAdvertising(BTAdapter *a, std::string msg); // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
86static void processDisconnectedDevice(BTDeviceRef device); // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
87
88static void setDevice(const BTDeviceRef& cd) {
90 connectedDevice = cd;
91}
92
95 return connectedDevice;
96}
97
98static bool matches(const BTDeviceRef& device) {
99 const BTDeviceRef d = getDevice();
100 return nullptr != d ? (*d) == *device : false;
101}
102
103static const jau::uuid128_t DataServiceUUID = jau::uuid128_t("d0ca6bf3-3d50-4760-98e5-fc5883e93712");
104static const jau::uuid128_t StaticDataUUID = jau::uuid128_t("d0ca6bf3-3d51-4760-98e5-fc5883e93712");
105static const jau::uuid128_t CommandUUID = jau::uuid128_t("d0ca6bf3-3d52-4760-98e5-fc5883e93712");
106static const jau::uuid128_t ResponseUUID = jau::uuid128_t("d0ca6bf3-3d53-4760-98e5-fc5883e93712");
107static const jau::uuid128_t PulseDataUUID = jau::uuid128_t("d0ca6bf3-3d54-4760-98e5-fc5883e93712");
108
109// DBGattServerRef dbGattServer = std::make_shared<DBGattServer>(
111 /* services: */
112 jau::make_darray( // DBGattService
113 std::make_shared<DBGattService> ( true /* primary */,
114 std::make_unique<const jau::uuid16_t>(GattServiceType::GENERIC_ACCESS) /* type_ */,
115 jau::make_darray ( // DBGattChar
116 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::DEVICE_NAME) /* value_type_ */,
118 jau::darray<DBGattDescRef>() /* intentionally empty */,
119 make_gvalue(adapter_name.c_str(), 128) /* value */, true /* variable_length */ ),
120 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::APPEARANCE) /* value_type_ */,
122 jau::darray<DBGattDescRef>() /* intentionally empty */,
123 make_gvalue((uint16_t)0) /* value */ )
124 ) ),
125 std::make_shared<DBGattService> ( true /* primary */,
126 std::make_unique<const jau::uuid16_t>(GattServiceType::DEVICE_INFORMATION) /* type_ */,
127 jau::make_darray ( // DBGattChar
128 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::MANUFACTURER_NAME_STRING) /* value_type_ */,
130 jau::darray<DBGattDescRef>() /* intentionally empty */,
131 make_gvalue("Gothel Software") /* value */ ),
132 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::MODEL_NUMBER_STRING) /* value_type_ */,
134 jau::darray<DBGattDescRef>() /* intentionally empty */,
135 make_gvalue("2.4.0-pre") /* value */ ),
136 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::SERIAL_NUMBER_STRING) /* value_type_ */,
138 jau::darray<DBGattDescRef>() /* intentionally empty */,
139 make_gvalue("sn:0123456789") /* value */ ),
140 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::HARDWARE_REVISION_STRING) /* value_type_ */,
142 jau::darray<DBGattDescRef>() /* intentionally empty */,
143 make_gvalue("hw:0123456789") /* value */ ),
144 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::FIRMWARE_REVISION_STRING) /* value_type_ */,
146 jau::darray<DBGattDescRef>() /* intentionally empty */,
147 make_gvalue("fw:0123456789") /* value */ ),
148 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::SOFTWARE_REVISION_STRING) /* value_type_ */,
150 jau::darray<DBGattDescRef>() /* intentionally empty */,
151 make_gvalue("sw:0123456789") /* value */ )
152 ) ),
153 std::make_shared<DBGattService> ( true /* primary */,
154 std::make_unique<const jau::uuid128_t>(DataServiceUUID) /* type_ */,
155 jau::make_darray ( // DBGattChar
156 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid128_t>(StaticDataUUID) /* value_type_ */,
158 jau::make_darray ( // DBGattDesc
159 std::make_shared<DBGattDesc>( BTGattDesc::TYPE_USER_DESC, make_gvalue("DATA_STATIC") )
160 ),
161 make_gvalue("Proprietary Static Data 0x00010203") /* value */ ),
162 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid128_t>(CommandUUID) /* value_type_ */,
164 jau::make_darray ( // DBGattDesc
165 std::make_shared<DBGattDesc>( BTGattDesc::TYPE_USER_DESC, make_gvalue("COMMAND") )
166 ),
167 make_gvalue(128, 64) /* value */, true /* variable_length */ ),
168 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid128_t>(ResponseUUID) /* value_type_ */,
170 jau::make_darray ( // DBGattDesc
171 std::make_shared<DBGattDesc>( BTGattDesc::TYPE_USER_DESC, make_gvalue("RESPONSE") ),
172 DBGattDesc::createClientCharConfig()
173 ),
174 make_gvalue((uint16_t)0) /* value */ ),
175 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid128_t>(PulseDataUUID) /* value_type_ */,
177 jau::make_darray ( // DBGattDesc
178 std::make_shared<DBGattDesc>( BTGattDesc::TYPE_USER_DESC, make_gvalue("DATA_PULSE") ),
179 DBGattDesc::createClientCharConfig()
180 ),
181 make_gvalue("Synthethic Sensor 01") /* value */ )
182 ) )
183 ) ) );
184
185
187
188 void adapterSettingsChanged(BTAdapter &a, const AdapterSetting oldmask, const AdapterSetting newmask,
189 const AdapterSetting changedmask, const uint64_t timestamp) override {
190 const bool initialSetting = AdapterSetting::NONE == oldmask;
191 if( initialSetting ) {
192 fprintf_td(stderr, "****** SETTINGS_INITIAL: %s -> %s, changed %s\n", to_string(oldmask).c_str(),
193 to_string(newmask).c_str(), to_string(changedmask).c_str());
194 } else {
195 fprintf_td(stderr, "****** SETTINGS_CHANGED: %s -> %s, changed %s\n", to_string(oldmask).c_str(),
196 to_string(newmask).c_str(), to_string(changedmask).c_str());
197 }
198 fprintf_td(stderr, "Status BTAdapter:\n");
199 fprintf_td(stderr, "%s\n", a.toString().c_str());
200 (void)timestamp;
201
202 if( !initialSetting &&
203 isAdapterSettingBitSet(changedmask, AdapterSetting::POWERED) &&
204 isAdapterSettingBitSet(newmask, AdapterSetting::POWERED) )
205 {
206 std::thread sd(::startAdvertising, &a, "powered-on"); // @suppress("Invalid arguments")
207 sd.detach();
208 }
209 }
210
211 void discoveringChanged(BTAdapter &a, const ScanType currentMeta, const ScanType changedType, const bool changedEnabled, const DiscoveryPolicy policy, const uint64_t timestamp) override {
212 fprintf_td(stderr, "****** DISCOVERING: meta %s, changed[%s, enabled %d, policy %s]: %s\n",
213 to_string(currentMeta).c_str(), to_string(changedType).c_str(), changedEnabled, to_string(policy).c_str(), a.toString().c_str());
214 (void)timestamp;
215 }
216
217 bool deviceFound(const BTDeviceRef& device, const uint64_t timestamp) override {
218 (void)timestamp;
219
220 fprintf_td(stderr, "****** FOUND__-1: NOP %s\n", device->toString(true).c_str());
221 return false;
222 }
223
224 void deviceUpdated(const BTDeviceRef& device, const EIRDataType updateMask, const uint64_t timestamp) override {
225 if( is_set(updateMask, EIRDataType::BDADDR)) {
226 fprintf_td(stderr, "****** UPDATED (ADDR-RESOLVED): %s of %s\n", to_string(updateMask).c_str(), device->toString(true).c_str());
227 } else if( SHOW_UPDATE_EVENTS ) {
228 fprintf_td(stderr, "****** UPDATED: %s of %s\n", to_string(updateMask).c_str(), device->toString(true).c_str());
229 }
230 (void)timestamp;
231 }
232
233 void deviceConnected(const BTDeviceRef& device, const bool discovered, const uint64_t timestamp) override {
234 fprintf_td(stderr, "****** CONNECTED (discovered %d): %s\n", discovered, device->toString(true).c_str());
235 const bool available = nullptr == getDevice();
236 if( available ) {
237 setDevice(device);
238 }
239 (void)discovered;
240 (void)timestamp;
241 }
242
243 void devicePairingState(const BTDeviceRef& device, const SMPPairingState state, const PairingMode mode, const uint64_t timestamp) override {
244 fprintf_td(stderr, "****** PAIRING STATE: state %s, mode %s, %s\n",
245 to_string(state).c_str(), to_string(mode).c_str(), device->toString().c_str());
246 (void)timestamp;
247 switch( state ) {
248 case SMPPairingState::NONE:
249 // next: deviceReady(..)
250 break;
251 case SMPPairingState::FAILED: {
252 // next: deviceReady() or deviceDisconnected(..)
253 } break;
254 case SMPPairingState::REQUESTED_BY_RESPONDER:
255 // next: FEATURE_EXCHANGE_STARTED
256 break;
257 case SMPPairingState::FEATURE_EXCHANGE_STARTED:
258 // next: FEATURE_EXCHANGE_COMPLETED
259 break;
260 case SMPPairingState::FEATURE_EXCHANGE_COMPLETED:
261 // next: PASSKEY_EXPECTED... or KEY_DISTRIBUTION
262 break;
263 case SMPPairingState::PASSKEY_EXPECTED: {
264 const BTSecurityRegistry::Entry* sec = BTSecurityRegistry::getStartOf(device->getAddressAndType().address, "");
265 if( nullptr != sec && sec->getPairingPasskey() != BTSecurityRegistry::Entry::NO_PASSKEY ) {
266 std::thread dc(&BTDevice::setPairingPasskey, device, static_cast<uint32_t>( sec->getPairingPasskey() ));
267 dc.detach();
268 } else {
269 std::thread dc(&BTDevice::setPairingPasskey, device, 0);
270 // 3s disconnect: std::thread dc(&BTDevice::setPairingPasskeyNegative, device);
271 dc.detach();
272 }
273 // next: KEY_DISTRIBUTION or FAILED
274 } break;
275 case SMPPairingState::NUMERIC_COMPARE_EXPECTED: {
276 const BTSecurityRegistry::Entry* sec = BTSecurityRegistry::getStartOf(device->getAddressAndType().address, "");
277 if( nullptr != sec ) {
278 std::thread dc(&BTDevice::setPairingNumericComparison, device, sec->getPairingNumericComparison());
279 dc.detach();
280 } else {
281 std::thread dc(&BTDevice::setPairingNumericComparison, device, true); // FIXME
282 dc.detach();
283 }
284 // next: KEY_DISTRIBUTION or FAILED
285 } break;
286 case SMPPairingState::PASSKEY_NOTIFY: {
287 fprintf_td(stderr, "****** \n");
288 fprintf_td(stderr, "****** \n");
289 fprintf_td(stderr, "****** Confirm on your device %s\n", device->getName().c_str());
290 fprintf_td(stderr, "****** PassKey: %s\n", device->getResponderSMPPassKeyString().c_str());
291 fprintf_td(stderr, "****** \n");
292 fprintf_td(stderr, "****** \n");
293 // next: KEY_DISTRIBUTION or FAILED
294 } break;
295 case SMPPairingState::OOB_EXPECTED:
296 // FIXME: ABORT
297 break;
298 case SMPPairingState::KEY_DISTRIBUTION:
299 // next: COMPLETED or FAILED
300 break;
301 case SMPPairingState::COMPLETED:
302 // next: deviceReady(..)
303 break;
304 default: // nop
305 break;
306 }
307 }
308
309 void deviceReady(const BTDeviceRef& device, const uint64_t timestamp) override {
310 (void)timestamp;
311 fprintf_td(stderr, "****** READY-1: NOP %s\n", device->toString(true).c_str());
312 }
313
314 void deviceDisconnected(const BTDeviceRef& device, const HCIStatusCode reason, const uint16_t handle, const uint64_t timestamp) override {
316 fprintf_td(stderr, "****** DISCONNECTED (count %zu): Reason 0x%X (%s), old handle %s: %s\n",
317 servedConnections.load(), static_cast<uint8_t>(reason), to_string(reason).c_str(),
318 to_hexstring(handle).c_str(), device->toString(true).c_str());
319
320 const bool match = matches(device);
321 if( match ) {
322 setDevice(nullptr);
323 }
324 std::thread sd(::processDisconnectedDevice, device); // @suppress("Invalid arguments")
325 sd.detach();
326 (void)timestamp;
327 }
328
329 std::string toString() const noexcept override {
330 return "MyAdapterStatusListener[this "+to_hexstring(this)+"]";
331 }
332
333};
334
336 private:
337 jau::service_runner pulse_service;
338
339 jau::sc_atomic_uint16 handlePulseDataNotify = 0;
340 jau::sc_atomic_uint16 handlePulseDataIndicate = 0;
341 jau::sc_atomic_uint16 handleResponseDataNotify = 0;
342 jau::sc_atomic_uint16 handleResponseDataIndicate = 0;
343
344 uint16_t usedMTU = BTGattHandler::number(BTGattHandler::Defaults::MIN_ATT_MTU);
345
346 void clear() {
348
349 handlePulseDataNotify = 0;
350 handlePulseDataIndicate = 0;
351 handleResponseDataNotify = 0;
352 handleResponseDataIndicate = 0;
353
354 dbGattServer->resetGattClientCharConfig(DataServiceUUID, PulseDataUUID);
355 dbGattServer->resetGattClientCharConfig(DataServiceUUID, ResponseUUID);
356 }
357
358 void pulse_worker_init(jau::service_runner& sr) noexcept {
359 (void)sr;
360 const BTDeviceRef connectedDevice_ = getDevice();
361 const std::string connectedDeviceStr = nullptr != connectedDevice_ ? connectedDevice_->toString() : "n/a";
362 fprintf_td(stderr, "****** Server GATT::PULSE Start %s\n", connectedDeviceStr.c_str());
363 }
364 void pulse_worker(jau::service_runner& sr) noexcept {
365 BTDeviceRef connectedDevice_ = getDevice();
366 if( nullptr != connectedDevice_ && connectedDevice_->getConnected() ) {
367 if( 0 != handlePulseDataNotify || 0 != handlePulseDataIndicate ) {
368 std::string data( "Dynamic Data Example. Elapsed Milliseconds: "+jau::to_decstring(environment::getElapsedMillisecond(), ',', 9) );
369 jau::POctets v(data.size()+1, jau::lb_endian::little);
370 v.put_string_nc(0, data, v.size(), true /* includeEOS */);
371 if( 0 != handlePulseDataNotify ) {
372 const bool res = connectedDevice_->sendNotification(handlePulseDataNotify, v);
373 fprintf_td(stderr, "****** GATT::sendNotification: PULSE (res %d) to %s\n", res, connectedDevice_->toString().c_str());
374 }
375 if( 0 != handlePulseDataIndicate ) {
376 const bool res = connectedDevice_->sendIndication(handlePulseDataIndicate, v);
377 fprintf_td(stderr, "****** GATT::sendIndication: PULSE (res %d) to %s\n", res, connectedDevice_->toString().c_str());
378 }
379 }
380 }
381 if( !sr.shall_stop() ) {
382 jau::sleep_for( 500_ms );
383 }
384 }
385 void pulse_worker_end(jau::service_runner& sr) noexcept {
386 (void)sr;
387 const BTDeviceRef connectedDevice_ = getDevice();
388 const std::string connectedDeviceStr = nullptr != connectedDevice_ ? connectedDevice_->toString() : "n/a";
389 fprintf_td(stderr, "****** Server GATT::PULSE End %s\n", connectedDeviceStr.c_str());
390 }
391
392 void sendResponse(jau::POctets data) { // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
393 BTDeviceRef connectedDevice_ = getDevice();
394 if( nullptr != connectedDevice_ && connectedDevice_->getConnected() ) {
395 if( 0 != handleResponseDataNotify || 0 != handleResponseDataIndicate ) {
396 if( 0 != handleResponseDataNotify ) {
397 const bool res = connectedDevice_->sendNotification(handleResponseDataNotify, data);
398 fprintf_td(stderr, "****** GATT::sendNotification (res %d): %s to %s\n",
399 res, data.toString().c_str(), connectedDevice_->toString().c_str());
400 }
401 if( 0 != handleResponseDataIndicate ) {
402 const bool res = connectedDevice_->sendIndication(handleResponseDataIndicate, data);
403 fprintf_td(stderr, "****** GATT::sendIndication (res %d): %s to %s\n",
404 res, data.toString().c_str(), connectedDevice_->toString().c_str());
405 }
406 }
407 }
408 }
409
410 public:
412 : pulse_service("MyGATTServerListener::pulse", THREAD_SHUTDOWN_TIMEOUT_MS,
413 jau::bind_member(this, &MyGATTServerListener::pulse_worker),
414 jau::bind_member(this, &MyGATTServerListener::pulse_worker_init),
415 jau::bind_member(this, &MyGATTServerListener::pulse_worker_end))
416 {
417 pulse_service.start();
418 }
419
420 ~MyGATTServerListener() noexcept override {
421 pulse_service.stop();
422 }
423
424 void close() noexcept {
425 pulse_service.stop();
426 clear();
427 }
428
429 void connected(const BTDeviceRef& device, const uint16_t initialMTU) override {
431 const bool match = matches(device);
432 fprintf_td(stderr, "****** GATT::connected(match %d): initMTU %d, %s\n",
433 match, (int)initialMTU, device->toString().c_str());
434 if( match ) {
435 usedMTU = initialMTU;
436 }
437 }
438
439 void disconnected(const BTDeviceRef& device) override {
441 const bool match = matches(device);
442 fprintf_td(stderr, "****** GATT::disconnected(match %d): %s\n", match, device->toString().c_str());
443 if( match ) {
444 clear();
445 }
446 }
447
448 void mtuChanged(const BTDeviceRef& device, const uint16_t mtu) override {
449 const bool match = matches(device);
450 fprintf_td(stderr, "****** GATT::mtuChanged(match %d): %d -> %d, %s\n",
451 match, match ? (int)usedMTU : 0, (int)mtu, device->toString().c_str());
452 if( match ) {
453 usedMTU = mtu;
454 }
455 }
456
457 bool readCharValue(const BTDeviceRef& device, const DBGattServiceRef& s, const DBGattCharRef& c) override {
458 const bool match = matches(device);
459 fprintf_td(stderr, "****** GATT::readCharValue(match %d): to %s, from\n %s\n %s\n",
460 match, device->toString().c_str(), s->toString().c_str(), c->toString().c_str());
461 return match;
462 }
463
464 bool readDescValue(const BTDeviceRef& device, const DBGattServiceRef& s, const DBGattCharRef& c, const DBGattDescRef& d) override {
465 const bool match = matches(device);
466 fprintf_td(stderr, "****** GATT::readDescValue(match %d): to %s, from\n %s\n %s\n %s\n",
467 match, device->toString().c_str(), s->toString().c_str(), c->toString().c_str(), d->toString().c_str());
468 return match;
469 }
470
471 bool writeCharValue(const BTDeviceRef& device, const DBGattServiceRef& s, const DBGattCharRef& c, const jau::TROOctets & value, const uint16_t value_offset) override {
472 const bool match = matches(device);
473 fprintf_td(stderr, "****** GATT::writeCharValue(match %d): %s '%s' @ %u from %s, to\n %s\n %s\n",
474 match, value.toString().c_str(), jau::dfa_utf8_decode( value.get_ptr(), value.size() ).c_str(),
475 value_offset,
476 device->toString().c_str(), s->toString().c_str(), c->toString().c_str());
477 return match;
478 }
479 void writeCharValueDone(const BTDeviceRef& device, const DBGattServiceRef& s, const DBGattCharRef& c) override {
480 const bool match = matches(device);
481 const jau::TROOctets& value = c->getValue();
482 fprintf_td(stderr, "****** GATT::writeCharValueDone(match %d): From %s, to\n %s\n %s\n Char-Value: %s\n",
483 match, device->toString().c_str(), s->toString().c_str(), c->toString().c_str(), value.toString().c_str());
484
485 if( match &&
486 c->getValueType()->equivalent( CommandUUID ) &&
487 ( 0 != handleResponseDataNotify || 0 != handleResponseDataIndicate ) )
488 {
489 jau::POctets value2(value);
490 std::thread senderThread(&MyGATTServerListener::sendResponse, this, value2); // @suppress("Invalid arguments")
491 senderThread.detach();
492 }
493 }
494
495 bool writeDescValue(const BTDeviceRef& device, const DBGattServiceRef& s, const DBGattCharRef& c, const DBGattDescRef& d, const jau::TROOctets & value, const uint16_t value_offset) override {
496 const bool match = matches(device);
497 fprintf_td(stderr, "****** GATT::writeDescValue(match %d): %s '%s' @ %u from %s\n %s\n %s\n %s\n",
498 match, value.toString().c_str(), jau::dfa_utf8_decode( value.get_ptr(), value.size() ).c_str(),
499 value_offset,
500 device->toString().c_str(), s->toString().c_str(), c->toString().c_str(), d->toString().c_str());
501 return match;
502 }
503 void writeDescValueDone(const BTDeviceRef& device, const DBGattServiceRef& s, const DBGattCharRef& c, const DBGattDescRef& d) override {
504 const bool match = matches(device);
505 const jau::TROOctets& value = d->getValue();
506 fprintf_td(stderr, "****** GATT::writeDescValueDone(match %d): From %s\n %s\n %s\n %s\n Desc-Value: %s\n",
507 match, device->toString().c_str(), s->toString().c_str(), c->toString().c_str(), d->toString().c_str(), value.toString().c_str());
508 }
509
510 void clientCharConfigChanged(const BTDeviceRef& device, const DBGattServiceRef& s, const DBGattCharRef& c, const DBGattDescRef& d, const bool notificationEnabled, const bool indicationEnabled) override {
511 const bool match = matches(device);
512 const jau::TROOctets& value = d->getValue();
513 fprintf_td(stderr, "****** GATT::clientCharConfigChanged(match %d): notify %d, indicate %d from %s\n %s\n %s\n %s\n Desc-Value: %s\n",
514 match, notificationEnabled, indicationEnabled,
515 device->toString().c_str(), s->toString().c_str(), c->toString().c_str(), d->toString().c_str(), value.toString().c_str());
516
517 if( match ) {
519 if( c->getValueType()->equivalent( PulseDataUUID ) ) {
520 handlePulseDataNotify = notificationEnabled ? c->getValueHandle() : 0;
521 handlePulseDataIndicate = indicationEnabled ? c->getValueHandle() : 0;
522 } else if( c->getValueType()->equivalent( ResponseUUID ) ) {
523 handleResponseDataNotify = notificationEnabled ? c->getValueHandle() : 0;
524 handleResponseDataIndicate = indicationEnabled ? c->getValueHandle() : 0;
525 }
526 }
527 }
528};
529
530static const uint16_t adv_interval_min=160; // x0.625 = 100ms
531static const uint16_t adv_interval_max=480; // x0.625 = 300ms
532static const AD_PDU_Type adv_type=AD_PDU_Type::ADV_IND;
533static const uint8_t adv_chan_map=0x07;
534static const uint8_t filter_policy=0x00;
535
536static bool startAdvertising(BTAdapter *a, std::string msg) { // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
537 if( useAdapter != EUI48::ALL_DEVICE && useAdapter != a->getAddressAndType().address ) {
538 fprintf_td(stderr, "****** Start advertising (%s): Adapter not selected: %s\n", msg.c_str(), a->toString().c_str());
539 return false;
540 }
541
542 {
543 const LE_Features le_feats = a->getLEFeatures();
544 fprintf_td(stderr, "startAdvertising: LE_Features %s\n", to_string(le_feats).c_str());
545 }
546 if( a->getBTMajorVersion() > 4 ) {
547 LE_PHYs Tx { LE_PHYs::LE_2M }, Rx { LE_PHYs::LE_2M };
548 HCIStatusCode res = a->setDefaultLE_PHY(Tx, Rx);
549 fprintf_td(stderr, "startAdvertising: Set Default LE PHY: status %s: Tx %s, Rx %s\n",
550 to_string(res).c_str(), to_string(Tx).c_str(), to_string(Rx).c_str());
551 }
552
554
555 EInfoReport eir;
556 EIRDataType adv_mask = EIRDataType::FLAGS | EIRDataType::SERVICE_UUID;
557 EIRDataType scanrsp_mask = EIRDataType::NAME | EIRDataType::CONN_IVAL;
558
559 eir.addFlags(GAPFlags::LE_Gen_Disc);
560 eir.addFlags(GAPFlags::BREDR_UNSUP);
561
563 eir.setServicesComplete(false);
564
565 eir.setName(a->getName());
566 eir.setConnInterval(8, 12); // 10ms - 15ms
567
570 if( nullptr != gattDevNameChar ) {
571 std::string aname = a->getName();
572 gattDevNameChar->setValue(reinterpret_cast<uint8_t*>(aname.data()), aname.size(), 0);
573 }
574
575 fprintf_td(stderr, "****** Start advertising (%s): EIR %s\n", msg.c_str(), eir.toString().c_str());
576 fprintf_td(stderr, "****** Start advertising (%s): adv %s, scanrsp %s\n", msg.c_str(), to_string(adv_mask).c_str(), to_string(scanrsp_mask).c_str());
577
578 HCIStatusCode status = a->startAdvertising(dbGattServer, eir, adv_mask, scanrsp_mask,
581 fprintf_td(stderr, "****** Start advertising (%s) result: %s: %s\n", msg.c_str(), to_string(status).c_str(), a->toString().c_str());
582 fprintf_td(stderr, "%s", dbGattServer->toFullString().c_str());
583 return HCIStatusCode::SUCCESS == status;
584}
585
586static bool stopAdvertising(BTAdapter *a, std::string msg) { // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
587 if( useAdapter != EUI48::ALL_DEVICE && useAdapter != a->getAddressAndType().address ) {
588 fprintf_td(stderr, "****** Stop advertising (%s): Adapter not selected: %s\n", msg.c_str(), a->toString().c_str());
589 return false;
590 }
591 HCIStatusCode status = a->stopAdvertising();
592 fprintf_td(stderr, "****** Stop advertising (%s) result: %s: %s\n", msg.c_str(), to_string(status).c_str(), a->toString().c_str());
593 return HCIStatusCode::SUCCESS == status;
594}
595
596static void processDisconnectedDevice(BTDeviceRef device) { // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
597 fprintf_td(stderr, "****** Disconnected Device (count %zu): Start %s\n",
598 servedConnections.load(), device->toString().c_str());
599
600 // already unpaired
601 stopAdvertising(&device->getAdapter(), "device-disconnected");
602 device->remove();
603
604 jau::sleep_for( 100_ms ); // wait a little (FIXME: Fast restart of advertising error)
605
606 if( !RUN_ONLY_ONCE ) {
607 startAdvertising(&device->getAdapter(), "device-disconnected");
608 }
609
610 fprintf_td(stderr, "****** Disonnected Device: End %s\n", device->toString().c_str());
611}
612
613static bool initAdapter(std::shared_ptr<BTAdapter>& adapter) {
614 if( useAdapter != EUI48::ALL_DEVICE && useAdapter != adapter->getAddressAndType().address ) {
615 fprintf_td(stderr, "initAdapter: Adapter not selected: %s\n", adapter->toString().c_str());
616 return false;
617 }
618 if( !adapter->isInitialized() ) {
619 // Initialize with defaults and power-on
620 const HCIStatusCode status = adapter->initialize( btMode, false );
621 if( HCIStatusCode::SUCCESS != status ) {
622 fprintf_td(stderr, "initAdapter: initialize failed: %s: %s\n",
623 to_string(status).c_str(), adapter->toString().c_str());
624 return false;
625 }
626 } else if( !adapter->setPowered( false ) ) {
627 fprintf_td(stderr, "initAdapter: setPower.1 off failed: %s\n", adapter->toString().c_str());
628 return false;
629 }
630 // adapter is powered-off
631 fprintf_td(stderr, "initAdapter.1: %s\n", adapter->toString().c_str());
632
633 {
634 HCIStatusCode status = adapter->setName(adapter_name, adapter_short_name);
635 if( HCIStatusCode::SUCCESS == status ) {
636 fprintf_td(stderr, "initAdapter: setLocalName OK: %s\n", adapter->toString().c_str());
637 } else {
638 fprintf_td(stderr, "initAdapter: setLocalName failed: %s\n", adapter->toString().c_str());
639 return false;
640 }
641
642 status = adapter->setSecureConnections( use_SC );
643 if( HCIStatusCode::SUCCESS == status ) {
644 fprintf_td(stderr, "initAdapter: setSecureConnections OK: %s\n", adapter->toString().c_str());
645 } else {
646 fprintf_td(stderr, "initAdapter: setSecureConnections failed: %s\n", adapter->toString().c_str());
647 return false;
648 }
649
650 const uint16_t conn_min_interval = 8; // 10ms
651 const uint16_t conn_max_interval = 40; // 50ms
652 const uint16_t conn_latency = 0;
653 const uint16_t supervision_timeout = 50; // 500ms
654 status = adapter->setDefaultConnParam(conn_min_interval, conn_max_interval, conn_latency, supervision_timeout);
655 if( HCIStatusCode::SUCCESS == status ) {
656 fprintf_td(stderr, "initAdapter: setDefaultConnParam OK: %s\n", adapter->toString().c_str());
657 } else if( HCIStatusCode::UNKNOWN_COMMAND == status ) {
658 fprintf_td(stderr, "initAdapter: setDefaultConnParam UNKNOWN_COMMAND (ignored): %s\n", adapter->toString().c_str());
659 } else {
660 fprintf_td(stderr, "initAdapter: setDefaultConnParam failed: %s, %s\n", to_string(status).c_str(), adapter->toString().c_str());
661 return false;
662 }
663
664 if( !adapter->setPowered( true ) ) {
665 fprintf_td(stderr, "initAdapter: setPower.2 on failed: %s\n", adapter->toString().c_str());
666 return false;
667 }
668 }
669 fprintf_td(stderr, "initAdapter.2: %s\n", adapter->toString().c_str());
670
671 adapter->setSMPKeyPath(SERVER_KEY_PATH);
672
673 std::shared_ptr<AdapterStatusListener> asl( std::make_shared<MyAdapterStatusListener>() );
674 adapter->addStatusListener( asl );
675 if( !startAdvertising(adapter.get(), "initAdapter") ) {
676 adapter->removeStatusListener( asl );
677 return false;
678 }
679 return true;
680}
681
682static void myChangedAdapterSetFunc(const bool added, std::shared_ptr<BTAdapter>& adapter) {
683 if( added ) {
684 if( nullptr == chosenAdapter ) {
685 if( initAdapter( adapter ) ) {
686 chosenAdapter = adapter;
687 fprintf_td(stderr, "****** Adapter ADDED__: InitOK: %s\n", adapter->toString().c_str());
688 } else {
689 fprintf_td(stderr, "****** Adapter ADDED__: Ignored: %s\n", adapter->toString().c_str());
690 }
691 fprintf_td(stderr, "****** Adapter Features: %s\n", direct_bt::to_string(adapter->getLEFeatures()).c_str());
692 } else {
693 fprintf_td(stderr, "****** Adapter ADDED__: Ignored (other): %s\n", adapter->toString().c_str());
694 }
695 } else {
696 if( nullptr != chosenAdapter && adapter == chosenAdapter ) {
697 chosenAdapter = nullptr;
698 fprintf_td(stderr, "****** Adapter REMOVED: %s\n", adapter->toString().c_str());
699 } else {
700 fprintf_td(stderr, "****** Adapter REMOVED (other): %s\n", adapter->toString().c_str());
701 }
702 }
703}
704
705void test() {
706 std::shared_ptr<BTManager> mngr = BTManager::get();
707
709
710 fprintf_td(stderr, "****** Test Start\n");
711
712 std::shared_ptr<MyGATTServerListener> listener = std::make_shared<MyGATTServerListener>();
713 dbGattServer->addListener( listener );
714
715 mngr->addChangedAdapterSetCallback(myChangedAdapterSetFunc);
716
717 while( !RUN_ONLY_ONCE || 0 == servedConnections ) {
718 jau::sleep_for( 2_s );
719 }
720
721 fprintf_td(stderr, "****** Test Shutdown.01 (DBGattServer.remove-listener)\n");
722 dbGattServer->removeListener( listener );
723
724 fprintf_td(stderr, "****** Test Shutdown.02 (listener.close)\n");
725 listener->close();
726
727 fprintf_td(stderr, "****** Test Shutdown.03 (DBGattServer.close := nullptr)\n");
728 dbGattServer = nullptr;
729
730 chosenAdapter = nullptr;
731
732 fprintf_td(stderr, "****** Test End\n");
733}
734
735#include <cstdio>
736
737int main(int argc, char *argv[])
738{
739 bool waitForEnter=false;
740
741 fprintf_td(stderr, "Direct-BT Native Version %s (API %s)\n", DIRECT_BT_VERSION, DIRECT_BT_VERSION_API);
742
743 for(int i=1; i<argc; i++) {
744 if( !strcmp("-dbt_debug", argv[i]) && argc > (i+1) ) {
745 setenv("direct_bt.debug", argv[++i], 1 /* overwrite */);
746 } else if( !strcmp("-dbt_verbose", argv[i]) && argc > (i+1) ) {
747 setenv("direct_bt.verbose", argv[++i], 1 /* overwrite */);
748 } else if( !strcmp("-dbt_gatt", argv[i]) && argc > (i+1) ) {
749 setenv("direct_bt.gatt", argv[++i], 1 /* overwrite */);
750 } else if( !strcmp("-dbt_l2cap", argv[i]) && argc > (i+1) ) {
751 setenv("direct_bt.l2cap", argv[++i], 1 /* overwrite */);
752 } else if( !strcmp("-dbt_hci", argv[i]) && argc > (i+1) ) {
753 setenv("direct_bt.hci", argv[++i], 1 /* overwrite */);
754 } else if( !strcmp("-dbt_mgmt", argv[i]) && argc > (i+1) ) {
755 setenv("direct_bt.mgmt", argv[++i], 1 /* overwrite */);
756 } else if( !strcmp("-wait", argv[i]) ) {
757 waitForEnter = true;
758 } else if( !strcmp("-show_update_events", argv[i]) ) {
759 SHOW_UPDATE_EVENTS = true;
760 } else if( !strcmp("-btmode", argv[i]) && argc > (i+1) ) {
761 btMode = to_BTMode(argv[++i]);
762 } else if( !strcmp("-use_sc", argv[i]) && argc > (i+1) ) {
763 use_SC = 0 != atoi(argv[++i]);
764 } else if( !strcmp("-adapter", argv[i]) && argc > (i+1) ) {
765 useAdapter = EUI48( std::string(argv[++i]) );
766 } else if( !strcmp("-name", argv[i]) && argc > (i+1) ) {
767 adapter_name = std::string(argv[++i]);
768 } else if( !strcmp("-short_name", argv[i]) && argc > (i+1) ) {
769 adapter_short_name = std::string(argv[++i]);
770 } else if( !strcmp("-mtu", argv[i]) && argc > (i+1) ) {
771 dbGattServer->setMaxAttMTU( atoi(argv[++i]) );
772 } else if( !strcmp("-seclevel", argv[i]) && argc > (i+1) ) {
773 adapter_sec_level = to_BTSecurityLevel(atoi(argv[++i]));
774 } else if( !strcmp("-iocap", argv[i]) && argc > (i+1) ) {
775 adapter_sec_io_cap = to_SMPIOCapability(atoi(argv[++i]));
776 } else if( !strcmp("-once", argv[i]) ) {
777 RUN_ONLY_ONCE = true;
778 }
779 }
780 fprintf_td(stderr, "pid %d\n", getpid());
781
782 fprintf_td(stderr, "Run with '[-btmode LE|BREDR|DUAL] [-use_sc 0|1] "
783 "[-adapter <adapter_address>] "
784 "[-name <adapter_name>] "
785 "[-short_name <adapter_short_name>] "
786 "[-mtu <max att_mtu>] "
787 "[-seclevel <int_sec_level>]* "
788 "[-iocap <int_iocap>]* "
789 "[-once] "
790 "[-dbt_verbose true|false] "
791 "[-dbt_debug true|false|adapter.event,gatt.data,hci.event,hci.scan_ad_eir,mgmt.event] "
792 "[-dbt_mgmt cmd.timeout=3000,ringsize=64,...] "
793 "[-dbt_hci cmd.complete.timeout=10000,cmd.status.timeout=3000,ringsize=64,...] "
794 "[-dbt_gatt cmd.read.timeout=500,cmd.write.timeout=500,cmd.init.timeout=2500,ringsize=128,...] "
795 "[-dbt_l2cap reader.timeout=10000,restart.count=0,...] "
796 "\n");
797
798 fprintf_td(stderr, "SHOW_UPDATE_EVENTS %d\n", SHOW_UPDATE_EVENTS);
799 fprintf_td(stderr, "adapter %s\n", useAdapter.toString().c_str());
800 fprintf_td(stderr, "adapter btmode %s\n", to_string(btMode).c_str());
801 fprintf_td(stderr, "adapter SC %s\n", to_string(use_SC).c_str());
802 fprintf_td(stderr, "adapter name %s (short %s)\n", adapter_name.c_str(), adapter_short_name.c_str());
803 fprintf_td(stderr, "adapter mtu %d\n", (int)dbGattServer->getMaxAttMTU());
804 fprintf_td(stderr, "adapter sec_level %s\n", to_string(adapter_sec_level).c_str());
805 fprintf_td(stderr, "adapter io_cap %s\n", to_string(adapter_sec_io_cap).c_str());
806 fprintf_td(stderr, "once %d\n", (int)RUN_ONLY_ONCE);
807 fprintf_td(stderr, "GattServer %s\n", dbGattServer->toString().c_str());
808 fprintf_td(stderr, "GattServer.services: %s\n", dbGattServer->getServices().get_info().c_str());
809 fprintf_td(stderr, "GattService.characteristics: %s\n", dbGattServer->getServices()[0]->getCharacteristics().get_info().c_str());
810
811 if( waitForEnter ) {
812 fprintf_td(stderr, "Press ENTER to continue\n");
813 getchar();
814 }
815 fprintf_td(stderr, "****** TEST start\n");
816 test();
817 fprintf_td(stderr, "****** TEST end\n");
818}
void writeDescValueDone(const BTDeviceRef &device, const DBGattServiceRef &s, const DBGattCharRef &c, const DBGattDescRef &d) override
Notifies completion of single or bulk writeCharValue() after having accepted and performed all write ...
void disconnected(const BTDeviceRef &device) override
Notification that device got disconnected.
bool writeDescValue(const BTDeviceRef &device, const DBGattServiceRef &s, const DBGattCharRef &c, const DBGattDescRef &d, const jau::TROOctets &value, const uint16_t value_offset) override
Signals attempt to write a single or bulk (prepare) value.
void writeCharValueDone(const BTDeviceRef &device, const DBGattServiceRef &s, const DBGattCharRef &c) override
Notifies completion of single or bulk writeCharValue() after having accepted and performed all write ...
void connected(const BTDeviceRef &device, const uint16_t initialMTU) override
Notification that device got connected.
void clientCharConfigChanged(const BTDeviceRef &device, const DBGattServiceRef &s, const DBGattCharRef &c, const DBGattDescRef &d, const bool notificationEnabled, const bool indicationEnabled) override
Notifies a change of the Client Characteristic Configuration Descriptor (CCCD) value.
bool readDescValue(const BTDeviceRef &device, const DBGattServiceRef &s, const DBGattCharRef &c, const DBGattDescRef &d) override
Signals attempt to read a value.
bool writeCharValue(const BTDeviceRef &device, const DBGattServiceRef &s, const DBGattCharRef &c, const jau::TROOctets &value, const uint16_t value_offset) override
Signals attempt to write a single or bulk (prepare) value.
void mtuChanged(const BTDeviceRef &device, const uint16_t mtu) override
Notification that the MTU has changed.
~MyGATTServerListener() noexcept override
bool readCharValue(const BTDeviceRef &device, const DBGattServiceRef &s, const DBGattCharRef &c) override
Signals attempt to read a value.
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
HCIStatusCode stopAdvertising() noexcept
Ends advertising.
Definition: BTAdapter.cpp:1671
constexpr LE_Features getLEFeatures() const noexcept
Return LE_Features for this controller.
Definition: BTAdapter.hpp:654
std::string toString() const noexcept override
Definition: BTAdapter.hpp:1331
void setServerConnSecurity(const BTSecurityLevel sec_level, const SMPIOCapability io_cap) noexcept
Sets the given BTSecurityLevel and SMPIOCapability for connecting device when in server (peripheral) ...
Definition: BTAdapter.cpp:735
HCIStatusCode startAdvertising(const DBGattServerRef &gattServerData_, EInfoReport &eir, EIRDataType adv_mask=EIRDataType::FLAGS|EIRDataType::SERVICE_UUID, EIRDataType scanrsp_mask=EIRDataType::NAME|EIRDataType::CONN_IVAL, const uint16_t adv_interval_min=160, const uint16_t adv_interval_max=480, const AD_PDU_Type adv_type=AD_PDU_Type::ADV_IND, const uint8_t adv_chan_map=0x07, const uint8_t filter_policy=0x00) noexcept
Starts advertising.
Definition: BTAdapter.cpp:1557
BDAddressAndType const & getAddressAndType() const noexcept
Returns the adapter's public BDAddressAndType, i.e.
Definition: BTAdapter.hpp:691
HCIStatusCode setDefaultLE_PHY(const LE_PHYs Tx, const LE_PHYs Rx) noexcept
Sets default preference of LE_PHYs.
Definition: BTAdapter.cpp:978
constexpr uint16_t getBTMajorVersion() const noexcept
Returns the Bluetooth major version of this adapter.
Definition: BTAdapter.hpp:657
std::string getName() const noexcept
Returns the name.
Definition: BTAdapter.hpp:714
Listener to remote master device's operations on the local GATT-Server.
Representing a complete list of Gatt Service objects from the GATTRole::Server perspective,...
Collection of 'Extended Advertising Data' (EAD), 'Advertising Data' (AD) or 'Extended Inquiry Respons...
Definition: BTTypes0.hpp:898
std::string toString(const bool includeServices=true) const noexcept
Definition: BTTypes0.cpp:854
void setServicesComplete(const bool v) noexcept
Definition: BTTypes0.hpp:999
void addFlags(GAPFlags f) noexcept
Definition: BTTypes0.hpp:992
bool addService(const std::shared_ptr< const jau::uuid_t > &uuid) noexcept
Definition: BTTypes0.cpp:834
void setConnInterval(const uint16_t min, const uint16_t max) noexcept
Set slave connection interval range.
Definition: BTTypes0.hpp:1014
Persistent endian aware octet data, i.e.
Definition: octets.hpp:560
std::string toString() const
Definition: octets.hpp:932
void put_string_nc(const nsize_t i, const std::string &v, const nsize_t max_len, const bool includeEOS) noexcept
Definition: octets.hpp:457
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
This class provides a RAII-style Sequentially Consistent (SC) data race free (DRF) critical block.
Service runner, a reusable dedicated thread performing custom user services.
bool stop() noexcept
Stops this service, if running.
bool shall_stop() const noexcept
Returns true if service shall stop.
void start() noexcept
Starts this service, if not running already.
static const uint8_t filter_policy
static const jau::uuid128_t StaticDataUUID
static bool RUN_ONLY_ONCE
int main(int argc, char *argv[])
static std::string adapter_name
static BTSecurityLevel adapter_sec_level
static uint64_t timestamp_t0
static bool SHOW_UPDATE_EVENTS
static const uint16_t adv_interval_max
static const jau::uuid128_t DataServiceUUID
static jau::sc_atomic_bool sync_data
static bool use_SC
static const uint8_t adv_chan_map
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(adapter_name.c_str(), 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")))), std::make_shared< DBGattService >(true, std::make_unique< const jau::uuid128_t >(DataServiceUUID), jau::make_darray(std::make_shared< DBGattChar >(std::make_unique< const jau::uuid128_t >(StaticDataUUID), BTGattChar::PropertyBitVal::Read, jau::make_darray(std::make_shared< DBGattDesc >(BTGattDesc::TYPE_USER_DESC, make_gvalue("DATA_STATIC"))), make_gvalue("Proprietary Static Data 0x00010203")), std::make_shared< DBGattChar >(std::make_unique< const jau::uuid128_t >(CommandUUID), BTGattChar::PropertyBitVal::WriteNoAck|BTGattChar::PropertyBitVal::WriteWithAck, jau::make_darray(std::make_shared< DBGattDesc >(BTGattDesc::TYPE_USER_DESC, make_gvalue("COMMAND"))), make_gvalue(128, 64), true), std::make_shared< DBGattChar >(std::make_unique< const jau::uuid128_t >(ResponseUUID), BTGattChar::PropertyBitVal::Notify|BTGattChar::PropertyBitVal::Indicate, jau::make_darray(std::make_shared< DBGattDesc >(BTGattDesc::TYPE_USER_DESC, make_gvalue("RESPONSE")), DBGattDesc::createClientCharConfig()), make_gvalue((uint16_t) 0)), std::make_shared< DBGattChar >(std::make_unique< const jau::uuid128_t >(PulseDataUUID), BTGattChar::PropertyBitVal::Notify|BTGattChar::PropertyBitVal::Indicate, jau::make_darray(std::make_shared< DBGattDesc >(BTGattDesc::TYPE_USER_DESC, make_gvalue("DATA_PULSE")), DBGattDesc::createClientCharConfig()), make_gvalue("Synthethic Sensor 01")))))))
static void processDisconnectedDevice(BTDeviceRef device)
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 const jau::uuid128_t PulseDataUUID
static jau::relaxed_atomic_nsize_t servedConnections
static BTDeviceRef getDevice()
static bool stopAdvertising(BTAdapter *a, std::string msg)
static std::string adapter_short_name
static const jau::uuid128_t ResponseUUID
static BTMode btMode
static const jau::uuid128_t CommandUUID
static bool startAdvertising(BTAdapter *a, std::string msg)
static void setDevice(const BTDeviceRef &cd)
static BTDeviceRef connectedDevice
static const uint16_t adv_interval_min
static EUI48 useAdapter
static const AD_PDU_Type adv_type
static SMPIOCapability adapter_sec_io_cap
void test()
static bool matches(const BTDeviceRef &device)
constexpr const char SERVER_KEY_PATH[]
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
constexpr const jau::fraction_i64 THREAD_SHUTDOWN_TIMEOUT_MS
Maximum time in fractions of seconds to wait for a thread shutdown.
Definition: DBTConst.hpp:73
SMPPairingState
SMP Pairing Process state definition.
Definition: SMPTypes.hpp:107
constexpr SMPIOCapability to_SMPIOCapability(const uint8_t v) noexcept
Definition: SMPTypes.hpp:223
SMPIOCapability
Vol 3, Part H, 2.3.2 IO capabilities.
Definition: SMPTypes.hpp:209
Entry * getStartOf(const EUI48 &addr, const std::string &name) noexcept
Returns a matching Entry,.
constexpr BTSecurityLevel to_BTSecurityLevel(const uint8_t v) noexcept
Definition: BTTypes0.hpp:300
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
std::string to_string(const DiscoveryPolicy v) noexcept
Definition: BTAdapter.cpp:58
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
BTSecurityLevel
Bluetooth Security Level.
Definition: BTTypes0.hpp:267
AD_PDU_Type
LE Advertising (AD) Protocol Data Unit (PDU) Types.
Definition: BTTypes0.hpp:397
PairingMode
Bluetooth secure pairing mode.
Definition: BTTypes0.hpp:317
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< DBGattDesc > DBGattDescRef
jau::POctets make_gvalue(const char *name)
Convenience jau::POctets ctor function to create DBGattChar or DBGattDesc values.
std::shared_ptr< DBGattService > DBGattServiceRef
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
void sync() noexcept
Synchronizes filesystems, i.e.
Definition: file_util.cpp:1932
jau::function< R(A...)> bind_member(C1 *base, R(C0::*mfunc)(A...)) noexcept
Bind given class instance and non-void member function to an anonymous function using func_member_tar...
constexpr uint32_t number(const iostate rhs) noexcept
Definition: byte_stream.hpp:72
std::string to_hexstring(value_type const &v) noexcept
Produce a lower-case hexadecimal string representation of the given pointer.
std::string to_decstring(const value_type &v, const char separator=',', const nsize_t width=0) noexcept
Produce a decimal string representation of an integral integer value.
constexpr bool is_set(const cpu_family_t mask, const cpu_family_t bit) noexcept
Definition: cpuid.hpp:122
__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 int getPairingPasskey() const noexcept
constexpr bool getPairingNumericComparison() 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
CXX_ALWAYS_INLINE _Tp load() const noexcept
@ GENERIC_ACCESS
This service contains generic information about the device.
@ DEVICE_INFORMATION
This service exposes manufacturer and/or vendor information about a device.
@ Notify
@ Indicate
@ WriteNoAck
@ Read
@ WriteWithAck
@ SOFTWARE_REVISION_STRING
@ SERIAL_NUMBER_STRING
@ MODEL_NUMBER_STRING
@ DEVICE_NAME
@ APPEARANCE
@ MANUFACTURER_NAME_STRING
@ FIRMWARE_REVISION_STRING
@ HARDWARE_REVISION_STRING