Skip to content
Plugins (183)

can

stable

Encode and decode CAN 2.0, CAN FD, and RTR frames, work with J1939 29-bit identifiers (PGN, priority, source address), and apply acceptance filters — pure protocol helpers with no bus I/O.

use plugin can::{encode_frame, decode_frame, j1939_pgn, …}
13 functions Systems
Runs with the native Zolo host

Plugin snippets can be edited in the Playground, but native capabilities are executed locally with the Zolo CLI.

/ filter jk navigate Esc clear
Functions (13)
  1. encode_frame Encode a CAN 2.0 data frame into bytes
  2. decode_frame Decode a frame back into id, data, and flags
  3. j1939_pgn Extract the Parameter Group Number from a J1939 ID
  4. j1939_source Extract the source address from a J1939 ID
  5. j1939_priority Extract the priority from a J1939 ID
  6. build_j1939_id Build a 29-bit J1939 CAN ID from its parts
  7. dlc_to_length Map a DLC code to its payload length
  8. length_to_dlc Map a payload length to its DLC code
  9. is_standard_id Check if an ID fits in 11 bits
  10. is_extended_id Check if an ID fits in 29 bits
  11. encode_rtr Encode a remote transmission request frame
  12. filter_match Test an ID against a filter/mask pair
  13. encode_fd_frame Encode a CAN FD frame with up to 64 data bytes

From import to result

  1. 01Import only what you use
  2. 02Copy the smallest example
  3. 03Run it with the Zolo CLI

Overview

The can plugin is a pure, I/O-free toolkit for the Controller Area Network family of protocols: classic CAN 2.0, CAN FD, and the SAE J1939 higher-layer protocol used in heavy-duty vehicles. It has no concept of a live bus — instead it turns frames into byte buffers and back, packs and unpacks 29-bit J1939 identifiers, maps between DLC codes and payload lengths, and evaluates hardware-style acceptance filters. Frames are represented as plain byte arrays produced by the encode_* functions and read back with decode_frame, which returns a table of {id, data, extended, rtr, dlc}. Reach for it when you need to build, parse, or route CAN traffic in software while the actual transmit/receive happens elsewhere (a driver, a socket, or a test harness).

Common patterns

Round-trip a classic frame through encode and decode:

use plugin can::{encode_frame, decode_frame}

let frame = encode_frame(0x1A0, [0x11, 0x22, 0x33], false)
let msg = decode_frame(frame)
print("id={msg["id"]} dlc={msg["dlc"]} rtr={msg["rtr"]}")

Compose and then decompose a J1939 identifier:

use plugin can::{build_j1939_id, j1939_pgn, j1939_priority, j1939_source}

let id = build_j1939_id(6, 65265, 17)   // priority 6, PGN 65265, source 0x11
print("pgn={j1939_pgn(id)} prio={j1939_priority(id)} src={j1939_source(id)}")

Apply an acceptance filter before processing an incoming frame:

use plugin can::{decode_frame, filter_match}

let msg = decode_frame(incoming)
// Only accept IDs whose top nibble is 0x1
if filter_match(msg["id"], 0x100, 0x700) {
  print("accepted id {msg["id"]}")
}

Encode a CAN 2.0 data frame into bytes

Encodes a classic CAN 2.0 data frame as id (4 bytes big-endian) + flags (1 byte: bit0=extended, bit1=rtr) + DLC (1 byte) + data. data may be at most 8 bytes; extended is optional and defaults to false. Errors if a standard ID exceeds 0x7FF or an extended ID exceeds 0x1FFFFFFF.

use plugin can::{encode_frame}

let frame = encode_frame(0x123, [0x01, 0x02, 0x03, 0x04], false)
print("frame: {frame}")

An extended (29-bit) frame sets the extended flag, which lets the ID exceed 0x7FF:

use plugin can::{encode_frame, decode_frame}

let frame = encode_frame(0x18FF0011, [0xAA, 0xBB], true)
let msg = decode_frame(frame)
print("extended={msg["extended"]} id={msg["id"]}")

Decode a frame back into id, data, and flags

Decodes a frame produced by encode_frame, encode_rtr, or encode_fd_frame back into its parts. Errors if the input is shorter than the 6-byte header.

use plugin can::{encode_frame, decode_frame}

let frame = encode_frame(0x123, [0xDE, 0xAD], false)
let msg = decode_frame(frame)
print("id={msg["id"]} dlc={msg["dlc"]} extended={msg["extended"]}")

Extract the Parameter Group Number from a J1939 ID

