みんなラジコン好きでしょ?
前回までの話
ATOM Motion Kitを購入したので、久々にセットアップをしました。
今回は、セットアップしたATOM Motion Kitにモーターをつなぎつつ、それをM5Stack BasicからEspNowでリモートコントロールすることでラジコンっぽくしていきます。
今回使うもの
使うものやら環境はこんな感じ。
- コーディング : UIFlow ver. 1.9.1 (on MacBook Air)
- ATOM Motion Kit (M5Stack ATOM Lite) : ver. 1.9.1
- M5Stack Basic : ver. 1.9.1
- タミヤ 楽しい工作シリーズ ダブルギヤボックス等
ダブルギヤボックスの2つのモーターをMotion KitのM1/M2端子に直接接続させています。本来はコンデンサを挟むとか挟まないとかあると思いますが、今回は省略しています。
コーディングはUIFlowで
今回もコーディングにはUIFlowを使っていきます。ATOM LiteとM5Stack Basicとの通信にはEspNowという通信を使っていきます。
(個人的解釈なので間違ってたらごめんなさいですが)BLEではなくWi-Fi上で通信をする独自通信らしく、UDP的にBroadcastしたりそれをReceiveしたりするのが簡単にできるメソッドです。ホントかよ。
一応、UIFlowの「Advanced」の中にEspNowというカテゴリがあり、ここから利用可能になっています。ありがたいことによく使う「初期化」や「送信」「受信」のテンプレートも用意されているので、それを自分のフローに持ってくるだけでM5Stack間通信が簡単にできてしまいます。
コントローラー側を実装
UIFlowはこんな感じです。
Setupで初期化のテンプレートをそのまま持ってきて、ボタンのPressedとReleasedでそれぞれ別のデータをBroadcastするようにしています。アドレスやチャネルを詳細に設定することも可能そうですが、今回はデフォルトのままで使っています。
ラジコン側を実装
ラジコン側も同じように初期化。データを受信した際に、データの中身によってモータを回転させたり止めたりしています。
今回は、
- Aボタンが押されたら、左のキャタピラが正回転し、離すと停止
- Bボタンが押されたら、左右のキャタピラが逆回転し、離すと停止
- Cボタンが押されたら、右のキャタピラが正回転し、離すと停止
こんな感じでブロックを組んでみました。(Bは別のボタンと同時に押さないこととしますw)
動いている様子がこちら
普通に動きますねー。タイムラグもなく、PressedとReleasedのタイミングで動作が切り替わっていることがわかります。パケットの取りこぼしもなさそうなので、キャタピラが動きっぱなしになる、みたいなこともなさそうです。
本当はBLEで通信させたかったんですが、持っているM5Stackの種類や、UIFlowでできることが限られているため、今回はEspNowを利用しましたが、こちらも簡単にM5stack間連携をさせることができたので、かなりおすすめです。
コードはこちら。
コントローラー側
from m5stack import * from m5ui import * from uiflow import * import espnow import wifiCfg setScreenColor(0x222222) wifiCfg.wlan_ap.active(True) wifiCfg.wlan_sta.active(True) espnow.init() def buttonA_wasPressed(): # global params espnow.send(id=1, data=str('A1')) pass btnA.wasPressed(buttonA_wasPressed) def buttonA_wasReleased(): # global params espnow.send(id=1, data=str('A2')) pass btnA.wasReleased(buttonA_wasReleased) def buttonC_wasPressed(): # global params espnow.send(id=1, data=str('C1')) pass btnC.wasPressed(buttonC_wasPressed) def buttonC_wasReleased(): # global params espnow.send(id=1, data=str('C2')) pass btnC.wasReleased(buttonC_wasReleased) def buttonB_wasPressed(): # global params espnow.send(id=1, data=str('B1')) pass btnB.wasPressed(buttonB_wasPressed) def buttonB_wasReleased(): # global params espnow.send(id=1, data=str('B2')) pass btnB.wasReleased(buttonB_wasReleased) print(espnow.get_mac_addr()) espnow.add_peer('ff:ff:ff:ff:ff:ff', id=1, channel=0, ifidx=0, encrypt=False)
ラジコン側
from m5stack import * from m5ui import * from uiflow import * import espnow import wifiCfg from base.Motion import Motion addr = None data = None speed = None wifiCfg.wlan_ap.active(True) wifiCfg.wlan_sta.active(True) espnow.init() def recv_cb(_): global addr,data,speed addr, _, data = espnow.recv_data(encoder='str') if data == 'A1': motion.set_motor_speed(1, speed) if data == 'A2': motion.set_motor_speed(1, 0) if data == 'B1': motion.set_motor_speed(1, speed * -1) motion.set_motor_speed(2, speed * -1) if data == 'B2': motion.set_motor_speed(1, 0) motion.set_motor_speed(2, 0) if data == 'C1': motion.set_motor_speed(2, speed) if data == 'C2': motion.set_motor_speed(2, 0) pass espnow.recv_cb(recv_cb) motion = Motion() speed = 96 espnow.add_peer('ff:ff:ff:ff:ff:ff', id=1, channel=0, ifidx=0, encrypt=False)