ESP-WROOM-02とRFID-RC522で非接触Lチカ

カードかざしたらピコーンと鳴らせてみたいと思ったのですが、鳴らすためのブザー用意するのも面倒だったので、カードかざしたらピカ！と光るLチカ(LEDをチカチカさせる)に挑戦してみました。

# 調査

## カードかざしたら反応するモジュールはどれ？
Suicaの技術あたりから攻めるのがよさそうなので、「Suica 仕様」で検索すると、「非接触型IC」という言葉が出てきました。 
「非接触型IC　チップ」で検索してみると、どうも「RFID」という技術ということです。

電子工作絡みで検索する時、「自作」というキーワードを入れると鉄板です。
今回も「RFID 自作」で検索すると、それっぽいのがいっぱい出てきました。

電子工作系は、古いと2000年の歴史、ではなくて西暦2000年頃からの記事もあって、現在も手に入るのかどうかよくわからない内容もあります。金額と調達可能性、開発の簡単さ（本当の難易度は実際やってみないとわからないので、加工しなくていいかとか、部品点数が少ないかとかをみる）を考慮して、モジュールを選定します。

調査していくと、Arduinoと組み合わせてRFIDでLチカがそこそこ見つかりました。実績があるRFID-RC522モジュールがよさそうです。

### 参考URL
+ [今日の工作：ArduinoでRFIDのタグ識別](http://masafuro.hatenadiary.jp/entry/2015/06/10/023110)
+ [Arduinoで近距離無線通信 RFID-RC522 NFC](http://www.geocities.jp/bokunimowakaru/diy/arduino/rfid.html)

## Wifiとも接続したいよ
Lチカできたら、その後はカードかざしたらインターネット接続して何かしたいよね、ということで将来を見据えてWifiモジュールで単体でArduinoとして使える、ESP-WROOM-02と組み合わせることにしました。

※今回の記事では、最終的にESP-WROOM-02上でLチカさせてますが、Wifiとして外部にHTTPリクエスト送ったりしていません。

## どうESP-WROOM-02とRFID-RC522を接続するの？
物理的につなげても、両方のモジュールがお話できないといけません。RFID-RC522の通信仕様をみると、SPI/UART/I2Cと書いてあるのですが、SPI以外の通信方法が可能かどうかわからなかったので、SPIで通信することにしました。（サンプルもあるので）

SPI通信のピンを確認すると、SS/MOSI/MISO/SCKの4本が最低必要なようです。また使っているかどうかわからないのですが、リセット用の信号線もRFID-RC522に接続されているので用意します。

ESP-WROOM-02のブレークアウトボードのピン設定の詳細がいまいち確認できないのですが、どうもmikroBUS規格互換だということで、そちらを見るのがよさそうということに気づきました。（なので+5VのところがESP-WROOM-02では無効化されている）

### 参考URL
+ [SPIモジュール（Serial Peripheral InterfaceModule）の使い方](http://www.asahi-net.or.jp/~gt3n-tnk/SPI1.html)
+ [mikroBUS™ socket](http://www.mikroe.com/mikrobus/)

# 用意するもの
## モジュール
+ ESP-WROOM-02＋ブレークアウトボード [マイクロテクニカ](http://www.microtechnica-shop.jp/shopdetail/000000000039/)
+ RFID-RC522 [Amazon 検索:RC522](http://www.amazon.co.jp/s/field-keywords=RC522)
+ AE-FT234X [秋月電子通商](http://akizukidenshi.com/catalog/g/gM-08461/)

※(リンク先はたぶん今回説明するのと同等品。購入時の参考まで)
## その他(今回使用したもの)
+ ブレッドボード（長）
+ ブレッドボード用　ジャンパーワイヤ各種
+ USBケーブル(PCと接続できる端子と、AE-FT234Xと接続できる端子が両端にあるケーブル）
+ 発光ダイオード(赤) x 2 (電源確認用と、Lチカ用)
+ 1KΩ抵抗 x 2 (LED用 ※抵抗の値は発光ダイオードの許容範囲にあわせて適宜調整してください)
+ 0.1μF積層セラミックコンデンサ x 1 (パスコン用)
+ 出力DC3.3V スイッチング電源(AC100Vコンセントから安定的に電源取得するため)
+ スイッチング電源用DCジャック

## 開発環境
+ MacBook Air (OS X Yosemite)
+ Arduino IDE 1.6.5

# 作業手順
1. Arduino IDEにESP-WROOM-02の開発ボード追加を行います
    + こちらを参考にしてください→ [技適済み格安高性能Wi-FiモジュールESP8266をArduinoIDEを使ってIoT開発する為の環境準備を10分でやる方法](http://qiita.com/azusa9/items/264165005aefaa3e8d7d)
    + Arduino IDEのメニューの「ツール」から、ボードを「Generic ESP8266 Module」に設定します。設定はデフォルトのままで変更していません。
2. Arduino IDEにRC522のライブラリを追加します
    + [MFRC522:github](https://github.com/miguelbalboa/rfid)から、一式をZIPダウンロードします。
    + Arduino IDEのメニューの「スケッチ」→「Add .ZIP Library..」から、ダウンロードしたファイルを選択します。
    + Arduino IDEのメニューの「ファイル」→「スケッチの例」の下の方に「MFRC522」という項目が追加されていたら追加完了です。
3. ブレッドボード上に配線します
    + ESP-WROOM-02モジュールの下の空間を利用できると、配線がすっきりします
    + 発光ダイオードは向きがあるので注意
4. USBケーブルでMacとAE-FT234Xを接続します
    + Arduino IDEのメニューの「ツール」から、ポートを「/dev/cu.usbserial～」にします。（環境によって～の後がいろいろ変わるようです）
5. プログラムをコンパイルしてESP-WROOM-02に書き込みます。
    + ESP-WROOM-02側のモードを書き込みモードにしないと、Arduino IDEで書き込めない旨のエラー表示となります
    + USBからは電源供給せずに、3.3Vのスイッチング電源側から供給しますので、USBとは別にスイッチング電源を接続して通電状態としてください(少なくともESP-WROOM-02には電源が供給されていないと書き込めません)
    + MFRC522ライブラリをESP-WROOM-02デバイスで使おうとすると、コンパイル時に、「WARNING: library MFRC522 claims to run on [avr, STM32F1] architecture(s) and may be incompatible with your current board which runs on [esp8266] architecture(s).」といって、警告が出ます。出ますが、コンパイル通るし動くので無視しました。
6. Arduino IDEがプログラム書き込んだ後、自動的にリセットされてESP-WROOM-02がフラッシュROMから立ち上がります。なので、そのままでも動きます。USBからのケーブルは抜きたい人は、一度電源切って、USB周りのケーブル抜いて、フラッシュROM起動モードにしてから再度電源ONにしてください。

# ブレッドボードに全部載せた
左上の3本ぐらい明後日の方向に伸びているのが、AE-FT234Xにつながっています（小さすぎてUSBのケーブルの重さでブレッドボードから離れてしまうので、あえて外しています）

これはプログラムの書き込みとシリアルコンソールでのデバッグに使うだけなので、動作時はこの明後日の方向に伸びているケーブルは外しています。

ちなみに電源はスイッチングアダプタから直供給で、電源スイッチとかはないです。

![IMG_0317.png](https://qiita-image-store.s3.amazonaws.com/0/32778/3b3b8419-d781-3190-eeee-428ee5aefd6b.png)

# 動かしてみた
実際に動かしてみました。RFID-RC522買ったらついてきたカードをかざすとカードが検出されます。

横方向に外れた場合にLEDが光らなくなる（カード未検出となる）のは理解しやすいですが、縦方向も10cm程度離すとLED光りませんでした。
![esp-wroom-02_with_rfid_rc522_2_anime.gif](https://qiita-image-store.s3.amazonaws.com/0/32778/c19913e2-27d3-1a83-43cd-0fca71b7b181.gif)

# 回路図
電子回路作ったら清書しましょうという教えに従い、作成しました。
[回路図エディタBSch3V](http://www.suigyodo.com/online/schsoft.htm)利用させて頂きました。とても使いやすいですね。
![esp-wroom-02_with_rfid_rc522_2.PNG](https://qiita-image-store.s3.amazonaws.com/0/32778/71db9a4d-04a8-9773-eb83-f80f78a54d3c.png)

# プログラム(Arduino)
ESP-WROOM-02のフラッシュROMを書き込みモードにして、Arduino IDEから突っ込むプログラムは以下になります。

+ SSピンと、REESTピンは自由に変更可能です。変更可能なのですが、ESP-WROOM-02のGPIO15ピン使おうとすると、どうも挙動があやしいので、GPIO15ピンは使わない方がいいかもしれません。（ジャンパー設定で使っているからかもしれませんし、ESP-WROOM-02のリセット挙動に使うピンらしいのでそのせいかもしれません）
+ 先ほども書きましたが、WifiでIPアドレス取得しているだけで、HTTP通信とかしていません。ただ、IPアドレスは取得しているので、SSIDとパスワードは必要です（不要なら削ってください）

```cpp:ESPWROOM02_RC522.ino
/*
 * ESP-WOORM-02とMFRC522を接続する
 * 2015/10/12
 * 
 * 配線(SPI通信)：
 * 　ESP-WROOM-02 : MFRC522
 *        GPIO 5 -> SDA(1) - ESP-WROOM-02側はSS_PINで定義
 *        GPIO14 -> SCK(2)
 *        GPIO13 -> MOSI(3)
 *        GPIO12 <- MISO(4)
 *                  RQ(5)   - 未使用
 *                  GND(6)  - GNDにつなぐ
 *        GPIO16 -> RST(7) - ESP-WROOM-02側はRST_PINで定義
 *                  3V3(8)  - 電源につなぐ
 * 
 * LED:   GPIO 4
 * 
 */
#include <SPI.h>
#include <MFRC522.h>
#include <ESP8266WiFi.h>

#define RST_PIN 16 // MFRC522用リセットピンGPIO NO
#define SS_PIN  5  // MFRC522用SS(SDA)ピンGPIO NO

MFRC522 mfrc522(SS_PIN, RST_PIN); // MFRC522のインスタンスを作成

#define CARD_PRESENT_PIN 4 // MFRC522にカードが検知されたら光らせるGPIO NO
bool cardset;     // MFRC522にカードが検知されているかどうか
int timeoutcount; // MFRC522からカードが連続で離れた回数を記録

const char* ssid = "xxxxxxxx";         // ESP-WROOM-02用 Wifi SSID(各自設定してください)
const char* password = "xxxxxxxxxxxx"; // ESP-WROOM-02用 Wifi パスワード(各自設定してください)

void setup() {
  // UART接続初期化
  Serial.begin(115200); // UARTの通信速度を変更したい場合はこちらを変更
  delay(10);

  // カード検出用のGPIO初期化
  pinMode(CARD_PRESENT_PIN, OUTPUT);
  digitalWrite(CARD_PRESENT_PIN, LOW);

  // MFRC522用変数初期化
  cardset = false;
  timeoutcount = 0;

  // MFRC522のスケッチのDumpInfoを参考にした。ref) https://github.com/miguelbalboa/rfid
  SPI.begin();          // SPI初期化
  mfrc522.PCD_Init();   // MFRC522初期化
  ShowReaderDetails();  // MFRC522 Card Readerのバージョンを返す。00かFFなら配線間違いの可能性
  
  // とりあえずWifiにつなぐ
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  
  // Print the IP address
  Serial.println(WiFi.localIP());
}

void loop() {
  // カードの検出
  if ( !mfrc522.PICC_IsNewCardPresent()) {
    // 検出されなかった場合の処理
    if (cardset) {
      // すでにカードが検出されていた場合で、連続４回未検出になったらLEDをLOWにする
      // 検出後、タイムアウトと検出を繰り返すのでその対策 
      if (timeoutcount > 4) {
        digitalWrite(CARD_PRESENT_PIN, LOW);
        Serial.println("LED LOW");
        cardset = false;
        timeoutcount = 0;
      } else {
        // ４回以内なら連続未検出回数を増やす
        timeoutcount++;
      }
    }
    delay(5);
    return;
  }

  // カードが初めてor改めてセットされた場合、LEDをHIGHにする
  if (!cardset) {
    digitalWrite(CARD_PRESENT_PIN, HIGH);
    Serial.println("LED HIGH");
    cardset = true;
  }
  timeoutcount = 0;
  delay(100);
}

 // MFRC522のスケッチのDumpInfoのまま。ref) https://github.com/miguelbalboa/rfid
 // LICENSE:https://github.com/miguelbalboa/rfid/blob/master/UNLICENSE
void ShowReaderDetails() {
  // Get the MFRC522 software version
  byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg);
  Serial.print(F("MFRC522 Software Version: 0x"));
  Serial.print(v, HEX);
  if (v == 0x91)
    Serial.print(F(" = v1.0"));
  else if (v == 0x92)
    Serial.print(F(" = v2.0"));
  else
    Serial.print(F(" (unknown)"));
  Serial.println("");
  // When 0x00 or 0xFF is returned, communication probably failed
  if ((v == 0x00) || (v == 0xFF)) {
    Serial.println(F("WARNING: Communication failure, is the MFRC522 properly connected?"));
  }
}
```

# 免責事項
回路図どおり、プログラムどおりに作っても動かなかったらごめんなさい。無保証です。

# おまけ
今回「動かしてみた」で使った動画は、デジタルカメラの動画モードで撮影→ffmpegで変換してアニメーションGIFとして作成しました。GUIで編集するよりも手軽で綺麗なアニメーションGIFが作れました。

   1. 入力動画を横320、縦は入力動画のアスペクト比に合わせて縮小
      + `$ ffmpeg -i input_origin.MP4 -vf scale=320:-1:flags=lanczos -strict -2 -y input_mini.mp4`
   2. 今回の動画で抽出したい部分を抽出するため、開始16秒から4秒間分だけ出力する
      + `$ ffmpeg -i input_mini.mp4 -ss 16 -t 4 -strict -2 -y input_mini_16_4.mp4`
   3. 最適化されたパレット画像を生成(fpsを10に設定)
      + `$ ffmpeg -i input_mini_16_4.mp4 -vf fps=10,palettegen=stats_mode=diff -y mp4_palette.png'
   4. パレット画像を利用してアニメーションGIFを生成(fpsを10に設定)
      + `$ ffmpeg -i input_mini_16_4.mp4 -i mp4_palette.png -lavfi fps=10,paletteuse -y output.gif`

動画フォーマットは圧縮が効いているのですが、アニメーションGIFにするとファイルサイズが肥大化するので、1Mbyte以内に収めようと思うと、5秒程度が限界ですね。

### 参考URL
+ [動画ファイルからより良いアニメーションGIFを作る](http://genjiapp.com/blog/2015/05/05/generating-much-better-animated-gif-from-movie-file.html)