Hand tools and fabrication machines
Christmas, 2017—"last Christmas", at the time I'm writing this—my father-in-law bought the one thing my wife still wanted from the time we got married: a Roomba. He bought a Roomba 805, to be exact—the current "Costco model". While I told him it was the best way to "one up" my Christmas game, he had one lament, "Well, I hope you have some fun with it, too."
How little did we know then how much "fun" I might have.
By now, this Roomba is remotely triggered, working collaboratively with a pair of DIY "virtual walls" to—as accurately as a Roomba can—clean exactly half of our house at a time. It took 3 Photons, a lot of network programming, and a veritable metric ton of R&D.
If you want the TL;DR, jump to Act 3, where I talk about the latest version.
It's a family affair: my wife excitedly liberates the Roomba from its carton, our kids watching with a mixture of anticipation (they've never seen a "real robot" before) and trepidation (they've been told it's a robot vacuum, which means it might eat any toys that aren't picked up...). While this scene unfolds, my eyes fall on a particular curiosity: a sticker that closed the bag immediately surrounding the robot. It contained the following fateful clause:
For software programmers interested in giving Roomba new functionality, we encourage you to do so.
I pointed out the sticker, chuckled a polite chuckle, and dismissed it without much thought: "I don't need any more projects."
Since I work from home, I spent the first couple weeks with the Roomba—Alfred, as it would come to be known—picking up detritus, starting Alfred, and watching it flounder about the house. Another day, another starting location, another attempt. After those weeks I figured out that Alfred could start from the middle of the house, and—if I placed its Virtual Walls in the right spot—cover half of the house on a single charge before returning Home.
It worked well enough. Each morning, I'd flip the two Walls and push the Clean button. The next morning, do the same. Again. And again. And again.
Maybe I should have left it there...
The thought was inescapable: if my morning routine with Alfred was that automatic, could it be automated?
Full disclosure: I am a software programmer, so that sticker was written to me. I am not, however, good with hardware—for this project to work, it would have to fit those skills.
A quick note on the microcontroller I used: Particle has made the world of hardware more accessible to me, for which I am grateful. Being able to drive precise timing or interface with other hardware entirely via firmware is invaluable for me to even attempt a project like this.
Before diving in, I made a list of the unknowns I would need to make known:
- How do these Virtual Walls work?
- How do you reprogram a Roomba?
- Why does iRobot encourage this?
First things first: though there are a great many resources on the Internet about Roombas, you need to remember—just as I realized while working on Alfred—that Roombas have been around since 2002 or so. Many of the resources you will find are out-of-date, and many of the Roombas folks decide to "hack" are older models, compounding the problem. If you're trying to recreate this work from scratch or you're reading this in 2030, expect a lot of trials and a lot of errors.
DIY Virtual Walls
Virtual Walls and Home Bases (the charging dock for the Roomba) "communicate" with a Roomba over infrared. These stationary components broadcast a specific signal, and the Roomba senses the infrared light to "see" where these components are. [This is the purpose of the round "nose" on the front of a Roomba.]
iRobot has iterated on the design of these signals a few times, but here's the gist of the state-of-the-art, circa 2018:
- One "burst" of IR light is emitted every 132 milliseconds.
- Each burst consists of three 38kHz pulses.
- Each pulse lasts 500 milliseconds with a 7500 millisecond pause between them.
Many of the "DIY Virtual Wall" designs I found built this (or something not entirely unlike this) entirely via hardware. Fortunately for me these capabilities are available in the Photon's firmware with methods like
analogWrite. Here is the source code I wound up with for the initial version.
In the event you decide to replicate this, I discovered that obsolete remote controls are wonderful for reclaiming existing infrared LEDs.
The fixture I built for this version—though I intend to make something nicer in the future—is two small pieces of wood nailed and glued into an L shape. I drilled two holes for the LEDs to shine through, and ran the wires along the baseboard to a Photon in a nearby closet. Please do something nicer than I did. 😂
Just as it was with the Walls, the prevailing challenge with programming Alfred was information: out-of-date specifications and write-ups and instructions and blog posts litter every corner of the online maker community. Fortunately iRobot itself was one step ahead of me: the 805 model I owned used the same interface as the iRobot Create 2, whose Open Interface is completely specified and publicly available.
Note: This is not the iRobot® Roomba® Serial Command Interface. This is not the iRobot® Create Open Interface or the iRobot® Create Open Interface v2. This is specifically the iRobot® Create 2 Open Interface. 🙄
With all that nonsense sorted, programming the behaviour was a matter of leveraging the
Serial1 interface of the Photon, which uses the built-in RX and TX pins. A call to
.begin(115200) enables the connection with the proper baud rate, while
.write(BYTE) sends the necessary byte(s) to the Roomba. To make things even easier, I used the Particle Cloud integration to trigger these calls: after
particle call Vacuum clean from the command line, Alfred would dutifully get to work.
I turned back to the frightening part of the project: the hardware. Namely, how do I power the Photon when it's not attached to my laptop? Alfred's serial port provides an interface to the onboard battery—great! Not so great: that battery is 13V, and the VIN pin on the Photon caps out at 5V. Apparently, that's where a DC-to-DC buck converter comes in.
Using these converters wound up almost as easy as the programming: wire the inputs of the converter to the battery, and attach a multimeter to the output of the converter. A small knob on the top of the converter can be adjusted with a similarly small screwdriver; keep turning until the output is 5V, and taste victory over your differences with that difference of potential difference.
With all the wiring in place, my prototype looked the part, complete with gaff tape housing. I used a liberal amount of the stuff to attach and conceal the Photon, breadboard, converter, and wires on the top of Alfred. Alfred now had a "hat".
Maybe there's a reason the Alfred of the Batman universe typically eschewed hats: apparently, if you are Alfred and you wear a hat while cleaning things:
- You will bump your hat into furniture.
- The tape will start to come off of your hat.
- Your hat will get between your "nose" and the Virtual Walls, and you will go outside your intended area.
- You will continue to bump your hat into furniture.
- Your hat will, eventually, start to break.
For all its hardware flaws, the software of the prototype version was remarkably stable. The Particle Cloud reliably delivered calls to the two Photons involved: one to toggle the desired infrared LEDs, and one to tell Alfred to start cleaning. The biggest issue was timing: messages take several seconds each to be received by the device and confirmed all the way back to the originating computer.
I've designed enough mesh networks to know I could do better, and a quick snoop inside the Roomba had me thinking, "What if I could embed the hardware inside?".
Time to do some real hacking.
As a matter of recap, the following aspects of the prototype design were effective:
- One Photon controls a series of infrared-LED-driven "walls" that direct the Roomba.
- A second Photon communicates with the Roomba's built-in brain over a serial connection.
- This second Photon can be powered directly via the Roomba's battery, with a DC-to-DC converter in-between.
- Both Photons can be driven by Particle Cloud functions.
However, the following challenges still need to be solved:
- Though Cloud functions are an extremely stable medium, they're high in latency.
- Mounting the Photon on top of the Roomba works, but that "hat" gets pretty well banged up.
At this point the solution was adequate, and I really didn't need any more projects. However, a catalyst emerged: The Internet of Solved Problems. I decided to submit a concept for an updated version of Alfred; if and only if it was selected would I spend the time on it.
Guess what? It was selected. 🤦♂️
Practical protocols 101
UDP provides a general approach to sending individual packets across a network like your home Wi-Fi. Nothing guarantees those packets are delivered in order. Nothing guarantees those packets are received at all. UDP has a huge superpower, however: UDP multicast, which allows you to send one package to any and all devices listening at a specific "multicast address".
TCP, on the other hand, guarantees both delivery and order of packets you send, and will manage "presence", the fancy name networking engineers give knowing whether the device your talking to is still connected or not. TCP has a huge downside, however: the client has to know where and how the server is listening for connections.
UDP, particularly multicast, works on its own if either:
- You don't care about whether or not and in what order packets are received. In Alfred's case, I wanted some guarantee so that the controlling laptop could send ordered or dependent commands. (e.g. Reconfigure the IR walls, then tell Alfred to clean.)
- You program your own logic for retrying or reordering packets. This is complex, and it would be very hard for me to test any custom code written for these scenarios.
TCP, on the other hand, works on its own if either:
- You keep the server at a specific IP address, and always listen on the same port. This might work today, but I would need to update Alfred's firmware any time the router needed to be reset or the network otherwise changed.
- You use another method to figure out where the server is located when you need it. It only I had a way to tell all machines on a network some piece of information like this...
Indeed, the answer is to use both: start a TCP server, then broadcast its address over UDP multicast to the rest of the network. This is precisely what the Quantum library, used by Alfred and its walls, now does.
After literally waking up with this solution in my head, I was struck with the realization that I've seen this design elsewhere. One version in particular is ZRE (implemented for PCs as Zyre), from the same folks as ZMTP (implemented for PCs as ZeroMQ). I initially tried to implement ZMTP and ZRE as Particle libraries, but failed and decided to redesign a simpler version. See here and here for those attempts.
If you're interested in more specifics around the code behind Alfred, see the Code attachments below. I hope they're relatively self-documenting, and I'm happy to answer GitHub Issues and add code comments if there's interest.
With all the other components of this system complete, it's back to the most terrifying aspect of the project: the hardware. Namely, taking Alfred apart to embed the Photon within its plastic corpus.
This is the most image-heavy aspect of this story, as it's the most risky. This is absolutely grounds for voiding your warranty, and making a mistake could result in destroying the Roomba for good.
For the brave souls that come after me and attempt a Herculean task like this, here are a few pointers:
There are videos like the following that walk you through the process of disassembling a Roomba—I recommend watching a few to familiarize yourself with the process. All 800-series Roombas are not created equal, but they are very similar.
When taking the plastic off the top, running a plastic pick under the edge is both efficient and safe: forcing the plastic with your hands can break the clips holding the plastic in place.
When removing screws from a consumer product like this, line the screws up, in order, as you remove them. Start at one screw, and go clockwise.
If you do this for multiple sets of screws, mark on the robot where each row of removed screws starts. I write the number of the row with a line pointing to the first screw.
The best way I could think to connect the Photon to Alfred was to solder a small ribbon cable to the serial port's pins on the back of the motherboard. If you do the same, remember: the schematic I've provided is from the perspective of someone plugging cables into the front of the serial port; youwillneedtofliptheconnectionsifyouconnecttotheback.
The body of the Roomba is efficiently packed with sensors, motors, and the like. However, if 12 o'clock is the direction a Roomba moves "forward", there is a cavity just inside the bump sensor, under the motherboard, at about 11 o'clock. Both the converter and the Photon should fit nicely inside of this cavity. [Apologies for the weird shot; I didn't disconnect all of the sensors from the motherboard, so the build was a bit...cozy.]
For my build, I went so far as to attach an external antenna to the Photon, which also fit handily into the same cavity. In the photo above, it's the grey wire going away from the Photon to the right side of the picture.
Where should this project go? If you ask my mother-in-law, there's a cottage industry waiting to be born out of this work, converting old Roombas into networked, remotely controllable robot assistants. If that were true, wouldn't it be nice to open an app on your phone—only available if you're at home, of course—and be able to tell your Roomba where in your home to clean?
That it would. If only I had a Raspberry Pi running a NodeJS server that provides a webapp and an HTTP API, communicating with the walls and Alfred when I push a button on the webapp... that would be nice...