ROIを使うとかなり細かいコントロールができる一方、シリアル接続なのでルンバにArduinoを載せる必要があります。
頑張ってルンバから電源を取りつつArduinoをルンバに内蔵することも可能ではありますが。。。
今回やりたいことは単なる電源ON/OFFだけなので、ルンバとは有線接続せずに赤外線で済ませたいところです。
lircでやろうとしたところ、リモコンコードを読み取れずエラーになってしまいました。
テレビと同様に38Hzのようですが、信号が長すぎるのかもしれません。
そこで、Arduinoで赤外線のリモコンコードを読み取って赤外線LEDでルンバに送ります。
やっていることはRaspberry Pi+licdと同じですが、Arduinoなのでもっと泥臭い感じになります。
- 材料を揃える。
- 母艦のRaspberry PiにNode-REDをセットアップしてAmazon Echo Dotに認識させる
- Raspberry Pi Zero WをヘッドレスでWiFi対応セットアップする
- Raspberry Piで赤外線リモコンのコードをコピーする
- Node-REDから赤外線機能を呼び出してテレビを操作する
- FlashAirのGPIO機能でArduinoをHTTP API経由で制御する
- (おまけ)ArduinoからROI経由でルンバを動かす
- Arduinoから赤外線経由でルンバを動かす【今回】
- Node-REDからFlashAir/Arduino経由でルンバを動かす
回路を組む
Arduinoに赤外線受信モジュールと赤外線送信モジュールを繋ぎます。受信モジュールは赤外線リモコンを覚えさせる際に使用するだけなので、覚えた後は外しても構いません。

リモコンコードをArduinoのEEPROMに書き込む
赤外線受信モジュールからの入力信号がHIGHとLOWの変化する時間間隔を計測します。計測結果を配列に詰めてEEPROMに書き込めばOK。
このコードでは2種類のリモコンコードを覚えられるようになっています。
#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");
}
EEPROMから読み込んだコードで赤外線送信する
今度はEEPROMから配列を読み込んで、覚えていた時間間隔でLEDを点滅させます。我が家のRoombaが具合悪いだけかもしれませんが、
1回送信しただけでは正常に反応しないことが多いので、3回連続で送信しています。
また、loop関数で繰り返し送信されてしまうので送信し終わったらスリープさせます。
#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() );
}
}
ここまででArduinoからRoombaに掃除をスタートするコマンドを送れるようになりました。
ところが、このままではArduinoの電源を入れた直後にしか信号が飛びません。
次回、Node-REDからのリクエストをFlashAirで受けて、Arduinoをスリープから復帰させるように改造します。
0 件のコメント:
コメントを投稿