diff --git a/Cargo.toml b/Cargo.toml index d4cea37d3..68dfe8125 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ clap = { version = "4.5.4", features = ["derive"] } [dev-dependencies] tracing-test = "0.2.4" +udp-stream = "0.0.11" [build-dependencies] convert_case = "0.6.0" diff --git a/README.md b/README.md index 0ca783b08..2419cd754 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ https://docs.bluerobotics.com/ping-rs/bluerobotics_ping/ To run examples use: ```shell -cargo run --example ping_1d -- --port-name /dev/ttyUSB0 +cargo run --example ping_1d -- --serial-port /dev/ttyUSB0 ``` Should output: @@ -34,4 +34,20 @@ The distance to target is: 4538 mm Waiting for 30 profiles... Received 30 profiles Turning-off the continuous messages stream from Ping1D -``` \ No newline at end of file +``` +### **Pro tip** :grey_exclamation: +For external use via UDP, consider using [bridges](https://github.com/patrickelectric/bridges) to share your serial device to the network. Detailed instructions can be found [here](https://github.com/patrickelectric/bridges?tab=readme-ov-file#install-zap). + +#### On the host :satellite: (Where ping device is connected): + +```shell +bridges --port /dev/ttyUSB0:115200 -u 0.0.0.0:8080 +``` + +#### On the client :computer:: + +```shell +cargo run --example ping_1d -- --udp-address 192.168.0.191 --udp-port 8080 +``` + +Enjoy exploring with ping-rs! :ocean: \ No newline at end of file diff --git a/examples/ping_1d.rs b/examples/ping_1d.rs index aa3c538ed..5519bbbc0 100644 --- a/examples/ping_1d.rs +++ b/examples/ping_1d.rs @@ -1,5 +1,11 @@ use clap::Parser; -use std::{convert::TryFrom, path::PathBuf}; +use std::{ + convert::TryFrom, + net::{IpAddr, SocketAddr}, + path::PathBuf, + str::FromStr, +}; +use udp_stream::UdpStream; use bluerobotics_ping::{ device::{Ping1D, PingDevice}, @@ -12,15 +18,14 @@ use tokio_serial::{SerialPort, SerialPortBuilderExt}; #[tokio::main] async fn main() -> Result<(), PingError> { - println!("Parsing user provided values..."); - let args = Args::parse(); - - let port = - tokio_serial::new(args.port_name.to_string_lossy(), args.baud_rate).open_native_async()?; - port.clear(tokio_serial::ClearBuffer::All)?; + println!("Parsing user provided values and creating port..."); + let port = create_port().await; println!("Creating your Ping 1D device"); - let ping1d = Ping1D::new(port); + let ping1d = match port { + Port::Serial(port) => Ping1D::new(port), + Port::Udp(port) => Ping1D::new(port), + }; // Creating a subscription channel which will receive 30 Profile measurements, we'll check this after the next methods! let mut subscribed = ping1d.subscribe(); @@ -136,8 +141,64 @@ async fn main() -> Result<(), PingError> { #[derive(Parser, Debug)] #[command(version, about, long_about = None)] struct Args { - #[arg(short, long)] - port_name: PathBuf, - #[arg(short, long, default_value_t = 115200)] - baud_rate: u32, + #[arg(long, group = "source", + conflicts_with_all = ["udp_address"])] + serial_port: Option, + #[arg(long, default_value_t = 115200)] + serial_baud_rate: u32, + #[arg(long, group = "source", + conflicts_with_all = ["serial_port"])] + udp_address: Option, + #[arg(long, default_value_t = 8080)] + udp_port: u32, +} + +enum Port { + Serial(tokio_serial::SerialStream), + Udp(udp_stream::UdpStream), +} + +async fn create_port() -> Port { + let args = Args::parse(); + + let port = match (args.serial_port, args.udp_address) { + (Some(serial_port), None) => { + println!("Using serial port: {:?}", serial_port); + let port = tokio_serial::new(serial_port.to_string_lossy(), args.serial_baud_rate) + .open_native_async() + .map_err(|e| { + eprintln!("Error opening serial port: {}", e); + e + }) + .unwrap(); + port.clear(tokio_serial::ClearBuffer::All).unwrap(); + Port::Serial(port) + } + (None, Some(udp_address)) => { + println!("Using UDP address: {}", udp_address); + let socket_addr = SocketAddr::from_str(&format!("{}:{}", udp_address, args.udp_port)) + .map_err(|e| { + eprintln!("Error parsing UDP address: {}", e); + e + }) + .unwrap(); + let port = UdpStream::connect(socket_addr) + .await + .map_err(|e| { + eprintln!("Error connecting to UDP socket: {}", e); + e + }) + .unwrap(); + Port::Udp(port) + } + (None, None) => { + eprintln!("Error: either serial_port_name or udp_address must be provided"); + std::process::exit(1); + } + (Some(_), Some(_)) => { + eprintln!("Error: serial_port_name and udp_address are mutually exclusive"); + std::process::exit(1); + } + }; + port }