| Crates.io | j1939-rs |
| lib.rs | j1939-rs |
| version | 0.1.1 |
| created_at | 2025-10-15 20:38:46.296801+00 |
| updated_at | 2025-10-15 20:40:33.706878+00 |
| description | A Rust library for working with SAE J1939 protocol messages in embedded and automotive systems. |
| homepage | https://github.com/cojmeister/j1939-rs |
| repository | https://github.com/cojmeister/j1939-rs |
| max_upload_size | |
| id | 1884922 |
| size | 49,905 |
A Rust library for working with SAE J1939 protocol messages in embedded and automotive systems.
bits = 5..13)no_std compatible - works in bare-metal embedded environmentsAdd this to your Cargo.toml:
[dependencies]
j1939-rs = "0.1"
Define a J1939 message:
use j1939_rs::prelude::*;
#[j1939_message(pgn = 61444, priority = 3)]
pub struct ElectronicEngineController1 {
/// Engine speed in RPM
#[j1939(bits = 0..16, scale = 0.125, unit = "rpm")]
pub engine_speed: f32,
/// Actual engine torque as percentage
#[j1939(bits = 16..24, scale = 1.0, offset = -125.0, unit = "%")]
pub actual_torque: f32,
#[j1939(bits = 24..64, reserved)]
pub reserved: (),
}
fn main() {
// Create and encode a message
let msg = ElectronicEngineController1 {
engine_speed: 1850.0,
actual_torque: 45.0,
reserved: (),
};
let mut j1939_msg = J1939Message::default();
msg.marshall(&mut j1939_msg).unwrap();
// Decode a message
let decoded = ElectronicEngineController1::unmarshall(&j1939_msg).unwrap();
assert_eq!(decoded.engine_speed, 1850.0);
}
Define type-safe enums for message fields:
use j1939_rs::prelude::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
#[j1939_enum]
pub enum TorqueMode {
LowIdle = 0,
AcceleratorControl = 1,
CruiseControl = 2,
SpeedControl = 3,
}
#[j1939_message(pgn = 61444)]
pub struct EngineController {
#[j1939(bits = 0..4)]
pub torque_mode: TorqueMode,
#[j1939(bits = 4..20, scale = 0.125, unit = "rpm")]
pub engine_speed: f32,
#[j1939(bits = 20..64, reserved)]
pub reserved: (),
}
The library supports multiple encoding strategies for different data types:
| Field Type | Attributes | Encoding | Description |
|---|---|---|---|
u8, u16, u32 |
- | UInt | Direct unsigned integer |
i8, i16, i32 |
- | SInt | Signed integer with sign extension |
f32 |
scale, offset? |
Scaled | Linear transformation: (raw * scale) + offset |
f32 |
encoding = "q9" |
Q9 | Fixed-point format (10 bits: 1 sign + 9 fractional) |
| Custom enum | - | Enum | Type-safe enum (requires #[repr(u8)] and #[j1939_enum]) |
() |
reserved |
Reserved | Unused bits set to zero |
This library is fully no_std compatible and designed for embedded systems:
#![no_std]
use j1939_rs::prelude::*;
#[j1939_message(pgn = 61444)]
pub struct EngineSpeed {
#[j1939(bits = 0..16, scale = 0.125)]
pub rpm: f32,
#[j1939(bits = 16..64, reserved)]
pub reserved: (),
}
// All of this works without std or heap allocations
fn send_engine_data() {
let msg = EngineSpeed { rpm: 1850.0, reserved: () };
let mut buffer = J1939Message::default();
msg.marshall(&mut buffer).unwrap();
// Send buffer over CAN bus...
}
J1939Message buffer: 1785 bytes maximum (SAE J1939 multi-packet limit)Comprehensive documentation is available at docs.rs/j1939-rs.
The #[j1939_message] macro automatically generates detailed documentation tables for each message, including:
Check out the examples directory for complete working examples:
electronic_engine_controller_1.rs - Standard J1939 EEC1 message with enums and scalingRun an example with:
cargo run --example electronic_engine_controller_1
This workspace contains three crates:
no_std)The project includes comprehensive tests:
proptesttrybuildRun all tests with:
cargo test --all
This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.
This library implements the SAE J1939 protocol as specified in the SAE J1939 standards for vehicle networking.