M5Unifiedへの移植のポイント

このドキュメントはArduino IDE及びESP-IDF用のライブラリであるM5Stack個別のライブラリからM5Unifiedへ変更する際の移行方法を簡単に紹介します。
※ M5Unified v0.0.7の情報を元にこのキュメントは作成されています。

M5Unifiedの概要

M5Unifiedは、M5Stack製品の仕様の異なるハードウェアを共通のAPIで扱うためのライブラリです。設定項目は増えますが、同じソースを違うデバイスでも利用可能であり下記のようなケースで活躍します。

  • M5Stackでアプリケーション開発してM5Atomで運用する。
    M5Unifiedは画面描画が機能が無いM5Atomともソースの共有が可能です。
    ※ スケッチサイズやメモリの消費量は増加します。
  • M5StackでもM5StickCでもどちらでも動作するアプリケーションを開発する。
    グラフィクスの座標を相対座標(例えば、ディスプレイの中心等)で指定する、あるいはスプライトを利用して拡大縮小すれば異なる画面の大きさにも対応が可能です。
  • スピーカーとブザーで音を鳴らすことが可能

M5Stack既存ライブラリとの大きな違い

  • Includeファイルが統一できる。
    従来はM5Stack Basicなら#include <M5Stack.h>,M5StickCなら#include <M5StickC.h>とデバイスによってIncludeするヘッダファイルが異なっていましたが、M5Unifiedでは#include <M5Unified.h>で済みます。デバイスの種類はライブラリ内で自動識別されます。

  • M5.begin()の引数が違う。
    従来のM5.begin()では各デバイスのライブラリで引数の内容も数も異なります。それを統一するために構造体を引数にしています。 詳しくは M5.begin() を参照してください。

  • M5Atom等のRGB LED制御には対応していない。
    M5Unified v0.0.7の時点では、M5AtomのLED制御には対応していません。別途、 FastLEDライブラリ をIncludeして実装してください。

  • TFCard(SDCard)の自動マウント(SD.begin())はしていない。
    ESP-IDFに対応するために、SD.hはデフォルトでは使うことができず、使用できるTFカードのライブラリでSDFatも選択することを想定し、SD.begin()を内部で実行していません。

  • グラフィクスライブラリがTFT-eSPIから、M5GFXへ変更
    既存ライブラリはTFT_eSPIを内部に持っていましたが、M5UnifiedはM5GFXに依存しておりM5GFXを利用するように変更されています。M5.Lcd(M5.Display)で利用できるという点は変わっていません。APIは異なるので注意してください。
    M5GFXライブラリについては M5GFXの始め方 を参照してください。

  • Speaker機能
    Core1は音にノイズが入っていますが、従来のライブラリよりも改善されます。また、圧電ブザー搭載機種でもブザーだけでなく音声ファイルの再生等が可能です。
    ※ 大音量の音声ファイルや低周波を大音量で流し続けるとスピーカーが壊れることがありますので十分ご注意ください。

  • Btn名の違い
    共通化するためにボタンが1つの機種でもBtnではなくBtnAとなります。

M5Unifiedを使うときの注意

  • 他のM5Stackライブラリと併用はできません。
    M5という名前が同一のため、下記のようなエラーがコンパイル時に出ます。
error: reference to 'jpeg_div_t' is ambiguous jpeg_div_t scale = JPEG_DIV_NONE);
error: conflicting declaration 'M5Stack M5' extern M5Stack M5;
  • M5Atom MatrixをFastLEDライブラリで使用する場合、 RGB LEDのbrightnessは20以下(設定範囲:0~100)で使用してください。それ以上の明るさを用いると熱でアクリル板とLEDにダメージを与えます。

M5.begin()

M5.config()及びM5.begin()の使い方

M5.begin()のパラメータは構造体で設定します。setup()関数の先頭で実行してください。

初心者の方はデフォルト設定を使う場合もなるべくsetup関数の最初に下記のように記述してください。
auto cfg = M5.config(); // 設定用の構造体を代入。

// 設定したい項目を設定する。下記の2行はデフォルト設定を使う場合は省略してください。
cfg.serial_baudrate = 115200;
cfg.output_power = true;

