| Crates.io | zencan-node |
| lib.rs | zencan-node |
| version | 0.0.3 |
| created_at | 2025-05-23 17:25:53.092844+00 |
| updated_at | 2026-01-20 19:56:11.543167+00 |
| description | Library for implementing an OPENCan node, primarily on an MCU |
| homepage | |
| repository | https://github.com/mcbridejc/zencan |
| max_upload_size | |
| id | 1686542 |
| size | 272,340 |
Crate for implementing a zencan device. Usually used in an embedded no_std context, but also can
be used elsewhere.
First create a TOML file to define the properties of your node. This includes information like the device name and identity information, as well as a list of application specific objects to be created. All nodes get a set of standard objects, in addition to the custom ones defined in the device config.
device_name = "can-io"
# The device identity includes the vendor, product, revision and a serial
# number. The serial number should be unique among all device instances, so it
# is provided by the application at run-time -- e.g. from a value stored in
# flash, or a MCU UID register.
[identity]
vendor_id = 0xCAFE
product_code = 32
revision_number = 1
# PDOs are used for sending "process data". Values which are transferred
# frequency on the bus, such as control commands or sensor readings, are
# typically sent and recieved using Transmit PDOs (TPDOs) and Receive PDOs
# (RPDOS). These are configured at run-time by writing to the appropriate PDO
# configuration objects, but the number of PDOs which can be configured must be
# defined here, at compile time.
[[pdos]]
num_tpdos = 4
num_rpdos = 4
# Create an array object to store the four u16 raw analog readings
# It is read-only, so it can only be updated by the application
# It can be mapped to transmit PDOs for sending data to the bus
[[objects]]
index = 0x2000
parameter_name = "Raw Analog Input"
object_type = "array"
data_type = "uint16"
access_type = "ro"
array_size = 4
default_value = [0, 0, 0, 0]
pdo_mapping = "tpdo"
# An object for storing the scaled analog values
[[objects]]
index = 0x2001
parameter_name = "Scaled Analog Input"
object_type = "array"
data_type = "uint16"
access_type = "ro"
array_size = 4
default_value = [0, 0, 0, 0]
pdo_mapping = "tpdo"
# A configuration object for controlling the frequency of analog readings
[[objects]]
index = 0x2100
parameter_name = "Analog Read Period (ms)"
object_type = "var"
data_type = "uint32"
access_type = "rw"
default_value = 10
# A configuration object which can be used to adjust the linear offset and scale transform used to
# calculate the value in 0x2001
[[objects]]
index = 0x2101
parameter_name = "Analog Scale Config"
object_type = "record"
[[objects.subs]]
sub_index = 1
parameter_name = "Scale Numerator"
data_type = "uint16"
access_type = "rw"
default_value = 1
[[objects.subs]]
sub_index = 2
parameter_name = "Scale Denominator"
data_type = "uint16"
access_type = "rw"
default_value = 1
[[objects.subs]]
sub_index = 3
parameter_name = "Offset"
data_type = "uint16"
access_type = "rw"
default_value = 0
zencan-build contains functions for generating the object dictionary code, and can be used as a
build dependency.
cargo add --build zencan-build
zencan-node is compiled in as a normal dependency.
cargo add zencan-node
build.rsIn your build.rs script, add code like this:
fn main() {
if let Err(e) = zencan_build::build_node_from_device_config("ZENCAN_CONFIG", "zencan_config.toml") {
eprintln!("Failed to parse zencan_config.toml: {}", e.to_string());
std::process::exit(-1);
}
}
When including the code, it is included using the name specified in build -- ZENCAN_CONFIG in this
case. This allows creating multiple object dictionaries in a single applicaation.
Typically, an application would add a snippet like this into main.rs:
mod zencan {
zencan_node::include_modules!(ZENCAN_CONFIG);
}
Then, you instantiate a Node object, wire up CAN message rx and tx, wire up any required Node callbacks, and then build your application logic. Store your application state in "objects" (defined in the device config) and it will be accessible over the CAN bus via the SDO server. Objects can also be mapped to PDOs, so that they can sent and received efficiently either periodically, in response to SYNC messages on the bus, or whenever data is changed.
For full details, see the docs or the examples.
You can also run a node on linux, with socketcan. This can be useful for testing with a virtual can adapter. See this example for a full implementation.