スマホで操作できるプラレールを作成

今回作成するもの

  • 低速ではしるプラレール(子供が切り替えポイントを操作して遊ぶため)
  • スマホで速度調整ができる。
  • スマホで前進も後進も操作できる。

必要なもの


ボディ等を作成するために必要なもの


まずはスマホの画面(UI)を作成

前進・後進・停止及び速度スライダーを設置。Bootstrap を利用しているので、どんなスマホでも横幅はぴったりと合います。
<!doctype html>
<html lang="ja">
    <head>
      <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

      <!-- Bootstrap CSS -->
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">

        <title>Go to</title>
        <style>input[type="range"] {-webkit-appearance: slider-vertical;}</style>


    </head>
    <body>
        <div class="container">
            <div class="row" style="height:50px"></div>
            <div class="row">
                <div class="col text-center">
                    <div class="row">
                        <div class="w-100 text-center">
                            <div class="slider-wrapper">
                                <input type="range" value="0" min="0" max="100" step="10" style="height: 500px;" id="input">
                            </div>
                        </div>
                    </div>
                    <div class="row">
                          <div class="col text-center text-info" style="font-size:50px">
                              <output id="output1">0</output>%
                          </div>
                    </div>
                </div>
                <div class="col">
                    <div class="row mt-1" style="height:150px">
                        <button type="button" class="btn btn-success w-100 h-100">前進</button>
                    </div>
                    <div class="row mt-1" style="height:150px">
                        <button type="button" class="btn btn-warning w-100 h-100">停止</button>
                    </div>
                    <div class="row mt-1" style="height:150px">
                        <button type="button" class="btn btn-danger w-100 h-100">後進</button>
                    </div>
                </div>
            </div>
        </div>

        <!-- Optional JavaScript -->
        <!-- jQuery first, then Popper.js, then Bootstrap JS -->
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
        <!-- <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> -->
        <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
        <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
        <script>
          $(function() {
              var $input = $('#input');
              var $output1 = $('#output1');
              var value;
              $input.on('input', function(event) {
              value = $input.val();
              $output1.text(value);
              $.get("?value=" + value + '&');
              });

              $("要素").click(function(){
                  if($(this).hasClass("clicked")){ // クリックされた要素がclickedクラスだったら
                    $(this).removeClass("clicked");
                  }else{
                    $(this).addClass("clicked");
                  }
              });

          });
        </script>
    </body>
</html>

前進・後進の切り替えはモータードライバー(TA7291P)を使用

端子の説明と接続先は下表のとおり
端子番号記号端子説明
1GNDグランド
2OUT1モーター端子に接続
3NC
4Vref速度制御端子 → pin 25
5IN1入力端子1 → pin 26 
HIGH or LOW でモーターの回転向きを制御
6IN2入力端子2 → pin 27
HIGH or LOW でモーターの回転向きを制御
7VCCモータードライブの電源 → pin 14
8VSモーターの電源
9NC
10OUT2モーター端子に接続

配線図

配線図イメージ

ESP32のコード

コード概要
  • WebServerとして利用し、getリクエストがきたらPIN操作する。
  • IN1→HIGH IN2→LOW で前進
  • IN1→LOW  IN2→HIGHで後進
  • IN1→LOW  IN2→LOWで停止
  • Vref端子で速度調整 最大値255 最小値0
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>

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

// HTTPリクエストを保存しておく変数
String header;

// ピンの状態を保存する変数の宣言
String goState = "off";
String stopState = "on";
String backState = "off";

String speedSlider = "0";

// 使用するピンに名前を付ける
const int speedPin = 25;
const int pin_in1 = 26;
const int pin_in2 = 27;
const int pin_vcc = 14;

/* TCP server at port 80 will respond to HTTP requests */
WiFiServer server(80);