M5.begin(cfg); // 設定した値でデバイスを開始する。

M5.config()のパラメータ

詳細については下記のリンクを参照してください。

M5.config()

従来のM5.begin()

従来のM5.begin()は下記のようにデバイスごとに引数の内容も数もバラバラです。

// M5Stack
M5.begin(bool LCDEnable = true, bool SDEnable = true, bool SerialEnable = true, bool I2CEnable = false);

// M5Core2
M5.begin(bool LCDEnable = true, bool SDEnable = true, bool SerialEnable = true, bool I2CEnable = false, mbus_mode_t mode = KMBusModeOutput);

// M5StickC
M5.begin(bool LCDEnable = true, bool PowerEnable = true, bool SerialEnable = true);

// M5StickCPlus
M5.begin(bool LCDEnable = true, bool PowerEnable = true, bool SerialEnable = true);

// M5Atom
M5.begin(bool SerialEnable = true, bool I2CEnable = true, bool DisplayEnable = false);

ボタンについて

M5Stack製品によって使用できるボタンが異なります。

  • BtnA, BtnB, BtnC
    カスタマイズ可能なボタン
  • BtnPWR
    電源ボタンもカスタマイズ可能な機種もあります。
  • BtnEXT
    詳細は下記のリンクを参照してください。

ボタンについて

ボタンの使い方

基本

ボタンAを押している間、"BtnA isPressed."、離すと"BtnA isReleased"と表示されます。

#include <M5Unified.h>

void setup() {
    auto cfg = M5.config();
    cfg.clear_display = true;
    M5.begin(cfg);
    M5.Lcd.setTextSize(2);
    M5.Lcd.println("Press BtnA");
}

void loop() {
    // ボタンの状態を更新する。
    M5.update();

    if (M5.BtnA.isPressed()) {
        M5.Display.setCursor(0, 16);
        M5.Display.println("BtnA isPressed ");
    } else {
        M5.Display.setCursor(0, 16);
        M5.Display.println("BtnA isReleased");
    }
}

多重ループの場合

ボタンの状態を更新するためにはM5.update()を実行する必要があります。ループが増えるとそのループにもM5.update()が必要なのでその点は注意してください。

#include <M5Unified.h>

uint32_t loop_count = 0;

void setup() {
    auto cfg = M5.config();
    cfg.clear_display = true;
    M5.begin(cfg);
    M5.Lcd.setTextSize(2);
    M5.Lcd.println("Press BtnA");
}

void loop() {

    M5.update();                                            // ボタンの状態を更新する。

    if (M5.BtnA.wasPressed()) {
        M5.Display.fillScreen(TFT_BLACK);
        M5.Display.setCursor(0, 0);
        M5.Display.println("BtnA wasPressed");
        M5.Display.println("Press BtnA Stop Count");

        while (true) {
        
            M5.update();                                    // サブループ内でボタンの状態を更新する。

            M5.Display.setCursor(0, 32);
            M5.Display.printf("count: %10d\n", loop_count);
            if (M5.BtnA.wasPressed()) {
                M5.Display.println("Count Stop");
                loop_count = 0;
                break;
            }
            loop_count++;
        }
    }
}

ボタンの状態について

ボタンの状態は下記の関数で取得できます。初心者の方は、wasPressed()かwasClicked()をまず使ってみてください。

  • was
    M5.update()実行前に状態が起きたかを検出します。

    • wasPressed
      押されたかどうか
    • wasReleased
      離されたかどうか
  • is
    M5.update()実行時に状態が起きているかどうかを検出します。

    • isPressed
      押されているかどうか
    • isReleased
      離されているかどうか
  • クリック
    クリックされたかどうかを検出します。

    • wasClicked
      クリックされる度にtrueを返します。
    • wasSingleClicked
      クリックを検出した回数が1回の時trueを返します。
    • wasDoubleClicked
      クリックを検出した回数が2回の時trueを返します。
    • wasDecideClickCount
      連続してクリックされた場合に最後のクリックを検出した時にtrueを返します。(※typoでwasDeciedClickCountのバージョンがあります。)
  • 長押し
    時間を指定して、長押しやボタンを離した状態を検出します。

    • pressedFor(uint32_t msec)
      ボタンを指定した時間以上押された場合にtrueを返します。
    • releasedFor(uint32_t msec)
      ボタンを指定した時間以上離した場合にtrueを返します。

