先日、Made By Google 2019で新しいGoogle Home Miniが発表されました。
Google Nest Mini」という名称に変更され、スピーカー、マイクの性能が向上しているそうです。
早速買ってみました!

新しいのが良い!

今年のGoogle I/O 2019では、Googleアシスタントの「Local Home SDK」が発表され、お家の中のIoT機器を直接(ローカルネットワーク内で)操作できるようになりました。
今まではクラウド経由でしかできませんでしたが、ローカルになったことで応答速度が向上し、使い勝手が良くなる感じですね。

ということで、新しく出たGoogle Nest Miniを使って、Local Home SDKを触ってみたいと思います。
このシリーズを担当しているのは、技術部のソフトウェア・サーバ担当のnodaです。

とはいえ、お家の中にIoT機器がそんなにないと思うので、よくある手段としてM5Stackを使ったりするようなんですが、せっかくなので、ロボットを使った方が面白そうなので、弊社のピッコロボIoTを使ってみたいと思います!

なんかカワイイ!

あと、IoTなので、センサ付けたいですよね。
僕はソフトウェアがメインなので、ハードウェアは初心者です。
Local Home SDKを使う人も、ソフトウェアがメインな人が多いと思うので、センサとか触れるように順番に解説していきたいと思います。

ピッコロボIoTに接続できるもの

ピッコロボIoTには、V-duino(VS-RC202)というボードが使われています。V-duinoのダウンロードページから「V-duino 取扱説明書.pdf」の3ページを見てみると、入力としては以下が使えるようです。(サーボは除く)

  • UARTピン
  • I2Cピン超音波センサ(専用ピン)
  • アナログピン(3つ)

超音波センサは専用ピンが用意されていて、扱うための関数も用意されているようですが、汎用的なピンでないので、今回は無視します。

「UART」や「I2C」という用語は、通信方式のようですね。(I2Cは今後の記事で扱う予定です)
V-duinoはArudino互換とのことで、UARTやI2Cは、各センサが用意しているArudinoのライブラリで値を取ったりできそうです。

取扱説明書の40ページを見ると、アナログは 0 ~ 1023 の1024段階で値が取れるようです。23ページにreadSensという関数があり、これで値が取れるようです。

準備

まずはアナログセンサを扱ってみましょう。最初なので、単純に手を叩いたらピッコロボIoTが動く、みたいなものをやってみたい思います。秋月電子でアナログサウンドセンサーモジュールがあり、ケーブルも加工せずすぐ刺せそうで手軽なので、これを使ってみます。

ピッコロボIoTの方は、組み立て済みの完成版もありますが、ボンドやねじ止めで作れるので、通常版か自律制御セットでもいいですね。
自律制御セットは通常版にセンサがセットになったものです。最終的にLocal Home SDKでセンサ値を取得する時の説明に、自律制御セットのセンサを使う予定は無いので、どっちでも構わないと思います。
組み立てに自信がなかったり、組み立てる時間が惜しい人は完成版、いろんなセンサで遊んでみたい人は自律制御セット、といった感じですかね。

V-duino 取扱説明書.pdf」の12ページの「10. セットアップ」から記載があるセットアップをして、Arduino IDEを使えるようにしておいてください。

サウンドセンサー

取り付け

秋月電子でアナログサウンドセンサーモジュールをゲットしたら、早速取り付けてみましょう!
V-duino 取扱説明書.pdf」の3ページのアナログピンを見ると、外側がGND、真ん中がVCC、内側がSignalと書いています。
4ページのVinの項目を見ると、アナログピンのVCCは3.3Vのようです。

秋月電子のアナログサウンドセンサーモジュールのページの説明に、ケーブルは青色が音声出力、赤色が電源入力、黒色がグランドとあります。

そのため、接続時には、黒色が外側、青色が内側になるように取り付けましょう。
とりあえずアナログピン1に取り付けました。

黒色が外側、青色が内側

センサ値の読み取り

