以前、CGIを使ってjavascriptからpythonへ値を受け渡す方法についてまとめました。↓
これは、ブラウザをコントローラーとして、jsからpythonに値を渡し、サーボモーターを制御したい為に採用した方法になります。python httpサーバー(HTML)→js(Ajax)→pythonスクリプト(CGI)→モーター
しかし、例えばDCモーターを制御したいだけならばpythonスクリプトでマイコンのgpioピンの出力をHIGHE,LOWにすればいいだけですので(それだけで連続して動いてくれる)、jsからクリックイベントで単発の値を受け渡すだけで良いですが、サーボモーターの場合ですとそうはいきません。
pythonスクリプトではwhileのループ処理が使えず、一連の動きの後に止まってしまうので、連続して動かしたい場合は都度jsの発火イベント(htmlのボタンをクリック)を発生させる必要があります。
今回はこれを解消すべく、「ボタンを押し続けている間は一定の間隔でjsからpythonへ値を受け渡す」方法についてまとめたいと思います。これで、例えば2足歩行ロボットの歩行動作を、ボタンを押している間は前進し、離すと止まる、といったことが可能になります。
jsの「touchstart」と「setInterval」を使います。
html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>テスト</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="style.css"> </head> <body> <div class="cam"> <img src="http://ローカルIP/?action=stream"/> </div> <main> <ul> <div class="a"> <li id="forward" class="ledoff">前進</li> </div> <div class="bc"> <div class="b"> <li id="left" class="ledoff">左旋回</li> </div> <div class="n"> <li></li> </div> <div class="c"> <li id="right" class="ledoff">右旋回</li> </div> </div> <div class="d"> <li id="backward" class="ledoff">後退</li> </div> </ul> </main> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script> <script src="main.js"></script> </body> </html> |
操作画面になります。
javascript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
$(function(){ let motor = "STOP"; // 「前進」ボタンが押されたときのイベント処理 $('#forward').bind('touchstart', function() { // 押されたとき if(motor == 'STOP') { $(this).addClass('ledon'); change_motor('FOWARD'); ff1 = setInterval(function(){ change_motor('FOWARD'); }, 2000); } }).bind('touchend', function() { // 離したとき $(this).removeClass('ledon'); change_motor('STOP'); clearInterval(ff1); }); // 「後退」ボタンが押されたときのイベント処理 $('#backward').bind('touchstart', function() { if(motor == "STOP") { $(this).addClass('ledon'); change_motor('BACKWARD'); ff1 = setInterval(function(){ change_motor('BACKWARD'); }, 2000); } }).bind('touchend', function() { $(this).removeClass('ledon'); motor = "STOP"; change_motor('STOP'); clearInterval(ff1); }); // 「右」ボタンが押されたときのイベント処理 $('#right').bind('touchstart', function() { if(motor == "STOP") { $(this).addClass('ledon'); change_motor('RIGHT'); ff1 = setInterval(function(){ change_motor('RIGHT'); }, 2000); } }).bind('touchend', function() { $(this).removeClass('ledon'); motor = "STOP"; change_motor('STOP'); clearInterval(ff1); }); // 「左」ボタンが押されたときのイベント処理 $('#left').bind('touchstart', function() { if(motor == "STOP") { $(this).addClass('ledon'); change_motor('LEFT'); ff1 = setInterval(function(){ change_motor('LEFT'); }, 2000); } }).bind('touchend', function() { $(this).removeClass('ledon'); motor = "STOP"; change_motor('STOP'); clearInterval(ff1); }); // 関数:モーターを動かすマクロ呼び出し function change_motor(typee) { motor = typee; if(typee == "FOWARD") { // 前進 //w().callMacro('FW'); console.log(typee); $.ajax({ url: 'cgi-bin/recieve.py', type: 'post', data: {name: 'hhhhh' } }).done(function(data){ console.log(data); }).fail(function(){ console.log('failed'); }); } else if(typee == "BACKWARD") { //w().callMacro('BK'); console.log(typee); $.ajax({ url: 'cgi-bin/recieve.py', type: 'post', data: {name: 'iiii' } }).done(function(data){ console.log(data); }).fail(function(){ console.log('failed'); }); } else if(typee == "RIGHT") { //w().callMacro('RT'); console.log(typee); $.ajax({ url: 'cgi-bin/recieve.py', type: 'post', data: {name: 'jjjj' } }).done(function(data){ console.log(data); }).fail(function(){ console.log('failed'); }); } else if(typee == "LEFT") { //w().callMacro('LT'); console.log(typee); $.ajax({ url: 'cgi-bin/recieve.py', type: 'post', data: {name: 'kkkk' } }).done(function(data){ console.log(data); }).fail(function(){ console.log('failed'); }); } else if(typee == "STOP") { //w().callMacro('ST'); console.log(typee); $.ajax({ url: 'cgi-bin/recieve.py', type: 'post', data: {name: 'ssss' } }).done(function(data){ console.log(data); }).fail(function(){ console.log('failed'); }); } } }); |
以前まとめたのソースではclickイベントとしていましたが、今回は「touchstart」イベントを使用しました。スマホ専用になりますが、タッチすると{}に記述したメソッドが実効されます。今回はそこに「setInterval」関数を埋め込んでいます。これは、指定した間隔で{}内のメソッドを繰り返します。したがって、今回の場合だとボタンを押している間は2000ms/毎にchange_motor関数が呼び出される仕組みになっています。
change_motor関数では、Ajaxでpythonに文字列をpostしているので、つまり2000ms/毎に文字列をpostする事になります。(モーターを動作させるトリガー)
ボタンを離せば「touchend」イベントが発生するので、change_motor関数でモーターを停止させる文字列を指定し、かつ「clearInterval」でsetIntervalを停止させています。clearIntervalの引数にはsetInterval関数を指定します。
なお、setInterval内の処理は、1度目も引数に指定した時間経過後に実行されてしまうので、それを防ぐ意味でsetIntervalの前にchange_motor関数を1度呼び出して実行させています。
また、setIntervalの時間指定は、「サーボモーターの一連の動作終了時間」に合わせると良い感じで連続して動作します。
python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#!C:\\Users\\xxxx\\AppData\\Local\\Programs\\Python\\Python39\\python.exe import cgi import time form = cgi.FieldStorage() recieve = form.getvalue('name') if recieve == 'hhhhh': print('Content-type: text/html\n') print(recieve) elif recieve == 'iiii': print('Content-type: text/html\n') print(recieve) elif recieve == 'jjjj': print('Content-type: text/html\n') print(recieve) elif recieve == 'kkkk': print('Content-type: text/html\n') print(recieve) elif recieve == 'ssss': print('Content-type: text/html\n') print(recieve) |
今回は分かりやすくjsからpostされた値をjsに返して、コンソール画面に出力するだけのコードにしています。
それでは、python http serverを立ち上げて動作を確認してみます。
うまく動作していれば、ボタンを押している間は、指定した時間の間隔で文字列がやり取りされているはずです。↓