-
-
Notifications
You must be signed in to change notification settings - Fork 37
feat:add example for BLE send message #81
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
adria0
wants to merge
2
commits into
meshtastic:main
Choose a base branch
from
adria0:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,229 @@ | ||
| //! Example: send_message_ble.rs | ||
| //! | ||
| //! This example demonstrates how to use the Meshtastic Rust API to send a message over | ||
| //! a Bluetooth Low Energy (BLE) connection and checking for its ACK. | ||
| //! | ||
| //! Usage: | ||
| //! cargo run --example send_message_ble --features="bluetooth-le tokio" -- <DESTINATION_NODE | BROADCAST> <MESSAGE> [BLE_DEVICE_NAME] | ||
| //! | ||
| //! If the BLE_DEVICE_NAME is not provided, the example will scan and prompt to specify a device if multiple | ||
| //! are found. | ||
| //! | ||
| //! if the DESTINATION_NODE is not found the example will dump all destination nodes found. | ||
|
|
||
| use std::collections::{BTreeMap, VecDeque}; | ||
| use std::convert::Infallible; | ||
| use std::io::Write; | ||
| use std::time::Duration; | ||
|
|
||
| use meshtastic::api::StreamApi; | ||
| use meshtastic::packet::{PacketDestination, PacketRouter}; | ||
| use meshtastic::protobufs::from_radio::PayloadVariant; | ||
| use meshtastic::protobufs::{mesh_packet, Data, MyNodeInfo, User}; | ||
| use meshtastic::protobufs::{FromRadio, MeshPacket, PortNum}; | ||
| use meshtastic::types::{MeshChannel, NodeId}; | ||
| use meshtastic::utils::generate_rand_id; | ||
| use meshtastic::utils::stream::{build_ble_stream, BleId}; | ||
| use meshtastic::Message; | ||
|
|
||
| struct Router { | ||
| sent: VecDeque<MeshPacket>, | ||
| node_id: NodeId, | ||
| } | ||
| impl Router { | ||
adria0 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| fn new(node_id: NodeId) -> Self { | ||
| Self { | ||
| sent: VecDeque::new(), | ||
| node_id, | ||
| } | ||
| } | ||
| } | ||
| impl PacketRouter<(), Infallible> for Router { | ||
| fn handle_packet_from_radio(&mut self, _packet: FromRadio) -> Result<(), Infallible> { | ||
| Ok(()) | ||
| } | ||
| fn handle_mesh_packet(&mut self, packet: MeshPacket) -> Result<(), Infallible> { | ||
| println!("Mesh packet sent.."); | ||
| self.sent.push_back(packet); | ||
adria0 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Ok(()) | ||
| } | ||
| fn source_node_id(&self) -> NodeId { | ||
| self.node_id | ||
| } | ||
| } | ||
|
|
||
| enum RecievedPacket { | ||
adria0 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| RoutingApp(Data), | ||
| MyInfo(MyNodeInfo), | ||
| NodeInfo(NodeId, User), | ||
| Other, | ||
| } | ||
| impl From<FromRadio> for RecievedPacket { | ||
| fn from(from_radio: FromRadio) -> Self { | ||
| use RecievedPacket::*; | ||
| let Some(payload) = from_radio.payload_variant else { | ||
| return Other; | ||
| }; | ||
| match payload { | ||
| PayloadVariant::MyInfo(my_node_info) => MyInfo(my_node_info), | ||
| PayloadVariant::NodeInfo(node_info) => { | ||
| if let Some(user) = node_info.user { | ||
| NodeInfo(NodeId::new(node_info.num), user) | ||
| } else { | ||
| Other | ||
| } | ||
| } | ||
| PayloadVariant::Packet(recv_packet) => { | ||
| let Some(pv) = recv_packet.payload_variant else { | ||
| return Other; | ||
| }; | ||
| let mesh_packet::PayloadVariant::Decoded(data) = pv else { | ||
| return Other; | ||
| }; | ||
| match PortNum::try_from(data.portnum) { | ||
| Ok(PortNum::RoutingApp) => RoutingApp(data), | ||
| Ok(PortNum::NodeinfoApp) => { | ||
| if let Ok(user) = User::decode(data.payload.as_slice()) { | ||
| NodeInfo(NodeId::new(recv_packet.from), user) | ||
| } else { | ||
| Other | ||
| } | ||
| } | ||
| _ => Other, | ||
| } | ||
| } | ||
| _ => Other, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| async fn get_ble_device() -> String { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 for showing discovery also, when device is not specified in the command line options. |
||
| println!("Scanning devices 5s, will connect if only one device is found,..."); | ||
| let devices = meshtastic::utils::stream::available_ble_devices(Duration::from_secs(5)) | ||
| .await | ||
| .expect("available_ble_devices failed"); | ||
|
|
||
| match devices.len() { | ||
| 0 => { | ||
| panic!("No BLE devices found"); | ||
| } | ||
| 1 => devices[0] | ||
| .name | ||
| .clone() | ||
| .expect("Device name should be present"), | ||
| _ => { | ||
| println!("Multiple BLE devices found: {:?}", devices); | ||
| panic!("Please specify a device as last argument"); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[tokio::main] | ||
| async fn main() { | ||
| let Some(to) = std::env::args().nth(1) else { | ||
| panic!("First argument should be the destination node or BROADCAST"); | ||
| }; | ||
| let Some(msg) = std::env::args().nth(2) else { | ||
| panic!("Second argument should be the message"); | ||
| }; | ||
| let ble_device = if let Some(ble_device) = std::env::args().nth(3) { | ||
| ble_device | ||
| } else { | ||
| get_ble_device().await | ||
| }; | ||
|
|
||
| // Initialize BLE stream | ||
| // ----------------------------------------------------------------------- | ||
| println!("Connecting to {}", ble_device); | ||
| let ble_stream = build_ble_stream(&BleId::from_name(&ble_device), Duration::from_secs(5)) | ||
| .await | ||
| .expect("Unable to build BLE stream"); | ||
|
|
||
| let stream_api = StreamApi::new(); | ||
| let (mut packet_rx, stream_api) = stream_api.connect(ble_stream).await; | ||
| let config_id = generate_rand_id(); | ||
| let mut stream_api = stream_api | ||
| .configure(config_id) | ||
| .await | ||
| .expect("Unable to open stream api"); | ||
|
|
||
| // Get MyInfo from the first message of stream | ||
| // ----------------------------------------------------------------------- | ||
| let from_radio = packet_rx.recv().await.expect("BLE stream closed"); | ||
| let RecievedPacket::MyInfo(my_node_info) = from_radio.into() else { | ||
| panic!("Failed to receive MyInfo"); | ||
| }; | ||
|
|
||
| println!("Got my node id {}", my_node_info.my_node_num); | ||
|
|
||
| // Retrieve all node names by processing incoming packets. | ||
| // This also clears the BLE connection buffer to free up space, | ||
| // ensuring there is room to send outgoing messages without issues. | ||
| // ----------------------------------------------------------------------- | ||
|
|
||
| // Map of node names to NodeId | ||
| let mut nodes: BTreeMap<_, _> = [(String::from("BROADCAST"), NodeId::new(u32::MAX))].into(); | ||
adria0 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| print!("Emptying I/O buffer & getting other nodes info..."); | ||
| loop { | ||
| tokio::select! { | ||
| packet = packet_rx.recv() => { | ||
| let packet = packet.expect("BLE stream closed"); | ||
| match RecievedPacket::from(packet).into() { | ||
| RecievedPacket::NodeInfo(node_id, node_info) => { | ||
| nodes.insert(node_info.short_name, node_id); | ||
| } | ||
| _ => {} | ||
| } | ||
| print!("."); | ||
| std::io::stdout().flush().unwrap(); | ||
| } | ||
| _ = tokio::time::sleep(Duration::from_millis(200)) => { | ||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| let Some(to) = nodes.get(&to) else { | ||
| println!("\nAvailable nodes: {:?}", nodes.keys()); | ||
| panic!("Specified node '{to}' not found"); | ||
| }; | ||
|
|
||
| // Send a message | ||
| // ----------------------------------------------------------------------- | ||
| print!("\nSending message..."); | ||
|
|
||
| let mut packet_router = Router::new(NodeId::new(my_node_info.my_node_num)); | ||
| stream_api | ||
| .send_text( | ||
| &mut packet_router, | ||
| msg, | ||
| PacketDestination::Node(*to), | ||
| true, | ||
| MeshChannel::default(), | ||
| ) | ||
| .await | ||
| .expect("Unable to send message"); | ||
|
|
||
| let sent_packet = packet_router.sent.pop_front().unwrap(); | ||
|
|
||
| // Wait for ACK | ||
| // ----------------------------------------------------------------------- | ||
| print!("Waiting for ACK (packet_id={})...", sent_packet.id); | ||
| std::io::stdout().flush().unwrap(); | ||
|
|
||
| loop { | ||
| let from_radio = packet_rx.recv().await.expect("BLE stream closed"); | ||
| match from_radio.into() { | ||
| RecievedPacket::RoutingApp(data) => { | ||
| if data.portnum == PortNum::RoutingApp as i32 && data.request_id == sent_packet.id { | ||
| println!("got ACK"); | ||
| break; | ||
| } | ||
| } | ||
| _ => {} | ||
| } | ||
| } | ||
|
|
||
| let _ = stream_api.disconnect().await.expect("Unable to disconnect"); | ||
| } | ||
adria0 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is
sentused for anything in the example, or do you just want to show that the router is informed when messages are sent by send_text() ?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is used to get the packet id, used later when checking for the ACK