Arduino IDEを起動し、新しくスケッチを作りましょう。
メニューの「ファイル > 新規ファイル」を選択します。

とりあえず、サウンドセンサーの値を読み取って、シリアルモニタに出力してみます。

V-duino(VS-RC202)のライブラリのリファレンスは「V-duino 取扱説明書.pdf」の22ページから記載があります。次のように実装してみました。

#include <vs-rc202.h>

void setup() {
    // シリアルモニタ(シリアル通信)を使用するための初期化処理
    Serial.begin(115200);
    // V-duino(VS-RC202)のライブラリを使用するための初期化処理
    initLib();
}

void loop() {
    // アナログピン1に接続されているセンサ値を読み取る
    // 今回はサウンドセンサーの値が取れる
    int soundVal = readSens(1);

    // シリアルモニタにサウンドセンサーの値を出力する
    Serial.print("soundVal:");
    Serial.println(soundVal);

    delay(500);
}

検証ボタンを押下すると、ソースコードのチェックができます。

ピッコロボIoTをPCに接続して下さい。
メニューの「ツール」から、ボードの設定や、ピッコロボIoTを接続しているポートに誤りがないか念のため確認してください。
(V-duino 取扱説明書.pdfの18ページに記載)

問題なければ、ピッコロボIoTに書き込んでみましょう!
マイコンボードに書き込むボタンを押下すると、書き込みが開始されます。

シリアルモニタにサウンドセンサーの値を出力するようにしているので、シリアルモニタを開いてみましょう。
メニューの「ツール > シリアルモニタ」を選択します。

手を叩いて音を出してみると、値が変わってますね!
とても簡単にセンサ値が取れましたね!
もし、何も表示されない場合は、赤丸で囲った箇所が「115200bps」になっているか確認してください。

音に合わせて動かす

次は、手を叩いて、音の強さでピッコロボIoTが動くようにしてみます。
GithubのVstoneOfficialのV-duinoでサンプルがあるので、それを参考にします。
Readmeのサンプルプログラム一覧で、「vs-rc202_servo_motion」が参考になりそうです。
ここの5つのモーションを使ってみます。
ちょっと長くなるので、モーションデータはmotion.hとして切り分けました。

motion.hを追加するには、上部右端の下三角マークをクリックし、「新規タブ」を選択してください。

下部にファイル名入力欄が表示されるので、「motion.h」と入力して、OKボタンを押下します。

スケッチ内容を次のように変更します。

#include <vs-rc202.h>
#include "motion.h"

// モーションを無効化するカウント数
#define CNT_NUM_DISABLE_MOTION 30

// モーションを無効化するカウント
int disableMotionCnt = 0;

void setup() {
    Serial.begin(115200);
    initLib();

    // サーボは頭1、腰1、脚2の合計4自由度になっている
    // 各サーボを有効化
    servoEnable(1, 1);
    servoEnable(2, 1);
    servoEnable(3, 1);
    servoEnable(4, 1);

}

void loop() {
    // アナログピン1に接続されているサウンドセンサーの値
    int soundVal = readSens(1);

    Serial.print("soundVal:");
    Serial.println(soundVal);

    detectMotion(soundVal);
    selectMotion();
    
    delay(100);
}

void detectMotion(int soundVal){
    // 小さすぎる数値は無視するようにする
    if(soundVal < 30) {
        // モーション無効化(初期状態にする)までのカウントをしておく
        disableMotionCnt++;
        // 無効化のカウント数までいっていない場合は、モーション番号を変えない
        if(disableMotionCnt < CNT_NUM_DISABLE_MOTION) {
            return;
        }
    }

    // モーション番号を変えるタイミングで無効化カウントをリセットしておく
    disableMotionCnt = 0;
    
    int motionNum = M_NUM0;
    
    // サウンドセンサーの値(音の大きさ)で、モーション番号を決める
    // 1024段階あるが、あまり大きな値は出ないので、低めの値で割り振っている
    if(soundVal < 30) {
        motionNum = M_NUM0;
    } else if(soundVal < 100) {
        motionNum = M_NUM1;
    } else if(soundVal < 200) {
        motionNum = M_NUM2;
    } else if(soundVal < 300) {
        motionNum = M_NUM3;
    } else if(300 <= soundVal) {
        motionNum = M_NUM4;
    }
    
    // モーション番号を設定する
    setMotionNumber(motionNum);
}

