pw_bluetooth#

Host-layer Bluetooth Low Energy APIs and utilities

Unstable C++

The pw_bluetooth module contains APIs and utilities for the host layer of Bluetooth Low Energy.

Host API#

Attention

This module is still under construction, the API is not yet stable.

The headers in public/pw_bluetooth constitute generic interfaces and types for a Bluetooth Host API. Currently, only Low Energy APIs exist.

low_energy::Central2#

class Central2#

Represents the LE central role. Used to scan and connect to peripherals.

Subclassed by pw::bluetooth_sapphire::Central

Public Types

enum class ConnectError : uint8_t#

Possible errors returned by Connect.

Values:

enumerator kUnknownPeer#

The peer ID is unknown.

enumerator kInvalidOptions#

The ConnectionOptions were invalid.

enumerator kAlreadyExists#

A connection to the peer already exists.

enumerator kCouldNotBeEstablished#

The connection procedure failed at the link layer or timed out immediately after being established. A “could not be established” error was reported by the controller. This may be due to interference.

using ConnectResult = pw::expected<Connection2::Ptr, ConnectError>#

The result type returned by Connect().

using ScanStartResult = pw::expected<ScanHandle::Ptr, StartScanError>#

The result type returned by Scan().

Public Functions

virtual async2::OnceReceiver<ConnectResult> Connect(PeerId peer_id, Connection2::ConnectionOptions options) = 0#

Connect to the peer with the given identifier.

The returned Connection2 represents the client’s interest in the LE connection to the peer. Destroying all Connection2 instances for a peer will disconnect from the peer.

The Connection will be closed by the system if the connection to the peer is lost or an error occurs, as indicated by Connection.OnError.

Possible errors are documented in ConnectError.

Parameters:
  • peer_id – Identifier of the peer to initiate a connection to.

  • options – Options used to configure the connection.

Returns:

Returns a result when a connection is successfully established, or an error occurs.

virtual async2::OnceReceiver<ScanStartResult> Scan(ScanOptions options) = 0#

Scans for nearby LE peripherals and broadcasters. The lifetime of the scan session is tied to the returned ScanHandle object in ScanStartResult. Once a scan is started, ScanHandle::PendResult can be called to get scan results. Only 1 scan may be active at a time.

Parameters:

options – Options used to configure the scan session. These options are suggestions only, and the implementation may use different parameters to meet power or radio requirements.

Returns:

Returns a ScanHandle object if the scan successfully starts, or a ScanError otherwise. ScanHandle::PendResult can be called to get ScanResults for LE peers that satisfy the filters indicated in options. The initial results may report recently discovered peers. Subsequent results will be reported only when peers have been scanned or updated since the last call.

struct ScanFilter#

Filter parameters for use during a scan. A discovered peer only matches the filter if it satisfies all of the present filter parameters.

Public Members

std::optional<Uuid> service_uuid#

Filter based on advertised service UUID.

std::optional<Uuid> service_data_uuid#

Filter based on service data containing the given UUID.

std::optional<uint16_t> manufacturer_id#

Filter based on a manufacturer identifier present in the manufacturer data. If this filter parameter is set, then the advertising payload must contain manufacturer-specific data with the provided company identifier to satisfy this filter. Manufacturer identifiers can be found at Assigned Numbers.

std::optional<bool> connectable#

Filter based on whether or not a device is connectable. For example, a client that is only interested in peripherals that it can connect to can set this to true. Similarly a client can scan only for broadcasters by setting this to false.

std::optional<std::string_view> name#

Filter results based on a portion of the advertised device name. Substring matches are allowed. The name length must be at most pw::bluetooth::kMaxDeviceNameLength.

std::optional<int8_t> max_path_loss#

Filter results based on the path loss of the radio wave. A device that matches this filter must satisfy the following:

  1. Radio transmission power level and received signal strength must be available for the path loss calculation.

  2. The calculated path loss value must be less than, or equal to, max_path_loss.

Note

This field is calculated using the RSSI and TX Power information obtained from advertising and scan response data during a scan procedure. It should NOT be confused with information for an active connection obtained using the “Path Loss Reporting” feature.

class ScanHandle#

Represents an ongoing LE scan.

Public Types

using Ptr = internal::RaiiPtr<ScanHandle, &ScanHandle::Release>#

Movable ScanHandle smart pointer. The controller will continue scanning until the ScanHandle::Ptr is destroyed.

Public Functions

virtual ~ScanHandle() = default#

Stops the scan.

virtual async2::Poll<pw::Result<ScanResult>> PendResult(async2::Context &cx) = 0#

Returns the next ScanResult if available. Otherwise, invokes cx.waker() when a ScanResult is available. Only one waker is supported at a time.

Returns:

Code

Description

OK

ScanResult was returned.

CANCELLED

An internal error occurred and the scan was cancelled.

struct ScanOptions#

Parameters used during a scan.

Public Members

pw::span<const ScanFilter> filters#

List of filters for use during a scan. A peripheral that satisfies any of these filters will be reported. At least 1 filter must be specified. While not recommended, clients that require that all peripherals be reported can specify an empty filter. The span memory must only be valid until the call to Scan() ends.

uint16_t interval#

The time interval between scans.

  • Time = N * 0.625ms

  • Range: 0x0004 (2.5ms) - 10.24s (0x4000)

uint16_t window#

The duration of the scan. The window must be less than or equal to the interval.

  • Time = N * 0.625ms

  • Range: 0x0004 (2.5ms) - 10.24s (0x4000)

ScanType scan_type#

Specifies whether to send scan requests, and if so, what type of address to use in scan requests.

