Cyphal Library¶
The Cyphal library provides two main classes to support different levels of Cyphal protocol functionality:
CyphalBase Class¶
The CyphalBase class offers essential Cyphal features with a minimal footprint, suitable for use cases where
full protocol support is not required (for example, enabling basic Cyphal communication on a secondary interface without CAN FD support).
Key features:
- CAN Mode: Normal (Classic CAN)
- Core Attributes: Node ID, operating mode, health state
- Core Members:
- libcanard instance (protocol core)
- Presentation layer
- Transport layer
- RX and TX threads for message handling
- Provided Components:
- Heartbeat (periodic node status publication)
- GetInfo (node information service)
Cyphal Class¶
The Cyphal class inherits from CyphalBase and extends it with a richer set of features, configurable via Kconfig options.
This class is intended for nodes requiring advanced Cyphal capabilities and CAN FD support.
Key features:
- CAN Mode: FD (CAN Flexible Data-rate)
- Extended Components:
- Register access and management
- ExecuteCommand service
- Device Firmware Update (DFU) with verification
- [Optional, Kconfig-controlled] LED command handling, Heartbeat monitoring, Diagnostic Record publication, Plug-and-Play (PnP) Node ID allocation, File services, Time synchronization
This class structure allows flexible deployment of Cyphal functionality, from lightweight nodes to fully featured devices, by selecting the appropriate class and enabling features as needed.
Cyphal Library Implementation¶
The underlying library for Cyphal consists of submodules, each implementing a part of the protocol:
application: contains the top-level API for the application. It defines some generic high-level functions and concepts that are expected to be useful in different application domains, like diagnostics.presentation: responsible for serializing and deserializing DSDL objects and for providing a higher-level api's as connectors for the application and transport layers.transport: determines how to transfer serialized data structures over the network.dsdl_gen: contains the transcompiled C header files from the dsdl data types such aspublic_regulated_data_types. Refer to the section Transcompile DSDL Objects below.libcanard: compact implementation of the Cyphal protocol for high-integrity real-time embedded systems. Libcanard implements the presentation and transport related functions to serialize and deserialize the dsdl structs to CAN, and registers handlers for callback of specific services, etc.
The structure of library and its mapping on to the Cyphal protocol is shown below:
The dependency relationship of the submodules are as follows:
Cyphal Message Structure: Transcompile DSDL Objects¶
The Cyphal library has a dependency on the nunavut generated DSDL definitions. Follow the following
steps to transcompile standard data types, i.e.,
public_regulated_data_types into
cyphal/dsdl_gen folder:
- Run
west update. This clones the repo public_regulated_data_types inside themodules/dsdldirectory. - Run
make pre-buildfrom the terminal to invoke nunavut to generate C headers.
Application Layer¶
The application layer provides the top-level API for the application and implements certain standard application-layer functions defined by the Cyphal Specification (chapter 5 Application layer).
Cyphal was known as UAVCAN before 2022.
The implementation offers an abstract class CyphalBaseApp to implement the handler of received dsdl structs.
Its handler (presentation_receive(...)) is called from the underlying libcanard instance during the processing of received CAN messages.
For outgoing messages, the publish function is used (presentation_publish(...)).
Services¶
The Cyphal class (cyphal/Cyphal.cpp and cyphal/CyphalBase.cpp) registers the following services:
| service | cyphal/application/ | Type | Description |
|---|---|---|---|
uavcan.node.GetInfo |
GetInfo | Handle | Handles UAVCAN GetInfo requests |
uavcan.node.ExecuteCommand |
ExecuteCommand | Handle | Handles UAVCAN ExecuteCommand requests |
-> Device_Firmware_Update |
DeviceFirmwareUpgrade | Thread + Handle | Responds, then starts a UAVCAN File Read of the new firmware image |
uavcan.file.GetInfo |
FileGetInfo | Handle | Handles requests for file information |
uavcan.file.List |
FileList | Handle | Handles directory listing requests |
uavcan.file.Read |
FileRead | Handle | Handles file read requests |
uavcan.file.Write |
FileWrite | Handle | Handles file write requests |
uavcan._register.Access |
RegisterAccessService | Handle | Handles register access requests |
uavcan._register.List |
RegisterList | Handle | Handles register list requests |
Publisher¶
The Cyphal class (cyphal/Cyphal.cpp and cyphal/CyphalBase.cpp) registers the following publishing features:
| service | cyphal/application/ | Type | Description |
|---|---|---|---|
uavcan.pnp.NodeIDAllocation |
NodeIDAllocation | Thread | Begins a PnP NodeID Client to request a Node ID |
uavcan.node.Heartbeat |
Heartbeat | Thread | Publishes a UAVCAN heartbeat message periodically |
uavcan.diagnostic.Record |
DiagnosticRecord | Backend for Log | Publishes diagnostic messages |
uavcan.time.Synchronization |
TimeSynchronization | Thread + Handle | Handles time synchronization messages |
Presentation Layer¶
The presentation layer acts as an intermediary between the application layer and the transport layer in the Cyphal library. It includes the Nunavut-generated support functions for serializing and deserializing data transfers.
Transport Layer¶
This layer is responsible for:
- configures the underlying transport medium, e.g. CAN FD or CAN NORMAL
- configures the hardware receive filters to only consume requests and messages relevant for the node
- send cyphal frames on the CAN bus
- receive incoming requests/messages, processes them, and forwards them to the presentation layer for further deserialization
It consists of two threads; one for receive and one for publish.
Diagrams¶
The following sequence diagrams illustrates process of frame reception and frame transmission.
Frame Reception¶
sequenceDiagram
participant bus as CAN bus
participant phy as Peripheral<br>and ISR
participant queue as RX MessageQueue
participant thread as RX Thread
participant canard as Canard Instance
thread ->> queue: k_msgq_get()
bus -x phy: any frame
Note over phy: frame is dropped:<br>no matching<br>acceptance filter
bus ->>+ phy: matching frame
phy ->>- queue: k_msgq_put()
thread ->>+ queue: k_msgq_get()
queue ->>- thread: msg received
activate thread
thread ->>+ canard: canardRxAccept()
canard ->> canard: transfer reassembly
canard -->>- thread: done
opt complete?
thread ->> thread: call handler for<br>completed transfer<br>on thread stack
thread ->> thread: free transfer payload
end
Note over thread: thread goes to sleep<br>until next frame arrives
deactivate thread
Frame Transmission¶
Successful Frame Transmission¶
sequenceDiagram
participant canard as Canard Instance
participant thread as TX thread
participant phy as Peripheral<br/> and ISR
participant bus as CAN bus
thread -->>+ thread: k_sem_take()
loop
thread ->>+ canard: canardTxPeek()
canard -->>- thread: new frame available
thread ->>+ phy: can_send() & send_callback register
phy ->> phy: frame queued
phy -->>- thread: done
thread ->>+ canard: canardTxFree()
canard -->>- thread: done
end
note over thread: thread goes to sleep<br>until next notification arrives
deactivate thread
par
phy ->> bus: frame sent
phy ->> phy: tx_callback
phy ->> thread: send_callback()
end
Failed Frame Transmission¶
Case 1: Frame Deadline Expired¶
sequenceDiagram
participant canard as Canard Instance
participant thread as TX thread
participant phy as Peripheral<br/> and ISR
participant bus as CAN bus
thread -->>+ thread: k_sem_take()
loop
thread ->>+ canard: canardTxPeek()
canard -->>- thread: new frame available
opt frame deadline expired
thread ->>+ canard: canardTxFree()
canard -->>- thread: done
end
end
note over thread: thread goes to sleep<br>until next notification arrives
deactivate thread
Case 2: CAN Tx FIFO not empty¶
sequenceDiagram
participant canard as Canard Instance
participant thread as TX thread
participant phy as Peripheral<br/> and ISR
participant bus as CAN bus
thread -->>+ thread: k_sem_take()
loop
thread ->>+ canard: canardTxPeek()
canard -->>- thread: new frame available
thread ->>+ phy: can_send()
phy -x phy: frame not queued<br/> Tx FIFO not empty
phy -->>- thread: error
note over thread: exit loop
end
note over thread: thread goes to sleep<br>until next notification arrives
deactivate thread
Example: GetInfo Request¶
sequenceDiagram
participant rx as RX Thread
participant tx as TX Thread
participant canard as Canard Instance
participant pl as Presentation<br>Layer
participant al as Application<br>Layer
rx -->>+ rx: k_msgq_get()
rx ->>+ canard: canardRxAccept()
canard ->> canard: transfer reassembly
canard -->>- rx: transfer complete
rx ->>+ pl: handle_GetInfo_Request()
pl ->> pl: deserialize request
pl ->>+ al: getinfo_request_handler()
al ->> al: gather information
al ->> al: fill response struct
al ->>+ pl: publish_GetInfo_Response()
pl ->> pl: serialize response
pl ->>+ canard: canardTxPush()
canard ->> canard: split transfer into frames
canard -->>- pl: successfully queued
pl ->> tx: notify
pl -->>- al: response published
al -->>- pl: request handled
pl -->>- rx: request handled
rx ->> rx: free request transfer payload
deactivate rx
tx -->>+ tx: k_sem_take()
loop
tx ->>+ canard: canardTxPeek()
canard -->>- tx: new frame available
tx ->> tx: can_send(), may block
tx ->>+ canard: canardTxFree()
canard -->>- tx: done
end
Note over tx: thread goes to sleep<br>until next notification arrives
deactivate tx