先日公開した、Chromebook・Windows・Mac・各種スマホで使えるwebUSBの技術を用いたプログラミング教材について、技術的な解説・カスタマイズ紹介の第2回目です。今回は初級編から若干中級編まで含んだ内容をお伝えします。
教材自体の作り方・導入方法はこちら↓
改良編第1回はこちら↓。本編では省略されたアナログ入力や気圧センサの使い方、Chromebookでの開発方法などを紹介しています。
第2回目では、モータの駆動、プログラミングアプリのカスタマイズ方法、などについて解説していきます。
モータの駆動
教材本体にモータを拡張し、ファームウェアもカスタマイズしてモータを動かせるようにしてみます。拡張機器の都合上、今回はこれまで使っていたPro MicroではなくArduino Leonaldベースで教材を作り直しています。
ハードウェアの準備
モータを動かすには、モータ部品以外にモータドライバが必要です。Arduinoではモータシールドが販売されているので、これを使うのが手ごろです。いわゆるL298というモータドライバを搭載した製品です。
しかし、このモーターシールドはいわゆる普通のArduinoサイズ・ピン配置向けの商品であり、Pro Microで使えるかどうかいまいちわかりません。多分ピン配置を合わせれば使えると思われますが、調べるのも大変なので、今回は手元に転がっていたArduino Leonaldで試してみます(手抜きですみません…)。
本編記事でも紹介した通り、Pro MicroとArduino Leonaldは互換性があるので、今回の教材のファームウェアを書き込んで動かすことができます。Arduino Leonardには上からかぶせるだけでモータシールドを取り付けられます。
モータはモータシールドの端にある水色のコネクタに接続します。このモータシールドは2個のモータを接続でき、コネクタの+/-の端子にそれぞれモータを配線します。
今回は入手しやすいFA130タイプのモータを使いました。これ以外のモータも使用できると思いますが、電源と必要な出力を考慮して別途準備してください(あまり大きなモータは動かしづらいと思われます)。
このモータシールドでは、特定のポートがモータ駆動用に定義されていて、s個で指定されたポートはユーザが任意に使えないようになっています。具体的には下記のポートがモータ用に使用されます。
- 回転方向: D12,D13
- PWM(回転速度): D3,D11
- ブレーキ: D9,D8
- 電流センサ:A0,A1
このうち、現在ファームウェアではD9をLED1、A0/A1をアナログ入力に使っているので、これらが本来の機能として利用できなくなります。とはいえA0/A1はセンサ値に電流値が入るだけなので、A0/A1に電流センサを接続したとも言えます。LEDについては、プログラミングアプリのカスタマイズも大変なので、今回はLED1/LED2を二つのモータに振り替えて使うことにします。
後は、モータをしっかり動かすために電池ボックスなどの外部電源も必要です。一応FA130モータを2個空転させる程度なら、USBバスパワーで何とか足りるようなので、省略できるかもしれませんが、スマホ・タブレットだと電力が不足するかもしれません。簡単なのは5VのACアダプタを購入してArduino Leonaldに接続する方法があります。
ファームウェアの改造
ハードウェアの準備ができたら、ファームウェア内のLED関連の処理をモータ出力に差し替える改造をしていきます。モータ自体の制御については、サンプルソースを探すと、下記などの記事が見つかりました。これらを参考に改造していきます。
https://www.qoosky.io/techs/8368bd61f2
LEDのポートを設定したマクロをコメントアウトし、代わりにモータのポートの定義を加えましょう。これに関する設定はファームウェアの冒頭付近にあります。モータ制御用のポートのうち、回転速度に使用するのはD3,D11なので、これらをMOT1_PIN、MOT2_PINとして定義します。
//LED出力ピン定義 下記2行をコメントアウトします。 //#define LED1_PIN (9) //#define LED2_PIN (6) //下記2行を追記します。 #define MOT1_PIN (3) #define MOT2_PIN (11)
併せて、メモリマップの名称も変えておきます。
#define SENSOR2_VAL (12) #define SENSOR3_VAL (13) #define SENSOR4_VAL (14) //下記2行をコメントアウト //#define LED1_VAL (15) //#define LED2_VAL (16) //下記2行を追加 #define MOT1_VAL (15) #define MOT2_VAL (16) #define SW_VAL (17) #define VAR1 (18)
次は初期化に関する処理を追加します。具体的には、モータ制御に割り当てられたポートのうち、出力に関するものを全てOUTPUTに設定する必要があるようです。ファームウェアのvoid setup()関数にそれに関する処理を追記していきます。またモータ速度やブレーキなどを初期化します。
void setup() { memmap_clear(); //下記2行をコメントアウト //pinMode(LED1_PIN, OUTPUT); //pinMode(LED2_PIN, OUTPUT); //下記6行を追加 pinMode(12, OUTPUT); // MOT1 回転方向 pinMode(9, OUTPUT); // MOT1 ブレーキ pinMode(MOT1_PIN, OUTPUT); // MOT1 回転速度 pinMode(13, OUTPUT); // MOT2 回転方向 pinMode(8, OUTPUT); // MOT2 ブレーキ pinMode(MOT2_PIN, OUTPUT); // MOT2 回転速度 //モータ関連の初期化処理を追加 memmap[MOT1_VAL] = memmap[MOT2_VAL] = 0; digitalWrite(9, LOW); // ブレーキ (LOW:無効) digitalWrite(8, LOW); // ブレーキ (LOW:無効) pinMode(SW_PIN, INPUT ); EEPROM_size = /*EEPROM.length()*/1024;
次にメモリマップ上のモータ速度を実際のモータに反映させる処理を記述します。こちらは void loop()関数の中に該当箇所があります。
//実行コードを1step進める step_seq(); //メモリマップの更新 //下記2行をコメントアウト //analogWrite( LED1_PIN, memmap[LED1_VAL] ); //analogWrite( LED2_PIN, memmap[LED2_VAL] ); //下記を追加 { char mot1_spd=memmap[MOT1_VAL],mot2_spd=memmap[MOT2_VAL]; if(memmap[MOT1_VAL]&0x80){ digitalWrite(12, HIGH); // 回転方向 (HIGH:正転) mot1_spd = 0x100 - mot1_spd; } else digitalWrite(12, LOW); // 回転方向 (LOW:逆転) if(memmap[MOT2_VAL]&0x80){ digitalWrite(13, HIGH); // 回転方向 (HIGH:正転) mot2_spd = 0x100 - mot2_spd; } else digitalWrite(13, LOW); // 回転方向 (LOW:逆転) analogWrite( MOT1_PIN, mot1_spd ); analogWrite( MOT2_PIN, mot2_spd ); } memmap[RANDOM]= random(255); memmap[TIMER]= counter/1000;
このモータシールドでは、モータの速度と回転方向を個別に設定でき、モータ速度は0~255から設定できます。今回はメモリマップの該当アドレスの-128~127の値で速度と回転方向両方を表すようにしようとおもいます。該当アドレスの数値がマイナスになると、0x80(128)でAND演算をすると0以外になるのでそこで方向を判断し、またマイナスの値の場合は0x100(256)から数値を減算して正の値に変換し、どちらの回転方向でも同じ速度値になるようにしています。
あとは、もしスイッチを未接続なら、スイッチの値がちらちら変化して勝手にプログラムが実行されてしまうので、スイッチ入力に応じてプログラムを実行する部分をコメントアウトしておきます。これはvoid loop()の最後に処理が書かれています。
memmap[SENSOR1_VAL]= (analogRead( SN1_PIN )>>2) & 0xff; memmap[SENSOR2_VAL]= (analogRead( SN2_PIN )>>2) & 0xff; memmap[SENSOR3_VAL]= (analogRead( SN3_PIN )>>2) & 0xff; memmap[SENSOR4_VAL]= (analogRead( SN4_PIN )>>2) & 0xff; //スイッチが未接続だとプログラムが勝手に実行されるので、その場合はコメントアウトする //if(memmap[SW_VAL]!=0 && memmap[EXEC_MODE]==EXEC_NONE) memmap[EXEC_MODE]=EXEC_START; }
さて、これで動かしてみるとアプリとの通信が何やらうまくいきません。モータ以外のハードウェアを接続していないことに起因するのでしょうか?特にI2Cデバイスは影響がありそうです。
改造したハードウェアにBME280を配線してみたところ、一応動くようになった感じですが、常に気温が250度・湿度が100%となり、数値が異常です。どこかのポート設定が干渉しているのか、原因が全くつかめません。仕方ないのでBME280に関する処理をコメントアウトして使うことにします。
void setup()内の「setup_bme280()」をコメントアウトします。
//I2Cに関する処理をコメントアウト //setup_bme280();
void loop()内の「bme280_task()」もコメントアウトします。
//I2Cに関する処理をコメントアウト //bme280_task();
それぞれ該当箇所が見つからない場合は、コメントアウトする関数名でソースを検索するとすぐ見つかります。
ちなみに、改造前のファームでBME280を正しく配線して動かしてみても何故かうまく動かないようで、モータに関係なく、Arduino LeonardでBME280がうまく動いていないだけのようでした。ただ、Arduino LeonardでBME280が使えないというのも腑に落ちないので、何か配線を間違えているのか初期化等に良くない点があるのかもしれません(Pro Microに戻してみるとちゃんと使えるので、基板の故障ではない…)。
これでようやく動かせるファームが出来上がったので、教材に書き込んで実際にモータを回してみましょう。プログラミングアプリでは、上級者向けのツールボックスに切り替えて、変数演算ブロックからLED1、LED2に何か数値を代入するプログラムを作って実行してみます。
実行するとモータが回り始めます。使用しているモータの種類によっては、数値が小さすぎて回らなかったり、逆に大きすぎて回転が速すぎたりするので、うまく動かない場合は数値をちょっとずつ変えて試してみましょう。なお、プログラムが終了してもメモリマップ上の速度が0にならない限りモータは回りっぱなしになります。
ちなみに、モータを使っていることが原因かわかりませんが、通信がまだ安定しないようで、時々全然通信できなくなったりします。そんな場合は一度教材をPC・タブレットから抜き差しして認識しなおすと治るかもしれません。
プログラミングアプリの改造
これまでの改造例は全てハードウェアまたはファームウェアに関する内容でしたが、PC・タブレット側のプログラミングアプリも、ファームと同じ下記リポジトリに追加されており(webフォルダ以下)、カスタマイズすることが可能です。
https://github.com/vstoneofficial/webusb_programland
例えば使用するブロックやメモリマップの内容などをカスタマイズできますが、一つ問題点として、そもそもこのアプリはvstoneのサーバ上にアップロードされているものをブラウザ・専用アプリから使う仕組みのため、PCのローカルファイルをカスタマイズしたものを使うには、別途サーバ環境を準備する必要があります。
その準備に関する説明と、前半で説明したモータ拡張に関する内容を改造例として紹介していきたいと思います。
サーバ環境の準備
まずは動かすためのサーバの準備です。もし既にファイルをアップロードできるサーバをお持ちの方は、そちらを利用すれば良いので以下の作業は不要です(ファイルの文字コード・アクセス権限等は正しく設定してください)。そうでない方は、PCのローカル環境に簡易サーバを立てる方法があります。
ローカルサーバを立てる方法はいくつかありますが、おそらく簡単なのはpythonのサーバ機能を利用する方法かと思います。pythonはMac・Chromeでは標準でインストールされており、Windowsでもインストーラを実行するだけで簡単に導入できます。また今回は1~2行の決められたコマンドを実行するだけで、python自体のプログラミングを学習する必要はありません。
なお、iPad/iPhone/Androidなどスマホ・タブレット環境をご利用の場合は、直接その端末上で開発・ローカルサーバによる稼働はできないため、別途PC上で開発し起動したサーバにつなぐ形になります。詳しい方法は後述します。
pythonのインストールとサーバの起動
Windowsの場合、下記などを参考にpythonをインストールします。基本的にインストーラが配布されておりそれを実行するだけのようです。pythonはバージョン3系を選択してください。
https://www.python.jp/install/windows/install.html
インストールしたら、コマンドプロンプトを開いて、下記のようにソース一式が含まれたディレクトリに移動し、pythonのコマンドを実行します。ここでは「C:\arduino_webusb\web」にソース一式があるものとします。この部分はお使いの環境に応じて正しいディレクトリ名に置き換えて下さい。
cd "C:\arduino_webusb\web" python -m http.server
入力すると、画面に「Serving HTTP on 0.0.0.0 port 8000 …」と表示されます。この状態でブラウザを開き、URL欄に「http://localhost:8000/」と入力すると、今回のプログラミングアプリと同じ画面が表示されます。これはvstoneで公開しているものではなく、PCのローカルにあるソース一式を読み込んで表示されたものです。
Macの場合、デフォルトでpythonがインストールされていますが、バージョンが古いことが多いようです。執筆環境で試したところではpython2系でした。この場合、ターミナルを開いて、下記のようにソース一式が含まれたディレクトリに移動し、pythonのコマンドを実行します。ここではデスクトップ上の「arduino_webusb/web」ディレクトリにソース一式があるものとします。この部分はお使いの環境に応じて正しいディレクトリ名に置き換えて下さい。
cd Desktop/arduino_webusb/web python >>> import SimpleHTTPServer >>> SimpleHTTPServer.test()
入力すると、画面に「Serving HTTP on 0.0.0.0 port 8000 …」と表示されます。この状態でブラウザを開き、URL欄に「http://localhost:8000/」と入力すると、今回のプログラミングアプリと同じ画面が表示されます。
尚、上記の方法はpython2系のみの可能性があります。python3系を使用している場合は、Windowsでの方法と同じく「python -m http.server」をお試しください。pythonのバージョンはターミナル上で「python -V」と入力すると確認できます。また、参考までにMacでpython3系に切り替える方法についてリンクを紹介します。
https://yukun.info/python-simplehttpserver/
Chromebookでは、まずLinuxをONに切り替える必要があります。ONにする方法は、前回の改良編1でChromebookにArduino IDEを導入する方法でも説明しましたが、Chromebookの設定画面を開いて下記のように操作します。
切り替えには数分時間がかかります。切り替え後にターミナルが自動で開く場合がありますので、それを使って続きの作業ができます。自分でターミナルを開く場合は、下記のようにアプリ一覧の「Linuxアプリ」に含まれるターミナルを選択してください。
LinuxをONにしたら、念のためpythonのバージョンを調べてみましょう。ターミナルを開いて「python -V」「python2 -V」「python3 -V」とそれぞれ入力してみてください。もしいずれかのコマンドで「Python x.x.x」のようにバージョン番号が表示されたら、それがお使いのPCで利用できるpythonコマンドとバージョンです。「command not found」と表示されたものは、そのPCでは使えないコマンドです。執筆環境では、「python3」コマンドのみ使えました。
それではpythonからサーバを起動させます。ソースファイル一式をLinuxフォルダにコピーしてください。
コピーしたらターミナルを開き、下記のようにソース一式が含まれたディレクトリに移動し、pythonのコマンドを実行します。ここではLinuxフォルダの「arduino_webusb/web」にソース一式があるものとします。この部分はお使いの環境に応じて正しいディレクトリ名に置き換えて下さい。まずはpython3系のコマンドです(「python3」ではなく「python」で呼び出せる場合は適宜置き換えてください)。
cd arduino_webusb/web python3 -m http.server
python2系の場合は下記を実行してください。
cd arduino_webusb/web python2 >>> import SimpleHTTPServer >>> SimpleHTTPServer.test()
入力すると、画面に「Serving HTTP on 0.0.0.0 port 8000 …」と表示されます。この状態でブラウザを開き、URL欄に「http://localhost:8000/」と入力すると、今回のプログラミングアプリと同じ画面が表示されます。
Chromebook・Mac・Androidをお使いの場合は、ここまで来れたら上記のURLをChromeブラウザで開けば、カスタマイズしたプログラミングアプリを使用できます。スマホ・タブレットの場合は、PCと同じLANに接続し、以降の説明中のURLで「localhost:8000」と記載している箇所を「(PCのローカルIPアドレス):8000」のように置き換えてください。例えば、PCのIPアドレスが「192.168.1.78」であれば、スマホ・タブレットで「http://192.168.1.78:8000」を開いてください。
Windows・iOSアプリでカスタマイズしたプログラミングアプリを開く
Windows・iOSの場合は、カスタマイズしたアプリを使いますが、実は内部的にはそれぞれブラウジング機能でvstone上のプログラミングアプリページをそのまま開いている仕組みになっており、開くURLを差し替えることでカスタマイズしたアプリを利用できるようになります。
Windows用のプログラミングアプリには、実行ファイルの存在するフォルダに「settings.ini」という名前のテキストファイルがあります。こちらをテキストエディタで開くと1行目にプログラミングアプリのURLが記載されているので、こちらをカスタマイズしたアプリのURLに差し替えてください。ファイルを保存してアプリを実行すると、カスタマイズしたアプリが開かれます。
https://www.vstone.co.jp/webusb/programland/index.html Arduino Leonard
iOSアプリでも、画面内の表示をvstoneサーバの物からカスタマイズしたアプリのURLに差し替えることで、カスタマイズしたアプリを使えるようになります。アプリを起動し、「接続/切断」ボタンを10秒以内に10回連打すると、設定用の画面を開きます。こちらに標準で開くURLが記載されているので、これをカスタマイズしたURLに変更して、アプリを再起動させてください。
なお、もし本アプリのURL以外を設定したり、そもそも間違ったURLを設定するなどした場合、画面中に「接続/切断」ボタンが表示されないためURLを再度戻せなくなります。この場合、アプリを一度削除してインストールし直すと、標準のURLに戻せます(突貫で対応した機能で扱いづらくなっています。申し訳ありません)。
メモリマップの改変とモータブロックの追加
これでカスタマイズできる下地が揃いました。今回はモータの追加についても説明したので、せっかくだからプログラミングアプリを改良して、メモリマップのLEDの項目をモータに差し替え、またモータ制御用のブロックを追加してみましょう。
実は、モータ制御用のブロックは、ソース内にコメントアウトされているだけで実装されていたりします。jsフォルダ内のblockly_blocks.jsのファイルを開き、ファイルの末尾を見ると、「Blockly.Blocks[‘pro_move_value’]」というメソッドがコメントアウトされていますが、これがモータ制御用ブロックの処理になります。このコメントアウトを外して保存してください。具体的には、関数の記述場所前後にある「/*」と「*/」をそれぞれ削除します。
/* この行を削除 Blockly.Blocks['pro_move_value'] = { init: function() { this.jsonInit({ "message0": 'モータ 1%1 %% 2%2 %% %3', "previousStatement": null, "nextStatement": null, "tooltip": "モータを動かします。\n時間をくっつけると、その時間だけ動きます。\n時間をくっつけないと、動きっぱなしになります。", "args0": [ { "type": "field_number", "name": "lspd", "min": -100, "max": 100, "value": 50, "precision": 1, "width" : 100, }, { "type": "field_number", "name": "rspd", "min": -100, "max": 100, "value": 50, "precision": 1, "width" : 100, }, { "type": "input_value", "name": "VALUE", "check": "Number" } ], "colour": 210, }); }, topID:-1, bottomID:-1, makecode:function(){ Blockly.makecode_commonfunc(this); var lspd = this.getFieldValue('lspd')/100*127; var rspd = this.getFieldValue('rspd')/100*127; BLOCKAREA.code += `${itohex(CODE_MAP.RC_MEMW_B)}${itohex(MEMMAP_TBL.LED1_VAL)}02${itohex(lspd)}${itohex(rspd)}`; BLOCKAREA.address += 5; //時間指定があるか var inputBlk = this.getInputTargetBlock('VALUE'); if(inputBlk){ //指定時間待ち var waittime=inputBlk.getFieldValue('FIELDNAME') || 0; waittime*=1000; BLOCKAREA.code += `${itohex(CODE_MAP.RC_WAIT_W)}`; BLOCKAREA.code += `${itohex(waittime/256)}${itohex(waittime%256)}`; BLOCKAREA.address += 3; //時間待ち後モータを止める BLOCKAREA.code += `${itohex(CODE_MAP.RC_MEMW_B)}${itohex(MEMMAP_TBL.LED1_VAL)}020000`; BLOCKAREA.address += 5; } this.bottomID = BLOCKAREA.address; Blockly.makecode_bottom(this); } }; この行を削除 */
これで内部的にはブロックが使えるようになりますが、そのままではツールボックスにブロックが並んでいないので、プログラム上に追加できません。続いてツールボックスの内容を変更していきます。
ツールボックスの設定は、同じjsフォルダに入っているtoolbox.xmlで設定されています。このファイルの真ん中を少し過ぎたところに、下記のような記述があります。こちらもコメントアウトされている所なので、該当箇所の「<!–」「–>」を削除して有効にしてください。
<!-- この行を削除 <category name="モータ" colour="210"> <block type="pro_move_value"> <field name="lspd">50</field> <field name="rspd">50</field> <value name="VALUE"> <block type="do_sec"> <field name="FIELDNAME">1</field> </block> </value> </block> <block type="pro_move_value"> <field name="lspd">0</field> <field name="rspd">0</field> </block> </category> <sep gap="3"></sep> この行を削除 -->
次にメモリマップの名前を書き換えます。メモリマップの名称は、jsフォルダ内のrobot.jsで設定されています。このファイル内のconst MEMMAP_TBL_BLOCKを定義している部分で、下記のようにLEDの名前をモータに変更します。
const MEMMAP_TBL_BLOCK=[ ["PRODUCT_ID", 0], ["VERSION_ID", 1], ["EXEC_MODE", 2], ["SEQ_COUNT_MSB",3], ["SEQ_COUNT_LSB",4], ["ランダム", 5], ["時間", 6], ["気温", 7], ["湿度", 8], ["ブザー音程", 9], ["ブザー時間", 10], ["センサ1", 11], ["センサ2", 12], ["センサ3", 13], ["センサ4", 14], //下記2行をコメントアウト //["LED1", 15], //["LED2", 16], //下記2行を追加 ["モータ1", 15], ["モータ2", 16], ["スイッチ", 17], ["変数1", 18], ["変数2", 19], ["変数3", 20], ["変数4", 21], ["変数5", 22], ["変数6", 23], ["変数7", 24], ["変数8", 25], ];
ここまで修正したら、PC上でサーバを起動して、改造したアプリを開いて見ましょう。ツールボックスを上級者向けに変更すると「モータ」のカテゴリがツールボックスに追加されています。このブロックでは2個のモータの速度を-100~+100の範囲で設定できます。また時間のブロックをくっつけると、指定した時間だけモータを回して最後に止めるようになります。
また、メモリマップを開くと、ちゃんとLEDの所がモータに書き換わっており、変数演算ブロック上の選択肢もモータに切り替わるのでわかりやすくなりました。
このような感じで、プログラミングアプリも改造することができます。ファームウェアも含めて本格的に改造すると、かなりのカスタマイズができますので、興味がある方はソースを読み込んでみてください。
次回予告
内容が中級者向け以上になると、解説が長く難解になってくるため、中々提供できるネタが限られてきて難しいですが、ネタが続く限りは続けていきたいと思います。次回は教材との通信に関する処理、及びiOSでのMDIデバイスを利用したUSB通信に関する解説をしていきます。