カワリモノ息子の技術メモ的な~

カワリモノ息子とその母の技術メモ的な〜

学校が苦手な息子くんの作品とその母の作品、はたまた日常などいろいろを在宅エンジニア母が綴る

M5StickC+PuppyCでラジコンおもちゃ『パピーちゃん』を作る

PuppyC、買っちゃいました

先日のIRユニット買ったときにじつはPuppyCも買ってたんです。
なにがどうって「パピー」って名前に惹かれちゃいますよね。

f:id:toriko0413:20200430145914j:plain
f:id:toriko0413:20200430150017j:plain

こんな感じにサーボモーターが4個ついてます。

PuppyC自体はHATなので、M5StickCにザクっとつなげて動作させます。

f:id:toriko0413:20200430150209j:plain

www.switch-science.com

PuppyCを動かしてみる

まずはサンプルコードを取得して動かしてみます。
スイッチサイエンスの製品紹介にもリンクがあるこちら ↓ を利用します。

github.com

ここのgitにUIFlowとArduinoふたつが格納されています。
どちらも動かしてみました。

UIFlowのサンプルプログラム

M5Flow(UIFlow)に読み込ませるとこんな感じ。
ひたすら4つのサーボモーターを動かしています。

f:id:toriko0413:20200430161031p:plain

前に進んでいきます。か、かわいい・・!!

そういえばここで「動かない!」ってちょっと躓いてたんでした。
原因は、PuppyCの電源をONにしていなかったというオチ・・。電源はパピーのおしり部分にあります。
製品紹介ページに書いてあるのでちゃーんと見ましょう。

Arduinoのサンプルプログラム

前に10回、そのあと後ろに10回動くというのを繰り返すプログラムでした。
(ここでは詳細は割愛しますが、このあとの改造ではこのプログラム PuppyC.ino をもとにして作っています。)

PuppyCをもっとパピーにしたい!

せっかくパピーCって名前なんでもっとパピー(子犬)っぽくしてみようと思いました。

パピー変身セット材料

f:id:toriko0413:20200430165534j:plain

・M5StickC+PuppyC
・セリアの犬の顔マグネット
・フェルトで作った体の着ぐるみ → 頭の部分(白い所)にはマグネットを縫い付け、しっぽには使い古しの乾電池を入れています。縫い目、汚い…見ないで…w
・小さいジャムのふた

着替え

最初に体を着せます。胴体の部分を髪留めゴムでとめます。

f:id:toriko0413:20200430165945j:plain

頭のマグネットにジャムのふたと犬の顔をくっつけます。

f:id:toriko0413:20200430170238j:plain

完成。

ここに至るまで試行錯誤あり、最終的にこの形になりました。

【工夫したところ】
・マグネットとマグネットの引っ付く力が弱かったので、間にジャムのふたを挟んで固定するようにしました。
・頭が重くなったのでバランスをとるためにしっぽに電池をいれておもりにしました。

歩いてみる

f:id:toriko0413:20200430173451g:plain

あんよがじょうず、あんよがじょうず!

パピーちゃん歩けるようになりました^^

さらにパピーちゃんをリモートコントロールできるようにしてみる

先日息子くんの書いてたArduinoプログラムを流用しながらPuppyCのArduinoサンプルプログラムを書き換え、パピーちゃんをリモコン操作できるように書いてみました。

Arduinoプログラム

#include <M5StickC.h>
#include "IIC_.h"
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiAP.h>

const char *ssid = "M5StickCtrl";
const char *password = "";

WiFiServer server(80);

TaskHandle_t pthreadHandle;
String mode = "Stop";

void puppy_start()
{
	angle_all_set(90,90,90,90);		//servo 1	2	3	4	=>	set	90°
	delay(100);
}

void puppy_go()
{
	angle_all_set(55,60,130,120);		//servo 1	2	3	4
	delay(100);
	angle_all_set(55,160,130,120);		//servo 1	2	3	4
	delay(100);
	angle_all_set(120,130,150,60);		//servo 1	2	3	4
	delay(100);
	angle_all_set(120,130,60,60);		//servo 1	2	3	4
	delay(100);
	angle_all_set(55,130,60,60);		//servo 1	2	3	4
	delay(100);
	angle_all_set(55,60,130,30);
	delay(100);
}

void puppy_back()
{
	angle_all_set(120,130,60,55);
	delay(100);
	angle_all_set(120,130,160,55);
	delay(100);
	angle_all_set(60,150,130,120);
	delay(100);
	angle_all_set(60,60,130,120);
	delay(100);
	angle_all_set(60,60,130,55);
	delay(100);
	angle_all_set(30,130,60,55);
	delay(100);
}


