Connected Roomba: remote signaling

Finding the right interface

With a basic setup working the next thing I wanted to do was make communication wireless. Thinking about my options I ruled out using WiFi pretty quick since I didn't want to worry about discovery and router issues. I thought about Bluetooth since I could send commands from my phone to the board on the Roomba, but decided against it due to my lack of mobile programming experience and not wanting to add yet another new thing to learn. (Side note I've since learned about the Bluefruit app) Looking at the other Feather options I decided to make use of LoRa for my communication layer since it would be easy to use, my packets are tiny and I didn't have to worry about software beyond CircuitPython.

The boards

With the protocol determined and sticking with CircuitPython I found a Feather with LoRa built in, and a Pi Zero Bonnet with some buttons and a small display that would make testing easier. After reading through the docs and tutorials for both boards I began work on signalling the Roomba to start with the push of a button.

One of the first road blocks I ran into was of my own creation. The Roomba library I wrote for prototyping was too big for the Feather. The good news was OpenInterface was still useful, and there was plenty of room for the base class after removing the debug and abstraction code, so I only compiled the commands I knew I was going to use for version 1 and continued moving forward.

class OpenInterface:
    def __init__(self, tx_pin, rx_pin, brc_pin, baud_rate=115200):
        self._board = busio.UART(tx_pin, rx_pin, baudrate=baud_rate)
        self._tx_pin = tx_pin
        self._rx_pin = rx_pin
        self._brc_pin = brc_pin
        self._brc_pin.direction = digitalio.Direction.OUTPUT
        self._baud_rate = baud_rate
        self._stopped = True

    def start(self):
        if self._stopped:
            self.wake_up()

        for command in (b"\x80", b"\x83", b"\x87"):
            self._board.write(command)

    def stop(self):
        for command in (b"\x85", b"\xAD"):
            self._board.write(command)
        self._stopped = True

    def wake_up(self):
        for i in range(3):
            self._brc_pin.value = False
            time.sleep(0.5)
            self._brc_pin.value = True
            time.sleep(0.5)
            self._brc_pin.value = False
            time.sleep(0.5)

        self._stopped = False

After stripping things down and getting the Feather to start and stop the Roomba from the REPL I turned my attention to the Pi.

Feather Open Interface Connections

With the Pi Zero providing more resources installing the OS, setting up SSH and compiling Python 3.7 took more time then getting the Circuit Python libraries working. Blinka worked like a charm and following the docs from above I had a quick script to send start and stop packets via LoRa working in no time.

while True:
    try:
        if not start_button.value:
            msg = "Starting Roomba."
            logger.info(msg)
            rfm9x.send(bytes("1", "ascii"))
            display.fill(0)
            display.text(msg, 25, 15, 1)
        elif not stop_button.value:
            msg = "Stopping Roomba."
            logger.info(msg)
            rfm9x.send(bytes("0", "ascii"))
            display.fill(0)
            display.text(msg, 25, 15, 1)

The display on the bonnet was a nice touch so that I could watch the Feather in a terminal while the Pi let me know immediately which button was pressed and which command I should expect the Feather to receive.

Pi Bonnet Display

Next Steps

With the boards talking to each other and the ability to start/stop the Roomba with the press of a button the last thing to do was make this work when we are not at home. While LoRa has a pretty good range I wanted this to work for my wife and I without having to worry about where we are. In part 3 I look at making this work with SMS.