pdf-icon

Migrating to M5Unified

This document briefly outlines how to migrate from other M5Stack driver libraries to M5Unified.

Major Differences from Existing M5Stack Libraries

  1. In previous M5Stack libraries, M5.begin() had different parameters and counts for different device libraries. To unify them, M5Unified uses a structure as a parameter to pass various configuration information.
  2. M5Unified does not integrate the RGB LED driver; you need to install the FastLED library separately.
  3. SDCard initialization is not integrated by default. To support ESP-IDF, SD.h is not used by default, and SD.begin() is not executed internally.
  4. The graphics library has changed from TFT-eSPI to M5GFX, and the drawing API is incompatible. For more information about the M5GFX library, refer to the Getting Started with M5GFX tutorial .
  5. The speaker functionality has been improved compared to previous libraries. Additionally, models with integrated buzzers now support playing audio files, among other features.
  6. The button naming conventions differ. For consistency, even if a device only has one button, its name is BtnA instead of Btn.

Notes on Using M5Unified

Note: M5Unified cannot be used in conjunction with existing M5Stack driver libraries because they contain an instance with the same name (M5), leading to the following compilation error:
error: reference to 'jpeg_div_t' is ambiguous jpeg_div_t scale = JPEG_DIV_NONE);
error: conflicting declaration 'M5Stack M5' extern M5Stack M5;

Changes to M5.begin

In earlier driver libraries, M5.begin() had different parameters for each device, as shown below:

// 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);

After switching to M5Unified, some initialization configuration information is implemented in a cfg structure:

auto cfg = M5.config();

// Set the items you want to configure. Omit the following two lines if you use the default settings.
cfg.serial_baudrate = 115200;
cfg.output_power = true;

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

Changes to M5.Btn

The number of available buttons varies by M5Stack product, primarily using the following naming conventions:

  • General button group:
    • BtnA, BtnB, BtnC
  • Power button group:
    • BtnPWR
  • Extended button group:
    • BtnEXT

Change example: Display the message "BtnA isPressed" when Button A is pressed, and "BtnA isReleased" when released.

#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() {
    // update the button state.
    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");
    }
}

To update the button state, execute 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(); // update the button state.

    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(); // update the button state within the subloop.

            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++;
        }
    }
}

For more information, refer to the M5Unified Button Class .

Changes to M5.Lcd

M5Unified relies on the M5GFX library . When installing M5Unified in the Arduino IDE, a popup will prompt you to install this dependency; please install all of them.

Change example: This example program supports M5Stack controllers with LCD or e-Ink displays, using the same set of drawing APIs regardless of screen type.

#include <M5Unified.h>

void setup() {

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

}

void loop() {
}

Changes to SDCard

After switching to M5Unified.h, SD.h is no longer included by default. You must manually add #include <SD.h>:

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

Add SD.begin():

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

Internal I2C Release

M5Unified initializes the internal I2C by default. For devices like Basic/Gray/Fire, where PORT.A is reused for internal I2C, call M5.In_I2C.release() if PORT.A is mapped to other functions.

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

M5Unified Migration Example

If you convert the FactoryTest.ino example from the M5Stack library to M5Unified, it will look like this:

  • Change the header file
  • Modify the IMU workflow
  • Add SD.begin
  • Add the definition of SPEAKER_PIN
  • Change the parameters of 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