前回開発したAndroidアプリに、画面をドラッグして歩行をコントロールする機能を追加します。右手と左手の親指をロボットの両足に見立てて画面上を歩くような動作で操縦できます。こちらのテーマは内容も複雑でコードの分量も増えますのでオプション(番外編)とします。スマホ画面に、ドラッグコントロールモードにするためのボタンと指の動きを拾うための部品(ImageView)を追加します。activity_main.xmlを変更します。次にMainActivity.javaの変更箇所を示します。ImageView上をドラッグするとonTouchイベントが呼ばれます。指を動かすとACTION_MOVEのところへ行き座標が取得できます。座標が領域の右側か左側か、縦方向の位置を0-100の値に変換してコマンドとして送信します。左:0)、command[2]:縦方向位置(0-100)となっていて、ESP32側で歩行プログラムの配列から関節角度を計算します。これまでサーボの動きを制御するのにArduinoのdelay()関数を使ってきました。delay()関数はスケッチ内容が比較的明確で単純な確認のような目的には有用ですが、ひとつ欠点があります。delay()実行中は他の処理が止まってしまうので、少し込み入ったプログラムの場合には不具合が生じることがあります。タイマーの割り込みは禁止されませんので前回までのプログラムは意図通りの動きをしてくれました。ここでは、delay()を使わないでサーボをコントロールする方法の例を示しておきます。具体的には、オーディオの再生で使用したタイマー割り込みをもう一つ30msecで常時まわして時間をカウントします。前回までの内容を全て盛り込んで完成版のロボットにしてみましょう。オリジナルの音声(フリーの効果音など)をArduinoのプログラムに組み込むために、2つのフリーソフトを利用します。以下のソフトをダウンロードしてインストールしてください。用意した音声ファイル(ここではrobot-footstep1.mp3というファイル)をAudacityで開きます。元の音声がステレオの場合には、「編集」->「選択」->「すべて」としてから、「トラック」->「ステレオからモノラルへ」でモノラルに変換します。PCM」として「保存」->「OK」します。次にstirlingを起動して今保存したwavファイルを開きます。右側の列を見て”data”となっているすぐあと辺りから後ろをすべて選択してコピーします。Windowsのメモ帳を開いて貼り付けます。エックス)を入力して「すべて置換」します。”0x”は16進数の印です。先頭のみ置換されていませんので”0x”を手入力したのち、全体をコピーしてArduinoプログラムの”sounddata.h”の当該箇所に貼り付けます。次にスピーカーから音声を鳴らしてみます。Converter)、これをオーディオアンプに入力してスピーカーを鳴らします。オーディオソースはサンプリング周波数8kHzのWAVデータとし、124usecごと(約8kHz)のタイマー割り込みでDAC端子にアナログ出力していきます。char)の値を行列データとしてプログラム内に直接記述し、フラッシュメモリに読み込ませます。圧縮していない形式で、1秒間の音声で1byte*8000=8Kbyte必要です。電源ONで「ロボットの足音」を1回だけ再生するプログラムを作っていきます。タイマー割り込みのスケッチ例を書き換えていきますので、「ファイル」->「スケッチ例」->「ESP32」->「Timer」->「RepeatTimer」を開いてmm08として保存してください。タイマー関連の不要部分を削除して、DAC関連のコードを追加します。音声データは”sounddata.h”として別ファイルで与えます。IDEの右側にある下向き三角をクリックして新規タブ名を”sounddata.h”としてOKすると新しいタブが現れますので、そこに以下をコピペします。ロボットで実行し音声が再生されるのを確認します。プログラムサイズが大きいので転送には時間がかかります。オーディオデータをインクルードします。wavデータの位置を124usecごとにカウントアップします。esp32マイコンのIO25ピンに音声データをアナログ出力します。onTimer()はタイマー割り込みで呼ばれる関数です。タイマー割り込みの間隔を124usecに設定します。オーディオアンプのCEピンをLOWにして有効化します。リモコンのボタンで音声が再生されるようにしてください。“mm08′′で保存します。前回のプログラムに少し追加してLEDをON/OFFしてみます。”mm06′′を開いて”mm07′′で名前を付けて保存します。“1”ボタンでON、”2′′ボタンでOFFです。loop()の中でdataCodeをシリアル出力していた部分をswitch文に書き換えて、dataCodeが”12′′(“1′′ボタンが押された)ならLEDをON、dataCodeが”24′′(“2′′ボタンが押された)ならLEDをOFFします。うまくいきましたら次はリモコンでロボットの歩行をコントロールしてみます。”1′′ボタンで前進、”2′′ボタンでストップです。現状のプログラムに以前作ったサーボのプログラム部分(mm05)を足していきます。リモコンボタンに割り当ててコントロールできるようにしてください。ヒント:リモコンボタンのデータコードは”mm06′′を実行して確認できます。“mm07′′で保存します。赤外線リモコンでロボットをコントロールしてみます。リモコン送信器にボタン電池(CR2025)を挿入してください。リモコン受信部はメイン基板上部の3本足の部品です。リモコン信号のフォーマットについてはこちらをご覧ください。図の横軸は時間で、左側からON,OFF,ON,OFF...とバーコードのような赤外線信号が受信器に到達します。これが5V,0Vの電圧に変換されてマイコンのピン(IO16)に入力されます。一応注意が必要なのが、ONが1信号、OFFが0信号とはならないことです。ONの後に長いOFFが来ると1、ONの後に短いOFFが来ると0と判断します。プログラムでは、IO16ピンの入力状態をチェックしてONの長さ、OFFの長さを時間計測しながら信号を解析(デコード)していきます。信号全体はリーダー、カスタムコード(16bit)、データコード(8bit)、データコード(8bit-反転)の順にやってきますのでその順で処理していき、今回はデータコード(8bit)の値を見てどのボタンが押されたのかを判断します。受信したデータコード(8bit)をシリアルモニタに出力してボタンに対するデータの値を確認します。いう「外部割込み」を使います(もう一つの割り込みは「タイマー割り込み」)。これを利用すると、ピンの状態を勝手にチェックしてくれて変化があった場合には指定の関数を自動で実行してくれます。プログラムは少し長いですが、フォーマットの図と見比べながら追ってみてください。リモコン受光部品の出力がHIGHやLOWに変化するたびにrmUpdate()が呼ばれ、HIGH時間、LOW時間を測定し信号が1なのか0なのかを調べていきます。ステートマシンという方法です。100%わからなくてもこのプログラムをコピペして実行し、リモコン送信でシリアルモニタに数字がでてくることを確認してください。“mm06′′として保存しておきます。ロボットを組み立て終わりましたらいよいよ電源を入れて立たせてみます。“mm03′′のプログラムを保存した方はもう一度開いて”mm05′′として保存します。loop()の中を削除します。この状態でロボットに転送して実行しますが、倒れないようにロボットは寝かせておきます。腕は割とラフで構いませんが、両足は前から見て、横から見て、まっすぐになるようにソフト的にこれを修正します。下記のように修正用の変数”offset[12]”(名前はなんでも良い)を使います。一応各関節(サーボ)に名前をつけました。サーボの角度指定のところに補正値を足して実際に出力される角度を修正しています。offset[]の値をいろいろ変えてロボットを立たせてみてほどよい値を見つけてください。サーボの回転方向はこちらの図で確認してください。最初から大きくずれてしまっている場合(30°以上)はサーボホーンを1歯ずらしてみてください。だいたい合ったところで電池を背負った状態で後傾になっていないかどうか確認します。後ろに体重がかかっていると歩行で倒れやすくなりますので、足首の関節で調節します。非常に根気のいる作業です。落ち着いて頑張りましょう。下図の矢印は各サーボの+の回転方向を示しています。調整が終わりましたら歩かせてみます。下記の変更部分を追加して動かしてみましょう。倒れずに前に進めば合格です。左右で動きが違う等の問題はイニシャル姿勢のオフセット調整で修正します。歩行動作の各関節の角度を配列で与えます。配列の行(全体の姿勢)を順番に送ってアニメーションさせます。データ更新の時間間隔を短くすれば滑らかな動きになりますが、逆にデータ量が増えてしまいます。このため大まかな姿勢データのみを与え(キーフレームと呼ぶ)、その間を細かい時間で補間する、という方法をとります。サーボのデータ更新は30msecごとに行うこととして(delay(30))、配列右端の数値でフレーム間を分割して角度が徐々に変化するようにします。いう感じで、分割カウンターは毎回+1して分割数に達したら0に戻します。そのタイミングで今回キーフレームと次回キーフレームを次の行に更新します。後ろに歩かせてください。左右旋回させてください。横歩きさせてください。サーボを動かしてみましょう。サーボの出力軸(白いギザギザのところ)は通常0°~180°の範囲で回転しますが、その位置はサーボの容器に対して絶対的に決まっています。そのため出力軸の回転位置を気にせずに適当にロボットを組み立ててしまうと、出力に取り付けられた部品が思い通りに動いてくれません。サーボの出力軸を90°の位置に停止させるプログラムをつくります。下の写真を参考に配線してください。サーボは電池から電源を供給する回路になっていますので、今回は電池をつなぎます。サーボは12個使いで対応するコネクタが12箇所ありますが、すべてのコネクタから90°の信号を出力します。サーボのコネクタは逆向きに挿しても動きませんが壊れることはありません。中央のピンがプラス電源となっているためです。電池のワイヤーは逆につけるとマイコンが壊れますので絶対に間違えないでください。コネクター側基板上に”G”と記載されている側がグランド(黒線)です。電源スイッチはOFF側にしておきます。サーボは非常に精密なギアで構成されています。出力軸を回そうとして(「バックドライブ」と言います。)引っ掛かりがある場合に無理に回そうとするとギアを破損します。ハードウェアの準備ができましたらプログラムを作っていきます。通常Arduinoでサーボを動かす場合専用の関数を使って簡単に駆動できるのですが、今回サーボが12個と多いので「PCA9685」というICを利用してESP32マイコンからI2Cで制御するようにします。IDEを起動して「ボード」と「シリアルポート」の設定をしてください。下記のプログラムを書き込みます。チェックボタンでコンパイルが成功したら、矢印ボタンでメイン基板に書き込みます。「ボードへの書き込みが完了しました。」と出たら基板の電源スイッチをONにします。サーボが動いて90°の位置(センター位置)で停止します。(新品のサーボを用いた場合はもともと90°の位置になっていて動かない場合があります。プログラムの内容的にはPCA9685とのI2C通信に関する部分がほとんどです。PCA9685のデータシートと回路図を参照しながら確認してください。初学者の方はsetup()の部分だけを見ておけばOKです。ArduinoでI2C通信を使う時にはプログラムの最初にこの行を書きます。IDEにあらかじめ用意されている”Wire.h”というファイルに実際にI2Cをコントロールするプログラムが書かれていて、我々は簡単にI2Cを使えるようになっています。ESP32のピンIO21,IO22にI2Cの機能を割り当てます。PINMAP)100kHzの周波数で使用します。PCA9685をイニシャライズし、角度を設定します。角度はグローバル変数としてtempAngles[12]で保持しています。IO19ピンをLOWにしてPCA9685を有効化します。今回はサーボを一度動かして終わりなのでloop()は何もしません。PCA9685データシートのP10~P17を参照してください。I2C通信はWire.beginTransmission(0x40)~Wire.endTransmission()で一区切りです。0x40はこのデバイス(PCA9685)のアドレスです。Wire.write(6)でP10の表の6行目を指定します。forループの中のWire.write()はP10の表の6-9行目に対して実行され、サーボ#0(LED0と表記)のパルス立ち上がりタイミングと立ち下がりタイミングを指定します。auto-incrementを指定していることによります。サーボに与えるパルスは通常15-20msec周期でONパルス幅1.5msecで90°(センター位置)となります。init_pca9685()の中でPWM周波数を60Hzに設定していて1周期のカウント数が4095となっていますので、パルス幅が1.5msecとなるカウント数は369となります。プログラム中のangle=の式は、サーボの可動範囲のセンターが90°となるように実測して決めた変換式です。forループでこれを12回まわしてすべてのサーボの角度を設定していきます。loop()の中身を書き換えてサーボを往復運動させてみます。#0サーボ(P2コネクタ)のみ動かします。プログラムの意味はわかると思います。#0サーボがセンター位置を中心に往復運動することを確認してください。そのままでは見えませんのでサーボホーンを取り付けてください。サーボとは直接関係ありませんが、プログラムが思い通りに動かない時などに原因を調べるひとつの方法です。上のプログラムに少し追加します。IDEの「ツール」->「シリアルモニタ」からシリアルモニタを起動します。Serial.print()に書いた内容が1000msごとに出てくると思います。(出てこない場合は画面下で9600bpsを選択)このように実行中のプログラムの状況を調べることができます。*/)して12個すべてのサーボを90°の位置に戻してサーボホーンを外しておいてください。今回の成果は”mm03′′などとして保存しておきましょう。パーツボードからメイン基板(部品が載ったパーツ)を切り離します。USBケーブルでメイン基板とパソコンを接続すると、USBシリアル変換ICのドライバがインストールされてプログラム開発ができるようになります。最初に二つの設定をします。Module”を、シリアルポートでメイン基板がつながっているポートを選択します。シリアルポートは、デスクトップ左下のWindowsアイコンを右クリックしてデバイスマネージャーを起動し、「ポート(COMとLPT)」から知ることができます。用意されているスケッチ例を使ってみます。LEDを点滅させるプログラムです。Blinkを選択します。プログラムの中で”LED_BUILTIN”となっているところ(3か所)を”14′′に変更します。上の左側のチェックマークボタンをクリックして「コンパイルが完了しました。」と出たら、隣りの矢印ボタンをクリックしてプログラムをメイン基板のマイコンに書き込みます。「ボードへの書き込みが完了しました。」と出たのち、LED(D1)が1秒ごとに点滅すれば成功です。setup(){...}はマイコンのピンの割り当てなどを書くところで最初に1回だけ実行されます。loop(){...}はマイコンにやらせる仕事を書くところで、上から順番に実行され下まで行ったらまた上に戻り、を永久に(電源が切られるまで)繰り返します。行は、Arduinoの14番ピンを出力(基本的にHigh(5V)とLow(0V)の電圧を出す)に設定しています。14という名称になっています。LEDについている抵抗R6はLEDに流れる電流を制限するためのものです。抵抗値が小さければ大きな電流が流れ(オームの法則)LEDが明るく光り、逆ならば暗くなります。14番ピンにHigh(5V)を出力しLEDを光らせます。何もしないで1000msec(1秒)待ちます。14番ピンにLow(0V)を出力しLEDを消します。何もしないで1000msec(1秒)待ちます。HIGH);に戻ります。LEDを0.5秒ごとに点滅させてください。*新しいプログラムは現在のプログラムの実行中でも矢印ボタンで上書きできます。ここでいったんプログラムを保存しておきます。「ファイル」->「名前を付けて保存」で”mm01′′などとして保存します。technology.。