Attic or Garret

M5Stackを使ってみる (MicroPython編)

Introduction

M5Stack(M5GO)/M5Camera/M5Stick/M5Atom
それぞれコンパクトなケースに沢山の機能が詰まった魅力的なIoTデバイス
マイコン世代のひとはM5と聴くとSORD m5を思い出すはずw
解析と動くロボットの製作などしています。
内容はIndexにまとめています。お好みの内容をまったりとご覧下さい。(^_^)
備忘録も兼ねています。バージョンなどの違いによって使い方が変わっている可能性もあるのでご注意ください。

公開している内容を著者に無断で転載あるいは商業目的で利用することを禁止します。 改造等は個人の責任のもとに行って下さい。発生した事故、故障などに対して一切の責任を持ちません。 公開している内容のアイデアを流用した作品、および公開している独自の解析結果を含む情報を、SNS、ブログ、書籍などで発表する際はご一報ください。こちらのサイトへのリファレンス(リンク)を加えてください。

Index

MicroPython編

ハード編

M5Stack / M5Stick / M5Camera / M5AtomをMicroPythonで使ってみる。

M5Stack / M5Stick / M5Camera / M5Atomで使えるMicroPythonファームウェアには複数の種類があります。さらにビルドに使ったESP-IDF(CPU開発元の公式SDK)のバージョンによっても機能が異なってきます。
micropython.org公式版(新v1.1x)ソースコードM5ハード対応[低]
loboris公式版(旧v1.9.4) カスタムソースコード開発休止??M5ハード対応[中]
M5cloudloboris カスタム開発休止M5ハード対応[中]
M5Stack公式loboris カスタムソースコード開発休止M5ハード対応[中]
flow.m5stack.com公式版(新v1.1x) カスタム?M5ハード対応[高]
それぞれ互換性がないため相互に使うにはプログラムの手直しが必要です。
M5Stack公式ではUIFlowを推奨しています。
UIFlowをオフラインで使う場合はUIFlow-Desktop-IDEを使います。ダウンロード

現状ではどのMicroPythonでも使えない機能があるため、今のところ機能が豊富なloboris版をM5Stack(M5Sstick/M5Atom)に最適化したリビルド済みのイメージファイルを用意しました!
追加した機能

@ M5Stack / M5Stick / M5Atom のハードウェアに対応

A MP3ファイルの再生に対応

B Madgwickフィルタに対応

C Bluetoothに対応 (btstackの一部の機能)

D バッファに格納されたイメージデータの表示に対応

E readintoを使ったときにデータ格納位置をオフセット指定できる。

F WAVファイルの再生時にバッファー容量を指定できる。

MicroPython[loborisリビルド版] ダウンロード
  • ※ FLASHサイズ/SPI速度/PSRAM別になっています。
  • ※ 名前にbtstackがあるファイルはBluetoothの一部機能が使えますが実験バージョンです。btstackがメモリを占有するため利用可能メモリが減っています。(PSRAMが搭載されていないとWiFiが使えません)
  • ※ 書き込みに失敗する場合は消去(erase_flash)をしてから再度書き込みを実行してください。

リビルド版を使っているときはMicroPython[loborisリビルド版]と記載しています。
開発環境はuPyCraft V0.28を使っています。v0.30(Win)は不具合で使えませんでした。

MicroPython[loboris]をリビルドする[玄人志向]Index

詳しくはBuilding MicroPython for ESP32を参考にしてください。
きほん備忘録です。リビルドに関しては日本語での解説ページもいくつかあるので参考にしてください。
  • ※ Linuxを使う場合はsamba経由でファイルをコピーせずに直接unzipすること

@ sdkconfig の準備

ビルドにはM5Stackに対応したsdkconfigファイルが必要です。M5Stack_MicroPythonのPython_BUILD/firmware/以下にあります。必要な種類から選びます。
MicroPython_BUILD/firmware/esp32_all/sdkconfig
※ menuconfigを実行することで公式版とloboris版の記述の差異も更新されます。

A menuconfigを実行してsdkconfigをカスタマイズ

B ./BUILD.shの編集

FILE_SIZE=16 変更したFlashサイズに合わせます。

C ビルトインモジュールの追加

