DEV Community

Wayson Chan
Wayson Chan

Posted on

I got tired of hand-rolling message queues in FreeRTOS. So I built embedmq.

Every FreeRTOS project I've worked on has the same problem.

You have a sensor task that reads temperature. You have a UI task that needs to display it. So you create a QueueHandle_t, pass it to both tasks at init, call xQueueSend on one side and
xQueueReceive on the other. Fine.

Then you add a WiFi task that also needs temperature. You add another queue. Then a logging task. Another queue. Soon your app_init() is a mess of queue handles being passed around, and
changing one task means touching everything it's connected to.

On bare metal it's the same story in a different shape — a dozen global flags in main: if (flag_sensor) ... if (flag_button) ... if (flag_timer) ..., each one added as the project grows,
none of them easy to trace back to where they're set.

On embedded Linux it's pointers — modules holding direct references to each other, so a change in one ripples everywhere.

I wanted one solution that works across all three without rewriting the dispatch logic every time.

embedmq collapses it to 3 functions:

embedmq_register(q, "sensor.temp", on_temp, NULL); // subscriber
embedmq_post(q, "sensor.temp", &data, sizeof(data)); // producer, any thread/task

No shared queue handles. No global flags. No direct pointers between modules. The library handles the ring buffer, the mutex, and the semaphore wakeup.

Same API, three platforms:

  • Linux: pthread + POSIX semaphore, zero external dependencies
  • FreeRTOS: counting semaphore + xTaskCreate, static mode for zero heap after init
  • Bare-metal: C11 atomic spinlock, drive dispatch with embedmq_poll() from your superloop

FreeRTOS PAL is verified on the POSIX simulator in CI — not real hardware yet, I'll be honest about that.

GitHub: https://github.com/w4ysonch/embedmq

Happy to answer questions about the design or the FreeRTOS porting details.

Top comments (0)