DIY Virtual Wall for Roomba – Part Two

By | August 9, 2014

This is the second and probably final part of my Roomba virtual wall project.  In my first post, I talk more about the technical details that of how the signalling works, etc., so I recommend it as a starting point.  In this post, I’ll talk about how I put it all together and deployed.  This is one of those projects I probably would’ve let fade into oblivion were it not for the questions I received, so thanks to those who read it and contacted me, thus encouraging me to see it through to completion.

Schematic

I have slightly updated the schematic since my last post to make the virtual wall a little more useful in my own scenario.  First, I added a red LED to flash periodically to let me know the virtual wall is on.  Second, I added a momentary switch for power control and setting run duration.  When the device isn’t running, it’s not really off, rather in a sleep state that should only consume a trivial amount of power.  When the button is first pressed, it wakes up the microprocessor and sets the run time to some default duration (currently about three hours).  Pressing the button when the device is already running will add another hour to the run time, up to a maximum of about 10 hours.  My Roomba isn’t fancy enough to allow for scheduling, so hitting the button on the virtual wall on my way to turn on the Roomba is no big deal, and this should help me get the most out of my batteries.

 

Screen Shot 2014-08-08 at 11.46.05 PM

Code

The biggest update in my code from the last pass is the addition of an interrupt handler for reacting to button presses.  This handler determines how long the device will stay awake and continue to send IR pulses.

I’ve also set the BURST_COUNT to a value of 100.  The BURST_COUNT is the number of IR bursts will be sent before the microprocessor takes a short nap to preserve battery life.  In my previous code, I had the value set at 10, which worked in my initial tests.  However, I was having  a problem with reliability, so I set it to 100 and left it there.  It’s probably worth fiddling with, but even when it’s set to 100, bursts are sent for 200ms, followed by a 272ms sleep state.  This means that the microprocessor is still spending more time dozing than working.

#include <htc.h>

__CONFIG(FOSC_INTOSCIO & WDTE_OFF & PWRTE_OFF & MCLRE_OFF & CP_OFF &
    CPD_OFF & BOREN_OFF & IESO_OFF & FCMEN_OFF);

#ifndef _XTAL_FREQ
 #define _XTAL_FREQ 2000000                                                                                                                                         
#endif

#define PWM_OFF CCP1CON = 0x10
#define PWM_ON CCP1CON = 0x1c

#define LED_PIN GP4

#define BURST_COUNT    100                                    // How many IR bursts to send
#define CYCLES_PER_HOUR 7627                             // (60min*60sec*1000ms) / (272ms + (BURST_COUNT * 2ms))  
#define MAX_CYCLES (CYCLES_PER_HOUR * 10)                // Allow a maximum of 10 hours on timer
#define BLINK_EVERY  (5000 / ((BURST_COUNT * 2) + 272))    // About 5 seconds between LED blinks
#define DEFAULT_ON_CYCLES (CYCLES_PER_HOUR * 3)            // Default to 3 hours on first button press

volatile unsigned long blinkEvery = 0;
volatile unsigned long wakeCount = 0;

void interrupt interruptHandler(void)
{

    if( GPIF )
    {
        // Looking for clean button release, so switching back to high
        if( GP3 )
        {
            __delay_ms(50);
            if( GP3 )
            {
                // Set default time for first press, otherwise add an hour
                if( wakeCount == 0 )
                    wakeCount = DEFAULT_ON_CYCLES;
                else
                {
                    wakeCount += CYCLES_PER_HOUR;
                    if( wakeCount > MAX_CYCLES )
                        wakeCount = MAX_CYCLES;
                }
                blinkEvery = 0;    // Force an LED blink for feedback
            }
        }
        GPIF = 0;        
    }

}

