First Notes On Firmware For Embedded Products
Early principles for firmware on a small embedded product: scope, module boundaries, logging, and handling errors without guessing.
Share

You can use these notes as a practical starting point, then tighten each decision as your product constraints become clearer.
Start With The Main Loop, Not Every Feature
The first version of firmware for a small product does not need every feature working. It needs one thing: a main loop that runs reliably and does not silently stop. Before adding sensor reads, communication, or UI logic, I want to know the board boots the same way every time, survives a reset, and keeps running without hanging.
This sounds obvious, but it is easy to skip. It is tempting to write the "real" feature first because it is more interesting. The cost shows up later, when a hang or a reset loop has no obvious cause because too many pieces were added before the base loop was proven stable.
Modules Should Be Easy To Isolate
Firmware for a small product usually ends up touching sensors, communication, timing, and some form of output. If all of that lives in one file with shared state everywhere, a bug in one area becomes hard to separate from the rest.
I try to keep a rough boundary between three kinds of code: code that talks to hardware directly, code that decides what to do, and code that reports status. It does not need to be a formal architecture. It only needs to be clear enough that when something behaves wrong, there is an obvious place to start looking — a bad sensor read, a wrong decision, or a broken status report are three different problems, and mixing them together makes each one harder to find.
Logging Matters More Than It Looks
A UART log line costs almost nothing to write and saves a disproportionate amount of debugging time. Early on, I want at minimum a boot message, a way to print the current state, and a way to report an unexpected value instead of silently ignoring it.
The mistake to avoid is adding logging only after something breaks. By then, the bug may already be intermittent or gone. Logging that exists from the first working version turns a mystery failure into a readable sequence of events, which is often enough to find the cause without adding a debugger session.
Handling Errors Without Guessing
Small embedded firmware often skips real error handling because "it should not happen." A sensor read fails once in a while. A message arrives malformed. A timer overflows earlier than expected. None of these are common, but all of them happen eventually, and unhandled they tend to produce confusing behavior far from the actual cause.
My rule for early firmware is smaller than a full error handling strategy: every value that comes from outside the code — a sensor, a communication line, user input — gets checked before it is trusted. If it fails the check, the firmware should report it, not silently continue with a bad value. This alone removes a large share of the strange bugs that only show up after hours of running.
This also applies to timing, not only data. A sensor read that normally takes a few milliseconds but occasionally hangs will eventually stall the whole loop if there is no timeout around it. Adding a simple timeout early, even a generous one, turns a full system hang into a single reported failure that the rest of the firmware can react to.
Notes For The Next Firmware Pass
The same discipline that applies to hardware notes applies here: write down why a value was chosen, not only what the value is. A timeout of 200ms, a retry count of three, a debounce delay of 20ms — none of these numbers are obvious months later without the reasoning attached.
Firmware for a small product does not need to be elegant on the first pass. It needs to be honest about what it does and does not handle yet, so the next version can improve on a known baseline instead of guessing at the current one.
References
These sources are useful background material for checking terminology, limits, and engineering recommendations before applying the notes to a real prototype.
- ESP-IDF Programming Guide: FreeRTOS (IDF)
- ESP-IDF Programming Guide: Logging library
- ESP-IDF Programming Guide: Error handling
Related reading
Share
Keep exploring
Read next
Related articles
TinyML on ESP32: Running AI on the Device
How I run a small person-detection model on ESP32 with TensorFlow Lite Micro and ESP-NN, from memory limits to testing Espressif's example.
Designing an Emotion State System for the Mochi Robot
Build a Mochi Robot emotion state machine for idle, listening, talking, happy, sad, thinking, and error states with replaceable animations.
Extending Battery Life in an ESP32 Robot with a Display
Analyze battery life for an ESP32 robot with a display: backlight, Wi-Fi, audio, peripherals, sleep modes, wake sources, and runtime.