The firmware mistakes in this article usually aren't noticed in a prototype, and none of them cause a problem until the product is already in custo...

The firmware mistakes I'm going to discuss usually aren't noticed in a prototype, and none of them cause a problem until the product is already in customers' hands.

One of these mistakes wears out the flash memory in a microcontroller in days instead of years.

Another one lets anyone easily copy all of your product's firmware.

And a third one is now against the law in the UK and California, and it'll block your CE mark in Europe.

After reviewing the firmware on a lot of startup products, these are the nine mistakes I see cause the most problems.

So I'm counting down all nine, and several of the fixes are only a few lines of code.

Click here to watch or read

Mistake #9 - Debug logging left in the shipping build

Almost every firmware project starts with print statements sprayed all over the code, and that's fine while you're developing.

The problem is leaving all of it turned on in the build you actually ship.

Each of those print calls has to format a string and push it out the serial port one character at a time, and with the default serial setup on most microcontrollers, that stalls the rest of your main code while it runs.

A single long line of text can tie up the processor for a couple of milliseconds, which is long enough to miss a sensor sample or throw off a timing loop.

It also burns power, because the chip can't drop into a low-power sleep mode while it's busy printing.

The bigger problem is what you're printing.

Development logs tend to include internal state, memory addresses, and sometimes the actual credentials the device is using.

Anyone with a five dollar USB to serial adapter can clip onto that UART pin and read it.

Production logging at a sane level is fine, and it's useful for debugging units that come back from the field.

The fix is to put your logging behind levels, so the verbose stuff is compiled out of the release build and only warnings and errors make it into the product.

Mistake #8 - Blocking delays in the main loop

The delay function is the first thing everyone learns, and it's the easiest way to make an LED blink.

The problem starts when delay becomes the timing architecture for the whole product.

Without an operating system, a blocking delay means your main loop sits and spins doing nothing for that entire time, so it can't check a button, service a sensor, or respond to a wireless packet.

A quick button press that happens entirely during a 500 ms delay won't be detected at all.

And this is something you may never catch in your own testing, because your button presses may just happen to land outside of that delay.

It's also brutal for battery life, because a processor spinning in a delay loop is drawing full run current the whole time instead of sleeping.

A few microseconds of delay to satisfy a peripheral's timing requirement is fine.

What you want instead is a non-blocking design, where a hardware timer or a simple state machine decides when things happen and the processor sleeps in between.

Even a basic loop that checks the clock before doing anything is a big step up, and if the product is complex enough, a small real-time operating system will handle the scheduling for you.

Mistake #7 - Hammerming flash or EEPROM with constant writes

Flash memory and EEPROM both wear out, and every part has a finite write budget.

A typical microcontroller's internal flash is rated for around 10,000 write and erase cycles per sector, and a dedicated EEPROM chip is usually rated somewhere around a million.

Those sound like big numbers until you do the math on firmware that saves a setting every second, because at that rate a million cycles are gone in under two weeks.

Doing this usually looks innocent, like saving the volume level or a run-time counter to non-volatile memory every time through the loop.

Once a cell wears out, reads start returning garbage and settings reset themselves, and there's no fixing it in the field because the memory is physically damaged.

The fix is to only write when something actually changes, and even then to wait a few seconds so a customer spinning a knob doesn't generate a hundred writes.

If you need to log data continuously, don't write to the same spot every time, spread the writes around the memory so no single section wears out early.

Most chip vendors offer a storage library that handles this for you.

Or use a part built for that job like FRAM, which is a type of non-volatile memory that can handle practically unlimited writes.

Mistake #6 - Watchdog timer never enabled

The watchdog timer is built into just about every microcontroller on the market, and unfortunately most products ship with it turned off.

A watchdog is a simple countdown that resets the chip unless your firmware keeps resetting the counter, which is sometimes called petting the watchdog.

If your code hangs, the counter stops getting reset, so the timer runs out and the chip reboots itself.

Without a watchdog, if the microcontroller hangs it stays in that hung state until someone cycles the power.

And firmware hangs for all kinds of reasons that never show up in your own testing, like an I2C sensor holding the data line low or a rare buffer overflow that only happens once a week.

The customer sees a product that just stops working, and most of them will return it rather than power cycle it.

The mistake I see even when people turn the watchdog on is resetting it from a timer interrupt.

That interrupt keeps firing even when the main loop is completely stuck, so the watchdog never trips and you've gained nothing.

Reset the watchdog from the main loop, and only after you've confirmed every critical task is still running.

Mistake #5 - Brown-out detection turned off

Brown-out detection is a circuit inside the microcontroller that watches the supply voltage and holds the chip in reset whenever the voltage drops below a safe level.

The same kind of low voltage protection shows up in lots of other chips as an undervoltage lockout, or UVLO.

When I was designing power management chips at TI, every chip had to have a UVLO, because a chip running on too little supply voltage behaves unpredictably.

And that can happen from a slow supply ramp at power-on just as easily as from a voltage dip.