components/micropython/esp32/modulesにpythonで記述されたファイルを追加するとビルトインモジュールとしてインポートすることが出来ます。
サイズの大きいファイル(16KB以上)のインポートは不安定に繋がるのでこちらに格納する必要があります。

D ビルド

./BUILD.sh clean QSTRに変更がある場合は必須
./BUILD.sh -v
なぜか下記の実行も必須でした。
../Tools\esp-idf\components\partition_table\ gen_esp32part.py partitions_mpy.csv partitions_mpy.bin

E M5Stackに書き込み (ファイルは上記で作成したファイル)

Windowsを使いました。
esptool.py -p COMシリアルポート番号 -b 921600 --before default_reset --after no_reset write_flash 0x1000 bootloader.bin 0xf000 phy_init_data.bin 0x10000 MicroPython.bin 0x8000 partitions_mpy.bin

MP3ファイルを再生する (MicroPython[loborisリビルド版]) (2018/11/15)Index

MP3ファイルの再生機能を追加しました。バックグラウンドで再生を続けるので処理を継続できます。
mp3ファイルはモノラルに変換してください。

DAC

PDMを使って再生しているので音質も向上しています。
dac=machine.DAC(Pin(25))

dac.mp3play('ファイル名.mp3',freq=48000(省略可),buffsize=8(省略可),stacksize=4096(省略可))

sleep(1) 少し間隔をあける
while dac.check_play():再生確認
  sleep(0.1)
dac.stopwave()再生停止
psramがない状態でbtstack(Bluetooth)と同時に使うときはbuffsizeを4以下に設定しないとハングします。

DAC (M5Stack)

PDMを使った再生では下記の対策を行うことで音質が向上します。
音声出力(GPIO25)からGNDの間に75Ωの抵抗と1μF(もしくは2μF)のコンデンサを並列に入れる。

I2S (M5Atom Echo)

M5Atom EchoのスピーカーはESP32のビルトインDACではなくI2SオーディオIC(NS4168)に接続されています。 そのため再生方法が異なります。
from machine import I2S

spk = I2S(I2S.NUM0, bck=Pin(19), ws=Pin(33), sdout=Pin(22), mode=I2S.MASTER_TX,
    dataformat=I2S.B16, channelformat=I2S.ALL_RIGHT,
    samplerate=48000, dmacount=2, dmalen=1024)

spk.mp3play('ファイル名.mp3',(省略可),stacksize=4096(省略可))
  • ※ I2Sのチャンネルは2チャンネルありますが、現在チャンネル0しか使えません。I2Sのチャンネル0はDACクラスでも一部(write)を除き使用している為、これらの機能は今のところ同時には使えません。
  • ※ SDカードからのデータを再生する際はdmacount=2では音飛びするみたいです。

WAVファイルを再生する (MicroPython[loborisリビルド版]) (2018/11/02)Index

dac.wavplay('/sd/ファイル名.wav', サンプルレート補正, バッファ数(省略可))
WAVデータはDMAを使ってDAコンバータに転送されます。 DMAのサイズはバッファ数2(バッファ長1024Byte)と固定されているため、バッファ数を指定できるようにオプションを追加しています。 32を指定した場合、バッファサイズは32KBになります。弊害として再生開始までの時間が長くなります。

M5Stack

SDカードスロットとLCDはインターフェース(VSPI)を共有しています。 SDカードとLCDへのアクセスが集中した状態ではLCDへの表示が遅れたり再生音が途切れることがあります。バッファーを増やすことで改善されます。

グラフィック/テキストを描画する (MicroPython[loborisリビルド版])Index

初期化

イメージデータを表示する (MicroPython[loborisリビルド版]) (2019/04/09-)

tft.image(0, 0, 'イメージファイル名')
tft.image(0, 0, '', type=tft.JPG, buf=バッファ)
バッファに格納されたイメージデータを表示できるようにオプションを追加しています。
※ バッファを指定する際はtypeの指定が必須。
jpegのデーコードはESP32のマスクROMに格納されたTiny JPEG Decompressorで行います。デコードされたデータは16x16ドットのブロックごとにDMA転送されます。

描画

