みんなラジコン好きでしょ?
前回までの話
ATOM Motion Kitを購入したので、久々にセットアップをしました。
relativelayout.hatenablog.com
今回は、セットアップした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という通信を使っていきます。
www.espressif.com
(個人的解釈なので間違ってたらごめんなさいですが)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():
espnow.send(id=1, data=str('A1'))
pass
btnA.wasPressed(buttonA_wasPressed)
def buttonA_wasReleased():
espnow.send(id=1, data=str('A2'))
pass
btnA.wasReleased(buttonA_wasReleased)
def buttonC_wasPressed():
espnow.send(id=1, data=str('C1'))
pass
btnC.wasPressed(buttonC_wasPressed)
def buttonC_wasReleased():
espnow.send(id=1, data=str('C2'))
pass
btnC.wasReleased(buttonC_wasReleased)
def buttonB_wasPressed():
espnow.send(id=1, data=str('B1'))
pass
btnB.wasPressed(buttonB_wasPressed)
def buttonB_wasReleased():
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)