●スマートフォンのアプリをつくる(Android)

iOSはこちら

 今回からAndroidフォンのリモコンアプリの制作となりますが、ゼロからすべてを作っていくのはハードルが高くなってしまいますので途中まで作成したサンプルプロジェクトにプログラムを追加していく方法となることをご了承ください。
 Androidアプリの開発にはAndroid Studioという開発環境を使いますので、お使いのパソコン環境に合わせてAndroid Studioのダウンロードページからダウンロードしてインストールしてください。Android Studioの基本的な使い方に関しては初心者向けの書籍等を参照してください。
 
 次にこちらからサンプルプロジェクトをダウンロードします。zipファイルを解凍し「AndroidStudioProjects」フォルダに移動します。もしユーザー名が日本語になっている場合は「AndroidStudioProjects」フォルダごとcドライブ直下に移しておいたほうが無難です。
 Android Studioを起動し、「Import project」からRobotControllerプロジェクトを指定してOKします。プロジェクトが開いたら左側の「1:Project」->「app」->「java」->「jp.co.meuse.robotcontroller」とたどって「MainActivity」をダブルクリックして開きます。上のメニューから「Build」->「Make Project」でエラーが出ないことを確認します。

 左側で「app」->「res」->「layout」とたどって「activity_main.xml」をダブルクリックして開きます。こちらはアプリの画面の設定となります。下のタブでDesignとTextの切り替えができます。各ボタンにはid(名前)がついています。

 MainActivityに戻って先頭のUUIDは、ESP32のプログラムに書かれていたものをそのまま記載します。これが違っているとマイコン側とスマートフォン側とで通信が成立しません。

 プログラム下方のpublic void onClick(View v)には、ボタンがタップされたときの処理が書かれています。現在はスキャン、接続、切断、左旋回の処理のみでその他の処理は未記載です。左旋回(LTRN)の記載内容を参考に残りの部分のコードを完成させてください。前回はBLEデータのやり取りを文字列で行いましたが、今回はBYTE配列を使います。(ESP32のプログラムも変更します。)実際のデータの送信は、
private void writeCharacteristic( UUID uuid_service, UUID uuid_characteristic, byte[] data )で実行されます。

public void onClick( View v )
{
    byte[] command = new byte[1];
    if( mButton_Scan.getId() == v.getId() ) {
        scanNewDevice();
        return;
    }
    if( mButton_Connect.getId() == v.getId() ) {
        mButton_Connect.setEnabled( false );
        connect();
        return;
    }
    if( mButton_Disconnect.getId() == v.getId() ) {
        mButton_Disconnect.setEnabled( false );
        disconnect();
        return;
    }
    if( mButton_LTRN.getId() == v.getId() ) {
        mButton_LTRN.setEnabled( false );
        command[0] = 1;
        writeCharacteristic( UUID_SERVICE_PRIVATE, UUID_CHARACTERISTIC_PRIVATE1, command );
        return;
    }
    if( mButton_FWRD.getId() == v.getId() ) {
        mButton_FWRD.setEnabled( false );
        command[0] = 2;
        writeCharacteristic( UUID_SERVICE_PRIVATE, UUID_CHARACTERISTIC_PRIVATE1, command );
        return;
    }
    if( mButton_RTRN.getId() == v.getId() ) {
        mButton_RTRN.setEnabled( false );
        command[0] = 3;
        writeCharacteristic( UUID_SERVICE_PRIVATE, UUID_CHARACTERISTIC_PRIVATE1, command );
        return;
    }
    if( mButton_LEFT.getId() == v.getId() ) {
        mButton_LEFT.setEnabled( false );
        command[0] = 4;
        writeCharacteristic( UUID_SERVICE_PRIVATE, UUID_CHARACTERISTIC_PRIVATE1, command );
        return;
    }
    if( mButton_STOP.getId() == v.getId() ) {
        mButton_STOP.setEnabled( false );
        command[0] = 5;
        writeCharacteristic( UUID_SERVICE_PRIVATE, UUID_CHARACTERISTIC_PRIVATE1, command );
        return;
    }
    if( mButton_RGHT.getId() == v.getId() ) {
        mButton_RGHT.setEnabled( false );
        command[0] = 6;
        writeCharacteristic( UUID_SERVICE_PRIVATE, UUID_CHARACTERISTIC_PRIVATE1, command );
        return;
    }
    if( mButton_LED_ON.getId() == v.getId() ) {
        mButton_LED_ON.setEnabled( false );
        command[0] = 7;
        writeCharacteristic( UUID_SERVICE_PRIVATE, UUID_CHARACTERISTIC_PRIVATE1, command );
        return;
    }
    if( mButton_BWRD.getId() == v.getId() ) {
        mButton_BWRD.setEnabled( false );
        command[0] = 8;
        writeCharacteristic( UUID_SERVICE_PRIVATE, UUID_CHARACTERISTIC_PRIVATE1, command );
        return;
    }
    if( mButton_LED_OFF.getId() == v.getId() ) {
        mButton_LED_OFF.setEnabled( false );
        command[0] = 9;
        writeCharacteristic( UUID_SERVICE_PRIVATE, UUID_CHARACTERISTIC_PRIVATE1, command );
        return;
    }
    if( mButton_SND.getId() == v.getId() ) {
        mButton_SND.setEnabled( false );
        command[0] = 0;
        writeCharacteristic( UUID_SERVICE_PRIVATE, UUID_CHARACTERISTIC_PRIVATE1, command );
        return;
    }
}

 mm12-2をもとに、ESP32側もBYTE配列でデータを受け取るように変更しmm13とします。

