Struct bluer::gatt::CharacteristicWriter
source · pub struct CharacteristicWriter { /* private fields */ }
bluetoothd
only.Expand description
Streams data to a characteristic with low overhead.
Implementations§
source§impl CharacteristicWriter
impl CharacteristicWriter
sourcepub fn adapter_name(&self) -> &str
pub fn adapter_name(&self) -> &str
Name of adapter.
sourcepub fn device_address(&self) -> Address
pub fn device_address(&self) -> Address
Address of remote device.
Examples found in repository?
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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
async fn main() -> bluer::Result<()> {
env_logger::init();
let session = bluer::Session::new().await?;
let adapter = session.default_adapter().await?;
adapter.set_powered(true).await?;
println!("Advertising on Bluetooth adapter {} with address {}", adapter.name(), adapter.address().await?);
let mut manufacturer_data = BTreeMap::new();
manufacturer_data.insert(MANUFACTURER_ID, vec![0x21, 0x22, 0x23, 0x24]);
let le_advertisement = Advertisement {
service_uuids: vec![SERVICE_UUID].into_iter().collect(),
manufacturer_data,
discoverable: Some(true),
local_name: Some("gatt_server".to_string()),
..Default::default()
};
let adv_handle = adapter.advertise(le_advertisement).await?;
println!("Serving GATT service on Bluetooth adapter {}", adapter.name());
let (service_control, service_handle) = service_control();
let (char_control, char_handle) = characteristic_control();
let app = Application {
services: vec![Service {
uuid: SERVICE_UUID,
primary: true,
characteristics: vec![Characteristic {
uuid: CHARACTERISTIC_UUID,
write: Some(CharacteristicWrite {
write: true,
write_without_response: true,
method: CharacteristicWriteMethod::Io,
..Default::default()
}),
notify: Some(CharacteristicNotify {
notify: true,
method: CharacteristicNotifyMethod::Io,
..Default::default()
}),
control_handle: char_handle,
..Default::default()
}],
control_handle: service_handle,
..Default::default()
}],
..Default::default()
};
let app_handle = adapter.serve_gatt_application(app).await?;
println!("Service handle is 0x{:x}", service_control.handle()?);
println!("Characteristic handle is 0x{:x}", char_control.handle()?);
println!("Service ready. Press enter to quit.");
let stdin = BufReader::new(tokio::io::stdin());
let mut lines = stdin.lines();
let mut value: Vec<u8> = vec![0x10, 0x01, 0x01, 0x10];
let mut read_buf = Vec::new();
let mut reader_opt: Option<CharacteristicReader> = None;
let mut writer_opt: Option<CharacteristicWriter> = None;
let mut interval = interval(Duration::from_secs(1));
pin_mut!(char_control);
loop {
tokio::select! {
_ = lines.next_line() => break,
evt = char_control.next() => {
match evt {
Some(CharacteristicControlEvent::Write(req)) => {
println!("Accepting write event with MTU {} from {}", req.mtu(), req.device_address());
read_buf = vec![0; req.mtu()];
reader_opt = Some(req.accept()?);
},
Some(CharacteristicControlEvent::Notify(notifier)) => {
println!("Accepting notify request event with MTU {} from {}", notifier.mtu(), notifier.device_address());
writer_opt = Some(notifier);
},
None => break,
}
}
_ = interval.tick() => {
println!("Decrementing each element by one");
for v in &mut *value {
*v = v.saturating_sub(1);
}
println!("Value is {:x?}", &value);
if let Some(writer) = writer_opt.as_mut() {
println!("Notifying with value {:x?}", &value);
if let Err(err) = writer.write(&value).await {
println!("Notification stream error: {}", &err);
writer_opt = None;
}
}
}
read_res = async {
match &mut reader_opt {
Some(reader) => reader.read(&mut read_buf).await,
None => future::pending().await,
}
} => {
match read_res {
Ok(0) => {
println!("Write stream ended");
reader_opt = None;
}
Ok(n) => {
value = read_buf[0..n].to_vec();
println!("Write request with {} bytes: {:x?}", n, &value);
}
Err(err) => {
println!("Write stream error: {}", &err);
reader_opt = None;
}
}
}
}
}
println!("Removing service and advertisement");
drop(app_handle);
drop(adv_handle);
sleep(Duration::from_secs(1)).await;
Ok(())
}
sourcepub fn mtu(&self) -> usize
pub fn mtu(&self) -> usize
Maximum transmission unit.
Examples found in repository?
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
async fn exercise_characteristic(char: &Characteristic) -> Result<()> {
let mut write_io = char.write_io().await?;
println!(" Obtained write IO with MTU {} bytes", write_io.mtu());
let mut notify_io = char.notify_io().await?;
println!(" Obtained notification IO with MTU {} bytes", notify_io.mtu());
// Flush notify buffer.
let mut buf = [0; 1024];
while let Ok(Ok(_)) = timeout(Duration::from_secs(1), notify_io.read(&mut buf)).await {}
let mut rng = rand::thread_rng();
for i in 0..1024 {
let mut len = rng.gen_range(0..20000);
// Try to trigger packet reordering over EATT.
if i % 10 == 0 {
// Big packet is split into multiple small packets.
// (by L2CAP layer, because GATT MTU is bigger than L2CAP MTU)
len = write_io.mtu(); // 512
}
if i % 10 == 1 {
// Small packet can use different L2CAP channel when EATT is enabled.
len = 20;
}
// Thus small packet can arrive before big packet.
// The solution is to disable EATT in /etc/bluetooth/main.conf.
println!(" Test iteration {i} with data size {len}");
let data: Vec<u8> = (0..len).map(|_| rng.gen()).collect();
// We must read back the data while sending, otherwise the connection
// buffer will overrun and we will lose data.
let read_task = tokio::spawn(async move {
let mut echo_buf = vec![0u8; len];
let res = match notify_io.read_exact(&mut echo_buf).await {
Ok(_) => Ok(echo_buf),
Err(err) => Err(err),
};
(notify_io, res)
});
// Note that write_all will automatically split the buffer into
// multiple writes of MTU size.
write_io.write_all(&data).await.expect("write failed");
println!(" Waiting for echo");
let (notify_io_back, res) = read_task.await.unwrap();
notify_io = notify_io_back;
let echo_buf = res.expect("read failed");
if echo_buf != data {
println!();
println!("Echo data mismatch!");
println!("Send data: {:x?}", &data);
println!("Received data: {:x?}", &echo_buf);
println!();
println!("By 512 blocks:");
for (sent, recv) in data.chunks(512).zip(echo_buf.chunks(512)) {
println!();
println!(
"Send: {:x?} ... {:x?}",
&sent[0..4.min(sent.len())],
&sent[sent.len().saturating_sub(4)..]
);
println!(
"Recv: {:x?} ... {:x?}",
&recv[0..4.min(recv.len())],
&recv[recv.len().saturating_sub(4)..]
);
}
println!();
panic!("echoed data does not match sent data");
}
println!(" Data matches");
}
println!(" Test okay");
Ok(())
}
More examples
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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
async fn main() -> bluer::Result<()> {
env_logger::init();
let session = bluer::Session::new().await?;
let adapter = session.default_adapter().await?;
adapter.set_powered(true).await?;
println!("Advertising on Bluetooth adapter {} with address {}", adapter.name(), adapter.address().await?);
let le_advertisement = Advertisement {
service_uuids: vec![SERVICE_UUID].into_iter().collect(),
discoverable: Some(true),
local_name: Some("gatt_echo_server".to_string()),
..Default::default()
};
let adv_handle = adapter.advertise(le_advertisement).await?;
println!("Serving GATT echo service on Bluetooth adapter {}", adapter.name());
let (char_control, char_handle) = characteristic_control();
let app = Application {
services: vec![Service {
uuid: SERVICE_UUID,
primary: true,
characteristics: vec![Characteristic {
uuid: CHARACTERISTIC_UUID,
write: Some(CharacteristicWrite {
write_without_response: true,
method: CharacteristicWriteMethod::Io,
..Default::default()
}),
notify: Some(CharacteristicNotify {
notify: true,
method: CharacteristicNotifyMethod::Io,
..Default::default()
}),
control_handle: char_handle,
..Default::default()
}],
..Default::default()
}],
..Default::default()
};
let app_handle = adapter.serve_gatt_application(app).await?;
println!("Echo service ready. Press enter to quit.");
let stdin = BufReader::new(tokio::io::stdin());
let mut lines = stdin.lines();
let mut read_buf = Vec::new();
let mut reader_opt: Option<CharacteristicReader> = None;
let mut writer_opt: Option<CharacteristicWriter> = None;
pin_mut!(char_control);
loop {
tokio::select! {
_ = lines.next_line() => break,
evt = char_control.next() => {
match evt {
Some(CharacteristicControlEvent::Write(req)) => {
println!("Accepting write request event with MTU {}", req.mtu());
read_buf = vec![0; req.mtu()];
reader_opt = Some(req.accept()?);
},
Some(CharacteristicControlEvent::Notify(notifier)) => {
println!("Accepting notify request event with MTU {}", notifier.mtu());
writer_opt = Some(notifier);
},
None => break,
}
},
read_res = async {
match &mut reader_opt {
Some(reader) if writer_opt.is_some() => reader.read(&mut read_buf).await,
_ => future::pending().await,
}
} => {
match read_res {
Ok(0) => {
println!("Read stream ended");
reader_opt = None;
}
Ok(n) => {
let value = read_buf[..n].to_vec();
println!("Echoing {} bytes: {:x?} ... {:x?}", value.len(), &value[0..4.min(value.len())], &value[value.len().saturating_sub(4) ..]);
if value.len() < 512 {
println!();
}
if let Err(err) = writer_opt.as_mut().unwrap().write_all(&value).await {
println!("Write failed: {}", &err);
writer_opt = None;
}
}
Err(err) => {
println!("Read stream error: {}", &err);
reader_opt = None;
}
}
}
}
}
println!("Removing service and advertisement");
drop(app_handle);
drop(adv_handle);
sleep(Duration::from_secs(1)).await;
Ok(())
}
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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
async fn main() -> bluer::Result<()> {
env_logger::init();
let session = bluer::Session::new().await?;
let adapter = session.default_adapter().await?;
adapter.set_powered(true).await?;
println!("Advertising on Bluetooth adapter {} with address {}", adapter.name(), adapter.address().await?);
let mut manufacturer_data = BTreeMap::new();
manufacturer_data.insert(MANUFACTURER_ID, vec![0x21, 0x22, 0x23, 0x24]);
let le_advertisement = Advertisement {
service_uuids: vec![SERVICE_UUID].into_iter().collect(),
manufacturer_data,
discoverable: Some(true),
local_name: Some("gatt_server".to_string()),
..Default::default()
};
let adv_handle = adapter.advertise(le_advertisement).await?;
println!("Serving GATT service on Bluetooth adapter {}", adapter.name());
let (service_control, service_handle) = service_control();
let (char_control, char_handle) = characteristic_control();
let app = Application {
services: vec![Service {
uuid: SERVICE_UUID,
primary: true,
characteristics: vec![Characteristic {
uuid: CHARACTERISTIC_UUID,
write: Some(CharacteristicWrite {
write: true,
write_without_response: true,
method: CharacteristicWriteMethod::Io,
..Default::default()
}),
notify: Some(CharacteristicNotify {
notify: true,
method: CharacteristicNotifyMethod::Io,
..Default::default()
}),
control_handle: char_handle,
..Default::default()
}],
control_handle: service_handle,
..Default::default()
}],
..Default::default()
};
let app_handle = adapter.serve_gatt_application(app).await?;
println!("Service handle is 0x{:x}", service_control.handle()?);
println!("Characteristic handle is 0x{:x}", char_control.handle()?);
println!("Service ready. Press enter to quit.");
let stdin = BufReader::new(tokio::io::stdin());
let mut lines = stdin.lines();
let mut value: Vec<u8> = vec![0x10, 0x01, 0x01, 0x10];
let mut read_buf = Vec::new();
let mut reader_opt: Option<CharacteristicReader> = None;
let mut writer_opt: Option<CharacteristicWriter> = None;
let mut interval = interval(Duration::from_secs(1));
pin_mut!(char_control);
loop {
tokio::select! {
_ = lines.next_line() => break,
evt = char_control.next() => {
match evt {
Some(CharacteristicControlEvent::Write(req)) => {
println!("Accepting write event with MTU {} from {}", req.mtu(), req.device_address());
read_buf = vec![0; req.mtu()];
reader_opt = Some(req.accept()?);
},
Some(CharacteristicControlEvent::Notify(notifier)) => {
println!("Accepting notify request event with MTU {} from {}", notifier.mtu(), notifier.device_address());
writer_opt = Some(notifier);
},
None => break,
}
}
_ = interval.tick() => {
println!("Decrementing each element by one");
for v in &mut *value {
*v = v.saturating_sub(1);
}
println!("Value is {:x?}", &value);
if let Some(writer) = writer_opt.as_mut() {
println!("Notifying with value {:x?}", &value);
if let Err(err) = writer.write(&value).await {
println!("Notification stream error: {}", &err);
writer_opt = None;
}
}
}
read_res = async {
match &mut reader_opt {
Some(reader) => reader.read(&mut read_buf).await,
None => future::pending().await,
}
} => {
match read_res {
Ok(0) => {
println!("Write stream ended");
reader_opt = None;
}
Ok(n) => {
value = read_buf[0..n].to_vec();
println!("Write request with {} bytes: {:x?}", n, &value);
}
Err(err) => {
println!("Write stream error: {}", &err);
reader_opt = None;
}
}
}
}
}
println!("Removing service and advertisement");
drop(app_handle);
drop(adv_handle);
sleep(Duration::from_secs(1)).await;
Ok(())
}
sourcepub async fn closed(&self) -> Result<()>
pub async fn closed(&self) -> Result<()>
Waits for the remote device to stop the notification session.
sourcepub fn is_closed(&self) -> Result<bool>
pub fn is_closed(&self) -> Result<bool>
Checks if the remote device has stopped the notification session.
sourcepub fn try_send(&self, buf: &[u8]) -> Result<()>
pub fn try_send(&self, buf: &[u8]) -> Result<()>
Tries to send the characteristic value using a single write or notify operation.
The length of buf
must not exceed Self::mtu.
Does not wait for send space to become available.
sourcepub async fn send(&self, buf: &[u8]) -> Result<()>
pub async fn send(&self, buf: &[u8]) -> Result<()>
Send the characteristic value using a single write or notify operation.
The length of buf
must not exceed Self::mtu.
Waits for send space to become available.
sourcepub fn into_raw_fd(self) -> Result<RawFd>
pub fn into_raw_fd(self) -> Result<RawFd>
Consumes this object, returning the raw underlying file descriptor.
Trait Implementations§
source§impl AsRawFd for CharacteristicWriter
impl AsRawFd for CharacteristicWriter
source§impl AsyncWrite for CharacteristicWriter
impl AsyncWrite for CharacteristicWriter
source§fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8]
) -> Poll<Result<usize>>
fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8] ) -> Poll<Result<usize>>
Attempt to write bytes from buf
into the characteristic value stream.
A single write operation will send no more than mtu bytes. However, attempting to send a larger buffer will not result in an error but a partial send.
source§fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>>
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>>
source§fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>>
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>>
source§fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>]
) -> Poll<Result<usize, Error>>
fn poll_write_vectored( self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>] ) -> Poll<Result<usize, Error>>
poll_write
, except that it writes from a slice of buffers. Read moresource§fn is_write_vectored(&self) -> bool
fn is_write_vectored(&self) -> bool
poll_write_vectored
implementation. Read more