Phy phys = Phy::k1Megabit#

A bitmask of the PHYs to scan with. Only the 1Megabit and LeCoded PHYs are supported.

struct ScanResult#

Public Members

PeerId peer_id#

Uniquely identifies this peer on the current system.

bool connectable#

Whether or not this peer is connectable. Non-connectable peers are typically in the LE broadcaster role.

std::optional<uint8_t> rssi#

The last observed signal strength of this peer. This field is only present for a peer that is broadcasting. The RSSI can be stale if the peer has not been advertising.

Note

This field should NOT be confused with the “connection RSSI” of a peer that is currently connected to the system.

pw::multibuf::MultiBuf data#

This contains the advertising data last received from the peer.

std::optional<InlineString<22>> name#

The name of this peer. The name is often obtained during a scan procedure and can get updated during the name discovery procedure following a connection.

This field is present if the name is known.

chrono::SystemClock::time_point last_updated#

Timestamp of when the information in this ScanResult was last updated.

low_energy::Peripheral2#

class Peripheral2#

Represents the LE Peripheral role, which advertises and is connected to.

Subclassed by pw::bluetooth_sapphire::Peripheral

Public Types

enum class AdvertiseError#

Errors returned by Advertise.

Values:

enumerator kNotSupported#

The operation or parameters requested are not supported on the current hardware.

enumerator kAdvertisingDataTooLong#

The provided advertising data exceeds the maximum allowed length when encoded.

enumerator kScanResponseDataTooLong#

The provided scan response data exceeds the maximum allowed length when encoded.

enumerator kInvalidParameters#

The requested parameters are invalid.

enumerator kNotEnoughAdvertisingSlots#

The maximum number of simultaneous advertisements has already been reached.

enumerator kFailed#

Advertising could not be initiated due to a hardware or system error.

using ScanResponse = AdvertisingData#

The fields that are to be sent in a scan response packet. Clients may use this to send additional data that does not fit inside an advertising packet on platforms that do not support the advertising data length extensions.

If present, advertisements will be configured to be scannable.

using ConnectionOptions = Connection2::ConnectionOptions#

If present, the controller will broadcast connectable advertisements which allow peers to initiate connections to the Peripheral. The fields of ConnectionOptions will configure any connections set up from advertising.

Public Functions

virtual async2::OnceReceiver<AdvertiseResult> Advertise(const AdvertisingParameters &parameters) = 0#

Start advertising continuously as a LE peripheral. If advertising cannot be initiated then result_callback will be called with an error. Once started, advertising can be stopped by destroying the returned AdvertisedPeripheral::Ptr.

If the system supports multiple advertising, this may be called as many times as there are advertising slots. To reconfigure an advertisement, first close the original advertisement and then initiate a new advertisement.

Parameters:

parameters – Parameters used while configuring the advertising instance.

Returns:

Asynchronously returns a result once advertising has started or failed. On success, returns an AdvertisedPeripheral2 that models the lifetime of the advertisement. Destroying it will stop advertising.

struct AdvertisingIntervalRange#

The range of the time interval between advertisements. Shorter intervals result in faster discovery at the cost of higher power consumption. The exact interval used is determined by the Bluetooth controller.

  • Time = N * 0.625ms.

  • Time range: 0x0020 (20ms) - 0x4000 (10.24s)

Public Members

uint16_t min = 0x0800#

Default: 1.28s.

uint16_t max = 0x0800#

Default: 1.28s.

struct AdvertisingParameters#

Represents the parameters for configuring advertisements.

Public Members

AdvertisingData data#

The fields that will be encoded in the data section of advertising packets.

AdvertisingIntervalRange interval_range#

See AdvertisingIntervalRange documentation.

std::optional<Address::Type> address_type#

The type of address to include in advertising packets. If null, the host stack will select an address type. If the address type could not be used (either because of controller error or host configuration), a kFailed error wil be returned.

AdvertisingProcedure procedure#

Specifies the advertising procedure to use and the parameters specific to that procedure.

struct ExtendedAdvertising#

Advertise using the newer extended advertising Protocol Data Unit (PDU), which aren’t supported by older devices.

Public Members

std::variant<std::monostate, ScanResponse, ConnectionOptions, Anonymous> configuration#

Extended advertisements can have a scan response, be connectable, be anonymous, or none of the above. See ScanResponse, ConnectionOptions, and Anonymous documentation.

std::optional<int8_t> tx_power#

The maximum power level to transmit with. Null indicates no preference. Range: -127 to +20 Units: dBm

Phy primary_phy = Phy::k1Megabit#

The primary physical layer configuration to advertise with. Can only be 1Megabit or LeCoded PHY. If the PHY is not supported, a kNotSupported error will be returned.

Phy secondary_phy = Phy::k1Megabit#

The secondary physical layer configuration to advertise with. Can be any PHY. If the PHY is not supported, a kNotSupported error will be returned.

struct Anonymous#

Anonymous advertisements do not include the address.

struct LegacyAdvertising#

Use legacy advertising PDUs. Use this if you need compatibility with old devices.

Public Members

std::optional<ScanResponse> scan_response#

See ScanResponse documentation.

std::optional<ConnectionOptions> connection_options#

See ConnectionOptions documentation.

low_energy::AdvertisedPeripheral2#

class AdvertisedPeripheral2#

AdvertisedPeripheral instances are valid for the duration of advertising.

Public Types

using Ptr = internal::RaiiPtr<AdvertisedPeripheral2, &AdvertisedPeripheral2::Release>#

Movable AdvertisedPeripheral2 smart pointer. The peripheral will continue advertising until the returned AdvertisedPeripheral::Ptr is destroyed.

