ROI allows fine-grained control, but since it's a wired serial connection, the Arduino has to ride on the Roomba itself.
Powering the Arduino from the Roomba and embedding it is possible, but it's a lot of work.
Since all we need is simple power ON/OFF, connecting via IR without any wired connection to the Roomba is the cleaner approach.
When I tried capturing the Roomba's remote codes with lirc, it failed with errors.
The carrier seems to be 38 kHz like a TV remote, but the signal may simply be too long.
So the plan is: use Arduino to capture the IR remote codes, then replay them to the Roomba with an IR LED.
It's the same idea as Raspberry Pi + lirc, but in Arduino style — which means a bit more hands-on.
- Gather parts.
- Set up Node-RED on the main Raspberry Pi and register it with Amazon Echo Dot
- Headless WiFi setup for Raspberry Pi Zero W
- Copy IR remote codes with Raspberry Pi
- Call IR functions from Node-RED to control the TV
- Control Arduino via HTTP API using FlashAir GPIO mode
- (Bonus) Control Roomba from Arduino via ROI serial interface
- Control Roomba from Arduino via IR [This article]
- Control Roomba from Node-RED via FlashAir/Arduino
Building the Circuit
Connect an IR receiver module and an IR transmitter module to the Arduino.The receiver is only needed while recording the remote codes — it can be removed afterward.

Writing Remote Codes to Arduino EEPROM
The idea is to measure the timing of HIGH/LOW transitions on the IR receiver's signal pin and store that array in EEPROM.This sketch can record two different remote codes.
#include <EEPROM.h>
#define PIN_IR_SENSOR 11
#define SCAN_INITIAL_TIMEOUT 10000000
#define SCAN_TIMEOUT 10000000
#define IR_BUF_LEN 64
volatile int IR_BUF[IR_BUF_LEN];
volatile bool SETUP_FLAG = false;
void setup() {
Serial.begin(9600);
SETUP_FLAG = true;
pinMode(PIN_IR_SENSOR, INPUT);
updateIR(0);
updateIR(1);
}
int updateIR(int pos) {
Serial.println("SCAN START");
memset(IR_BUF,0,IR_BUF_LEN);
for( int i=0; i<3; i++ ){
Serial.print("PUSH BUTTON ");
Serial.println(pos);
int result = scanIR(i);
for(int j=0; j<result; j++){
Serial.print(IR_BUF[j]);
Serial.print(" ");
}
Serial.print("\n");
Serial.print(result);
Serial.println(" bytes were read");
delay(3000);
}
saveBuffer(pos * IR_BUF_LEN*2);
Serial.println("SCAN END");
return 1;
}
int scanIR(int repeat) {
int idx=0;
int ir_status = HIGH;
unsigned long lastStatusChanged = 0;
unsigned long timeout = SCAN_INITIAL_TIMEOUT;
while(1){
if ( ir_status == LOW ){
while( digitalRead(PIN_IR_SENSOR) == LOW){
// wait
;
}
} else {
if( wait_high_signal( micros() + timeout ) < 0 ){
break;
}
}
unsigned long now = micros();
if( lastStatusChanged > 0 ){
int val = (int)((now - lastStatusChanged) / 10);
if( repeat > 0 ){
if( IR_BUF[idx] > 1 ){ break; }
if( abs((int)(IR_BUF[idx] - (int)val)) > IR_BUF[idx]*0.3 ){ idx = 0; break; }
IR_BUF[idx] = (int)( IR_BUF[idx] * repeat + val ) / (repeat + 1);
} else {
IR_BUF[idx] = val;
}
idx++;
if( idx == IR_BUF_LEN -1 ){ break; }
}
lastStatusChanged = now;
if (ir_status == HIGH) {
ir_status = LOW;
} else {
ir_status = HIGH;
}
timeout = SCAN_TIMEOUT;
} // while 1
if( idx > 0 ){ IR_BUF[idx] = -1; }
return idx;
}
int wait_high_signal(unsigned long timeout) {
while( digitalRead(PIN_IR_SENSOR) == HIGH ){
if( micros() > timeout ) { return -1; }
}
return 1;
}
void saveBuffer(int pos) {
Serial.println("SAVE START");
byte buf;
for( int i=0; i < IR_BUF_LEN; i++ ){
buf = lowByte(IR_BUF[i]);
EEPROM.write( pos+i*2, buf );
delay(5);
buf = highByte(IR_BUF[i]);
EEPROM.write( pos+i*2+1, buf );
delay(5);
if( buf < 0 ){ break; }
}
Serial.println("SAVE END");
}
Replaying the Stored Code via IR Transmission
This sketch reads the timing array from EEPROM and pulses the IR LED at those intervals.My Roomba often doesn't respond to a single transmission (might be specific to my unit),
so the code sends the signal three times in a row.
Since the loop function would keep sending forever, the Arduino enters sleep mode after finishing.
#include <EEPROM.h>
#define PIN_IR 10
#define IR_BUF_LEN 64
volatile int IR_BUF[IR_BUF_LEN];
void setup() {
Serial.begin(9600);
pinMode(PIN_IR, OUTPUT);
Serial.println("Start!");
}
void loop() {
Serial.println("Run");
loadBuffer(0);
executeIR();
executeIR();
executeIR();
check_sleep(true);
}
void wakeup(){
Serial.println("Wakeup");
}
void check_sleep(bool flag) {
if ( ! flag ) { return; }
Serial.println("Sleep");
delay(1000);
attachInterrupt(0, wakeup, CHANGE);
set_sleep_mode(SLEEP_MODE_STANDBY);
sleep_enable();
sleep_mode();
sleep_disable();
}
void loadBuffer(int pos) {
Serial.println("LOAD START");
int h, l;
for( int i=0; i<IR_BUF_LEN; i++ ){
l = EEPROM.read(pos+i*2);
h = EEPROM.read(pos+i*2+1);
IR_BUF[i] = (h << 8) + l;
Serial.print(IR_BUF[i]);
Serial.print(" ");
if( IR_BUF[i] < 0 ){ break; }
}
Serial.println("\nLOAD END");
}
void executeIR() {
for(int i=0; IR_BUF[i] > 0; i++){
unsigned long len = (unsigned long)IR_BUF[i] * 10;
unsigned long now = micros();
do {
digitalWrite(PIN_IR, 1-i&1);
delayMicroseconds(8);
digitalWrite(PIN_IR, 0);
delayMicroseconds(7);
} while( now + len > micros() );
}
}
At this point, Arduino can send the "start cleaning" command to the Roomba via IR.
The issue is that in the current form, the signal only fires once — right when the Arduino powers on.
Next time: modify the setup so Arduino wakes from sleep when it receives a request from Node-RED via FlashAir.
No comments:
Post a Comment