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)