Skip to content
Workshop: repairs and experiments

Bosch dishwasher board SWS4118: what the firmware does and what use it is (part 3)

Published 12.11.2026
Contents 8
  1. What this board is
  2. How we analysed it
  3. What the firmware consists of
  4. What the firmware switches on in the chip
  5. How the board talks to the panel: the D-Bus
  6. What use this is to us
  7. Next
  8. Article changelog

The third part about the Bosch dishwasher board 702168823.01GA / SWS4118 Rev.4. Part one covers what is on the board and the EEPROM copy, part two how we read the STM32 firmware. This part is about what is in that firmware:

  • what this board is in the dishwasher and who it talks to;
  • what the firmware consists of: a bootloader and the main program;
  • which parts of the microcontroller it switches on, read from the live board, read only;
  • two three-phase inverters, overcurrent protection, temperature sensor tables, a self-test;
  • how the board talks to the panel: the D-Bus;
  • what use all this is to us and what we do next.

draft, a first analysis, still being extended

⚠ Everything here was read without writing anything. The firmware was analysed on a computer from the copy, and the peripheral settings were read from registers while the ST-Link powered the board from USB (3.3 V, no mains).

What this board is

The hn/bsh-home-appliances project describes Bosch dishwasher power modules of the EPG700xx series: the same STM32F301VCT6 microcontroller in a 100-pin package, on some boards the same FSB50550AB inverter, an X20 debug connector. Our board looks like one of that family. (The die itself identifies as an STM32F302/F303, see part two; judging by the blocks the firmware uses, TIM8, six comparators and four op-amps, it is most likely an STM32F303xC: the F302 has no TIM8.)

According to that description, this is the washing control unit (address 1 on the bus): it runs the wash program and drives the heater, pumps and valves. The button panel is a separate node (address 2) that only sends it commands: which program is selected, which options. They talk over the internal D-Bus, described below.

This matters to us: the "run the pump" command does not come from the panel, it is generated inside this firmware while it runs a wash program.

How we analysed it

WhatWithWhat for
the firmware copy (256 KB)the Capstone disassembler (a Python library), for a first lookto see the structure, tables and peripheral accesses
the microcontroller peripheral settingsSTM32_Programmer_CLI with -r32: reading registers on the live boardto find out what the firmware actually switched on
deeper analysis (next)Ghidra: ARM Cortex, little endian, base 0x08000000command handlers, program logic

We read the registers like this (every command only reads; the output is hexadecimal words). GPIO was read for every port with a 0x400 step; one is shown here, GPIOE:

STM32_Programmer_CLI -c port=SWD mode=HOTPLUG freq=100 -r32 0x40021000 0x20
STM32_Programmer_CLI -c port=SWD mode=HOTPLUG freq=100 -r32 0x48001000 0x28
STM32_Programmer_CLI -c port=SWD mode=HOTPLUG freq=100 -r32 0x40012C00 0x50
STM32_Programmer_CLI -c port=SWD mode=HOTPLUG freq=100 -r32 0x40013400 0x50
STM32_Programmer_CLI -c port=SWD mode=HOTPLUG freq=100 -r32 0x40013800 0x14
AddressWhat is there
0x40021000RCC: clocks
0x48000000…0x48001400GPIOA…GPIOF: the mode of every pin (step 0x400)
0x40012C00, 0x40013400timers TIM1 and TIM8: the inverters' PWM
0x40013800USART1: only the configuration registers, up to 0x10
0x4001001C…0x40010044comparators and op-amps
0x40007400DAC

⚠ We do not read the serial ports' data registers (from 0x24 in USART1): reading them clears flags and can lose a byte the board is receiving at that moment.

What the firmware consists of

Flash areaSizeWhat is there
0x08000000–0x08000433≈ 1 KBthe bootloader (it takes the first 2 KB page)
0x08000800–≈0x0801E000≈ 118 KBthe main program: interrupt table, code, tables
0x0802C000–0x0803400032 KBdata: settings, including a factory copy of EEPROM block 1
from 0x08038000≈ 200 bytesa little more data
the rest≈ 100 KBempty (FF)

The bootloader

