---
layout: post
title: "Recovering an ESP32 DevKit from Reverse Polarity on the 3V3 Pin"
date: 2026-03-07
categories: [electronics, esp32, tasmota]
tags: [esp32, tasmota, reverse-polarity, recovery, esptool, platformio]
---

*Writeup generated with AI*

What started as an accidental reverse polarity event on an ESP32-U DevKit board turned into a surprisingly successful recovery. This post documents the full diagnostic process and the steps that ultimately brought the board back to life running Tasmota.

## The Incident

Reverse polarity was applied directly to the **3V3 pin** of an ESP32-U DevKit board. This is arguably worse than reversing the 5V input, because it bypasses the onboard LDO regulator entirely — the reversed voltage goes straight onto the 3.3V rail feeding the ESP32 and all other components directly.

## Initial Symptoms

- The USB-to-UART bridge was alive and enumerating correctly (expected, as it is powered from the 5V USB rail, upstream of the LDO)
- The board was unresponsive to flashing attempts
- **0V measured on the 3V3 pin** while powered from USB

## Diagnosis

### Ruling out a shorted rail

Measuring resistance between GND and 3V3 with the board unpowered showed **750Ω** — a healthy value indicating no shorted decoupling capacitors or blown ESD diodes on the rail. This was an encouraging sign pointing to the LDO as the failed component rather than the ESP32 itself.

### LDO thermal shutdown

The AMS1117-3.3 LDO on ESP32 DevKit boards includes built-in thermal shutdown protection. The reverse polarity event likely caused excessive current through the LDO, triggering thermal shutdown. After sufficient cooling time during the resistance measurements, the LDO **recovered on its own** and 3V3 was restored — consistent with thermal shutdown behaviour rather than permanent damage.

### Flash chip diagnosis

With power restored, flashing still failed with:

```
Warning: Failed to communicate with the flash chip, read/write operations will fail.
```

Using `esptool` with the `--no-stub` flag also failed, and `flash-id` returned `0xff/0xffff` — indicating no response on the SPI bus from the ROM bootloader.

However, running `flash-id` **with** the stub flasher returned a valid response:

```
Manufacturer: 68
Device: 4016
Detected flash size: 4MB
```

Manufacturer `0x68` is **Boya Micro (BY25Q32)** — a legitimate, working flash chip. The discrepancy between ROM bootloader and stub was due to different SPI bus initialisation methods; the stub's optimised driver worked where the ROM's did not.

## Recovery Steps

### 1. Erasing the flash in chunks

A full `erase-flash` operation failed mid-way with:

```
A fatal error occurred: Packet content transfer stopped (received 9 bytes).
```

This was consistent with marginal LDO stability under sustained load. Erasing in smaller regions worked reliably:

```bash
esptool erase-region 0x3D0000 0x20000
```

### 2. Reflashing Tasmota

Direct reflash via esptool succeeded:

```bash
esptool --baud 115200 write-flash 0x0 tasmota32-FR.bin
```

### 3. Diagnosing the safeboot loop

After reflashing, the device booted but the web interface was unresponsive (IP pinged but empty HTTP answer from http://192.168.4.1/).
Serial console output revealed:

```
Project tasmota - Tasmota Version 15.3.0(release-safeboot)-3.3.7(2026-02-19T13:53:54)
WARNING This version does not support persistent settings
```

The erase operations had wiped the OTA selection flag at `0xe000`, causing the bootloader to fall back to the safeboot partition instead of the main firmware.

The safeboot firmware is a special trimmed down version of Tasmota used during OTA upgrades. It specifically does not include the _initial configuration_ web page.

### 4. Full PlatformIO reflash

A complete PlatformIO rebuild and reflash restored all partition components including the OTA flag:

| Offset | File |
|--------|------|
| `0x1000` | `bootloader.bin` |
| `0x8000` | `partitions.bin` |
| `0xe000` | `boot_app0.bin` ← OTA selection flag |
| `0x10000` | `tasmota32-safeboot.bin` |
| `0xe0000` | `firmware.bin` ← main firmware |

This restored normal boot into the full `tasmota32-FR` firmware.

## How the Tasmota32 Safeboot System Works

The Tasmota32 dual-partition safeboot architecture is worth understanding:

- On every normal boot, the OTA flag at `0xe000` points the bootloader to the main firmware at `0xe0000`
- If the device **reboots 4 times in quick succession** (crash loop), Tasmota automatically switches the OTA flag to the safeboot partition as a recovery mechanism
- Safeboot can also be triggered manually via `Restart 99` from the Tasmota console
- From the safeboot web interface, you can OTA flash a new main firmware, which resets the OTA flag back to normal

The safeboot partition is intentionally minimal (~300–400KB) — it includes only a web server, OTA flash capability, basic WiFi AP, and a stripped console. It has no persistent settings, no MQTT, and most Tasmota commands are unavailable.

## Outcome

The board made a **full recovery** with no permanent damage:

- ESP32 SoC: intact
- Flash chip: intact
- LDO: recovered from thermal shutdown
- Tasmota: running normally

## Lessons Learned

- Reverse polarity on the 3V3 pin is more dangerous than on the 5V pin as it bypasses the LDO entirely
- The AMS1117 thermal shutdown can mimic permanent failure but recover after cooling
- Always use the stub flasher (`esptool` without `--no-stub`) for flash communication — the ROM bootloader SPI initialisation can fail where the stub succeeds
- Partial flash erase operations can corrupt the OTA selection flag, causing unexpected safeboot behaviour
- A full PlatformIO reflash is the safest way to restore a complete and correct partition layout
- Consider adding reverse polarity protection (Schottky diode or P-channel MOSFET) on power inputs in future designs

## Alternate exit path from Safeboot

I could have reset the safeboot flag using:
```
esptool write-flash 0xe000 ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin
```

This just rewrites the OTA selection flag directly, telling the bootloader to boot the main firmware partition — without touching anything else. A full reflash would not have been necessary.
From within the safeboot console, there isn't a direct command to flip the flag — that's intentional, since the whole point of safeboot is to force you through the OTA web interface to upload known-good firmware before returning to normal boot.