クリックの回数

ボタンを連続してクリックすると回数が加算されます。回数はM5.Btn.getClickCount()で取得できます。

画面描画について

M5Unifiedは M5GFXライブラリ に依存しており、インストールしていない場合は一緒にインストールしてください。(ArduinoIDEはM5Unifiedをインストールする際にダイアログメッセージが表示されます。PlatformIOでは自動でインストールされます。)

M5.Display(またはM5.Lcd)

画面付きのデバイスではM5.DisplayでM5GFXのAPIを使用することができます。定義は必要ありません。

M5UnifiedでHelloWorld

M5UnifiedでHelloWorldするには下記のように記述します。LCD搭載機種でもe-Ink Display搭載機種でも同じソースを利用できます。また、M5Atomでも画面の動作は何も動きませんがそのまま利用できます。

#include <M5Unified.h>

void setup() {

  auto cfg = M5.config();
  M5.begin(cfg);
  M5.Display.setTextSize(3);
  M5.Display.println("HelloWorld!");

}

void loop() {
}

M5Unified化する際の主な変更点

APIも若干の違いがありますが、細かい点は省いています。

includeするヘッダファイルの変更

製品個別のライブラリのヘッダからM5Unified.hへ変更します。SDへアクセスする際はSD.hも追加します。

- #include <M5Stack.h>
+ #include <SD.h>
+ #include <M5Unified.h>

M5.begin()の変更

M5.begin()を、M5.config()を実行して下記のように変更します。

- M5.begin(true, true, true, true);
+ auto cfg = M5.config();
+ cfg.internal_imu = true;
+
+ M5.begin(cfg);

IMUの処理

IMUの処理はM5.Imuでアクセスするように変更します。

SD.begin()の追加

SD.begin()を追加します。

+    while (false == SD.begin(GPIO_NUM_4, SPI, 25000000))
+    {
+      delay(500);
+    }

内部I2Cのリリース

M5Stack Basic/Gray/Fire等のPort.AをI2C以外で使用したい場合、M5.In_I2C.release()を実行する必要があります。

auto cfg = M5.config();
.
.
.
M5.begin(cfg);
M5.In_I2C.release();

【参考】FactoryTest.inoのM5Unified化

M5StackのExampleであるFactoryTest.inoをM5Unifiedに変更すると下記のようになります。

  • ヘッダファイルの変更
  • IMUの処理を変更
  • SD.beginの追加
  • SPEAKER_PINの定義を追加
  • M5.Btn.pressedForの引数を変更
diff --git a/src/main.cpp b/src/main.cpp
index b735e30..dae9b99 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,10 +1,10 @@
 #include <Arduino.h>
-#include <M5Stack.h>
+#include <SD.h>
+#include <M5Unified.h>
 #include <stdlib.h>
 //#include "FastLED.h"
 
 #include "WiFi.h"
-#include "utility/MPU9250.h"
 
 extern const unsigned char gImage_logoM5[];
 extern const unsigned char m5stack_startup_music[];
@@ -13,7 +13,8 @@ extern const unsigned char m5stack_startup_music[];
 #define min(a, b) (((a) < (b)) ? (a) : (b))
 #endif
 
-MPU9250 IMU;
+
+#define SPEAKER_PIN 25
 
 // #define LEDS_PIN 15
 // #define LEDS_NUM 10
