Skip to content

Commit 9517c12

Browse files
authored
feat(ble_gatt_server): Add (unneccessary) definitions for GenericAccessService (#389)
* Add `espp::GenericAccessService` * Add method in `BleGattServer` to read remote `Appearance` of client * Update docs Adds functionality for reading remote client info and adds some information about the BLE GAS - though its not needed for implementation. * Build `ble_gatt_server/example`
1 parent f43eb87 commit 9517c12

File tree

5 files changed

+215
-2
lines changed

5 files changed

+215
-2
lines changed

components/ble_gatt_server/include/ble_gatt_server.hpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "ble_appearances.hpp"
1919
#include "ble_gatt_server_callbacks.hpp"
2020
#include "device_info_service.hpp"
21+
#include "generic_access_service.hpp"
2122

2223
namespace espp {
2324
/// BLE GATT Server
@@ -536,12 +537,12 @@ class BleGattServer : public BaseComponent {
536537
// refresh the services
537538
client->getServices(true);
538539
// now get Generic Access Service
539-
auto gas = client->getService(NimBLEUUID("1800"));
540+
auto gas = client->getService(NimBLEUUID(GenericAccessService::SERVICE_UUID));
540541
if (!gas) {
541542
return {};
542543
}
543544
// now get the Device Name characteristic
544-
auto name_char = gas->getCharacteristic(NimBLEUUID("2A00"));
545+
auto name_char = gas->getCharacteristic(NimBLEUUID(GenericAccessService::NAME_CHAR_UUID));
545546
if (!name_char) {
546547
return {};
547548
}
@@ -554,6 +555,39 @@ class BleGattServer : public BaseComponent {
554555
return name;
555556
}
556557

558+
/// Get the connected device appearance
559+
/// @param conn_info The connection information for the device.
560+
/// @return The connected device appearance.
561+
std::optional<uint16_t> get_connected_device_appearance(const NimBLEConnInfo &conn_info) const {
562+
if (!server_) {
563+
return {};
564+
}
565+
// since this connection is handled by the server, we won't manually
566+
// connect, and instead inform the client that we are already connected
567+
// using this conn handle
568+
auto client = server_->getClient(conn_info);
569+
// refresh the services
570+
client->getServices(true);
571+
// now get Generic Access Service
572+
auto gas = client->getService(NimBLEUUID(GenericAccessService::SERVICE_UUID));
573+
if (!gas) {
574+
return {};
575+
}
576+
// now get the Appearance characteristic
577+
auto characteristic =
578+
gas->getCharacteristic(NimBLEUUID(GenericAccessService::APPEARANCE_CHAR_UUID));
579+
if (!characteristic) {
580+
return {};
581+
}
582+
// make sure we can read it
583+
if (!characteristic->canRead()) {
584+
return {};
585+
}
586+
// and read it
587+
auto value = characteristic->readValue();
588+
return GenericAccessService::parse_appearance(value);
589+
}
590+
557591
/// Get the connected device PnP ID
558592
/// @param conn_info The connection information for the device.
559593
/// @return The connected device PnP ID.
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
#pragma once
2+
3+
#include <sdkconfig.h>
4+
5+
#include <string>
6+
7+
#if CONFIG_BT_NIMBLE_ENABLED || defined(_DOXYGEN_)
8+
9+
#include "NimBLEDevice.h"
10+
11+
#include "base_component.hpp"
12+
13+
namespace espp {
14+
/// Generic Access Service
15+
/// This class is responsible for creating and managing the Generic Access Service.
16+
///
17+
/// The service is created with the following characteristics:
18+
/// - Name (read)
19+
/// - Appearance (read)
20+
///
21+
/// The Generic Access Service is a required Bluetooth service that provides
22+
/// information about the device. This information can be used by a client to
23+
/// identify the device and determine its capabilities. The Generic Access
24+
/// Service is defined by the Bluetooth SIG and is intended to be used with any
25+
/// device.
26+
///
27+
/// NOTE: as a developer, you should not need to actually use this service
28+
/// directly as the NimBLE stack will automatically include it in the device
29+
/// information. This class is provided for completeness and for developers who
30+
/// want to customize the service. I'm not really sure why I created this file,
31+
/// but I have so here we are.
32+
class GenericAccessService : public BaseComponent {
33+
public:
34+
static constexpr uint16_t SERVICE_UUID = 0x1800;
35+
static constexpr uint16_t NAME_CHAR_UUID = 0x2A00;
36+
static constexpr uint16_t APPEARANCE_CHAR_UUID = 0x2A01;
37+
38+
/// Parse the appearance value from raw bytes
39+
/// \param bytes The characteristic value
40+
/// \return The appearance value
41+
static uint16_t parse_appearance(const std::vector<uint8_t> &bytes) {
42+
return parse_appearance(bytes.data(), bytes.size());
43+
}
44+
45+
/// Parse the appearance value from raw bytes
46+
/// \param bytes The characteristic value
47+
/// \param size The size of the characteristic value
48+
/// \return The appearance value
49+
static uint16_t parse_appearance(const uint8_t *bytes, size_t size) {
50+
if (size != 2) {
51+
return 0;
52+
}
53+
return (bytes[1] << 8) | bytes[0];
54+
}
55+
56+
/// Constructor
57+
/// \param log_level The log level for the component
58+
explicit GenericAccessService(espp::Logger::Verbosity log_level = espp::Logger::Verbosity::WARN)
59+
: BaseComponent("GenericAccessService", log_level) {}
60+
61+
/// Initialize the Service
62+
/// \param server The BLE server to add the service to
63+
void init(NimBLEServer *server) { make_service(server); }
64+
65+
/// Deinitialize the Service
66+
/// \note This should only be called after NimBLEDevice::deinit(true) has been
67+
/// called, since that will free the memory used by the service
68+
void deinit() {
69+
service_ = nullptr;
70+
name_ = nullptr;
71+
appearance_ = nullptr;
72+
}
73+
74+
/// Start the service
75+
/// \note This must be called after the service has been initialized
76+
void start() {
77+
if (!service_) {
78+
logger_.error("Service not created");
79+
return;
80+
}
81+
if (!service_->start()) {
82+
logger_.error("Failed to start service");
83+
return;
84+
}
85+
}
86+
87+
/// Get the service object
88+
/// \return The service object
89+
NimBLEService *get_service() { return service_; }
90+
91+
/// Get the UUID of the service
92+
/// \return The service UUID
93+
NimBLEUUID uuid() {
94+
if (service_) {
95+
return service_->getUUID();
96+
}
97+
return NimBLEUUID(SERVICE_UUID);
98+
}
99+
100+
/// Set the device name
101+
/// \param name The device name
102+
void set_name(const std::string &name) {
103+
if (!name_) {
104+
logger_.error("Characteristic not created");
105+
return;
106+
}
107+
name_->setValue(name);
108+
}
109+
110+
/// Set the device appearance
111+
/// \param appearance The appearance value
112+
void set_appearance(uint16_t appearance) {
113+
if (!appearance_) {
114+
logger_.error("Characteristic not created");
115+
return;
116+
}
117+
appearance_->setValue(appearance);
118+
}
119+
120+
/// Get the device name
121+
/// \return The device name
122+
std::string get_name() {
123+
if (!name_) {
124+
logger_.error("Characteristic not created");
125+
return "";
126+
}
127+
return name_->getValue();
128+
}
129+
130+
/// Get the device appearance
131+
/// \return The appearance value
132+
uint16_t get_appearance() {
133+
if (!appearance_) {
134+
logger_.error("Characteristic not created");
135+
return 0;
136+
}
137+
return parse_appearance(appearance_->getValue());
138+
}
139+
140+
protected:
141+
void make_service(NimBLEServer *server) {
142+
service_ = server->createService(NimBLEUUID(SERVICE_UUID));
143+
if (!service_) {
144+
logger_.error("Failed to create service");
145+
return;
146+
}
147+
148+
name_ = service_->createCharacteristic(NimBLEUUID(NAME_CHAR_UUID), NIMBLE_PROPERTY::READ);
149+
150+
appearance_ =
151+
service_->createCharacteristic(NimBLEUUID(APPEARANCE_CHAR_UUID), NIMBLE_PROPERTY::READ);
152+
}
153+
154+
NimBLEService *service_{nullptr};
155+
NimBLECharacteristic *name_{nullptr};
156+
NimBLECharacteristic *appearance_{nullptr};
157+
};
158+
} // namespace espp
159+
160+
#endif // CONFIG_BT_NIMBLE_ENABLED || defined(_DOXYGEN_)

doc/Doxyfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ INPUT += $(PROJECT_PATH)/components/ble_gatt_server/include/ble_gatt_server.hpp
113113
INPUT += $(PROJECT_PATH)/components/ble_gatt_server/include/ble_gatt_server_callbacks.hpp
114114
INPUT += $(PROJECT_PATH)/components/ble_gatt_server/include/ble_gatt_server_menu.hpp
115115
INPUT += $(PROJECT_PATH)/components/ble_gatt_server/include/device_info_service.hpp
116+
INPUT += $(PROJECT_PATH)/components/ble_gatt_server/include/generic_access_service.hpp
116117
INPUT += $(PROJECT_PATH)/components/bldc_driver/include/bldc_driver.hpp
117118
INPUT += $(PROJECT_PATH)/components/bldc_haptics/include/bldc_haptics.hpp
118119
INPUT += $(PROJECT_PATH)/components/bldc_haptics/include/detent_config.hpp
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Generic Access Service
2+
**********************
3+
4+
The `GenericAccessService` implements the required standard BLE Generic Access
5+
service, providing device information from a BLE peripheral to a BLE central.
6+
7+
It should be noted that as a developer, you are not required to use this
8+
service, as one is created for you automatically by the BLE stack.
9+
10+
I'm not really sure why I created this file, but I have so here we are.
11+
12+
.. ---------------------------- API Reference ----------------------------------
13+
14+
API Reference
15+
-------------
16+
17+
.. include-build-file:: inc/generic_access_service.inc

doc/en/ble/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ BLE APIs
88
ble_gatt_server
99
ble_gatt_server_example
1010
device_info_service
11+
generic_access_service
1112
gfps_service
1213
gfps_service_example
1314
hid_service

0 commit comments

Comments
 (0)