Public Functions

virtual async2::Poll<Connection2::Ptr> PendConnection(async2::Context &cx) = 0#

For connectable advertisements, this method returns Ready when an LE central connects to the advertisement.

The returned Connection2 can be used to interact with the peer. It also represents a peripheral’s ownership over the connection: the client can drop the object to request a disconnection. Similarly, the Connection2 error handler is called by the system to indicate that the connection to the peer has been lost. While connections are exclusive among peripherals, they may be shared with centrals, preventing disconnections.

After a connection is returned, advertising will be paused until PendConnection() is called again. This method may return multiple connections over the lifetime of an advertisement.

Parameters:

cx – Awoken when a connection is established.

virtual void StopAdvertising() = 0#

Requests that advertising be stopped. PendStop() can be used to wait for advertising to stop (e.g. before starting another advertisement). Destroying this object will also stop advertising, but there will be no way to determine when advertising has stopped. This method is idempotent.

virtual async2::Poll<pw::Status> PendStop(async2::Context &cx) = 0#

Returns Ready when advertising has stopped due to a call to StopAdvertising() or due to error. Awakens cx on stopping.

Returns:

Code

Description

OK

Advertising was stopped successfully after a call to StopAdvertising().

CANCELLED

An internal error occurred and the advertisement was cancelled.

low_energy::Connection2#

class Connection2#

Class that represents a connection to a peer. This can be used to interact with GATT services and establish LE L2CAP channels.

The lifetime of this object is tied to that of the LE connection it represents. Destroying the object results in a disconnection.

Public Types

enum class ConnectionParameterUpdateError : uint8_t#

Possible errors when updating the connection parameters.

Values:

enumerator kFailure#
enumerator kInvalidParameters#
enumerator kRejected#
enum class DisconnectReason : uint8_t#

Possible reasons a connection was disconnected.

Values:

enumerator kFailure#
enumerator kRemoteUserTerminatedConnection#
enumerator kConnectionTimeout#

This usually indicates that the link supervision timeout expired.

using Ptr = internal::RaiiPtr<Connection2, &Connection2::Disconnect>#

Movable Connection2 smart pointer. When Connection::Ptr is destroyed the Connection2 will disconnect automatically.

Public Functions

virtual ~Connection2() = default#

If a disconnection has not occurred, destroying this object will result in disconnection.

virtual async2::Poll<DisconnectReason> PendDisconnect(async2::Context &cx) = 0#

Returns Ready after the peer disconnects or there is a connection error that caused a disconnection. Awakens cx on disconnect.

virtual gatt::Client2 *GattClient() = 0#

Returns a GATT client to the connected peer that is valid for the lifetime of this Connection2 object. Connection2 is considered alive as long as PendDisconnect() returns pending and the object hasn’t been destroyed.

virtual uint16_t AttMtu() = 0#

Returns the current ATT Maximum Transmission Unit. By subtracting ATT headers from the MTU, the maximum payload size of messages can be calculated.

virtual async2::Poll<uint16_t> PendAttMtuChange(async2::Context &cx) = 0#

Returns Pending until the ATT MTU changes, at which point cx will be awoken. Returns Ready with the new ATT MTU once the ATT MTU has been changed. The ATT MTU can only be changed once.

virtual ConnectionParameters Parameters() = 0#

Returns the current connection parameters.

virtual async2::OnceReceiver<pw::expected<void, ConnectionParameterUpdateError>> RequestParameterUpdate(
RequestedConnectionParameters parameters,
) = 0#

Requests an update to the connection parameters.

Returns:

Asynchronously returns the result of the request.

virtual async2::OnceReceiver<pw::Result<Channel::Ptr>> ConnectL2cap(ConnectL2capParameters parameters) = 0#

Connect to an L2CAP LE connection-oriented channel.

Parameters:

parameters – The parameters to configure the channel with.

Returns:

The result of the connection procedure. On success, contains a Channel that can be used to exchange data.

struct ConnectionOptions#

Represents parameters that are set on a per-connection basis.

Public Members

bool bondable_mode = true#

When true, the connection operates in bondable mode. This means pairing will form a bond, or persist across disconnections, if the peer is also in bondable mode. When false, the connection operates in non-bondable mode, which means the local device only allows pairing that does not form a bond.

std::optional<Uuid> service_filter#

When present, service discovery performed following the connection is restricted to primary services that match this field. Otherwise, by default all available services are discovered.

std::optional<RequestedConnectionParameters> parameters#

When present, specifies the initial connection parameters. Otherwise, the connection parameters will be selected by the implementation.

std::optional<uint16_t> att_mtu#

When present, specifies the ATT MTU to request. The actual MTU used may be smaller depending on peer and controller support. If none is specified, the host implementation will select the ATT MTU. Note that an MTU of 247 is the largest that can fit into a single LE data packet with the Data Length Extension.

  • LE ATT MTU Range: 23 to 517

  • LE EATT MTU Range: 64 to 517

struct ConnectionParameters#

Actual connection parameters returned by the controller.

Public Members

uint16_t interval#

The connection interval indicates the frequency of link layer connection events over which data channel PDUs can be transmitted. See Core Spec v6, Vol 6, Part B, Section 4.5.1 for more information on the link layer connection events.

  • Range: 0x0006 to 0x0C80

  • Time: N * 1.25 ms

  • Time Range: 7.5 ms to 4 s.

uint16_t latency#

The maximum allowed peripheral connection latency in number of connection events. See Core Spec v5, Vol 6, Part B, Section 4.5.1.

  • Range: 0x0000 to 0x01F3

uint16_t supervision_timeout#

