LEGOとATOM LiteでDIYリモコンカー 6 (IOTなサーボモータ編)

 IOT的にサーボモータを動かします。IOT的なLチカで使った「SimpleWiFiServer」に以下の記事で作ったコードを埋め込んでいく感じです。

haseharu.hatenablog.com

#include <M5Atom.h>
#include <ESP32Servo.h>

Servo myservo1;
Servo myservo2;

void setup() {
  M5.begin(true, false, false);

  ESP32PWM::allocateTimer(0);
  ESP32PWM::allocateTimer(1);
  ESP32PWM::allocateTimer(2);
  ESP32PWM::allocateTimer(3);
  
  myservo1.setPeriodHertz(50);
  myservo1.attach(22, 500, 2500);

  myservo2.setPeriodHertz(50);
  myservo2.attach(33 500, 2500);
}

 ここまではまるっと入れていく感じだと思います。
 リモートカーの動作は「前進」「後進」「右向く」「左向く」「停止」の5つとし、クライアントからATOM Liteへ送信するデータは「F」「B」「R」「L」「S」とします。ここは何でもよいです。「client.print」でATOM Liteから出力するhtmlを書いていきます。

client.print("<html lang=\"ja\"><head><meta charset=\"UTF-8\"></head><body>");
client.print("<a href=\"/F\">↑ すすめ</a><br>");
client.print("<a href=\"/R\">→ みぎ</a><br>");
client.print("<a href=\"/L\">← ひだり</a><br>");
client.print("<a href=\"/B\">↓ うしろ</a><br>");
client.print("<a href=\"/S\">■ とまれ</a><br>");
client.print("</body></html>");

 クライアントへのデータ出力部分と受け取る部分はaタグのhref属性に書いていきます。エスケープシーケンスでダブルクォーテーション(")のところにバックスラッシュ(or ¥もある?)が入っています。
 loop関数部分は「myservo1.write()」「myservo2.write()」をそれぞれの動作にあわせて追記していきます。

if (currentLine.endsWith("GET /F")) {
 servo1.write(150); 
 servo2.write(40);
}
if (currentLine.endsWith("GET /B")) {
 servo1.write(40); 
 servo2.write(150);
}
if (currentLine.endsWith("GET /R")) {
 servo1.write(110); 
 servo2.write(110);
}
if (currentLine.endsWith("GET /L")) {
 servo1.write(70); 
 servo2.write(70);
}
if (currentLine.endsWith("GET /s")) {
 servo1.write(90); 
 servo2.write(90);
}

 サーボモータの出力値は調整が必要です。右回り、左周りもこの例だと逆転させていますが、他にもやり方があると思います。あと、モータの右左の付け方によっては、前進と後進、右向き、左向きが想定していた向きと逆になると思います。その場合GPIO部分を差し替えればコードの修正は不要だと思います(雑ですみません)。
 というところまでで以下のようなコードになると思います。

#include <M5Atom.h>
#include <ESP32Servo.h>
#include <WiFi.h>

Servo myservo1;
Servo myservo2;

const char* ssid     = "yourssid";
const char* password = "yourpasswd";

IPAddress ip(XXX,XXX,XX,XX);
IPAddress subnet(255,255,255,0);

WiFiServer server();

void setup()
{
    Serial.begin(115200);
    
    M5.begin(true, false, true);
    delay(50);
    M5.dis.drawpix(0, 0x0ff000);

    //servo motor
    ESP32PWM::allocateTimer(0);
    ESP32PWM::allocateTimer(1);
    ESP32PWM::allocateTimer(2);
    ESP32PWM::allocateTimer(3);

    myservo1.setPeriodHertz(50); // standard 50 hz servo
    myservo1.attach(22, 600, 2400);
    myservo2.setPeriodHertz(50); // standard 50 hz servo
    myservo2.attach(33, 600, 2400);
    delay(1000);

    // wifi
    WiFi.config(ip,ip,subnet);
    delay(10);

    Serial.println();
    Serial.println();
    Serial.print("Connecting to ");
    Serial.println(ssid);

    WiFi.begin(ssid, password);

    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }

    Serial.println("");
    Serial.println("WiFi connected.");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
    
    server.begin();
}