Extracts the 18-bit Parameter Group Number from a 29-bit J1939 CAN ID. For PDU1 messages (PDU Format < 240) the PDU Specific byte is a destination address and is excluded from the PGN; for PDU2 messages it is included.

use plugin can::{j1939_pgn}

// Engine speed (EEC1) message
let pgn = j1939_pgn(0x0CF00400)
print("PGN: {pgn}")  // 61444

Extract the source address from a J1939 ID

Extracts the source address (bits 0-7) from a 29-bit J1939 CAN ID, identifying which ECU sent the message.

use plugin can::{j1939_source}

let src = j1939_source(0x0CF00400)
print("source: {src}")  // 0

Extract the priority from a J1939 ID

Extracts the message priority (bits 26-28) from a 29-bit J1939 CAN ID. Priority 0 is highest, 7 is lowest.

use plugin can::{j1939_priority}

let prio = j1939_priority(0x0CF00400)
print("priority: {prio}")  // 3

Build a 29-bit J1939 CAN ID from its parts

Builds a 29-bit J1939 CAN ID from a priority (0-7), an 18-bit PGN (≤ 0x3FFFF), and a source address (0-255). Inverse of the j1939_* extractors.

use plugin can::{build_j1939_id, j1939_pgn}

let id = build_j1939_id(3, 61444, 0)
print(j1939_pgn(id) == 61444)  // true

Use it to address a specific ECU by source, then encode a frame for that ID:

use plugin can::{build_j1939_id, encode_frame}

let id = build_j1939_id(6, 65265, 0x11)
let frame = encode_frame(id, [0x00, 0x7D, 0x7D], true)
print("frame bytes: {frame}")

Map a DLC code to its payload length

Maps a DLC code (0-15) to the actual payload length in bytes. DLC 0-8 map directly; DLC 9-15 use the CAN FD lengths 12, 16, 20, 24, 32, 48, and 64.

use plugin can::{dlc_to_length}

print(dlc_to_length(8))   // 8
print(dlc_to_length(15))  // 64

Map a payload length to its DLC code

Maps a payload length (0-64 bytes) to its DLC code, rounding up to the next valid CAN FD length. Inverse of dlc_to_length.

use plugin can::{length_to_dlc}

print(length_to_dlc(8))   // 8
print(length_to_dlc(20))  // 11
print(length_to_dlc(33))  // 14 (rounds up to 48 bytes)

Check if an ID fits in 11 bits

Returns true if the ID fits in 11 bits (≤ 0x7FF), i.e. it is valid as a standard CAN identifier.

use plugin can::{is_standard_id}

print(is_standard_id(0x7FF))  // true
print(is_standard_id(0x800))  // false

Check if an ID fits in 29 bits

Returns true if the ID fits in 29 bits (≤ 0x1FFFFFFF), i.e. it is valid as an extended CAN identifier.

use plugin can::{is_extended_id}

print(is_extended_id(0x0CF00400))  // true
print(is_extended_id(0x20000000))  // false

Encode a remote transmission request frame

Encodes a remote transmission request frame: same layout as encode_frame but with the RTR flag set and no data bytes. dlc (0-8) tells the responder how many bytes are being requested; extended is optional and defaults to false.

use plugin can::{encode_rtr, decode_frame}

let req = encode_rtr(0x123, 8, false)
let msg = decode_frame(req)
print("rtr={msg["rtr"]} dlc={msg["dlc"]}")  // rtr=true dlc=8

Test an ID against a filter/mask pair

Tests a CAN ID against an acceptance filter, returning true when (id & mask) == (filter & mask) — the same semantics used by hardware acceptance filters. Bits where the mask is 0 are don't-care.

use plugin can::{filter_match}

// Accept any ID in the 0x100-0x10F range
print(filter_match(0x105, 0x100, 0x7F0))  // true
print(filter_match(0x205, 0x100, 0x7F0))  // false

Encode a CAN FD frame with up to 64 data bytes

Encodes a CAN FD frame with up to 64 data bytes, computing the DLC code from the data length automatically. The flags byte sets bit2=fd, plus bit0 when extended and bit3 when brs (bit rate switch); both flags are optional and default to false.

use plugin can::{encode_fd_frame, decode_frame}

let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
let frame = encode_fd_frame(0x123, data, false, true)
let msg = decode_frame(frame)
print("dlc={msg["dlc"]}")  // dlc=9 (12 bytes)

Global index

Find your way through Zolo

Try an idea

Start here

9 results

9 results

en