A microcontroller with insufficient supply voltage doesn't just stop, it skips instructions and corrupts registers while it thinks everything is fine.

Now picture that happening in the middle of a flash write.

A battery sags under load or a motor kicks on, the voltage dips for a few milliseconds, and the firmware is halfway through saving your settings.

What ends up in flash is corrupted, and depending on what was being written, the unit becomes non-functional.

A member of my Hardware Academy ran into this problem, with a microcontroller that kept brown-out resetting.

The common reaction is to make the resets stop by turning off the brown-out detection.

The advice they got was to be grateful the detector was tripping, because it was catching a real power problem that needed fixing.

Turning it off would have only masked that problem.

Also make sure the threshold is set above the minimum voltage the chip needs for reliable flash writes, not just the minimum it needs to run.

An external voltage supervisor chip can also be useful if you need a tighter threshold, but turning off the built-in detector with nothing in its place is asking for problems.

Mistake #4 - Debug port left open with no readout protection

The debug port on a microcontroller, usually SWD or JTAG, is how you program and debug the chip during development.

Ship a product with that port wide open and anyone with a debugger can pull your entire firmware off the chip in seconds, and that's your whole product in a file.

For a lot of products the hardware is the easier part to copy, especially if you aren't using any custom designed chips, so your firmware is often the only thing standing between you and a knockoff.

Don't make it easy for them to copy that too.

Almost every chip family has a fix built in, and it's usually one setting.

STM32 has readout protection levels, and Nordic parts have an access port protection setting.

The ESP32 needs a permanent fuse that disables the debug port, plus flash encryption to keep the firmware itself unreadable.

Most product creators never turn it on, usually because nobody wants to lose the ability to debug a returned unit.

Set it as the final step in your production programming, after the firmware is loaded and tested.

Just be careful with the permanent options some chips offer, like the highest readout level on most STM32 parts, which can never be undone even by you, so pick the level that blocks readout but still lets you do a full erase for warranty repairs.

Mistake #3 - Secrets hardcoded in the firmware image

Hardcoding private API keys, cloud secrets, or Wi-Fi credentials into the firmware is one of the most common mistakes, because it's the fastest way to get a prototype talking to the cloud.

The problem is that a firmware image is just a file, and every plain text string inside it is sitting there in the open.

Anyone who pulls the image off the chip, or downloads it from your update server, can run one command that lists every readable string in the file.

And since the same key is baked into every unit, one compromised device can compromise every device you've sold.

That's when you end up with somebody pushing fake data into your cloud, running up your API bill, or pulling other customers' data.

Wi-Fi credentials are a slightly different case, because the ones that end up hardcoded are usually your own, left over from prototyping on your office network, and now anyone who reads your firmware can get onto that network.

The customer's network details should come in through a provisioning step, where they enter them through your app or a temporary setup mode, so there's never a reason for any Wi-Fi password to live in the firmware image.

Cloud credentials should be unique per device, programmed in at the factory or generated on first boot, and stored in a secure element or a protected region of memory rather than in the main image.

Mistake #2 - No secure, recoverable firmware update path

Firmware bugs are about the only bugs you can fix after the product ships without getting units back from customers, but only if you built a way to do it.

A surprising number of products ship with no update path at all, so the first serious bug you find becomes a permanent feature of every unit you've sold.

The other version of this mistake is almost worse, an update path with no security on it, where the device accepts any firmware image somebody pushes to it.

That means anyone who figures out your update protocol can load their own code onto your customers' devices.

A proper update path needs two things.

First, every firmware image has to be signed, and the device has to check that signature before it runs any of the new code.

Second, the update has to be recoverable, so a failed download or a power loss halfway through doesn't leave the unit non-functional.

The standard way to do that is keeping two copies of the firmware in flash, so the device runs the old one until the new one is verified and then switches over.

That also means you need room for two copies of your firmware, either in the microcontroller's internal flash or in an external flash chip, so keep it in mind when you're choosing a microcontroller.

Most modern chip families come with a bootloader framework that handles this, but you have to set it up before the first unit leaves the factory.

Mistake #1 - One default password shared across every unit

I saved this one for last on purpose, because it's the only mistake on this list that's now against the law in parts of the world.

Shipping every unit with the same default login, like admin and password, was standard practice for years, and manufacturers even sometimes printed it in the manual so customers could get into their settings.

The problem is that attackers know those default logins too, and there are automated tools that scan the internet for devices still using them.

Since April 2024 the UK has banned universal default passwords on connected consumer products, with fines up to ten million pounds or 4% of global revenue.

California has required unique or user-set passwords on connected devices that can be logged into remotely since 2020.

And in Europe the new cybersecurity rules for internet-connected wireless products, which took effect in August 2025, treat a shared default password as a failure of the standard.

So in practice that means no CE mark through the normal self-certification route.

The fix is a unique password per unit, generated at the factory and printed on the label, or forcing the customer to set their own the first time they power it on.

Talk soon,

John

P.S. If you need help making sure your firmware doesn't have these problems, then you can get help from me and other experts inside the Hardware Academy.