int value = 0;

void loop(){
 WiFiClient client = server.available();

  if (client) {
    Serial.println("New Client.");
    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");
            client.println();

            client.print("<html lang=\"ja\"><head><meta charset=\"UTF-8\"></head><body>");
            client.print("<a href=\"/F\">↑ すすめ</a><br>");
            client.print("<a href=\"/R\">→ みぎ</a><br>");
            client.print("<a href=\"/L\">← ひだり</a><br>");
            client.print("<a href=\"/B\">↓ うしろ</a><br>");
            client.print("<a href=\"/S\">■ とまれ</a><br>");
            client.print("</body></html>");
            
            client.println();
            break;
          } else {
            currentLine = "";
          }
        } else if (c != '\r') {
          currentLine += c;
        }

        if (currentLine.endsWith("GET /F")) {
           myservo1.write(150); 
           myservo2.write(40);
        }
        if (currentLine.endsWith("GET /B")) {
          myservo1.write(40);
          myservo2.write(150);
        }
        if (currentLine.endsWith("GET /R")) {
          myservo1.write(110);
          myservo2.write(110);
        }
        if (currentLine.endsWith("GET /L")) {
          myservo1.write(70);
          myservo2.write(70);
        }
        if (currentLine.endsWith("GET /S")) {
          myservo1.write(90);
          myservo2.write(90);
        }
        
      }
    }
    client.stop();
    Serial.println("Client Disconnected.");
  }
}

 
 まっすぐ走らない、回るスピードが早すぎる遅すぎるなどは調整いただければある程度は解消されると思います。ATOM TailBATフル充電で10分くらい走りますので「電池が切れたらおしまい」という点でこどもと少し遊ぶにはよいおもちゃだと思います。

 もう少し作り込もうかと、Raspberry Pi Zeroにカメラモジュールを付けて、それをリモコンカーにのせて、ライブ映像を受信できるようやろうとやってみましたが、そのときはLEGOにガチガチに作り込んだラズパイ+カメラが重くて、リモコンカーの馬力が足りず動作が少し残念でした。

www.switch-science.com

akizukidenshi.com

 あと、ブレッドボードと乾電池利用での例を記録しておしまいにしようと思います。

 f:id:haseharu:20210804220735j:plain

 ATOM Liteは仕様上は5Vでの動作となります。

docs.m5stack.com

 5V出力昇圧DCDCコンバータを使います。注意としてはあくまでも昇圧なので、今回であれば5V以上の電源をつながない、でしょうか。9V電池を三端子レギュレータで5Vに降圧させて動作させる、などでも良いかなと思います。

akizukidenshi.com

 データシート上は入力は「0.9-5V」で出力が「5V」になります、とのことです。

akizukidenshi.com

 とりあえず単4電池を2本、

f:id:haseharu:20210804223622j:plain

 およそ3Vあるものをコンバータにつなげてみると、

f:id:haseharu:20210804223519j:plain

 「4.97V」です、いいですね。「3V」から「5V」へきちんと昇圧されています*1。仕事してくれてます。この5VをATOM Liteとサーボモータへつなげます。本当はモータとATOM Liteの電源を別にして上げたほうがいいんでしょうがとりあえず動くのができれば良しとします。想定していたよりもジャンパワイヤが結構必要でやはりぴょんぴょんしてつながりがわかりにくいですね。手軽でいいのですが、見づらくなるのが難点。

f:id:haseharu:20210804223056j:plain

 この部品とケーブル類を処理してあげれば完成です。単4電池2本を5V昇圧で何分位動作するかは試していないのでご注意ください。

 ということでここでおしまいとなります。

 この記事は7月中に終わらせる予定だったのが想定外に長引きました。というか1年に1本書くか書かないかのテンポで書いてきたブログだったので、これで6年分程度は先に書ききりました。

*1:テスターのリード線、逆についてます。電圧も影になっていて見にくいですがマイナスですね。後で見直して気が付いたので追記。 20211005