2018年4月21日土曜日

Alexa と Raspberry Piでルンバに掃除してもらう:(5)FlashAirのGPIO機能でArduinoのHTTP API

前回までで、Alexaからテレビを操作できるようになりました。
同じ方法でルンバも操作しようとしたところ、lircでルンバのリモコンコードを読み取る際にエラーになってしまいました。
理由がまるで分からず。。。

そこで、Raspberry PiではなくArduinoからルンバを操作することにしました。
ただ単に、Raspberry PiとArduinoのどちらでも赤外線が使える、というだけの話ですが。

Arduinoを使う場合、まずは母艦のNode-REDからの通信を受け取らないとどうにもなりません。
とは言え、数ビットの無線信号を受け取れれば良いだけなので、ESP-WROOM-02やBluetoothなど手段はいくらでもあります。
今回は、余っていた古いFlashAir(W-02)を使いました。

  1. 材料を揃える。
  2. 母艦のRaspberry PiにNode-REDをセットアップしてAmazon Echo Dotに認識させる
  3. Raspberry Pi Zero WをヘッドレスでWiFi対応セットアップする
  4. Raspberry Piで赤外線リモコンのコードをコピーする
  5. Node-REDから赤外線機能を呼び出してテレビを操作する
  6. FlashAirのGPIO機能でArduinoをHTTP API経由で制御する【今回】
  7. (おまけ)ArduinoからROI経由でルンバを動かす
  8. Arduinoから赤外線経由でルンバを動かす
  9. Node-REDからFlashAir/Arduino経由でルンバを動かす

FlashAirのGPIO機能

FlashAirのGPIO機能を使うと、SDカードの端子のうちの5つをHTTP経由でGPIOピンとして使用できます。
FlashAir Developersに詳しく説明されています。

つまり、FlashAirの端子とArduinoのピンを接続すると、ArduinoのピンのHigh/LowをHTTPから切り替えられます。
そのかわり、FlashAirのストレージとしての機能は使えなくなります。

FlashAirの動作を変更するには /SD_WLAN/CONFIG ファイルを編集します。
通常は不可視フォルダになっていますが、LinuxやMacでマウントするとShell経由で普通に編集できます。
Windowsの場合は不可視フォルダを表示すれば良いと思います。

このファイルを開いて末尾にIFMODE=1を追加すればGPIO機能が有効になります。

FlashAirをWiFi子機にする

通常、FlashAirはWiFiの親機として動作します。
そのため、自宅のLANの中にあるNode-REDからのHTTPリクエストを受け取れません。
そこで、FlashAirを無線子機としてWiFiルータに接続させます。

この設定についてもFlashAir Developersに詳しく書かれています。
ステーションモードの利用

そんなこんなで、出来上がったCONFIGファイルは次のようになります。
APPMODE=5
APPNAME={FlashAirの名前}
APPSSID={WiFiのSSID}
APPNETWORKKEY={WiFiのパスワード}
CIPATH=/DCIM/100__TSB/FA000001.JPG
VERSION=F19BAW3AW2.00.00
CID=02544d535730384708c00b7d7800d201
PRODUCT=FlashAir
VENDOR=TOSHIBA
MASTERCODE=18002d4ff0a2
IFMODE=1

FlashAirとArduinoを接続

こんな感じのSDカードスロットを使ってArduinoと接続します。 今回はルンバの起動ができれば良いだけなので1ビット送れれば十分です。
そのため、信号ピン1つと3.3V、GNDの3ピンだけ接続します。

FlashAirのSDIをArduinoの11ピンに繋ぎました。
3.3VとGNDは適宜。

HTTPからFlashAirのGPIOを書き換える

GPIO機能のHTTP APIについてはFlashAir Developersの「SDインターフェース端子のI/O利用(op=190)」に書かれています。
今回はSDOピン(CMD)をGPIOとして使うので、0x01のHIGH/LOWを切り替えられれば良いです。

つまり、URLパラメータとGPIOの組み合わせは次の通りです。
  • SDO=HIGH → op=190&CTRL=0x1f&DATA=0x01
  • SDO=LOW → op=190&CTRL=0x1f&DATA=0x00

ArduinoでFlashAirのGPIOを読む

Arduinoからピンの状態を読むのは普通に「digitalRead」で良いです。
ライブラリも何も必要ありません。
注意点としては、FlashAir起動時は全てのピンがHIGHになっていることでしょうか。

つまり、SDOピンがHIGHの状態を検知してルンバを起動させようと考えた場合、
FlashAirの電源投入時にHIGHなのでいきなりルンバが起動してしまいます。

電子回路的にHIGH/LOWを反転させても良いのですが、部品点数が増えて面倒なので、
Node-RED側で一旦LOWに落としてからHIGHにするようなリクエストを投げることで回避しました。
もしかしたら、Arduinoで一旦pinModeをOUTPUTにしてLOWに落とせるのかもしれませんが試していません。

Arduinoのコードはこんな感じです。
#define PIN_SD_CLEAN 11

void setup() {
  Serial.begin(9600);
  pinMode(PIN_SD_CLEAN, INPUT);
}

// Arduino起動直後は全てHIGHになっているが無視させる
// 一旦LOWに落ちてからHIGHになると清掃開始
volatile bool clean_flag = HIGH;
void loop() {
  if ( clean_flag == LOW && digitalRead(PIN_SD_CLEAN) == HIGH ){
    Serial.println("Run Roomba with clean mode");
  }
  clean_flag = digitalRead(PIN_SD_CLEAN);
  delay(1000);
}

Node-REDからArduinoにHTTPリクエスト

少々ブサイクですが、次のようにしました。
Alexaに「ルンバで掃除して」と言うと左端の「ルンバ」ノードが呼ばれます。
まず最初に真ん中のフローで「Roomba reset」が呼ばれます。
「Roomba reset」はHTTPリクエストのノードです。
FlashAirに「op=190&CTRL=0x1f&DATA=0x00」を投げて全てのピンをゼロリセットしています。
FlashAirの起動後、2回目以降の「ルンバ掃除して」の場合は前回呼び出し時にゼロリセットされているので、この処理は無意味になります。

先ほどのRoomba resetが先に実行されるように、上部のフローでは2秒delayが入っています。
本当はdelayよりも、Roomba resetの実行後にフラグを書き換えるなどしてロックした方が良さそうに思いますが手抜きです。
この手抜きのせいで、「ルンバ掃除して」と言ってから実行されるまでに2秒掛かってしまうので微妙に違和感があります。

2秒後に「Roomba clean」が呼ばれます。
これは「op=190&CTRL=0x1f&DATA=0x01」でSDOピンをHIGHにしています。
この時点でArduino側ではルンバの制御に入ります。

その後、5秒delayを掛けて再度ゼロリセットしておきます。
5秒というのは、Arduino側がHIGHを確実に検知できるよう、長めに取っています。

最初のdelayについては、もし検知し損ねても処理に失敗するのは起動後最初の1回だけなのでイライラが少ないです。
また、長すぎると「ルンバ掃除して」からのタイムラグが長くなってしまうため、できるだけ短く。
2回目のdelayは短かすぎると毎回失敗し、長くてもユーザビリティに影響ないので長めに。

ここまででNode-REDからHTTP経由でArduinoに1ビット信号を送ることができるようになりました。
次回はArduinoからルンバを操作します。

0 件のコメント:

コメントを投稿