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

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)」の作業内容の続きをまとめます。

LチカのプログラムをCatFansに移植したので、今度はこれを使って扇風機のファンを回します。それからができたら、サーボモータを回す処理を追加します。

#1. 扇風機のファンを回す
[前回](https://qiita.com/tomato_sugar/items/731895899212cdefce95)のプログラムのピン名を
`String pinName = "BCM6";`
から
`String pinName = "BCM23";`
に変更するだけで、扇風機のファンが回ります。また、メソッド名を次のように変え、コメントも書き換えました。

`BlinkRunnable → turnFan`

こんな感じでファンが回っています。ちなみに写真の猫は我が家のライトです。(*^^*)
![30.jpg](https://qiita-image-store.s3.amazonaws.com/0/222292/167d6d48-930c-20b5-6f9a-f2de22f02748.jpeg)

#2. サーボを回す
サーボモータを動かしてみます。Lチカのサンプルプログラムにはサーボモータを動かすプログラム（PWM sample）も入っているので、今回はそれを使うことにしました。扇風機のファンとは逆で、猫がいなければサーボを回し、猫がいたらサーボを止めます。

##(1) Lチカのプログラムの移植

###(i) mChangePWMRunnableメソッドとPWMメソッドの追加
「Simple PIO」のcom.example.androidthings.simplepioの中のonCreateメソッドとmChangePWMRunnableメソッドを参考に次のようなメソッドを追加する。

```java:CatFansActivity.java
    private Pwm mPwm;
    private boolean mIsPulseIncreasing = true;
    private double mActivePulseDuration;

    // Parameters of the servo PWM
    private static final double MIN_ACTIVE_PULSE_DURATION_MS = 0.8;
    private static final double MAX_ACTIVE_PULSE_DURATION_MS = 2.3;
    private static final double PULSE_PERIOD_MS = 20;  // Frequency of 50Hz (1000/20)

    // Parameters for the servo movement over time
    private static final double PULSE_CHANGE_PER_STEP_MS = 0.08;
    :
    :
    //サーボモータの処理
    private void mChangePWMRunnable(boolean onoff){
            // Exit Runnable if the port is already closed
            if (mPwm == null) {
                Log.w(TAG, "Stopping runnable since mPwm is null");
                return;
            }

            // Change the duration of the active PWM pulse, but keep it between the minimum and
            // maximum limits.
            // The direction of the change depends on the mIsPulseIncreasing variable, so the pulse
            // will bounce from MIN to MAX.
            if (mIsPulseIncreasing) {
                mActivePulseDuration += PULSE_CHANGE_PER_STEP_MS;
            } else {
                mActivePulseDuration -= PULSE_CHANGE_PER_STEP_MS;
            }

            // Bounce mActivePulseDuration back from the limits
            if (mActivePulseDuration > MAX_ACTIVE_PULSE_DURATION_MS) {
                mActivePulseDuration = MAX_ACTIVE_PULSE_DURATION_MS;
                mIsPulseIncreasing = !mIsPulseIncreasing;
            } else if (mActivePulseDuration < MIN_ACTIVE_PULSE_DURATION_MS) {
                mActivePulseDuration = MIN_ACTIVE_PULSE_DURATION_MS;
                mIsPulseIncreasing = !mIsPulseIncreasing;
            }

            Log.d(TAG, "Changing PWM active pulse duration to " + mActivePulseDuration + " ms");

            try {

                // Duty cycle is the percentage of active (on) pulse over the total duration of the
                // PWM pulse
                if(onoff){
                    mPwm.setPwmDutyCycle(100 * mActivePulseDuration / PULSE_PERIOD_MS);
                }

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


    private void PWM(){
        //サーボモータの処理
        PeripheralManagerService service = new PeripheralManagerService();
        try {
            String pinName = "PWM0";
            mActivePulseDuration = MIN_ACTIVE_PULSE_DURATION_MS;

            mPwm = service.openPwm(pinName);

            // Always set frequency and initial duty cycle before enabling PWM
            mPwm.setPwmFrequencyHz(1000 / PULSE_PERIOD_MS);
            mPwm.setEnabled(true);

            // Post a Runnable that continuously change PWM pulse width, effectively changing the
            // servo position
            Log.d(TAG, "Start changing PWM pulse");
            mChangePWMRunnable(false);
        } catch (IOException e) {
            Log.e(TAG, "Error on PeripheralIO API", e);
        }
    }

```

Lチカと同じようにピンは固定にするので、
`String pinName = "PWM0";`
とする。
ラズパイのピン名は基本BCM~になるが、サーボモータのみPWM0、PWM1となる。また、サーボモータはパルス幅で回転する位置が決まり、パルス幅はパルス波の周期とデューティー比で求まる。

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

###(iii) onDestroyメソッドの修正
onDestroyメソッドを次のように修正。

```java:CatFansActivity.java
    protected void onDestroy() {
        :
        :
        //サーボ
        // Remove pending Runnable from the handler.
        mChangePWMRunnable(false);
        // Close the PWM port.
        Log.i(TAG, "Closing port");
        try {
            mPwm.close();
        } catch (IOException e) {
            Log.e(TAG, "Error on PeripheralIO API", e);
        } finally {
            mPwm = null;
        }
    }
```

###(iv) onPictureTakenの修正
onPictureTakenメソッドを次のように修正する。

```java:CatFansActivity.java
    private void onPictureTaken(final byte[] imageBytes) {
                :
                :
                        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);
                                    log.child("annotations").setValue(annotations);

                                    mChangePWMRunnable(false);
                                    turnFan(true);
                                    Log.d(TAG, "Turn the fan");
                                } else {
                                    mChangePWMRunnable(true);
                                    turnFan(false);
                                    Log.d(TAG, "Stop the fan");
                                }
                            }
                            else {
                                mChangePWMRunnable(true);
                                turnFan(false);
                                Log.d(TAG, "Stop the fan");
                            }
                        }
                    } catch (IOException e) {
                        Log.e(TAG, "Cloud Vison API error: ", e);
                    }
                }
            });
        }
    }
```

###(v) 実行確認
以下のように扇風機のファンの下につけたサーボが、猫がいなければ回り、猫がいたら止まる。

![31.jpg](https://qiita-image-store.s3.amazonaws.com/0/222292/34634484-0243-3159-1981-a50190440c5f.jpeg)


第8回はまだ続きます。