To use Libcanard, you must implement several platform-specific callback functions to bridge the library with your hardware:
- Timekeeping: Implement
app_now to return the current monotonic time in microseconds. - Memory Management: Provide
app_alloc and app_free via a canard_mem_vtable_t to manage heap or static memory. - Transmission: Implement
app_tx to non-blockingly submit CAN frames to your hardware. - 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;
}