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 window showing current date and time

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 ...

    …navigating the menu …

    ...to get to the time & date screen

    …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:

flowchart TD A[Main screen]-->B[Menu screen] B-->C[Date and time screen] A-->C

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?