Crate j1939_rs

Crate j1939_rs 

Source
Expand description

§j1939-rs

A Rust library for working with SAE J1939 protocol messages in embedded and automotive systems.

This library provides a powerful macro-based approach to defining, encoding, and decoding J1939 messages with compile-time validation and automatic documentation generation.

§Features

  • Type-safe message definitions using Rust structs
  • Automatic marshalling/unmarshalling with the Marshall and Unmarshall traits
  • Bit-level field packing with arbitrary bit ranges
  • Scaling and offset support for physical unit conversions
  • Enum support with automatic documentation generation
  • Compile-time validation of message layouts
  • Comprehensive documentation automatically generated from your definitions
  • no_std compatible for embedded systems

§Quick Start

Add 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 EngineController {
    /// 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: (),
}

// Create and encode a message
let msg = EngineController {
    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 = EngineController::unmarshall(&j1939_msg).unwrap();
assert_eq!(decoded.engine_speed, 1850.0);

§Working with Enums

use j1939_rs::prelude::*;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
#[j1939_enum]
/// Transmission gear selection
pub enum GearPosition {
    Park = 0,
    Reverse = 1,
    Neutral = 2,
    Drive = 3,
}

#[j1939_message(pgn = 65265)]
pub struct TransmissionStatus {
    #[j1939(bits = 0..2)]
    pub current_gear: GearPosition,

    #[j1939(bits = 2..64, reserved)]
    pub reserved: (),
}

§Core Concepts

§Messages

Messages are defined using the #[j1939_message] attribute macro on structs. Each message has:

  • A PGN (Parameter Group Number) - unique identifier
  • A priority level (0-7, where 0 is highest)
  • A length in bits (defaults to 64 bits / 8 bytes)

§Fields

Each field in a message struct must specify its bit range using #[j1939(bits = start..end)]. Additional attributes control encoding:

  • scale: Multiply/divide factor for float values
  • offset: Additive offset for values (e.g., temperature with -40°C offset)
  • encoding: Special encoding schemes like “q9” for fixed-point
  • unit: Documentation string for physical units
  • reserved: Mark unused bits

§Marshalling

The Marshall trait encodes your struct into a J1939Message:

let msg = EngineController { engine_speed: 1850.0, reserved: () };
let mut j1939_msg = J1939Message::default();
msg.marshall(&mut j1939_msg)?;

§Unmarshalling

The Unmarshall trait decodes a J1939Message back into your struct:

let decoded = EngineController::unmarshall(&j1939_msg)?;

§Prelude

For convenience, import everything you need with:

use j1939_rs::prelude::*;

This brings in all macros, traits, and core types.

§Embedded & no_std Support

This library is designed for embedded systems and is fully no_std compatible:

  • No dynamic allocations: All operations use fixed-size buffers on the stack
  • No standard library: Works in bare-metal embedded environments
  • Compile-time code generation: The proc macros run during compilation on your host machine
  • Runtime efficiency: Generated code is zero-cost abstractions with no overhead

§Architecture

The library has two execution contexts:

  1. Compile-time (proc macros): The j1939-macros crate uses std because it runs during compilation on your development machine. This is normal and required for proc macros.

  2. Runtime (target): The j1939-core and generated code are no_std and run on your embedded target without requiring heap allocation or the standard library.

§Memory Usage

  • Message structures: Stack-allocated, fixed size
  • J1939Message buffer: 1785 bytes maximum (SAE J1939 multi-packet limit)
  • No heap allocations during encoding/decoding
  • All transformations compile to inline arithmetic

§Example for Embedded

#![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 allocations
fn send_engine_data() {
    let msg = EngineSpeed { rpm: 1850.0, reserved: () };
    let mut buffer = J1939Message::default();
    msg.marshall(&mut buffer).unwrap();
}

§See Also

Modules§

bitfield
fixed_point
message
prelude
Convenient prelude that imports all commonly used items.

Structs§

J1939Message
A J1939 CAN message containing header information and payload data.
Q9
A Q9 fixed-point number representation for precise fractional values.

Enums§

Error

Traits§

Marshall
Trait for encoding structured data into J1939 message format.
Unmarshall
Trait for decoding J1939 messages into structured data.

Functions§

decode_bitfield
Decodes a value from a bitfield at the specified bit offset.
encode_bitfield
Encodes a value into a bitfield at the specified bit offset.
round_f32
Round f32s in a no_std environment.
sign_extend
Sign-extends a value based on its bit length.

Type Aliases§

Result

Attribute Macros§

j1939_enum
Attribute macro for registering enums used in J1939 messages.
j1939_message
Attribute macro for defining J1939 message structures with automatic marshalling/unmarshalling.