This defines the maximum time between two received data packet PDUs before the connection is considered lost. See Core Spec v6, Vol 6, Part B, Section 4.5.2.

  • Range: 0x000A to 0x0C80

  • Time: N * 10 ms

  • Time Range: 100 ms to 32 s

struct ConnectL2capParameters#

Public Members

Psm psm#

The identifier of the service to connect to.

uint16_t max_receive_packet_size#

Maximum supported packet size for receiving.

SecurityRequirements security_requirements#

The security requirements that must be met before data is exchanged on the channel. If the requirements cannot be met, channel establishment will fail.

struct RequestedConnectionParameters#

Connection parameters that either the local device or a peer device are requesting.

Public Members

uint16_t min_interval#

Minimum value for the connection interval. This shall be less than or equal to max_interval. The connection interval indicates the frequency of link layer connection events over which data channel PDUs can be transmitted. See Core Spec v6, Vol 6, Part B, Section 4.5.1 for more information on the link layer connection events.

  • Range: 0x0006 to 0x0C80

  • Time: N * 1.25 ms

  • Time Range: 7.5 ms to 4 s.

uint16_t max_interval#

Maximum value for the connection interval. This shall be greater than or equal to min_interval. The connection interval indicates the frequency of link layer connection events over which data channel PDUs can be transmitted. See Core Spec v6, Vol 6, Part B, Section 4.5.1 for more information on the link layer connection events.

  • Range: 0x0006 to 0x0C80

  • Time: N * 1.25 ms

  • Time Range: 7.5 ms to 4 s.

uint16_t max_latency#

Maximum peripheral latency for the connection in number of connection events. See Core Spec v6, Vol 6, Part B, Section 4.5.1.

  • Range: 0x0000 to 0x01F3

uint16_t supervision_timeout#

This defines the maximum time between two received data packet PDUs before the connection is considered lost. See Core Spec v6, Vol 6, Part B, Section 4.5.2.

  • Range: 0x000A to 0x0C80

  • Time: N * 10 ms

  • Time Range: 100 ms to 32 s

low_energy::Channel#

class Channel : public pw::channel::Channel<kReliable, kReadable, kWritable>#

A duplex datagram channel that models the lifetime of a connection-oriented channel. Closing or destroying Channel will close the underlying channel.

Public Functions

virtual uint16_t max_transmit_size() = 0#

Maximum payload size (SDU) that the peer supports receiving.

virtual uint16_t max_receive_size() = 0#

Maximum payload size (SDU) that this channel supports receiving.

low_energy::ChannelListenerRegistry#

class ChannelListenerRegistry#

Public Functions

virtual async2::OnceReceiver<pw::Result<ChannelListener::Ptr>> ListenL2cap(ListenParameters parameters) = 0#

Register a listener for incoming channels. The registry will assign a protocol/service multiplexer value that is unique for the local device, as well as create a ChannelListener for accepting incoming channels. In the unlikely event that all PSMs have been assigned, this call will fail with RESOURCE_EXHAUSTED.

Note that the method of service discovery or advertising is defined by the service or protocol, so it is the responsibility of the caller to update the GATT database or other service discovery mechanism.

Parameters:

parameters – Parameters for the local side of the channel.

Returns:

The result of starting the listener. On success, contains a ChannelListener that can be used to receive new channels.

struct ListenParameters#

The parameters to use for incoming channels.

Public Members

uint16_t max_receive_size#

Maximum supported payload size (SDU) for receiving.

SecurityRequirements security_requirements#

The security requirements that must be met before data is exchanged on the channel. If the requirements cannot be met, channel establishment will fail.

low_energy::ChannelListener#

class ChannelListener#

Represents a service or protocol that accepts incoming channels for a PSM. Destroying this object will cease accepting any incoming channels, but existing established channels will not be affected. Additionally, once this object is destroyed the implementation is free to reuse the PSM that was previously assigned for this instance.

Public Functions

virtual async2::Poll<Channel::Ptr> PendChannel(async2::Waker &&waker) = 0#

Poll to receive incoming channels.

virtual Psm psm() = 0#

The protocol/service multiplexer for this listener.

gatt::Server2#

class Server2#

Interface for a GATT server that serves many GATT services.

Public Types

using PublishServiceResult = pw::expected<LocalService2::Ptr, PublishServiceError>#

The Result passed by PublishService.

Public Functions

virtual async2::OnceReceiver<PublishServiceResult> PublishService(
const LocalServiceInfo &info,
LocalServiceDelegate2 &delegate,
) = 0#

Publishes the service defined by info and implemented by delegate so that it is available to all remote peers.

The caller must assign distinct handles to the characteristics and descriptors listed in info per call to PublishService (handles can be reused across calls). These identifiers will be used in requests sent to delegate.

Returns:

On success, a LocalService::Ptr is returned via result_sender. When the LocalService::Ptr is destroyed or an error occurs (LocalServiceDelegate.OnError), the service will be unpublished.

struct LocalServiceInfo#

Parameters for registering a local GATT service.

Public Members

ServiceHandle handle#

A unique (within a Server) handle identifying this service.

bool primary#

Indicates whether this is a primary or secondary service.

Uuid type#

The UUID that identifies the type of this service. There may be multiple services with the same UUID.

span<const Characteristic2> characteristics#

The characteristics of this service.

span<const ServiceHandle> includes#

Handles of other services that are included by this service. The included services need to already be published.

gatt::LocalService2#

class LocalService2#

Interface provided by the backend to interact with a published service. LocalService is valid for the lifetime of a published GATT service. It is used to control the service and send notifications/indications.

Public Types