// GP2 - PWM output
void main() {

    int i;

    OSCCON = 0x51;                      // Internal 2MHz osc.
    
    ADCON0 = 0;                         // all pins digital
    ANSEL = 0;                          // all pins digital
    CMCON0 = 7;                         // Comparators off.
    TRISIO = 0x08;                      // all output except GP3
    GPIO = 0;                           // all pins low
    
    PR2 = 0b00001100 ;                    // Set up PWM for roughly 38kHz
    T2CON = 0b00000100 ;
    CCPR1L = 0b00000110 ;
    PWM_OFF;
    
    WDTCON = 0b00010000;                // WDT Prescalar = 1000 = 8192 = 272ms
    PSA = 0;                            // Assign prescalar to Timer0

    IOC3 = 1;                            // Enable Interrupt on Change for pin 3 (button)
    GPIE = 1;                            // Enable pin interrupts
    GIE = 1;                            // Enable global interrupts

    LED_PIN = 1;                        // Turn on the LED for a few seconds at startup
    __delay_ms(5000);
    LED_PIN = 0;    

    while (1){

        // Flash the LED periodically
        if( blinkEvery++ % BLINK_EVERY == 0 )
            LED_PIN = 1;
        else
            LED_PIN = 0;

        // Send a few IR bursts
        i = BURST_COUNT;
        while( i-- )
        {
            // Virtual wall is 1ms on, and 1ms off
            PWM_ON;
            __delay_us(1000);
            PWM_OFF;
            __delay_us(1000);
        }
    
        // Then sleep for a moment
        SWDTEN = 1;        // Enable watch dog timer
        asm("sleep");
        SWDTEN = 0;      // Disable watch dog timer

        // See if it's time to go to sleep until the button
        // is pushed again
        if( wakeCount > 0 )
            wakeCount--;
        else
        {
            // Small visual indicator before sleeping (mostly for test)
            for(i = 0; i < 5; i++)
            {
                LED_PIN = ! LED_PIN;
                __delay_ms(200);
            }
            LED_PIN = 0;
            asm("sleep");
            blinkEvery = 0;
        }

    }
}

Enclosure

As I had predicted, getting this project into an enclosure was my biggest hurdle.  I bought a project enclosure from RadioShack for about $5 to use as a starting point and was able to get everything mounted in there pretty well, including the proto board, status LED and switch.  I originally had the IR LED soldered onto my board, and I used a small piece black tubing that extended from the front of the enclosure and over the IR LED to try to give the infrared light some direction.  My experimentation with this method didn’t prove to be very successful.  In hindsight, however, I think it may have been more of a software problem.

Nonetheless, having just had a positive experience with 3D printing, I tried another route for mounting the IR LED.  I measured the width and depth of the IR output hole on a real virtual wall and tried to create something simple and cheap to reproduce it.  What I came up with is a little IR LED holder that cost me less than $1 to print.  The part is pushed through a hole on the front of the enclosure and holds the IR LED in place while creating an output path similar to the actual virtual wall.  I won’t claim that it’s pretty to look at.  In fact, the printed part I ended up with is flawed in that it doesn’t exactly match the model’s dimensions, but it’s good enough.  You can view, modify and download a model of the IR LED socket here.

IR LED Holder

IR LED Holder

 

Finished Product

I spent about $8 specifically for this project, which includes the enclosure, the battery holder, and the 3D-printed IR LED socket.  The rest of the components were already part of my stock of nerd stuff, and probably have a value of about $3 to $5.  This makes it less expensive than an OEM virtual wall, provided you don’t count all the hours I spent building, coding, experimenting.  You can’t put a price on fun though, right?

Here you can see the virtual wall in action at doorway between my dining room and the kitchen, precisely where it is meant to live out its days.  You’ll notice at about the five second mark, the Roomba starts to enter the kitchen, but does an about-face when it hits the virtual wall’s IR beam.  The testing I’ve done seems to indicate a reliable range of about 10 feet, though modifying the BURST_COUNT constant in the code and using a better IR LED socket might improve on that slightly.

Finished circuit on permaproto board.

This is the entire circuit and all the components soldered onto a perma-proto board.

Everything in enclosure

Everything installed in the enclosure. I drove a single screw through an unused portion of the perma-proto board to hold it steady. The battery holder is free to flop about, which is fine for this application. Ignore the really ugly cut on the inside plastic cover. Just pretend like you didn’t even see it.

