きっかけ
micro:bit 用のモータドライバーの一種である SparkFun moto:bitを入手しました。
Kitronik のモータドライバボードより安くてよいのですが、基盤をながめてみると GPIO からモータドライバICに直接つながっているわけではなさそう。回路図を参照してみると、I2C で送った命令を PSoC で処理してモータドライバを制御するというインテリジェントなものになっている模様。
おかげで他の入出力ピンを消費することなく、モータ制御ができるのはよいのですが、利用インタフェースは Microsoft の Makecode にしか提供されていません。MicroPython から使いたかったので、Makecode 用のブロックの実装を調べて I2C でどう制御しているのか調べてみました。
moto:bit ブロックのソース(TypeScript)は以下で参照できます。
調査結果
I2C アドレス
I2C アドレスは 0x59 (89)
I2C で送る制御情報のフォーマット
すべて2バイト構成です。1バイト目は制御の種類、2バイト目はパラメータになっています。
スピードの指定
左モータ(LEFT MOTOR)のスピード指定を行う場合は1バイト目が 0x21、右モータ(RIGHT MOTOR)のスピード指定を行う場合は1バイト目が 0x20 になります。
2バイト目には進行方向とスピードを指定します。
-
最上位ビットが 1 であれば 前進(Forward)
- 下位7ビット 0-127 でスピードを指定(大きくなるほど速い)
-
最上位ビットが 0: 後進(Reverse)
- 下位7ビット 0-127 でスピードを指定(大きくなるほど遅い)
※ Makecode のブロックで指定するスピードは 0-100 (大きいほど速い)で、これを 0-127 にマッピングして制御しています
進行方向の反転指定
モータの前進/後進を反転する指定があります。
左モータ(LEFT MOTOR)の反転指定を行う場合は1バイト目が 0x13、右モータ(RIGHT MOTOR)の反転指定を行う場合は1バイト目が 0x12 になります。
2バイト目は反転する/しないの指定で、1であれば反転、0であれば反転を止める(元に戻す)指定となります。
モータの有効化/無効化の指定
モータの有効化/無効化の指定を行う場合は1バイト目は 0x70 になります。
2バイト目は有効化/無効化の指定で、1であれば有効化、0であれば無効化となります。
そんなわけで作成してみた MycroPython ライブラリ
ソースコード
from microbit import i2c
class Motor:
# command
LEFT = 0x21
RIGHT = 0x20
LEFT_INVERT = 0x13
RIGHT_INVERT = 0x12
ENABLE = 0x70
# direction
FORWARD = 0x80
REVERSE = 0x00
# i2c
_I2C_ADDR = 0x59
_I2C_SDA = 20
_I2C_SCL = 19
_i2c_buf = bytearray(2)
def __init__(self, motor):
self.motor = motor
def set_speed(self, speed, direction=FORWARD):
pwr = speed * 127 // 100
Motor._i2c_buf[0] = self.motor
Motor._i2c_buf[1] = (pwr | direction) \
if direction == Motor.FORWARD else (127 - pwr)
i2c.write(Motor._I2C_ADDR, Motor._i2c_buf)
def invert(self, invert):
if self.motor == Motor.LEFT:
Motor._i2c_buf[0] = Motor.LEFT_INVERT
else:
Motor._i2c_buf[0] = Motor.RIGHT_INVERT
Motor._i2c_buf[1] = int(invert)
i2c.write(Motor._I2C_ADDR, Motor._i2c_buf)
@staticmethod
def enable():
Motor._i2c_buf[0] = Motor.ENABLE
Motor._i2c_buf[1] = 1
i2c.write(Motor._I2C_ADDR, Motor._i2c_buf)
@staticmethod
def disable():
Motor._i2c_buf[0] = Motor.ENABLE
Motor._i2c_buf[1] = 0
i2c.write(Motor._I2C_ADDR, Motor._i2c_buf)
motor_left = Motor(Motor.LEFT)
motor_right = Motor(Motor.RIGHT)
ソースは github にも置いておきました。
使い方
モジュールのインポート
import motobit
API
motobit.Motor.enable()
モータを有効化します。
motobit.motor_left.set_speed(speed, direction=Motor.FORWARD)
左モータのスピードを設定します。
speed: モータのスピードを 0-100 で指定。100 が最大スピード
direction: Motor.FORWARD で前進、Motor.REVERSE で後進
motobit.motor_right.set_speed(speed, direction=Motor.FORWARD)
右モータのスピードを設定します。
speed: モータのスピードを 0-100 で指定。100 が最大スピード
direction: Motor.FORWARD で前進、Motor.REVERSE で後進
motobit.motor_left.invert(invert)
左モータの進行方向を反転するかを設定します。
invert: True - 反転する、False - 反転しない
motobit.motor_left.invert(invert)
右モータの進行方向を反転するかを設定します。
invert: True - 反転に設定する、False - 反転に設定しない
motobit.Motor.disable()
モータを無効化します。
サンプルコード
SparkFun サイトの "Experiment 1: Driving and Turning"と同じことを MicroPython で書いてみたコードです。
from microbit import button_a, sleep
from microbot import *
while True:
if button_a.is_pressed():
Motor.enable()
motor_left.set_speed(100)
motor_right.set_speed(100)
sleep(1000)
motor_left.set_speed(100, Motor.REVERSE)
motor_right.set_speed(100)
sleep(200)
motor_left.set_speed(100)
motor_right.set_speed(100)
sleep(1000)
Motor.disable()
余談
この moto:bit を使った micro:bot kit は、Forward で BACK 方向に進み、Reverse で FRONT 方向に進んでしまいます。最初、組み立てを間違ったのかと思ったのですが、SparkFun の 実験動画でもそのように動いています。ひどいですねぇ。
MicroPython ドキュメントにある microbit.i2c.init() を使ったところ、定義されていないとエラーになりました。まあ、デフォルト(freq=100000, sda=pin20, scl=pin19)で問題ないのですが。github の履歴によれば 2016/11/11 に追加されているのですが、開発環境にはまだ反映されていないようです。
micro:bit の公式サイトで MicroPython ドキュメントが日本語にならなかったの残念です。勝手訳を公開しています。日本語ドキュメントは公式からリンクしてもらいました