Dumping Firmware from ESP8684

In the past I’ve had bench power supplies that have an linux or windows app and you connect it via USB. So when I recently purchased a a new bench power supply, I noticed it has a mobile application. Why? Who knows.

More surprisingly though, a ESP on a breakout board fell out of the box when I was unpacking it. Tbh, I’m happy about that, as now my bench PSU isn’t accessible over Bluetooth or WiFi.

It also however, made me very interested in what firmware is flashed on the chipset as most ESP in commercial products I’ve ran into run custom firmware.

Thinking it’d be nice and easy, I pulled out my bus pirate connected to the labeled pins on the breakout board and ‘W’ to give it some power. However, nothing happened. This started me on a few hour journey to figuring out clearly how to access this chip while it is still soldered to this breakout board.

Turns out it’s a ESP8684-WROOM-04C. You can check its datasheet HERE.

Oddly the breakout board had one 3.3v and one 5v labeled pin.

So I decided to first figure out how to get it into UART (Download) or SPI (Download) mode first and then figure out the breakout. Turns out though, this chipset also has a “Joint Download” mode which puts it both in UART and SPI (Download) mode.

So I went the regular way of connecting probes connected to a logic analyzer and seeing data like:

Eventually, I finally got it to a point where I had UART output via it’s UART output “force” pin.

First I saw the classic gobbly gook:

Then eventually by changing the baud to 74880 (used for ESP debug I guess) and finally I got some output on turning on the chip.

As I didn’t find any clear examples of someone interacting with this chip (it’s based? off the ESP32-C2 so you can look at some of that for help if you need it) I decided to post some information about it, hopefully it’ll make it easier for y’all.

I ended up using two separate devices, one just pulling up/down the required PINs and then one that was actually doing the RX and TX.

Here’s an overview of what you need for each mode:

Here’s the details for Joint Download:

74880 Baud Otherwise it’s the regular 8n1.

+——————-+———————-+—————+
| ESP8684 Pin | Connect To | Description |
+——————-+———————-+—————+
| IO0 | GND | (Forces flashing mode)
| IO9 | GND | (Joint Download Boot Mode)
| IO8 | GND (Optional) | (Enables UART boot messages)
| EN | 3.3V (via 10kΩ pull-up) | (Enable chip, reset via LOW→HIGH)
| GND | Bus Pirate GND | (Common ground)
| 3V3 | Bus Pirate VOUT (3.3V) | (Power supply)
| TX (Pin 22) | Bus Pirate RX | (ESP TX → Bus Pirate RX)
| RX (Pin 21) | Bus Pirate TX | (ESP RX → Bus Pirate TX)
+——————-+———————-+———————————–+

I used a WaveShare UART adapter for the EN 3.3v and then for the 3 grounds (IO0, IO8, IO9)

And then Bus Pirate for the rest.

If it’s working properly you’ll see a message like:

(SPI/UART DOWNLOAD MODE)

For Reg UART Download Mode:

+——————-+———————-+——————–+
| ESP8684 Pin | Connect To | Description
+——————-+———————-+——————–+
| IO0 | GND | (Forces flashing mode)
| IO8 | GND (Optional) | (Enables UART boot messages)
| EN | 3.3V (via 10kΩ pull-up) | (Enable chip, reset via LOW→HIGH)
| GND | Bus Pirate GND | (Common ground)
| 3V3 | Bus Pirate VOUT (3.3V) | (Power supply)
| TX (Pin 22) | Bus Pirate RX | (ESP TX → Bus Pirate RX)
| RX (Pin 21) | Bus Pirate TX | (ESP RX → Bus Pirate TX)
+——————-+———————-+——————–+

And you should see

If you do SPI Download Mode:

To be very clear:

IO9, IO8, IO3 = DOWN + IO2 = UP | SPI download boot mode

IO9 = UP == SPI Boot Mode

IO9 = DOWN+ IO8 = UP == Joint Download Boot Mode

