M5StickV:GPIOによるPLEN:bitとの連携

M5Stackから、新製品の M5StickV が発売されました。今回はESP32ではなくて、K210 RISC-V搭載です。顔検出などが高速にできるプロセッサとのこと。無線機能はついてないので、技適は必要なし!よって日本でも即発売となりました!

発売当初品薄だったのですが、運よく初回販売で入手することができました。

初期プログラムとして、顔認識するプログラムが入っています。起動すると即、顔認識起動!すごい!

M5StickVについては、さいとてつやさんのサイトが情報満載です。

さいとてつやさんのM5StickVまとめ
https://scrapbox.io/saitotetsuya/M5StickV

起動時には、本体内フラッシュの boot.py が自動的に起動されます。boot.py消すと、標準のboot.py が自動的に作成されてそれが起動されるみたいです。microSDカードのルートにboot.pyというファイル名で入れておけば、そちらを起動してくれます。これは便利!とはいえ、自動的に起動されてしまうので、止めたい時のために「起動時にボタンを押すとプログラム終了する」ような仕掛けを入れておく必要がありますね。


プログラムは、基本的には MaixPy (MicroPython) で行います。

こちらで紹介されている、20種類の物を認識するプログラムを動かしてみました。

MaixPy Run 20-classes object detection based on tiny-yolov2 in 30 lines~
https://bbs.sipeed.com/t/topic/683

データの読み元を、フラッシュメモリアドレス指定からSDカード指定に変更して動作しました。


せっかくなので、判別した結果によって、何かアクションを起こしてみたいですね。
ということで、PLEN:bit と通信して動かしてみました。

通信はGPIO。コード2本を使って2種類の信号を通信してみます。シンプルで、贅沢な方法ですw

M5StickV のピン番号は、ケースのフタ裏側に書いてあります。

これによれば、GROVE端子には、GND / Vout / G35 / G34 が接続されてます。

MaixPyでのG34へのGPIO送信はこれでできました。

from Maix import GPIO
from fpioa_manager import fm
fm.register(34, fm.fpioa.GPIOHS0, force=True)
pinout34 = GPIO(GPIO.GPIOHS0, GPIO.OUT)
pinout34.value(0)

あまり資料見つけられなくて、いろいろ試行錯誤しました。(ファームウェアバージョンによっては force=True つけなくていいかも?)

任意のピンに任意の機能を割り振る、FPIOA という仕掛けがあるみたいです。

FPIOA (Field Programmable Input and Output Array)
https://maixpy.sipeed.com/en/libs/Maix/fpioa.html

こちらの公式のサンプルが参考になりました。

MaixPy_scripts/hardware/demo_gpio.py
https://github.com/sipeed/MaixPy_scripts/blob/master/hardware/demo_gpio.py

ソース全体はこういう感じ。

import sensor,image,lcd,time
import KPU as kpu
from Maix import GPIO
from fpioa_manager import fm

lcd.init(freq=15000000)
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_vflip(1)
sensor.run(1)

fm.register(34, fm.fpioa.GPIOHS0, force=True)
fm.register(35, fm.fpioa.GPIOHS1, force=True)

pinout34 = GPIO(GPIO.GPIOHS0, GPIO.OUT)
pinout34.value(0)
pinout35 = GPIO(GPIO.GPIOHS1, GPIO.OUT)
pinout35.value(0)

clock = time.clock()
classes = ['aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor']
# task = kpu.load(0x500000) 
task = kpu.load("/sd/20class.kmodel")
anchor = (1.889, 2.5245, 2.9465, 3.94056, 3.99987, 5.3658, 5.155437, 6.92275, 6.718375, 9.01025)
a = kpu.init_yolo2(task, 0.5, 0.3, 5, anchor)
while(True):
    clock.tick()
    img = sensor.snapshot()
    code = kpu.run_yolo2(task, img)
    print(clock.fps())
    if code:
        for i in code:
            a=img.draw_rectangle(i.rect())
            a = lcd.display(img)
            for i in code:
                #lcd.draw_string(i.x(), i.y(), classes[i.classid()], lcd.RED, lcd.WHITE)
                #lcd.draw_string(i.x(), i.y()+12, '%f1.3'%i.value(), lcd.RED, lcd.WHITE)
                if classes[i.classid()] == 'cat':
                    lcd.draw_string(0,0,'neko!',lcd.RED, lcd.WHITE)
                    print('cat')
                    pinout34.value(1)
                    time.sleep_ms(1000)
                    pinout34.value(0)
                if classes[i.classid()] == 'car':
                    lcd.draw_string(0,0,'kuruma!',lcd.RED, lcd.WHITE)
                    print('car')
                    pinout35.value(1)
                    time.sleep_ms(1000)
                    pinout35.value(0)
    else:
        a = lcd.display(img)
a = kpu.deinit(task)

20-classes object detectionに処理追加して、”cat” の場合、34ピンに 1 を出力、”car”の場合、35ピンに 1 を出力するようにしました。


PLEN:bit 側は MicrosoftCode でプログラム作成。

plenbit.servoInitialSet()
basic.forever(function () {
    basic.showLeds(`
        # # # # #
        # . . . #
        . . # # #
        . . . . .
        . . # . .
        `)
    if (pins.digitalReadPin(DigitalPin.P0) == 1) {
        basic.showIcon(IconNames.Happy)
        plenbit.eyeLed(plenbit.LedOnOff.On)
        plenbit.servoWrite(4, 120)
        basic.pause(2000)
        plenbit.servoWrite(4, 20)
        plenbit.eyeLed(plenbit.LedOnOff.Off)
        basic.pause(1000)
    } else {
        if (pins.digitalReadPin(DigitalPin.P2) == 1) {
            basic.showIcon(IconNames.Happy)
            plenbit.eyeLed(plenbit.LedOnOff.On)
            plenbit.servoWrite(0, 20)
            basic.pause(2000)
            plenbit.servoWrite(0, 100)
            plenbit.eyeLed(plenbit.LedOnOff.Off)
            basic.pause(1000)
        }
    }
})

センサー0 が ON になれば右手を挙げて、センサー2 が ON になれば左手を挙げるだけです。
せっかくなので認識に合わせて胸のLED表示も変更。

PLEN:bitに、ネコと車の旗を持ってもらいました。G34 / G35 と GND を PLEN:bit の端子に接続しています。


シリアルでつないで、文字列をそのまま送信するのもやってみました。

UARTでの送信こういう感じ。

from machine import UART
from fpioa_manager import fm
fm.register(34, fm.fpioa.UART1_TX, force=True)
fm.register(35, fm.fpioa.UART1_RX, force=True)
uart_A = UART(UART.UART1, 115200,8,0,0, timeout=1000, read_buf_len=4096)
uart_A.write("string"+"\r\n")

改行文字は受け手に合わせて。