Eufy RoboVac error 3 is the drop sensors on a 35C, not the side brush the error table says
This worked, and here is why.
The problem
A Eufy RoboVac 35C (T2117, Tuya MCU + TYWE1S running ESPHome) had been opened up and reassembled. Afterwards it refused to clean: on Play it reversed off the dock, then stopped within about a second and beeped. Starting it from the robot's button and from ESPHome over Wi-Fi gave the identical result.
The ESPHome log showed the main board aborting on its own, with the error on datapoint 106:
[D][tuya]: Datapoint 15 update to 0 # cleaning
[D][tuya]: Datapoint 15 update to 1 # paused, 0.9 s later
[D][tuya]: Datapoint 106 update to 3 # error 3
The widely used error table for Tuya-based Eufy robots (the CodeFoodPixels/robovac Home Assistant integration, errors.py) maps 3 → "Side brush", and Eufy's own numbered list for newer models agrees. That sent the diagnosis the wrong way: brushes cleaned, shafts checked, motor connectors reseated — and it still stopped with both side brushes removed. A 4-pin PC-TEST header on the main board looked like a debug UART but stayed silent on both signal pins at every baud from 9600 to 921600, through power cycles and starts.
The fix
On the 35C, datapoint 106 = 3 is the drop (cliff) sensor error. The front IR drop sensors had shifted during reassembly; re-seating/aligning them fixed it immediately.
What actually identified it was the robot's own signal, not a code table: 3 beeps, repeated, with a flashing red light. For Eufy's bump-and-turn models (11S/15C/30C/35C) the beep + light pattern is the documented diagnosis:
| Beeps | Light | Fault |
|---|---|---|
| 1 | solid | wheel stuck |
| 2 | flashing | front bumper |
| 2 | solid | side brush |
| 3 | flashing | drop sensors |
| 3 | solid | suction fan |
| 4 | flashing | low battery |
| 4 | solid | rolling brush |
The lessons that generalise:
- A vendor error table is per model family, not per vendor. The Tuya enum on dp106 is a small integer the main board picks; a table compiled across many Eufy models can be wrong for yours. Map codes from your own device's evidence and write the confirmed mapping into the config, with the date and how it was confirmed:
- sensor_datapoint: 106
datapoint_type: enum
then:
- text_sensor.template.publish:
id: text_error
state: !lambda |-
switch (x) {
case 0: return "";
case 3: return "Drop sensors"; // confirmed: 3 beeps + flashing red
default: return "Error " + to_string(x);
}
Do the cheap discriminating test before the expensive one. "Fails within 1 s even with the side brushes removed" ruled out the side brush outright; the beep count named the real fault. A silent factory-test header was a dead end — don't send unknown bytes to a main board's test port to make it talk.
Separate firmware from hardware by the command path. The same failure from the physical button and from Wi-Fi, with no stop command in the ESPHome log, proves the main board is aborting on its own — the Wi-Fi firmware is not involved.
After a reassembly, suspect what was moved. Drop sensors are small IR LED + receiver pairs that read reflected light; a few millimetres of misalignment reads as "no floor". A phone's front camera shows whether each IR LED is lit, which separates an unpowered sensor (cable) from a misaligned or blocked one.