zencan-node
Crate for implementing a zencan device. Usually used in an embedded no_std context, but also can
be used elsewhere.
Usage
Create a device config
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.
Sample device config
= "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.
[]
= 0xCAFE
= 32
= 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.
[[]]
= 4
= 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
[[]]
= 0x2000
= "Raw Analog Input"
= "array"
= "uint16"
= "ro"
= 4
= [0, 0, 0, 0]
= "tpdo"
# An object for storing the scaled analog values
[[]]
= 0x2001
= "Scaled Analog Input"
= "array"
= "uint16"
= "ro"
= 4
= [0, 0, 0, 0]
= "tpdo"
# A configuration object for controlling the frequency of analog readings
[[]]
= 0x2100
= "Analog Read Period (ms)"
= "var"
= "uint32"
= "rw"
= 10
# A configuration object which can be used to adjust the linear offset and scale transform used to
# calculate the value in 0x2001
[[]]
= 0x2101
= "Analog Scale Config"
= "record"
[[]]
= 1
= "Scale Numerator"
= "uint16"
= "rw"
= 1
[[]]
= 2
= "Scale Denominator"
= "uint16"
= "rw"
= 1
[[]]
= 3
= "Offset"
= "uint16"
= "rw"
= 0
Add zencan-build and zencan-node as dependencies
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 the generated code in build.rs
In your build.rs script, add code like this:
Include the generated code in your application
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:
Instantiate a node
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.
Socketcan Example
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.