{"id":22038320,"url":"https://github.com/opencyphal/libcanard","last_synced_at":"2026-01-12T15:27:48.180Z","repository":{"id":36051488,"uuid":"40349382","full_name":"OpenCyphal/libcanard","owner":"OpenCyphal","description":"A compact implementation of the Cyphal/CAN protocol in C for high-integrity real-time embedded systems","archived":false,"fork":false,"pushed_at":"2024-09-24T13:46:19.000Z","size":1263,"stargazers_count":331,"open_issues_count":8,"forks_count":192,"subscribers_count":48,"default_branch":"master","last_synced_at":"2024-10-29T18:47:18.791Z","etag":null,"topics":["aerospace","c","c11","can","can-bus","can-fd","canbus","communication-protocol","cyphal","embedded","embedded-systems","misra","opencyphal","protocol","robotics","socketcan","space","uav","uavcan","vehicle-bus"],"latest_commit_sha":null,"homepage":"http://opencyphal.org","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/OpenCyphal.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"open_collective":"opencyphal","github":"OpenCyphal"}},"created_at":"2015-08-07T08:16:09.000Z","updated_at":"2024-10-17T10:04:14.000Z","dependencies_parsed_at":"2023-12-28T06:03:34.764Z","dependency_job_id":"170f46dc-3c11-43e4-8e85-d986c0b90606","html_url":"https://github.com/OpenCyphal/libcanard","commit_stats":{"total_commits":543,"total_committers":35,"mean_commits":"15.514285714285714","dds":0.430939226519337,"last_synced_commit":"03fc5fecc2c81bd6bfc6e474af8059fca2db4647"},"previous_names":["uavcan/libcanard"],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenCyphal%2Flibcanard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenCyphal%2Flibcanard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenCyphal%2Flibcanard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenCyphal%2Flibcanard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OpenCyphal","download_url":"https://codeload.github.com/OpenCyphal/libcanard/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247694875,"owners_count":20980733,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aerospace","c","c11","can","can-bus","can-fd","canbus","communication-protocol","cyphal","embedded","embedded-systems","misra","opencyphal","protocol","robotics","socketcan","space","uav","uavcan","vehicle-bus"],"created_at":"2024-11-30T11:05:30.031Z","updated_at":"2026-01-12T15:27:48.174Z","avatar_url":"https://github.com/OpenCyphal.png","language":"C++","funding_links":["https://opencollective.com/opencyphal","https://github.com/sponsors/OpenCyphal"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# Cyphal/CAN transport in C\n\n[![Main Workflow](https://github.com/OpenCyphal/libcanard/actions/workflows/main.yml/badge.svg)](https://github.com/OpenCyphal/libcanard/actions/workflows/main.yml)\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=libcanard\u0026metric=alert_status)](https://sonarcloud.io/dashboard?id=libcanard)\n[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=libcanard\u0026metric=reliability_rating)](https://sonarcloud.io/dashboard?id=libcanard)\n[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=libcanard\u0026metric=coverage)](https://sonarcloud.io/dashboard?id=libcanard)\n[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=libcanard\u0026metric=ncloc)](https://sonarcloud.io/dashboard?id=libcanard)\n[![Forum](https://img.shields.io/discourse/users.svg?server=https%3A%2F%2Fforum.opencyphal.org\u0026color=1700b3)](https://forum.opencyphal.org)\n\n\u003c/div\u003e\n\n-----\n\nLibcanard is a robust implementation of the Cyphal/CAN transport layer in C for high-integrity real-time embedded systems.\n\n[Cyphal](https://opencyphal.org) is an open lightweight data bus standard designed for reliable intravehicular\ncommunication in aerospace and robotic applications via CAN bus, Ethernet, and other robust transports.\n\n**Read the docs in [`libcanard/canard.h`](/libcanard/canard.h).**\n\nFind examples, starters, tutorials on the\n[Cyphal forum](https://forum.opencyphal.org/t/libcanard-examples-starters-tutorials/935).\n\nIf you want to contribute, please read [`CONTRIBUTING.md`](/CONTRIBUTING.md).\n\n## Features\n\n- Full test coverage and extensive static analysis.\n- Compliance with automatically enforceable MISRA C rules (reach out to \u003chttps://forum.opencyphal.org\u003e for details).\n- Detailed time complexity and memory requirement models for the benefit of real-time high-integrity applications.\n- Purely reactive API without the need for background servicing.\n- Support for the Classic CAN and CAN FD.\n- Support for redundant network interfaces.\n- Compatibility with 8/16/32/64-bit platforms.\n- Compatibility with extremely resource-constrained baremetal environments starting from 32K ROM and 32K RAM.\n- Implemented in ≈1000 SLoC.\n\n## Platforms\n\nThe library is designed to be usable out of the box with any conventional 8/16/32/64-bit platform,\nincluding deeply embedded baremetal platforms, as long as there is a standard-compliant compiler available.\nThe platform-specific media IO layer (driver) is supposed to be provided by the application:\n\n    +---------------------------------+\n    |           Application           |\n    +-------+-----------------+-------+\n            |                 |\n    +-------+-------+ +-------+-------+\n    |   Libcanard   | |   CAN driver  |\n    +---------------+ +-------+-------+\n                              |\n                      +-------+-------+\n                      | CAN controller|\n                      +---------------+\n\nThe OpenCyphal Development Team maintains a collection of various platform-specific components in a separate repository\nat \u003chttps://github.com/OpenCyphal/platform_specific_components\u003e.\nUsers are encouraged to search through that repository for drivers, examples, and other pieces that may be\nreused in the target application to speed up the design of the media IO layer (driver) for the application.\n\n## Example\n\nThe example augments the documentation but does not replace it.\n\nThe library requires a constant-complexity deterministic dynamic memory allocator.\nWe could use the standard C heap, but most implementations are not constant-complexity,\nso let's suppose that we're using [O1Heap](https://github.com/pavel-kirienko/o1heap) instead.\nWe are going to need basic wrappers:\n\n```c\nstatic void* memAllocate(void* const user_reference, const size_t size)\n{\n    (void) user_reference;\n    return o1heapAllocate(my_allocator, size);\n}\n\nstatic void memFree(void* const user_reference, const size_t size, void* const pointer)\n{\n    (void) user_reference;\n    (void) size;\n    o1heapFree(my_allocator, pointer);\n}\n```\n\nInit a library instance:\n\n```c\nconst struct CanardMemoryResource memory = {NULL, memFree, memAllocate};\nstruct CanardInstance canard = canardInit(memory);\ncanard.node_id = 42;                        // Defaults to anonymous; can be set up later at any point.\n```\n\nIn order to be able to send transfers over the network, we will need one transmission queue per redundant CAN interface:\n\n```c\nconst struct CanardMemoryResource tx_memory = {NULL, memFree, memAllocate};\nstruct CanardTxQueue queue = canardTxInit(\n    100,                // Limit the size of the queue at 100 frames.\n    CANARD_MTU_CAN_FD,  // Set MTU = 64 bytes. There is also CANARD_MTU_CAN_CLASSIC.\n    tx_memory);\n```\n\nPublish a message (message serialization not shown):\n\n```c\nstatic uint8_t my_message_transfer_id;  // Must be static or heap-allocated to retain state between calls.\nconst struct CanardTransferMetadata transfer_metadata = {\n    .priority       = CanardPriorityNominal,\n    .transfer_kind  = CanardTransferKindMessage,\n    .port_id        = 1234,                       // This is the subject-ID.\n    .remote_node_id = CANARD_NODE_ID_UNSET,       // Messages cannot be unicast, so use UNSET.\n    .transfer_id    = my_message_transfer_id,\n};\n++my_message_transfer_id;  // The transfer-ID shall be incremented after every transmission on this subject.\nint32_t result = canardTxPush(\u0026queue,               // Call this once per redundant CAN interface (queue).\n                              \u0026canard,\n                              tx_deadline_usec,     // Zero if transmission deadline is not limited.\n                              \u0026transfer_metadata,\n                              47,                   // Size of the message payload (see Nunavut transpiler).\n                              \"\\x2D\\x00\" \"Sancho, it strikes me thou art in great fear.\",\n                              NULL);\nif (result \u003c 0)\n{\n    // An error has occurred: either an argument is invalid, the TX queue is full, or we've run out of memory.\n    // It is possible to statically prove that an out-of-memory will never occur for a given application if the\n    // heap is sized correctly; for background, refer to the Robson's Proof and the documentation for O1Heap.\n}\n```\n\nUse [Nunavut](https://github.com/OpenCyphal/nunavut) to automatically generate\n(de)serialization code from DSDL definitions.\n\nThe CAN frames generated from the message transfer are now stored in the `queue`.\nWe need to pick them out one by one and have them transmitted.\nNormally, the following fragment should be invoked periodically to unload the CAN frames from the\nprioritized transmission queue (or several, if redundant network interfaces are used) into the CAN driver:\n\n```c\nfor (struct CanardTxQueueItem* ti = NULL; (ti = canardTxPeek(\u0026queue)) != NULL;)  // Peek at the top of the queue.\n{\n    if ((0U == ti-\u003etx_deadline_usec) || (ti-\u003etx_deadline_usec \u003e getCurrentMicroseconds()))  // Check the deadline.\n    {\n        if (!pleaseTransmit(ti))               // Send the frame over this redundant CAN iface.\n        {\n            break;                             // If the driver is busy, break and retry later.\n        }\n    }\n    // After the frame is transmitted or if it has timed out while waiting, pop it from the queue and deallocate:\n    canardTxFree(\u0026queue, \u0026canard, canardTxPop(\u0026queue, ti));\n}\n```\n\n💡 New in v4.0: optionally, you can now use `canardTxPoll()` that automates the above for you.\n\nTransfer reception is done by feeding frames into the transfer reassembly state machine\nfrom any of the redundant interfaces.\nBut first, we need to subscribe:\n\n```c\nstruct CanardRxSubscription heartbeat_subscription;\n(void) canardRxSubscribe(\u0026canard,   // Subscribe to messages uavcan.node.Heartbeat.\n                         CanardTransferKindMessage,\n                         7509,      // The fixed Subject-ID of the Heartbeat message type (see DSDL definition).\n                         16,        // The extent (the maximum possible payload size) provided by Nunavut.\n                         CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,\n                         \u0026heartbeat_subscription);\n\nstruct CanardRxSubscription my_service_subscription;\n(void) canardRxSubscribe(\u0026canard,   // Subscribe to an arbitrary service response.\n                         CanardTransferKindResponse,  // Specify that we want service responses, not requests.\n                         123,       // The Service-ID whose responses we will receive.\n                         1024,      // The extent (see above).\n                         CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,\n                         \u0026my_service_subscription);\n```\n\nThe \"extent\" refers to the minimum amount of memory required to hold any serialized representation of any compatible\nversion of the data type; or, in other words, it is the maximum possible size of received objects.\nThis parameter is determined by the data type author at the data type definition time.\nIt is typically larger than the maximum object size in order to allow the data type author to introduce more\nfields in the future versions of the type;\nfor example, `MyMessage.1.0` may have the maximum size of 100 bytes and the extent 200 bytes;\na revised version `MyMessage.1.1` may have the maximum size anywhere between 0 and 200 bytes.\nExtent values are provided per data type by DSDL transcompilers such as Nunavut.\n\nIn Libcanard we use the term \"subscription\" not only for subjects (messages), but also for RPC services.\n\nWe can subscribe and unsubscribe at runtime as many times as we want.\nNormally, however, an embedded application would subscribe once and roll with it.\nOkay, this is how we receive transfers:\n\n```c\nstruct CanardRxTransfer transfer;\nconst int8_t result = canardRxAccept(\u0026canard,\n                                     rx_timestamp_usec,          // When the frame was received, in microseconds.\n                                     \u0026received_frame,            // The CAN frame received from the bus.\n                                     redundant_interface_index,  // If the transport is not redundant, use 0.\n                                     \u0026transfer,\n                                     NULL);\nif (result \u003c 0)\n{\n    // An error has occurred: either an argument is invalid or we've ran out of memory.\n    // It is possible to statically prove that an out-of-memory will never occur for a given application if\n    // the heap is sized correctly; for background, refer to the Robson's Proof and the documentation for O1Heap.\n    // Reception of an invalid frame is NOT an error.\n}\nelse if (result == 1)\n{\n    processReceivedTransfer(redundant_interface_index, \u0026transfer);  // A transfer has been received, process it.\n    canard.memory_free(\u0026canard, transfer.payload);                  // Deallocate the dynamic memory afterwards.\n}\nelse\n{\n    // Nothing to do.\n    // The received frame is either invalid or it's a non-last frame of a multi-frame transfer.\n    // Reception of an invalid frame is NOT reported as an error because it is not an error.\n}\n```\n\nA simple API for generating CAN hardware acceptance filter configurations is also provided.\nAcceptance filters are generated in an extended 29-bit ID + mask scheme and can be used to minimize\nthe number of irrelevant transfers processed in software.\n\n```c\n// Generate an acceptance filter to receive only uavcan.node.Heartbeat.1.0 messages (fixed port-ID 7509):\nstruct CanardFilter heartbeat_config = canardMakeFilterForSubject(7509);\n// And to receive only uavcan.register.Access.1.0 service transfers (fixed port-ID 384):\nstruct CanardFilter register_access_config = canardMakeFilterForService(384, ins.node_id);\n\n// You can also combine the two filter configurations into one (may also accept irrelevant messages).\n// This allows consolidating a large set of configurations to fit the number of hardware filters.\n// For more information on the optimal subset of configurations to consolidate to minimize wasted CPU,\n// see the Cyphal specification.\nstruct CanardFilter combined_config =\n        canardConsolidateFilters(\u0026heartbeat_config, \u0026register_access_config);\nconfigureHardwareFilters(combined_config.extended_can_id, combined_config.extended_mask);\n```\n\nFull API specification is available in the documentation.\nIf you find the examples to be unclear or incorrect, please, open a ticket.\n\n## Revisions\n\n### v4.0\n\nUpdating from Libcanard v3 to v4 involves several changes in memory management and TX frame expiration.\nPlease follow the [MIGRATION_v3.x_to_v4.0](MIGRATION_v3.x_to_v4.0.md) guide and carefully update your code.\n\n### v3.2\n\n- Added new `canardRxGetSubscription`.\n\n### v3.1\n\n- Remove the Dockerfile; use [toolshed](https://github.com/OpenCyphal/docker_toolchains/pkgs/container/toolshed)\n  instead if necessary.\n- Simplify the transfer reassembly state machine to address [#212](https://github.com/OpenCyphal/libcanard/issues/212).\n  See also \u003chttps://forum.opencyphal.org/t/amendment-to-the-transfer-reception-state-machine-implementations/1870\u003e.\n\n#### v3.1.1\n\n- Refactor the transfer reassembly state machine to enhance its maintainability and robustness.\n\n#### v3.1.2\n\n- Allow redefinition of CANARD_ASSERT via the config header;\n  see [#219](https://github.com/OpenCyphal/libcanard/pull/219).\n\n### v3.0\n\n- Update branding as [UAVCAN v1 is renamed to Cyphal](https://forum.opencyphal.org/t/uavcan-v1-is-now-cyphal/1622).\n- Improve MISRA compliance by removing use of flex array: ([#192](https://github.com/OpenCyphal/libcanard/pull/192)).\n- Fix dependency issues in docker toolchain.\n\n  There are no API changes in this release aside from the rebranding/renaming:\n  `CANARD_UAVCAN_SPECIFICATION_VERSION_MAJOR` -\u003e `CANARD_CYPHAL_SPECIFICATION_VERSION_MAJOR`\n  `CANARD_UAVCAN_SPECIFICATION_VERSION_MINOR` -\u003e `CANARD_CYPHAL_SPECIFICATION_VERSION_MINOR`\n\n#### v3.0.1\n\n- Remove UB as described in [203](https://github.com/OpenCyphal/libcanard/issues/203).\n\n#### v3.0.2\n\n- Robustify the multi-frame transfer reassembler state machine\n  ([#189](https://github.com/OpenCyphal/libcanard/issues/189)).\n- Eliminate the risk of a header file name collision by renaming the vendored Cavl header to `_canard_cavl.h`\n  ([#196](https://github.com/OpenCyphal/libcanard/issues/196)).\n\n### v2.0\n\n- Dedicated transmission queues per redundant CAN interface with depth limits.\n  The application is now expected to instantiate `CanardTxQueue` (or several in case of redundant transport) manually.\n\n- Replace O(n) linked lists with fast O(log n) AVL trees\n  ([Cavl](https://github.com/pavel-kirienko/cavl) library is distributed with libcanard).\n  Traversing the list of RX subscriptions now requires recursive traversal of the tree.\n\n- Manual DSDL serialization helpers removed; use [Nunavut](https://github.com/OpenCyphal/nunavut) instead.\n\n- Replace bitwise CRC computation with much faster static table by default\n  ([#185](https://github.com/OpenCyphal/libcanard/issues/185)).\n  This can be disabled by setting `CANARD_CRC_TABLE=0`, which is expected to save ca. 500 bytes of ROM.\n\n- Fixed issues with const-correctness in the API ([#175](https://github.com/OpenCyphal/libcanard/issues/175)).\n\n- `canardRxAccept2()` renamed to `canardRxAccept()`.\n\n- Support build configuration headers via `CANARD_CONFIG_HEADER`.\n\n- Add API for generating CAN hardware acceptance filter configurations\n  ([#169](https://github.com/OpenCyphal/libcanard/issues/169)).\n\n### v1.1\n\n- Add new API function `canardRxAccept2()`, deprecate `canardRxAccept()`.\n- Provide user references in `CanardRxSubscription`.\n- Promote certain internal fields to the public API to allow introspection.\n\n### v1.0\n\nThe initial release.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopencyphal%2Flibcanard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopencyphal%2Flibcanard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopencyphal%2Flibcanard/lists"}