AndroidThings & Raspberry Pi3 で猫用扇風機を作る（5）

2017年11月18日、19日に実施された「[東北Tech道場 郡山道場 第8回](https://medium.com/@koriyamadojo/%E6%9D%B1%E5%8C%97tech%E9%81%93%E5%A0%B4-%E9%83%A1%E5%B1%B1%E9%81%93%E5%A0%B4-%E7%AC%AC%EF%BC%93%E6%9C%9F-%E7%AC%AC%EF%BC%98%E5%9B%9E%E9%96%8B%E5%82%AC-d46f5514cfa9)」の作業内容をまとめます。

12月2日に東北Tech道場全体の発表会が行われたのですが、その直前の回です。前回までの作業で、5秒おきに写真を撮って、Cloud Vision APIで解析して、解析結果とともにFirebaseにアップロードし、スマホからそれを確認するところまでできました。そろそろハードと合わせないと間に合いません。それと、当初予定していた、

- 画像をAIで判別し、猫が写っていて、設定温度と湿度を超えていれば扇風機のモータを回す。このとき、距離センサで猫との距離を測り、離れすぎている場合には扇風機は回さない。

という仕様ですが、これはもう時間的に全部は完成させられそうにないので、

- 画像をAIで判別し、猫が写っていれば扇風機のモータを回す。
- サーボモータで扇風機そのものを回転させ、猫を探す。

に限定して開発することになりました。なお、下の方の仕様は、距離センサの代替案です。

#1.  ハードウェアの状態
この時点でのハードウェアは次のような状態です。市販の扇風機から取り外したファンと別途購入したサーボモータをRaspberry Pi3で制御します。
![29.jpg](https://qiita-image-store.s3.amazonaws.com/0/222292/a8535933-7a8c-29e0-adf8-d9a0313940cd.jpeg)

#2. Lチカを試す

まず、Raspberry Pi3のPIOの使い方を覚えるためにLEDを点灯させるプログラムLチカを作成しました。参考にしたのは、「[電子工作、プログラミングのメモ的なブログ](http://denshibu.blog.fc2.com/blog-entry-147.html)」です。

##(1) 配線
サイトに載っている通りにブレッドボードにLEDと抵抗の配線をした。抵抗は1kΩを使用した。

##(2) サンプルプログラムのインポート
サイトに載っている「Simple PIO」をダウロード・解凍し、Android Studioにインポートした。インポートしたとき、バージョンが異なっていたらしく、アップデートを促すポップアップがでたので指示に従いアップデートした。

##(3) 実行
サイトの説明の通り、「RUN」の隣にあるプルダウンから「blink」を選択して実行した。すると、ちゃんと1秒ごとにLEDが点滅した。

#3. CatFansへLチカを移植
CatFansにLチカのプログラムをいれます。画像を解析して、猫がいたらLEDをOn、猫がいなかったらLEDをOffにするプログラムにします。実際にはLEDのOn/OffをファンのOn/Offに使えるようにすることになります。


##(1) mBlinkRunnableメソッドの追加
Lチカのプログラム、BlinkActivityの79行目から97行目にあるLEDの処理、「mBlinkRunnable」メソッドをコピーして、以下のように書き換える。これで、LEDを1秒おきではなく、引数でon/offできるようになる。

```java:CatFansActivity.java
    //LEDの処理
    private void mBlinkRunnable(boolean onoff) {
        // Exit Runnable if the GPIO is already closed
        if (mLedGpio == null) {
            return;
        }
        try {
            mLedGpio.setValue(onoff);
            Log.d(TAG, "State set to " + onoff);

        } catch (IOException e) {
            Log.e(TAG, "Error on PeripheralIO API", e);
        }
    }
```

##(2) GPIO()メソッドの追加
CatFansのCatFansActivityに新しいメソッド「GPIO()」を作り、その中にLチカのプログラムのBlinkActivityの49行目から60行目までをコピーする。このとき、ピン名は「String pinName = "BCM6"」の様にCantFansの中で固定する。

```java:CatFansActivity.java
import com.google.android.things.pio.PeripheralManagerService;
    :
    private Gpio mLedGpio;  //LED
    :
    private void GPIO(){
        // GPIOの処理
        PeripheralManagerService service = new PeripheralManagerService();
        try {
            String pinName = "BCM6";
            mLedGpio = service.openGpio(pinName);
            mLedGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);
            Log.i(TAG, "Start blinking LED GPIO pin");
            // Post a Runnable that continuously switch the state of the GPIO, blinking the
            // corresponding LED
            mBlinkRunnable(false);
        } catch (IOException e) {
            Log.e(TAG, "Error on PeripheralIO API", e);
        }
    }
```

GPIO()の呼び出し部分をonCreateメソッドに追加する。

```java:CatFansActivity.java
public void onCreate(Bundle savedInstanceState) {
    :
    :
    GPIO();
}

```

##(3) onDestroyメソッドの修正
CatFansのonDestroyの中にLチカのプログラムのonDestroyを追記する。Lチカのプログラム、BlinkActivityの66行目から76行目をコピーする。

```java:CatFansActivity.java
    protected void onDestroy() {
        :
        :
        //LED
        // Remove pending blink Runnable from the handler.
        mBlinkRunnable(false);
        // Close the Gpio pin.
        Log.i(TAG, "Closing LED GPIO pin");
        try {
            mLedGpio.close();
        } catch (IOException e) {
            Log.e(TAG, "Error on PeripheralIO API", e);
        } finally {
            mLedGpio = null;
        }
    }
```

##(4) onCreateメソッドの修正
OnCreateメソッドの最後に
`GPIO();`
を追加。

#4. 猫がいたらLEDをOn、猫がいなかったらLEDをOffにする処理
猫がいたらLEDをOnにし、猫がいなかったらLEDをOffにする処理を追加します。

##(1) onPictureTakenメソッドの修正
onPictureTakenメソッドの中に、画像解析の結果、Catが80%以上だったらLEDをつける、そうでなかったのならば、LEDを消す処理を追加する。そして、今まで画像を先にFirebaseに投げてそのあと解析していたが、if文の中でFirebaseに投げるようにする。

```java:CatFansActivity.java
    String imageStr;
    private void onPictureTaken(final byte[] imageBytes) {
        if (imageBytes != null) {
            final DatabaseReference log = mDatabase.getReference("logs").push();

            //画像を画面サイズに合わせて縮小する
            Bitmap b = createScaleBitmap(imageBytes, 1.0F, 1.0F);
            byte[] temp = bmp2byteArray(b, Bitmap.CompressFormat.JPEG, 100);
            final byte[] imageB = temp;

            imageStr = Base64.encodeToString(imageB, Base64.NO_WRAP | Base64.URL_SAFE);

            mCloudHandler.post(new Runnable() {
                @Override
                public void run() {
                    Log.d(TAG, "sending image to cloud vision");
                    // Cloud Vision APIにアップロードして画像に注釈を付ける
                    try {
                        Map<String, Float> annotations = CloudVisionUtils.annotateImage(imageB);
                        Log.d(TAG, "cloud vision annotations:" + annotations);
                        if (annotations != null) {
                            if (annotations.containsKey("cat") == true) {
                                if (annotations.get("cat") >= 0.8F) {
                                    // イメージをfirebaseにアップロードする
                                    log.child("timestamp").setValue(ServerValue.TIMESTAMP);
                                    log.child("image").setValue(imageStr);

                                    mBlinkRunnable(true);
                                    Log.d(TAG, "LED ON");
                                    log.child("annotations").setValue(annotations);
                                } else {
                                    mBlinkRunnable(false);
                                    Log.d(TAG, "LED OFF");
                                }
                            }
                            else {
                                mBlinkRunnable(false);
                                Log.d(TAG, "LED OFF");
                            }
                        }
                    } catch (IOException e) {
                        Log.e(TAG, "Cloud Vison API error: ", e);
                    }
                }
            });
        }
    }
```

##(2) 実行
実行して、猫の画像にカメラを向けるとLEDが点灯し、そうでないときにはLEDが消灯するように動いた。

第8回はまだ続きます。