If you have something up/down/ not connected that doesn’t make sense, you’ll see it boot into “unknown” mode:

I’d suggest just using classic UART download mode if it isn’t disabled in the EFUSE as it’s just simpler. So once you have it booted into UART Download you can use ESPTool but you’ll notice that the ESP8684 isn’t a chipset option. You’ll see that it recognizes it as a ESP32-C2 and works fine. Here’s the output from running ‘chip_id’

Command:

python -m esptool --port COM13 --baud 74880 chip_id

Output:

esptool.py v4.7.0
Serial port COM13
Connecting...
Detecting chip type... ESP32-C2
Chip is ESP32-C2 (revision v1.0)
Features: WiFi, BLE
Crystal is 26MHz
MAC: 34:98:7a:2a:XX:XX
Uploading stub...
Running stub...
Stub running...
Warning: ESP32-C2 has no Chip ID. Reading MAC instead.
MAC: 34:98:7a:2a:XX:XX
Hard resetting via RTS pin...

And running ‘espefuse summary’

Command:

python -m espefuse --port COM13 --baud 74880 summary

Output:

espefuse.py v4.7.0
Connecting....
Detecting chip type... ESP32-C2

=== Run "summary" command ===
EFUSE_NAME (Block) Description  = [Meaningful Value] [Readable/Writeable] (Hex Value)
----------------------------------------------------------------------------------------
Calibration fuses:
OCODE (BLOCK2)                                     OCode                                              = 87 R/W (0b1010111)
TEMP_CALIB (BLOCK2)                                Temperature calibration data                       = -6.1000000000000005 R/W (0b100111101)
ADC1_INIT_CODE_ATTEN0 (BLOCK2)                     ADC1 init code at atten0                           = -88 R/W (0x96)
ADC1_INIT_CODE_ATTEN3 (BLOCK2)                     ADC1 init code at atten3                           = 60 R/W (0b01111)
ADC1_CAL_VOL_ATTEN0 (BLOCK2)                       ADC1 calibration voltage at atten0                 = -104 R/W (0x9a)
ADC1_CAL_VOL_ATTEN3 (BLOCK2)                       ADC1 calibration voltage at atten3                 = 16 R/W (0b000100)
DIG_DBIAS_HVT (BLOCK2)                             BLOCK2 digital dbias when hvt                      = -24 R/W (0b10110)
DIG_LDO_SLP_DBIAS2 (BLOCK2)                        BLOCK2 DIG_LDO_DBG0_DBIAS2                         = -60 R/W (0b1001111)
DIG_LDO_SLP_DBIAS26 (BLOCK2)                       BLOCK2 DIG_LDO_DBG0_DBIAS26                        = -60 R/W (0x8f)
DIG_LDO_ACT_DBIAS26 (BLOCK2)                       BLOCK2 DIG_LDO_ACT_DBIAS26                         = 28 R/W (0b000111)
DIG_LDO_ACT_STEPD10 (BLOCK2)                       BLOCK2 DIG_LDO_ACT_STEPD10                         = 16 R/W (0x4)
RTC_LDO_SLP_DBIAS13 (BLOCK2)                       BLOCK2 DIG_LDO_SLP_DBIAS13                         = 8 R/W (0b0000010)
RTC_LDO_SLP_DBIAS29 (BLOCK2)                       BLOCK2 DIG_LDO_SLP_DBIAS29                         = 12 R/W (0b000000011)
RTC_LDO_SLP_DBIAS31 (BLOCK2)                       BLOCK2 DIG_LDO_SLP_DBIAS31                         = 0 R/W (0b100000)
RTC_LDO_ACT_DBIAS31 (BLOCK2)                       BLOCK2 DIG_LDO_ACT_DBIAS31                         = 28 R/W (0b000111)
RTC_LDO_ACT_DBIAS13 (BLOCK2)                       BLOCK2 DIG_LDO_ACT_DBIAS13                         = 16 R/W (0x04)
ADC_CALIBRATION_3 (BLOCK2)                         Store the bit [86:96] of ADC calibration data      = 0 R/W (0b00000000000)

