DEV Community

Alex Root
Alex Root

Posted on

I Bought a Robot Vacuum, Not a Cloud Spy

Background: Buying a Used Robot

I didn’t buy this robot vacuum because I urgently needed automated cleaning.
I bought it used — on purpose.
Buying a brand-new device comes with invisible obligations:

  • warranty
  • the mental barrier of “don’t touch it, you might break something”
  • the expectation that it must “work off its price” before you can experiment.

A new device feels like something you’re borrowing from the manufacturer.
A used device is different.
When hardware is already second-hand, already “imperfect”, you’re free.
You’re not breaking a product — you’re giving it a second life.
This mindset matters a lot for engineering projects.

Why This Model

The robot is a Xiaomi Robot Mop Essential.
On the local market inside my country, I paid $25, including delivery.
A new one costs around $125.
For $25 I got:
a fully functional robotic platform
motors, sensors, MCU, Wi-Fi
something I could open without guilt
For $125 I would have gotten the same hardware… plus obligations.
The choice was obvious.

Expectations vs Reality

To be clear: this robot does its job well.
It drives. It cleans. It creates internal maps and attempts to send them to the cloud.
It does randomly crash into walls — literally.
The robot builds its maps by:

  • bumping into obstacles with its bumper
  • calculating position from wheel encoders
  • using basic orientation and motion sensors
  • No LiDAR. No magic. Just contact, math, and retries.

And it works surprisingly well.
The problem was never cleaning quality.
The problem was control.

Mechanical Issues and Repairs

This project didn’t start as reverse engineering.
It started with very practical, very physical problems.

  1. The Wheel Problem

The robot was used, and it showed it.
One of the metal bushings had literally destroyed the plastic seat in the wheel.
Instead of a clean fit, the bushing had “eaten” its way through the plastic, leaving a loose, unusable connection.

My fix was simple but effective:

  • Lubricate the metal bushing.
  • Fill the damaged plastic seat with a small tube of cyanoacrylate glue (superglue).
  • Wait about 2 hours for full curing.
  • Remove the bushing and reinstall. Result: firm fit, no wobble, wheel restored. This was my first signal: this thing is repairable.
  1. Wet Surface Traction (Unsolved)

One mechanical issue still remains: traction on wet surfaces.
The robot sometimes slips during mopping.

Possible reasons:

-insufficient downforce
-poor contact area
-unsuitable wheel tread design

I’m experimenting with:
-different TPU filaments
-various 3D-printed tread patterns

Goal: reliable traction without overloading motors.
This problem is open, and that’s fine — some solutions require iteration.

  1. Wet Cleaning System

The water nozzles were clogged.
Fix: a thin needle normally used for cleaning 3D-printer nozzles.

While I was there, I also repaired the small water pump feeding the mop cloth.
No replacement parts, no manuals — just understanding the system.

From Buttons to Control: Where the Real Problem Started

After fixing the mechanical issues, the robot worked… almost.
To start cleaning, I still had to:

  • walk up to it
  • press a physical button
  • watch it desperately try to connect to Wi-Fi
  • and eventually give up

Factory resets didn’t help.
Creating a fresh Mi Home account didn’t help.
Different phones didn’t help.

The robot wanted Wi-Fi, but it never fully joined.
It built internal maps, sent data to the cloud, and occasionally learned your walls by crashing into them — literally.
At some point, I realized something absurd:
-The robot was Wi-Fi enabled, cloud-dependent — and completely unusable without the cloud.
-That was the breaking point.
-I didn’t buy a robot assistant to turn it into a cloud-controlled spy.
I wanted control, not stupid permissions.
So I did what any curious engineer would do: I opened it.

Inside the Xiaomi Robot Mop Essential

The robot has a very clear internal hierarchy: two brains.
The Main MCU — The Real Robot
The MCU is the heart of the machine. It’s responsible for everything that actually matters:

  • Motor control
  • Wheel encoders
  • Bump sensors
  • gyroscope and orientation
  • navigation logic
  • map building

Based on available technical data and analysis of similar Xiaomi robots, this MCU typically features:

  • a 32-bit RISC core for control logic
  • a dedicated DSP core for real-time sensor processing
  • hardware blocks optimized for navigation and SLAM-like calculations
  • external Flash memory for firmware and maps

The MCU doesn’t care about Wi-Fi. It doesn’t know what a cloud is.
It just wants to clean floors.
The ESP32-WROOM — The Permission Relay

The ESP32 module’s job is limited but critical:

  • Wi-Fi connectivity
  • communication with Xiaomi cloud
  • OTA updates
  • relaying commands and permissions

It does not drive motors.
It does not decide where the robot goes.
It mostly acts as a translator and gatekeeper between the cloud and the real robot.

Listening Instead of Guessing: UART Sniffing
(No photos of how I connected the UARTs from the Orange Pi in MITM mode — I didn’t think they’d be useful at the time.)
To fully understand the system, I connected an Orange Pi RV2 as a man-in-the-middle.
Wiring scheme:
UART5 RX and UART5 TX → MCU
UART7 TX and UART7 RX → ESP32

Why not just an ESP32?

Because I needed three UART ports simultaneously:

  • MCU → ESP32 communication
  • ESP32 → MCU communication
  • MCU diagnostic port (which I connected later, after freeing UART5 while keeping UART7 dedicated to command transmission)

The Orange Pi quietly sniffed all traffic:

  • no injections
  • no modifications
  • just observation

