Edison+Arduino上で超音波距離センサモジュールHC-SR04をnodejsとmraaを使って動かしてみる


こんな感じになる
![IMG_0518.jpg](https://qiita-image-store.s3.amazonaws.com/0/18766/773fa6cf-a586-f1ef-9eae-631fb2e9f0f5.jpeg "IMG_0518.jpg")

ちなみに、[Edison Intelに乗せる前にArduinoで超音波距離センサモジュールHC-SR04を使って人がある範囲を通過したらカウントするというデバイスを作ってみた](http://qiita.com/kiyotaman/items/82620ba1e90de3df4073)の続き。

Edison用のOSの選択肢は幾つかあるが、Webサーバーが最低限動けば良いので、nodejsが標準搭載されている[Yocto](https://www.yoctoproject.org)を使うことにする。セットアップは[Intel Edison How to](http://www.intel.com/support/maker/edison.htm#how)も参考になる。

## 前提

* Edisonのセットアップが済んでいること ([参考ページ](http://www.intel.com/support/edison/sb/CS-035344.htm))([時間合わせ](http://qiita.com/CLCL/items/e991e23f4bdbca5ff28b))
* Yocotのセットアップが完了していること ([参考ページ](https://communities.intel.com/docs/DOC-23193))
* Wifiの設定が終わっていること ([参考ページ](http://www.intel.com/support/edison/sb/CS-035342.htm))
* Intel® XDK IoT Editionの準備が終わっていること ([参考ページ](https://software.intel.com/en-us/html5/xdk-iot))（使わなくても良いが、一応）

## 準備

nodejsがデフォルトで準備されているのでそれを使うことにする。`libmraa`も使うことになるので`opkg`を使ってまずはパッケージのアップデートを行う。[Update libraries on Edison](https://software.intel.com/en-us/node/532592)に従ってやってみる。

### Edisonの設定

* configure_edisonで行う

```shell
$ configure_edison --help
```

###Wifiの確認

```shell
$ ifconfig
ifconfig | grep -A1 wlan0
wlan0     Link encap:Ethernet  HWaddr 78:4b:87:a7:86:eb
          inet addr:192.168.10.17  Bcast:192.168.10.255  Mask:255.255.255.0
```

192.168.10.17にブラウザーで接続してみる。うまくいかない場合、以下をしてみると良いらしい。こんな感じで表示された。

![Screen Shot 2015-02-22 at 4.09.44 PM.png](https://qiita-image-store.s3.amazonaws.com/0/18766/eba10c74-6c9f-c3b9-a19c-181b4466eebd.png "Screen Shot 2015-02-22 at 4.09.44 PM.png")

###パッケージのアップデート

```shell
# as a root
$ echo "src intel-iotdk http://iotdk.intel.com/repos/1.1/intelgalactic" > /etc/opkg/intel-iotdk.conf
$ opkg update
$ opkg upgrade
```

###XDKの準備

####接続する予定のIPを追加

```shell
$ xdk-whitelist --add 192.168.10.14 # 同一ネットワーク内の自宅PCIP
```

*うまく接続できない場合以下をやってみると良いらしい*

```shell
$ systemctl enable xdk-daemon
$ systemctl restart xdk-daemon
```

*XDKからアップしたプロジェクトはedisonの以下に格納される*

```shell
$ ls /node_app_slot
```

*直接サーバー上でごにょごにょしてもよい*

###mraaの準備

* MRAA Github Repository: https://github.com/intel-iot-devkit/mraa

```shell
$ echo "src mraa-upm http://iotdk.intel.com/repos/1.1/intelgalactic" > /etc/opkg/mraa-upm.conf
$ opkg update
$ opkg install libmraa0
```

###johnny-five

入れなくても良いが試しに入れてみた

* johnny-five https://www.npmjs.com/package/johnny-five
* この辺りも参考になるのかな https://github.com/rwaldron/galileo-io/

####インストール

```shell
$ npm install johnny-five
```

*export NODE_PATHをして、NODE_PATHにjohnny-fiveのnode_moduleを追加しておく*

```shell:例
$ echo $NODE_PATH
/usr/lib/node_modules/:/home/root/.node_app_slot/node_modules/johnny-five/node_modules/
```

#### LED Blinkテスト

```node:blink_test.js
var five = require("johnny-five");
var Edison = require("galileo-io");
var board = new five.Board({
  io: new Edison()
});

board.on("ready", function() {
  var led = new five.Led(13);
  led.blink();
});
```

実行

```shell
$ node blink_test.js
```

正しくブリンクすればOK。

###センサーのドキュメント

ドキュメントがあったので一応目を通してみた。10us（microseconds)TTLをシグナルピンに投げると、センサーが8回 40kHz Pulse投げてecho back受けるっぽい。当然ながらドキュメントに色々と情報あることを学んだ。

* https://docs.google.com/document/d/1Y-yZnNhMYy7rwhAgyL_pfa39RsB-x2qR4vP8saG73rE/edit#

```
The timing diagram of HC-SR04 is shown. To start measurement, Trig of SR04 must receive a pulse of high (5V) for at least 10us, this will initiate the sensor will transmit out 8 cycle of ultrasonic burst at 40kHz and wait for the reflected ultrasonic burst. When the sensor detected ultrasonic from receiver, it will set the Echo pin to high (5V) and delay for a period (width) which proportion to distance. To obtain the distance, measure the width (Ton) of Echo pin. 

Time = Width of Echo pulse, in uS (micro second)
Distance in centimeters = Time / 58
Distance in inches = Time / 148
Or you can utilize the speed of sound, which is 340m/s
```

![Screen Shot 2015-02-23 at 12.01.38 AM.png](https://qiita-image-store.s3.amazonaws.com/0/18766/ea0a8025-0ea3-5dfb-fd1d-db7c71031176.png "Screen Shot 2015-02-23 at 12.01.38 AM.png")


###hc_sr04 javascript版サンプルソース

**microseconds**をインストールしておく

```shell
$ npm install microseconds
```

とりあえず自分の書いた[Arduino版ソース](http://qiita.com/kiyotaman/items/82620ba1e90de3df4073#サンプルスケッチ)を参考にやっつけで書いてみた。最初`pulseIn`をどう表現するか迷ったがなんとかいけたっぽい。ただ、センサーの情報が時々期待通り帰ってこなかったりする。

多分、物理的な問題があるのだろう。オーバーヘッドがあったりするのかな。専門じゃないからわからないけど、microsecondsモジュールのおかげでなんとか動いている感じ。

johnny-fiveでもやってみたが、イマイチうまく動かなかったのと、mraaの方がArduinoからのソースを書き換えるだけで済むのでmraaを使うことにした。

```javascript:hc_sr04.js
var m = require('mraa');
var μs = require('microseconds');

var echoPin = new m.Gpio(12);
var trigPin = new m.Gpio(11);
var LEDPin = new m.Gpio(13);

var defaultDistance = 0; // centimètre 
var prevDistance = 0;
var counter = 0;
var adjustment = 3; // centimètre
var lock = false;
var maximumRange = 500; // Maximum range needed
var minimumRange = 3; // Minimum range needed
var unlockReadyCounter = 0;
var unlockReadyCounterAdjusment = 0;
var loopDelay = 60; // ms
var LOW = 0;
var HIGH = 0;

trigPin.dir(m.DIR_OUT);
echoPin.dir(m.DIR_IN);
LEDPin.dir(m.DIR_OUT);
LEDBlink(3);

// sleep microseconds
function usleep(us) {
    start = μs.now();
    while (true) {
        if (μs.since(start) > us) {
            return;
        }
    }
}

function lockCounter() {
    LEDPin.write(LOW);
    unlockReadyCounter = 0;
    lock = true;
}

function unlockCounter() {
    if (unlockReadyCounter > unlockReadyCounterAdjusment) {
        LEDPin.write(HIGH);
        unlockReadyCounter = 0;
        lock = false;
    } else {
        unlockReadyCounter++;
    }
}

function LEDBlink(c) {
    for (var i = 0; i < c; i++) {
        LEDPin.write(HIGH);
        usleep(50000); // 50ms
        LEDPin.write(LOW);
    }
}

function init() {
    lock = true;
    defaultDistance = 0;
    prevDistance = 0;
    unlockReadyCounter = 0;
    //counter = 0;
}

setInterval(function(){
    var pulseOn, pulseOff;
    var duration, distance;
    trigPin.write(HIGH); 
    usleep(2);
    trigPin.write(HIGH);
    usleep(10); 
    trigPin.write(LOW);
    while (echoPin.read() == 0) {
        pulseOff = μs.now();
    }
    while (echoPin.read() == 1) {
        pulseOn = μs.now();
    }
    duration = pulseOn - pulseOff;
    distance = parseInt(duration / 58.2);

    if (
        !distance ||
        (!defaultDistance && prevDistance > 0)
       ) {
        init();
        defaultDistance = distance;
        console.log("defaultDistance: " + defaultDistance + "cm.");
        LEDBlink(2);
    } else if ( 
            distance >= maximumRange ||
            distance <= minimumRange ||
            defaultDistance <= distance ||
            defaultDistance > prevDistance
            ) { // lock condtion
        lockCounter();
    } else if (
            (defaultDistance - adjustment) > distance
            ) { // unlock condtion
        unlockCounter();
        console.log("unlockReadyCounter: " + unlockReadyCounter + " count.");
        console.log(" -> Distance: " + distance + "cm.");
    }
    if (lock == false) {
        LEDBlink(1);
        lockCounter();
        counter++;
        console.log("Counter: " + counter + ".");
        console.log(" -> Distance: " + distance + "cm.");
    }
    if (distance && unlockReadyCounter == 0) {
        prevDistance = distance;
    }
}, loopDelay);

```

## 感想
後は、nodeのexpressを入れてリアルタイムでカウンターを見えるようにするとか、若干のコントロールする機能を入れるとかしたらよいのかな？以外と簡単にできた。

おもちゃレベルかもしれないが、こんなにサクサクできるなんて素敵。それと、簡単にインターネットにも繋がるし可能性が広がる。IoT！

趣味として楽しむにはいい感じかも。

今度は、慣れたPythonを使ってAWSにもつないでみようかな。で、[PyCon JPのポスターセッション](http://www.pycon.jp)とかで何かやれたらいのかなぁ。もっとハードに詳しい人に教えてもらいたい。