今回は、Node-REDからFlashAirを呼び出して、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経由でルンバを動かす【今回】
FlashAirのGPIO
FlashAirではSDカードの各ピンをGPIOとして使用できます。詳しくはFlashAir Developersで説明されています。
準備として、FlashAirのSD_WLANフォルダにある「CONFIG」ファイルに「IFMODE=1」と書き足します。
CONFIGは隠しフォルダになっているのですが、MacやLinuxの場合はTerminalで普通にアクセスできます。
Windowsでも隠しフォルダにアクセスできるツールを使えばOKです。
今回、FlashAir Developersで紹介されているものと同様に秋月電子通商の「SDカードスロットDIP化モジュール」を使いました。
意外に盲点なのですが、このDIP化モジュールはFlashAirの全てのピンが引き出されている訳ではありません。
そのため、FlashAirのGPIOピンを全て使えるわけではありません。
具体的には、DIP化モジュールの取り扱い説明書の回路図を参照すると分かります。
下図の通り、DAT1とDAT2はプルアップ抵抗で3.3Vに繋がっているだけなので、ピンが引き出されていません。
つまり、FlashAirの0x04と0x08を使えません。(使うにはジャンパーを半田付けする必要があります。)
半田付けなしで使用できるピンの対応付けは次のようになります。
「Arduino」列は今回接続したArduino側のピン、「用途」列は今回の用途です。
ビット
割り当てFlashAir DIP化
モジュールArduino 用途 0x01CMDSDID5ルンバON 0x02DAT0SDOD4スリープ enable/disable 0x10DAT3CSD2ソフトウェアリセット -3.3VVCC3.3VFlashAirへの給電 -GNDGNDGNDGND
ArduinoとFlashAirの接続
上の表の配線を使って、Arduinoには次のような動作をさせます。- D2ピンのHIGH/LOWが変化するとソフトウェアリセットでスリープから復帰する。
- D5ピンがHIGHになっていたらルンバに赤外線送信する。
- D4ピンがHIGHになっていたらスリープする。
とは言え、DAT2, DAT3 を外部に引き出すと、WiFi経由で合計3ビット分のコマンドを表現できるので、
8種類(2の3乗)の赤外線コードを出し分けられます。
そのような将来的な拡張を考慮してD5ピンにFlashAirのCMDを割り当てました。
Node-REDからFlashAir
Node-REDからFlashAirへは次のようにHTTPリクエストを出すことでArduinoを動作させます。- FlashAirの全てのピンをLOWにする。つまりFlashAirに0x00を送信する。(http://〜/command.cgi?op=190&CTRL=0x1f&DATA=0x00)
- 一秒待つ(1.がFlashAir側で確実に受け取り完了させるため)
- FlashAirの0x10, 0x01, 0x02 をHIGHにする。つまり、FlashAirに0x13を送信する。(http://〜/command.cgi?op=190&CTRL=0x1f&DATA=0x13)
つまり、ArduinoのリセットピンをLOWからHIGHに変えることで、Arduinoをスリープから復帰させます。
スリープ復帰時にArduinoがCMD(0x01)を参照してルンバに赤外線コードを送信してくれれば、ルンバが起動します。
さらにその後、ArduinoがDAT0(0x02)を参照し、HIGHの時にスリープさせることで省電力化します。
本当は、LOWでスリープさせた方が省電力になるのかもしれません。
ところが、FlashAirは最初に電源が入った時点で全てのGPIOピンをHIGHにしてしまいます。
つまり、DAT0=LOWでスリープに入るようにしてしまうと、電源投入時にスリープしなくなってしまいます。
DAT0=HIGHをスリープに割り当てておけば電源投入直後にいきなりスリープしてくれます。
Arduinoのコーディング
以上の動作をArduinoに組み込むと次のようになります。赤外線の記憶と送信に関しては前回と同じなので省略します。
#include <EEPROM.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
// Arduino - board - SD
// 2 pin - CS - DAT3 - 1pin - 0x10 (RESET)
// 4 pin - SDO - DAT0 - 7pin - 0x02 (SLEEP)
// 5 pin - SDI - CMD - 2pin - 0x01 (CLEAN)
#define PIN_IR 10
#define PIN_IR_SENSOR 11
// FlashAir SDD pin (0x02)
#define PIN_SD_SLEEP 4
// FlashAir SDI pin (0x01)
#define PIN_SD_CLEAN 5
#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, OUTPUT);
pinMode(PIN_IR_SENSOR, INPUT);
pinMode(PIN_SD_CLEAN, INPUT);
pinMode(PIN_SD_SLEEP, INPUT);
pinMode(2, INPUT_PULLUP);
Serial.println("Start!");
}
// リセットからの復帰時はloopさえ動けば良いので特に処理無し
void wakeup(){
Serial.println("Wakeup");
}
// Arduino起動直後は全てHIGHになっているがSETUP_FLAGを使って無視させる
// 一旦LOWに落ちてからHIGHになると清掃開始
void loop() {
//Serial.println(".");
Serial.println("Run");
if ( ! SETUP_FLAG ) {
if ( digitalRead(PIN_SD_CLEAN) == HIGH ){
Serial.println("Run Roomba in clean mode");
loadBuffer(0);
executeIR();
executeIR();
executeIR();
}
// FlashAirの他のピンを引き出してコマンド拡張する場合はここに書く
}
SETUP_FLAG = false;
delay(500);
check_sleep(digitalRead(PIN_SD_SLEEP));
delay(500);
}
// sleep pinがHIGHだったらスリープ
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();
}
完成
以上で完成です。テレビ、プロジェクター、ルンバを音声操作できました。
紹介しませんでしたが、Node-REDを工夫すると1つの音声コマンドで複数機器を同時に動作させることも可能です。
この動画の例ではプロジェクターを起動する際にアンプの音声出力をテレビからプロジェクターに切り替えています。
0 件のコメント:
コメントを投稿