esp32_nimble/client/
ble_writer.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
use crate::{
  ble,
  utilities::{as_void_ptr, voidp_to_ref, OsMBuf},
  BLEError, Signal,
};
use esp_idf_svc::sys as esp_idf_sys;

pub struct BLEWriter {
  conn_handle: u16,
  handle: u16,
  signal: Signal<u32>,
}

impl BLEWriter {
  pub fn new(conn_handle: u16, handle: u16) -> Self {
    Self {
      conn_handle,
      handle,
      signal: Signal::new(),
    }
  }

  pub async fn write_value(&mut self, data: &[u8], response: bool) -> Result<(), BLEError> {
    unsafe {
      // ble_att_mtu() returns 0 for a closed connection
      let mtu = esp_idf_sys::ble_att_mtu(self.conn_handle);
      if mtu == 0 {
        return BLEError::convert(esp_idf_sys::BLE_HS_ENOTCONN as _);
      }
      let mtu = { mtu - 3 } as usize;

      if !response && data.len() <= mtu {
        return ble!(esp_idf_sys::ble_gattc_write_no_rsp_flat(
          self.conn_handle,
          self.handle,
          data.as_ptr() as _,
          data.len() as _
        ));
      }

      if data.len() <= mtu {
        ble!(esp_idf_sys::ble_gattc_write_flat(
          self.conn_handle,
          self.handle,
          data.as_ptr() as _,
          data.len() as _,
          Some(Self::on_write_cb),
          as_void_ptr(self),
        ))?;
      } else {
        let om = OsMBuf::from_flat(data);
        ble!(esp_idf_sys::ble_gattc_write_long(
          self.conn_handle,
          self.handle,
          0,
          om.0,
          Some(Self::on_write_cb),
          as_void_ptr(self),
        ))?;
      }

      ble!(self.signal.wait().await)?;
    }

    Ok(())
  }

  extern "C" fn on_write_cb(
    conn_handle: u16,
    error: *const esp_idf_sys::ble_gatt_error,
    _service: *mut esp_idf_sys::ble_gatt_attr,
    arg: *mut core::ffi::c_void,
  ) -> i32 {
    let writer = unsafe { voidp_to_ref::<Self>(arg) };
    if writer.conn_handle != conn_handle {
      return 0;
    }

    writer.signal.signal(unsafe { (*error).status as _ });
    0
  }
}