Web屋の電子工作DIY：夜更かしテレビを罰金性にして、早起き生活を手に入れる②

# 今回の内容
![夜更かしテレビ罰金制度導入ツール.jpg](https://qiita-image-store.s3.amazonaws.com/0/66816/c4c67d5b-2644-ae80-c6d6-80c114b6f234.jpeg)

以下の順番で記載中。

- [IoT、はじめました：プログラミング技術の海で上手に情報を拾う方法まとめ](http://qiita.com/xiao/items/0e179865ce8e6efd7393)
- [Web屋の電子工作DIY：夜更かしテレビを罰金性にして、早起き生活を手に入れる①](http://qiita.com/xiao/items/5f18d826f7811f29afd2)
- [Web屋の電子工作DIY：夜更かしテレビを罰金性にして、早起き生活を手に入れる 1.5](http://qiita.com/xiao/items/72addd5c61b93efbc3b9)

前回光センサーの値をPythonで取得していたのですが、せっかくなので使ってみたかったNode.jsに切り替えてWebサイトを作りたいと思います。
今回はNode.jsの導入〜光センサーの値を画面表示するところまでです。

# Node.jsフレームワークexpress+mongoDBを設定
設定の方法については色んなサイトに載っているのでそちらにまわします。
前回やったようにMacとRaspberryPiをLanで接続してターミナルで作業しました。
mongoDBの起動コマンドメモ：
> mongod --dbpath ~/tmp/mongodb --fork --logpath ~/tmp/mongodb/log

参考サイト・書籍：

- サイト：[Vagrant+node.js+express4+MongoDBで簡単なWebアプリを構築](http://webdesign-dackel.com/2015/09/29/vagrant-node-express4-mongodb/)

- サイト：[Raspbian用のmongo-dbインストール](https://github.com/tjanson/mongodb-armhf-deb/)

- 書籍：[『はじめてのNode.js』](http://www.amazon.co.jp/%E3%81%AF%E3%81%98%E3%82%81%E3%81%A6%E3%81%AENode-js-%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%82%B5%E3%82%A4%E3%83%89JavaScript%E3%81%A7Web%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E9%96%8B%E7%99%BA%E3%81%99%E3%82%8B-%E6%9D%BE%E5%B3%B6-%E6%B5%A9%E9%81%93/dp/4797370904)

# UIとDB構成を決めて作る
普通にサイトとか作る考えるのと同じですが、いちおうログ的に手順を残しておきます。

## 表示する内容を文字で書き出す
- 現在の状態+α
-- 待機中・・監視開始までの時間
-- 停止中・・停止した時間
-- 監視中・・現在ONの場合：残ON時間／現在OFFの場合は最後についていた時間

- 履歴
１日ごとの課金額

- 今までの総課金額

## UIをざっくり描いてみる
こんな感じで！やってることがうざ行為なのでUIのかわいさでカバーします。
![DSC_0574.JPG](https://qiita-image-store.s3.amazonaws.com/0/66816/f09a687c-04fb-058e-13a0-b1ad146db4e0.jpeg)

## DB構成を考える
### mongoDB
あんまり考える余地もないですが、せっかくなのでmongoDBについてはざーっと調べました。
RDBとだいぶ違っておもしろいですね。
・[MongoDBの薄い本](http://www.cuspy.org/diary/2012-04-17/the-little-mongodb-book-ja.pdf)
この本が知りたいことに一番答えてくれたように思います。

### コレクション（テーブル）決定
必要な情報は今のところ履歴のとこだけかなあと思うので、以下のように登録するようにします。
**pay_history**
> pay_date : String,
> pay_count : 32-bit integer

## 実装しなきゃいけない動作を確認
で、ここまで作って何しなきゃいけないんだっけ・・？を忘れかけたのでいったん実装をちゃんと言語化しました。

> ※RaspberryPiのcronで0:30に以下を起動するようにセット
> Loop開始
> 　光センサー：TVのON/OFF状態を取得
> 　重さセンサー：貯金箱の重さを取得 **★まだ**
> 　if (重さが変わっていたら)
> 　　15分のタイマーをセットして抜ける（１５分後にLoopを開始）
> 　　（！Webサイト更新：時間）
> 　else
> 　　TVがONの状態の場合はOFFにする（赤外線を利用） **★まだ**
> 　　if (1.5H以上OFFのまま OR 5:00を過ぎている)
> 　　　　Loopを抜けて終了する
> 　　　　（！Webサイト更新：ステータス）
> 　　else
> 　　　　3分のタイマーをセットして抜ける（3分後にLoopを開始）
> 　　　　（！Webサイト更新：時間）

あ、ちょっといろいろ遠いと今更気づく。。

# Web画面を作る
こういうときはテンションのあがるところから着手します。
というわけで、画面のviewを作成しました。

## Bootstrapを導入してViewを作成
[公式サイト](http://getbootstrap.com/)からBootstrapをローカルにダウンロードします。
linuxコマンドでダウンロードしたファイルをRaspberryPiに転送します。
> scp (ローカルファイル) pi@(IPアドレス):(あげたいディレクトリ)

Node.jsのviewsの中でマークアップしていきます。

> /views
> ├── index.ejs
> └── partials
> 　　├── header.ejs
> 　　└── footer.ejs
> /public
> ├── css
>｜　├── style.css
>｜　└── bootstrap.min.css
>｜
> ├── js
>｜　　├── bootstrap.min.js
>｜　　├── [footerFixed.js](http://blog.webcreativepark.net/2007/11/16-012253.html)
>｜　　└── jquery-1.12.3.min.js
>｜
> └── images
>  　　└── 使った画像


引っかかったところ：

- ejsでHTML埋め込むのにprintとかechoとか使ったらコンパイルエラーになった。。
地味に悩んだのですが、結論は「<%- （html）%>」で埋め込み可能です。

- 基本的にターミナルでの作業になるので、今更ながらVimをちゃんと使ってみることにしました。
地味に難しくて時間食った・・でも慣れると愛せるエディタな気がします。
チートシート（と、シートを貼るために初めて使ったマスキングテープ）が良い仕事する。
参考サイト：[僕がサクラエディタからVimに乗り換えるまで](http://blog.jnito.com/entry/20120101/1325420213)

最終的にこんな感じに。
画像はfirealpacaというフリーソフトで適当に書きました。
色はなんとなくガチャ●ンと同色です。
![スクリーンショット 2016-04-08 14.45.15.png](https://qiita-image-store.s3.amazonaws.com/0/66816/e04cee62-ce80-1f84-01c5-e6e7abc1656c.png)

# 光センサーの値を取得して表示する
長くなったので終わりにして次の記事にいきたい気持ちなのですが、
これだけで終わるとWebの解説になってしまうので電子工作に戻ります。。

参考サイト：[Arduino UNO R3でとったセンサーデータをC3.jsでグラフ表示する](http://qiita.com/itagakishintaro/items/7b345a63a046c511d128)

## Socket.ioの導入
package.jsonにいくつか追加して、npm installを実行します。
いちおうdependenciesだけ載せておきます。

```json:package.json
  "dependencies": {
    "ejs": "~2.3.3",
    "express": "~4.13.1",
    "express-session": "^1.13.0",
    "johnny-five": "*",
    "mongoose": "^4.4.10",
    "serialport": "*",
    "socket.io": "*"
  }
```

## もろもろ変更

引っかかったところ：

- **Device or Firmware Error A timeout occurred while connecting to the Board.**
というエラーがでて結構長いこと引っかかり、、以下２点を実施することで解決しました。
・スケッチをFirmataの例に書き換える
・以下が出たのを確認してからアクセスする
　　Device(s) /dev/ttyACM0
　　Connected /dev/ttyACM0
　　Repl Initialized

- Express×Socket.ioによるファイルの分割方法に迷って。。
基本的に[『はじめてのNode.js』](http://www.amazon.co.jp/%E3%81%AF%E3%81%98%E3%82%81%E3%81%A6%E3%81%AENode-js-%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%82%B5%E3%82%A4%E3%83%89JavaScript%E3%81%A7Web%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E9%96%8B%E7%99%BA%E3%81%99%E3%82%8B-%E6%9D%BE%E5%B3%B6-%E6%B5%A9%E9%81%93/dp/4797370904)に従いました。
ただExpress3の書き方になっているので、4に合わせてちょっと変更する必要があります。

※以下、主要なところだけ。。node app.jsで実行します。

```js:app.js
var http = require('http'),
    express = require('express'),
    routes = require('./routes/index'),
    path = require('path'),
    arduino = require('./sockets/arduino'),
    app,
    server,
    io;

app = express();
app.set('port', process.env.PORT || 3000);
app.set('view engine', 'ejs');
app.set('views', __dirname + '/views');

app.use(express.static(__dirname + '/public'));

app.get('/', routes);

server = http.createServer(app);
io = require('socket.io').listen(server);

server.listen(app.get('port'), function(){
  console.log("Express server listening on port " + app.get('port'));
});

io.sockets.on('connection', arduino.onConnection);
```


```js:models/Arduino.js
var five = require('johnny-five');
var board = new five.Board();
var sensor;

module.exports = function(socket){
  board.on('ready', function() {
    sensor = new five.Sensor({
        pin: 'A0',
        freq: 100
    });
    board.repl.inject({
        pot: sensor
    });
    
    sensor.scale(0, 100).on('data', function() {
      var value = this.value;
      if(socket){
        socket.emit('get A0', { value: value });
      }
    });
  });
};
```

```js:routes/index.js
var express = require('express');
var router = express.Router();
var CalendarView = require("../models/CalendarView");
var ArduinoTest = require("../models/ArduinoTest");

/* GET home page. */
router.get('/', function(req, res, next) {
    var ar = new ArduinoTest();
    var clview = new CalendarView(new Date);
    res.render("index", {
      title: "夜更かしテレビは罰金性",
      clview: clview.getView(),
      ar: ar.getData()
    });
});

module.exports = router;
```

```js:sockets/arduino.js
var arduino = require('../models/Arduino.js');

exports.onConnection = function (socket) {
  arduino(socket);
};
```

```js:public/socket-arduino.js
(function(){
  var socket;

  $(document).ready(function () {
     socket = io.connect();

     socket.on('connected', function(data) {
     });

     socket.on('get A0', function(data){
      $('#arduino-test').html(data['value']);
     });
  });
})();
```

```html:views/header.ejs
（前略）
    <script type="text/javascript" src="/socket.io/socket.io.js"></script>
    <script>
      var socket = io();
    </script>
    <script type="text/javascript" src="/js/socket-arduino.js"></script>
（略）
```

# 蛇足な感想
いったりきたりして読みにくい記事かもと思うけれど、書きながら実践すると「やりきらなきゃ！」と思えたり、どこで見てこうしたんだっけ・・？と忘れていたことなんかを思い出せたりして予想以上に便利です。（自分の記憶って本っ当に頼りにならない・・）
まだまだがんばるぞ。