diff --git a/cflib2/_rust.pyi b/cflib2/_rust.pyi index ce5611b..fb1ef42 100644 --- a/cflib2/_rust.pyi +++ b/cflib2/_rust.pyi @@ -841,6 +841,24 @@ class LinkContext: # Returns List of URIs found """ + async def send_radio_broadcast( + self, + radio_nth: builtins.int, + channel: builtins.int, + address: typing.Sequence[builtins.int], + data: typing.Sequence[builtins.int], + ) -> None: + r""" + Send a radio broadcast packet (no acknowledgement) on a specific radio and channel + + This sends a raw packet without expecting an ack, useful for P2P communication. + + # Arguments + * `radio_nth` - Radio dongle index (usually 0) + * `channel` - Radio channel number (0-125) + * `address` - 5-byte destination address + * `data` - Packet payload bytes + """ class LinkError(CrazyflieError): r""" diff --git a/rust/Cargo.lock b/rust/Cargo.lock index a64f593..752ebd9 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -108,6 +108,7 @@ version = "0.1.0" dependencies = [ "crazyflie-lib", "crazyflie-link", + "crazyradio", "futures", "pyo3", "pyo3-async-runtimes", @@ -190,9 +191,9 @@ dependencies = [ [[package]] name = "crazyflie-link" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7aa54c1b5e50767eb2a04aea44a8ae172e11ff52745a32c670bd415a206374e" +checksum = "c546b30f976817451936078445d21d5efecbe2033fc3ea25e8fac99d37e733d9" dependencies = [ "async-trait", "bitflags", @@ -212,9 +213,9 @@ dependencies = [ [[package]] name = "crazyradio" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cabf480d2a2bba8ad41426a8486be4581459ca360a522a14836cde64ead1bf" +checksum = "fb0ef9ca124036f6ae818da8529ce53da1bae563b88fc3ca847c78c6258bb989" dependencies = [ "flume", "rusb", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 9aa5abc..af85f12 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -15,7 +15,8 @@ extension-module = ["pyo3/extension-module"] pyo3 = { version = "0.26" } pyo3-async-runtimes = { version = "0.26", features = ["tokio-runtime"] } crazyflie-lib = "0.7.1" -crazyflie-link = "0.4.2" +crazyflie-link = "0.4.3" +crazyradio = "0.6.0" tokio = { version = "1.48", features = ["full"] } futures = "0.3.31" pyo3-stub-gen-derive = "0.17.0" diff --git a/rust/src/link_context.rs b/rust/src/link_context.rs index 3479f66..aa5f273 100644 --- a/rust/src/link_context.rs +++ b/rust/src/link_context.rs @@ -22,7 +22,7 @@ //! Link context for scanning and discovering Crazyflies use pyo3::prelude::*; -use pyo3::exceptions::PyRuntimeError; +use pyo3::exceptions::{PyRuntimeError, PyValueError}; use pyo3_stub_gen_derive::*; use std::sync::Arc; @@ -65,7 +65,7 @@ impl LinkContext { // Default to E7E7E7E7E7 if no address provided let addr = if let Some(addr_vec) = address { if addr_vec.len() != 5 { - return Err(PyRuntimeError::new_err( + return Err(PyValueError::new_err( "Address must be exactly 5 bytes" )); } @@ -83,4 +83,34 @@ impl LinkContext { Ok(uris.into_iter().map(|uri| uri.to_string()).collect::>()) }) } + + /// Send a radio broadcast packet (no acknowledgement) on a specific radio and channel + /// + /// This sends a raw packet without expecting an ack, useful for P2P communication. + /// + /// # Arguments + /// * `radio_nth` - Radio dongle index (usually 0) + /// * `channel` - Radio channel number (0-125) + /// * `address` - 5-byte destination address + /// * `data` - Packet payload bytes + #[gen_stub(override_return_type(type_repr = "collections.abc.Coroutine[typing.Any, typing.Any, None]"))] + fn send_radio_broadcast<'py>(&self, py: Python<'py>, radio_nth: usize, channel: u8, address: Vec, data: Vec) -> PyResult> { + if address.len() != 5 { + return Err(PyValueError::new_err("Address must be exactly 5 bytes")); + } + let mut addr_array = [0u8; 5]; + addr_array.copy_from_slice(&address); + + let ch = crazyradio::Channel::from_number(channel) + .map_err(|_| PyValueError::new_err(format!("Invalid channel {}: must be 0-125", channel)))?; + + let inner = self.inner.clone(); + pyo3_async_runtimes::tokio::future_into_py(py, async move { + let mut radio = inner.get_radio(radio_nth).await + .map_err(|e| PyRuntimeError::new_err(format!("Failed to get radio: {:?}", e)))?; + radio.send_packet_no_ack_async(ch, addr_array, data).await + .map_err(|e| PyRuntimeError::new_err(format!("Broadcast failed: {:?}", e)))?; + Ok(()) + }) + } }