using ValueChangedResult = pw::expected<void, Error>#

The Result type for a ValueChanged indication or notification message. The error can be locally generated for notifications and either locally or remotely generated for indications.

using Ptr = internal::RaiiPtr<LocalService2, &LocalService2::UnpublishService>#

Movable LocalService smart pointer. When the LocalService::Ptr object is destroyed the service will be unpublished.

Public Functions

virtual async2::OnceReceiver<ValueChangedResult> NotifyValue(ValueChangedParameters &&parameters) = 0#

Sends a notification to peers. Notifications should be used instead of indications when the service does not require peer confirmation of the update.

Notifications should not be sent to peers which have not enabled notifications on a particular characteristic or that have disconnected. If notifications are sent, they will not be propagated and the result_sender will be set to an error condition. The Bluetooth stack will track this configuration for the lifetime of the service.

The maximum size of the parameters.value field depends on the Maximum Transmission Unit (MTU) negotiated with the peer. A 3-byte header plus the value contents must fit in a packet of MTU bytes.

Parameters:

parameters – The parameters associated with the changed characteristic.

Returns:

The result is returned when the notification has been sent to all peers or an error is produced when trying to send the notification to any of the peers. This value is only set only once when all associated work is done, if the implementation wishes to receive a call on a per-peer basis, they should send this event with a single PeerId in parameters.peer_ids. Additional values should not be notified until this notification completes.

virtual async2::OnceReceiver<ValueChangedResult> IndicateValue(ValueChangedParameters &&parameters) = 0#

Sends an indication to peers. Indications should be used instead of notifications when the service does require peer confirmation of the update.

Indications should not be sent to peers which have not enabled indications on a particular characteristic. If they are sent, they will not be propagated and the result_sender will be set to an error condition. The Bluetooth stack will track this configuration for the lifetime of the service.

If any of the peers in parameters.peer_ids fails to confirm the indication within the ATT transaction timeout (30 seconds per Bluetooth 6.0 Vol. 3 Part F 3.3.3), the link between the peer and the local adapter will be closed.

The maximum size of the parameters.value field depends on the MTU negotiated with the peer. A 3-byte header plus the value contents must fit in a packet of MTU bytes.

Parameters:

parameters – The parameters associated with the changed characteristic.

Returns:

When all the peers listed in parameters.peer_ids have confirmed the indication, the result is returned. If the implementation wishes to receive indication confirmations on a per-peer basis, they should send this event with a single PeerId in parameters.peer_ids. Additional values should not be indicated until this procedure completes.

struct ValueChangedParameters#

The parameters used to signal a characteristic value change from a LocalService to a peer.

Public Members

pw::span<const PeerId> peer_ids#

The peers peers to signal. The LocalService should respect the Characteristic Configuration associated with a peer+handle when deciding whether to signal it. If empty, all peers which configured the handle are signalled.

Handle handle#

The handle of the characteristic value being signaled.

multibuf::MultiBuf value#

The new value for the descriptor/characteristic.

gatt::LocalServiceDelegate2#

class LocalServiceDelegate2#

Interface for serving a local GATT service. This is implemented by the API client.

Public Functions

virtual void OnError(Error error) = 0#

Called when there is a fatal error related to this service that forces the service to close. LocalServiceDelegate methods will no longer be called. This invalidates the associated LocalService. It is OK to destroy both LocalServiceDelegate and the associated LocalService::Ptr from within this method.

virtual void CharacteristicConfiguration(PeerId peer_id, Handle handle, bool notify, bool indicate) = 0#

This notifies the current configuration of a particular characteristic/descriptor for a particular peer. It will be called when the peer GATT client changes the configuration.

The Bluetooth stack maintains the state of each peer’s configuration across reconnections. As such, this method will be called with both notify and indicate set to false for each characteristic when a peer disconnects. Also, when a peer reconnects this method will be called again with the persisted state of the newly-connected peer’s configuration. However, clients should not rely on this state being persisted indefinitely by the Bluetooth stack.

Parameters:
  • peer_id – The PeerId of the GATT client associated with this particular CCC.

  • handle – The handle of the characteristic associated with the notify and indicate parameters.

  • notify – True if the client has enabled notifications, false otherwise.

  • indicate – True if the client has enabled indications, false otherwise.

virtual async2::OnceReceiver<pw::expected<multibuf::MultiBuf, Error>> ReadValue(
PeerId peer_id,
Handle handle,
uint32_t offset,
) = 0#

Called when a peer requests to read the value of a characteristic or descriptor. It is guaranteed that the peer satisfies the permissions associated with this attribute.

Parameters:
  • peer_id – The PeerId of the GATT client making the read request.

  • handle – The handle of the requested descriptor/characteristic.

  • offset – The offset at which to start reading the requested value.

Returns:

Returns the value of the characteristic on success, or an Error on failure. The value will be truncated to fit in the MTU if necessary.

virtual async2::OnceReceiver<pw::expected<void, Error>> WriteValue(
PeerId peer_id,
Handle handle,
uint32_t offset,
multibuf::MultiBuf &&value,
) = 0#

Called when a peer issues a request to write the value of a characteristic or descriptor. It is guaranteed that the peer satisfies the permissions associated with this attribute.

Parameters:
  • peer_id – The PeerId of the GATT client making the write request.

  • handle – The handle of the requested descriptor/characteristic.

  • offset – The offset at which to start writing the requested value. If the offset is 0, any existing value should be overwritten by the new value. Otherwise, the existing value between offset:(offset + len(value)) should be changed to value.

  • value – The new value for the descriptor/characteristic.

Returns:

The result of the write.

