今回は、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 件のコメント:
コメントを投稿