tft.set_bg(tft.WHITE)
tft.set_fg(tft.BLACK)
tft.clear()
tft.rect(1, 1, 158, 78) 端から端まで線を引く
tft.font(tft.FONT_Ubuntu) フォント指定
tft.text(0, 0, 'test', tft.BLACK) テキストの表示
詳しくは…
display・loboris/MicroPython_ESP32_psRAM_LoBo Wiki・GitHub

LovyanGFXでグラフィック/テキストを描画する (MicroPython[loborisリビルド版]) (2020/08/07-)Index

lovyan03さん製作の超高速グラフィックライブラリLovyanGFXをMicroPythonでも使えるようにしました。
displayモジュールと共存出来ません、別途リビルドが必要です。
MicroPython[loboris+lovyan03リビルド版] ダウンロード (実験バージョン)
  • ※ MicroPython対応にあたってlovyan03さんにご対応頂きました。この場を借りて感謝申し上げます。

初期化

import lgfx

lgfx.init()
model=lgfx.getBoard()
lgfx.setColorDepth(lgfx.COLOR_BITS24)
if model==lgfx.M5Stack:
  pass
elif model==lgfx.M5StickC:
  lgfx.setRotation(lgfx.LANDSCAPE_FLIP)
elif model==lgfx.M5StickCPlus:
  lgfx.setRotation(lgfx.LANDSCAPE_FLIP)

LovyanGFXでイメージデータを表示する (MicroPython[loborisリビルド版])

lgfx.drawJpgFile('イメージファイル名', 0, 0) x, yの位置指定が出来ます
lgfx.drawJpg(data, 0, 0) バッファに格納されたイメージデータ,x, yの位置指定が出来ます
  • ※ LovyanGFXのsprite機能にはただいま対応中
  • ※ printfは実装していません
以下loborisリビルド版限定の現象の可能性が高いです。
  • ※ PSRAMを使う場合は40MHz駆動にする必要があります。
  • ※ PSRAMとLovyanGFXとBluetooth(BTSTACK)を組み合わせて使うとIRAMが不足します。SPIのIRAM使用を停止することでビルドできます。
  • ※ LovyanGFXとBluetooth(BTSTACK)を組み合わせた状態でFTPを使うと転送速度が落ちます。

イメージデータを連続表示する (MicroPython[loborisリビルド版]) (2019/04/09-)Index

M5Stack

上の動画は内蔵フラッシュメモリーに保存した画像ファイル(jpeg)を連続表示して実現しています。 全画面(320×240)にjpeg画像1枚を表示する時間を測定すると約0.1ミリ秒でした。 全画面表示はLCD(ILI9341)のアクセススピードより、画像データ保管場所からの転送スピードに影響されます。

SDカードをマウントする (MicroPython[loboris])Index

SDカードは次の初期化を行うことでマウントできます。

M5Stack

import uos

uos.sdconfig(uos.SDMODE_SPI, clk=18, mosi=23, miso=19, cs=4, maxspeed=40)
uos.mountsd()
SDカードスロットはインターフェース(VSPI)をLCDと共有しています。
SDカードとLCDを同時にアクセスする際には注意が必要。
maxspeedはSPIインターフェースのクロック速度を指定します。(初期値40、省略できます)実際の転送速度はSDカードとの相性によるようです。

M5StickC

import uos

uos.sdconfig(uos.SDMODE_SPI, clk=0, mosi=26, miso=36, cs=2, maxspeed=14)
uos.mountsd()
GPIOマトリックスにより任意のピンを割り当てます。maxspeedは14Mでないとアクセスできませんでした。

外部SPI機器を使う際のトラブル&対策Index

ESP32モジュールにはHSPIとVSPIの2系統のSPIインターフェースが用意されています。(FlashとpsRAM用は別) またSPIはDMAも使用しておりMicroPythonでは現状下記に固定されています。
  • ・ LCD (DMA1)
  • ・ SDスロット (DMA2)
  • ・ 外部SPI機器 (DMA1)
このためLCDと外部SPI機器は同時に使用出来ず、次のエラーが発生します。
spi_master: spi_bus_initialize(96): dma channel already in use
[SPI_UTILS]: spi initialization failed with rc=0x103

M5Stack

