pub struct BleDriver<'ble, S = ()> { /* private fields */ }Expand description
The NimBLE host handle and primary entrypoint to BLE.
It is role-agnostic: the type parameter S is the GATT-server service table, defaulting to
() (no server). A central or broadcaster uses new (S = ()); a GATT server
uses new_with_services, whose S: Deref<Target = [ble_gatt_svc_def]>
owns the table and keeps it alive — drop order guarantees nimble_port_deinit (in Drop) runs
before the table field is freed, so NimBLE never sees a dangling pointer.
The GAP / GATT-server / GATT-client operations are grouped into separate impl blocks
(gap.rs, gatt/gatts.rs, and — later — the client), each mirroring a NimBLE subsystem; the
GATT ones are #[cfg]-gated on the corresponding Kconfig. start takes &self (interior
started-flag).
Implementations§
Source§impl<'d, S> BleDriver<'d, S>
GAP operations on the BleDriver: advertising, the device name, and GAP event
subscription. Available for any role (S). &self, so callable re-entrantly from within the
GAP event callback.
impl<'d, S> BleDriver<'d, S>
GAP operations on the BleDriver: advertising, the device name, and GAP event
subscription. Available for any role (S). &self, so callable re-entrantly from within the
GAP event callback.
Sourcepub fn set_device_name(&self, name: &str) -> Result<(), BleError>
pub fn set_device_name(&self, name: &str) -> Result<(), BleError>
Set the device name exposed via the GAP service.
Sourcepub fn gap_subscribe<F>(&self, callback: F)where
F: FnMut(GapEvent) -> i32 + Send + 'static,
pub fn gap_subscribe<F>(&self, callback: F)where
F: FnMut(GapEvent) -> i32 + Send + 'static,
Subscribe to GAP events (connect / disconnect / subscribe / MTU / notify-tx / — for a
client — notify-rx). The callback runs on the NimBLE host task and returns the GAP status
code (0 on success). The trampoline is wired at adv_start (server) or
at connect time (client), so this only needs to be set before whichever of those you use.
Sourcepub unsafe fn gap_subscribe_nonstatic<F>(&self, callback: F)where
F: FnMut(GapEvent) -> i32 + Send + 'd,
pub unsafe fn gap_subscribe_nonstatic<F>(&self, callback: F)where
F: FnMut(GapEvent) -> i32 + Send + 'd,
§Safety
The non-'static counterpart of gap_subscribe: the callback may
borrow data that lives as long as the BleDriver. It stays registered until the driver is
dropped, which un-subscribes it, so the driver must not be core::mem::forget-ten. See
BleDriver::host_subscribe_nonstatic.
Sourcepub fn gap_unsubscribe(&self)
pub fn gap_unsubscribe(&self)
Stop delivering GAP events to the subscribed callback.
Sourcepub fn adv_set_data(&self, data: &[u8]) -> Result<(), BleError>
pub fn adv_set_data(&self, data: &[u8]) -> Result<(), BleError>
Set the raw advertising payload.
Sourcepub fn adv_set_fields(&self, fields: &BleAdvFields<'_>) -> Result<(), BleError>
pub fn adv_set_fields(&self, fields: &BleAdvFields<'_>) -> Result<(), BleError>
Encode fields into the advertising payload.
Sourcepub fn adv_start(
&self,
own_addr_type: u8,
params: &BleAdvParams,
) -> Result<(), BleError>
pub fn adv_start( &self, own_addr_type: u8, params: &BleAdvParams, ) -> Result<(), BleError>
Start a legacy advertising procedure. Drive this from an host_subscribe closure once the host
has synced, and restart it from a GapEvent::Disconnect handler. Events for the
resulting connection are delivered to the gap_subscribe callback.
pub fn adv_stop(&self) -> Result<(), BleError>
Source§impl<'d, S> BleDriver<'d, S>
GATT-client operations on the BleDriver. Available for any role (S) — a device can be
both a server and a client. &self, so callable re-entrantly from within the client callback.
impl<'d, S> BleDriver<'d, S>
GATT-client operations on the BleDriver. Available for any role (S) — a device can be
both a server and a client. &self, so callable re-entrantly from within the client callback.
Sourcepub fn gattc_subscribe<F>(&self, callback: F)where
F: for<'a> FnMut(GattcEvent<'a>) + Send + 'static,
pub fn gattc_subscribe<F>(&self, callback: F)where
F: for<'a> FnMut(GattcEvent<'a>) + Send + 'static,
Subscribe to GATT-client events (GattcEvent): per-operation completions plus received
notifications/indications.
Sourcepub unsafe fn gattc_subscribe_nonstatic<F>(&self, callback: F)where
F: for<'a> FnMut(GattcEvent<'a>) + Send + 'd,
pub unsafe fn gattc_subscribe_nonstatic<F>(&self, callback: F)where
F: for<'a> FnMut(GattcEvent<'a>) + Send + 'd,
§Safety
The non-'static counterpart of gattc_subscribe. See
BleDriver::host_subscribe_nonstatic for the borrowing
rules and the core::mem::forget hazard.
Sourcepub fn gattc_unsubscribe(&self)
pub fn gattc_unsubscribe(&self)
Stop delivering GATT-client events to the subscribed hook.
Sourcepub fn connect(&self, own_addr_type: u8, peer: &BleAddr) -> Result<(), BleError>
pub fn connect(&self, own_addr_type: u8, peer: &BleAddr) -> Result<(), BleError>
Initiate a connection to peer. The connect/disconnect outcome arrives on the GAP hook
(gap_subscribe); the connection’s received notifications arrive
on the GATTC hook.
Sourcepub fn disconnect(&self, conn_handle: ConnHandle) -> Result<(), BleError>
pub fn disconnect(&self, conn_handle: ConnHandle) -> Result<(), BleError>
Terminate the connection conn_handle.
Sourcepub fn discover_services(&self, conn_handle: ConnHandle) -> Result<(), BleError>
pub fn discover_services(&self, conn_handle: ConnHandle) -> Result<(), BleError>
Discover all of the peer’s primary services. Results arrive as GattcEvent::Service.
Sourcepub fn discover_characteristics(
&self,
conn_handle: ConnHandle,
start_handle: AttrHandle,
end_handle: AttrHandle,
) -> Result<(), BleError>
pub fn discover_characteristics( &self, conn_handle: ConnHandle, start_handle: AttrHandle, end_handle: AttrHandle, ) -> Result<(), BleError>
Discover the peer’s characteristics in the attribute-handle range [start_handle, end_handle] (e.g. a service’s range). Results arrive as GattcEvent::Characteristic.
Sourcepub fn read(
&self,
conn_handle: ConnHandle,
attr_handle: AttrHandle,
) -> Result<(), BleError>
pub fn read( &self, conn_handle: ConnHandle, attr_handle: AttrHandle, ) -> Result<(), BleError>
Read the value of attr_handle on the peer (a Read Request). The result arrives as
GattcEvent::ReadComplete.
Sourcepub fn write(
&self,
conn_handle: ConnHandle,
attr_handle: AttrHandle,
data: &[u8],
) -> Result<(), BleError>
pub fn write( &self, conn_handle: ConnHandle, attr_handle: AttrHandle, data: &[u8], ) -> Result<(), BleError>
Write data to attr_handle on the peer as a Write Request (acknowledged): the peer
sends a Write Response, and its completion arrives as GattcEvent::WriteComplete.
Sourcepub fn write_cmd(
&self,
conn_handle: ConnHandle,
attr_handle: AttrHandle,
data: &[u8],
) -> Result<(), BleError>
pub fn write_cmd( &self, conn_handle: ConnHandle, attr_handle: AttrHandle, data: &[u8], ) -> Result<(), BleError>
Write data to attr_handle on the peer as a Write Command (unacknowledged): the peer
sends no response, so this is fire-and-forget — there is no completion event. The
returned Result only reflects whether the command was accepted for transmission.
Source§impl<'d, S> BleDriver<'d, S>where
S: AsRef<[ble_gatt_svc_def]>,
GATT-server operations on the BleDriver, available only when the driver was built with a
service table (S: AsRef<[ble_gatt_svc_def]>) via
new_with_services. &self, so callable re-entrantly.
impl<'d, S> BleDriver<'d, S>where
S: AsRef<[ble_gatt_svc_def]>,
GATT-server operations on the BleDriver, available only when the driver was built with a
service table (S: AsRef<[ble_gatt_svc_def]>) via
new_with_services. &self, so callable re-entrantly.
Sourcepub fn gatts_subscribe<F>(&self, callback: F)where
F: for<'a> FnMut(GattsEvent<'a>) -> u8 + Send + 'static,
pub fn gatts_subscribe<F>(&self, callback: F)where
F: for<'a> FnMut(GattsEvent<'a>) -> u8 + Send + 'static,
Subscribe to GATT-server events (GattsEvent). Set this before
start: the Register events (carrying the attribute handles NimBLE
assigned) fire during host start.
Sourcepub unsafe fn gatts_subscribe_nonstatic<F>(&self, callback: F)where
F: for<'a> FnMut(GattsEvent<'a>) -> u8 + Send + 'd,
pub unsafe fn gatts_subscribe_nonstatic<F>(&self, callback: F)where
F: for<'a> FnMut(GattsEvent<'a>) -> u8 + Send + 'd,
§Safety
The non-'static counterpart of gatts_subscribe. See
BleDriver::host_subscribe_nonstatic for the borrowing
rules and the core::mem::forget hazard.
Sourcepub fn gatts_unsubscribe(&self)
pub fn gatts_unsubscribe(&self)
Stop delivering GATT-server events to the subscribed hook.
Sourcepub fn indicate(
&self,
conn_handle: ConnHandle,
val_handle: AttrHandle,
data: &[u8],
) -> Result<(), BleError>
pub fn indicate( &self, conn_handle: ConnHandle, val_handle: AttrHandle, data: &[u8], ) -> Result<(), BleError>
Send a “free-form” characteristic indication to conn_handle.
Source§impl<'d, S> BleDriver<'d, S>
L2CAP CoC operations on the BleDriver. Available for any role (S); a channel just needs a
GAP connection underneath. &self, so callable re-entrantly from within the L2CAP hook (e.g.
calling l2cap_recv_ready from an Accept).
impl<'d, S> BleDriver<'d, S>
L2CAP CoC operations on the BleDriver. Available for any role (S); a channel just needs a
GAP connection underneath. &self, so callable re-entrantly from within the L2CAP hook (e.g.
calling l2cap_recv_ready from an Accept).
Sourcepub fn l2cap_subscribe<F>(&self, callback: F)where
F: for<'a> FnMut(L2capEvent<'a>) -> i32 + Send + 'static,
pub fn l2cap_subscribe<F>(&self, callback: F)where
F: for<'a> FnMut(L2capEvent<'a>) -> i32 + Send + 'static,
Subscribe to L2CAP CoC events (L2capEvent) — connection lifecycle, received SDUs, and
flow-control notifications for every channel.
Sourcepub unsafe fn l2cap_subscribe_nonstatic<F>(&self, callback: F)where
F: for<'a> FnMut(L2capEvent<'a>) -> i32 + Send + 'd,
pub unsafe fn l2cap_subscribe_nonstatic<F>(&self, callback: F)where
F: for<'a> FnMut(L2capEvent<'a>) -> i32 + Send + 'd,
§Safety
The non-'static counterpart of l2cap_subscribe. See
BleDriver::host_subscribe_nonstatic for
the borrowing rules and the core::mem::forget hazard.
Sourcepub fn l2cap_unsubscribe(&self)
pub fn l2cap_unsubscribe(&self)
Stop delivering L2CAP events to the subscribed hook.
Sourcepub fn l2cap_create_server(&self, psm: u16, mtu: u16) -> Result<(), BleError>
pub fn l2cap_create_server(&self, psm: u16, mtu: u16) -> Result<(), BleError>
Listen for incoming L2CAP CoC connections on psm, negotiating an MTU of mtu. Incoming
connections arrive as L2capEvent::Accept on the L2CAP hook. May be called at runtime
(unlike a GATT service table, which is fixed at construction).
Sourcepub fn l2cap_connect(
&self,
conn_handle: ConnHandle,
psm: u16,
mtu: u16,
) -> Result<(), BleError>
pub fn l2cap_connect( &self, conn_handle: ConnHandle, psm: u16, mtu: u16, ) -> Result<(), BleError>
Open an L2CAP CoC to the peer’s psm over the existing connection conn_handle, negotiating
an MTU of mtu. The outcome arrives as L2capEvent::Connected. The initial receive buffer
is allocated internally (mtu bytes from os_msys).
Sourcepub fn l2cap_send(
&self,
chan: L2capChan,
data: &[u8],
) -> Result<SendOutcome, BleError>
pub fn l2cap_send( &self, chan: L2capChan, data: &[u8], ) -> Result<SendOutcome, BleError>
Send data as one SDU over chan. On success returns SendOutcome::Sent; if the peer’s
credits run out mid-SDU it returns SendOutcome::Stalled and the remainder resumes on
L2capEvent::TxUnstalled (do not call again until then).
Sourcepub fn l2cap_recv_ready(
&self,
chan: L2capChan,
sdu_size: u16,
) -> Result<(), BleError>
pub fn l2cap_recv_ready( &self, chan: L2capChan, sdu_size: u16, ) -> Result<(), BleError>
Signal readiness to receive another SDU of up to sdu_size bytes on chan, replenishing the
peer’s credits. Call this to provide the first buffer on L2capEvent::Accept and to
re-arm after each L2capEvent::Received. The buffer is allocated internally from os_msys.
Sourcepub fn l2cap_disconnect(&self, chan: L2capChan) -> Result<(), BleError>
pub fn l2cap_disconnect(&self, chan: L2capChan) -> Result<(), BleError>
Disconnect the L2CAP channel chan. The completion arrives as L2capEvent::Disconnected.
Source§impl<'ble> BleDriver<'ble, ()>
impl<'ble> BleDriver<'ble, ()>
Sourcepub fn new<M: BluetoothModemPeripheral + 'ble>(
modem: M,
) -> Result<Self, EspError>
pub fn new<M: BluetoothModemPeripheral + 'ble>( modem: M, ) -> Result<Self, EspError>
Initialize the NimBLE host with no GATT server — the role-agnostic form used by a
central, a broadcaster, or an observer. Performs nimble_port_init and the standard
GAP/GATT service init, but does not start the host task; configure callbacks/security,
then call start.
Source§impl<'ble, S> BleDriver<'ble, S>where
S: AsRef<[ble_gatt_svc_def]>,
impl<'ble, S> BleDriver<'ble, S>where
S: AsRef<[ble_gatt_svc_def]>,
Sourcepub fn new_with_services<M: BluetoothModemPeripheral + 'ble>(
modem: M,
services: S,
) -> Result<Self, EspError>
pub fn new_with_services<M: BluetoothModemPeripheral + 'ble>( modem: M, services: S, ) -> Result<Self, EspError>
Initialize the NimBLE host as a GATT server, registering services in NimBLE’s
pre-start window (this is why service registration is a construction concern, not a runtime
one — see ble_gatts_add_svcs). S may be an owned bundle built at runtime (e.g.
BleGattServices), a Box<[ble_gatt_svc_def]>, a
&'static [ble_gatt_svc_def], or a &'static static table built with the
gatt_services! macro; whatever it is, it must keep the entire
pointer graph the table references (characteristics, UUIDs) alive and at stable addresses for
as long as it is held. The driver owns it, so drop order does the rest.
Does not start the host task; hook gatts_subscribe (to learn the
assigned attribute handles), configure security/callbacks, then call start.
Source§impl<'ble, S> BleDriver<'ble, S>
impl<'ble, S> BleDriver<'ble, S>
Sourcepub fn host_subscribe<F>(&self, callback: F)where
F: FnMut(HostEvent) + Send + 'static,
pub fn host_subscribe<F>(&self, callback: F)where
F: FnMut(HostEvent) + Send + 'static,
Subscribe to host-lifecycle events (HostEvent): Sync when the host and controller are
synchronized (you must delay BLE operations until then), and Reset when the host resets.
The hook must be re-entrant — a reset is followed by another Sync once re-synced.
See https://mynewt.apache.org/latest/network/ble_setup/ble_sync_cb.html
Sourcepub unsafe fn host_subscribe_nonstatic<F>(&self, callback: F)where
F: FnMut(HostEvent) + Send + 'ble,
pub unsafe fn host_subscribe_nonstatic<F>(&self, callback: F)where
F: FnMut(HostEvent) + Send + 'ble,
§Safety
The non-'static counterpart of host_subscribe: the callback may
borrow variables that live as long as this BleDriver. It stays registered with the
running NimBLE host task until the driver is dropped, which un-subscribes it.
Care must be taken NOT to core::mem::forget the driver: that skips the un-subscription,
leaving the host task holding a callback with dangling borrows. This “local borrowing” can
only be expressed safely once/if !Leak types are introduced to Rust.
Sourcepub fn host_unsubscribe(&self)
pub fn host_unsubscribe(&self)
Stop delivering host-lifecycle events to the subscribed hook.
Sourcepub fn set_security(&mut self, security: &BleSecurity) -> Result<(), EspError>
pub fn set_security(&mut self, security: &BleSecurity) -> Result<(), EspError>
Configure the Security Manager (SMP) parameters. Must be called before
start; the settings take effect once the host task runs.
This writes the global ble_hs_cfg, which the running host task reads on its own thread with
no lock we could share — so it is refused (with ESP_ERR_INVALID_STATE) once the host has
started. It takes &mut self rather than &self so this write cannot race a concurrent
config call from another thread; the operational, post-start API is all &self (and the
driver is Sync).
Sourcepub fn start(&self) -> Result<(), EspError>
pub fn start(&self) -> Result<(), EspError>
Start the NimBLE host task. It runs in the background and calls the
Sync via host_subscribe callback once the stack is ready for use. Call this once
services, security and callbacks are set up; you must retain the driver, as the BLE stack
is stopped when it drops.
Takes &self (flipping an interior started-flag) rather than consuming the driver, so the
service table it owns and every subscribed callback stay put across the call.