I salvaged 3 robot mowers from the junkyard. Two of them were Bosch Indego, first generation (something like Bosch Indego 1200).

This is my first time touching a robot mower and I have no idea how it works. A quick search for “Bosch Indego hack” returned a page from a blog named Work-is-Playing which contains a lot of helpful technical data. I suggest that you go through this article first to get familiar with how it works.

The first obstacle I encountered was that both robots were protected by a PIN code. This is supposed to prevent thieves from using the robot (and discourage stealing). But here it prevents me to reuse a perfectly fine piece of equipment and contributes to generating e-waste.

The PIN code contains 4 digits (0 to 9). After 3 wrong attempts, the robot locks itself with a message saying “Contact aftersales service” and needs to be reset. The only way to have the PIN reset on this model is to send both the base station and the robot to Bosch. You probably need to bring a proof of sale and might even pay a fee.

Instead, let’s hook up the Indego to our laptop and use a USB webcam in order to brute force the PIN code.

You can find the python code on my Github repository.

Preliminary research

A quick search returned denperss11’s repository and his YouTube video.

So someone did it already! That’s great. He used a Raspberry pi, a webcam and a python script to program the GPIOs and control the Indego buttons.

Let’s try it! But ditch the Raspberry first. I’ll use an USB FTDI dongle from my laptop instead.

Setting up the testbench

Note

The Indego screen flex connector is very fragile. It is also very short and you will tug on it several times. Be very gentle while disassembling. If it breaks inside the COG LCD screen, it will be impossible to repair. A replacement screen cannot be found new and is expensive on the used market. Guess how I know.

I had a small FTDI USB dongle with a FT232RL chip lying around, so I used that. But any FTDI-based dongle will work. Only the code will need to be adapted to its specific pinout.

alt
The FTDI with soldered wires

Generate button inputs from the FTDI

The buttons on the Indego are mechanical switches that are opened by default. Each button line has a pull-up to 3.3V. When a button is pressed, the corresponding line is pulled to ground.

alt
Button schematic. Source: https://www.roboter-forum.com/threads/bosch-indego-folientaster-defekt.17277/post-191052

I used three GPIOs from the FT232RL. One for the up arrow button, another for the right arrow and the last one for the enter button. By default, the python script sets them high, so 3.3V. When we want to generate a button press, we briefly toggle the line low. Having the FTDI actively pulling the lines high by default is not the cleanest: there might be a small current loop between the FTDI and the Indego. But that current will be negligible. Ideally, we would set the GPIOs to float (“open drain”), but it is not possible on the FT232RL and I couldn’t be bothered to put an extra transistor for each line.

alt
I used a regular 2.54 female header that I plugged into the connector

alt
The FTDI controls a relay that switches the main board on and off

alt
The entire setup

alt
The script in action with the camera pointing at the screen

Bruteforcing the PIN

We must be able to parse the webcam images so that we know which screen we’re dealing with and when our PIN attempt succeeds. The original script uses an OCR library (tesseract). I’ve found that OCR yielded poor results with the Indego screen. The font is likely too thin (only a few pixels wide). As a workaround, he attempted to find only small 3-letter portions of strings. He probably had a few false-positives as well judging that he implemented some restart logic (the script remembers the last PIN that was tried).

After battling with OCR until 2am, I had to give up and go to bed. I brainstormed a bit and decided to follow another approach. How about I capture a picture of each screen and compare it with the webcam output? A python module called imagehash does just that. Unlike a cryptographic hash function, the image diffing algorithm returns a hash value that differs only a little when the image changes a little. This worked really well! Note that in order to have a big-enough hash difference when the PIN succeeds, we need to crop the images to contain only the text (the “ROI” variable in the script.

The original author used a sorted list of PINs. It tooks only around a day for one PIN to be cracked. I also tried all the 9999 pins in only 3 days. In my cases, the pins of each mower were 2009 and 1974.

Going further

Assuming the PIN code is stored in flash, the MCU must be able to retrieve it. With that in mind, I tried capturing the SPI bus at two different times. Once when the lock screen is shown and a PIN is tried, the other at boot. Unfortunately, it was not immediately obvious which transaction contained the PIN code. A lot of communication is happening on the bus (for example, the SPI flash probably contains the entire map of the garden).

Now that I have the PIN, I could perform two captures: one with one PIN code (e.g. 1337), a second one with another PIN code (e.g. 9876) and do a diff. That way, I could hopefully find the address of the PIN in flash. Knowing the location of the PIN in flash would be an easier method than brute forcing it with a camera. But I will never come back to this ;). Let me know if you do it.