Bosch dishwasher board SWS4118: copying the STM32 firmware with an ST-Link (part 2)
The second part about the Bosch dishwasher board 702168823.01GA / SWS4118 Rev.4. Part one covers what is on the board and the EEPROM copy. This part is about the STM32 microcontroller, read only:
- where the SWD pins are and what the X20 pads carry;
- how to connect an ST-Link V2 (and, in a collapsed block, a Raspberry Pi);
- which software and drivers you need and where to get them;
- how to check whether the firmware is read-protected;
- how to make a copy and how to make sure it is correct;
- what we read from our board.
What the firmware itself can do and what use it is to us is in part three.
draft, still being discussed and extended
⚠ The board runs straight off 230 V mains with no isolation. While a programmer is connected, the board stays off the mains: it gets only 3.3 V, from the ST-Link or a separate supply. Otherwise mains voltage ends up on the computer's USB.
The STM32 microcontroller, read only
The aim of this step is to find out exactly which chip this is, whether the firmware is protected against reading, and if not, to save a copy. Nothing is written or erased. The CH341B programmer will not help here: an STM32 is read over SWD, which the CH341 does not have. We read it with an ST-Link V2 (a cheap clone); how to do the same with a Raspberry Pi 3B+ is in the collapsible block below.
Where to find the pins
| Pin | Signal | What for | Needed for reading |
|---|---|---|---|
| 72 | PA13, SWDIO | debugger data | yes |
| 76 | PA14, SWCLK | debugger clock | yes |
| 14 | NRST | reset | advisable: helps if the link will not come up |
| 99, 74 | VSS | GND | yes (any GND point on the board) |
| 100, 75 | VDD | 3.3 V | board power |
| 94 | BOOT0 | boot mode select | no, leave it alone |
| 68, 69 | PA9, PA10 | USART1: ST's built-in bootloader | no (a fallback route: needs BOOT0 = 1, and read protection applies here too) |
- The pin numbers are ST's standard 100-pin layout (as on the STM32F303VC). They have not yet been checked against a datasheet for a chip marked
STM32F301 VCT6, so first check the orientation with a multimeter: pins 99 and 74 should ring through to GND (for example, to EEPROM pin 4), and 100 and 75 to the 3.3 V rail (EEPROM pin 8). If they match, the numbers are right. - Do not solder to the pins: at 0.5 mm pitch it is easy to bridge neighbours.
What the X20 pads carry (D in the microcontroller photo; counted left to right as in the photo, along the bottom of the board), traced with a continuity tester:
| X20 pad | Goes to | What it is |
|---|---|---|
| 1 | pin 76 | SWCLK (PA14) |
| 2 | pin 72 | SWDIO (PA13) |
| 3 | pin 14 | NRST (reset), not direct but through a 220 Ω resistor (marked 221) |
| 4 | pin 90 | PB4 (NJTRST in JTAG); the firmware keeps it as an input, and on the live board it reads logic 1 |
| 5 | GND | common ground |
| 6 | 3.3 V | the 3.3 V supply rail |
So X20 is a debug connector: SWD and reset are already on it, and the X20 pads are a convenient place for probes or soldered wires.
221 (220 Ω) → pin 14, NRST, on the bottom side of the package. Green: pad 4 → pin 90 on the left side of the package.- The 220 Ω resistor in the reset line does no harm: the ST-Link pulls NRST to ground, and 220 Ω only limits the current, most likely in case the pad is shorted to the supply by accident.
- Pad 4 (PB4) is not needed for reading. The firmware sets it up as an input without an internal pull resistor, and on the live board it reads 1, so there is probably an external pull-up. It may be a factory service-mode input; what it does has not yet been found in the firmware. Do not short it to ground on a working board until we know what it is.
Checked against an open description. The hn/bsh-home-appliances project describes Bosch dishwasher power modules with the same STM32F301VCT6 and gives this X20 order: SWCLK, SWDIO, BOOT0, RESET, GND, 3.3 V. The first two and the last two pads match what we traced. Pads 3 and 4 are different on our board: 3 is NRST (pin 14), 4 is PB4 (pin 90), and BOOT0 is not brought out to X20. So either hn's boards are a different revision or that description has the order mixed up: trust the continuity check on your own board.
Connecting an ST-Link V2
| ST-Link V2 | X20 pad | On the board |
|---|---|---|
| SWCLK | 1 | pin 76 (PA14) |
| SWDIO | 2 | pin 72 (PA13) |
| GND | 5 | GND |
| 3.3 V | 6 | 3.3 V rail |
| RST | 3 | NRST, pin 14 (through 220 Ω), if needed |
- Power. On cheap ST-Link clones the 3.3 V pin is an output: it powers the board, so no separate supply is needed, and none should be connected: two sources on one rail fight each other. On a genuine ST-Link this pin only senses the voltage: then a separate 3.3 V supply powers the board, and pad 6 is wired to the ST-Link's 3.3 V pin for sensing. Once connected, measure the voltage on pad 6: it should be ≈ 3.3 V. If a clone gives less than about 3 V, its regulator cannot carry the board: disconnect its 3.3 V pin and power the board from a separate supply (shared GND).
- Keep the board off the mains the whole time. It is not galvanically isolated from the mains: plugging it into a socket with the ST-Link connected puts mains voltage on the computer's USB.
- Keep the wires short (15–20 cm at most).
- Software: ST's STM32CubeProgrammer (Windows, Linux, macOS) or OpenOCD. On Windows, the ST-Link driver is installed together with STM32CubeProgrammer.
How to connect a Raspberry Pi 3B+ instead of an ST-Link
| Raspberry Pi 3B+ pin | Pi signal | On the board |
|---|---|---|
| 22 | GPIO25 | SWCLK, X20 pad 1, pin 76 |
| 18 | GPIO24 | SWDIO, X20 pad 2, pin 72 |
| 20 | GND | GND, X20 pad 5 |
| 12 | GPIO18 | NRST, X20 pad 3, pin 14 |
Power the board with 3.3 V from a separate supply (X20 pad 6), with GND shared with the Pi: better not to load the Pi's own 3.3 V pin with the board. The software is OpenOCD:
sudo apt install openocd telnet
sudo openocd -f interface/raspberrypi2-native.cfg -c "transport select swd" -f target/stm32f3x.cfg -c "adapter speed 100"
If OpenOCD reports that raspberrypi2-native.cfg does not exist, your version has merged it into interface/raspberrypi-native.cfg. After that, use the same commands in telnet localhost 4444 as for the ST-Link below.
Software and drivers
| What | Where to get it | What for |
|---|---|---|
| STM32CubeProgrammer | st.com: STM32CubeProg, free; downloading needs an ST account | reading the flash and option bytes; it has both a window and a command-line STM32_Programmer_CLI |
| ST-Link driver for Windows | installed together with STM32CubeProgrammer; separately as STSW-LINK009 | so that Windows can see the ST-Link |
| OpenOCD | Linux: sudo apt install openocd; Windows: xPack OpenOCD builds | a free alternative; works with both an ST-Link and a Raspberry Pi |
| Ghidra | ghidra-sre.org | analysing the firmware copy on a computer |
On Windows the command-line program ends up here after installation:
C:\Program Files\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\STM32_Programmer_CLI.exe
Below we simply write STM32_Programmer_CLI: either add the bin folder to PATH or run the commands from it.
The steps in short:
- The board stays off the mains. Wire the ST-Link to X20 as in the diagram above, then plug it into USB.
- Check that the computer sees the ST-Link:
STM32_Programmer_CLI -l st. It lists the ST-Link, its firmware version and its serial number (if you publish the output, better remove the number). - Check the protection:
STM32_Programmer_CLI -c port=SWD mode=HOTPLUG freq=100 -ob displ. AnRDPline reading0xAA (Level 0, no protection)means there is no protection and the firmware can be read. The other values are explained in the next section. - Read the firmware twice and compare the copies: see If there is no protection: a copy.
Can the firmware be saved?
An STM32 has read protection, the RDP level (0, 1 or 2), and whether the firmware can be read depends on it.
STM32CubeProgrammer (the easiest way). Choose ST-LINK, connection mode "Normal" (or "Hot plug", to connect without resetting the microcontroller), and press Connect. The software shows the chip name and flash size, and the OB (Option bytes) tab shows RDP: AA is level 0, CC is level 2, BB or anything else is level 1. Do not change anything on the OB tab and do not press Apply. If the software offers to update the ST-Link's own firmware, decline: clones sometimes stop working after such an update.
OpenOCD (Windows, Linux):
openocd -f interface/stlink.cfg -f target/stm32f3x.cfg -c "adapter speed 100"
Once OpenOCD has found the microcontroller, in a second window:
telnet localhost 4444
and there, one at a time (each command only reads):
mdw 0xE0042000
mdh 0x1FFFF7CC
mdw 0x4002201C
mdb 0x1FFFF800 16
| Command | What it shows | How to read it |
|---|---|---|
mdw 0xE0042000 | the device ID | the last three hex digits: 422 is the F302/F303 family with 128–256 KB; 439 is F301/F302 with 32–64 KB; 446 is F302/F303 with 384–512 KB |
mdh 0x1FFFF7CC | flash size in KB | 0x0100 = 256 KB, 0x0040 = 64 KB |
mdw 0x4002201C | the FLASH_OBR register | bits 2–1: 00 is level 0, 01 is level 1 (a last hex digit of 0 or 1 means level 0; 2 or 3 means level 1) |
mdb 0x1FFFF800 16 | option bytes, the chip settings | a first byte of aa means level 0 |
| Result | Level | What it means | What we do |
|---|---|---|---|
bits 2–1 = 00, first option byte aa | 0 | no protection | save the whole flash (below) |
bits 2–1 = 01, first byte something else (not aa) | 1 | the flash cannot be read over SWD | the firmware cannot be copied. Protection can only be removed together with erasing all the firmware, and we do not do that |
| the software cannot see the microcontroller although the wiring has been checked | possibly 2 | debugging is disabled for good | the firmware can be neither read nor replaced; first check the wires, power and NRST again |
If there is no protection: a copy
In STM32CubeProgrammer: the "Memory & File editing" tab (the pencil icon), address 0x08000000, size 0x40000 (256 KB; if the software showed a different flash size after Connect, use that), Read, then Save As… to a file. Do it twice and compare the files.
From the STM32CubeProgrammer command line (this is how we read it; every command only reads):
STM32_Programmer_CLI -c port=SWD mode=HOTPLUG freq=100 -ob displ
STM32_Programmer_CLI -c port=SWD mode=HOTPLUG freq=100 -u 0x08000000 0x40000 flash_1.bin
STM32_Programmer_CLI -c port=SWD mode=HOTPLUG freq=100 -u 0x08000000 0x40000 flash_2.bin
STM32_Programmer_CLI -c port=SWD mode=HOTPLUG freq=100 -u 0x1FFFF800 0x10 optbytes.bin
mode=HOTPLUGconnects without resetting the microcontroller: its program keeps running.freq=100means 100 kHz: slow (one 256 KB copy takes ≈ 33 s), but reliable even with probe wires.-u(upload) means reading from the microcontroller into a file, confusing as the name is. Writing to the microcontroller is done by-dand-w, erasing by-e; we do not use those here.- To compare the copies: on Windows
fc /b flash_1.bin flash_2.bin("FC: no differences encountered" means identical), on Linuxcmp flash_1.bin flash_2.bin(no output means identical). Or compare checksums:certutil -hashfile flash_1.bin SHA256on Windows,sha256sum flash_*.binon Linux.
In OpenOCD, in the same telnet window:
halt
dump_image flash_1.bin 0x08000000 0x40000
dump_image flash_2.bin 0x08000000 0x40000
dump_image optbytes.bin 0x1FFFF800 16
resume
0x40000is 256 KB. Ifmdh 0x1FFFF7CCshowed a different size, use that instead: the size in KB × 1024, in hex.haltonly stops the program running andresumecontinues it; nothing is written.- The files appear in the folder OpenOCD was started from. The two copies must be identical (
cmp flash_1.bin flash_2.bin) and must not be allFFor all00.
What else to save
| What | Where | Why | In the article |
|---|---|---|---|
| the firmware (flash) | 0x08000000, 256 KB | the master copy, so we can always go back | no: it is the manufacturer's program |
| option bytes | 0x1FFFF800, 16 bytes | the protection level and chip settings | yes, the values and what they mean |
| device ID, flash size | 0xE0042000, 0x1FFFF7CC | the exact model | yes |
| the chip's unique ID | 0x1FFFF7AC, 12 bytes | identifies this particular chip | no, it is private |
| the 24C08WP EEPROM | part one | settings and counters | by the part one rule |
| ST's built-in bootloader | system memory | the same on every such chip, no need to save it | — |
What you can do without breaking anything
| Allowed (read only) | Why |
|---|---|
| read the device ID, flash size and option bytes | to know what the chip is and how it is configured |
| read the flash and RAM | a copy of the firmware; the state of variables while running |
| read peripheral registers (GPIO, timers, UART settings; not the UART and SPI data registers, as reading them clears their flags) | to learn which pins drive the inverter PWM and which UART goes to the panel |
halt / resume | stop the program to look at its state, then carry on |
analyse the firmware copy on a computer (Ghidra: ARM Cortex-M, base 0x08000000) | to find how the board receives the "run the pump" command and the PWM settings |
| Not allowed: it breaks something, or breaks it for good | What happens |
|---|---|
removing level 1 protection (stm32f1x unlock, setting RDP to 0) | all the firmware is erased |
| setting level 2 | debugging is disabled for good; there is no way back |
flash erase…, flash write…, program, "Erase", "Program", "Download" | the firmware is erased or replaced |
changing the option bytes (stm32f1x options_write) | can turn on protection or change how the chip behaves |
| connecting the board to the mains while a Pi or programmer is connected | mains voltage on the Pi and the computer |
The rule for this step: every command here only reads. If any software offers to erase, write or "unlock" anything, the answer is no.
What we read
We read it with an ST-Link V2 clone connected to the X20 pads: pad 1 SWCLK, pad 2 SWDIO, pad 5 GND, pad 6 3.3 V. The ST-Link itself powered the board. Pads 3 (NRST) and 4 were left unconnected: no reset was needed. The commands are the ones in the copy section, run from the STM32CubeProgrammer v2.23 command line.
| What | Result |
|---|---|
| board voltage (from the ST-Link) | 3.26 V |
| device ID | 0x422, revision Y: the STM32F302xB/xC and F303xB/xC family, Cortex-M4 core |
| flash | 256 KB (0x1FFFF7CC = 0x0100) |
| read protection (RDP) | 0xAA: level 0, no protection |
| write protection | off on all areas |
| option bytes | aa 55 ff 00 ff 00 ff 00 ff 00 ff 00 ff 00 ff 00: factory values |
| firmware copy | saved: two 256 KB copies, byte-for-byte identical; ≈ 33 s each |
| option bytes copy | saved, 16 bytes |
The marking does not match the die. The package says STM32F301 VCT6, but the die identifies as an STM32F302/F303 with 256 KB (the F301 line has ID 0x439 and at most 64 KB). The chip looks like it was marked to order for the board maker. For working with it we use the STM32F302VC / F303VC documentation. Judging by the blocks the firmware uses (the TIM8 timer, six comparators, four op-amps), it is most likely an STM32F303xC; see part three. The pinout agrees: SWD really is on pins 72 and 76, as on the F303VC.
What we saved besides the firmware: the option bytes (16 bytes), the device ID and flash size, and the die's unique number (0x1FFFF7AC, 12 bytes). The unique number stays in private notes only: it identifies this one die. It can be read like this:
STM32_Programmer_CLI -c port=SWD mode=HOTPLUG freq=100 -r32 0x1FFFF7AC 12
We do not publish the firmware itself: it is the manufacturer's program. What is inside it is in part three.
Next
- Now that we have a copy, we can safely take the firmware apart. That is part three: what it consists of, which parts of the chip are in use, how the board talks to the panel, and what use all this is to us.
- Pads 3 and 4 of X20 have been traced (NRST and PB4): see the table above.
- The EEPROM and the rest of the board are in part one.
Safety
- Do not plug the board into the mains while an ST-Link or Raspberry Pi is connected. The board is not isolated from the mains: mains voltage would end up on the computer's USB.
- The bus capacitor holds ~325 V for anything from a few seconds to a few minutes after switch-off. Before touching a board that has been on the mains, measure the voltage across it.
- Read only. If any software offers to erase, write or "unlock" anything, the answer is no.
Article changelog
- 2026-11-10 — first draft: split off from part one (step 2, the STM32); added software and drivers, the steps in short, command-line commands, the results from our board, and a check of X20 against an open description.
- 2026-11-10 — X20 pads 3 and 4 traced: 3 is NRST through 220 Ω, 4 is pin 90 (PB4); a tracing photo; the ST-Link and Raspberry Pi diagrams updated.