VSPIにLCDとSDカードスロットが接続されています。外部用SPI端子もVSPIに接続されます。

ジャイロ、加速度、地磁気センサを使う (MicroPython[loborisリビルド版])Index

なるべく共通化できるようにプログラムを記述していますが、設定値は各センサーの仕様によります。

Madgwickフィルタが簡単に使える (MicroPython[loborisリビルド版])Index

Madgwickフィルタを使う事によりロール、ピッチ、ヨー角が高精度に取得できます。
  • ※ センサーがキャリブレーションされていないとドリフトします。
import madgwick
madgwick.begin(50) 50Hz:周波数が高い方が精度があがります ループ内の処理に合わせて決めます。

st=time.ticks_ms()
while True:
  diff=time.ticks_diff(time.ticks_ms(), st)
  if diff<20: 20ms:設定した周波数に対応したインターバル
    sleep_ms(diff)
  st=time.ticks_ms()

  gyro=imu.gyro
  acc=imu.acceleration
  # 地磁気センサーなし
  madgwick.updateIMU(gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2])

  # 地磁気センサーあり
  mag=imu.magnetic
  madgwick.update(gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2], mag[0], mag[1], mag[2])

  print(madgwick.getRoll(), madgwick.getPitch(), madgwick.getYaw()) ロール、ピッチ、ヨーの取得

DeepSleepを使う (MicroPython[loboris])Index

DeepSleep中は消費電力を節約できます。
import machine

指定期間(msec)DeepSleepした後に自動復帰
machine.deepsleep(3000) 任意の秒数(msec)を指定

指定のボタンを押したときにDeepSleepから復帰
rtc = machine.RTC()
rtc.wake_on_ext0(39, 0) 復帰に使うGPIO番号とレベル(0=LowActive / 1=HighActive)を指定
machine.deepsleep()
  • ※ 復帰に使えるGPIOの番号 0, 2, 4, 12-15, 25-27, 32-39
  • ※ LowActiveの場合は指定のGPIO(ひとつだけ)での復帰しか出来ません。

次のメソッドでDeepSleepからの復帰を確認できます。
machine.wake_reason()
machine.wake_description()
  • ※ DeepSleepからの復帰後はメモリーの状態が保持されていません(一部の特殊なメモリーを除く)そのため、MicroPythonではリセットとほぼ同じ動作になります。
  • ※ 指定期間での自動復帰以外を指定した場合はmodmachine.cにあるwake_stubが復帰時に実行されます。
  • ※ M5StickCではDeepSleepの前にi2c.writeto_mem(0x34, 0x12, b'\x41')を実行することでLDO(TFT)への電源供給を停止することができます。DeepSleep中でも5Vと3.3Vは出力されたままになります。(省電力機能がないハットなどでは、DeepSleep中でも電力が消費されます)、パワーマネジメントIC(AXP192)にシャットダウンを指示することで全ての電力供給を停止することが出来ます。この場合、電源ボタンを押す以外の復帰方法はありません。(復帰後にCPUがリセットされます)

動作クロックを変更する (MicroPython[loboris])Index

動作クロックには2MHz, 80Mhz, 160MHz, 240MHzを指定できます。
import machine

machine.freq(240) 任意の動作クロックを指定、省略した場合は現在のクロックを返します。
  • ※ DeepSleepと違いクロック変更後でもそのままプログラムが動作します。ただし通信系の機能を使っている場合など影響が起きる可能性があります。

Bluetoothを使う (MicroPython[loborisリビルド版]+btstack) (2019/08〜)Index

Bluetoothに関する情報は少ないのでMicroPythonに実装するのは苦労しました。モジュールさえ作ってしまえばあとの扱いは簡単です。 いまのところbtstackとの組み合わせがとても安定してます。btstackは登場からかなり経っていることもあり情報もそこそこあってソースコードも読みやすく、サンプルコードも充実しています。でもAPI構成(BLE ANCS Client API)が独自なのでMicroPythonのモジュールにするのはちょっと面倒です。 公式MicroPython(ESP32)がBluetooth(BLE)に対応しました。クラシックには対応してないようです。だんだんbtstackを使う必要性が薄れてくるかもしれません。
  • ※ Callbackルーチン内でのメモリ確保はエラーの要因になります。globalの使用を推奨します。
  • ※ run_loop_execute()を使うとPythonに制御が戻らないため、run_noloop_execute(timeout_ms)を追加

