pamoja_can/lib.rs
1#![cfg_attr(not(any(test, feature = "bus")), no_std)]
2// The crate doc links the bus types, which a build without the `bus` feature lacks.
3#![cfg_attr(not(feature = "bus"), allow(rustdoc::broken_intra_doc_links))]
4
5//! CAN for the pamoja SDK: the frames, J1939, and a node on a bus.
6//!
7//! CAN is the bus that connects the moving parts of a machine: motor controllers, servos,
8//! battery management, and the engines, gensets, and farm equipment that speak J1939 on
9//! top of it. It is how a robot or a vehicle's pieces talk to each other reliably over a
10//! short, noisy two-wire link, which is why it is the SDK's path to actuators and to the
11//! diesel-and-hydraulic world of rural machinery.
12//!
13//! The byte layer needs no controller and no allocation:
14//!
15//! - [`CanId`] - a standard 11-bit or extended 29-bit identifier, always masked to width.
16//! - [`Frame`] - a classic CAN 2.0 frame, a CAN-FD frame at the discrete CAN-FD lengths,
17//! or a remote frame, with [`len_to_dlc`] and [`dlc_to_len`] for the length encoding
18//! CAN-FD uses above eight bytes.
19//! - [`J1939Id`] - the priority, parameter group, and addresses J1939 packs into a 29-bit
20//! identifier, decoded from one and composed back into one.
21//! - [`Signals`] - the eight data bytes of a J1939 frame, read and written by the offsets
22//! its parameter group publishes, starting every signal as not available.
23//!
24//! The controller hardware handles the wire itself (arbitration, bit timing, the frame
25//! CRC); this is the identifier and payload layer above it, the part an application
26//! actually reasons about.
27//!
28//! With the `bus` feature, [`bus::CanBus`] is one node's place on a bus: a bus simulated
29//! inside the program, or with the `linux` feature, a kernel interface such as `can0`
30//! through SocketCAN. A node hears every frame the others send and none of its own, and keeps
31//! the ones its [`bus::Filter`]s pass, by identifier or by J1939 parameter group.
32//!
33//! # Examples
34//!
35//! ```
36//! use pamoja_can::{CanId, Frame, J1939Id, Signals};
37//!
38//! // A classic frame: the motor controller at identifier 0x20A told to run at 500 rpm,
39//! // sent big-endian as its protocol asks.
40//! let command = Frame::new(CanId::standard(0x20A), &500u16.to_be_bytes())?;
41//! assert_eq!(command.dlc(), 2);
42//!
43//! // A J1939 broadcast: a genset's engine controller, at source address 0, reports its
44//! // speed in EEC1, parameter group 61444. The speed sits in bytes 4 and 5 of the payload,
45//! // offset 3 counting from zero, at 0.125 rpm per bit, and every signal the controller does
46//! // not report stays marked not available.
47//! const EEC1: u32 = 61_444;
48//! let mut payload = Signals::new();
49//! payload.set_u16(3, (1500.0 / 0.125) as u16);
50//! let report = Frame::new(J1939Id::broadcast(3, EEC1, 0).to_id(), payload.as_bytes())?;
51//!
52//! // A listener reads the group from the identifier and the speed from the payload.
53//! let heard = J1939Id::from_id(report.id()).expect("an extended identifier");
54//! assert_eq!(heard.pgn(), EEC1);
55//! assert!(heard.is_broadcast());
56//! let speed = report.signals().and_then(|signals| signals.u16(3)).expect("a speed");
57//! assert_eq!(f64::from(speed) * 0.125, 1500.0);
58//! # Ok::<(), pamoja_can::CanError>(())
59//! ```
60
61#[cfg(feature = "bus")]
62pub mod bus;
63mod error;
64mod frame;
65mod id;
66mod j1939;
67mod signals;
68
69pub use error::CanError;
70pub use frame::{dlc_to_len, len_to_dlc, Frame};
71pub use id::CanId;
72pub use j1939::{priority, J1939Id, BROADCAST_ADDRESS};
73pub use signals::{Signals, NOT_AVAILABLE};