Androidのメモとか

ポキオの日記です。今日も遅延してない。

M5Stack ATOM Motion KitをEspNowでラジコンにしてみた

みんなラジコン好きでしょ?

ポキオ M5Stack Atom Motion Kit EspNow

前回までの話

ATOM Motion Kitを購入したので、久々にセットアップをしました。

relativelayout.hatenablog.com

今回は、セットアップしたATOM Motion Kitにモーターをつなぎつつ、それをM5Stack BasicからEspNowでリモートコントロールすることでラジコンっぽくしていきます。

今回使うもの

使うものやら環境はこんな感じ。

ポキオ M5Stack Atom Motion Kit 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したりするのが簡単にできるメソッドです。ホントかよ。

ポキオ M5Stack Atom Motion Kit EspNow

一応、UIFlowの「Advanced」の中にEspNowというカテゴリがあり、ここから利用可能になっています。ありがたいことによく使う「初期化」や「送信」「受信」のテンプレートも用意されているので、それを自分のフローに持ってくるだけでM5Stack間通信が簡単にできてしまいます。

コントローラー側を実装

UIFlowはこんな感じです。

ポキオ M5Stack Atom Motion Kit EspNow

Setupで初期化のテンプレートをそのまま持ってきて、ボタンのPressedとReleasedでそれぞれ別のデータをBroadcastするようにしています。アドレスやチャネルを詳細に設定することも可能そうですが、今回はデフォルトのままで使っています。

ラジコン側を実装

ラジコン側も同じように初期化。データを受信した際に、データの中身によってモータを回転させたり止めたりしています。

ポキオ M5Stack Atom Motion Kit EspNow

今回は、

  • 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)

ATOM Lite

ATOM Lite

  • スイッチサイエンス
Amazon

「Androidのメモとか」は、Amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、Amazonアソシエイト・プログラムの参加者です。

このブログは個人的なメモ書きであったり、考えを書く場所であります。執筆者の所属する団体や企業のコメントや意向とは無関係であります。また、このブログは必ずしも正しいことが書かれているとは限らず、誤字脱字や意図せず誤った情報を載せる場合がありえます。それが原因で読者が不利益を被ったとしても、執筆者はいかなる責任も負いません。ありがとうございます。