sixaxis (DUALSHOCK3)

DUALSHOCK3のデータを直接取得することが出来ます。
  • ※ DUALSHOCK3をPlayStation以外で使うためには事前の準備が必要です。
    Windowsの場合は、株式会社ベストテクノロジーさんのSIXAXISの設定が詳しいです。
    上記リンクから取得できる sixpair を使ってホスト側(M5xxxxx)のMACアドレスを DUALSHOCK3に書き込みます。 ホスト側のMACアドレスは下記の sixaxis.read_mac() で取得できます。
    USB 入力デバイスで認識されているときはDUALSHOCK3として認識されていません、うまく接続出来ないときはDUALSHOCK3をリセットします。

spp[Server] (Serial Port Profile)

仮想シリアルポートとして動作します。PC側の設定は着信(デバイスが接続を開始する)

spp[Client] (Serial Port Profile)

仮想シリアルポートとして動作します。PC側の設定は発信(コンピュータが接続を開始する)

hid (Wii Remote/Other)

WiiリモコンなどのHIDタイプのゲームコントローラからデータを直接取得することが出来ます。

hid[Slave] (Keyboard/Mouse)

HIDのスレーブ動作をします。キーボード、マウスとして機能します。

hciパケットをAndroidでキャプチャして解析

マイクを使う(M5StickC/M5Atom Echo) (MicroPython[loborisリビルド版]) (2019/06/26)Index

MicroPythonからMicophone SPM1423にアクセスする。
from machine import I2S

samples = bytearray(2*8192)
mic = I2S(I2S.NUM0, ws=Pin(0), sdin=Pin(34), mode=I2S.MASTER_RX_PDM,
    dataformat=I2S.B16, channelformat=I2S.RIGHT_LEFT,
    samplerate=10000, dmacount=2, dmalen=128) 任意のサンプルレート
    mic.set_clk(samplerate=10000,
dataformat=I2S.B16,channel=I2S.MONO)

numread = audio.readinto(samples,
    timeout=I2S.portMAX_DELAY) 録音実行
  • ※ I2Sのチャンネルは2チャンネルありますが、現在チャンネル0しか使えません。I2Sのチャンネル0はDACクラスでも一部(write)を除き使用している為、これらの機能は今のところ同時には使えません。

PMICを使う(M5StickC) (MicroPython[loborisリビルド版]) (2019/07/18)Index

MicroPythonからパワーマネジメントIC AXP192にアクセスする。
import axp192

i2c = machine.I2C(sda=21, scl=22)
pm=axp192.AXP192(i2c)

RTCを使う(M5StickC) (MicroPython[loborisリビルド版]) (2019/07/18)Index

MicroPythonからリアルタイムクロックBM8563にアクセスする。
import bm8563

i2c = machine.I2C(sda=21, scl=22)
rtc=bm8563.BM8563(i2c)

rtc.GetBm8563Time() (年, 月, 日, 時, 分, 秒, 週)が戻ります。
rtc.GetTime() (時, 分, 秒)が戻ります。
rtc.SetTime(Hours, Minutes, Seconds) 時間を設定します。
rtc.GetData() (年, 月, 日, 週)が戻ります。
rtc.SetData(Year, Month, Date, WeekDay) 日付を設定します。

撮影データを取得する(M5Camera) (MicroPython[loborisリビルド版]) (2019/01/23)Index

M5CameraにmicroPythonを載せて
MicroPythonから直接Camera(OV2640)にアクセス出来るようにしました。
import camera

camera.init([,reset] [,xclk] [,siod] [,sioc] [,d7] [,d6] [,d5] [,d4] [,d3] [,d2] [,d1] [,d0]
    [,vsync] [,href] [,pclk] [,freq] [,timer] [,channel] [,pixformat]
    [,framesize] [,quality] [,fb_count]) イニシャライズ

