未分類

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

今回作成するもの

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

必要なもの


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


まずはスマホの画面(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円程度なら絶対にこちらがおすすめです。

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

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

ツイッチ マルチ

サイトについて

https://www.onegoup.com/multi/
ゲーム配信サイトtwitchの複数の配信を1画面で視聴することができます。2画面or4画面or8画面、チャット有り無しが選べます

使い方(お手軽マルチ画面作成)

視聴したいゲームの種類を選ぶと、テキストボックスに現在そのゲームを配信している視聴者数の多い上位8名の名前がテキストボックスに入力されます。(日本語フォルター済み)
ゲーム種類
テキストボックス
次に画面数とチャット有無を選びます。他の視聴者と配信者との掛け合いもゲーム視聴の楽しみのひとつですので、個人的にはチャットありがおすすめです。
画面作成ボタン 
あとは画面作成ボタンを押すだけでマルチ画面が作成されます。大人の都合によりTOPページには広告をのせてありますが、ゲーム視聴画面には広告はありませんので、安心して下さい。画面作成ボタンは複数配置してありますが、どれも同じです。
2画面 チャットあり
4画面 チャットあり
また、ゲームの種類をまたいでマルチ画面を作成したい場合は、下段のゲーム毎ランキングからチェックボックスに2〜8個チェックを入れることでマルチ画面を作成できます。
ゲーム毎の上位10名

使い方(個別マルチ画面作成)

テキストボックスは配信者のチャンネルアドレス(例https://www.twitch.tv/yaa_boo)を直接入力することが可能です。例えば、PUBGのカスタムマッチを開催する際に、複数の配信者が参加しているようでしたら、1〜8名の配信者URLをそれぞれテキストボックス1〜8まで入力していただければ、大会みたいなプレイヤー毎の視点を簡単に作成できます。
同じ人を4画面でみることも可能です(必要ないけど)

改善要望等

ありましたお気軽にコメント欄にご記入ください。