|
ELEC-C7222
Libraries for ELEC C7222 Course Work
|
This module encapsulates BTstack’s ATT database and GATT‑level routing with C++ classes. It provides parsing, handle‑based routing, and characteristic‑level events while preserving BTstack’s ATT semantics.
c7222::AttributeServer (libs/elec_c7222/ble/gatt/include/attribute_server.hpp)Service/Characteristic objects.c7222::Service (libs/elec_c7222/ble/gatt/include/service.hpp)Attribute entries produced by parsing.c7222::Characteristic (libs/elec_c7222/ble/gatt/include/characteristic.hpp)EventHandler for semantic events (read/write, CCCD changes, indication completion).c7222::Attribute (libs/elec_c7222/ble/gatt/include/attribute.hpp) is the leaf object for a single ATT DB entry:.gatt files.c7222::AttributeServer::Init(att_db) parses the ATT DB once and builds services.att_read_callback / att_write_callback.AttributeServer finds the target handle and routes to:c7222::Characteristic::HandleAttributeRead/Write() if the handle belongs to a characteristic.c7222::Attribute::InvokeRead/WriteCallback() for service‑level attributes.DYNAMIC property in the .gatt file.NOTIFY/INDICATE in .gatt.SetUserDescription() / SetUserDescriptionText().In GATT/ATT, a UUID identifies the type of an attribute. It is how BTstack distinguishes a Primary Service declaration from a Characteristic Value or a descriptor. UUIDs can be:
In the compiled ATT database, each attribute stores its UUID. When parsing the DB, BTstack (and this library) reads either a 16‑bit or 128‑bit UUID based on the entry size and flags. This UUID is then used to classify the attribute and route it to the correct higher‑level object.
c7222::Uuid (libs/elec_c7222/ble/gatt/include/uuid.hpp) is the strongly‑typed wrapper used throughout parsing and lookup:
Service, Characteristic, and Attribute classification.In BTstack, the ATT database is a linear list of attributes. Each attribute is a record that contains:
An attribute is the unit that the ATT protocol reads and writes. GATT operations (read/write/notify/indicate) ultimately target an attribute handle.
The compiled att_db blob is a packed binary representation of these entries. The ATT server reads this list to answer read/write requests and calls user callbacks for dynamic entries.
c7222::Attribute mirrors a single BTstack attribute entry and adds C++‑level safety and callbacks:
std::vector<uint8_t> to allow runtime updates.std::function).Characteristic installs the correct read/write handler for that descriptor.BleError and update the dynamic storage on success.DYNAMIC flag): value is immutable at runtime. Reads return the bytes embedded in the ATT DB blob. Writes are rejected.DYNAMIC flag): reads and writes are forwarded to the application (if callbacks are installed). If no write callback is installed, the value is still stored and the write succeeds.BleError semantics.In BTstack’s ATT database, a Characteristic is represented as a group of attributes in a fixed order:
This is why ATT operations always target a handle: each characteristic is really multiple attributes, and each attribute has its own handle.
The c7222::Characteristic class groups these related attributes into a single C++ object, eliminating the need to manually track individual handles and descriptors across the ATT DB. This abstraction lets applications work with high-level characteristic concepts (read, write, notify) rather than juggling raw handles and callbacks, making the code more maintainable and less error-prone.
c7222::Characteristic can be created in two ways:
c7222::Characteristic::ParseFromAttributes() groups a Declaration, Value, and Descriptors into a single object.During parsing, descriptor UUIDs determine which descriptor objects are created and which handlers they receive.
Characteristic properties are a bitfield (read, write, notify, indicate, etc.) derived from the ATT DB. Security permissions (read/write encrypted/authenticated/authorized, SC requirement, and key size) are encoded in the attribute properties and evaluated at runtime by helper methods such as:
ReadRequiresEncryption() / WriteRequiresEncryption()ReadRequiresAuthentication() / WriteRequiresAuthentication()ReadRequiresAuthorization() / WriteRequiresAuthorization()ReadRequiresSecureConnections() / WriteRequiresSecureConnections()These permissions influence CCCD/SCCD writes and read/write access to the value attribute.
Every characteristic includes:
Optional descriptors are created when present in the ATT DB:
NOTIFY or INDICATE is set.Access is managed through the underlying Attribute objects:
std::function).c7222::Characteristic::HandleAttributeRead/Write().Descriptor‑level behavior also uses attribute read/write callbacks:
Default behavior:
OnWrite if using the default handler.c7222::Characteristic::EventHandler provides a semantic, higher‑level event interface on top of raw attribute access. It exists to:
Handlers are stored as raw pointers. Multiple handlers can be registered and will be invoked in registration order. Remove with RemoveEventHandler() if needed.
In BTstack, a service is a logical grouping of attributes that begins with a Service Declaration attribute and continues until the next Service Declaration. The service is identified by the UUID stored in that declaration.
In BTstack, a Service is represented by a Service Declaration attribute (UUID 0x2800 for Primary, 0x2801 for Secondary), followed by all characteristic and descriptor attributes that belong to that service until the next Service Declaration.
c7222::Service groups the service‑level attributes and its characteristics into a single object:
declaration_attr_) stores the service UUID and identifies Primary vs Secondary.included_service_declarations_ (raw ATT attributes, used for accurate DB representation)included_services_ (higher‑level Service objects for easier access)characteristics_) stores all characteristic objects that belong to the service.Service is a container and router for characteristics; the actual functional behavior of a service (business logic, data production/validation) must be implemented by the application using characteristic callbacks and event handlers.
Services are created either by:
AddCharacteristic() or CreateCharacteristic().c7222::Service::ParseFromAttributes() consumes attributes in order and builds the service and all its characteristics.During parsing, characteristics are extracted in discovery order using c7222::Characteristic::ParseFromAttributes().
Service objects provide:
FindServiceAttributeByHandle) so the AttributeServer can dispatch reads/writes that are not part of a characteristic value.HasCharacteristicsRequiringAuthentication/Encryption/Authorization) to inform security configuration decisions before connections.When SetConnectionHandle() is called on a service, the handle is propagated to all characteristics and included services. This enables notifications/indications from value updates without each characteristic needing to be updated manually.
c7222::AttributeServer is the C++ wrapper around BTstack’s ATT server model. The core (platform‑agnostic) responsibilities are:
Attribute objects, then builds Service and Characteristic objects in discovery order.Characteristic (value or descriptor) or a service‑level Attribute.DispatchBleHciPacket().The public API is intentionally thin: it exposes Init(), lookup helpers (by UUID/handle), and connection/security setters. All read/write semantics follow BTstack’s ATT expectations (read returns byte count or error; write returns ATT error codes).
The Pico W port lives in libs/elec_c7222/ble/gatt/platform/rpi_pico/attribute_server.cpp and wires the C++ layer into BTstack’s C callbacks:
att_server_init(att_db, att_read_callback, att_write_callback) registers the compiled DB and the C callbacks.att_db blob into Attribute objects and calls InitServices() to build services/characteristics.att_read_callback calls c7222::AttributeServer::ReadAttribute() and maps the result back to BTstack’s ATT status codes. att_write_callback calls c7222::AttributeServer::WriteAttribute() and returns 0 on success or an ATT error code.BTstack provides a low‑level C interface: raw ATT DB bytes, handles, and callback functions. The C++ wrapper adds:
Attribute separates static vs dynamic storage and centralizes permission checks.c7222::Characteristic::EventHandler provides semantic hooks (CCCD changes, indication complete) without re‑implementing ATT logic.