virtual void MtuUpdate(PeerId peer_id, uint16_t mtu) = 0#

Called when the MTU of a peer is updated. Also called for peers that are already connected when the server is published.

Notifications and indications must fit in a single packet including both the 3-byte notification/indication header and the user-provided payload. If these are not used, the MTU can be safely ignored as it is intended for use cases where the throughput needs to be optimized.

gatt::Client2#

class Client2#

Represents a GATT client that interacts with services on a GATT server.

Public Functions

virtual async2::Poll<RemoteServiceInfo> PendServiceUpdate(async2::Context &cx)#

Enumerates existing services found on the peer that this object represents and notifies of modifications to services or new services thereafter. If service discovery hasn’t happened yet, it may be started. To further interact with services, clients must obtain a RemoteService2 protocol by calling ConnectToService.

Returns:

Will return Ready with RemoteServiceInfo when there are services that are updated/modified. This can can be called repeatedly until Pending is returned to get all previously discovered services.

virtual async2::Poll<ServiceHandle> PendServiceRemoved(async2::Context &cx)#

Returns the handles of services that have been removed. Note that handles may be reused, so it is recommended to check for removed services before calling PendServiceUpdate. This should be called repeatedly until Pending is returned.

Parameters:

cx – Awoken when a service is removed after Pending is returned.

virtual pw::expected<RemoteService2::Ptr, Error> ConnectToService(ServiceHandle handle) = 0#

Connects to a RemoteService2. Only 1 connection per service is allowed.

Parameters:

handle – The handle of the service to connect to.

Returns:

kInvalidParameters handle does not correspond to a known service.

struct RemoteServiceInfo#

Represents a remote GATT service.

Public Members

ServiceHandle handle#

Uniquely identifies this GATT service.

bool primary#

Indicates whether this is a primary or secondary service.

Uuid type#

The UUID that identifies the type of this service. There may be multiple services with the same UUID.

gatt::RemoteService2#

class RemoteService2#

An interface for interacting with a GATT service on a peer device.

Public Types

enum class WriteMode : uint8_t#

Represents the supported write modes for writing characteristics & descriptors to the server.

Values:

enumerator kDefault#

Wait for a response from the server before returning but do not verify the echo response. Supported for both characteristics and descriptors.

enumerator kReliable#

Every value blob is verified against an echo response from the server. The procedure is aborted if a value blob has not been reliably delivered to the peer. Only supported for characteristics.

enumerator kWithoutResponse#

Delivery will not be confirmed before returning. Writing without a response is only supported for short characteristics with the WRITE_WITHOUT_RESPONSE property. The value must fit into a single message. It is guaranteed that at least 20 bytes will fit into a single message. If the value does not fit, a kFailure error will be produced. The value will be written at offset 0. Only supported for characteristics.

using Ptr = internal::RaiiPtr<RemoteService2, &RemoteService2::Disconnect>#

Movable RemoteService2 smart pointer. The remote server will remain connected until the returned RemoteService2::Ptr is destroyed.

Public Functions

virtual async2::Poll<RemoteServiceError> PendError(async2::Context &cx) = 0#

Poll for an Error status on this service, waking cx and returning Ready when there is an error condition. When an error condition is present, any previous RemoteService2 Waker and OnceReceiver instances may or may not be woken and all other methods will be no-ops. Only 1 waker can be set at a time (additional calls will replace the existing waker).

virtual void DiscoverCharacteristics(async2::OnceRefSender<Vector<Characteristic2>> characteristics_sender) = 0#

Asynchronously sends the characteristics in this service, up to Vector::.max_size(). May perform service discovery if the characteristics are not yet known.

virtual async2::OnceReceiver<pw::expected<Vector<ReadByTypeResult, 5>, Error>> ReadByType(Uuid uuid) = 0#

Reads characteristics and descriptors with the specified type. This method is useful for reading values before discovery has completed, thereby reducing latency.

This may fail with the following errors:

  • kInvalidParameters: if uuid refers to an internally reserved descriptor type (e.g. the Client Characteristic Configuration descriptor).

  • kTooManyResults: More results were read than can fit in the Vector. Consider reading characteristics/descriptors individually after performing discovery.

  • kFailure: The server returned an error not specific to a single result.

Parameters:

uuid – The UUID of the characteristics/descriptors to read.

Returns:

The result of the read. Results may be empty if no matching values are read. If reading a value results in a permission error, the handle and error will be included.

virtual async2::OnceReceiver<pw::expected<ReadValue, Error>> ReadCharacteristic(
Handle handle,
std::optional<LongReadOptions> options,
) = 0#

Reads the value of a characteristic.

Parameters:
  • handle – The handle of the characteristic to be read.

  • options – If null, a short read will be performed, which may be truncated to what fits in a single message (at least 22 bytes). If long read options are present, performs a long read with the indicated options.

Returns:

The result of the read and the value of the characteristic if successful. Returns the following errors:

  • kInvalidHandle handle is invalid.

  • kInvalidParameters options is invalid.

  • kReadNotPermitted The server rejected the request.

  • kInsufficient* The server rejected the request.

  • kApplicationError* An application error was returned by the GATT profile.

  • kFailure The server returned an error not covered by the above.

virtual async2::OnceReceiver<pw::expected<void, Error>> WriteCharacteristic(
Handle handle,
pw::multibuf::MultiBuf &&value,
WriteOptions options,
) = 0#

Writes value to the characteristic with handle using the provided options.

Parameters:
  • handle – Handle of the characteristic to be written to

  • value – The value to be written.

  • options – Options that apply to the write.

Returns:

