Skip to main content

pamoja_modbus/
lib.rs

1#![cfg_attr(not(test), no_std)]
2
3//! Modbus RTU framing for the pamoja SDK.
4//!
5//! Modbus is the lingua franca of cheap industrial sensing. Soil NPK probes, energy
6//! meters, water-quality transmitters, and pump controllers overwhelmingly speak Modbus
7//! over RS485, a serial bus that reaches hundreds of metres down a single cable, which
8//! is exactly what a dispersed farm or a rural water network needs. To talk to those
9//! devices a node has to put the right bytes on the wire and trust the bytes it gets
10//! back, and Modbus RTU pins down precisely what those bytes are.
11//!
12//! This crate is that byte layer, with no serial port and no allocation:
13//!
14//! - [`crc16`] - the CRC-16/MODBUS that every RTU frame ends with, the check that lets a
15//!   receiver reject a frame mangled by electrical noise on a long cable.
16//! - [`Pdu`] - the protocol data unit: a function code and its data. Constructors build
17//!   the standard requests (read and write coils and registers) so callers never hand-pack
18//!   a frame, with a [`raw`](Pdu::raw) escape hatch for the function codes this crate does
19//!   not name.
20//! - [`Adu`] - the RTU application data unit: a unit address, a PDU, and the CRC. It both
21//!   [assembles](Adu::from_pdu) a frame to send and [parses](Adu::parse) one received,
22//!   verifying the CRC so a corrupt frame never reaches the application.
23//! - [`Response`] - reads the values back out of a reply: the 16-bit registers of a
24//!   read-registers response and the packed bits of a read-coils response, plus the
25//!   [`Exception`] a device returns when it refuses a request.
26//!
27//! Everything is exact integer and byte work, so the same framing runs on the smallest
28//! microcontroller hanging off the bus. Driving the RS485 line itself arrives with the
29//! hardware-I/O layer; this is the protocol half ahead of it.
30//!
31//! # Examples
32//!
33//! ```
34//! use pamoja_modbus::{Adu, Pdu};
35//!
36//! // Ask unit 0x11 for three holding registers starting at 0x006B.
37//! let request = Pdu::read_holding_registers(0x006B, 3).to_adu(0x11);
38//! assert_eq!(request.as_bytes(), &[0x11, 0x03, 0x00, 0x6B, 0x00, 0x03, 0x76, 0x87]);
39//!
40//! // The device replies with three 16-bit registers; the frame carries its own CRC,
41//! // so a receiver validates it before reading the values.
42//! let on_wire = Adu::from_pdu(0x11, &[0x03, 0x06, 0x02, 0x2B, 0x00, 0x00, 0x00, 0x64])?;
43//! let reply = Adu::parse(on_wire.as_bytes())?;
44//! let registers: Vec<u16> = reply.response().registers()?.collect();
45//! assert_eq!(registers, [0x022B, 0x0000, 0x0064]);
46//! # Ok::<(), pamoja_modbus::ModbusError>(())
47//! ```
48
49mod adu;
50mod crc;
51mod error;
52mod function;
53mod pdu;
54mod response;
55
56pub use adu::Adu;
57pub use crc::crc16;
58pub use error::ModbusError;
59pub use function::{Exception, Function};
60pub use pdu::Pdu;
61pub use response::{Coils, Registers, Response};