この記事はmicrobit Advent Calendar 2017の18日目の投稿です。
MicroPythonとmicrobit、どちらのAdvent Calendarに投稿しようか迷いましたが、こちらに投稿しておきます。
10月に購入済だった Kitronik :MOVE mini を MicroPython で制御してみました。
Kitronik :MOVE mini は、スイッチサイエンスからmicro:bit用 :MOVEミニバギーキットとして購入できます。
中身はサーボモータドライバボードのmicro:bit用 Servo:Lite、サーボモータ2個、単4電池4本(要るのは3本なのに4本つけてくるところが大雑把でいいですね)、シャーシー用パーツ、組み立て用ネジ・ナット類、説明書(英語)です。
組み立てるとこんなのになります。モータドライバボードとmicro:bit がくっつきすぎで、差し込めるUSBケーブルを探すのが大変です。一日目の投稿にあるように、間にワッシャー入れるのはよいアイデアですね。リセットボタンも普通には押せなくなってしまいますが、爪楊枝なんかをつかうとなんとか押せます。
説明書は Microsoft PTX でのプログラミング方法を説明しています。また、説明書にも記述は無いのですが、今ではもっと便利なカスタムブロックも提供されています。
私としてはMicroPythonで動かしたいので、試してみました。
ボードについている5つのLEDはNeoPixel互換です。MicroPythonにもNeoPixelのAPIはあるので簡単だと思ったのですが、説明書の2章STEP15で使っている rotateブロックに相当するAPI が無かったので自作するしかありません。
そんなこんだで作ってみた LEDローテーションのプログラムが以下です(説明書の2章STEP15のものに相当します)。
from microbit import pin0, button_a, button_b, sleep
from neopixel import NeoPixel
COLOR_RED = (0xff, 0, 0)
COLOR_YELLOW = (0xff, 0xff, 0)
COLOR_GREEN = (0, 0xff, 0)
COLOR_BLUE = (0, 0, 0xff)
COLOR_VIOLET = (0x8a, 0xa2, 0xe2)
COLOR_PURPLE = (0xff, 0, 0xff)
np = NeoPixel(pin0, 5)
def light_up_pixels():
np[0] = COLOR_RED
np[1] = COLOR_YELLOW
np[2] = COLOR_GREEN
np[3] = COLOR_BLUE
np[4] = COLOR_PURPLE
def turn_off_pixels():
np.clear()
def rotate_pixels():
save_pixel = np[0]
for i in range(len(np)-1):
np[i] = np[i+1]
np[-1] = save_pixel
while True:
if button_a.is_pressed():
light_up_pixels()
if button_b.is_pressed():
turn_off_pixels()
rotate_pixels()
np.show()
sleep(200)
これを転送して、ボタンAを押すと以下のようにLEDがローテーションし、ボタンBを押すとLEDが消えます。
さて、サーボモータのほうです。普通のサーボモータは180度くらいの可変範囲を持っていて、PWMで回転角度を制御するのですが、:MOVE mini で使われているのは、連続回転サーボ(Continuous Rotation Servo)というもので、連続して回転することができ、PWMも角度ではなく回転スピードを制御することになります。
MicroPythonでもPWM制御はできるのですが、PTXのようにサーボモータに最適化したAPIは持っていません。ちょと探してみたらこちらにサーボ用のクラスが公開されていたので、少しいじって利用させていただきました。結果として、説明書の8章STEP5に相当するものを実装してみたのが以下です。
from microbit import pin0, pin1, pin2, button_a, button_b, sleep
from neopixel import NeoPixel
class Servo:
def __init__(self, pin, freq=50, min_us=600, max_us=2400, angle=180):
self.min_us = min_us
self.max_us = max_us
self.us = 0
self.freq = freq
self.angle = angle
self.analog_period = 0
self.pin = pin
analog_period = round((1/self.freq) * 1000) # hertz to miliseconds
self.pin.set_analog_period(analog_period)
def write_us(self, us):
us = min(self.max_us, max(self.min_us, us))
duty = round(us * 1024 * self.freq // 1000000)
self.pin.write_analog(duty)
def write_angle(self, degrees=None):
degrees = degrees % 360
total_range = self.max_us - self.min_us
us = self.min_us + total_range * degrees // self.angle
self.write_us(us)
COLOR_RED = (0xff, 0, 0)
COLOR_WHITE = (0xff, 0xff, 0xff)
COLOR_OFF = (0, 0, 0)
np = NeoPixel(pin0, 5)
right = Servo(pin1)
left = Servo(pin2)
def forward():
np[0] = COLOR_WHITE
np[4] = COLOR_WHITE
right.write_angle(0)
left.write_angle(180)
def backward():
np[0] = COLOR_RED
np[4] = COLOR_RED
right.write_angle(180)
left.write_angle(0)
def stop():
np[0] = COLOR_OFF
np[4] = COLOR_OFF
right.write_angle(90)
left.write_angle(90)
while True:
if button_a.is_pressed() and button_b.is_pressed():
stop()
elif button_a.is_pressed():
forward()
elif button_b.is_pressed():
backward()
np.show()
sleep(200)
これを転送して、ボタンAを押すと白いヘッドライトを点けて前進、Bボタンを押すと赤いヘッドライトを点けて後進、A+Bボタン同時押しで停止します。
以下はこのプログラムを少し変えて遊んでみた例です。
そのうち、radioも使ってリモコン化しようと思います。