Right at the start sits the string EDS_ST_STM32F3_02_210313, which looks like a build name and date (13 March 2021). What the bootloader does at power-on:

  1. It starts the crystal oscillator and a ×3 frequency multiplier. With an 8 MHz crystal (not yet checked on the board) that is 24 MHz.
  2. It sets up the USART1 serial port on PE0/PE1 (pins 97 and 98): 9600 baud, 8 bits with parity (at 24 MHz a divider of 2500 gives exactly 9600, one more argument for an 8 MHz crystal).
  3. It sends the byte 0x54 and waits ≈ 30 ms for a reply (up to 1 s if the previous start asked for longer).
  4. If there is no reply, it leaves a 55 AA marker in RAM and restarts the chip with the watchdog. The next start sees the marker and hands over straight to the main program.
  5. If there is a reply, it accepts commands:
Command byteWhat it does (from our analysis)
0x0Alink check, answers 0x8A
0x5Breturns the version string (EDS_ST_…, 32 bytes)
0x77sets an address (most likely where to receive data)
0x04changes the port speed
0x1Aruns code from the given address

The bootloader itself does not write to flash: updating apparently works by receiving a flashing program into RAM, which then rewrites the main program. In other words, the firmware can be updated over the same serial port the board uses to talk to the panel (only the format differs: the bootloader uses parity, 8E1, the main program does not, 8N1). We are not doing that, but it explains why the bootloader is there.

The main program

The interrupt table starts at 0x08000800. Apart from the standard ones, only seven handlers are in use, and they show straight away what the program is about:

InterruptMost likely
ADC1_2ADC results ready: phase currents and bus voltage, the core of motor control
TIM1_UPevery PWM period of the first inverter
TIM1_BRK, TIM8_BRKemergency shutdown of the inverters
USART1receiving and sending on the D-Bus
UART5another serial port (see below)
EXTI9_5an event on one of pins 5–9 of some port; which one is still unknown

The program can write to its own flash (it contains the flash unlock sequence), most likely to keep its settings in the 0x0802C000 area.

What the firmware switches on in the chip

This was read from the live board's registers with the program already running (the board on 3.3 V from the ST-Link, no motors connected).

PeripheralWhat we readWhat it means
clockscrystal × 9, APB2 bus undivided, APB1 /2with an 8 MHz crystal that is 72 MHz, this chip's top speed
USART1 (PE0, PE1)divider 7500, 8 bits without parity, interrupt-driven receive9600 baud, 8N1: exactly the D-Bus
TIM1 (PE8–PE13)three complementary output pairs, centre-aligned PWM, period 1562, 2 µs dead timea three-phase inverter, PWM ≈ 23 kHz
TIM8 (PC6–PC8, PC10–PC12)the same, period 3446a second three-phase inverter, PWM ≈ 10.4 kHz
outputs of both timersmain output enable offthe motors are stopped, as they should be without mains
comparators COMP1–COMP6all six on, outputs routed to the timers' break inputsovercurrent protection: three per inverter, one per phase
op-amps OPAMP1–OPAMP4onthey amplify the current-shunt signals for the ADC
DAC, both channels1806 of 4095 (≈ 1.46 V)most likely the comparator threshold
ADC 1–4≈ 25 pins in analogue modecurrents, voltages, temperatures, levels
TIM2 (PA9, PA10), TIM3 (PB5), TIM4 (PD13, PD15)pins in timer modepulse or frequency measurement (sensors) or PWM; not yet known
UART5transmit only, ≈ 900 baud; not routed to any pinpurpose unknown
watchdogsindependent and windowif the program hangs, the chip restarts
CRC unitcomputes a checksum of flash areas while runninga self-test (see below)

Pin numbers in the 100-pin package:

SignalsMicrocontroller pins
TIM1: PE8–PE1339–44
TIM8: PC6, PC7, PC863, 64, 65
TIM8: PC10, PC11, PC1278, 79, 80
USART1 (D-Bus): PE0, PE197, 98

Two inverters, one power module

The firmware is ready to drive two three-phase motors: both timers are set up the same way, each with its own protection. Our board carries one FSB50550AB power module. Either the second motor is driven some other way (on another board or another version of this one), or the firmware is shared between several board versions. Which timer feeds the FSB50550AB we will find with a continuity tester: its inputs should ring through either to pins 39–44 (TIM1) or to 63–65 and 78–80 (TIM8).

In Bosch dishwashers both the circulation pump and the drain pump can be three-phase brushless motors; according to hn, on some boards the FSB50550AB drives the drying fan. What ours drives we will find with the same continuity check and from the connector its outputs go to.

Field-oriented motor control