camera.sensor_set([,pixformat] [,framesize] [,contrast] [,brightness] [,saturation]
    [,gainceiling] [,quality] [,colorbar] [,whitebal] [,gain_ctrl] [,exposure_ctrl]
    [,hmirror] [,vflip] [,aec2] [,awb_gain] [,agc_gain] [,aec_value] [,special_effect]
    [,wb_mode] [,ae_level] [,dcw] [,bpc] [,wpc] [,raw_gma] [,lenc])

fb=camera.get_fb() フレームバッファ構造体のポインタを返す。
img=camera.buffer(fb) imgに取得したイメージデータが格納される
width=camera.width(fb) イメージ幅
height=camera.height(fb) イメージ高さ
camera.return_fb(fb) 取得イメージ解放(データの処理後は必ず解放)
#camera.reset()
camera.deinit()
公開準備中…

readintoでデータ格納位置をオフセット指定する (MicroPython[loborisリビルド版])Index

分割入力が出来るようにオフセットのオプションを追加しています。
sock.readinto(バッファ, 入力バイト数(省略可), オフセット(省略可))
例はソケット受信になります。jpegなどを分割転送できます。受信したデータはバッファから直接表示することが出来ます。

FTPサーバーに接続する (MicroPython[loborisリビルド版]) (2019/07/24)Index

自力でFTPサーバーに繋げるようにしました。スクリーンキーボードは加速値で動かしています。REPLを使わずに高速ファイル転送ができるので便利。

DualShock/2を使う(M5Stack) (MicroPython[loborisリビルド版]) (2018/06/24)Index

DualShock/2はSPIインターフェースで通信できます。
spi = SPI(
  spihost=SPI.VSPI,
  #mode = SPI.MASTER
  baudrate = 250000, polarity = 1,
  phase = 0, # Cable=1 / Remote=0
  bits = 8, firstbit = SPI.LSB,
  duplex = True,
  sck = Pin(18, Pin.OUT), mosi = Pin(23, Pin.OUT), miso = Pin(19, Pin.IN),
  cs = Pin(26, Pin.OUT))
DualShock/2の資料はこちらが詳しくとても分かりやすいです。
プレイステーション2専用ローリングスイッチ技術資料

ワイヤレスタイプのPS2コントローラーをM5Stackプロトモジュールに取り付ける場合の配線図。
プレイステーションのコネクタは除去しています。表面実装のクリスタルに交換しています。交換はハンダ付けの難易度が高いです。

ディスプレイに表示する(M5Stick[初代]) (MicroPython[loborisリビルド版]) (2019/01/27)Index

MicroPythonからOLED SH1107にアクセスする。(framebufを使っています)
import sh1107

oled = sh1107.SH1107_SPI(64, 128, spi, dc=Pin(27), res=Pin(33), cs=Pin(14), seg_remap=1)
# seg_remap=1 上下反転
oled.line(0, 0, 64, 128, 1) 端から端まで線を引く
oled.text('Micropython', 0, 0, 1, 1) '表示文字',X座標,Y座標,1=白,1=右90°回転
oled.scroll(-8, 0)
oled.show() 画面リフレッシュ

oled.bitmap('i2is.bmp') 'ビットマップファイル名' 128x64ドット 単色
oled.show() 画面リフレッシュ
  • ※ テストは電子レンジの中でやっています。初代M5Stickの工事設計認証(通称:技適)取得は予定にないとのことです。認証済みのESP32に載せ替えなければ電波暗室以外では使えない状況です。

エラー定数Index

ESP_ERR_NO_MEM0x101
ESP_ERR_INVALID_ARG0x102
ESP_ERR_INVALID_STATE0x103
ESP_ERR_INVALID_SIZE0x104
ESP_ERR_NOT_FOUND0x105
ESP_ERR_NOT_SUPPORTED0x106
ESP_ERR_TIMEOUT0x107
ESP_ERR_INVALID_RESPONSE0x108
ESP_ERR_INVALID_CRC0x109


各種改造は個人の責任のもとに行って下さい。改造により発生した事故,故障などに対し Attic or Garret では一切の責任を持ちません。また各種改造を施した製品は,各種保険及び保証を受ける権利を失う可能性があります。
Attic or Garret 中の各記事,写真及び改造内容を著者に無断で転載あるいは商業目的で利用することを禁じます。

Copyright © 1996-2021 Itoi.All Rights Reserved.