//! Attach and send/receive BT Mesh messages //! //! Example meshd //! [bluer/bluer]$ sudo /usr/libexec/bluetooth/bluetooth-meshd --config ${PWD}/examples/meshd/config --storage ${PWD}/examples/meshd/lib --debug //! //! Example send //! [bluer/bluer]$ RUST_LOG=TRACE cargo +nightly run --features=mesh --example mesh_sensor_server -- --token dae519a06e504bd3 //! //! Example receive //! [bluer/bluer]$ RUST_LOG=TRACE cargo +nightly run --features=mesh --example mesh_sensor_client -- --token 7eb48c91911361da use bluer::mesh::{ application::{Application, ApplicationMessage}, element::*, node::Node, }; use btmesh_common::{opcode::Opcode, CompanyIdentifier, InsufficientBuffer, ModelIdentifier, ParseError}; use btmesh_models::{ sensor::{ PropertyId, SensorConfig, SensorData, SensorDescriptor, SensorMessage, SensorStatus, SENSOR_SERVER, }, Message, Model, }; use clap::Parser; use futures::StreamExt; use tokio::{ signal, sync::{mpsc, mpsc::Sender}, time::{self, sleep, Duration}, }; use tokio_stream::wrappers::ReceiverStream; use uuid::Uuid; #[derive(Parser)] #[clap(author, version, about, long_about = None)] struct Args { #[clap(short, long)] token: Option, } #[tokio::main(flavor = "current_thread")] async fn main() -> Result<(), Box> { env_logger::init(); let args = Args::parse(); let session = bluer::Session::new().await?; let mesh = session.mesh().await?; let (_element_control, element_handle) = element_control(5); let (app_tx, app_rx) = mpsc::channel(1); let sim = Application { device_id: Uuid::new_v4(), elements: vec![Element { location: None, models: vec![SENSOR_SERVER], control_handle: Some(element_handle), }], events_tx: app_tx, provisioner: None, agent: Default::default(), properties: Default::default(), }; let registered = mesh.application(sim.clone()).await?; let mut node: Option = None; let (messages_tx, mut messages_rx) = mpsc::channel(10); let mut app_stream = ReceiverStream::new(app_rx); match args.token { Some(token) => { println!("Attaching with token {}", token); node = Some(mesh.attach(sim.clone(), &token).await?); start_sending(messages_tx.clone()); } None => { println!("Joining device: {}", sim.device_id.as_simple()); mesh.join(sim.clone()).await?; } }; println!("Sensor server ready. Press enter to send a message. Press Ctrl+C to quit"); loop { tokio::select! { _ = signal::ctrl_c() => { break }, Some(message) = messages_rx.recv() => { if let Some(ref n) = node { n.send::(&message, 0, 0x00bc as u16, 0 as u16).await?; } }, app_evt = app_stream.next() => { match app_evt { Some(msg) => { match msg { ApplicationMessage::JoinComplete(token) => { println!("Joined with token {:016x}", token); //wait a bit for configuration to take effect sleep(Duration::from_secs(5)).await; println!("Attaching"); node = Some(mesh.attach(sim.clone(), &format!("{:016x}", token)).await?); start_sending(messages_tx.clone()); }, ApplicationMessage::JoinFailed(reason) => { println!("Failed to join: {}", reason); break; }, } }, None => break, } } } } println!("Shutting down"); drop(registered); sleep(Duration::from_secs(1)).await; Ok(()) } fn start_sending(sender: Sender) -> () { println!("Starting to send messages!"); let lines_sender = sender.clone(); tokio::spawn(async move { let mut interval = time::interval(Duration::from_secs(16)); loop { interval.tick().await; _ = sender.send(generate_message()).await; } }); std::thread::spawn(move || loop { let mut line = String::new(); std::io::stdin().read_line(&mut line).unwrap(); _ = lines_sender.clone().blocking_send(generate_message()); }); } fn generate_message() -> BoardSensorMessage { SensorMessage::Status(SensorStatus::new(Temperature(21.0))) } #[derive(Clone, Debug)] pub struct SensorModel; #[derive(Clone, Debug, Default)] pub struct Temperature(f32); impl SensorConfig for SensorModel { type Data = Temperature; const DESCRIPTORS: &'static [SensorDescriptor] = &[SensorDescriptor::new(PropertyId(0x4F), 1)]; } impl SensorData for Temperature { fn decode(&mut self, id: PropertyId, params: &[u8]) -> Result<(), ParseError> { if id.0 == 0x4F { self.0 = params[0] as f32 / 2.0; Ok(()) } else { Err(ParseError::InvalidValue) } } fn encode( &self, _: PropertyId, xmit: &mut heapless::Vec, ) -> Result<(), InsufficientBuffer> { let value = (self.0 * 2 as f32) as u8; xmit.extend_from_slice(&value.to_le_bytes()).map_err(|_| InsufficientBuffer)?; Ok(()) } } type BoardSensorMessage = SensorMessage; const COMPANY_IDENTIFIER: CompanyIdentifier = CompanyIdentifier(0x05F1); const COMPANY_MODEL: ModelIdentifier = ModelIdentifier::Vendor(COMPANY_IDENTIFIER, 0x0001); #[derive(Clone, Debug)] pub struct VendorModel; impl Model for VendorModel { const IDENTIFIER: ModelIdentifier = COMPANY_MODEL; type Message = VendorMessage; fn parse(_opcode: &Opcode, _parameters: &[u8]) -> Result, ParseError> { unimplemented!(); } } #[derive(Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum VendorMessage {} impl Message for VendorMessage { fn opcode(&self) -> Opcode { unimplemented!(); } fn emit_parameters( &self, _xmit: &mut heapless::Vec, ) -> Result<(), InsufficientBuffer> { unimplemented!(); } }