### Publish Large Message with Scattered Payload (13-bit Subject-ID) Source: https://context7.com/opencyphal/libcanard/llms.txt Publishes a message composed of multiple data fragments, useful for large payloads. The `canard_bytes_chain_t` structure allows chaining data buffers. This example uses a 500ms deadline and high priority. ```c // Example: Publish with scattered payload (multi-fragment) bool publish_large_message(canard_t* node, const uint8_t* header, size_t header_len, const uint8_t* body, size_t body_len) { // Chain multiple payload fragments const canard_bytes_chain_t body_fragment = { .bytes = { .size = body_len, .data = body }, .next = NULL, }; const canard_bytes_chain_t header_fragment = { .bytes = { .size = header_len, .data = header }, .next = &body_fragment, }; static uint8_t transfer_id = 0; const canard_us_t deadline = node->vtable->now(node) + 500000; // 500ms return canard_publish_13b( node, deadline, CANARD_IFACE_BITMAP_ALL, canard_prio_high, 1234U, transfer_id++, header_fragment, NULL ); } ``` -------------------------------- ### Process Incoming CAN Frames and Poll for Transmissions Source: https://context7.com/opencyphal/libcanard/llms.txt Ingests received CAN frames for protocol parsing and callback dispatch using `canard_ingest_frame()`. Periodically call `canard_poll()` to manage TX deadlines and session cleanup. This example shows integration with Linux SocketCAN. ```c #include // Main receive loop (platform-specific CAN driver integration) void process_can_traffic(canard_t* node, int can_socket) { // Read frame from CAN driver (Linux SocketCAN example) struct canfd_frame frame; ssize_t nbytes = read(can_socket, &frame, sizeof(frame)); if (nbytes >= CAN_MTU) { // Only process extended data frames (Cyphal/DroneCAN use 29-bit IDs) if ((frame.can_id & CAN_EFF_FLAG) && !(frame.can_id & (CAN_RTR_FLAG | CAN_ERR_FLAG))) { canard_bytes_t data = { .size = frame.len, .data = frame.data, }; // Ingest the frame - callbacks are invoked synchronously bool ok = canard_ingest_frame( node, node->vtable->now(node), // Timestamp 0U, // Interface index (0 for non-redundant) frame.can_id & CAN_EFF_MASK, // 29-bit extended CAN ID data ); if (!ok) { // Invalid arguments } } } // Poll to process TX deadlines and session timeouts // Call with bitmap of interfaces ready for transmission canard_poll(node, CANARD_IFACE_BITMAP_ALL); } // Check which interfaces have pending transmissions uint8_t get_pending_interfaces(canard_t* node) { return canard_pending_ifaces(node); } ``` -------------------------------- ### Initialize and use a Libcanard node Source: https://github.com/opencyphal/libcanard/blob/master/README.md Demonstrates setting up memory management, node configuration, subscription, and message publication. Requires defining callback functions for time, memory allocation, and transmission. ```c++ #include #include #include "canard.h" // Return the current monotonic time in microseconds starting from some arbitrary instant in the past (e.g., boot). static canard_us_t app_now(const canard_t* const self); // Embedded systems may prefer https://github.com/pavel-kirienko/o1heap. 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); } // Transmit a CAN frame non-blockingly; return true if submitted, false if would block (will try again later). // Frame transmission may be aborted if it doesn't hit the bus before the deadline. 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); // Process a received message. The user may attach arbitrary context to the subscription via the user context pointer. 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) { // payload.view contains the useful payload bytes. if (payload.origin.data != NULL) { // The application may keep the payload if necessary; eventually must release. free(payload.origin.data); } } int main(void) { 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 }; // Set up the local node. The node-ID will be allocated automatically. canard_t node; if (!canard_new(&node, &vtable, mem_set, 100, UID_OR_TRUE_RANDOM_NUMBER, 0U)) { return -1; } if (!canard_set_node_id(&node, 42U)) { canard_destroy(&node); return -1; } // Subscribe for messages. 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) { // NULL on invalid args, otherwise points to the incumbent subscription. canard_destroy(&node); return -1; } // Publish a message. const canard_bytes_chain_t payload = { .bytes = {.size = 12, .data = "Hello world!"} }; assert(canard_publish_13b(&node, app_now(&node) + 1000000, CANARD_IFACE_BITMAP_ALL, canard_prio_nominal, 7509U, 0U, payload, NULL)); canard_poll(&node, CANARD_IFACE_BITMAP_ALL); // Call again whenever one or more ifaces become writable. // Later, when a CAN frame is received: // static const uint8_t rx_frame_bytes[8] = { ... }; // (void)canard_ingest_frame(&node, // app_now(&node), // 0U, // rx_extended_can_id, // (canard_bytes_t){.size = sizeof(rx_frame_bytes), .data = rx_frame_bytes}); canard_unsubscribe(&node, &sub); canard_destroy(&node); return 0; } ``` -------------------------------- ### Implement Service Request and Response Handling in C Source: https://context7.com/opencyphal/libcanard/llms.txt Demonstrates sending a service request, defining a subscription callback for incoming requests, and configuring subscriptions for both service servers and clients. ```c #include // Send a GetInfo service request (service-ID 430) bool send_get_info_request(canard_t* node, uint8_t target_node_id) { // GetInfo request has empty payload const canard_bytes_chain_t payload = { .bytes = { .size = 0, .data = NULL }, .next = NULL, }; static uint8_t transfer_id = 0; const canard_us_t deadline = node->vtable->now(node) + 1000000; // 1s timeout return canard_request( node, deadline, canard_prio_nominal, 430U, // Service-ID for GetInfo target_node_id, transfer_id++, payload, NULL ); } // Subscription callback for incoming requests static void on_get_info_request(canard_subscription_t* self, canard_us_t timestamp, canard_prio_t priority, uint_least8_t source_node_id, uint_least8_t transfer_id, canard_payload_t payload) { canard_t* node = self->owner; (void)timestamp; // Build GetInfo response uint8_t response_data[64]; size_t response_len = 0; // Protocol version response_data[response_len++] = CANARD_CYPHAL_VERSION_MAJOR; response_data[response_len++] = CANARD_CYPHAL_VERSION_MINOR; // ... add more fields as needed const canard_bytes_chain_t response_payload = { .bytes = { .size = response_len, .data = response_data }, .next = NULL, }; const canard_us_t deadline = node->vtable->now(node) + 500000; canard_respond( node, deadline, priority, // Match request priority 430U, // Service-ID source_node_id, // Respond to requester transfer_id, // Match request transfer-ID response_payload, NULL ); if (payload.origin.data != NULL) { free(payload.origin.data); } } // Subscribe to service requests int setup_service_server(canard_t* node) { static canard_subscription_t sub_get_info; static const canard_subscription_vtable_t vtable = { .on_message = on_get_info_request }; canard_subscription_t* result = canard_subscribe_request( node, &sub_get_info, 430U, // Service-ID 64U, // Extent CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_us, &vtable ); return (result == &sub_get_info) ? 0 : -1; } // Subscribe to service responses (for clients) int setup_service_client(canard_t* node) { static canard_subscription_t sub_response; static const canard_subscription_vtable_t vtable = { .on_message = on_get_info_request }; // Response subscriptions have zero transfer-ID timeout canard_subscription_t* result = canard_subscribe_response( node, &sub_response, 430U, // Service-ID 64U, // Extent &vtable ); return (result == &sub_response) ? 0 : -1; } ``` -------------------------------- ### Initialize Libcanard Instance Source: https://context7.com/opencyphal/libcanard/llms.txt Configures memory vtables, platform callbacks, and initializes the node instance. Requires defining allocation and transmission callbacks before calling canard_new. ```c #include #include // Memory allocation callbacks static void* app_alloc(const canard_mem_t mem, const size_t size) { (void)mem; return malloc(size); } static void app_free(const canard_mem_t mem, const size_t size, void* ptr) { (void)mem; (void)size; free(ptr); } // Platform callbacks static canard_us_t app_now(const canard_t* self) { (void)self; struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (int64_t)ts.tv_sec * 1000000LL + ts.tv_nsec / 1000LL; } static bool app_tx(canard_t* self, void* user_context, canard_us_t deadline, uint_least8_t iface_index, bool fd, uint32_t extended_can_id, canard_bytes_t can_data) { // Submit frame to CAN driver (implementation-specific) return true; // Return true if frame accepted, false if mailbox full } int main(void) { // Set up memory resources 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, .rx_filters = mem, }; // Set up platform vtable const canard_vtable_t vtable = { .now = app_now, .tx = app_tx, .filter = NULL }; // Initialize the node (node-ID allocated automatically) canard_t node; uint64_t prng_seed = 0x1234567890ABCDEFULL; // Use hardware RNG or unique ID if (!canard_new(&node, &vtable, mem_set, /*tx_queue_capacity=*/100, prng_seed, /*filter_count=*/0)) { return -1; // Initialization failed } // Optionally set a specific node-ID (automatic allocation disabled) if (!canard_set_node_id(&node, 42U)) { canard_destroy(&node); return -1; } // Node is ready for use... // node.node_id now contains the assigned node-ID (42 or auto-allocated) canard_destroy(&node); return 0; } ``` -------------------------------- ### Run tests with coverage measurement Source: https://github.com/opencyphal/libcanard/blob/master/CLAUDE.md Commands to configure, build, and execute tests with coverage analysis enabled for the libcanard project. ```bash cmake -B build -DENABLE_COVERAGE=ON # Optionally, add -DNO_STATIC_ANALYSIS=ON cmake --build build -j$(nproc) cd build && ctest && make coverage xdg-open coverage-html/index.html ``` -------------------------------- ### Configure Heartbeat Monitor Application Build Source: https://github.com/opencyphal/libcanard/blob/master/demos/CMakeLists.txt This CMake script defines the build for the heartbeat_monitor executable, linking it with libcanard and setting strict compiler flags for C99 compliance. ```cmake cmake_minimum_required(VERSION 3.12) add_executable(heartbeat_monitor heartbeat_monitor.c ${CMAKE_SOURCE_DIR}/libcanard/canard.c) target_include_directories(heartbeat_monitor PRIVATE ${CMAKE_SOURCE_DIR}/libcanard) target_include_directories(heartbeat_monitor SYSTEM PRIVATE ${CMAKE_SOURCE_DIR}/lib/cavl2) set_target_properties( heartbeat_monitor PROPERTIES C_STANDARD 99 C_EXTENSIONS OFF COMPILE_FLAGS "-Wall -Wextra -Werror -pedantic -Wconversion -Wsign-conversion -Wno-unused-function" C_CLANG_TIDY "" C_CPPCHECK "" CXX_CLANG_TIDY "" CXX_CPPCHECK "" ) ``` -------------------------------- ### Apply Kconfig definitions to libcanard Source: https://github.com/opencyphal/libcanard/blob/master/esp_metadata/CMakeLists.txt Conditionally sets compile definitions based on project-level Kconfig settings for assertions and CRC tables. ```cmake if(NOT CONFIG_CANARD_ASSERTIONS) target_compile_definitions(${COMPONENT_LIB} PRIVATE "CANARD_ASSERT=(void)") endif() if(CONFIG_CANARD_CRC_TABLE) target_compile_definitions(${COMPONENT_LIB} PRIVATE CANARD_CRC_TABLE=1) else() target_compile_definitions(${COMPONENT_LIB} PRIVATE CANARD_CRC_TABLE=0) endif() ``` -------------------------------- ### Register libcanard as an ESP-IDF component Source: https://github.com/opencyphal/libcanard/blob/master/esp_metadata/CMakeLists.txt Registers the source files and include directories for the libcanard component. ```cmake idf_component_register(SRCS "canard.c" INCLUDE_DIRS "include") ``` -------------------------------- ### Manage Subscriptions in Libcanard Source: https://context7.com/opencyphal/libcanard/llms.txt Use these functions to locate, modify, or remove subscriptions. All subscriptions must be explicitly removed before destroying the node instance. ```c #include // Find an existing subscription canard_subscription_t* find_heartbeat_subscription(canard_t* node) { return canard_find_subscription(node, canard_kind_message_13b, 7509U); } // Dynamically modify subscription extent void update_subscription_extent(canard_t* node, uint16_t subject_id, size_t new_extent) { canard_subscription_t* sub = canard_find_subscription(node, canard_kind_message_13b, subject_id); if (sub != NULL) { // Extent can be modified at runtime; in-flight transfers retain old value sub->extent = new_extent; } } // Clean up subscriptions before destroying the node void cleanup_node(canard_t* node) { // Find and unsubscribe from all known subscriptions canard_subscription_t* sub; sub = canard_find_subscription(node, canard_kind_message_13b, 7509U); if (sub != NULL) { canard_unsubscribe(node, sub); } sub = canard_find_subscription(node, canard_kind_request, 430U); if (sub != NULL) { canard_unsubscribe(node, sub); } // All subscriptions MUST be removed before canard_destroy() canard_destroy(node); } ``` -------------------------------- ### Subscribe to DroneCAN Messages Source: https://context7.com/opencyphal/libcanard/llms.txt Sets up a subscription to DroneCAN messages, specifically the NodeStatus message. The `on_dronecan_status` callback is invoked when a message is received. Ensure `canard_v0_subscribe` is called with the correct data type ID, CRC seed, and extent. ```c #include // Subscribe to DroneCAN messages static void on_dronecan_status(canard_subscription_t* self, canard_us_t timestamp, canard_prio_t priority, uint_least8_t source_node_id, uint_least8_t transfer_id, canard_payload_t payload) { (void)self; (void)timestamp; (void)priority; (void)transfer_id; if (payload.view.size >= 7) { const uint8_t* d = (const uint8_t*)payload.view.data; uint32_t uptime_ms = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24); uint8_t health = d[4] & 0x03; uint8_t mode = (d[4] >> 2) & 0x07; printf("DroneCAN node %u: uptime=%ums\n", source_node_id, uptime_ms); } if (payload.origin.data != NULL) { free(payload.origin.data); } } int setup_dronecan_subscription(canard_t* node) { static canard_subscription_t sub; static const canard_subscription_vtable_t vtable = { .on_message = on_dronecan_status }; const uint16_t crc_seed = get_dronecan_crc_seed(DRONECAN_NODE_STATUS_SIGNATURE); canard_subscription_t* result = canard_v0_subscribe( node, &sub, DRONECAN_NODE_STATUS_DTID, crc_seed, 7U, // Extent CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_us, &vtable ); return (result == &sub) ? 0 : -1; } ``` -------------------------------- ### Subscribe to Cyphal Messages (C) Source: https://context7.com/opencyphal/libcanard/llms.txt Registers subscriptions for incoming messages using 13-bit and 16-bit subject IDs. The callback is invoked when a complete transfer is received. Ensure to free the payload origin buffer for multi-frame transfers. ```c #include #include #include // Callback for received heartbeat messages static void on_heartbeat_received(canard_subscription_t* self, canard_us_t timestamp, canard_prio_t priority, uint_least8_t source_node_id, uint_least8_t transfer_id, canard_payload_t payload) { (void)self; (void)timestamp; (void)priority; (void)transfer_id; if (payload.view.size >= 7) { const uint8_t* data = (const uint8_t*)payload.view.data; uint32_t uptime = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); uint8_t health = data[4] & 0x03; uint8_t mode = data[5] & 0x07; printf("Node %u: uptime=%us, health=%u, mode=%u\n", source_node_id, uptime, health, mode); } // IMPORTANT: Free the origin buffer for multi-frame transfers if (payload.origin.data != NULL) { free(payload.origin.data); } } static const canard_subscription_vtable_t heartbeat_vtable = { .on_message = on_heartbeat_received, }; int setup_subscriptions(canard_t* node) { // Subscribe to Cyphal v1.0 heartbeat (13-bit subject-ID 7509) static canard_subscription_t sub_heartbeat; canard_subscription_t* result = canard_subscribe_13b( node, &sub_heartbeat, 7509U, // Subject-ID 7U, // Extent (max payload size) CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_us, // 2 seconds &heartbeat_vtable ); if (result != &sub_heartbeat) { // NULL = invalid args, other pointer = duplicate subscription exists return -1; } // Subscribe to a 16-bit subject-ID message static canard_subscription_t sub_sensor; static const canard_subscription_vtable_t sensor_vtable = { .on_message = on_heartbeat_received }; result = canard_subscribe_16b( node, &sub_sensor, 40000U, // 16-bit subject-ID 64U, // Extent CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_us, &sensor_vtable ); if (result != &sub_sensor) { canard_unsubscribe(node, &sub_heartbeat); return -1; } return 0; } ``` -------------------------------- ### Retain TX Frames with Reference Counting Source: https://context7.com/opencyphal/libcanard/llms.txt Manage frame lifetimes for deferred transmission or hardware retries. Ensure reference counts are balanced using inc/dec functions to prevent memory leaks. ```c #include // TX callback that retains frames for hardware retry static bool app_tx_with_retry(canard_t* self, void* user_context, canard_us_t deadline, uint_least8_t iface_index, bool fd, uint32_t extended_can_id, canard_bytes_t can_data) { // Try to submit to CAN driver bool submitted = try_submit_to_can_driver(extended_can_id, can_data.data, can_data.size); if (!submitted) { // Driver busy - retain the frame for later retry canard_refcount_inc(can_data); // Store frame info in retry queue store_for_retry(self, deadline, iface_index, fd, extended_can_id, can_data); } return submitted; } // Later, when retry is attempted or deadline expires void retry_or_release_frame(canard_t* node, canard_bytes_t can_data, canard_us_t deadline) { canard_us_t now = node->vtable->now(node); if (now < deadline) { // Try again bool ok = try_submit_to_can_driver(0, can_data.data, can_data.size); if (ok) { // Success - release our reference canard_refcount_dec(node, can_data); } // If still failed, keep for next retry } else { // Deadline expired - release the frame canard_refcount_dec(node, can_data); } } ``` -------------------------------- ### Monitor and Reset Error Statistics Source: https://context7.com/opencyphal/libcanard/llms.txt Access the node's internal error counters for diagnostic purposes. Resetting counters is optional and typically excludes collision data. ```c #include #include void print_canard_stats(canard_t* node) { printf("Canard Statistics:\n"); printf(" Node ID: %u\n", node->node_id); printf(" TX queue: %zu / %zu frames\n", node->tx.queue_size, node->tx.queue_capacity); printf(" TX sequence: %llu\n", (unsigned long long)node->tx.seqno); printf("\n"); printf("Error Counters:\n"); printf(" OOM errors: %llu\n", (unsigned long long)node->err.oom); printf(" TX capacity: %llu\n", (unsigned long long)node->err.tx_capacity); printf(" TX sacrificed: %llu\n", (unsigned long long)node->err.tx_sacrifice); printf(" TX expired: %llu\n", (unsigned long long)node->err.tx_expiration); printf(" RX frame errors: %llu\n", (unsigned long long)node->err.rx_frame); printf(" RX transfer err: %llu\n", (unsigned long long)node->err.rx_transfer); printf(" Node collisions: %llu\n", (unsigned long long)node->err.collision); } // Reset error counters void reset_error_counters(canard_t* node) { node->err.oom = 0; node->err.tx_capacity = 0; node->err.tx_sacrifice = 0; node->err.tx_expiration = 0; node->err.rx_frame = 0; node->err.rx_transfer = 0; // collision counter typically not reset (useful for diagnostics) } ``` -------------------------------- ### Publish Sensor Reading (16-bit Subject-ID) Source: https://context7.com/opencyphal/libcanard/llms.txt Publishes sensor data using the extended 16-bit subject-ID space. This function serializes a float value into a 4-byte payload and uses a 100ms deadline. The `subject_id` parameter can range from 0 to 65535. ```c #include // Publish sensor data using 16-bit subject-ID bool publish_sensor_reading(canard_t* node, uint16_t subject_id, float value) { // Serialize float value (little-endian IEEE 754) uint8_t payload_data[4]; memcpy(payload_data, &value, sizeof(float)); const canard_bytes_chain_t payload = { .bytes = { .size = sizeof(payload_data), .data = payload_data }, .next = NULL, }; static uint8_t transfer_id = 0; const canard_us_t deadline = node->vtable->now(node) + 100000; // 100ms return canard_publish_16b( node, deadline, CANARD_IFACE_BITMAP_ALL, canard_prio_nominal, subject_id, // 16-bit subject-ID (0-65535) transfer_id++, payload, NULL ); } ``` -------------------------------- ### Publish Heartbeat Message (13-bit Subject-ID) Source: https://context7.com/opencyphal/libcanard/llms.txt Enqueues a heartbeat message for publication. Ensure the `canard_t` structure is properly initialized. The function uses a fixed subject-ID (7509) and a 1-second deadline. ```c #include #include // Publish a heartbeat message (subject-ID 7509) bool publish_heartbeat(canard_t* node, uint32_t uptime_seconds, uint8_t health, uint8_t mode) { // Heartbeat payload: 4 bytes uptime + 1 byte health + 1 byte mode + 1 byte VSSC uint8_t payload_data[7]; payload_data[0] = (uint8_t)(uptime_seconds >> 0); payload_data[1] = (uint8_t)(uptime_seconds >> 8); payload_data[2] = (uint8_t)(uptime_seconds >> 16); payload_data[3] = (uint8_t)(uptime_seconds >> 24); payload_data[4] = health & 0x03U; payload_data[5] = mode & 0x07U; payload_data[6] = 0; // VSSC (vendor-specific status code) const canard_bytes_chain_t payload = { .bytes = { .size = sizeof(payload_data), .data = payload_data }, .next = NULL, }; static uint8_t transfer_id = 0; const canard_us_t deadline = node->vtable->now(node) + 1000000; // 1 second timeout bool ok = canard_publish_13b( node, deadline, CANARD_IFACE_BITMAP_ALL, // Send on all interfaces canard_prio_nominal, // Nominal priority 7509U, // Heartbeat subject-ID transfer_id++, payload, NULL // User context ); return ok; } ``` -------------------------------- ### Publish DroneCAN NodeStatus Message Source: https://context7.com/opencyphal/libcanard/llms.txt Publishes a DroneCAN NodeStatus message. Ensure the `canard_t` node is initialized and the `DRONECAN_NODE_STATUS_DTID` and `DRONECAN_NODE_STATUS_SIGNATURE` are correctly defined. Note that v0 always uses Classic CAN. ```c #include #define DRONECAN_NODE_STATUS_DTID 341U #define DRONECAN_NODE_STATUS_SIGNATURE 0x0F0868D0C1A7C6F1ULL // Compute CRC seed from data type signature (required for v0) uint16_t get_dronecan_crc_seed(uint64_t signature) { return canard_v0_crc_seed_from_data_type_signature(signature); } // Publish DroneCAN NodeStatus bool publish_dronecan_status(canard_t* node, uint32_t uptime_ms, uint8_t health, uint8_t mode) { uint8_t payload_data[7]; // DroneCAN uses millisecond uptime payload_data[0] = (uint8_t)(uptime_ms >> 0); payload_data[1] = (uint8_t)(uptime_ms >> 8); payload_data[2] = (uint8_t)(uptime_ms >> 16); payload_data[3] = (uint8_t)(uptime_ms >> 24); payload_data[4] = (health & 0x03) | ((mode & 0x07) << 2); payload_data[5] = 0; // Vendor-specific payload_data[6] = 0; const canard_bytes_chain_t payload = { .bytes = { .size = sizeof(payload_data), .data = payload_data }, .next = NULL, }; static uint8_t transfer_id = 0; const canard_us_t deadline = node->vtable->now(node) + 1000000; const uint16_t crc_seed = get_dronecan_crc_seed(DRONECAN_NODE_STATUS_SIGNATURE); // Note: v0 always uses Classic CAN regardless of node->tx.fd setting return canard_v0_publish( node, deadline, CANARD_IFACE_BITMAP_ALL, canard_prio_nominal, DRONECAN_NODE_STATUS_DTID, crc_seed, transfer_id++, payload, NULL ); } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.