はじめてのElectronとESP-WROOM-02でカミワザ！！

# やりたいこと
最近うちの子どもが「[カミワザワンダ](http://www.tbs.co.jp/anime/kamiwaza_wanda/)」という、妖怪ウォッチ的な~~パクリ~~アニメにハマっております。このアニメ、妖怪ウォッチの妖怪メダルのように、主人公がカミワザプロカというカードを集めるのですが、もちろんカミワザプロカも現実世界でも[おもちゃ](http://www.takaratomy.co.jp/products/kamiwazawanda/toy/index2.html#toyNav)として売られております。このカード、RFIDが埋め込まれているんですね（妖怪メダルはQRコード？）。
そこで、子どもの心を鷲掴みにするべく、カミワザプロカのRFIDを読み取って、対応するキャラクターをMacの画面にかっこよく表示するアプリを作りたいと思います。このアプリ、「カミワザ図鑑」と勝手に名付けましょう。せっかくなので、ずっと気になっていたElectronでデスクトップアプリとして作りたいと思います。イメージ的にはこんな感じです。

![arch.png](https://qiita-image-store.s3.amazonaws.com/0/160451/31ff3acb-f93d-ec3a-274d-d6d678e64fa2.png)


# 用意するもの
まず、RFIDを読み取るために簡単な電子工作が必要です。RFIDリーダーとElectronアプリとの間はWiFi経由で通信しますので、WiFiが簡単に使えるESP-WROOM-02を選びました。
 - [ESP-WROOM-02開発ボード](https://www.switch-science.com/catalog/2500/) 
 - [RFID-RC522](http://amzn.asia/dX52205)
 - ブレッドボード、ジャンパワイヤー、LED

# 作成手順
## RFIDリーダ組み立て
まず、ESP-WROOM-02とRFID-RC522で自作RFIDリーダを組立てます。以下の参考URLを真似して、ブレッドボード上にジャンパワイヤを差していくだけです。（RC522のピンだけ半田付け必要）
![IMG_6545.JPG](https://qiita-image-store.s3.amazonaws.com/0/160451/dea29dee-a6bc-ad44-945d-ea2155b5ed1c.jpeg)


 * 参考URL
[ESP-WROOM-02とRFID-RC522で非接触Lチカ](http://qiita.com/daxanya1/items/b9c2b971a946c8bf063e#参考url)

## ESP-WROOM-02のプログラム
次に、RFIDリーダの動作をプログラミングします。RFIDリーダーにRFIDをかざすと、そのUUIDをElectronアプリにHTTPで送信するようにします。
ESP-WROOM-02の開発はArduinoIDEで行えます。[ArduinoIDE](https://www.arduino.cc/en/main/software)をダウンロード＆インストールしたら以下の参考URLに従ってセットアップしましょう。

 * 参考URL
[ESP-WROOM-02開発ボードをArduino IDEで開発する方法](http://trac.switch-science.com/wiki/esp_dev_arduino_ide)

RFIDを読み取る処理は先ほどの「[ESP-WROOM-02とRFID-RC522で非接触Lチカ](http://qiita.com/daxanya1/items/b9c2b971a946c8bf063e)」のコードをそのまま流用し、RFIDのUIDを取得する処理は「[Arduinoで複数のNFCタグを区別する](http://qiita.com/yuji_miyano/items/f00f9e3f2edc8025b704)」から以下のコードを流用させていただきました。

```c:read.c
  String strBuf[mfrc522.uid.size];
    for (byte i = 0; i < mfrc522.uid.size; i++) {
      strBuf[i] =  String(mfrc522.uid.uidByte[i], HEX);  // (E)using a constant integer
      if(strBuf[i].length() == 1){  // 1桁の場合は先頭に0を追加
        strBuf[i] = "0" + strBuf[i];
      }
    }
```

流用しかありませんが、一応ソースを[Github](https://github.com/vimyum/arduino/tree/master/rfid)にあげました。

## アプリのプログラム
### Node.jsインストール
ようやく本題。アプリはElectronで作ります。Electronアプリ開発のために、まずはNode.jsのインストール。Macであればbrewで簡単にインストールできます。

```bash:
$ brew install node
```

### Electronインストール
続いてElectronのインストールです。npmで簡単です。

```
$ npm -g install electron-prebuilt
```

### その他ライブラリインストール
これで最低限の環境が整いました。今回、ElectronアプリはRFIDリーダからのHTTPリクエストを受け付けるので、WebアプリケーションフレームワークであるExpressもインストールします。HTTPリクエストを受け付けるだけなのに少々大げさな気もしますが、便利なので使います。
また、表示部のバインディングにVue.jsを使ってみたいと思いますのでこれもインストールします。

```
$ npm install --save express vue
```
### ExpressによるHTTPリクエスト受け付け

Electron内でExpressを立ち上げるには、Expressをロードしてlistenメソッドを呼びだします。
空のWindowだけ立ち上げて、Expressでリクエストを受け付けるサンプルは以下になります。

```js:main.js
/**
 * メインプロセス
 */   
const electron = require('electron');
const Express = require("express");
const express = Express();
    
let app = electron.app;
let BrowserWindow = electron.BrowserWindow;
  
app.on('ready', function() {
  mainWindow = new BrowserWindow({});
  /**
   * 画面にHTMLファイルを表示したい場合はloadURLで読み込む
   */
  // mainWindow.loadURL('file://' + __dirname + '/index.html');
  
  mainWindow.webContents.on('did-finish-load', () => { 
      //load後に実施したい処理はここに書く
  }); 
  
  mainWindow.on('closed', function() {
    mainWindow = null;
  });
});

/**
 * Expressのセットアップ
 */
express.listen(3000, "localhost");
express.get('/:id', function(req, res, next) {
    /**
     * "GET /123"のリクエストを受信すると
     *  req.params.idに123が入る
     */
    console.log("Request Param is " + req.params.id);
    res.sendStatus(200);
}); 
```

起動は、`$ electron main.js`です。ウィンドウが立ち上がったら、ブラウザから`http://local:3000/Hello`とか入力して、200応答が返ってきたら成功です。

### プロセス間通信

Electronはメインプロセスと、描画を担当するレンダラープロセスに分かれているようです。上のサンプルでは、main.jsがメインプロセスで、loadURLでロードされたHTMLファイルに記述されたJavaScriptがレンダラープロセスで動作するイメージです。Expressはメインプロセスで動いているので、Epxressが受け取ったHTTPリクエストの情報を画面に反映するには、レンダラープロセスへのプロセス間通信(IPC)が必要になります。
IPCは最近仕様が変わったようで、動かないサンプルの記事に苦しめられました。Electronバージョン1.4.3では以下のやりかたのようです。

#### メインプロセスからレンダラープロセスに通信

```js:main.js
mainWindow = new BrowserWindow({});
// 'keyword'で待ち受けるrenderer側に第２引数(Hello)を送信
mainWindow.webContents.send('keyword', 'Hello');
```

```js:renderer.js
const ipcRenderer = require( 'electron' ).ipcRenderer;
// 'keyword'宛のメッセージを待ち受ける
ipcRenderer.on('keyword', function(ev, msg) {
    // メッセージを受け取ったあとの処理
});
```

### 音を鳴らす
IPCでメインプロセスからRFIDの情報を受け取ったら、レンダラープロセス側ではVue.jsによって画面表示を切り替えるとともに、RFIDに対応するカッコいいサウンドを鳴らします。音を鳴らすのにはHTML５のaudioタグを使いました。

```html:index.html
<div id="app" class="audio">
    <p>{{sound}}</p>
    <audio id="audio" controls>
        <source :src="sound"/>
    </audio>    
</div> 
```

vue.jsで`{{sound}}`の値を切り替えるだけでは再生しないので、コントローラからload()とplay()が必要です。load()を忘れてしばらくはまりました。

```js:renderer.js
app.sound="./audio/sound.mp3"; //Viewの書き換え
audio.load(); // 再読み込み ★ここ重要
audio.play(); // 音を鳴らす
```

### デバッグ
レンダラープロセスが思う通りに動かない時に、デバッグのために差し込んだconsole.logがどこにも表示されず困りました。
結論としては、メインプロセス側で`openDevTools()`を呼んでおくと開発ツールっぽいのが表示されて、コンソールログも無事に見れるようになりました。

```js:main.js
let BrowserWindow = require('electron').BrowserWindow;
mainWindow = new BrowserWindow({width: 800, height: 680});
mainWindow.webContents.openDevTools(); //←ここ
```

### その他ハマったところ
Electron上でHTMLのinputフォームを作ったのですが、なにも入力できない状態になりました。どうやら、tmuxからElectronを起動しているのが原因だったようです([#773](https://github.com/electron/electron/issues/733))。tmuxを経由しないでElectronを起動したところ、無事に入力できるようになりました。

### できあがり

<blockquote class="twitter-video" data-lang="en"><p lang="ja" dir="ltr"><a href="https://t.co/mr788cc25V">pic.twitter.com/mr788cc25V</a></p>&mdash; saga (@vimyum) <a href="https://twitter.com/vimyum/status/835318538399170560">February 25, 2017</a></blockquote> <script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

ソースコードは[Github](https://github.com/vimyum/kamiwaza-viewer/tree/master)にあげてます。画像や音声ファイル、カード説明文は著作権に引っかかると思うので、削除してます。