nari が 2021年05月08日17時20分13秒 に編集
初版
タイトルの変更
ラジコンカー&RFIDでミニゲーム作った
タグの変更
IoT
ラジコン
RFID
メイン画像の変更
本文の変更
# 作ったもの&はじめに スマホで操作できるラジコンカーを使って、RFIDに予め書き込んだポイントを集めていくミニゲームを作成しました。 ラジコンカーは入門編として作成する人が多いと思いますが、次のステップとして別のモジュールを組み合わせてゲーム化することで小中学生なども楽しめる&想像力を掻き立てるようなものにしたいと思い作成しました! # ゲームルール **基本ルール**:制限時間内フィールドのタグを検知してポイントを集めていく **制限時間**:90秒 **タグ**:2種類あり ・ポイントタグ(10ポイント) ・アイテムタグ 当たり①:制限時間プラス(30秒) 当たり②:ポイントプラス(30ポイント) ハズレ:ポイントマイナス(10ポイント) **その他ルール**:同じタグは2回連続で検知できない。アイテムタグは1回のみ検知することができる # ゲーム動画 まずはご覧ください! @[youtube](https://youtu.be/1_abwIzLAl4) 操作画面について ![キャプションを入力できます](https://camo.elchika.com/9d40ed048fb43eac050f3711c45d85fac5654547/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f66333132303635322d646430312d346433332d393262342d3235653634353563613832392f37366466333733352d366533642d346231632d623231312d656561366330646239356330/) # 部品一覧 | マイコン | obniz Board 1Y | |:---:|:---| | モバイルバッテリー | cheero Canvas 3200mAh IoT対応 | | RFID | RC522 | | モータ | 下記参照 | | モータドライバ | L91110S | 写真 # ゲーム画面 スマホUI ラジコンカーをスマホで操作する&ゲーム画面を作成します。こちらはCSSなどを利用してボタンに高級感を出しています。 ![キャプションを入力できます](https://camo.elchika.com/ca9ed35349ea67bc3ae82252257501401ac9277c/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f66333132303635322d646430312d346433332d393262342d3235653634353563613832392f32633365353762612d633736622d343135382d626431622d636439303462313130326565/) コード該当部 ```java <style type="text/css"> .btn-circle { display: inline-block; text-decoration: none; font-size:80px; background: #2ECFCA; color: #FFF; width: 120px; height: 120px; line-height: 120px; border-radius: 80%; text-align: center; font-weight: bold; overflow: hidden; box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.05); border-bottom: solid 6px #359ddb; transition: .05s; } .btn-circle:active { -webkit-transform: translateY(4px); transform: translateY(4px); box-shadow: 0 0 1px rgba(0, 0, 0, 0.15); border-bottom: none; } body{ background-color: #F9F3DE; font-size: 20px; font-weight: bold; -webkit-touch-callout: none; -webkit-user-select:none; // テキスト長押しの選択ボックスを無効化 } p{ font-family: "Arial", "メイリオ"; } </style> ``` # ラジコンカー アマゾンで購入しました。モータ2つ+電池ボックス付きです。これは走ればなんでもいいと思います。今回はモータドライバも合わせて使用します。 ![キャプションを入力できます](https://camo.elchika.com/37212a48b5aedf5cc6a2f99e54555173b3670021/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f66333132303635322d646430312d346433332d393262342d3235653634353563613832392f63313265663830352d653933302d343563612d616338312d646535626563643439633831/) # RFID こちらもアマゾンで購入しました。予めタグを読み取りIDを記録しておきます。 ![キャプションを入力できます](https://camo.elchika.com/5cce02a84bd47e3f70fcd60d899a54407084a243/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f66333132303635322d646430312d346433332d393262342d3235653634353563613832392f39333838346363372d363338642d346339382d396564362d383030363163316264633937/) ちょっと無理やりですが付けました。 ![キャプションを入力できます](https://camo.elchika.com/0a2e2cfbcf06dd20112821938f82d7982df110f6/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f66333132303635322d646430312d346433332d393262342d3235653634353563613832392f38656131386133342d306337332d343331372d613235622d303832623064663265333465/) ## タグID読み取りコード ```java <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" /> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script src="https://unpkg.com/obniz@3.x/obniz.js" crossorigin="anonymous" ></script> </head> <body> <div id="obniz-debug"></div> <script> var obniz = new Obniz("OBNIZ_ID_HERE"); // Javascript Example obniz.onconnect = async function () { // If obniz connect var mfrc522 = obniz.wired("MFRC522", { cs: 0, clk: 1, mosi: 2, miso: 3, gnd: 4, rst: 5, vcc: 6 }); while(true) { try { let card = await mfrc522.findCardWait(); console.log("Card is detected!"); console.log("UID : " + card.uid); console.log("PICC Type : " + card.PICC_Type); obniz.display.print(card.uid); } catch(e) { // Not Found or Error console.log("タグをかざしてください") obniz.display.print("タグをかざしてください"); obniz.display.clear() // console.error(e) }; }; }; </script> </body> </html> ``` # 結線図 ## 結線簡易図 ![キャプションを入力できます](https://camo.elchika.com/b65c64c62d9b9e9c98422e8c1fbc78e21842ed0e/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f66333132303635322d646430312d346433332d393262342d3235653634353563613832392f38303165373431662d383162652d346162362d393534372d623836616464646338333761/) ## 結線表 | obniz 0 | RFID cs | |:---:|:---| | obniz 1 | RFID clk | | obniz 2 | RFID mosi | | obniz 3 | RFID miso | | obniz 4 | RFID rst | | obniz 5 | RFID gnd | | obniz 6 | RFID vcc | | obniz 7 | モータドライバ A1 | | obniz 8 | モータドライバ A2 | | obniz 9 | モータドライバ B1 | | obniz 10 | モータドライバ B2 | ## 実写 実際に配線するとゴチャゴチャしてしまうので今回はテイッシュボックスの箱で隠してしまいました。 実際の配線 ![キャプションを入力できます](https://camo.elchika.com/b683da44111b9d0596b3b6729aff3f079cc0f354/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f66333132303635322d646430312d346433332d393262342d3235653634353563613832392f39396332373938382d396136612d346161352d383136322d626166336361396164353031/) 隠したあと ![キャプションを入力できます](https://camo.elchika.com/c63195d9e83197bb60662d77e53ad06f79fd475a/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f66333132303635322d646430312d346433332d393262342d3235653634353563613832392f62346562306463622d626138302d343835312d613637372d376430653030386562313661/) # コード ```java <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" /> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script src="https://unpkg.com/obniz@3.x/obniz.js" crossorigin="anonymous" ></script> <style type="text/css"> .btn-circle { display: inline-block; text-decoration: none; font-size:80px; background: #2ECFCA; color: #FFF; width: 120px; height: 120px; line-height: 120px; border-radius: 80%; text-align: center; font-weight: bold; overflow: hidden; box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.05); border-bottom: solid 6px #359ddb; transition: .05s; } .btn-circle:active { -webkit-transform: translateY(4px); transform: translateY(4px); box-shadow: 0 0 1px rgba(0, 0, 0, 0.15); border-bottom: none; } body{ background-color: #F9F3DE; font-size: 20px; font-weight: bold; -webkit-touch-callout: none; -webkit-user-select:none; // テキスト長押しの選択ボックスを無効化 } p{ font-family: "Arial", "メイリオ"; } </style> </head> <body> <div id="obniz-debug"></div> <div style="inline"> <span>TotalPoint:</span> <span id = "score"></span> </div> <span>Time:</span> <span id = "cntdown"></span> <div id="text-button"><p id="text" style="text-align:center;font-size: 20px;width: 250px;border: solid">Ready??(Push Start)</p></div> <div class="mt-5"> <div class="text-center"> <div id="front" class="btn-circle" ontouchstart="">↑</div> <div id="turn_l" class="btn-circle" ontouchstart="">↻</div> </div> <div class="text-center"> <div id="back" class="btn-circle" ontouchstart="">↓</div> <div id="turn_r" class="btn-circle" ontouchstart="">↺</div> </div> </div> <script> /*RFIDミニゲーム ver0*/ var obniz = new Obniz("OBNIZ_ID_HERE"); var score = 0; var timer1 = null; var cntdown = 90; var timeLimit = cntdown * 1000; let startTime; var flag = 1; var random_tag_flag = 0; var plus_time = 30; //ランダムダグ 時間追加 var plus_score = 30; //ランダムダグ ポイント追加 var minus_score = 10; //ランダムダグ ポイント追加 var temp_tag = ""; //一つ前に読み取ったタグ document.getElementById('score').innerHTML = score; //スコア表示 document.getElementById('cntdown').innerHTML = cntdown; //カウントダウン表示 const tags_normal = [ {uid:"[204,1,174,46,77]",point:10}, {uid:"[138,169,67,134,230]",point:10}, {uid:"[42,205,7,133,101]",point:10}, {uid:"[172,208,70,35,25]",point:10}, {uid:"[106,210,50,134,12]",point:10}, {uid:"[183,47,208,198,142]",point:10}, {uid:"[202,206,63,134,189]",point:10}, ]; const tag_random = "[234,24,49,134,69]" //タイマー function updateTimer(){ cntdown = startTime + timeLimit - Date.now(); document.getElementById('cntdown').innerHTML = parseInt(cntdown/1000); const timeoutId = setTimeout(() => { updateTimer(); }, 10); if(cntdown <= 0){ document.getElementById('cntdown').innerHTML = "Time UP!!"; flag = 0; } } //ランダムタグ function random_tag(){ if(random_tag_flag == 0 ){ var ram_no = Math.floor( Math.random() * 3 ); switch(ram_no){ case 0: //残り時間追加 console.log("時間追加!!"); timeLimit += plus_time*1000; random_tag_flag = 1; document.getElementById("text").innerHTML = "Time Plus!!"; break; case 1: //スコア追加 console.log("スコア追加!!") score += plus_score; random_tag_flag = 1; document.getElementById('score').innerHTML = score ; document.getElementById("text").innerHTML = "Point Plus!!"; break; case 2: //クラッシュ console.log("クラッシュ!!") score -= minus_score; random_tag_flag = 1; document.getElementById('score').innerHTML = score ; document.getElementById("text").innerHTML = "Point Minus!!"; break; } } }; obniz.onconnect = async function () { // If obniz connect var mfrc522 = obniz.wired("MFRC522", { cs: 0, clk: 1, mosi: 2, miso: 3, gnd: 5, rst: 4, vcc: 6 }); //RC522ライブラリ ピン配置は数値の通りに行う //wire one motor (left) to obniz let motorA = obniz.wired("DCMotor", { forward: 7, back: 8 }); motorA.power(60); //wire the other motor (right) to obniz let motorB = obniz.wired("DCMotor", { forward: 9, back: 10 }); motorB.power(60); //ボタンを押してスタート document.getElementById("text-button").onclick = async function() { document.getElementById("text").innerHTML = "Go!!"; startTime = Date.now(); //メインループ while(flag) { try { //while upper front button is clicked $("#front").on("touchstart mousedown", () => { //rotate left motor forward motorA.move(true); motorB.move(true); console.log("前進") }); //if upper front button is released $("#front").on("touchend mouseup", () => { //stop left motor motorA.stop(); motorB.stop(); }); //while back left button is clicked $("#back").on("touchstart mousedown", () => { //rotate left motor backward motorA.move(false); motorB.move(false); }); //if lower back button is released $("#back").on("touchend mouseup", () => { //stop left motor motorA.stop(); motorB.stop(); }); //while turn_left upper button is clicked $("#turn_l").on("touchstart mousedown", () => { //rotate right motor forward motorA.move(true); motorB.move(false); }); //if turn_left upper button is released $("#turn_l").on("touchend mouseup", () => { //stop right motor motorA.stop(); motorB.stop(); }); //while turn_right lower button is clicked $("#turn_r").on("touchstart mousedown", () => { //rotate right motor backward motorA.move(false); motorB.move(true); }); //if turn_right lower button is released $("#turn_r").on("touchend mouseup", () => { //stop right motor motorA.stop(); motorB.stop(); }); updateTimer(); let card = await mfrc522.findCardWait(); //タグidを検索 TF if(tags_normal.some((v) => v.uid === JSON.stringify(card.uid))&&temp_tag!=JSON.stringify(card.uid)){ const targetList = tags_normal.find((v) => v.uid === JSON.stringify(card.uid)); //ポイントを抽出 console.log("point",targetList.point); obniz.display.print("ポイント = ",targetList.point); score += targetList.point; //ポイントを加算 document.getElementById('score').innerHTML = score ; document.getElementById("text").innerHTML = "Get!"; // console.log("Card is detected!"); // console.log("UID : " + card.uid); // console.log("PICC Type : " + card.PICC_Type); }else if(tag_random==JSON.stringify(card.uid)&&temp_tag!=JSON.stringify(card.uid)){ //ランダムタグ random_tag(); }else{ console.log("ERROR!!") obniz.display.print("ERROR!!"); console.log("UID : " + card.uid); document.getElementById("text").innerHTML = "Get!"; } } catch(e) { // Not Found or Error obniz.display.clear() // console.error(e) }; }; }; }; </script> </body> </html> ``` # やってみてわかったこと・気になったこと ・モータ、RFIDを動かすまではサンプルコードがあるため簡単 ・ピンが少ないので大掛かりな装置は工夫が必要 ・ロボットの制御マイコンとして使うには反応が悪いためむいていない(こればかりはobnizの仕様のため仕方ない。) # 終わりに 今回の作業を通じてobnizを本格的に触りましたが、とても素晴らしいものです。このデバイスを足掛かりにスキルアップに努めたいと思います。