micropython
ESP32
M5stack

M5stackにWiiヌンチャクを繋いで使う。

パッと目にしたWiichuck adapterがGroveコネクタと配線の順序が一緒だった。

wiiヌンチャク、クラシックコントローラはi2cデバイスなんですよね~。
一時期流行ったものですねー…。

と、ボタンが三つしかない入力が不自由なM5stackに持ってこいなのでは?
microPythonでドライブしている事例を探したものの見つからず。
んじゃ、つくりますか。

使うもの。

これと、何らかのピッチ変換・・・無理すれば普通の2.54mmピッチでも何とかなりますが…或いは、

これで配線する。こっちの方が綺麗に出来そう。

以前だとXbee breakoutという2.54mmピッチと2mmピッチを変換する小さい基板がswitch scienceさんから販売されていたんですが現在は取り扱っていないようです。 wiichuckアダプタも同様。残念。

荒っぽいことするならケーブルばらして直接GROVEプロトシールドに配線です。
中古屋さんにごろごろしてますからねwiiヌンチャクもクラシックコントローラも。

わたしは前述のXbee breakoutという基板を持っていたのでこんな感じにアダプタを作りました。
adapter.jpg
が、この写真ではGroveコネクタの表裏が間違えています。あとで気づいて直しました。

adapter2.jpg
本当はクラシックコントローラが使いたい。
しかしいくつかハマりポイントがあってとりあえずヌンチャクの方が簡単なのでは? とヌンチャクをひとまずやってみることにしました。

ハマりどころ。

アダプタが出来て、すぐ接続までは確認できたのでいろいろと弄っていたのですけれどもどうもおかしい、デバイスが出てきたり消えたり。
何が原因なのか解らずしばらくいろいろ試したりしていました、その結果わかったのが。
I2Cのクロックスピードを落とさないとWiiヌンチャクが通信に失敗することが解りました。
クロックスピード40kくらい。ですか。それ以上にすると安定しないみたい。

WiiヌンチャクもクラシックコントローラもI2Cデバイスですけども繋いだら即読めるようなもんではなかったです。単に初期化のために1byte送るだけなんですが見落としていてこれもハマりました。

タイミング問題もありました。microPythonはI2Cのリクエストとリードを同時に行います。のでデータがなくても読みにいってエラーになっちゃいます。
割り込みを使ってデータリクエストしていたのだけどどうもよろしくなかったので泥臭く書き直したら安定したので泥臭いコードになっています。

ではコード。

nunchuck.py
from machine import I2C,Pin
import time

def init():
    global i2c,tm,count
    i2c = I2C(sda=Pin(21),scl=Pin(22),freq=40000)
    i2c.writeto_mem(82,0x40,b'\0',addrsize=8)
    time.sleep_ms(200)
    i2c.writeto_mem(82,0,b'\0',addrsize=8)
    count = time.ticks_ms()

def readNunchuck():
    global count
    if( time.ticks_ms() - count > 50 ):
        try:
            data = i2c.readfrom(82,6)
        except:
            data = bytes([0]*6)

        buf = {}
        buf["joyX"]   = data[0]
        buf["joyY"]   = data[1]
        buf["accelX"] = data[2]
        buf["accelY"] = data[3]
        buf["accelZ"] = data[4]
        buf["btnZ"]   = 0
        buf["btnC"]   = 0

        btnBit = data[5] & 0x3
        if( (btnBit == 2) | (btnBit == 0) ):
            buf["btnZ"] = 1
        if( (btnBit == 2) | (btnBit == 1) ):
            buf["btnC"] = 1

        if( (data[5]>>2) & 1 ):
            buf["accelX"] += 2
        if( (data[5]>>3) & 1 ):
            buf["accelX"] += 1
        if( (data[5]>>4) & 1 ):
            buf["accelY"] += 2
        if( (data[5]>>5) & 1 ):
            buf["accelY"] += 1
        if( (data[5]>>6) & 1 ):
            buf["accelZ"] += 2
        if( (data[5]>>7) & 1 ):
            buf["accelZ"] += 1

        i2c.writeto_mem(82,0,b'\0',addrsize=8)
        count = time.ticks_ms()
        return buf

本当はclassにしたほうがいいんだろうなーと思いつつ。
ひとまず動作確認ってことでね。

つかいかたは

import nunchuck
nunchuck.init()
while True:
    print(nunchuck.readNunchuck())
    nunchuck.time.sleep_ms(200)

みたいな感じ。

  • joyX
  • joyY
  • accelX
  • accelY
  • accelZ
  • btnC
  • btnZ

それぞれの値が返ってきます。

nd = nunchuck.readNunchuck()
nd["joyX"]

みたいにして値をとりだします。

これはmicroPython関係無いんですがアナログスティックも加速度センサも値がフラフラしているので移動平均取るなりして安定させないと実用厳しげ。
そこまでコードに含むか悩んだんですがとりあえず入れるのはやめました。
こういう補助的なコードは最小サイズで最小の機能を持つのがかっこいいなと思いますので。

さて、次は本題のクラシックコントローラですね。