Most embedded developers treat malloc() like a poison: “Tiny RAM + dynamic allocation = instant disaster”. But is this really true? Let me show you a real project where malloc() made life’s better.
a real project: a real case
One of my recent projects was a fertilizer mixer controller. Its job is to mix fertilizers and acid with water to achieve the desired electroconductivity and pH for irrigation in the field. During feature research, one obvious requirement emerged: recipe selection based on timetables.
For any developer, that immediately means: RTC clock + ui for adjusting date & time.
After boot, the device runs self-diagnostics and lands on the main screen:
main screen already displaying the current date & time
If we followed the “no malloc() ever” rule and did everything statically, adjusting the time would look like this:
-
Activate the menu screen
-
Navigate through options to find “Date & Time” settings one
-
Activate the option
-
Adjust the data
-
Get back to the menu screen
-
Get back to the main one
…navigating the menu …
…to get to the time & date screen
Too much hassle, right? I don’t like it either.
the solution we all got used to
On a PC you have at least two ways: deep into settings or just double-click the clock on the taskbar. Much more convenient:
So why not do the same in embedded?
The real challenge is knowing where to return after adjusting time:
- back to menu, then to main?
- or directly to main, because time was opened straight from there? I bet you’re about to say: back to the place we came from!
the solution code
To solve this elegantly, I introduced a simple window stack structure:
typedef struct {
unsigned char parent; // index/pointer to parent window
unsigned char current_screen;// enum type of this window
unsigned char current_item; // currently selected/clicked item
unsigned char items; // total number of clickable items
bool active; // is this position "active" for value change
} WINDOW;
WINDOW* active_window; // global pointer to current window
And two key functions
void window_destroy(WINDOW *ptr) {
if (ptr->parent == NULL) return; // don't destroy the main/root window
ptr->active = false;
active_window = (WINDOW*)ptr->parent;
free(ptr);
}
bool window_create(WINDOW *parent, unsigned char current_screen,
unsigned char items, unsigned char current_item,
bool active) {
WINDOW* new_win = (WINDOW*)malloc(sizeof(WINDOW));
if (new_win == NULL) return false;
new_win->parent = (unsigned char)parent;
new_win->current_screen = current_screen;
new_win->items = items;
new_win->current_item = current_item;
new_win->active = active;
active_window = new_win;
return true;
}
Result? Every time we open a new window (menu, settings, date/time), we allocate exactly 5 bytes. Yes – 5 bytes.
the aftermath
On an Atmega328 where nearly half the RAM was already reserved for the OLED frame buffer, this was completely negligible.Navigation became dramatically better: we navigate with cursor to date → enter opens adjustment window → escape button: right back to main. No more tedious menu diving.
So why do so many embedded developers still demonize malloc()? I suspect the real issue for many is discomfort with memory pointers. Once you master pointers and memory management, malloc() stops being scary – it becomes just another tool. And in some cases (like this one), it becomes the best tool.
What’s your experience? Have you successfully used malloc() in a resource-tight embedded project? Or do you still avoid it at all costs?