A lightweight, resource-efficient CAN bus communication protocol and code generation tool designed specifically for embedded systems and microcontrollers for the Monash Nova Rover team. The specification can be found
The core specification is defined in LaTeX and compiled to PDF.
📄 View the PDF
Nova-CAN is inspired by the OpenCyphal protocol but simplified for resource-constrained microcontrollers and the need of the team.
- Device-First Design: Define device interfaces in YAML, generate code automatically
- Code Generation Tool (NCC): Transforms device configurations into ready-to-use C functions
- Message and Service Support: One-way messages and two-way request/response services
- Hardware-Level CAN Filtering: Reduces CPU load through intelligent frame filtering
- Multi-frame Message Support: Handles large data transfers automatically
- OpenCyphal DSDL Compatibility: Uses proven data structure definitions
-
Protocol Specification (
spec/protocol/)- Complete protocol definition in LaTeX
- CAN ID layout and message formats
- Protocol subject definitions
-
Code Generation Tool (
tooling/ncc/)- NCC (Nova CAN Compiler): Core code generation engine
- YAML Configuration: Device interface definitions
- Template System: Jinja2 templates for C code generation
- DSDL Integration: OpenCyphal Data Structure Description Language support
-
Libraries (
src/)- C Library (
src/c/): Core CAN ID manipulation functions - Python Library (
src/python/): Device interface and system definition models
- C Library (
-
Examples (
examples/)- Basic mock motor driver for Linux testing
- System configuration examples
- Build system integration
Create a YAML configuration file that defines what your device can send and receive:
# motor_driver.yaml
name: MotorDriver
version: 1.0.0
messages:
receive:
- name: Command
port_type: nova.motor_driver.msg.Command.1.0
transmit:
- name: Status
port_type: nova.motor_driver.msg.Status.1.0
services:
server:
- name: SetPIDConstant
port_type: nova.motor_driver.srv.SetPIDConstant.1.0Preferred: Use existing DSDL message types from the examples/dsdl/ directory:
# Command.1.0.dsdl
uint2 CURRENT = 0
uint2 VELOCITY = 1
uint2 POSITION = 2
uint2 mode
int16 value
@sealed
If needed: Create new message types following the OpenCyphal DSDL format.
Use the Nova CAN Compiler (NCC) to generate device-specific C headers:
# Generate device headers
python3 tooling/ncc/ncc.py -d motor_driver.yaml -o build/include
# Generate DSDL headers (if using custom types)
nnvg --target-language c --outdir build/include/dsdl_headers dsdl/nova#include "nova_can.h"
#include "motor_driver.h" // Generated headerThe generated code provides callback function signatures. Implement them to handle your device logic:
// Auto-generated callback signature
int nova_can_motor_driver_command_callback(NOVA_CAN_CANID *can_id,
nova_motor_driver_msg_Command_1_0 *data) {
// Your device-specific logic here
switch (data->mode) {
case nova_motor_driver_msg_Command_1_0_CURRENT:
motor_set_current(data->value);
break;
case nova_motor_driver_msg_Command_1_0_VELOCITY:
motor_set_velocity(data->value);
break;
}
return 0;
}// Initialize CAN hardware
uint32_t filter, mask;
nova_can_get_canid_filter(NODE_ID, &filter);
nova_can_get_canid_mask(&mask);
// Apply filter/mask to your CAN controller
// Main processing loop
while (1) {
// Read CAN frame from hardware
if (can_receive(&frame)) {
// Process with generated Nova-CAN functions
nova_can_motor_driver_rx(&frame.id, frame.data, &frame.length);
}
}Once you have individual devices working, you can compose them into complete systems using system composition files. These files define what devices are connected to the system:
# system_definition.yaml
name: Rover
can_buses:
- name: CAN1
rate: 125000
devices:
- name: FrontLeftWheel
node_id: 1
device_type: motor_driver
- name: FrontRightWheel
node_id: 2
device_type: motor_driver
- name: CAN2
rate: 125000
devices:
- name: Thermometer
node_id: 4
device_type: thermometer
System composition files serve several purposes:
- System Documentation: Provide a clear overview of all devices connected to each can bus
- Bus Configuration: Define CAN bus parameters (bit rates, etc.)
- Tool Interpretation: Enables tools such as nova-candump and nova-cansend (not yet implemented) to intepret the messages, and provides clear mappings for conversion to mqtt topics.
While device interfaces focus on individual device capabilities, system composition files focus on how devices work together as a complete system.
- Python 3.7+
- nunavut (for DSDL compilation)
- C compiler (GCC recommended)
# Clone the repository
git clone <repository-url>
cd nova-can
# Install Python dependencies
pip install -r tooling/requirements.txt
# Install nunavut (for DSDL compilation)
pip install nunavut- Protocol Specification: View PDF - Complete protocol details
- Examples: See the
examples/directory for working implementations - Schema Definitions: YAML schemas in
spec/schema/
- Follow the existing code style and structure
- Add tests for new features
- Update documentation for API changes
- Ensure all examples build and run correctly
[Add your license information here]
- Inspired by the OpenCyphal protocol
- Uses nunavut for DSDL compilation
- Developed for the Monash Nova Rover team