M5StackAuto : M5StickC, M5StickC Plus, M5Stack, M5ATOMを単独ソースで開発する

概要

もともとM5StickCの互換ライブラリを準備していたのですが、らびやんさん作成の高速LCDライブラリのLovyanGFXが複数ボードの自動認識機能が実装されたのでM5StickC風のライブラリで、M5シリーズを単独ソースの単独バイナリで同時開発できるものを作ってみました。

対応表

ボードLCD/LED電源管理IMURTCボタンAボタンBボタンCTone
M5StickCM5.Lcd
LovyanGFX
M5.Axp
AXP192
M5.Imu
SH200Q
MPU6886
M5.Rtc
BM8563
M5.BtnA
GPIO37
M5.BtnB
GPIO39
M5.BtnC
電源ボタン
M5StickC PlusM5.Lcd
LovyanGFX
M5.Axp
AXP192
M5.Imu
MPU6886
M5.Rtc
BM8563
M5.BtnA
GPIO37
M5.BtnB
GPIO39
電源ボタンM5.Beep
M5Stack BASICM5.Lcd
LovyanGFX
M5.Power
IP5306
M5.BtnA
GPIO39
M5.BtnB
GPIO38
M5.BtnC
GPIO37
M5.Beep
M5Stack GrayM5.Lcd
LovyanGFX
M5.Power
IP5306
M5.Imu
MPU6886
M5.BtnA
GPIO39
M5.BtnB
GPIO38
M5.BtnC
GPIO37
M5.Beep
M5Stack FireM5.Lcd
LovyanGFX
M5.Power
IP5306
M5.Imu
SH200Q
MPU6886
M5.BtnA
GPIO39
M5.BtnB
GPIO38
M5.BtnC
GPIO37
M5.Beep
ATOM LiteM5.dis
FastLED
M5.BtnA
GPIO39
ATOM MatrixM5.dis
FastLED
M5.Imu
MPU6886
M5.BtnA
GPIO39
ATOM EchoM5.dis
FastLED
M5.BtnA
GPIO39
TTGO T-watchM5.Lcd
LovyanGFX
M5.Imu
BMA423
M5.Rtc
BM8563
M5.BtnA
GPIO36

ちょっと大きな表になってしまいましたが、対応表になります。

利用ライブラリ

外部ライブラリ

LovyanGFX

ボード自動判定機能付きのLCDライブラリです。既存のTFT_eSPIライブラリとの互換性はありますので、ほぼ同じイメージで利用可能です。

FastLED

ATOMでのLED制御で利用しています。既存ライブラリと同じなので特に変わりはありません。

I2C_AXP192

M5StickCの電源管理などで使われているAXP192の単独ライブラリはなかったので、自作しました。既存との互換性は考えずに、単独で使いやすいように作っています。

I2C_MPU6886

こちらもM5StickCの6軸IMU用ライブラリを自作しました。

I2C_BM8563

こちらもM5StickCのRTC用ライブラリの自作ですが、そもそも他のRTCコンパチだったので既存ライブラリが使えた気がします、、、

内蔵ライブラリ

I2C_SH200Q

古いM5StickCなどで使われている6軸IMUですが、あまり利用例がないので独立ライブラリではなく内蔵させています。

I2C_IP5306

M5Stackで使われている電源管理ライブラリです。もともとはモバイルバッテリーなどの管理用ICなのでAXP192と比べるとかなり機能が少ないです。

I2C_BMA423

TTGO T-watchの3軸IMUです。M5シリーズではないのですが、対応できそうなので作ってみました。

使い方

ライブラリマネージャーに登録済みなので、「ESP32LitePack」で検索してください。

インストールするときに、必要なライブラリも一緒にインストールするかと聞かれるので「Install all」を選んでください。

ちなみにM5StickCやM5Stackの既存ライブラリは必要ありません。公式ライブラリには依存せずに互換ライブラリを構築しています。

スケッチ例

#include "M5StackAuto.h"

void setup() {
  M5.begin();
  M5.Axp.ScreenBreath(10);

  Serial.printf("Lcd Width=%d, Height=%d\n", M5.Lcd.width(), M5.Lcd.height());

  M5.Imu.Init();
  Serial.printf("IMU : %d\n", M5.Imu.imuType);

  M5.dis.drawpix(0, CRGB(255, 0, 0));
  M5.dis.drawpix(2, CRGB(0, 255, 0));
  M5.dis.drawpix(4, CRGB(0, 0, 255));
  M5.dis.drawpix(6, CRGB::White);

  // Not Support(ESP_LOGE)
  M5.Axp.GetWarningLevel();
}

