FreeRTOS (C) Integration
This guide explains how to use the ferrite-sdk C FFI library from a FreeRTOS project. The setup is similar to the Zephyr integration -- you link the same static library and use the same C header.
Overview
FreeRTOS applications typically use a dedicated task for telemetry upload. The pattern is:
- Call
ferrite_sdk_init()inmain()before starting the scheduler. - Record metrics from any task or ISR.
- Run
ferrite_upload()from a dedicated upload task on a periodic timer.
Build and link
Build the static library for your target (see Zephyr guide, Step 1). Then add it to your build system.
Makefile
LIBS += lib/libferrite_ffi.a
CFLAGS += -IincludeCMake
add_executable(my_firmware src/main.c)
target_include_directories(my_firmware PRIVATE include)
target_link_libraries(my_firmware PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/lib/libferrite_ffi.a
)Copy ferrite_sdk.h from the Zephyr guide into your include/ directory.
FreeRTOS example
#include "FreeRTOS.h"
#include "task.h"
#include "ferrite_sdk.h"
#include "uart_driver.h" /* Your UART HAL */
/* ---- Transport callback ---- */
static int32_t uart_send_chunk(const uint8_t *data, uint32_t len, void *ctx)
{
(void)ctx;
return uart_transmit(data, len) == 0 ? 0 : -1;
}
/* ---- Ticks function ---- */
static uint64_t get_ticks(void)
{
return (uint64_t)xTaskGetTickCount();
}
/* ---- Upload task ---- */
static void upload_task(void *pvParameters)
{
(void)pvParameters;
ferrite_transport_t transport = {
.send_chunk = uart_send_chunk,
.is_available = NULL, /* always available */
.ctx = NULL,
};
for (;;) {
vTaskDelay(pdMS_TO_TICKS(60000)); /* 60 seconds */
ferrite_upload_stats_t stats;
ferrite_error_t err = ferrite_upload(&transport, &stats);
if (err == FERRITE_OK) {
printf("Upload: %lu chunks, %lu bytes\n",
(unsigned long)stats.chunks_sent,
(unsigned long)stats.bytes_sent);
}
}
}
/* ---- Sensor task ---- */
static void sensor_task(void *pvParameters)
{
(void)pvParameters;
for (;;) {
float temp = read_temperature_sensor();
ferrite_metric_gauge("temperature", temp);
ferrite_metric_increment("readings", 1);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
/* ---- Main ---- */
int main(void)
{
hardware_init();
/* Initialize the SDK before starting the scheduler */
ferrite_ram_region_t regions[] = {
{ .start = 0x20000000, .end = 0x20020000 },
};
ferrite_sdk_init(
"freertos-device",
"2.0.0",
0,
get_ticks,
regions,
1
);
ferrite_record_reboot_reason(1); /* PowerOnReset */
/* Create tasks */
xTaskCreate(sensor_task, "sensor", 256, NULL, 2, NULL);
xTaskCreate(upload_task, "upload", 512, NULL, 1, NULL);
/* Start scheduler */
vTaskStartScheduler();
/* Should not reach here */
for (;;) {}
}Thread safety notes
ferrite_metric_increment,ferrite_metric_gauge, andferrite_metric_observeare safe to call from multiple FreeRTOS tasks. The SDK uses Cortex-M critical sections (CPSID I/CPSIE I) internally.ferrite_upload()must be called from only one task at a time. It is not reentrant.- Do not call
ferrite_upload()from an ISR -- it performs blocking I/O through the transport callback. ferrite_metric_*functions can be called from ISRs (they are interrupt-safe via critical sections), but keep ISR execution time in mind.
Linker script for retained RAM
FreeRTOS projects typically use a custom linker script. Add the retained RAM section:
/* At the end of your MEMORY block */
RETAINED (rwx) : ORIGIN = 0x2001FF00, LENGTH = 0x100
/* In your SECTIONS block */
.uninit.ferrite (NOLOAD) : {
. = ALIGN(4);
KEEP(*(.uninit.ferrite))
. = ALIGN(4);
} > RETAINEDEnsure your _estack and heap boundaries account for the 256 bytes reserved for retained RAM.
Stack sizing
The ferrite_upload() function uses approximately 400 bytes of stack space (for chunk encoding buffers). Size your upload task stack accordingly -- 512 words (2 KB) is a safe default.