pamoja-can
CAN 2.0 and CAN-FD frames with 11- and 29-bit identifiers, plus J1939 decode and compose. One capability of pamoja, one memory-safe Rust core with bindings for TypeScript, Python, and C#.
Install
pip install pamoja-can
from pamoja import can
This pulls in pamoja-native, the compiled engine. pip install pamoja is the whole framework in one package.
Example
The script the test suite runs, spliced here as it ran.
From bindings/python/guides/can.py:
from pamoja.can import (
NOT_AVAILABLE,
Priority,
broadcast_j1939,
compose_j1939,
decode_j1939,
fd_frame,
frame,
signals,
signals_from,
)
from pamoja.core import PamojaError
# The nodes on this bus, by the address each answers to, and the two parameter groups
# in play. J1939 publishes both, so naming them is what makes the traffic readable.
ENGINE = 0
GATEWAY = 1
GEARBOX = 33
ENGINE_CONTROLLER_1 = 61_444 # carries engine speed
REQUEST = 59_904 # asks another node for a parameter group
# Where engine speed sits inside that group, and the scale the standard fixes for it.
# Naming both is what stops a sender and a receiver disagreeing about either.
ENGINE_SPEED_AT = 3
RPM_PER_BIT = 0.125
# J1939 keeps its addressing inside the CAN identifier: a priority, the parameter
# group, and the address of whatever sent it. A broadcast has no destination, so it is
# its own constructor rather than a magic address a caller has to know.
speed_id = broadcast_j1939(Priority.CONTROL, ENGINE_CONTROLLER_1, ENGINE)
speed = decode_j1939(speed_id)
print(f"broadcast pgn {speed.pgn} at priority {speed.priority}")
# A parameter group below the PDU1 limit is addressed rather than broadcast, so those
# eight identifier bits carry a destination instead of extending the group number.
request_id = compose_j1939(Priority.DEFAULT, REQUEST, GATEWAY, GEARBOX)
print(f"request pgn {decode_j1939(request_id).pgn} addressed to node {GEARBOX}")
# Reading one back off the bus is the same thing in reverse, so a receiver never
# unpacks 29 bits by hand.
heard = decode_j1939(request_id)
print(f"heard from node {heard.source} for node {heard.destination}")
# The payload. Every signal starts marked not available, and this controller reports
# only engine speed, so that is the only one it writes.
reported = signals()
reported.set_u16(ENGINE_SPEED_AT, int(1000 / RPM_PER_BIT))
eec1 = frame(speed_id, reported.bytes, extended=True)
# The receiving node reads the same offset back, so neither end slices the payload.
rpm = signals_from(eec1.data).u16(ENGINE_SPEED_AT) * RPM_PER_BIT
print(f"engine {rpm} rpm, carried in {eec1.dlc} bytes")
# Above eight bytes CAN-FD encodes the length in steps rather than exactly, and a
# classic frame still refuses a ninth byte.
print(f"32 bytes carries length code {fd_frame(speed_id, bytes(32), extended=True).dlc}")
try:
frame(speed_id, bytes(9), extended=True)
print("a classic frame took nine bytes, which should never happen")
except PamojaError as error:
print(f"classic refused nine bytes: {error}")
# J1939 never rides an 11-bit identifier, so a standard frame is not one of its
# messages however its bits happen to line up.
print(f"an 11-bit identifier is J1939: {decode_j1939(291, extended=False) is not None}")
The same capability in every language
| Language | Package | Reference |
|---|---|---|
| Rust | pamoja-can |
reference, docs.rs, install |
| TypeScript | @pamoja/can |
reference, install |
| Python | pamoja-can |
reference, install |
| C# | Pamoja.Can |
reference, install |
Documentation
pamoja.canreference, every class and function in this module.- The CAN and J1939 guide, with the same example in Rust, TypeScript, and C#.
- Every capability, and the install page.
License
MIT
Release files for pamoja-can 0.1.18
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pamoja_can-0.1.18.tar.gz | 5.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pamoja_can-0.1.18-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 11.3 kB
Release files / pamoja_can-0.1.18.tar.gz
| Download URL | pamoja_can-0.1.18.tar.gz |
|---|---|
| Size | 5.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9f4fc74099e9d596477360d0f416409ca21f8fd9739f5a7593dc9c9e2adcb260
|
|
BLAKE2b-256 checksum How to use checksums |
d10879ed24d193e11289694a3b82a3cc7e3db90251a49b2893d18cd06a557e90
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.15
|
Release files / pamoja_can-0.1.18-py3-none-any.whl
| Download URL | pamoja_can-0.1.18-py3-none-any.whl |
|---|---|
| Size | 6.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bcb482bfa48301fa69b43d353adef8be7fd533dcf79796db66043dedac36dae3
|
|
BLAKE2b-256 checksum How to use checksums |
59bc01a53beeb9c7fb69c8db654cfa59f21d68055c5e5bd22168b329607513a0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.15
|