void selectMotion(){
    // 設定していたモーション番号を取得し、その番号毎にモーションを実行する
    // playMotionOnce、またはplayMotionが呼ばれるたびに、次のパターンを実行していくようになる
    // (呼ばれるごとにパターンのインデックスをインクリメントしている)
    switch(getMotionNumber()){
        case M_NUM0:
            // モーションを一度だけ実行する
            // 第二引数には実行するパターン数を指定するので、
            // 基本的にモーション配列の2次元の要素数を指定すればいい
            playMotionOnce(motion0, 1);
            break;
        case M_NUM1:
            playMotionOnce(motion1, 3);
            break;
        case M_NUM2:
            playMotionOnce(motion2, 3);
            break;
        case M_NUM3:
            // モーションを繰り返し実行する
            playMotion(motion3, 5);
            break;
        case M_NUM4:
            playMotion(motion4, 5);
            break;
    }
}

motion.hの内容を次のようにします。
こっちがモーションデータになります。

#define M_NUM0 0
#define M_NUM1 1
#define M_NUM2 2
#define M_NUM3 3
#define M_NUM4 4

// モーションデータは2次元配列にする
// motion[パターン数][11]
// 1次元の要素数は、遷移時間(mm)とSV1の目標位置 ~ SV10の目標値の11要素になる
// 目標位置の範囲は -1800 ~ 1800。ただし、オフセット値を除く

// 初期姿勢(正面を向いている状態)
int motion0[1][11] = {
                  {600,0,0,0,0,0,0,0,0,0,0},
                };
                
int motion1[3][11] = {
                  {600,1000,1000,1000,1000,0,0,0,0,0,0},
                  {1200,-1000,-1000,-1000,-1000,0,0,0,0,0,0},
                  {600,0,0,0,0,0,0,0,0,0,0},
                };
                
int motion2[3][11] = {
                  {600,1000,-1000,1000,-1000,0,0,0,0,0,0},
                  {1200,-1000,1000,-1000,1000,0,0,0,0,0,0},
                  {600,0,0,0,0,0,0,0,0,0,0},
                };

int motion3[5][11] = {
                  {600,1000,0,0,0,0,0,0,0,0,0},
                  {600,1000,1000,0,0,0,0,0,0,0,0},
                  {600,1000,1000,1000,0,0,0,0,0,0,0},
                  {600,1000,1000,1000,1000,0,0,0,0,0,0},
                  {600,0,0,0,0,0,0,0,0,0,0},
                };

int motion4[5][11] = {
                  {600,1000,0,0,0,0,0,0,0,0,0},
                  {600,1000,-1000,0,0,0,0,0,0,0,0},
                  {600,1000,-1000,1000,0,0,0,0,0,0,0},
                  {600,1000,-1000,1000,-1000,0,0,0,0,0,0},
                  {600,0,0,0,0,0,0,0,0,0,0},
                 };

サーボモーターを動かすには、電池が入っていないと動きません。
なので、電池を入れておいてください。
また、電池の残量が少ないと、うまく動かないことがあります。

先ほどと同じようにピッコロボIoTに書き込んで、書き込みが終わったら手を叩いてみましょう。
手を叩くと、その音の大きさに合わせて、ピッコロボIoTがバタバタ動きましたね!

まだまだ続くので、今回は、ここまでとします。
なお、今回のソースコードはGithubのvstoneofficial/SoundSensor_PiccoRoboIoTに上げています。
次回からはLocal Home SDKを使ってみましょう!
最終的にはピッコロボIoTをNest Miniで操作(状態のやり取り)をできるようにしていきたいと思います!