Skip to main content

pamoja_mesh/
lib.rs

1#![cfg_attr(not(test), no_std)]
2
3//! Mesh packet framing for the pamoja SDK.
4//!
5//! When the fixed infrastructure is gone or was never there, devices have to carry each
6//! other's traffic. A flood-warning sensor on a riverbank, a handheld in a search team, a
7//! solar node on a rooftop: each can hear only its nearest neighbours over a cheap radio,
8//! yet a message has to cross the whole area. The answer is a mesh, where every node
9//! relays what it hears so a packet hops node to node until it arrives. This is the
10//! messaging backbone for exactly the places the SDK is built for, and it rides on the
11//! cheapest radios there are, the connectionless ESP-NOW of an ESP32 swarm and the
12//! pennies-per-node nRF24, neither of which gives you addressing, hops, or integrity on
13//! its own.
14//!
15//! This crate is that missing layer, as pure logic with no radio and no allocation:
16//!
17//! - [`Frame`] - an addressed packet: a source and destination node, a sequence number,
18//!   a hop limit, a payload, and a checksum. It [encodes](Frame::new) a packet to send
19//!   and [parses](Frame::parse) one received, rejecting anything a noisy radio mangled.
20//!   The checksum deliberately covers everything except the hop limit, so a packet's
21//!   integrity check is end to end and survives relaying unchanged.
22//! - [`Frame::relayed`] - the forwarding primitive: the same packet with one hop spent,
23//!   or nothing once its hops run out, which is what stops a flood from circulating
24//!   forever.
25//! - [`SeenCache`] - the duplicate suppressor: a fixed-size memory of recently seen
26//!   packets, so a node relays each packet once however many copies reach it across the
27//!   mesh. Without it a flood multiplies without bound.
28//!
29//! Together these are enough to build a flood: receive a frame, drop it if it is a
30//! duplicate, use it if it is for you, and relay it onward if it still has hops. Driving
31//! an actual radio arrives with the hardware-I/O layer; this is the packet half ahead of
32//! it.
33//!
34//! # Examples
35//!
36//! ```
37//! use pamoja_mesh::{Frame, SeenCache};
38//!
39//! // A flood-warning sensor broadcasts a reading into the mesh.
40//! let reading = Frame::broadcast(0x1234_5678, 1, b"level=high")?;
41//! let on_air = reading.as_bytes();
42//!
43//! // A neighbour receives it, checks it has not already seen this packet, and reads it.
44//! let mut seen: SeenCache<32> = SeenCache::new();
45//! let received = Frame::parse(on_air)?;
46//! assert!(received.is_broadcast());
47//! assert_eq!(received.payload(), b"level=high");
48//! assert!(seen.record(received.dedup_key())); // true: new to us
49//!
50//! // It forwards the packet one hop further into the mesh.
51//! let forwarded = received.relayed().unwrap();
52//! assert_eq!(forwarded.hop_limit(), received.hop_limit() - 1);
53//!
54//! // The same packet arriving again by another path is recognised and dropped.
55//! assert!(!seen.record(received.dedup_key()));
56//! # Ok::<(), pamoja_mesh::MeshError>(())
57//! ```
58
59// `cfg(test)` already builds this crate against std, so the runtime-sized cache compiles
60// for the test run whether or not the feature is on, and its tests always execute.
61#[cfg(any(feature = "alloc", test))]
62extern crate alloc;
63
64mod crc;
65mod error;
66mod frame;
67mod seen;
68
69pub use crc::{crc16, Crc16};
70pub use error::MeshError;
71pub use frame::{Frame, BROADCAST};
72#[cfg(any(feature = "alloc", test))]
73pub use seen::DynamicSeenCache;
74pub use seen::SeenCache;