年末は自粛期間のためアキバでM5Stackを購入し軽く遊んでみたのでそれを記事にします。
※M5StackはESP32を搭載したマイコンモジュールです。
# 背景
背景として在宅勤務で家電を操作することにストレスを抱えていました。NatureRemoとAlexaを連携させているので問題はないのですがエアコンに関してはある問題がありました。
それは部屋が二つあるのですがエアコンがそれぞれの部屋にあり物理的距離が近いためNatureRemoの赤外線の範囲から二台ともエアコンが反応してしまう問題です。
Remoの場所を変えても変わらなかったので手動でON/OFFをしていましたが別の方法でできないかと思い、どうせなら作ってみることにしました。(M5Stackに触れたかっただけもある)
学習用も兼ねてM5Stack GrayスターターキットとM5StickC、IR Unitセンサーを購入しました。
では早速赤外線センサーで電源制御をAlexaから操作するようにしてみます。使うのはM5StackとIR Unitセンサーを使います。IR Unitセンサは実際試してみたところ大体1~2メートルぐらいの赤外線範囲でした。
作成手順
流れ的には以下の通りです。
1. Arduino IDEで環境構築
2. 使用する赤外線をIR Unitで受信して解析する
3. 2で解析した赤外線を送信する
4. WIFIをつなげる(Alexaにデバイスとして認識してもらうため)
5. ESPAlexaを利用しAlexaにデバイスを登録する。
6. 動作確認
## 1. Arduino IDEで環境構築
自分はみんなのM5Stackを購入して環境構築しましたが、設定方法などはブログなどでたくさん記事化されていたのでここは触れません。
## 2. 使用する赤外線をIR Unitで受信して解析する
IRremoteESP8266を利用してエアコンのリモコンの赤外線を解析します。
スケッチ例からIRrecvDumpV2を選択してスケッチを表示します。
変更点は41行目の受信PINの値のみです。
const uint16_t kRecvPin = 14; // default
const uint16_t kRecvPin = 22; // 22に変更する
よくM5Stackの赤外線センサー受信のGPIOのPINを36に設定している記事があったので少しハマってしまいましたが、M5Stack GrayのAポートに差し込んだ場合はI2Cで受信するSDAのGPIOは22でした。ちゃんと仕様読まなきゃですね。。
受信ができれば以下のようなデータがシリアルモニターで流れていることがわかります。
もしシリアルモニターが文字化けしていれば通信速度の問題であるので115200bpsに設定します。コードにもそのように記載されています。
https://github.com/crankyoldgit/IRremoteESP8266/blob/master/examples/IRrecvDumpV2/IRrecvDumpV2.ino#L43-L48
## 3. 2で解析した赤外線を送信する
GPIOをSCLの21にして赤外線センサーから送信してみます。
#include <M5Stack.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
// int IR_RECV_PIN = GPIO_NUM_22;
const int IR_SEND_PIN = GPIO_NUM_21;
const int TRANSMIT_CAPTURE_SIZE = 38;
const int IR_RAW_DATA_SIZE = 295;
uint16_t onRawData[IR_RAW_DATA_SIZE] = {4414, 4364, 570, 1594, 568, 1596,~~中略~~};
IRsend irsend(IR_SEND_PIN);
void setup() {
M5.begin();
irsend.begin();
Serial.begin(115200);
Serial.println("change on");
irsend.sendRaw(onRawData, IR_RAW_DATA_SIZE, TRANSMIT_CAPTURE_SIZE);
//irsend.sendPanasonic("0x555AF148688B", TRANSMIT_CAPTURE_SIZE);
}
void loop() {
}
上のコードでは先ほど解析して手に入れたbufferデータを仕込んでsendRaw関数で赤外線を送信しています。
解析した際にProtocolが何故かUnknownになっていたのでbufferデータで送るようにしています。もしPanasonicの赤外線を送る場合などはコメントアウトしているsendPanasonic関数でコマンドを送ることができます。
その他のメーカーにも対応しているそう。
https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/IRsend.h
ここまででM5Stackにプログラムを書き込むと一度だけ送信されることが確認できます。
## 4.WIFIをつなげる(Alexaにデバイスとして認識してもらうため)
Alexaと連携させるためにAlexaとつながっているWIFIにM5Stackをつなげます。
以下のコードでWIFIに接続し、connectionが成功したらディスプレイに表示されるので確認ができれば問題ないです。
#include <M5Stack.h>
#include <WiFi.h>
const char* SSID = "YOUR SSID";
const char* PASSWORD = "YOUR PASSWORD";
// the setup routine runs once when M5Stack starts up
void setup(){
// Initialize the M5Stack object
M5.begin();
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
M5.Lcd.print(',');
}
M5.Lcd.print("\r\nWiFi connected\r\nIP address: ");
M5.Lcd.println(WiFi.localIP());
}
// the loop routine runs over and over again forever
void loop() {
}
## 5. ESPAlexaを利用しAlexaにデバイスを登録する。
それではAlexaと連携するためにESPAlexaというライブラリを追加してコードを書いていきます。
完成したコードは以下になります。
#include <M5Stack.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <Espalexa.h>
#include <WiFi.h>
#define JST (3600L * 9)
const char* SSID = "YOUR SSID";
const char* PASSWORD = "YOUR PASSWORD";
// int IR_RECV_PIN = 22;
const int IR_SEND_PIN = 21;
const int TRANSMIT_CAPTURE_SIZE = 38;
const int IR_RAW_DATA_SIZE = 295;
uint16_t offRawData[IR_RAW_DATA_SIZE] = {4414,4364,~~中略~~};
uint16_t onRawData[IR_RAW_DATA_SIZE] = {4342, 4438,~~中略~~};
IRsend irsend(IR_SEND_PIN);
void airconChanged(EspalexaDevice* dev);
Espalexa espalexa;
void setup() {
M5.begin();
irsend.begin();
Serial.begin(115200);
// wifi connection
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
M5.Lcd.print(',');
}
// espalexaをデバイスを認識させる
espalexa.addDevice("エアコン", airconChanged, EspalexaDeviceType::onoff);
espalexa.begin();
// 時刻を表示
configTime(JST, 0, "ntp.nict.jp", "time.google.com","ntp.jst.mfeed.ad.jp");
}
void loop() {
espalexa.loop();
displayTime();
}
void airconChanged(EspalexaDevice *d) {
if (d == nullptr) return;
Serial.println(d->getValue());
bool request_flag = d->getValue() ? true : false;
airconPowerSwitch(request_flag);
}
void airconPowerSwitch(bool request_flag) {
if (request_flag) {
Serial.println("change on");
irsend.sendRaw(onRawData, IR_RAW_DATA_SIZE, TRANSMIT_CAPTURE_SIZE);
} else {
Serial.println("change off");
irsend.sendRaw(offRawData, IR_RAW_DATA_SIZE, TRANSMIT_CAPTURE_SIZE);
}
}
void displayTime() {
struct tm tm;
if (getLocalTime(&tm)){
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setCursor(60, 80);
M5.Lcd.setTextSize(3);
M5.Lcd.printf("%d%2d%2d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
M5.Lcd.setCursor(80, 140);
M5.Lcd.printf("%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);
}
delay(1000);
}
ディスプレイが寂しかったので簡易時計を入れました。。
## 6. 動作確認
Alexaアプリで新規デバイスを検出をしたところM5Stackが検出されたらOKです。何故か照明として検出される謎を追えていないですが声で操作ができれば良いのでこのままにしています。
Alexaは標準でデバイス名+[をつけて|を消して]でON/OFFをしてくれるのでもし他の呼び方をしたい場合は定型アクションを登録してカスタマイズすることも可能です。
これで無事にリビングのエアコンのみを動かすことができるようになりました。
##まとめ
M5Stackを初めて触るので少し苦戦しましたがなんとか動くものができました。
C++も先人のコードを頼りにノリで書いた感じなのでもう少し機能を追加していくなら色々考慮しないとです。。
あと当初はM5StickCで作る予定だったが誤ってobnizのM5StickCデバイスを購入してしまった。ファームウェアを書き換えて普通のM5StickCにもできるらしいですがちょっともったいないので今度の機会に触ってみようかなと。
ちなみにobnizはJSでデバイスを制御できるようになっているのでC++よりはもう少し手軽に使えそう。