// the setup routine runs once when M5StickC starts up
void setup(){ 
  
  // Initialize the M5StickC object
  M5.begin();

  // LCD display
  M5.Lcd.println(" ");
  M5.Lcd.println("  IIC_PUPPY");
  IIC_Init();     //sda  0     scl  26 
  
  
  /*
        1              2
        3              4 
  */
  angle_set(1,90);		//servo 1	=>	set	90°	
  angle_set(2,90);
  angle_set(3,90);
  angle_set(4,90);
//   delay(1000);

  // WiFi INIT
  Serial.println("Configuring access point...");
 
  WiFi.softAP(ssid, password);
  IPAddress myIP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(myIP);
  server.begin();
 
  Serial.println("Connect:");
  Serial.println(ssid);
  Serial.println("Enter:");
  Serial.println(myIP);

  mode = "Stop";
  xTaskCreatePinnedToCore(moveTask, "moveTask", 4096, NULL, 1, &pthreadHandle, 0);

  delay(1000);
//   M5.Lcd.qrcode(myIP.toString(),
//     80, 0, 80, 15);

}

void moveTask(void* arg) {
  puppy_start();
  while(true){
    if (mode == "Forward"){
      puppy_go();
    }else if (mode == "Back"){
      puppy_back();
    }else{
      delay(100);
    }
  }
}

void loop() {
  M5.update();
  WiFiClient client = server.available();
 
  if (client) {
    Serial.println("Client Connected.");
    String currentLine = "";
    
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        if (c == '\n') {
          if (currentLine.length() == 0) {
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html; charset=utf-8;");
            client.println();
  
            client.println("<!DOCTYPE HTML>");
            client.println("<html>");
            client.println("<head>");
            client.println("<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />");
            client.println("</head>");
  
            client.println("<body>");

            client.println("<form method=\"get\">");
            client.println("<input name=\"control\" type=\"text\" value=\"Forward\" style=\"display:none;\" \\>");
            client.println("<button type=\"submit\">Go Forward</button>");
            client.println("</form>");

			      client.println("<form method=\"get\">");
            client.println("<input name=\"control\" type=\"text\" value=\"Stop\" style=\"display:none;\" \\>");
            client.println("<button type=\"submit\">Stop</button>");\
            client.println("</form>");

			      client.println("<form method=\"get\">");
            client.println("<input name=\"control\" type=\"text\" value=\"Back\" style=\"display:none;\" \\>");
            client.println("<button type=\"submit\">Go Back</button>");\
            client.println("</form>");

            client.println("</body>");
            client.println("</html>");
  
            Serial.println("html Rendering");
  
            break;
          } else if (currentLine.indexOf("GET /?") == 0) {
			      Serial.print("currentLine = ");
			      Serial.println(currentLine);

            int pos1 = 0;
            int pos2 = 0;
            String val = "";

            pos1 = currentLine.indexOf('control=', pos2);
            pos2 = currentLine.indexOf(' ', pos1);
            val = currentLine.substring(pos1 + 1, pos2);
            Serial.print("val = ");
            Serial.println(val);

            client.println("HTTP/1.1 302 Request Succsessful");
            client.println("Connection: close");
            client.println("Location: /");
            client.println();
            client.println("Request was succsessful. redirect to <a href=\"/\"> here. </a>");
            client.println();
            
            if (val == "Forward") {
              Serial.println("[Button Push] Forward");
              // 前に進む
              mode = "Forward";

            } else if(val == "Back") {
              Serial.println("[Button Push] Back");
              // 後ろに進む
              mode = "Back";

            } else {
              // ストップ
              mode = "Stop";
			      }
          } else {
            currentLine = "";
          }
        } else if (c != '\r') {
          currentLine += c;
        }
      }
    }
 
    client.stop();
    Serial.println("Client Disconnected.");
  }

 // Aボタンが押されたら準備状態にする
  if (M5.BtnA.wasPressed()){
    puppy_start();
  }
}

Arduinoプログラム解説

流用したという先日息子くんの書いてたプログラムっていうのはこれ ↓
siroitori.hatenablog.com

リモコン(今回はPCブラウザで実行)とM5StickCをつなぐために、M5StickCをサーバとして起動しています。
クライアントとなるリモコン側からはまずはWiFiのつなぎ先をプログラム中にあるSSID「M5StickCtrl」に接続します。(このへんは上記のリモコン操作のブログに書いています。)

今回のプログラムでは、最初にパピーちゃんを動かす「moveTask」というタスクを起動しました。ここでは、ブラウザのボタン押された現在の状態を確認しながら、どう動かすか処理しています。

ブラウザには「Forward」「Stop」「Back」というボタンを表示させるようにして、それぞれ押下時に現在の状態を切り替えています。

ブラウザの画面 ↓
f:id:toriko0413:20200430175836p:plain

最終形

PCのブラウザからパピーちゃんが操作できる様子がわかります^^
ちょっと歩き方ぎこちないですけどね。

「さいごまで よんでくれて ありがとワン」

f:id:toriko0413:20200430180757j:plain