Config fuses:
[CUT]
UART_PRINT_CONTROL (BLOCK0)                        Set the default UARTboot message output mode       = Disable R/W (0b11)
[CUT]

Flash fuses:
FORCE_SEND_RESUME (BLOCK0)                         Set this bit to force ROM code to send a resume co = False R/W (0b0)
                                                   mmand during SPI boot
FLASH_TPUW (BLOCK0)                                Configures flash waiting time after power-up; in u = 0 R/W (0x0)
                                                   nit of ms. If the value is less than 15; the waiti
                                                   ng time is the configurable value.  Otherwise; the
                                                    waiting time is twice the configurable value

Identity fuses:
DISABLE_WAFER_VERSION_MAJOR (BLOCK0)               Disables check of wafer version major              = False R/W (0b0)
DISABLE_BLK_VERSION_MAJOR (BLOCK0)                 Disables check of blk version major                = False R/W (0b0)
WAFER_VERSION_MINOR (BLOCK2)                       WAFER_VERSION_MINOR                                = 0 R/W (0x0)
WAFER_VERSION_MAJOR (BLOCK2)                       WAFER_VERSION_MAJOR                                = 1 R/W (0b01)
PKG_VERSION (BLOCK2)                               EFUSE_PKG_VERSION                                  = 1 R/W (0b001)
BLK_VERSION_MINOR (BLOCK2)                         Minor version of BLOCK2                            = With calib R/W (0b001)
BLK_VERSION_MAJOR (BLOCK2)                         Major version of BLOCK2                            = 0 R/W (0b00)

[CUT]

Security fuses:
DIS_DOWNLOAD_ICACHE (BLOCK0)                       The bit be set to disable icache in download mode  = False R/W (0b0)
DIS_DOWNLOAD_MANUAL_ENCRYPT (BLOCK0)               The bit be set to disable manual encryption        = False R/W (0b0)
SPI_BOOT_CRYPT_CNT (BLOCK0)                        Enables flash encryption when 1 or 3 bits are set  = Enable R/W (0b111)
                                                   and disables otherwise
XTS_KEY_LENGTH_256 (BLOCK0)                        Flash encryption key length                        = 256 bits key R/W (0b1)
[CUT]
BLOCK_KEY0 (BLOCK3)                                BLOCK_KEY0 - 256-bits. 256-bit key of Flash Encryp
   = ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? -/-
                                                   tion
BLOCK_KEY0_LOW_128 (BLOCK3)                        BLOCK_KEY0 - lower 128-bits. 128-bit key of Flash
   = ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? -/-
                                                   Encryption
BLOCK_KEY0_HI_128 (BLOCK3)                         BLOCK_KEY0 - higher 128-bits. 128-bits key of Secu
   = ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? -/-
                                                   re Boot

Wdt fuses:
WDT_DELAY_SEL (BLOCK0)                             RTC watchdog timeout threshold; in unit of slow cl = 40000 R/W (0b00)
                                                   ock cycle

Although I cut some information out of this output, if you didn’t notice, flash encryption is enabled so sadly, the firmware I dumped is encrypted…

Command:

python -m espefuse --port COM13 --baud 74880 read_flash 0x00000 0x400000 encrypted_dump.bin

However, I did find some interesting configurations which hopefully I’ll be able to make a post about soon.

FYI, turns out I needed to feed the breakout board 5v separately to power it and once I did that I was able to utilize it’s pins to access the ESP8684. So then I had to use a 5v consistent power, a 3.3v for the EN/etc for what kind of a mode I wanted to boot into and then a 3.3v to power the chipset and send TX/RX. Sheesh!

Anyway, hope that helps someone out there.

END TRANSMISSION

Leave a Reply