void loop() {
  static int vol = 11;

  M5.update();

  M5.Lcd.setCursor(0, 4, 1);

  M5.Lcd.printf("M5StackAuto\n");

  M5.Lcd.printf("Battery\n");
  M5.Lcd.printf(" Temp :%6.1f\n", M5.Axp.GetTempInAXP192());  // AXP192 Internal temperature
  M5.Lcd.printf(" V(V) :%6.3f\n", M5.Axp.GetBatVoltage());    // Battery Voltage(3.0V-4.2V)
  M5.Lcd.printf(" I(mA):%6.1f\n", M5.Axp.GetBatCurrent());    // Battery Current(+:charge, -:decharge)

  M5.Lcd.printf("ASP\n");
  M5.Lcd.printf(" V(V) :%6.3f\n", M5.Axp.GetAPSVoltage());    // ESP32 Voltage

  M5.Lcd.printf("VBus(USB)\n");
  M5.Lcd.printf(" V(V) :%6.3f\n", M5.Axp.GetVBusVoltage());   // USB Voltage
  M5.Lcd.printf(" I(mA):%6.1f\n", M5.Axp.GetVBusCurrent());   // USB Current

  M5.Lcd.printf("VIN(5V-In)\n");
  M5.Lcd.printf(" V(V) :%6.3f\n", M5.Axp.GetVinVoltage());    // 5V IN Voltage
  M5.Lcd.printf(" I(mA):%6.1f\n", M5.Axp.GetVinCurrent());    // 5V IN Current

  if (M5.Imu.imuType != 0) {
    float ax;
    float ay;
    float az;
    float gx;
    float gy;
    float gz;
    float t;

    M5.Imu.getAccelData(&ax, &ay, &az);
    M5.Imu.getGyroData(&gx, &gy, &gz);
    M5.Imu.getTempData(&t);

    Serial.printf(" %f,%f,%f,%f,%f,%f,%f\n", ax, ay, az, gx, gy, gz, t);

    M5.Lcd.printf(" Accel   Gyro\n");
    M5.Lcd.printf("X%5.2f%7.1f\n", ax, gx);
    M5.Lcd.printf("Y%5.2f%7.1f\n", ay, gy);
    M5.Lcd.printf("Z%5.2f%7.1f\n", az, gz);
  }

  RTC_TimeTypeDef RTC_TimeStruct;
  RTC_DateTypeDef RTC_DateStruct;
  M5.Rtc.GetTime(&RTC_TimeStruct);
  M5.Rtc.GetData(&RTC_DateStruct);
  M5.Lcd.printf("%04d-%02d-%02d\n", RTC_DateStruct.Year, RTC_DateStruct.Month, RTC_DateStruct.Date);
  M5.Lcd.printf("%02d:%02d:%02d\n", RTC_TimeStruct.Hours, RTC_TimeStruct.Minutes, RTC_TimeStruct.Seconds);

  if (M5.BtnA.wasPressed()) {
    Serial.println("M5.BtnA.wasPressed()");
    M5.Beep.tone(1000, 100);
  }
  if (M5.BtnB.wasPressed()) {
    Serial.println("M5.BtnB.wasPressed()");
    vol--;
    M5.Beep.setVolume(vol);
    Serial.printf("vol = %d\n", vol);
  }
  if (M5.BtnC.wasPressed()) {
    Serial.println("M5.BtnC.wasPressed()");
    vol++;
    M5.Beep.setVolume(vol);
    Serial.printf("vol = %d\n", vol);
  }
  if (M5.BtnA.wasReleased()) {
    Serial.println("M5.BtnA.wasReleased()");
  }
  if (M5.BtnB.wasReleased()) {
    Serial.println("M5.BtnB.wasReleased()");
  }
  if (M5.BtnC.wasReleased()) {
    Serial.println("M5.BtnC.wasReleased()");
  }

  delay(100);
}

M5StackAuto.hを読み込むことで、M5StickC的なライブラリ構成でプログラムを行うことができます。基本的に利用できないデバイスのクラスを呼んでもエラーにならないようになっています。画面の大きさやボタンの数などで条件わけをする必要はありますが、既存に比べると同一ソースでプログラムをしやすいとは思います。