I read both the diagnostic UART and the official ESP32 UART simultaneously in a single terminal window.
This let me see exactly what happened when I sent commands, and compare the robot’s reactions to the actual status updates.

I noticed:
The official UART (ESP32 ↔ MCU) reports only two types of errors:
-Incorrect command (command doesn’t apply to this MCU)

  • Wrong command The diagnostic UART shows much more: motor currents, encoder values, gyro readings, and internal flags. Example diagnostic output when starting cleaning:
Oc[3]=410  Ic[3]=-43  k p=101  
Oc[3]=363  Ic[3]=-58  
p offsg 213  
gyrod=10029  
sg 181  
crc=15fb  
STL start clean  
default map enable:1
Enter fullscreen mode Exit fullscreen mode

Commands sent to the MCU via official UART produce status codes (ok, error…) numerically.
The diagnostic UART shows the robot’s internal response in real time.
What Actually Happens During Startup
Here’s a simplified version of the handshake:

MCU -> ESP32 : platform_name
ESP32 -> MCU : ok
MCU -> ESP32 : firmware_id
ESP32 -> MCU : ok
MCU -> ESP32 : mac_device ?
ESP32 -> MCU : <device_id>
(success handshake)
MCU -> ESP32 : “Can I request OTA updates?”
ESP32 -> MCU : ok
ESP32 can now send commands and listen to response from MCU.
Normal operation begins.
No cryptography. No authentication at this layer. No magic.

This alone tells you everything:
The MCU asks for permission.
The ESP32 simply relays answers from the cloud.
The robot doesn’t talk to the internet - it asks if it’s allowed to.
Replacing the Cloud with My Own ESP32

Once it became clear that the MCU was merely asking for permission from the ESP32, the solution was obvious:
If the original ESP32 only replies “ok” to the MCU… then I can reply “ok” too.
Instead of the original chain:
MCU <-> Xiaomi Cloud <-> App
I wanted:
MCU <-> My ESP32 <-> Optional UI / Terminal / Custom Features
Same expectations. Same protocol.
No cloud. No spying.
The factory ESP32 was removed.
My own ESP32 took its place.
Not to fully emulate the cloud — but to get it out of the way.
Now, the MCU can clean floors, build maps, and respond to commands without ever contacting the internet.

Minimal ESP32 Firmware Architecture.
The firmware I built is modular, with clear responsibilities:
Core system (critical for multitasking and reliability):
dispatcher — task manager
scheduler — time and event scheduling
multicore — leveraging both cores of the ESP32
Hardware & peripherals:
uart — read both diagnostic and official MCU ports simultaneously
ext_flash — work with external SPI flash
display — optional screen for status
buttons & leds — physical interface
Communication & protocols:
miio_miot — simplified implementation of Xiaomi MIOT protocol, enough to relay commands and monitor state
http — web interface for control
auth — authentication for local UI
wifi_mgr — optional Wi-Fi management
Utility & support:

terminal — serial terminal for debugging

logger — log all UART traffic and events
sysinfo — system diagnostics
time_sync — keep time in sync
storage — configuration and map persistence
ota — optional firmware updates(ESP32)

Dual UART Monitoring
One of the key features: reading two UARTs simultaneously.
Official MCU <-> ESP32 UART: status, commands, simple errors
Diagnostic UART: detailed motor currents, encoder readings, gyro data, internal flags
This allowed me to:
verify that commands were correctly interpreted
see detailed internal responses
log all activity for debugging and development
The result is a full local control system, where nothing is hidden behind the cloud.

Current Status

At the moment, the ESP32 firmware is still under development. Most of the core system is functional — about 80% complete.

The modules are in place, dual UART monitoring works, and the dispatcher, scheduler, and multicore systems are running. Basic MIOT-like protocol handling (JSON API) is already implemented.

Some modules, like OTA, are already integrated, which will make future modifications — including connecting the display and enhancing the interface — much easier to implement.

Once finished, the robot will have full local control, reliable telemetry, and optional display feedback — all without ever needing the cloud.

Physical Mods: Replacing Buttons with a “Head”
Original Button Modul
Original button modul

My parallel connection to button / uart to esp32 and diagnostic port

Temporary installation of buttons and LEDs

Once the software and UART control were in place, the next step was physical accessibility.
My NodeMCU board and button cluster couldn’t fit comfortably in the original housing if I wanted to add a display and retain easy access to the interface.
The solution: create a small “tower” or “head” on top of the robot.
What I did
-Relocated the original button block to the new head
-Kept parallel functionality: the robot still responds to factory buttons
-Replaced the original 8 factory LEDs with 2 custom RGB LEDs for clear status indication
Integrated a small display for additional information ( planned for future update)

This physical modification achieves several goals:

  • Makes interacting with the robot intuitive even without an app
  • Keeps visual status indicators simple and visible
  • Allows me to retain full MCU control while optionally using the original buttons

Think of it as a “command tower” — a small cockpit for my robot that gives it a personality and a clear interface.

What I’d Change if Designing This Robot

If I were designing the Xiaomi Robot Mop Essential from scratch, here’s what I would do:
-Provide full local control out of the box, without requiring cloud connection

  • Make cloud telemetry optional and clearly permission-based
  • Expose an official UART or API for advanced users
  • Allow easy replacement or expansion of LEDs and buttons for custom interfaces
  • Include a modular design for attaching displays or additional sensors

This would let engineers and hobbyists:

  • Understand what the robot is doing
  • Customize behavior safely

Avoid turning a simple cleaning robot into an unintended cloud spy.😁

Top comments (0)