///////////////////////////
void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.print("Configuring access point...");
  /* You can remove the password parameter if you want the AP to be open. */
  // 出力ピンに割り当て
  pinMode(pin_in1, OUTPUT);
  pinMode(pin_in2, OUTPUT);
  pinMode(pin_vcc, OUTPUT);

  // ピンを初期化
  digitalWrite(pin_in1, LOW);
  digitalWrite(pin_in2, LOW);
  digitalWrite(pin_vcc, HIGH);
  
  ledcSetup(0, 12800, 8);
  ledcAttachPin(speedPin, 0);

  
   // ssidとpasswordを用いてWi-Fiに接続
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // IPアドレスを出力し、webserverをスタート
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();
}

void loop() {
  /* Check if a client has connected */
    WiFiClient client = server.available();
    if (!client) {
        return;
    }
    if (client) {                             // クライアントが来たとき
    Serial.println("New Client.");          
    String currentLine = "";                // クライアントからくるデータを格納する変数
    while (client.connected()) {            // クライアントがつながっている間、以下をループ
      if (client.available()) {             // クライアントからデータが来ているとき
        char c = client.read();             // データを読み込み
        Serial.write(c);                    // 届いたデータをシリアルモニタに出力
        header += c;
        if (c == '\n') {                    // 届いたデータが改行コードだった時
          // もし現在の行が空白ならば、この改行コードのみ受け取る
          // つまりHTTPリクエストの終わりなので、レスポンスを返す
          if (currentLine.length() == 0) {
            // HTTPヘッダは(HTTP/1.1 200 OK)のようなステータスコードから始まる
            // 次にコンテントタイプを送信。今回はhtml形式なので以下のようにする
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close"); 
            client.println();
            
            // リクエストに従ってGPIOをスイッチする
            if (header.indexOf("GET /GO/on") >= 0) {
              Serial.println("GO on");
              goState = "on";
              stopState ="off";
              backState ="off";
              digitalWrite(pin_in1, HIGH);
              digitalWrite(pin_in2, LOW);            
            } else if(header.indexOf("GET /GO/off") >= 0){
              Serial.println("GO off");
              goState = "off";
              digitalWrite(pin_in1, LOW);
              digitalWrite(pin_in2, LOW);
              speedSlider = "0";
              ledcWrite(0, speedSlider.toInt() * 2.5);
            }

            if (header.indexOf("GET /STOP/on") >= 0) {
              Serial.println("STOP on");
              goState = "off";
              stopState ="on";
              backState ="off";
              digitalWrite(pin_in1, LOW);
              digitalWrite(pin_in2, LOW);
              speedSlider = "0";
              ledcWrite(0, speedSlider.toInt() * 2.5);
            }

            if (header.indexOf("GET /BACK/on") >= 0) {
              Serial.println("BACK on");
              goState = "off";
              stopState ="off";
              backState ="on";
              digitalWrite(pin_in1, LOW);
              digitalWrite(pin_in2, HIGH);
            } else if(header.indexOf("GET /BACK/off") >= 0){
              Serial.println("BACK off");
              goState = "off";
              digitalWrite(pin_in1, LOW);
              digitalWrite(pin_in2, LOW);
              speedSlider = "0";
              ledcWrite(0, speedSlider.toInt() * 2.5);
            } 

            if(header.indexOf("GET /GO/on?value=") >= 0){
               int pos1 = header.indexOf("=");
               int pos2 = header.indexOf("&");
               speedSlider = header.substring(pos1+1, pos2);
               Serial.print("Value="); Serial.println(speedSlider);
               ledcWrite(0, speedSlider.toInt() * 2.5);
            }

            if(header.indexOf("GET /BACK/on?value=") >= 0){
               int pos1 = header.indexOf("=");
               int pos2 = header.indexOf("&");
               speedSlider = header.substring(pos1+1, pos2);
               Serial.print("Value="); Serial.println(speedSlider);
               ledcWrite(0, speedSlider.toInt() * 2.5);
            }
            
            // htmlを表示
            client.println("<!doctype html>");
            client.println("<html lang=\"ja\">");
            client.println("<head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">");
            client.println("<link rel=\"stylesheet\" href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css\" integrity=\"sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z\" crossorigin=\"anonymous\">");
            client.println("<title>Go to</title>");
            client.println("<style>input[type=\"range\"] {-webkit-appearance: slider-vertical;}</style>");
            client.println("</head>");
            client.println("<body><div class=\"container\">");
            client.println("<div class=\"row\" style=\"height:50px\"></div>");
            client.println("<div class=\"row\">");
            client.println("<div class=\"col text-center\">");
            client.println("<div class=\"row\">");
            client.println("<div class=\"w-100 text-center\">");
            client.println("<div class=\"slider-wrapper\">");
            if(stopState=="off"){
              client.println("<input type=\"range\" class=\"slider\" value=\"0\" min=\"0\" max=\"100\" step=\"10\" style=\"height: 500px;\" id=\"input\">");     
            }else{  
            }
            client.println("</div></div></div>");
            client.println("<div class=\"row\">");
            client.println("<div class=\"col text-center text-info\" style=\"font-size:50px\">");
            if(stopState=="off"){
              client.println("<output id=\"output1\">0</output>%");  
            }else{
              client.println("<output>停車中</output>");  
            }
            client.println("</div></div></div>");
            client.println("<div class=\"col\">");
            client.println("<div class=\"row mt-1\" style=\"height:150px\">");
            if(goState=="on") {
              client.println("<a href=\"/GO/off\" class=\"w-100 h-100\"><button type=\"button\" class=\"btn btn-dark w-100 h-100\" style=\"font-size:30px\">前進</button></a></div>");              
            }else{
              client.println("<a href=\"/GO/on\" class=\"w-100 h-100\"><button type=\"button\" class=\"btn btn-success w-100 h-100\" style=\"font-size:30px\">前進</button></a></div>");  
            }
            client.println("<div class=\"row mt-1\" style=\"height:150px\">");
            if(stopState=="on"){
              client.println("<a href=\"/STOP/off\" class=\"w-100 h-100\"><button type=\"button\" class=\"btn btn-dark w-100 h-100\" style=\"font-size:30px\">停止</button></a></div>");  
            }else{
              client.println("<a href=\"/STOP/on\" class=\"w-100 h-100\"><button type=\"button\" class=\"btn btn-warning w-100 h-100\" style=\"font-size:30px\">停止</button></a></div>");  
            }
            client.println("<div class=\"row mt-1\" style=\"height:150px\">");
            if(backState=="on"){
              client.println("<a href=\"/BACK/off\" class=\"w-100 h-100\"><button type=\"button\" class=\"btn btn-dark w-100 h-100\" style=\"font-size:30px\">後進</button></a></div>");
            }else{
              client.println("<a href=\"/BACK/on\" class=\"w-100 h-100\"><button type=\"button\" class=\"btn btn-danger w-100 h-100\" style=\"font-size:30px\">後進</button></a></div>");
            }
            client.println("</div></div></div>");
            client.println("<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js\"></script>");
            client.println("<script src=\"https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js\" integrity=\"sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN\" crossorigin=\"anonymous\"></script>");
            client.println("<script src=\"https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js\" integrity=\"sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV\" crossorigin=\"anonymous\"></script>");
            client.println("<script>$(function(){var $input = $('#input');var $output1 = $('#output1');var value;$input.on('input', function(event) {value = $input.val();$output1.text(value);$.get(\"\?value=\" + value + '&');});});</script>");
            client.println("</body></html>");
            
            // HTTPレスポンスの最後は改行で終了
            client.println();
            // whileループの終了
            break;
          } else { // 改行コードを取得したら、currentLineをリセット
            currentLine = "";
          }
        } else if (c != '\r') {  // 改行以外の何かしらのコードが来ているとき
          currentLine += c;      // currentLineに追加
        }
      }
    }
    // ヘッダーをリセット
    header = "";
    // 接続をリセット
    client.stop();
    Serial.println("Client disconnected.");
    Serial.println("");
  }
}

OVERLAY 16:9

配信用のオーバーレイを作成しました。大したものではないですが、自由に使って下さい。12色相環で12秒ループです。 下記アドレスからダウンロードできます。 https://www.onegoup.com/file/overlay.avi
OBSを例に使い方を解説します。 まずはメディアソースを追加
参照でダウンロードしたファイルを選択してください。繰り返しにはチェックを入れてください。
メディアソースを選択した状態で右クリック→変換→画面サイズに合わせる。
以上で完成です。もしよろしければ使って下さい。

温度設定可能な電気ケトル

妻が保温ができる電気ケトルを購入してみました。電気ポットでいいじゃんと思いましたが、使ってみると、とても便利なので皆さんに紹介したいと思います。
便利だと感じた点
  • すぐにお湯を100度・95度・90度・85度・80度・70度・60度にできる。
  • 従来のものより注ぎ口の水切れがいい。
  • 蓋が取れるので中を洗いやすい。
小さなこどもがいるとどうしてもぬるま湯が必要となる場面が結構あります。従来のものは沸騰しかできませんでしたので、水で薄める等の手間がありましたが、この商品はお湯を設定温度にすることができるので大変便利です。 また、湯たんぽにお湯を注ぐ時に感じたのですが、注ぎ口の水切れが良くなってます。前は、注ぎ始めと終わりで水が垂れたのですが、シュパっと切れます。見た目はまったく変わらないのですが、きっとすごい試行錯誤して改善したのではないでしょうか。

私が買った時は6,000円程度でしたが、現在人気で1万円程になっています。従来のものが4,000円程で買えるのでかなり割高ですね。従来のものから+2〜3,000円程度なら絶対にこちらがおすすめです。

EF70-300mm F4-5.6 IS II USMをEOS Mに装着 焦点距離480mm

4月に息子が幼稚園に入園します。今後の入園式や運動会などのイベントを見越して、望遠のカメラレンズが必要と思い、お手頃で評価の高いレンズを購入しました。 結婚前(通帳等全て召し上げられる前)に買った最後の大きな買い物のEOS6Dに装着して、最大限に伸ばすとこんな感じです。入園式に持って行ったら引かれるレベルだと思いますが、そんなことは気にせず息子の写真をアップで撮りたいと思います。
そう言えば、フルサイズのレンズをセンサーサイズがAPS-Cのカメラに装着すると焦点距離が1.6倍になるとWEBのカメラ講座で教わりました。てことは、300*1.6=480mm!!試してみたい。嫁に昔買ってあげたEOSーMのキットにはアダプターがついていたのを思い出し、ひっぱりだしてきて装着してみました!
スタイリッシュで結構かっこいいじゃないですか。しかも軽いので気軽に持っていけるところもポイントが高いと思います。が、AFがめちゃめちゃ遅くなります。レビューを見ると、このレンズの売りは速いAFだそうですが・・・ きっと景色等なら使えるはずです。今度の休みに外で撮影してみます。

エレキット フォロ

amazonで3,000円程度で購入できるロボットキットです。
説明書はかなり詳しく記載されているので、プラモデルを普段作成しない私でも迷うことなく作成することできました。
駆動部分は胴体に、センサーとマイコンは頭の部分に入っています。
完成まで2時間ほどかかりましたが、ハンダ付けなど一切不要で、本当によくできたキットだと思いました。また、この複雑な動きをモーター2つで行っていることに驚いています。設計者は天才だと素直に思いました。

エレベーターのおもちゃ

息子がエレベーターにとても興味があるので5ドル程のエレベーターのおもちゃをgearbest.comという外国の販売サイトで購入してみました。期待通り、説明書は中国語でしたが、写真た沢山のっているとても親切な説明書でしたので無問題です。
ハンダ付けが必要ですので、4か所だけですので、気軽に組み立てられます。
1時間程度で組み立てることができます。昇降のスピードも子供が遊ぶにはちょうどいいです。
レゴとの相性がとてもいいおもちゃです。息子はレゴで2階建ての家を作ってホームエレベーターとして楽しそうに遊んでいます。

Razer Kraken7.1イヤーパッド等の交換

ヘッドセットRazer Kraken7.1の合皮の部分(ヘッドパットとイヤーパット)は経年劣化でボロボロになります。本体機能はまだまだ使えるのに、ボロボロとカスが頭に付くのでどうしたものかと悩んでいました。そこで思い切って交換してみることにしました。純正の交換品もあるようですが、どうせまた劣化するでしょうから、代替品でなるべく安いものを購入しました。
まずは、既存のパットをビリビリと剥がします。
ヘッドパットの端部の部分は隠しねじになってます。
ヘッドパッドが思っていた以上に小さく少し不恰好です。イヤーパッドはぴったりで、しかも肉厚でとても着け心地がいいです。 不恰好とはいえ、1,000円程度ヘッドセットが快適になりとても満足です。

プロジェクト障害物回避自動車#1

早速ですが現在までの成果品をご覧ください。超音波センサーで距離を測定し、20センチ以内に障害物がある場合は、ストップして後退するようにプログラミングしてあります。
まずは、駆動部分を作成します。ものは「タミヤ 楽しい工作シリーズ No.104 ブルドーザー工作基本セット (70104)」と「タミヤ 楽しい工作シリーズ No.157 ユニバーサルプレート 2枚セット (70157)」を使いました。
1段目が駆動部分、2段目がarduinoと超音波センサーで3段目が電池の構成で作成します。
半田付けで大活躍したのが「TSK ツールクリッパー TX303」です。レビューで言われている通り、100均の底にマグネットが付いているねじ皿を逆さにして固定すれば、安定感バッチリで頼もしい相棒となります。
超音波センサーの固定穴がM1.7程度で近くのお店ではねじが売っていません。現在固定方法について思案しているところです。
手軽に組み立てられるユニバーサルプレートは最高です。最初木板で作ろうと穴を開けていたのですが、なかなかネジ芯が通らず嫌気がさしてユニバーサルプレートを購入しました。
プログラムについては次回記載しますね。

ぬか漬け

私は「ぬか漬け美人 TK-32」でぬか漬けを作っています。冷蔵庫の一段に入る高さで、サイズ感でとても使い勝手が良いです。冷蔵庫に入れることで、毎日掻き混ぜなくてもいいという点もポイントが高いです。
食材はもっぱら、キュウリと大根なのですが、最近のお気にりいはうずらの卵です。スーパーで水煮10個入りで売っていると思いますが、もう皮も剥いてありますので、水気を切ってそのまま漬けるだけでOKです。
ゆで卵にただ塩をふった時とはまた違った、深みのある塩気がゆで卵に合いめちゃめちゃ美味しいです。お手軽ですので皆さんも是非試して見て下さい。

タコの燻製おつまみ

今日はタコの燻製おつまみを作って見ました。スーパーで買ってきた茹でタコを麺つゆに漬けて冷蔵庫で2時間ほど冷やします。
その後一時間ほど乾燥させます。
あとは5gのチップで10分ほど燻せば完成です。燻製器は「サーモス 保温燻製器 イージースモーカー ブラック RPD-13 BK」です。
噛めば噛むほど旨味が出て、お酒の肴にぴったりです。お酒は、ストロングゼロダブルレモンが最近のお気に入りですが、まさにベストマッチな組み合わせだと思います。 晩酌の肴の準備をお昼からする休日ってのも贅沢だと思いませんか?