Kitronik :MOVE mini を MicroPython で制御する

この記事は[microbit Advent Calendar 2017](https://qiita.com/advent-calendar/2017/microbit)の18日目の投稿です。

MicroPythonとmicrobit、どちらのAdvent Calendarに投稿しようか迷いましたが、こちらに投稿しておきます。

10月に購入済だった Kitronik :MOVE mini を MicroPython で制御してみました。

Kitronik :MOVE mini は、スイッチサイエンスから[micro:bit用 :MOVEミニバギーキット](https://www.switch-science.com/catalog/3415/)として購入できます。

![IMG_4186.JPG](https://qiita-image-store.s3.amazonaws.com/0/106817/e6294641-9880-9132-fdef-0f942c2c6a7f.jpeg)

中身はサーボモータドライバボードの[micro:bit用 Servo:Lite](https://www.switch-science.com/catalog/3414/)、サーボモータ２個、単４電池４本(要るのは３本なのに４本つけてくるところが大雑把でいいですね)、シャーシー用パーツ、組み立て用ネジ・ナット類、説明書（英語）です。

![IMG_4187.JPG](https://qiita-image-store.s3.amazonaws.com/0/106817/9d398c35-3250-215d-3dbf-71eb0cdf73f9.jpeg)

組み立てるとこんなのになります。モータドライバボードとmicro:bit がくっつきすぎで、差し込めるUSBケーブルを探すのが大変です。[一日目の投稿](http://pneskin2.nekoget.com/press/?p=1728)にあるように、間にワッシャー入れるのはよいアイデアですね。リセットボタンも普通には押せなくなってしまいますが、爪楊枝なんかをつかうとなんとか押せます。

![IMG_4197.JPG](https://qiita-image-store.s3.amazonaws.com/0/106817/457cc0a5-baaf-9d28-dd75-2abd1034d3ec.jpeg)

説明書は Microsoft PTX でのプログラミング方法を説明しています。また、説明書にも記述は無いのですが、今では[もっと便利なカスタムブロック](https://www.kitronik.co.uk/blog/kitronik-custom-makecode-editor-servo-blocks/)も提供されています。

私としてはMicroPythonで動かしたいので、試してみました。

ボードについている５つのLEDはNeoPixel互換です。MicroPythonにもNeoPixelのAPIはあるので簡単だと思ったのですが、説明書の２章STEP15で使っている rotateブロックに相当するAPI が無かったので自作するしかありません。

そんなこんだで作ってみた LEDローテーションのプログラムが以下です(説明書の２章STEP15のものに相当します)。

```python3
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が消えます。

![IMG_4250.MOV.gif](https://qiita-image-store.s3.amazonaws.com/0/106817/307d5029-2ba3-6f8d-e616-5fdb93faef17.gif)

さて、サーボモータのほうです。普通のサーボモータは180度くらいの可変範囲を持っていて、PWMで回転角度を制御するのですが、:MOVE mini で使われているのは、連続回転サーボ(Continuous Rotation Servo)というもので、連続して回転することができ、PWMも角度ではなく回転スピードを制御することになります。

MicroPythonでもPWM制御はできるのですが、PTXのようにサーボモータに最適化したAPIは持っていません。ちょと探してみたら[こちら](https://github.com/microbit-playground/microbit-servo-class)にサーボ用のクラスが公開されていたので、少しいじって利用させていただきました。結果として、説明書の８章STEP5に相当するものを実装してみたのが以下です。

```python3
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ボタン同時押しで停止します。

以下はこのプログラムを少し変えて遊んでみた例です。

![IMG_4253.mov.gif](https://qiita-image-store.s3.amazonaws.com/0/106817/8bc46150-8bb3-d130-c181-1e92dd7c0fd2.gif)

そのうち、radioも使ってリモコン化しようと思います。