# ESP8266 SPI Flash Dump

Dump the content of common SPI flash chips with an ESP8266.

This requires:

- An ESP8266, or other arduino-compatible MCU with SPI. (more info [here](#using-others-boards)).

- A PC with the Arduino IDE, Python and permission to serial access.

- A compatible flash chip to target. (more info [here](#compatible-flash-chips)).

This was developed and tested on an Arch Linux machine, but should technically work on windows and mac as well.

## Dumping

This process is relatively straightforward. This is assuming you are using an ESP8266.
If you want to use another board, please read [Using other boards](#using-others-boards) and make the necessary adjustments to the following instructions.

### 1: Prepare software

First you need to prepare your ESP, PC and Chip for dumping. Let's start with the simple stuff: software.

First, clone this repo:

`git clone https://git.zervo.org/zervo/ESP8266-Flash-Dump.git`

Navigate into the cloned repository.

Open `spi_dumper.ino`, connect your ESP and adjust the board setttings as needed. Flash the code to the ESP.
This should flash without issues. If you are using another board you might need to change baud rate.
Make sure to adjust the SPI clock speed as needed, different flash chips support different speeds.
Setting it too high might cause issues.

**NOTE:** If compiling fails, you might need to add the SPIMemory library to the IDE.
This is done by going to *Sketch > Include Library > Manage Libraries*, searching for and installing SPIMemory.

Now for the python part.

The script only relies on one library that is not builtin: pyserial. You can install it with `pip3 install pyserial`.

**NOTE:** If your python environment is controlled by your system-wide package manager, create and activate a new venv and then execute the previous command:

`python3 -m venv . && source ./bin/activate`

### 2: Prepare hardware

Now to the slightly more difficult part: preparing the hardware.

Below is a table with the connections that need to be made. If you are using a different board, make sure to match the pin functions to the appropriate pins on your MCU.

| **Function** | **ESP8266**             | **Flash Chip** |
| ------------ | ----------------------- | -------------- |
| *Example*    | *PinId* (*PinFunction*) | *PinId*        |
| MISO         | D6  (HMISO)             | DO             |
| MOSI         | D7  (HMOSI)             | DI             |
| Chip Select  | D2  (GPIO4 *(CS)*)      | CS             |
| Clock        | D5  (HSCLK)             | CLK            |
| 3.3V         | 3V  (3.3V)              | VCC            |
| Ground       | G   (GND)               | VSS / GND      |

This looks quite simple, and connecting it should mostly be relatively simple for the most part.

If your target chip is integrated into a device, you might need to desolder the chip first.
To determine this you would have to probe around with a multimeter, which I will not describe here.

You could be fine connecting directly to the device without desoldering the flash chip,
as most modern well-designed devices should be able to handle voltage supplied to individual components without harming the device, but don't take my word for it.

The chip I'm targeting in this case is a GigaDevice GD25B256D. It is a 32MB flash chip with a common pinout (shown below).
If you have a clamp for this package you can use that instead of soldering to the chip.

Before doing anything, please read the datasheet for your target chip and make sure it follows common opcodes and SFDP standard (more info [here](#compatible-flash-chips)).
Generally, as long as opcode 3 is read you should be fine.

![GD25B256D Pinout](assets/flash_pinout.png)

**NOTE:** Pinout might differ between chips, always refer to the datasheet of your target chip.

In my case, I determined that soldering directly to the target device would be the best course of action.

Below is an image of the wires soldered to the chip and connected to the ESP8266.

**WARNING!!!** BEFORE CONNECTING ANY POWER, double-check your connections. If you are using a board other than ESP8266: MAKE SURE THE MCU LOGIC VOLTAGE MATCHES THAT OF YOUR TARGET CHIP!

![Setup with ESP8266 connected](assets/setup.png)

And with that you can move on to the next step! Just check your connections again to be sure ;D.

### 3: Testing the connection

Before moving on to using the python script to dump the flash, we should test the connection.

Open the serial monitor in the arduino IDE and reset the ESP. Note that the IDE might freeze if you are unlucky.

If it freezes almost instantly, there could be an issue. Or just bad luck.
The program doesn't start reading from the chip until 4 seconds after reset, and doesn't start dumping until another 4.5 seconds have passed.

What you are expecting to see after a reset followed by an immediate clearing of the serial monitor, is first a few lines of flash chip information, and then a bunch of nonsense approximately 4 seconds later.

Working? Good, move on to the next step. Having issues? Make sure the serial monitor is set to the correct baud rate.
Try lowering the SPI clock in the code and reupload.
Try adjusting the baud rate.

### 4: Dumping the flash

So before dumping the flash, let's quickly go through what will happen.

- ESP is connected to PC (by user).

- Python script is started (by user).

- ESP is reset. Light is turned on.

- ESP waits 4 seconds, then light is turned off and ESP attempts to read flash chip info. If successfull, the python script should receive the info and print it out.

- ESP waits another 4 seconds, then starts dumping the flash. You know it worked if the python script starts printing progress updates.

And that is the rough process. You could try doing it off of that description, or read the more detailed instructions below.

There are two blinking sequences the light can have: error, or success.

The error sequence indicates a failure and is a square pulse of 0.4 seconds on, 0.4 seconds off.
The success sequence indicates the end of a successful dump and is an uneven pulse of 0.8 seconds on, 1.5 seconds off.

Now here is the step-by-step guide to dumping a flash.

1. Connect the ESP to the PC. It will try to start dumping the flash on it's own after a few seconds. This is not a problem, don't worry about it.

2. Find the serial port of the ESP. This can be done by listing the devices present under `/dev/tty*`. It usually is /dev/ttyUSB0 or /dev/ttyACM0.

3. Start the python script with the correct arguments. The first argument is the port, which you just found.
The second is the baud rate which is set in the ESP code (default 115200). The third is the path to the output file.
A full command might look like this: `python3 ./flash_dump_capture.py /dev/ttyUSB0 115200 ./flash_dump.bin`.

4. Make sure the python script hasn't started reading anything yet, if it has you should close it and open it again.

5. Reset the ESP. As soon as the builtin LED goes out, the python script should start receiving data.

6. Wait, and hope for a success!

The dumping might take a while, since this solution is pretty slow. This is intentional since I want to keep it as stable as possible (the ESP probably wasn't intended for this).

If the dumping is excruciatingly slow, you can try bumping the SPI clock speed in the ESP code, or increasing the block size on both sides. Not recommended though.

## Compatible flash chips

This flash dumper is very crude and crude, and should be compatible with most common (Winbond / Giga devices / Microchip) SPI flash memory chips.

Compability is mostly dependent on the [SPIMemory](https://github.com/Marzogh/SPIMemory) library (formerly SPIFlash).

It should technically work with any chip that is compliant with the SFDP standard as defined in [JESD216B](https://www.jedec.org/standards-documents/docs/jesd216b).

## Using others boards

This project is designed (aka hastily thrown together) for the ESP8266 NodeMCU V3 development board.

It should technically be compatible with any MCU that meets the following requirements:

- Has the same logic level voltage as the chip you are targeting (IMPORTANT).

- Is compatible with an arduino core.

- Has support for external SPI flash devices.

- Has support for a serial connection.

**"My board meets those requirements, what should I do?"**

You should only have to make a few very basic adjustments, listed below.

Adjust the Chip Select pin. This is defined at the top of the arduino code as FLASH_CS_PIN. Make sure to set it to a pin that isn't occupied, and that doesn't conflict with other functions such as LED or SPI.

Adjust the serial baud rate in the arduino code. Make sure it is set to something that your board can reliably handle.

Does your board have a builtin LED? If not, get rid of those lines in the arduino code or replace it with another pin number and place an external LED on it.

Substitue the pins and wiring mentioned in the tutorials with that of your board. You basically just need to look up the pinout for SPI for your board and use those pins instead of the ones listed for ESP8266.
On some boards you might have to manually specify the SPI pins in the code. You have to do a bit of googling yourself.

And again, MAKE SURE that your board has the same logic voltage levels as the chip you are targeting. This is very very important.

And that should be it, I think :)

## Troubleshooting

Common problems and potential solutions.

### My computer just freezes or I get errors when dumping

Try lowering the SPI clock speed in the ESP code.

Make sure the same baud rate is used everywhere, and that your board can reliably support that baud rate.

Maybe try lowering the baud rate.

Make sure block size is valid and consistent everywhere.

### Python script recevies something, but chip info is blank

The chip information (id, capacity etc) is blank or 0.

This could indicate a read failure (see other problems), or could just be that the chip does not provide this information.

If the latter is true, the read can continue anyway without problems. The only difference is that you can't get a calculated percentage in your status updates.

### READ_ERROR_AT_ADDRESS

Try the solutions mentioned above.

Try adjusting the block size (default 256). This must be changed on both the ESP and in the python script.

Try adjusting delays between reads in the ESP code.

Make sure the flash is connected correctly.

### ABORTED_DUMP_FAILURE

Make sure the flash is connected correctly.

Make sure the SPI clock is not too high, same for baud rate.

Doublecheck that the connections are firm, I had a cable slip out a couple of times..