Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ble_gatt_server): Add (unneccessary) definitions for GenericAccessService #389

Merged
merged 1 commit into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions components/ble_gatt_server/include/ble_gatt_server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "ble_appearances.hpp"
#include "ble_gatt_server_callbacks.hpp"
#include "device_info_service.hpp"
#include "generic_access_service.hpp"

namespace espp {
/// BLE GATT Server
Expand Down Expand Up @@ -536,12 +537,12 @@ class BleGattServer : public BaseComponent {
// refresh the services
client->getServices(true);
// now get Generic Access Service
auto gas = client->getService(NimBLEUUID("1800"));
auto gas = client->getService(NimBLEUUID(GenericAccessService::SERVICE_UUID));
if (!gas) {
return {};
}
// now get the Device Name characteristic
auto name_char = gas->getCharacteristic(NimBLEUUID("2A00"));
auto name_char = gas->getCharacteristic(NimBLEUUID(GenericAccessService::NAME_CHAR_UUID));
if (!name_char) {
return {};
}
Expand All @@ -554,6 +555,39 @@ class BleGattServer : public BaseComponent {
return name;
}

/// Get the connected device appearance
/// @param conn_info The connection information for the device.
/// @return The connected device appearance.
std::optional<uint16_t> get_connected_device_appearance(const NimBLEConnInfo &conn_info) const {
if (!server_) {
return {};
}
// since this connection is handled by the server, we won't manually
// connect, and instead inform the client that we are already connected
// using this conn handle
auto client = server_->getClient(conn_info);
// refresh the services
client->getServices(true);
// now get Generic Access Service
auto gas = client->getService(NimBLEUUID(GenericAccessService::SERVICE_UUID));
if (!gas) {
return {};
}
// now get the Appearance characteristic
auto characteristic =
gas->getCharacteristic(NimBLEUUID(GenericAccessService::APPEARANCE_CHAR_UUID));
if (!characteristic) {
return {};
}
// make sure we can read it
if (!characteristic->canRead()) {
return {};
}
// and read it
auto value = characteristic->readValue();
return GenericAccessService::parse_appearance(value);
}

/// Get the connected device PnP ID
/// @param conn_info The connection information for the device.
/// @return The connected device PnP ID.
Expand Down
160 changes: 160 additions & 0 deletions components/ble_gatt_server/include/generic_access_service.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
#pragma once

#include <sdkconfig.h>

#include <string>

#if CONFIG_BT_NIMBLE_ENABLED || defined(_DOXYGEN_)

#include "NimBLEDevice.h"

#include "base_component.hpp"

namespace espp {
/// Generic Access Service
/// This class is responsible for creating and managing the Generic Access Service.
///
/// The service is created with the following characteristics:
/// - Name (read)
/// - Appearance (read)
///
/// The Generic Access Service is a required Bluetooth service that provides
/// information about the device. This information can be used by a client to
/// identify the device and determine its capabilities. The Generic Access
/// Service is defined by the Bluetooth SIG and is intended to be used with any
/// device.
///
/// NOTE: as a developer, you should not need to actually use this service
/// directly as the NimBLE stack will automatically include it in the device
/// information. This class is provided for completeness and for developers who
/// want to customize the service. I'm not really sure why I created this file,
/// but I have so here we are.
class GenericAccessService : public BaseComponent {
public:
static constexpr uint16_t SERVICE_UUID = 0x1800;
static constexpr uint16_t NAME_CHAR_UUID = 0x2A00;
static constexpr uint16_t APPEARANCE_CHAR_UUID = 0x2A01;

/// Parse the appearance value from raw bytes
/// \param bytes The characteristic value
/// \return The appearance value
static uint16_t parse_appearance(const std::vector<uint8_t> &bytes) {
return parse_appearance(bytes.data(), bytes.size());
}

/// Parse the appearance value from raw bytes
/// \param bytes The characteristic value
/// \param size The size of the characteristic value
/// \return The appearance value
static uint16_t parse_appearance(const uint8_t *bytes, size_t size) {
if (size != 2) {
return 0;
}
return (bytes[1] << 8) | bytes[0];
}

/// Constructor
/// \param log_level The log level for the component
explicit GenericAccessService(espp::Logger::Verbosity log_level = espp::Logger::Verbosity::WARN)
: BaseComponent("GenericAccessService", log_level) {}

/// Initialize the Service
/// \param server The BLE server to add the service to
void init(NimBLEServer *server) { make_service(server); }

/// Deinitialize the Service
/// \note This should only be called after NimBLEDevice::deinit(true) has been
/// called, since that will free the memory used by the service
void deinit() {
service_ = nullptr;
name_ = nullptr;
appearance_ = nullptr;
}

/// Start the service
/// \note This must be called after the service has been initialized
void start() {
if (!service_) {
logger_.error("Service not created");
return;
}
if (!service_->start()) {
logger_.error("Failed to start service");
return;
}
}

/// Get the service object
/// \return The service object
NimBLEService *get_service() { return service_; }

/// Get the UUID of the service
/// \return The service UUID
NimBLEUUID uuid() {
if (service_) {
return service_->getUUID();
}
return NimBLEUUID(SERVICE_UUID);
}

/// Set the device name
/// \param name The device name
void set_name(const std::string &name) {
if (!name_) {
logger_.error("Characteristic not created");
return;
}
name_->setValue(name);
}

/// Set the device appearance
/// \param appearance The appearance value
void set_appearance(uint16_t appearance) {
if (!appearance_) {
logger_.error("Characteristic not created");
return;
}
appearance_->setValue(appearance);
}

/// Get the device name
/// \return The device name
std::string get_name() {
if (!name_) {
logger_.error("Characteristic not created");
return "";
}
return name_->getValue();
}

/// Get the device appearance
/// \return The appearance value
uint16_t get_appearance() {
if (!appearance_) {
logger_.error("Characteristic not created");
return 0;
}
return parse_appearance(appearance_->getValue());
}

protected:
void make_service(NimBLEServer *server) {
service_ = server->createService(NimBLEUUID(SERVICE_UUID));
if (!service_) {
logger_.error("Failed to create service");
return;
}

name_ = service_->createCharacteristic(NimBLEUUID(NAME_CHAR_UUID), NIMBLE_PROPERTY::READ);

appearance_ =
service_->createCharacteristic(NimBLEUUID(APPEARANCE_CHAR_UUID), NIMBLE_PROPERTY::READ);
}

NimBLEService *service_{nullptr};
NimBLECharacteristic *name_{nullptr};
NimBLECharacteristic *appearance_{nullptr};
};
} // namespace espp

#endif // CONFIG_BT_NIMBLE_ENABLED || defined(_DOXYGEN_)
1 change: 1 addition & 0 deletions doc/Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ INPUT += $(PROJECT_PATH)/components/ble_gatt_server/include/ble_gatt_server.hpp
INPUT += $(PROJECT_PATH)/components/ble_gatt_server/include/ble_gatt_server_callbacks.hpp
INPUT += $(PROJECT_PATH)/components/ble_gatt_server/include/ble_gatt_server_menu.hpp
INPUT += $(PROJECT_PATH)/components/ble_gatt_server/include/device_info_service.hpp
INPUT += $(PROJECT_PATH)/components/ble_gatt_server/include/generic_access_service.hpp
INPUT += $(PROJECT_PATH)/components/bldc_driver/include/bldc_driver.hpp
INPUT += $(PROJECT_PATH)/components/bldc_haptics/include/bldc_haptics.hpp
INPUT += $(PROJECT_PATH)/components/bldc_haptics/include/detent_config.hpp
Expand Down
17 changes: 17 additions & 0 deletions doc/en/ble/generic_access_service.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Generic Access Service
**********************

The `GenericAccessService` implements the required standard BLE Generic Access
service, providing device information from a BLE peripheral to a BLE central.

It should be noted that as a developer, you are not required to use this
service, as one is created for you automatically by the BLE stack.

I'm not really sure why I created this file, but I have so here we are.

.. ---------------------------- API Reference ----------------------------------

API Reference
-------------

.. include-build-file:: inc/generic_access_service.inc
1 change: 1 addition & 0 deletions doc/en/ble/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ BLE APIs
ble_gatt_server
ble_gatt_server_example
device_info_service
generic_access_service
gfps_service
gfps_service_example
hid_service
Expand Down