seabean が 2021年05月14日02時16分33秒 に編集
コメント無し
本文の変更
# 概要 子供が小さい頃に遊んだプラレールを発見。 子供といっしょに線路をたくさん繋げて遊んだことを思い出しました。 プラレールはSWをONにしたら一定の速度でひたすら走る電車のおもちゃ。 これをobnizでいろいろ遠隔操作できれば大人でも楽しめそう。と思い製作。 前進、後進、スピードコントロール、自動運転、ポイントコントロールが出来ます。 また、操作性を良くするため、使わなくなったビデオデッキのリコモンを使った操作も出来るようにしました。 obnizは2台使用。 一台は車両コントロールに、もう一台はポイントコントロール、リモコン操作に使っています。 ![obniz #1](https://camo.elchika.com/bd6fd4968b5e023a350d3974a9eb59f187886433/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f30343231656137392d393863322d343831312d613230652d6162323032656134363861332f62316130633532612d636438342d343434392d613231382d313432306664313464393534/) ![obniz #2](https://camo.elchika.com/0862f250a28fa91c95e966759211232a5fd8836d/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f30343231656137392d393863322d343831312d613230652d6162323032656134363861332f33303864623264312d653730642d343937622d386331642d393836666565643137663235/) # デモ @[youtube](https://youtu.be/mxkXWztwF7U) @[youtube](https://youtu.be/zm9Yo4hL9D0) # 手動運転 画面のボタンを操作して、前進、後進、スピードコントロールができます。 スピードコントロールは画面上のスライダーを使って行います。 ポイントコントロールでは駅ホームの線路、駅通過の線路の切り換えができます。 駅ホームへ切り換えた時はLEDが”緑”、駅通過へ切り換えた時はLEDが”赤”となります。 ![画面](https://camo.elchika.com/ca579b0387096c5cbe9fbb59cbd7f746bec5c3fa/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f30343231656137392d393863322d343831312d613230652d6162323032656134363861332f36656336383830622d643838632d343465662d396338342d656532383661633234346236/) # 自動運転 画面のAUTOボタンを押すことで自動運転がスタートします。 何周通過するか乱数で決め車両を走らせます。 車両の周回は車両下に取り付けたリードスイッチ(磁石に反応する)を使っています。 ポイント近くの線路上に磁石を貼り付けて車両が通過することで回数をカウントしています。 駅での発車、停車はスピードコントロールして加速、減速させています。 ![リードスイッチ](https://camo.elchika.com/a4123245d7d8e55ca43f390bd0cb6b10ee0ae988/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f30343231656137392d393863322d343831312d613230652d6162323032656134363861332f35393631373733392d633566362d346439642d383331642d363931343531346231356165/) ![磁石](https://camo.elchika.com/ce70c7862c1674116ea0c45edb0d12a378165962/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f30343231656137392d393863322d343831312d613230652d6162323032656134363861332f38383034633363372d383066662d346237332d616436652d626462363633626535313161/) # リモコン操作 使わなくなったビデオデッキのリモコンがあったので、リモコンでの操作も出来るようにしました。 リモコンから送信される赤外線信号を赤外線受信モジュールで受信して、どのボタンが押されたか判定しています。
リモコンにはジョグシャトル(くるくる回すやつ)が付いていましたので、これでスピードコントロールが出来るようにしました。 車両を見ながら操作できるので操作性UPです。
リモコンにはジョグシャトル(くるくる回すやつ)が付いていましたので、これでスピードコントロールを出来るようにしました。 車両を見ながら操作できるようになり操作性UPです。
![リモコン](https://camo.elchika.com/04f1aef31965dc4b8f1201d7a599e71ec6fb05c3/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f30343231656137392d393863322d343831312d613230652d6162323032656134363861332f30613837373066332d363539302d346366302d616135612d616435643135303064636530/) ![赤外線受信モジュール](https://camo.elchika.com/6ffc452dbc29cb71a54721215a6b6290ab821221/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f30343231656137392d393863322d343831312d613230652d6162323032656134363861332f39383532623636372d306536312d346230382d623031642d356337386431353364376463/)
# 部品
- obniz Board 1Y - obniz Board - ブレッドボード - ジャンパーケーブル 数本 - サーボモーター SG92R - 赤外線受信モジュール OSRB38C9AA - リードスイッチ - 磁石 - 抵抗付きLED 緑 - 抵抗付きLED 赤 - モバイルバッテリー - USB ACアダプター - USBケーブル Type-B - USBケーブル Type-C - プラレール 車両 一編成 - プラレール 線路 数本 - プラレール 駅 - LEGOブロック 数個 - obniz車両への取り付け具 ※3Dプリンタでオリジナル製作 - サーボモーター線路への取り付け具 ※3Dプリンタでオリジナル製作 - obnizケース ※LEGOブロックでオリジナル製作
# 配線図
| A | B | |:---:|:---| | C | D |
# ソースコード
``` <html> <!-- ---------------------------------------------------------------------------------------------------- --> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script src="https://unpkg.com/obniz@3.14.0/obniz.js" crossorigin="anonymous"></script> <link href="https://obniz.com/users/1656/repo/roundslider.min.css" rel="stylesheet" /> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script src="https://obniz.com/users/1656/repo/roundslider.min.js"></script> </head> <!-- ---------------------------------------------------------------------------------------------------- --> <style type="text/css"> .rslider { display: inline-block; margin-top: 0px; margin-left: 0px; } </style> <!-- ---------------------------------------------------------------------------------------------------- --> <body> <div></div> <h1></h1> <table> <tr> <td> <div id="sld_pwr" class="rslider"></div> </td> <td width="30"> </td> <td> <div> <center> <button id="btn_mae" style="width:100px;height:50px" disabled>前</button> </center> </div> <div> <center> <button id="btn_usiro" style="width:100px;height:50px" disabled>後</button> </center> </div> <br> <div> <center> <button id="btn_auto" style="width:100px;height:50px" disabled>AUTO</button> </center> </div> <br> <div> <center> <button id="btn_tomaru" style="width:100px;height:50px;background-color:#FF6928" disabled>止まる</button> </center> </div> </td> <td width="100"> </td> <td> <div> <center> <button id="btn_pointeki" style="width:200px;height:50px" disabled>ポイント・駅</button> </center> </div> <div> <center> <button id="btn_pointtuuka" style="width:200px;height:50px" disabled>ポイント・通過</button> </center> </div> </td> </tr> </table> <!-- ---------------------------------------------------------------------- --> <script> let obniz1 = new Obniz("9999-9999"); // obniz 車両 ←IDは消してあります let obniz2 = new Obniz("9999-9999"); // obniz ポイント ←IDは消してあります let motor1; // モーター 駆動 let motor1_pwrmax = 35; let motor1_pwrrate = 100; let motor1_pwrrate_back = 60; let motor1_pwr = motor1_pwrmax * motor1_pwrrate * 0.01; let flg_move = 0; // 動作フラグ(0:停止、1:前、2:後、3::AUTO) let flg_point = 0; // ポイントフラグ(0:駅、1:1:通過) let rdsw; // リードスイッチ let rdsw_count = 0; // リードスイッチカウンタ let rdsw_loop; // リードスイッチループ let servo1; // サーボモーター ポイント let servo1_pointeki = 64; let servo1_pointtuuka = 107; let led_green; // LED ポイント 緑 let led_red; // LED ポイント 赤 let irsens; // 赤外線受信センサー let data = new Array(); // 取得データ let data_ptn; // パターンデータ // -------------------------------------------------- const ir_vhs = "11110110101011010110110101011010110110101101111011010101101011011010101101011011010110111101101010110101101101010110101101101011011110110101011010110110101011010110110101101111011010101101011011010101"; const ir_vd8 = "11110101101011010110110101011010110110101101111010110101101011011010101101011011010110111101011010110101101101010110101101101011011110101101011010110110101011010110110101101111010110101101011011010101"; const ir_rgt = "11110101011011011010101011010101011110101011011011010101011010101011110101011011011010101011010101011110101011011011010101011010101011110101011011011010101011010101011110101011011011010101011010101011"; const ir_lft = "11110110110101101101010101101010101111011011010110110101010110101010111101101101011011010101011010101011110110110101101101010101101010101111011011010110110101010110101010111101101101011011010101011010"; const ir_jgr = "111101011010110101101010101101011011010110111101011010110101101010101101011011010110111101011010110101101010101101011011010110"; const ir_jgl = "111101101101011010110101010110101101101011011110110110101101011010101011010110110101101111011011010110101101010101101011011010"; const ir_ply = "111101011010110110101010110101010111101011010110110101010110101010111101011010110110101010110101010111101011010110110101010110101010111101011010110110101010110101010"; const ir_stp = "1111010101011011010101011010101011110101010110110101010110101010111101010101101101010101101010101111010101011011010101011010101011110101010110110101010110101010"; const ir_out = "111101011011010110101010110101010111101011011010110101010110101010111101011011010110101010110101010111101011011010110101010110101010111101011011010110101010110101010111101011011010110101010110101010111101011011010110101010110101010"; // -------------------------------------------------- // スライダー・パワーを準備する $("#sld_pwr").roundSlider({ sliderType: "min-range", disabled: true, value: motor1_pwrrate }); // -------------------------------------------------- // obniz1に接続する obniz1.onconnect = async function () { // モーターを準備する motor1 = obniz1.wired("DCMotor", {forward:0, back:1}); // モーター 駆動 motor1.power(motor1_pwrmax); // リードスイッチを準備する rdsw = obniz1.wired("Button", {signal:2, gnd:3}); // コントロールを有効化する control_enabled(); // リードスイッチが変化したときのcallback関数を定義する rdsw.onchange = function(pressed) { if (pressed == true) { console.log("RDSWがON"); // AUTOモードの時 if (flg_move == 3) { // リードスイッチカウンタをインクリメントする rdsw_count++; if (rdsw_count + 1 == rdsw_loop) { // ポイントを駅に切り替える auto_pointeki(); } else if (rdsw_count >= rdsw_loop) { // 駅に停車させる auto_station(); } } } else { console.log("RDSWがOFF"); } } console.log("obniz1 準備完了"); } // -------------------------------------------------- // obniz2に接続する obniz2.onconnect = async function () { // サーボモーターを準備する servo1 = obniz2.wired("ServoMotor", {gnd:0, vcc:1, signal:2}); // サーボモーター ポイント servo1.angle(servo1_pointeki); // LEDを準備する led_green = obniz2.wired("LED", {anode:3, cathode:5}); // LED 緑 led_red = obniz2.wired("LED", {anode:4, cathode:5}); // LED 赤 led_green.on(); led_red.off(); // 赤外線受信センサーを準備する irsens = obniz2.wired("IRSensor", {vcc:8, gnd:7, output:6}); // 赤外線受信センサー OSRB38C9AA // 赤外線信号を検出したときのcallback関数を定義する irsens.start(function(arr) { // 取得データ配列に取得データをセットする data = arr; // 取得データをパターン文字列に変換する data_ptn = change_bin2ptn().substr(0, 150); console.log(data_ptn); // 80文字以下のパターン文字列は無効とする if (data_ptn.length >= 80) { if (data_ptn.indexOf(ir_out) > -1 || ir_out.indexOf(data_ptn) > -1) { console.log("リモコンのカセット取出しボタンを検出しました"); document.getElementById("btn_auto").click(); } else if (data_ptn.indexOf(ir_ply) > -1 || ir_ply.indexOf(data_ptn) > -1) { console.log("リモコンのカセット再生ボタンを検出しました"); document.getElementById("btn_mae").click(); } else if (data_ptn.indexOf(ir_stp) > -1 || ir_stp.indexOf(data_ptn) > -1) { console.log("リモコンのカセット停止ボタンを検出しました"); document.getElementById("btn_tomaru").click(); } else if (data_ptn.indexOf(ir_vhs) > -1 || ir_vhs.indexOf(data_ptn) > -1) { console.log("リモコンのVHSボタンを検出しました"); document.getElementById("btn_pointeki").click(); } else if (data_ptn.indexOf(ir_vd8) > -1 || ir_vd8.indexOf(data_ptn) > -1) { console.log("リモコンのVideo8ボタンを検出しました"); document.getElementById("btn_pointtuuka").click(); } else if (data_ptn.indexOf(ir_rgt) > -1 || ir_rgt.indexOf(data_ptn) > -1) { console.log("リモコンの>>ボタンを検出しました"); document.getElementById("btn_mae").click(); } else if (data_ptn.indexOf(ir_lft) > -1 || ir_lft.indexOf(data_ptn) > -1) { console.log("リモコンの<<ボタンを検出しました"); document.getElementById("btn_usiro").click(); } else if (data_ptn.indexOf(ir_jgr) > -1 || ir_jgr.indexOf(data_ptn) > -1) { console.log("リモコンのジョグ右回転を検出しました"); // AUTOモード以外の時 if (flg_move != 3) { motor1_pwrrate += 5; motor1_power(motor1_pwrrate); } } else if (data_ptn.indexOf(ir_jgl) > -1 || ir_jgl.indexOf(data_ptn) > -1) { console.log("リモコンのジョグ左回転を検出しました"); // AUTOモード以外の時 if (flg_move != 3) { motor1_pwrrate -= 5; motor1_power(motor1_pwrrate); } } } }) console.log("obniz2 準備完了"); } </script> <script> // -------------------------------------------------- // スライダー・パワーが変更されたら // -------------------------------------------------- $("#sld_pwr").change(function() { motor1_pwrrate = $("#sld_pwr").roundSlider("getValue"); motor1_pwr = parseInt(motor1_pwrmax * motor1_pwrrate * 0.01); console.log("パワーが変更された " + motor1_pwr); motor1.power(motor1_pwr); }); // -------------------------------------------------- // ボタン・前がクリックされたら // -------------------------------------------------- $('#btn_mae').click(function () { console.log("前が押された"); // 動作フラグをセットする flg_move = 1; // 使用できないコントロールを無効化する control_disabled(); // モーターを回転させる motor1.forward(); }); // -------------------------------------------------- // ボタン・後がクリックされたら // -------------------------------------------------- $('#btn_usiro').click(function () { console.log("後が押された"); // 動作フラグをセットする flg_move = 2; // 使用できないコントロールを無効化する control_disabled(); $("#sld_pwr").roundSlider({ disabled: true }); // モーターパワーを変更する motor1_pwrrate = motor1_pwrrate_back; motor1_pwr = parseInt(motor1_pwrmax * motor1_pwrrate * 0.01); // モーターパワーを変更する motor1.power(motor1_pwr); // スライダー・パワーを変更する $("#sld_pwr").roundSlider({ value: motor1_pwrrate }); // モーターを逆回転させる motor1.reverse(); }); // -------------------------------------------------- // ボタン・AUTOがクリックされたら // -------------------------------------------------- $('#btn_auto').click(function () { console.log("AUTOが押された"); // 動作フラグをセットする flg_move = 3; // リードスイッチカウンタを初期化する rdsw_count = 0; // 使用できないコントロールを無効化する control_disabled(); $("#sld_pwr").roundSlider({ disabled: true }); // 発車させる auto_start(); }); // -------------------------------------------------- // ボタン・止まるがクリックされたら // -------------------------------------------------- $('#btn_tomaru').click(function () { console.log("止まるが押された"); // 動作フラグをセットする flg_move = 0; // モーターを停止させる motor1.stop(); // コントロールを有効化する control_enabled(); }); // -------------------------------------------------- // ボタン・ポイント・駅がクリックされたら // -------------------------------------------------- $('#btn_pointeki').click(function () { console.log("ポイント・駅が押された"); // ポイントフラグをセットする flg_point = 0; // サーボモーターを動かす servo1.angle(servo1_pointeki); // LEDを緑で点灯する led_green.on(); led_red.off(); }); // -------------------------------------------------- // ボタン・ポイント・通過がクリックされたら // -------------------------------------------------- $('#btn_pointtuuka').click(function () { console.log("ポイント・通過が押された"); // ポイントフラグをセットする flg_point = 1; // サーボモーターを動かす servo1.angle(servo1_pointtuuka); // LEDを赤で点灯する led_green.off(); led_red.on(); }); // -------------------------------------------------- // AUTOでスタートさせる // -------------------------------------------------- async function auto_start() { var mtrrate; console.log("発車します"); // リードスイッチのループ数をランダムで決める rdsw_loop = Math.floor(Math.random() * 3) + 1; console.log("ループ数は" + rdsw_loop); // ポイントフラグをセットする flg_point = 1; // ポイントを通過に切り替える servo1.angle(servo1_pointtuuka); // LEDを赤で点灯する led_green.off(); led_red.on(); // 次は通過か? if (rdsw_loop == 1) { // ポイントフラグをセットする flg_point = 0; // ポイントを駅に切り替える servo1.angle(servo1_pointeki); // LEDを緑で点灯する led_green.on(); led_red.off(); } // スタートは0からモーターを回転させる mtrrate = 0; motor1_forward(mtrrate); // 10回に別けて加速させる for (count = 0; count < 10; count++) { // レートをアップする mtrrate += 10; // レートを変更する motor1_power(mtrrate); // 待ち await obniz1.wait(300); // 動作フラグを確認する if (flg_move == 0) { return; } } } // -------------------------------------------------- // AUTOでストップさせる // -------------------------------------------------- async function auto_stop() { var mtrrate; console.log("停車します"); // スタートは100からモーターを回転させる mtrrate = 100; motor1_forward(mtrrate); // 10回に別けて加速させる for (count = 0; count < 10; count++) { // レートをアップする mtrrate -= 10; // レートを変更する motor1_power(mtrrate); // 待ち await obniz1.wait(500); // 動作フラグを確認する if (flg_move == 0) { return; } } } // -------------------------------------------------- // AUTOで駅にストップさせる // -------------------------------------------------- async function auto_station() { var mtrrate; console.log("駅に停車します"); // スタートは100からモーターを回転させる mtrrate = 100; motor1_forward(mtrrate); // 10回に別けて減速させる for (count = 0; count < 10; count++) { // レートをアップする mtrrate -= 10; // レートを変更する motor1_power(mtrrate); // 待ち await obniz1.wait(550); // 動作フラグを確認する if (flg_move == 0) { return; } } // 待ち await obniz1.wait(5000); // 動作フラグを確認する if (flg_move == 0) { return; } // リードスイッチカウンタを初期化する rdsw_count = 0; // 発車させる auto_start(); } // -------------------------------------------------- // AUTOでポイントを駅に切り替える // -------------------------------------------------- async function auto_pointeki() { var mtrrate; console.log("ポイントを駅に切り替えます"); // ポイントフラグをセットする flg_point = 0; // 待ち await obniz1.wait(3000); // ポイントを駅に切り替える servo1.angle(servo1_pointeki); // LEDを緑で点灯する led_green.on(); led_red.off(); } // -------------------------------------------------- // 指定レートでモーターを回転させる // -------------------------------------------------- function motor1_forward(pwrrate) { // モーターパワーを変更する motor1_power(pwrrate); // モーターを回転させる motor1.forward(); } // -------------------------------------------------- // 指定レートに変更する // -------------------------------------------------- function motor1_power(pwrrate) { // モーターパワーを変更する if (pwrrate > 100) { pwrrate = 100; } else if (pwrrate < 0) { pwrrate = 0; } motor1_pwrrate = pwrrate; motor1_pwr = parseInt(motor1_pwrmax * motor1_pwrrate * 0.01); // モーターパワーを変更する motor1.power(motor1_pwr); // スライダー・パワーを変更する $("#sld_pwr").roundSlider({ value: motor1_pwrrate }); } // -------------------------------------------------- // 取得データをパターン文字列に変換する // -------------------------------------------------- function change_bin2ptn() { var strptn = ""; // パターン文字列 var chrbit = ""; // ビットデータ var count; // カウント strptn = ""; for (var k = 0; k < data.length; k++) { if (data[k].toString(10) != chrbit) { // 受信データから1ビット分のデータを取得する chrbit = data[k].toString(10); // パターン文字列を組み立てる strptn += chrbit; count = 1; } else { // 10文字単位に文字を追加する if (count == 10 && chrbit == "1") { // パターン文字列を組み立てる strptn += chrbit; count = 0; } // カウントをインクリメントする count++; } } return strptn; // ----- (RET) } // -------------------------------------------------- // コントロールを有効化する // -------------------------------------------------- function control_enabled() { $("#sld_pwr").roundSlider({ disabled: false }); document.getElementById("btn_mae").disabled = false; document.getElementById("btn_usiro").disabled = false; document.getElementById("btn_auto").disabled = false; document.getElementById("btn_tomaru").disabled = false; document.getElementById("btn_pointeki").disabled = false; document.getElementById("btn_pointtuuka").disabled = false; } // -------------------------------------------------- // コントロールを無効化する // -------------------------------------------------- function control_disabled() { document.getElementById("btn_mae").disabled = true; document.getElementById("btn_usiro").disabled = true; document.getElementById("btn_auto").disabled = true; if (flg_move == 3) { document.getElementById("btn_pointeki").disabled = true; document.getElementById("btn_pointtuuka").disabled = true; } } </script> </body> </html> ``` # 最後に 遠隔操作できる車両、ポイントなど増やして、複数の車両を使った運行管理などをやってみたいと思っています。 また、今回の製作ではobniz Board 1Yの低電圧で動かすことやスリープ機能などは利用しませんでした。 これらの機能を使ったアイデアを考え試してみたいと思っています。 私がやっているこういった製作を見て、子供たちも興味を示してくれるとうれしいのですが... まぁ、子供は子供ですね。