The program contains a 256-entry sine table (−32767 to 32767). Together with complementary PWM outputs, current measurement through op-amps and the ADC, and protection comparators, that is the usual kit for field-oriented control (FOC) of a brushless motor without position sensors, the same way ST's motor control library is built. The motor turns smoothly and quietly, and the firmware most likely works out the rotor position from the currents.

Temperature sensors

The firmware has two tables that convert a 12-bit ADC reading (0–4095) into degrees. Both run from 0 to 125 °C: these are thermistors (NTC), most likely in the water and on the heater or the power module.

°CTable 1, ADCTable 2, ADC
031473492
1028103244
2024392948
3020622616
4017022268
5013781923
6011011600
708751317
826611029
90548870
100435703
125249416

The hotter it gets, the lower the number: the thermistor is in the lower leg of the divider. Between the points the firmware most likely interpolates linearly. For our water heater this is a ready calibration table: measure the voltage at the ADC input (with the same divider) and you have the temperature. The 82 °C point instead of 80 is as it appears in the firmware table.

Flash self-test

While running, the program uses the hardware CRC unit to checksum areas of its own flash, following a table of ranges. Household appliances commonly do this so that corrupted memory never ends up driving the heater and the motor.

The consequence for us: you cannot simply patch even one byte of the original firmware, because the self-test will notice. So there are two options: the original firmware unchanged, or entirely our own.

How the board talks to the panel: the D-Bus

What hn describes, and what matches our analysis:

Whathn's descriptionIn our firmware
wiresthree: GND, DATA, VBUS; in dishwashers VBUS ≈ 13.5 V, DATA is 5 V logicUSART1 on PE0/PE1; where it goes is still to be found
speed and format9600 baud, 8N1yes: divider 7500 at 72 MHz, no parity
framelength, address and subsystem, command (2 bytes), data, CRC16 (XMODEM)still being analysed
acknowledgementone byte right after the framestill being analysed

Node addresses according to hn: 1 the washing control unit (our board), 2 the control panel, 0x6 the floor time projector, 0xA/0xB internet modules. Known commands: 17.10-10 xx selects wash program xx, 17.10-00 the options, 24.20-06 the rinse aid and salt indicators, 25.20-08 the remaining time.

According to hn, some nodes can return their memory contents straight over the D-Bus. We have not tried that, and no longer need to: we have the copy.

⚠ All of this is described for hn's boards; for ours we still need to record the real exchange between the panel and the board.

What use this is to us

What we learnedWhat it gives us
no protection, copy savedthe board can always be returned to its factory state, so we can experiment freely
a bootloader on the serial portthe firmware can be updated without taking anything apart, over the same three wires
D-Bus: 9600 8N1, known frame formatan ESP32 can listen and send commands without changing the firmware
PWM pins, frequency, dead time, protectioneverything our own firmware needs is already known from the original
NTC tablesa ready temperature conversion for the water heater
CRC self-testpatching the original firmware is a bad idea: either the original unchanged or our own
two inverters in the firmwarewe need to find out which one feeds our FSB50550AB

Our plan now:

  1. First, the original firmware and the D-Bus. We have the panel: we connect the board and the panel, listen to the DATA line with a logic analyser or an ESP32 (hn's project has a ready ESPHome component) and record what the panel sends when we start a program. Then we try sending those commands ourselves. Nothing gets erased.
  2. The catch with this route: the original firmware runs the whole wash program, so it will expect the level and water sensors, valves and the door lock. Without them it will most likely stop with an error. A service test might help; we will look for it in the exchange and in the firmware too.
  3. If the original firmware will not simply run the pump, our own firmware. The hardware is now known down to the pin, and the factory copy sits alongside, so we can go back at any time.

Next

  1. Trace the FSB50550AB inputs: TIM1 (pins 39–44) or TIM8 (63–65, 78–80).
  2. Find which connector PE0/PE1 (pins 97, 98) go to: that is the D-Bus line; measure VBUS on it.
  3. Find the crystal and check it is 8 MHz: all the frequencies above depend on it.
  4. Record the panel ↔ board exchange.
  5. Ghidra: the USART1 handler (0x0800BA3D) and the D-Bus command table.
  6. Find out what is on the TIM2–TIM4 timer pins and on the EXTI9_5 interrupt: flow, level or motor sensors.

🔒 Identifiers and original copies: hidden, only the author can see this.

Article changelog

  • 2026-11-10 — first draft: firmware layout, the bootloader, chip peripherals from the live board, two inverters, NTC tables, the self-test, the D-Bus, what use it is and the plan.