Expand description
A pamoja transport over a LoRa radio, carrying topics in pamoja-mesh frames.
MeshRadio turns a radio into a [Transport] and a [Receive]. A message goes out
as a broadcast [Frame] whose payload is the topic’s length in one byte, the topic, and
the payload. Every node that hears it drops copies it has already seen, delivers what
its subscriptions match, and relays the frame onward while hops remain, so a message
crosses a mesh of radios that each hear only their neighbors. A DutyCycle holds the
radio silent for the off time the region requires after each transmission, its own
messages and its relays alike.
The radio is driven from the task that awaits the transport. It is read every POLL,
with the tokio timer sleeping in between, so neither a frame’s airtime nor a quiet
channel blocks the runtime. Topic filters follow MQTT, through [topic_matches].
§Examples
Two nodes whose air is a queue each, with the frame carried across by hand:
use std::collections::VecDeque;
use std::convert::Infallible;
use pamoja_core::{Receive, Transport};
use pamoja_lora::LinkSettings;
use pamoja_radios::mesh::{LoraRadio, MeshRadio};
#[derive(Default)]
struct Air(VecDeque<Vec<u8>>);
impl LoraRadio for Air {
type Error = Infallible;
fn link(&self) -> Option<LinkSettings> {
Some(LinkSettings::new(7, 125_000))
}
fn start_transmit(&mut self, frame: &[u8]) -> Result<u64, Infallible> {
self.0.push_back(frame.to_vec());
Ok(LinkSettings::new(7, 125_000).airtime_us(frame.len()))
}
fn finish_transmit(&mut self) -> Result<bool, Infallible> {
Ok(true)
}
fn listen(&mut self) -> Result<(), Infallible> {
Ok(())
}
fn take_frame(&mut self, buffer: &mut [u8]) -> Result<Option<usize>, Infallible> {
Ok(self.0.pop_front().map(|frame| {
buffer[..frame.len()].copy_from_slice(&frame);
frame.len()
}))
}
}
// A soil sensor on a 1% duty cycle, and a gateway that listens without relaying.
let mut sensor = MeshRadio::new(Air::default(), 0x0A, 10);
let mut gateway = MeshRadio::new(Air::default(), 0x0B, 10).without_relaying();
sensor.connect().await?;
gateway.connect().await?;
gateway.subscribe("garden/+/moisture").await?;
sensor.send_text("garden/bed-1/moisture", "28.5").await?;
let frame = sensor.radio_mut().0.pop_front().expect("the sensor transmitted");
gateway.radio_mut().0.push_back(frame);
let reading = gateway.recv().await?.expect("the gateway heard it");
assert_eq!(reading.topic, "garden/bed-1/moisture");
assert_eq!(reading.number()?, 28.5);Structs§
- Mesh
Radio - A node on a LoRa mesh: a radio, the node’s address, and the rules it keeps.
Constants§
- MAX_
MESSAGE - The most topic and payload bytes one message carries together: a frame’s payload less the byte that holds the topic’s length.
- POLL
- How often the transport reads the radio for a received frame or a finished transmission.
- SEEN_
CAPACITY - How many recent frames a node remembers, so copies arriving by other paths are dropped.
- TRANSMIT_
GRACE - How long past a frame’s airtime the transport waits for the radio to report it sent.