@@ -127,15 +128,15 @@ void writeFile(fs::FS &fs, const char *path, const char *message) {
 }
 
 void buttons_test() {
-    if (M5.BtnA.wasReleased() || M5.BtnA.pressedFor(1000, 200)) {
+    if (M5.BtnA.wasReleased() || M5.BtnA.pressedFor(1000)) {
         M5.Lcd.printf("A");
         Serial.printf("A");
     }
-    if (M5.BtnB.wasReleased() || M5.BtnB.pressedFor(1000, 200)) {
+    if (M5.BtnB.wasReleased() || M5.BtnB.pressedFor(1000)) {
         M5.Lcd.printf("B");
         Serial.printf("B");
     }
-    if (M5.BtnC.wasReleased() || M5.BtnC.pressedFor(1000, 200)) {
+    if (M5.BtnC.wasReleased() || M5.BtnC.pressedFor(1000)) {
         M5.Lcd.printf("C");
         Serial.printf("C");
     }
@@ -475,8 +476,11 @@ void setup() {
     //     GPIO_test();
     // }
 
+    auto cfg = M5.config();
+    cfg.internal_imu = true;
+
     // initialize the M5Stack object
-    M5.begin();
+    M5.begin(cfg);
 
     /*
       Power chip connected to gpio21, gpio22, I2C device
@@ -484,6 +488,10 @@ void setup() {
       If used battery, please call this function in your project
     */
     M5.Power.begin();
+    while (false == SD.begin(GPIO_NUM_4, SPI, 25000000))
+    {
+      delay(500);
+    }
 
     // dac test
     // if (gpio_test_flg)
@@ -492,7 +500,6 @@ void setup() {
     // }
     startupLogo();
     // ledBar();
-    Wire.begin();
 
     // Lcd display
     M5.Lcd.setBrightness(100);
@@ -609,54 +616,33 @@ void setup() {
         M5.Lcd.setBrightness(i);
         delay(2);
     }
-
-    byte c = IMU.readByte(MPU9250_ADDRESS, WHO_AM_I_MPU9250);
-    Serial.print("MPU9250 ");
-    Serial.print("I AM ");
-    Serial.print(c, HEX);
-    Serial.print(" I should be ");
-    Serial.println(0x71, HEX);
-    Serial.println("");
-    M5.Lcd.setCursor(20, 0);
-    M5.Lcd.print("MPU9250");
-    M5.Lcd.setCursor(0, 10);
-    M5.Lcd.print("I AM");
-    M5.Lcd.setCursor(0, 20);
-    M5.Lcd.print(c, HEX);
-    M5.Lcd.setCursor(0, 30);
-    M5.Lcd.print("I Should Be");
-    M5.Lcd.setCursor(0, 40);
-    M5.Lcd.println(0x71, HEX);
-    M5.Lcd.println();
-    delay(100);
-
-    IMU.initMPU9250();
-    // Initialize device for active mode read of acclerometer, gyroscope, and
-    // temperature
-    Serial.println("MPU9250 initialized for active data mode....");
-
-    // Read the WHO_AM_I register of the magnetometer, this is a good test of
-    // communication
-    byte d = IMU.readByte(AK8963_ADDRESS, WHO_AM_I_AK8963);
-    Serial.print("AK8963 ");
-    Serial.print("I AM ");
-    Serial.print(d, HEX);
-    Serial.print(" I should be ");
-    Serial.println(0x48, HEX);
-
-    // M5.Lcd.fillScreen(BLACK);
-    M5.Lcd.setCursor(20, 100);
-    M5.Lcd.print("AK8963");
-    M5.Lcd.setCursor(0, 110);
-    M5.Lcd.print("I AM");
-    M5.Lcd.setCursor(0, 120);
-    M5.Lcd.print(d, HEX);
-    M5.Lcd.setCursor(0, 130);
-    M5.Lcd.print("I Should Be");
-    M5.Lcd.setCursor(0, 140);
-    M5.Lcd.print(0x48, HEX);
+    const char* name;
+    // run-time branch : imu model check
+    switch (M5.Imu.getType())
+    {
+      case m5::imu_t::imu_mpu6050:
+        name = "MPU6050";
+        break;
+      case m5::imu_t::imu_mpu6886:
+        name = "MPU6886";
+        break;
+      case m5::imu_t::imu_mpu9250:
+        name = "MPU9250";
+        break;
+      case m5::imu_t::imu_sh200q:
+        name = "SH200Q";
+        break;
+      default:
+        name = "none";
+        break;
+    }
+    M5.Display.print("IMU:");
+    M5.Display.println(name);
+    M5.Display.endWrite();
+    ESP_LOGI("setup", "imu:%s", name);
     delay(1000);
 
+
     M5.Lcd.setCursor(0, 0);
     M5.Lcd.println("wifi test:");
     M5.Lcd.fillScreen(BLACK);
On This Page