libcanard Documentation

repository·master·Indexed 19 days ago

https://github.com/opencyphal/libcanard

A high-integrity C implementation of the Cyphal/CAN transport layer for real-time embedded systems. It supports Cyphal v1.1, v1.0, and legacy UAVCAN v0 (DroneCAN), with compatibility for Classic CAN and CAN FD across 8/16/32/64-bit platforms. The library provides a minimal interface for adaptation to various CAN-enabled MCUs, requiring minimal resources (32K ROM and 32K RAM).

Tokens
1.8K
Snippets
1
Records
4
Agent score
16%

What's inside libcanard

  1. Overview of Libcanard

    master

    Libcanard is a robust C implementation of the Cyphal/CAN transport layer designed for high-integrity real-time embedded systems. It supports Cyphal v1.1, v1.0, and legacy UAVCAN v0 (DroneCAN).

    Key features include:

    • Support for both Classic CAN and CAN FD.
    • Support for redundant and non-redundant configurations.
    • Compatibility with 8/16/32/64-bit platforms, including resource-constrained baremetal MCUs (minimum 32K ROM and 32K RAM).
    • A minimal interface with the underlying platform for easy adaptation to various CAN-enabled MCUs.

    For platform-specific drivers and examples, refer to the OpenCyphal platform_specific_components repository.

  2. Key changes in Libcanard v5.0.alpha

    master

    Version 5 is a major rework optimized for use with the Cy framework. Significant improvements include:

    • Protocol Support: Adds support for Cyphal/CAN v1.1 (16-bit subject-IDs) and legacy UAVCAN v0 (DroneCAN).
    • Node ID Autoconfiguration: Introduces a new passive, decentralized node-ID autoconfiguration based on an occupancy observer.
    • Automatic Filter Configuration: Automatically configures CAN acceptance filters based on the current subscription set.
    • Improved TX Pipeline: Uses per-transfer queue granularity and efficient CAN frame deduplication across redundant interfaces, significantly reducing heap memory footprint (often by 2x or more).
    • Improved RX Pipeline: Supports priority level preemption without transfer loss by maintaining concurrent reassemblers per priority level.
  3. Quick start with Libcanard

    master

    To use Libcanard, you must implement several platform-specific callback functions to bridge the library with your hardware:

    1. Timekeeping: Implement app_now to return the current monotonic time in microseconds.
    2. Memory Management: Provide app_alloc and app_free via a canard_mem_vtable_t to manage heap or static memory.
    3. Transmission: Implement app_tx to non-blockingly submit CAN frames to your hardware.
    4. Message Handling: Implement a callback (e.g., app_on_message) to process received payloads.

    Basic workflow:

    • Initialize the node using canard_new.
    • Set the node ID using canard_set_node_id.
    • Subscribe to subjects using canard_subscribe_* functions.
    • Publish messages using canard_publish_* functions.
    • Periodically call canard_poll to process the transmission queues.
    • Ingest received hardware frames using canard_ingest_frame.
    #include <assert.h>
    #include <stdlib.h>
    #include "canard.h"
    
    // 1. Implement platform callbacks
    static canard_us_t app_now(const canard_t* const self); 
    static void* app_alloc(const canard_mem_t memory, const size_t size) { return malloc(size); }
    static void app_free(const canard_mem_t memory, const size_t size, void* const pointer) { free(pointer); }
    static bool app_tx(canard_t* const      self, void* const user_context, const canard_us_t deadline, const uint_least8_t iface_index, const bool fd, const uint32_t extended_can_id, const canard_bytes_t can_data);
    
    static void app_on_message(canard_subscription_t* const self, const canard_us_t timestamp, const canard_prio_t priority, const uint_least8_t source_node_id, const uint_least8_t transfer_id, const canard_payload_t payload)
    {
        if (payload.origin.data != NULL) { free(payload.origin.data); }
    }
    
    int main(void)
    {
        // 2. Setup memory and vtables
        static const canard_mem_vtable_t mem_vtable = { .free  = app_free, .alloc = app_alloc };
        const canard_mem_t mem = { .vtable  = &mem_vtable, .context = NULL };
        const canard_mem_set_t mem_set = { .tx_transfer = mem, .tx_frame = mem, .rx_session = mem, .rx_payload = mem };
        const canard_vtable_t vtable = { .now = app_now, .tx = app_tx };
        static const canard_subscription_vtable_t sub_vtable = { .on_message = app_on_message };
        
        // 3. Initialize node
        canard_t node;
        if (!canard_new(&node, &vtable, mem_set, 1, 100, UID_OR_TRUE_RANDOM_NUMBER, 0U)) return -1;
        if (!canard_set_node_id(&node, 42U)) { canard_destroy(&node); return -1; }
        
        // 4. Subscribe
        canard_subscription_t sub;
        const canard_subscription_t* const installed = canard_subscribe_13b(&node, &sub, 7509U, 63U, CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_us, &sub_vtable);
        if (installed != &sub) { canard_destroy(&node); return -1; }
    
        // 5. Publish
        const canard_bytes_chain_t payload = { .bytes = {.size = 12, .data = "Hello world!"} };
        canard_publish_13b(&node, app_now(&node) + 1000000, CANARD_IFACE_BITMAP_ALL, canard_prio_nominal, 7509U, 0U, payload, NULL);
    
        // 6. Poll
        canard_poll(&node, CANARD_IFACE_BITMAP_ALL);
    
        canard_unsubscribe(&node, &sub);
        canard_destroy(&node);
        return 0;
    }
  4. Migrating from Libcanard v3 to v4

    master
    When upgrading from Libcanard v3 to v4, you must account for changes in memory management and how TX frame expiration is handled. Detailed instructions are provided in the MIGRATION_v3.x_to_v4.0.md guide located within the v4 codebase.