ラビット・エンジニアリング

趣味の電子工作を、備忘録もかねて・・・・

M5CAMERAでラジコン戦車作ってみた〜【Part6: picoweb使ってモーターをリモート駆動する】

はじめに

PicoWebサーバーを利用して、PCやスマホのブラウザからM5CAMERAにGroveケーブルで接続したモータを動かしてみます。これが完成すれば、いわゆるラジコンが作れたことになります。

 

全体構成

WiFi経由でモータを動かすイメージを図で説明します。

  1. スマホとM5CAMERAをそれぞれWiFiに接続
  2. PicoWebサーバー上で操作画面となるHPを稼働
  3. スマホのブラウザから操作画面のHPにアクセスしスマホで表示
  4. ブラウザ上でボタンを押すと、PicoWebサーバーを介してPythonプログラムが駆動
  5. PythonプログラムにI2C経由でモータドライバに指令

f:id:Pin-Pon-Usagi:20210604202534p:plain

図1:picowebサーバーを用いて、スマホからモータを動かす信号伝達イメージ

 

 

ブラウザの画面に、ON/OFFボタンを作成し押すとモータが動いて戦車のキャタピラが回るようにします。 

M5CameraのPythonプログラム

 電源投入後、boot.pyが自動的に実行されWiFiに接続します。boot.pyに続いてmain.pyが実行されるので、main.pyの中でPicoWebサーバを稼働させて、スマホからのアクセス待ち状態にしておきます。main.pyのプログラムは以下のようにしました。

 

main.py

import picoweb

import machine

 

app = picoweb.WebApp('app')

i2c = machine.I2C(scl=machine.Pin(13), sda=machine.Pin(4))

 

@app.route("/")

def index(req, resp):

    yield from picoweb.start_response(resp, content_type = 'text/html')

    htmlFile = open('btnCtrl.html', 'r')

    for line in htmlFile:

        yield from resp.awrite(line)

 

 

@app.route("/ButtonPressed")

def index(req, resp):

    queryString = req.qs

    equalSplit = queryString.split("=")

    yield from picoweb.start_response(resp)

    yield from resp.awrite(equalSplit[1])

 

    val = int(equalSplit[1])

 

    if val==0:  #Go

        i2c.writeto_mem(101, 0,b'\x45')

        i2c.writeto_mem(96,  0,b'\x45')

 

    if val==1:  #Stop

        i2c.writeto_mem(101, 0,b'\x7c')

        i2c.writeto_mem(96,  0,b'\x7c')

 

app.run(debug=True, host = '192.168.0.148')

 

M5CAMERAのPythonプログラム解説

  • import部分では、picowebとmachineをインポートして、picowebサーバーとI2Cを使えるようにしています
  • @app.route("/")部分は、ブラウザからM5CAMERAのIPアドレスにアクセス時に実行されるプログラムです。ブラウザに表示されるhtmlを読み込んでブラウザからのリクエストに対し、htmlをレスポンスとして返します。
  • @app.route("/ButtonPressed") この部分は、ブラウザでボタンが押された時のアクションを定義しています。押したボタンごとに異なる値をパラメータとして送るように設定しておき、受け取ったリクエストに含まれるパラメータを確認して、0の場合は前進命令を、1の場合は停止命令をI2Cを用いてモータドライバに送っています。
  • app.run(debug=True, host = '192.168.0.148')の部分で、指定したIPアドレスでwebサーバーを稼働します。このアドレスにブラウザからアクセスすると、コントロール画面がレスポンスとしてブラウザ側に帰されます。

ブラウザ側のhtmlプログラム

htmlで操作画面を作ります。

  • Goボタンを押した時に、Goボタンが押された事をサーバーに送る
  • Stopボタンを押した時に、Stopボタンが押された事をサーバーに送る

HTMLの画面とプログラムはこんな感じ。

 

f:id:Pin-Pon-Usagi:20210627150955p:plain

ブラウザに表示される操作画面。GoとStopボタンのみ。statusにパラメータを表示

btnCtrol.html

<!DOCTYPE html>

<html>

    <head>

        <title>Remote Test</title>

    </head>

    <body>

        <center>

        <br><div id="status"> status </div><br>

        <button type="button" onclick="MyFunction(this)"    id="0"    >Go     </button>

        <button type="button" onclick="MyFunction(this)"    id="1"    >Stop   </button>

        <p> @2021 Developed by Ping-Pong-USAGI</p>

        </center>

    </body>

    

    <script>

        function MyFunction(e){

            var id_value = e.id;

            var xhr = new XMLHttpRequest();

            xhr.onreadystatechange = function() {

                if (this.readyState == 4 && this.status == 200) {

                    var resp = this.responseText;

                    document.getElementById("status").innerHTML = resp;

                }

            }

            xhr.open("GET", "ButtonPressed?param=" + id_value, true);

            xhr.send();

        }

    </script>

</html>

 

ブラウザ側のhtmlプログラムの解説

<body>~</body>部分は画面レイアウトで、画面中央にボタンを2つ設置しています。

 

<script>~<script>の部分で、設置した2つのボタンクリックに対するアクションを定義しています。ボタンがクリックされると、

  • id_value変数に、ボタンのidを格納
  • XMLHttpRequestを生成し、非同期でmain.pyの@app.route("/ButtonPressed")を呼び出します。

@app.route("/ButtonPressed")の内容

ボタンがクリックされると、下記のプログラムが呼び出されます。

 

def index(req, resp):

    queryString = req.qs

    equalSplit = queryString.split("=")

    yield from picoweb.start_response(resp)

    yield from resp.awrite(equalSplit[1])

 

    val = int(equalSplit[1])

 

    if val==0:  #Go

        i2c.writeto_mem(101, 0,b'\x45')

        i2c.writeto_mem(96,  0,b'\x45')

 

    if val==1:  #Stop

        i2c.writeto_mem(101, 0,b'\x7c')

        i2c.writeto_mem(96,  0,b'\x7c')

 

 

ブラウザ側で、GoとStopのどちらのボタンがクリックされたのかを判定するために、html側では以下のようなxmlHttpRequestのopenメソッドでid_valueとしてパラメータを渡します。

 

xhr.open("GET", "ButtonPressed?param=" + id_value, true);

 

Pythonプログラム側では、equalSplit = queryString.split("=")のように、split()関数を用いて、文字列”=” を目標にして、パラメータとして受け取った文字列を分割することで、パラメータ部分を抽出します。処理の完了を通知するために、パラメータはxml側に返されてブラウザに表示しています。パラメータの値が0の場合は戦車を前進、1の場合はストップするようにモータドライバにi2cで命令します。

 

動作確認

boot.py, main.py, btnCtrol.htmlの3ファイルをampyコマンドでM5Cametaに転送して再起動します。wiif接続が確立したら、ウェブブラウザから、M5CameraのIPアドレスにアクセスします。アドレスが、192.168.0.148の場合、ブラウザのアドレス入力部分に

192.168.0.148:8081/ と入力します。@app.route("/")部分が実行されて、btnCtrol.htmlが読み込まれることでブラウザに操作画面が表示されます。あとはボタンをクリックして戦車が動けばOKです。

 

こちらに動作確認風景を録画しました。

 

www.youtube.com