クラスの紹介

M5クラス

begin()とupdate()関数以外はクラスを持っているだけです。begin()の引数はM5StickCのものを採用していますが、通常は引数無しで実行するだけで良いと思います。

update()関数はBeep.updte()も内部で呼んでいます。

M5.Lcdクラス

LovyanGFXライブラリを利用していますので、互換性はありますがかなりの数の機能が追加されています。既存の機能もかなり高速化されています。

M5.disクラス

ATOMのLED制御クラスです。ほぼ同じラッパークラスを作っているので同じように利用できると思います。ただし、色の並びがおかしいのは修正していますのでCRGB::RedやCRGB(255, 0, 0)は正しく赤く光ります。

M5.Imuクラス

M5StickCのImu自動判定と同じようなことをしています。自動判定自体私がPRして本家に取り込んでもらったので、、、

SH200Q、MPU6886、BMA423を自動判定しています。M5Stack系は自動判定がなかったのでかなり便利に使えるのではないでしょうか?

今後軸が違う可能性があるので、座標系をM5StickCに合わせる改修が入る可能性があります。TTGOのT-watchで使われているBMA423は3軸なんで加速度しか取れませんのでご注意ください。

MahonyAHRSはまだ取り込んでいませんので使えませんので注意してください。MahonyAHRSは単体ライブラリとして存在しているので、そちらを使うのを推奨します。そのうち依存ライブラリに追加して内部に取り込むかもしれません。

M5.Axpクラス

AXP192の既存ライブラリで使いそうな関数のみ実装しています。Sleep系は実装していませんのでご注意ください。

M5.Powerクラス

M5Stackの電源管理クラスです。バッテリー残量取得などは実装していますがスリープ系は未実装です。使える機能が少ないのでAxpクラスに統合するかもしれません。

M5.Rtcクラス

M5StickCとM5StickC Plus、TTGO T-watchで使われているRTCをサポートしています。

M5.BtnA, BtnB, BtnCクラス

ここはM5StickCではなく、M5Stackにそろえています。M5StickCの電源ボタンはM5.Axp.GetBtnPress()ではなく、M5.BtnC.wasReleased()を利用してください。

ATOMとTTGO T-watchは物理的にボタンが1つしかないので、BtnAのみになります。

M5.Beepクラス

M5StackC PlusのBeepクラスをベースにしています。M5StackはSpeakerクラスだったので注意してください。M5Stackは爆音か変な音だったのであまり使っている人がいないのでしょうか?

M5Stackは、M5StackC Plusと同じぐらいの大きさのクリアな音がするように調整しました。ただPWMで出力を絞っているだけなので、周波数が変わって音がちょっと違います。。。

Wire1クラス

内蔵しているI2Cにアクセスするクラスになります。こちらはATOMだけピン配置が違うのですがボードの自動判定に応じたピンで初期化してあります。

Groveなどの端子で別系統のI2Cを使う場合にはWireクラスの方を利用してください。M5StackのPortAなどは内部と同じ端子が出ているのでWire1になります。

開発の注意点

開発で利用するボードは常にM5Stick-Cで大丈夫です。シリアルポートだけ利用するボードに変更してください。アップロード速度は1500000のままで、すべてのボードに転送できます。

また、一部サポートしていない関数があるので、Core Debug Levelはエラー以上にしてもらえると、シリアルモニタにで「Not Support」と関数名と一緒に表示されると思います。

まとめ

まだ資料が圧倒的に足りていませんが、M5StickCの開発経験がある人であればそれほど違和感なくM5Stackの開発もできると思います。とにかくシリアルポートを変更することだけで複数のボードで開発できるのは便利です!

当初はM5StickCとM5StickC Plusを同時開発するライブラリの予定だったのですが、LovyanGFXが他のボードも自動判定してくれたのでこんな感じのライブラリになりました。

もともとは個人的に便利なスケッチ例などを入れる予定だったので、ESP32LitePackという名前になっているので、中身と名前がミスマッチなのです、、、

1件のコメント

コメントする

メールアドレスが公開されることはありません。

管理者承認後にページに追加されます。公開されたくない相談はその旨本文に記載するかTwitterなどでDM投げてください。またスパム対策として、日本語が含まれない投稿は無視されますのでご注意ください。