esp32_nimble/client/
ble_remote_characteristic.rs

1use core::borrow::Borrow;
2
3use super::ble_client::BLEClientState;
4use super::ble_remote_service::BLERemoteServiceState;
5use super::{BLEReader, BLEWriter};
6use crate::BLEAttribute;
7use crate::utilities::OsMBuf;
8use crate::{
9    BLEError, BLERemoteDescriptor, Signal, ble,
10    utilities::{ArcUnsafeCell, BleUuid, WeakUnsafeCell, as_void_ptr, voidp_to_ref},
11};
12use alloc::{boxed::Box, vec::Vec};
13use bitflags::bitflags;
14use core::ffi::c_void;
15use esp_idf_svc::sys as esp_idf_sys;
16
17bitflags! {
18  #[repr(transparent)]
19  #[derive(Debug, Clone, Copy, PartialEq, Eq)]
20  pub struct GattCharacteristicProperties: u8 {
21    const BROADCAST = esp_idf_sys::BLE_GATT_CHR_PROP_BROADCAST as _;
22    const READ = esp_idf_sys::BLE_GATT_CHR_PROP_READ as _;
23    const WRITE_NO_RSP = esp_idf_sys::BLE_GATT_CHR_PROP_WRITE_NO_RSP as _;
24    const WRITE = esp_idf_sys::BLE_GATT_CHR_PROP_WRITE as _;
25    const NOTIFY = esp_idf_sys::BLE_GATT_CHR_PROP_NOTIFY as _;
26    const INDICATE = esp_idf_sys::BLE_GATT_CHR_PROP_INDICATE as _;
27    const AUTH_SIGN_WRITE = esp_idf_sys::BLE_GATT_CHR_PROP_AUTH_SIGN_WRITE as _;
28    const EXTENDED =  esp_idf_sys::BLE_GATT_CHR_PROP_EXTENDED as _;
29  }
30}
31
32#[allow(clippy::type_complexity)]
33pub struct BLERemoteCharacteristicState {
34    pub(crate) service: WeakUnsafeCell<BLERemoteServiceState>,
35    pub(crate) uuid: BleUuid,
36    pub handle: u16,
37    end_handle: u16,
38    properties: GattCharacteristicProperties,
39    descriptors: Option<Vec<BLERemoteDescriptor>>,
40    signal: Signal<u32>,
41    on_notify: Option<Box<dyn FnMut(&[u8]) + Send + Sync>>,
42}
43
44impl BLEAttribute for BLERemoteCharacteristicState {
45    fn get_client(&self) -> Option<ArcUnsafeCell<BLEClientState>> {
46        match self.service.upgrade() {
47            Some(x) => x.get_client(),
48            None => None,
49        }
50    }
51}
52
53impl BLERemoteCharacteristicState {
54    pub fn conn_handle(&self) -> u16 {
55        match self.service.upgrade() {
56            Some(x) => x.conn_handle(),
57            None => esp_idf_sys::BLE_HS_CONN_HANDLE_NONE as _,
58        }
59    }
60}
61
62#[derive(Clone)]
63pub struct BLERemoteCharacteristic {
64    state: ArcUnsafeCell<BLERemoteCharacteristicState>,
65}
66
67impl BLERemoteCharacteristic {
68    pub(crate) fn new(
69        service: WeakUnsafeCell<BLERemoteServiceState>,
70        chr: &esp_idf_sys::ble_gatt_chr,
71    ) -> Self {
72        Self {
73            state: ArcUnsafeCell::new(BLERemoteCharacteristicState {
74                service,
75                uuid: BleUuid::from(chr.uuid),
76                handle: chr.val_handle,
77                end_handle: 0,
78                properties: GattCharacteristicProperties::from_bits_truncate(chr.properties),
79                descriptors: None,
80                signal: Signal::new(),
81                on_notify: None,
82            }),
83        }
84    }
85
86    pub(crate) fn state(&self) -> &BLERemoteCharacteristicState {
87        &self.state
88    }
89
90    pub fn uuid(&self) -> BleUuid {
91        self.state.uuid
92    }
93
94    pub fn properties(&self) -> GattCharacteristicProperties {
95        self.state.properties
96    }
97
98    pub async fn get_descriptors(
99        &mut self,
100    ) -> Result<core::slice::IterMut<'_, BLERemoteDescriptor>, BLEError> {
101        if self.state.descriptors.is_none() {
102            self.state.descriptors = Some(Vec::new());
103
104            if self.state.end_handle == 0 {
105                unsafe {
106                    ble!(esp_idf_sys::ble_gattc_disc_all_chrs(
107                        self.state.conn_handle(),
108                        self.state.handle,
109                        self.state.service.upgrade().unwrap().end_handle,
110                        Some(Self::next_char_cb),
111                        as_void_ptr(self),
112                    ))?;
113                }
114
115                ble!(self.state.signal.wait().await)?;
116            }
117
118            if self.state.handle != self.state.end_handle {
119                unsafe {
120                    ble!(esp_idf_sys::ble_gattc_disc_all_dscs(
121                        self.state.conn_handle(),
122                        self.state.handle,
123                        self.state.end_handle,
124                        Some(Self::descriptor_disc_cb),
125                        as_void_ptr(self),
126                    ))?;
127                }
128                ble!(self.state.signal.wait().await)?;
129            }
130        }
131
132        Ok(self.state.descriptors.as_mut().unwrap().iter_mut())
133    }
134
135    pub async fn get_descriptor(
136        &mut self,
137        uuid: BleUuid,
138    ) -> Result<&mut BLERemoteDescriptor, BLEError> {
139        let mut iter = self.get_descriptors().await?;
140        iter.find(|x| x.uuid() == uuid)
141            .ok_or_else(|| BLEError::fail().unwrap_err())
142    }
143
144    extern "C" fn next_char_cb(
145        conn_handle: u16,
146        error: *const esp_idf_sys::ble_gatt_error,
147        chr: *const esp_idf_sys::ble_gatt_chr,
148        arg: *mut c_void,
149    ) -> i32 {
150        let characteristic = unsafe { voidp_to_ref::<Self>(arg) };
151        if characteristic.state.conn_handle() != conn_handle {
152            return 0;
153        }
154
155        let error = unsafe { &*error };
156        if error.status == 0 {
157            characteristic.state.end_handle = unsafe { (*chr).def_handle - 1 };
158        } else if error.status == esp_idf_sys::BLE_HS_EDONE as _ {
159            characteristic.state.end_handle =
160                characteristic.state.service.upgrade().unwrap().end_handle;
161        }
162
163        characteristic.state.signal.signal(error.status as _);
164        esp_idf_sys::BLE_HS_EDONE as _
165    }
166
167    extern "C" fn descriptor_disc_cb(
168        conn_handle: u16,
169        error: *const esp_idf_sys::ble_gatt_error,
170        _chr_val_handle: u16,
171        dsc: *const esp_idf_sys::ble_gatt_dsc,
172        arg: *mut c_void,
173    ) -> i32 {
174        let characteristic = unsafe { voidp_to_ref::<Self>(arg) };
175        if characteristic.state.conn_handle() != conn_handle {
176            return 0;
177        }
178
179        let error = unsafe { &*error };
180
181        if error.status == 0 {
182            let dsc = unsafe { &*dsc };
183            let descriptor =
184                BLERemoteDescriptor::new(ArcUnsafeCell::downgrade(&characteristic.state), dsc);
185            characteristic
186                .state
187                .descriptors
188                .as_mut()
189                .unwrap()
190                .push(descriptor);
191            return 0;
192        }
193
194        characteristic.state.signal.signal(error.status as _);
195        esp_idf_sys::BLE_HS_EDONE as _
196    }
197
198    pub async fn read_value(&mut self) -> Result<Vec<u8>, BLEError> {
199        let mut reader = BLEReader::new(self.state.conn_handle(), self.state.handle);
200        reader.read_value().await
201    }
202
203    pub async fn write_value(&mut self, data: &[u8], response: bool) -> Result<(), BLEError> {
204        let mut writer = BLEWriter::new(self.state.conn_handle(), self.state.handle);
205        writer.write_value(data, response).await
206    }
207
208    pub async fn subscribe_notify(&mut self, response: bool) -> Result<(), BLEError> {
209        self.set_notify(0x01, response).await
210    }
211
212    pub async fn subscribe_indicate(&mut self, response: bool) -> Result<(), BLEError> {
213        self.set_notify(0x02, response).await
214    }
215
216    pub async fn unsubscribe(&mut self, response: bool) -> Result<(), BLEError> {
217        self.set_notify(0x00, response).await
218    }
219
220    async fn set_notify(&mut self, val: u16, response: bool) -> Result<(), BLEError> {
221        let desc = self.get_descriptor(BleUuid::from_uuid16(0x2902)).await?;
222        desc.write_value(&val.to_ne_bytes(), response).await
223    }
224
225    pub fn on_notify(&mut self, callback: impl FnMut(&[u8]) + Send + Sync + 'static) -> &mut Self {
226        self.state.on_notify = Some(Box::new(callback));
227        self
228    }
229
230    pub fn can_notify(&self) -> bool {
231        self.properties()
232            .contains(GattCharacteristicProperties::NOTIFY)
233    }
234
235    pub fn can_indicate(&self) -> bool {
236        self.properties()
237            .contains(GattCharacteristicProperties::INDICATE)
238    }
239
240    pub fn can_read(&self) -> bool {
241        self.properties()
242            .contains(GattCharacteristicProperties::READ)
243    }
244
245    pub fn can_write(&self) -> bool {
246        self.properties()
247            .contains(GattCharacteristicProperties::WRITE)
248    }
249
250    pub fn can_write_no_response(&self) -> bool {
251        self.properties()
252            .contains(GattCharacteristicProperties::WRITE_NO_RSP)
253    }
254
255    pub fn can_broadcast(&self) -> bool {
256        self.properties()
257            .contains(GattCharacteristicProperties::BROADCAST)
258    }
259
260    pub(crate) unsafe fn notify(&mut self, om: *mut esp_idf_sys::os_mbuf) {
261        if let Some(no_notify) = self.state.on_notify.as_mut() {
262            let om = OsMBuf(om);
263            no_notify(om.as_flat().as_slice());
264        }
265    }
266}
267
268impl core::fmt::Display for BLERemoteCharacteristic {
269    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
270        write!(f, "BLERemoteCharacteristic[{}]", self.state.uuid)
271    }
272}
273
274impl core::fmt::Debug for BLERemoteCharacteristic {
275    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
276        f.debug_struct("BLERemoteCharacteristic")
277            .field("uuid", &self.state.uuid)
278            .field(
279                "service",
280                &self.state.service.upgrade().map(|svc| svc.borrow().uuid),
281            )
282            .field("handle", &self.state.handle)
283            .field("end_handle", &self.state.end_handle)
284            .field("properties", &self.state.properties)
285            .finish()
286    }
287}