Skip to content

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 as public_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:

image

The dependency relationship of the submodules are as follows:

image

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:

  1. Run west update. This clones the repo public_regulated_data_types inside the modules/dsdl directory.
  2. Run make pre-build from 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

Hold "Alt" / "Option" to enable pan & zoom
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

Hold "Alt" / "Option" to enable pan & zoom
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
Hold "Alt" / "Option" to enable pan & zoom
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
Hold "Alt" / "Option" to enable pan & zoom
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

Hold "Alt" / "Option" to enable pan & zoom
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