class MyCallbacks: public BLECharacteristicCallbacks {
    void onWrite(BLECharacteristic *pCharacteristic) {
      std::string value = pCharacteristic->getValue();

      if (value.length() > 0) {
        for (int i = 0; i < value.length(); i++){
          byte data = (uint8_t)value[i];
          Serial.print(data);
          if(data == 0 || data == 7 || data == 9) {
            switch(data){
            case 0:  //"0"ボタン
              start_playback();
              break;
            case 7:  //"7"ボタン  
              digitalWrite(14,HIGH);
              break;
            case 9:  //"9"ボタン  
              digitalWrite(14,LOW);
              break;
            default:
              break;
            }
          } else {
            switch(data){  //<-switch文を追加
            case 2:  //"2"ボタン
              actionMode = FWRD;
              memcpy(motionAngles, fwrdAngles, sizeof(fwrdAngles));
              maxRows = sizeof(fwrdAngles) / sizeof(*fwrdAngles) - 1;
              break;
            case 5:  //"5"ボタン
              actionMode = STOP;
              for(int i=0; i<12; i++) {
                tempAngles[i] = stopAngles[i];
              }
              set_angle();
              break;
            case 8:  //"8"ボタン
              actionMode = BWRD;
              memcpy(motionAngles, bwrdAngles, sizeof(bwrdAngles));
              maxRows = sizeof(bwrdAngles) / sizeof(*bwrdAngles) - 1;
              break;
            case 1:  //"1"ボタン
              actionMode = LTRN;
              memcpy(motionAngles, ltrnAngles, sizeof(ltrnAngles));
              maxRows = sizeof(ltrnAngles) / sizeof(*ltrnAngles) - 1;
              break;
            case 3:  //"3"ボタン
              actionMode = RTRN;
              memcpy(motionAngles, rtrnAngles, sizeof(rtrnAngles));
              maxRows = sizeof(rtrnAngles) / sizeof(*rtrnAngles) - 1;
              break;
            case 4:   //"4"ボタン
              actionMode = LEFT;
              memcpy(motionAngles, leftAngles, sizeof(leftAngles));
              maxRows = sizeof(leftAngles) / sizeof(*leftAngles) - 1;
              break;
            case 6:  //"6"ボタン
              actionMode = RGHT;
              memcpy(motionAngles, rghtAngles, sizeof(rghtAngles));
              maxRows = sizeof(rghtAngles) / sizeof(*rghtAngles) - 1;
              break;
            default:
            break;
            }
            divCounter = 0;
            keyFrame = 0;
            nextKeyFrame = 1;
          }
        }  
      }
    }
};

<= マイクロマシーン チュートリアル(12)
チュートリアルベース
マイクロマシーン チュートリアル (14) =>