I built EcclesRTLib — RuntimeMemory, a plain-C99 fixed-pool memory allocator designed specifically for microcontrollers where RAM is limited and a fragmented heap can eventually become a real field failure.
The problem is familiar:
Your firmware works perfectly.
You run it for hours, days, maybe weeks.
Then eventually:
malloc(96);
returns “NULL” even though the system still has plenty of total free RAM.
The memory isn’t necessarily gone.
It’s fragmented.
For an embedded device with only a few KB of RAM, that can be a nasty failure to reproduce.
So I took a different approach
Instead of continuously carving arbitrary-sized chunks from a general-purpose heap, EcclesRTLib divides a fixed memory budget into three fixed-size pools.
Requests go to the smallest suitable pool and escalate when necessary.
That means the physical layout doesn’t progressively fragment.
#include “eccles_rtmem.h”
void setup(void)
{
eccles_rtmem_init();
}
void on_radio_frame(void)
{
uint8_t \*buf = eccles_rt_malloc(96);
if (buf) {
// process frame...
eccles_rt_free(buf);
}
}
Same familiar allocation/free workflow.
Different failure characteristics.
But I didn’t want it to be just another pool allocator
One thing I particularly wanted was validated frees.
Instead of silently accepting a bad pointer, “eccles_rt_free()” can distinguish cases such as:
ECCLES_RT_FREE_OK
ECCLES_RT_FREE_ALREADY_FREE
ECCLES_RT_FREE_MID_RUN
ECCLES_RT_FREE_OUT_OF_RANGE
ECCLES_RT_FREE_MISALIGNED
So things like double-frees, foreign pointers, and pointers into the middle of an allocation can be detected rather than silently corrupting allocator state.
And then portability became the interesting part
The original allocator started life inside an ESP32 project.
I eventually wanted the same memory-management design to work across completely different embedded environments.
So EcclesRTLib now has compile-time locking backends for things including:
-
Arduino ESP32 / ESP-IDF → FreeRTOS mutex
-
Arduino AVR → interrupt-state save/restore
-
STM32 + CMSIS-RTOS2
-
STM32 bare metal → Cortex-M PRIMASK
-
RP2040/RP2350 → Pico SDK critical section
-
Zephyr → “k_mutex”
-
MSP430 → interrupt-state handling
-
Microchip XC16/XC32
- and manual locking hooks for platforms I haven’t implemented yet
The RP2040 case was particularly important because simply disabling interrupts on one core isn’t enough for a dual-core system.
It also sizes itself around the MCU
The allocator can derive its memory budget from the target’s available RAM, while allowing you to explicitly configure everything when you need precise control.
For example:
#define ECCLES_RT_MEM_SIZE 4096ul
#define ECCLES_RT_BLOCK_A_SIZE 32ul
#define ECCLES_RT_BLOCK_B_SIZE 128ul
#define ECCLES_RT_BLOCK_C_SIZE 512ul
Pool percentages, alignment, heap-backed mode, allocation strategy, logging and custom locking can all be configured.
I also tried to test it like embedded code actually matters
The repository includes randomized stress testing with:
- 200,000 allocation/free operations
- unique canary patterns on live buffers
- allocator invariant checks
- 12 configuration combinations
- AddressSanitizer
- UndefinedBehaviorSanitizer
Those tests actually caught bugs during development—including an out-of-bounds issue and an incomplete alignment guarantee—which were then fixed.
That’s one of the reasons I’m sharing it publicly: I’d rather have other embedded developers attack the design than assume it’s correct because it passed my own tests.
PlatformIO is one of the easiest ways to try it
Add this to “platformio.ini”:
lib_deps =
https://github.com/Igwe-Starking/eccles-rtmem.git
Then:
#include <eccles_rtmem.h>
The library is plain C99 and also works from C++ through “extern “C””.
It isn’t tied to Arduino.
It isn’t tied to ESP32.
It isn’t tied to one RTOS.
That’s the whole point.
And yes, there are trade-offs
This isn’t a magical replacement for every allocator.
A fixed pool can waste space internally.
Pools are independent.
An oversized allocation needs a contiguous run.
The current allocator has a 255-block limit.
And while the design is bounded and deterministic in structure, I haven’t yet published measured worst-case execution times on real hardware.
I’m deliberately stating those limitations because I want the project to be evaluated as engineering, not marketing.
Why I built it
This started from a real ESP32 smart-bike project where dynamic memory was being used alongside real-time control, audio and Android communication.
I wanted memory behavior that was more predictable.
Then I started asking:
«What happens if I take the allocator out of the ESP32 and make it work across actual embedded platforms?»
That experiment became EcclesRTLib.
Now I’m looking for embedded developers who are willing to break it.
If you use:
- ESP32
- AVR
- STM32
- RP2040/RP2350
- nRF52
- Zephyr
- FreeRTOS
- bare-metal C/C++
- or another memory-constrained MCU
I’d especially like to hear about:
real hardware results, allocator behavior under your workload, portability problems, fragmentation comparisons, timing measurements, and bugs.
GitHub:
If you have a few minutes, clone it, throw it into a PlatformIO project, and tell me what happens.
I’d much rather receive a reproducible bug report than a compliment. ![]()
#PlatformIO #Embedded #EmbeddedSystems #Microcontrollers #C #C99 arduino #ESP32 stm32 #FreeRTOS #Zephyr #RP2040 #MemoryManagement #IoT