A result is returned when a response to the write is received. For WriteWithoutResponse, this is set as soon as the write is sent. Returns the following errors:

  • kInvalidHandle handle is invalid.

  • kInvalidParameters`options is invalid.

  • kWriteNotPermitted The server rejected the request.

  • kInsufficient* The server rejected the request.

  • kApplicationError* An application error was returned by the GATT profile.

  • kFailure The server returned an error not covered by the above errors.

virtual async2::OnceReceiver<pw::expected<ReadValue, Error>> ReadDescriptor(
Handle handle,
std::optional<LongReadOptions> options,
) = 0#

Reads the value of the characteristic descriptor with handle and returns it in the reply.

Parameters:
  • handle – The descriptor handle to read.

  • options – Options that apply to the read.

  • result_sender – Set to a result containing the value of the descriptor on success.

Return values:
  • kInvalidHandlehandle is invalid.

  • kInvalidParametersoptions is invalid.

  • kReadNotPermitted

  • kInsufficient* – The server rejected the request.

  • kApplicationError* – An application error was returned by the GATT profile.

  • kFailure – The server returned an error not covered by the above errors.

virtual async2::OnceReceiver<pw::expected<void, Error>> WriteDescriptor(Handle handle, pw::multibuf::MultiBuf &&value) = 0#

Writes value to the descriptor with handle using the provided. It is not recommended to send additional writes while a write is already in progress.

Parameters:
  • handle – Handle of the descriptor to be written to.

  • value – The value to be written.

Returns:

The result upon completion of the write. Possible errors:

  • kInvalidHandle handle is invalid.

  • kInvalidParameters `options is invalid.

  • kWriteNotPermitted The server rejected the request.

  • kInsufficient* The server rejected the request.

  • kApplicationError* An application error was returned by the GATT profile.

  • kFailure The server returned an error not covered by the above errors.

virtual async2::OnceReceiver<pw::expected<void, Error>> EnableNotifications(Handle handle) = 0#

Subscribe to notifications & indications from the characteristic with the given handle.

Either notifications or indications will be enabled depending on characteristic properties. Indications will be preferred if they are supported. This operation fails if the characteristic does not have the “notify” or “indicate” property.

A write request will be issued to configure the characteristic for notifications/indications if it contains a Client Characteristic Configuration (CCC) descriptor. This method fails if an error occurs while writing to the descriptor.

On success, PendNotification will return Ready when the peer sends a notification or indication. Indications are automatically confirmed.

Subscriptions can be canceled with StopNotifications.

Parameters:

handle – the handle of the characteristic to subscribe to.

Returns:

The result of enabling notifications/indications.

  • kFailure The characteristic does not support notifications or indications.

  • kInvalidHandle handle is invalid.

  • kWriteNotPermitted CCC descriptor write error.

  • kInsufficient* Insufficient security properties to write to CCC descriptor.

virtual async2::Poll<ReadValue> PendNotification(Handle handle, async2::Context &cx) = 0#

After notifications have been enabled with EnableNotifications, this method can be used to check for notifications. This method will safely return Pending when notifications are disabled.

Parameters:
  • handle – The handle of the characteristic to await for notifications.

  • cx – The Context to awaken when a notification is available. Only one Waker per handle is supported at a time (subsequent calls will overwrite the old Waker).

virtual async2::OnceReceiver<pw::expected<void, Error>> StopNotifications(Handle handle) = 0#

Stops notifications for the characteristic with the given handle.

Returns:

The result of disabling notifications/indications. Possible errors:

  • kFailure The characteristic does not support notifications or indications.

  • kInvalidHandle handle is invalid.

  • kWriteNotPermitted CCC descriptor write error.

  • Insufficient* CCC descriptor write error.

struct LongReadOptions#

Represents the supported options to read a long characteristic or descriptor value from a server. Long values are those that may not fit in a single message.

Public Members

uint16_t offset = 0#

The byte to start the read at. Must be less than the length of the value.

uint16_t max_bytes = kMaxValueLength#

The maximum number of bytes to read.

struct ReadByTypeResult#

A result returned by ReadByType.

Public Members

Handle handle#

Characteristic or descriptor handle.

pw::expected<ReadValue, Error> result#

The value of the characteristic or descriptor, if it was read successfully, or an error explaining why the value could not be read.

struct ReadValue#

Wrapper around a possible truncated value received from the server.

Public Members

Handle handle#

Characteristic or descriptor handle.

multibuf::MultiBuf value#

The value of the characteristic or descriptor.

bool maybe_truncated#

True if value might be truncated (the buffer was completely filled by the server and the read was a short read). ReadCharacteristic or ReadDescriptor should be used to read the complete value.

struct WriteOptions#

Represents the supported options to write a characteristic/descriptor value to a server.

Public Members

WriteMode mode = WriteMode::kDefault#

The mode of the write operation. For descriptors, only WriteMode::kDefault is supported

uint16_t offset = 0#

Request a write starting at the byte indicated. Must be 0 if mode is WriteMode.kWithoutResponse.

Controller2#

class Controller2 : public pw::channel::Implement<pw::channel::ReliableDatagramReaderWriter>#

The Controller class is a shim for communication between the Host and the Controller. Controller is a pw::Channel used to send and receive HCI packets. The first byte of each datagram is a UART packet indicator (H4PacketType Emboss enum).

Public Types

enum class FeaturesBits : uint32_t#

Bitmask of features the controller supports.

Values:

enumerator kHciSco#

Indicates support for transferring Synchronous Connection-Oriented (SCO) link data over the HCI. Offloaded SCO links may still be supported even if HCI SCO isn’t.

enumerator kSetAclPriorityCommand#

