M5Stackで赤外線リモコンを作る（送受信）

素直にスマートスピーカーに対応のリモコンでもつければいいだろうに、半端者が無茶をして物凄い苦労したシリーズ。

# 目的
* エアコンのリモコンをネットワークで操作できるようにする
    + ネットワーク機能 → M5Stack
    + M5Stackをリモコン化する
       - M5Stackで赤外線信号の受信および送信を行う
* （ちなみに大目的はエアコンの操作の自動化。そのためのデータ採取をネットワーク上で行うこと。データを集めたら機械学習する。）

## 目的に対する課題
* M5Stackでサポートされている赤外線受信機・送信機はない（ないはず）
  + なので自分でドライバーを作成する必要がある

# ハードウェア構成
* [M5Stack](https://m5stack.com/)
* [Grove - Infrared Emitter](http://wiki.seeedstudio.com/Grove-Infrared_Emitter/)
* [Grove - Infrared Receiver](http://wiki.seeedstudio.com/Grove-Infrared_Receiver/)

以降、単にエミッターとレシーバーと書きます。

## 接続
片方ずつ使う場合は関係ないが、２つ同時に使う場合の（使えるので）接続を示します。

* レシーバー: Groveで接続（GPIOの22を使うことになる）
* エミッター: [GROVE - 4ピン-ジャンパメスケーブル](https://www.switch-science.com/catalog/1048/) を使用し下記のように接続する。

    ケーブル色 | 役割 | 接続先
    ---|---|---
    赤 | 電源 | 3.3V
    黒 | グランド | GND
    黄色 | IO | G2 GPIO
    白　|| 接続しない。もしくはGND

もしM5Stackのピンについて知りたい場合は[こちらの公式ドキュメント](https://www.m5stack.com/assets/docs/)を見ると良い。

# 実装・検証環境
* 開発マシン: Centos7
* 開発環境: Arduino IDE
* エアコン: 富士通ゼネラル製の何か
  + リモコンの型番はAR-RFC2J

# プログラム
## 成果物
https://github.com/hakua-doublemoon/IRSendRev_M5Stack

## 解説とか
### プログラムの元ネタと主な修正点
SeeedStudioの[IRSendRev](https://github.com/Seeed-Studio/IRSendRev)を一応参考にしました。
変更点としてはいろいろあるというか解体して組みなおしたので原型を留めてない感じもしますが、

* 赤外線デバイスのクラスを解体してexternで直接使ってる
* PWMの機能の使い方がわからなかったのでタイマーで直接制御
  + 上記に伴い、特に送信処理が大改造
* その他メモリ管理などで細かな修正

ライセンスに関しては私のプログラムとSeeedStudioのプログラムはMITとしているが、License.txtにはGPLv2みたいのが書いてある。これは、URLを忘れたのだが、赤外線デバイス関連のプログラムを探しているとより古いプログラムで似たようなプログラムがあり、おそらくはそのライセンスがGPLv2なのだと思う。

### プログラムの基本原理
赤外線通信の原理は下記などがわかりやすいと思う。
http://www.geocities.jp/zattouka/GarageHouse/micon/InfraredCOM/InfraredCOM.htm
要するに:

* 560μs（1800Hz？）とか38kHz（26μs）で赤外線がON/OFFしているので、38kHzでレシーバーをチェックしてRawデータを記録し、
* 受信が終わったときにエンコード（プログラム上はdecodeという表現になっているがエンコードだよね……）する
* Rawデータは300個ぐらいのデータだが、エンコードすると20個ぐらいになる。

送信するときは逆に:

* エンコードされたデータをデコードして
* それに従って38kHzでエミッターをON/OFFしてRawデータを発信する
    * 38kHz、すなわち26μsのうちにエミッターをHigh/Low、もしくはLow/Lowにしなければならない

このような原理だが、私のプログラムでは後述の理由で76kHzで制御している。

### タイマー制御 
タイマー割り込みの設定はArduinoと同じようにかけない気がしたので下記のように書いた。

``` .cpp
  void setup()
  {
    :
    // 76kHz
    hw_timer_t *timer = timerBegin(0, 21, true);
    timerAttachInterrupt(timer, &onTimer, true);
    timerAlarmWrite(timer, 50, true);
    timerAlarmEnable(timer);
    :
  }
```

MPUであるESP32が80MHzなので、50と21のスケーラーにすると76kHzになります。


### 受信処理
上記でonTimer()を割り込みハンドラーにしたので、SeeedStudioのプログラムのISR()でやっていたことをonTimer()で行います。
やっていることは上述のとおり、呼ばれるたびにレシーバーをdigitalReadしてMARKとSPACEに合わせ、下図のような状態変化をしていきます。

<img src="https://docs.google.com/drawings/d/e/2PACX-1vSY1bgiTY0YSG8gLnNH59Jn7-R05kgj9b04faJrEAL1R_uDlSv-cnSODK-8uVi-n48j1OVeXNNbxi3-/pub?w=237&amp;h=206">

すなわち、はじめがIDLEで、始めのMARK（たぶんリーダーとかそういうやつ）が来たらMARK状態に移行し、次がMARKならその長さをカウント、SPACEならSPACE状態に移行、SPACEの長さを数える……SPACEが長く続けばSTOPします。このMARKとSPACEでのカウントが赤外線信号のRawデータになります。RawデータのエンコードはArduino式のloop()で行います。
なお前述しましたが、レシーバーをGroveの口に接続した場合はGPIOは22番になりますのでそこをReadします。

### 送信処理
これが大変だった……。
基本的には上述のとおり受信の逆回しでデコードしてエミッターをチカチカさせるだけですが、13μs単位でチカチカさせなければなりません。PWMを使えばよさそうですが、M5StackでPWMの使い方がわからなかったので、76kHzでタイマー割り込みが来るたびにエミッターにdigitalWriteを掛けることで力技のPWM制御をしています。
ざっくり書くと下図のようなループをタイマー割り込みで進めています。

<img src="https://docs.google.com/drawings/d/e/2PACX-1vTndqqorT4FlMkI8nxuYZz_rxddxeJWQnPvTnPp7e1UHODlr3ZkCXYoC1nP4-Ft3vJBBwvL7tfgGPma/pub?w=265&amp;h=336">

そもそも物を知らぬ身としてはなぜ38kHzでチカチカさせないといけないのか謎で、始めはエミッターを光りっぱなしにしてたりしましたが、それだと更に不思議なことに信号が安定しない（受信がふらふらする）現象にぶちあたり、長い研究の末にチカチカさせることで信号が不思議と安定することがわかりました。

#### SeeedStudioのプログラムだと
PWMを使うので、タイマー割り込みとかなしにまさかのスリープの繰り返しをします。リアルタイム性がそれで保たれるのか不思議。

# 設置した様子（余談ですね）
机の上の棚に置くことにした。電源ピンは3.3V-5Vと書いてあるが、3.3Vじゃないとある程度遠くまで飛ばない様子だった。

<img src="https://img.pawoo.net/media_attachments/files/010/830/224/original/d34c72c672126518.jpeg" height="300">

> https://pawoo.net/web/statuses/101323053768742327

これであとは冒頭の目的のように、ネットワーク機能（WebAPI）と統合してネットワークで操作できるエアコンに仕立て上げます。

# 所感とか
* SeeedStudioのプログラムは酷いと思う。謎の定数が埋め込まれてたり同じようなdefineが２つあったり……
    + 私のプログラムも最悪かもだが……気が向いたらコメントとか書きます。はい。
* 途中で一度GrovePiを買って（Arduinoはなぜか買わなかった）試してみましたが、そもそも受信のサンプルコードがまともに動かせなかったです。
* そんなこんなでえらい苦労はしましたが、GroveにデジタルIOしたりタイマー割り込みを使ったり、いろいろ勉強にはなったです。
* なぜか38kHzを32kHzだと思ったりして話してたりします。間違いを見つけてもそういうことだと思って変換してください。

# （2019/3/16）データを集めて機械学習してみた
* これを作り始めた大目的はエアコン操作の自動化であり、データを集めやすいようにこれを作った。一冬過ごしてある程度データを蓄えたので、ようやくはやりの機械学習をやってみた。
* 実施内容自体はありふれてるし機械学習ネタの投稿はまじ多いのでGistにレポートを載せておくだけにしておく。
  + https://gist.github.com/hakua-doublemoon/520425be659abf1ea2dc608f67ebbc95