Define the line rate once, in wiredsensor-core

BAUD_RATE was declared separately in firmware/src/board.rs and
bridge/src/main.rs with a comment asking that they be kept in agreement
by hand. A baud mismatch between two ends of an RS485 segment is silent
and presents as random CRC failures, which is a poor thing to debug and a
poor thing to leave to a comment.

It now lives in wiredsensor_core::timing alongside the arithmetic that
derives from it, because the line rate is a property of the segment
rather than of any one board. board.rs re-exports it, and the bridge
takes a dependency on core purely to read it — it still knows nothing
about the protocol itself.

INTER_FRAME_GAP_US and CHAR_TIME_US move with it and are now derived at
the definition site, with a test asserting they cannot go stale if the
rate changes.

Also fills two documentation gaps: the bridge and PC test suite were
committed without any README coverage, and the account of the RTIM
pitfall still described the incomplete understanding held before the
hardware test found the real mechanism.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Oliver Walter
2026-07-28 20:51:57 +02:00
parent 25dba97c06
commit 46c26796cc
6 changed files with 166 additions and 32 deletions
+30 -4
View File
@@ -1,12 +1,29 @@
//! Wire timing derived from the line rate.
//! The line rate, and the wire timing derived from it.
//!
//! Kept here rather than in the firmware so the arithmetic — which decides
//! whether frames are delimited correctly and whether the transceiver is
//! released at the right moment — is covered by host tests.
//! Kept here rather than in the firmware for two reasons. The arithmetic —
//! which decides whether frames are delimited correctly and whether the
//! transceiver is released at the right moment — is covered by host tests. And
//! [`BAUD_RATE`] is a property of the *segment*, not of any one board: the node
//! and every master on the bus must agree on it, so it is defined once here
//! rather than copied into each firmware and kept in sync by hand.
/// Line rate for the whole segment.
///
/// 19200 8N1 is the Modbus default and a safe starting point for long runs.
/// Changing it here changes it for every device built from this workspace,
/// which is the point — a mismatch between two ends is silent and looks like
/// random CRC failures.
pub const BAUD_RATE: u32 = 19_200;
/// Bits on the wire per character: 1 start + 8 data + 1 stop, i.e. 8N1.
pub const BITS_PER_CHAR: u32 = 10;
/// Inter-frame idle gap at [`BAUD_RATE`], in microseconds.
pub const INTER_FRAME_GAP_US: u32 = inter_frame_gap_us(BAUD_RATE);
/// Time to clock out one character at [`BAUD_RATE`], in microseconds.
pub const CHAR_TIME_US: u32 = char_time_us(BAUD_RATE);
/// Idle time that delimits one frame from the next, in microseconds.
///
/// Modbus specifies 3.5 character times, but pins the value at a fixed 1750 µs
@@ -79,4 +96,13 @@ mod tests {
assert_eq!(tx_time_us(1, 19_200), char_time_us(19_200));
assert_eq!(tx_time_us(21, 19_200), 21 * 520);
}
/// The derived constants must track whatever [`BAUD_RATE`] is set to, so
/// changing the line rate cannot leave a stale gap or character time behind.
#[test]
fn derived_constants_follow_the_configured_rate() {
assert_eq!(INTER_FRAME_GAP_US, inter_frame_gap_us(BAUD_RATE));
assert_eq!(CHAR_TIME_US, char_time_us(BAUD_RATE));
assert!(INTER_FRAME_GAP_US > CHAR_TIME_US);
}
}