One firmware codebase, four device variants
/ 2 min read
Table of Contents
When I joined my internship, the firmware lived in Arduino IDE sketches. Each device variant had its own copy of the same drivers, the same Wi-Fi handling, the same MQTT publish loop — and every bug had to be fixed four times.
The problem with copy-paste firmware
Sketch-per-device feels harmless at two devices. At four it means:
- every fix is an N-way cherry-pick,
- pin maps drift between variants,
- there is no place to put a unit test,
- and onboarding means reading four files to learn one program.
The shape I moved to
A single modular C++ project on PlatformIO:
- PlatformIO instead of the IDE — real dependency management, reproducible builds, and a CI command that is not “open the IDE and press upload”.
- Config profiles per variant — one repository, four build environments. The pins, sensor set,
and feature flags live in configuration, not in
#ifdefsoup spread across files. - Layered code — drivers, then services (network, telemetry), then the application loop. The application never touches a register; the driver never knows about MQTT.
What actually improved
Code duplication dropped to near zero for shared behaviour, and adding a variant became a configuration change rather than a fork. The bigger win is conceptual: firmware started being treated as a product with a structure, not a folder of sketches that happen to compile.
If you are at the point where you copy a sketch before editing it, that is the signal to move to PlatformIO and profiles.