IR LED Socket

This is the 3D-printed IR LED socket from the inside and out. The final print didn’t quite match the original model, but it seems to be working well enough. I used hot glue to affix it to the enclosure.

 

 

16 thoughts on “DIY Virtual Wall for Roomba – Part Two

  1. Pingback: DIY Virtual Wall for Roomba | misc.ws

  2. romand

    Hello,

    Thanks for the great project. Just one thing – that the S1 in the schematics stand for?

    Thanks,
    Romand

    Reply
    1. Justin Post author

      Thank you, Romand. The switch is a normally open, momentary contact switch.

      Reply
  3. Guillermo

    Great project. I’ve got two questions:

    – Do batteries last for months just as the original products?
    – Could I schedule the Walls? I’ve got a big house so I have to manually zone them with Virtual Walls. It would be great to have this zoning automatically.

    Thanks

    Reply
  4. Jan Pobořil

    What about power consumption? How long it runs on battery if it is turned in non-stop (so user do not have to push button always before cleaning)?

    Reply
  5. Richard

    I bought a virtual wall for a Haier robot vacuum for $6.95 that looks exactly like the VW for my Roomba 650 but it only turned the vacuum once then went right by it. Could this VW for the Haier not be sending out enough IR frequencies to turn the 650. How would I fix or up the number o frequencies

    Reply
  6. James Richards

    What series of Roomba do you have? Wondering if it would work on the latest versions?

    Thanks

    Reply
  7. Bartek

    Hello,
    I’ve failed with 1ms ON and 1ms OFF with my Roomba 880. So I’ve checked how the communications between the wall and the Roomba (via IR) works. Currently I am working on the DIY construction for virtual wall as well. Please check my website for details (http://eka.tomeczko.pl/index.php/diy-roomba-virtual-wall). Currently I have working Arduino-based prototype and I am working on a customized solution (with automatic time-based turn off, etc).

    Reply
  8. King

    So this is about an $8 project, but for someone starting from scratch they would need to buy the PICkit 3 In-Circuit Debugger for $48 in order to program the microcontroller, right?

    Reply
  9. Tom

    Many thanks! The circuit works. I’ve built a mains powered version by adding an old cellphone (5V) battery charger. Had to increase R2 and R4 to keep thecurrent through both LEDs down to an acceptable level. Other than that, it’s running perfectly.

    Reply
  10. Greg

    Very cool project -I’ve built three of them now for about $8 each (I already had the programmer) instead of $50 each retail!

    Of course I had a few mods of my own 🙂 I originally had only limited success stopping the Roomba – it seems the IR timings are off a bit. Using an IR detector, I found that instead of 1ms on/1ms off, the actual timing of the OEM Virtual Wall that I have is about 500us on/7500us off. Do that three times, then sleep for 132ms (WDT prescaler 4096). I also used larger resistor values in order to save power, and my tests show that I’m getting about the same distance range as the OEM model. My model pulls about 0.25 mA, which is about the same as the OEM model. At that rate, the batteries will last for months running 24/7, so no need for the pushbutton. I also enabled the ADC in the PIC12LF1571 that I used and added a small amount of code to provide a low battery indication, so when the battery voltage drops below 2.0V, the LED will flash twice every cycle.

    Cheers!

    Reply
    1. Justin Post author

      Thanks for the comment. Your project sounds awesome. If you have it documented somewhere, I’d love to see the link. Or if you’re interested in posting about it here, let me know.

      Reply
      1. Greg

        I don’t have a link to anything, but I have a Fritzing schematic, BOM, and some pics. I would like to set up a site similar to this – what software are you using for this site? I was thinking of doing something in WordPress.

        Reply
      2. Greg

        Just scrolled to the bottom and see that you’re using WordPress too. I’ll post a link to my project files when I get them set up (next couple of days hopefully).

        Reply

Leave a Reply

Your email address will not be published. Required fields are marked *