Indicates support for the Set Acl Priority command.

enumerator kAndroidVendorExtensions#

Indicates support for the LE_Get_Vendor_Capabilities command documented at HCI requirements.

Public Functions

virtual async2::Poll<Status> PendError(async2::Context &cx) = 0#

Returns Ready when fatal errors occur after initialization. After a fatal error, this object is invalid.

virtual async2::OnceReceiver<Status> Initialize() = 0#

Initializes the controller interface and starts processing packets. Asynchronously returns the result of initialization.

On success, HCI packets may now be sent and received with this object.

virtual async2::OnceReceiver<Status> ConfigureSco(
ScoCodingFormat coding_format,
ScoEncoding encoding,
ScoSampleRate sample_rate,
) = 0#

Configure the HCI for a SCO connection with the indicated parameters.

Returns:

  • OK - success, packets can be sent/received.

  • UNIMPLEMENTED - the implementation/controller does not support SCO over HCI

  • ALREADY_EXISTS - a SCO connection is already configured

  • INTERNAL - an internal error occurred

virtual async2::OnceReceiver<Status> ResetSco() = 0#

Releases the resources held by an active SCO connection. This should be called when a SCO connection is closed. No-op if no SCO connection is configured.

Returns:

  • OK - success, the SCO configuration was reset.

  • UNIMPLEMENTED - the implementation/controller does not support SCO over

  • HCI INTERNAL - an internal error occurred

virtual async2::OnceReceiver<FeaturesBits> GetFeatures() = 0#
Returns:

A bitmask of features supported by the controller.

virtual async2::OnceReceiver<Result<multibuf::MultiBuf>> EncodeVendorCommand(VendorCommandParameters parameters) = 0#

Encode the vendor-specific HCI command for a generic type of vendor command, and return the encoded command in a buffer.

Parameters:

parameters – Vendor command to encode.

Returns:

Returns the result of the encoding request. On success, contains the command buffer.

Module Configuration Options#

The following configurations can be adjusted via compile-time configuration of this module, see the module documentation for more details.

PW_BLUETOOTH_DESCRIPTOR_VEC_SIZE#

Controls the size of Vector<Descriptor> in the above APIs.

Emboss Packet Definitions#

pw_bluetooth contains Emboss packet definitions. So far, some packets and enums from the following protocols are defined:

  • HCI

  • L2CAP

  • H4

Usage#

  1. Add Emboss to your project as described in Emboss.

  1. Add the Emboss target you require to your build target’s dependency list - for example, @pigweed//pw_bluetooth:emboss_hci_group for HCI packets.

  1. Add the Emboss target you require to your build target’s dependency list - for example, $dir_pw_bluetooth/emboss_hci_group for HCI packets.

  1. Add the Emboss target you require to your build target’s dependency list - for example, pw_bluetooth.emboss_hci_group for HCI packets.

  1. Include the generated header files you require in your source files.

#include "pw_bluetooth/hci_commands.emb.h"
#include "pw_bluetooth/hci_android.emb.h"
  1. Construct an Emboss view over a buffer as described in the Emboss User Guide.

  std::array<uint8_t, 4> buffer = {0x00, 0x01, 0x02, 0x03};
  auto view = emboss::MakeTestCommandPacketView(&buffer);
  EXPECT_TRUE(view.IsComplete());
  EXPECT_EQ(view.payload().Read(), 0x03);

Note

When you first add generated headers, clangd may complain that the generated header files do not exist. You need to build your project to resolve this. Similarly, you need to rebuild in order for .emb file updates to be reflected in the generated headers.

Contributing#

Emboss .emb files can be edited to add additional packets and enums.

Emboss files should be formatted by running the following from the Pigweed root.

(export EMBOSS_PATH="environment/packages/emboss" &&
    export PYTHONPATH+=":${EMBOSS_PATH}" &&
    python3 "${EMBOSS_PATH}/compiler/front_end/format.py" pw_bluetooth/public/pw_bluetooth/*.emb)

If adding files, be sure to update the GN, Bazel, and CMake build rules. Presubmit runs the emboss_test.cc test on all three.

Size Report#

Delta of +972 when constructing the first packet view and reading/writing a field. This includes the runtime library and the 4-byte buffer.

Label

Segment

Delta

Make view and write field

FLASH

-4

quorem

+56

main

NEW

+44

emboss::prelude::UIntView<>::TryToWrite<>()

NEW

+44

emboss::support::ContiguousBuffer<>::GetOffsetStorage<>()

NEW

+24

emboss::support::ContiguousBuffer<>::ReadLittleEndianUInt<>()

NEW

+24

pw::bluetooth::emboss::GenericTestCommandPacketView<>::payload()

NEW

+22

emboss::support::ContiguousBuffer<>::WriteLittleEndianUInt<>()

NEW

+14

emboss::prelude::UIntView<>::Write<>()

+224

Delta of +96 when adding a second packet view and reading/writing a field.

Label

Segment

Delta

Size difference when adding a second view

FLASH

+4

quorem

+40

main

+46

emboss::support::ContiguousBuffer<>::GetOffsetStorage<>()

-2

emboss::support::ContiguousBuffer<>::ReadLittleEndianUInt<>()

NEW

+24

pw::bluetooth::emboss::GenericTestEventPacketView<>::payload()

+112

Roadmap#

  • Bluetooth Proxy (WIP in in pw_bluetooth_proxy)

  • Add automated Emboss file formatting to pw format (b/331195584)

  • Create a backend for the Bluetooth API using Fuchsia’s Bluetooth stack (Sapphire).

  • Stabilize the Bluetooth API.

  • Add BR/EDR APIs.