M5Stack ATOMS3を購入

概要

M5Stackから画面付きのATOMが販売されましたので、確かめてみました。LED RGBを16個搭載したATOM Matrixと同じ価格ながら画面があるのはやっぱり便利です。(ドルベースでは同じ価格ですが、日本ではATOMS3のほうが安いです!)

製品

上記から購入できます。おそらく日本向けは戦略価格なのでいまは日本から購入したほうが早いし、安いと思います。販売直後は品薄ですが、レギュラー商品なのでもう少ししたら在庫も潤沢になると思います。

こんな感じで届きました。パッケージは簡略化されていますが、かなりがんばって価格を下げるためだと思われます。

こんな感じで前面がLEDになっています。

裏側は従来どおりですね。

ライブラリについて

公式ライブラリは準備されていますが、現状でいまいちでした。。。

公式ライブラリ

発売当初のライブラリは未完成な状態でした。ライブラリマネージャにも登録されておりませんし、Arduino IDEでもエラーがでて使うことができません。

今後調整されることを期待します。ただグラフィックライブラリがIn_eSPIをつかっていて、M5GFX(LovyanGFX)ではありません。積極的に使う必要は無い気がします。

M5Unified

M5Stack製品共通で使えるM5UnifiedでもATOMS3に対応しています。ATOMS3では画面とボタン、IMUに対応しています。IRには対応していませんが、公式ライブラリも同じなのでM5Unifiedの利用をおすすめします。

こちらのライブラリはライブラリマネージャから追加可能です。

開発環境

Arduino IDE

Arduino IDEは2系をおすすめします。この記事を書いているときの最新は2.0.3でした。アイコンが四角い方が2系になります。

ESP32ボードマネージャー

M5StackのボードマネージャーではATOMS3がまだ追加されていませんでした。M5Stack版ではなくESP32の公式ボードマネージャーを使ったほうがいいと思います。今後M5Stack版にも追加されると思いますのでATOMS3がボードの選択肢になかったESP32公式を使っていください。

また、最近でたESP32 2.0.6を使うことでデバッグが可能となります。

Arduino IDEの設定

ボード設定は「ESP32S3 Dev Module」を選択します。

細かい設定がいろいろありますが、気をつけないといけないのはUSB系の設定です。

項目設定値備考
USB CDC On BootEnableUSB SerialをSerialクラスとして使う
CPU Frequency240MHz
Core Debug Levelなし
USB DFU On BootDisabled
Erase All Flash Before Sketch UploadDisabled
Events Run OnCore 1
Flash ModeQIO 80MHz
Flash Size4MB
JTAG AdapterIntegrated USB JTAG
Arduino Runs OnCore 1
USB Fireware MSC On BootDisabled
Partition SchemeDefault 4MB with spiffs
PSRAMDisabled
Update ModeUSB-OTG CDC
Update Speed921600
USB ModeHardware CDC and JTAGHardware CDC and JTAGを選択

※USBまわりの設定によりシリアル出力の方法が変わります。別の記事にてまとめる予定です。

また、ボードの設定を変更するとUSB接続でシリアルが利用できないモードになる可能性があります。その場合にはATOMS3の左側にあるリセットボタンを2秒以上押すことで緑のLEDが光って強制書き込みモードにすることが可能です。

サンプルスケッチ

M5Unifiedの「HowToUse」をまずは動かすのがよいと思います。画面とボタン、IMUの使い方がわかります。

#include <M5Unified.h>

void setup(void) {
  auto cfg = M5.config();
  M5.begin(cfg);

  M5.Display.setTextSize(1);

  const char* name;
  switch (M5.getBoard()) {
    case m5::board_t::board_M5AtomS3:
      name = "ATOM S3";
      break;
    default:
      name = "Who am I ?";
      break;
  }
  M5.Display.startWrite();
  M5.Display.print("Core:");
  M5.Display.println(name);
  Serial.printf("core:%s\n", name);

  switch (M5.Imu.getType()) {
    case m5::imu_t::imu_mpu6886:
      name = "MPU6886";
      break;
    default:
      name = "none";
      break;
  }
  M5.Display.print("IMU:");
  M5.Display.println(name);
  M5.Display.endWrite();
  Serial.printf("imu:%s\n", name);
}

void loop(void) {
  delay(1);
  int h = M5.Display.height() / 8;

  M5.update();

  static constexpr const int colors[] = { TFT_WHITE, TFT_CYAN, TFT_RED, TFT_YELLOW, TFT_BLUE, TFT_GREEN };
  static constexpr const char* const names[] = { "none", "wasHold", "wasClicked", "wasPressed", "wasReleased", "wasDeciedCount" };

  M5.Display.startWrite();

  auto state = M5.BtnA.wasHold()          ? 1
          : M5.BtnA.wasClicked()          ? 2
          : M5.BtnA.wasPressed()          ? 3
          : M5.BtnA.wasReleased()         ? 4
          : M5.BtnA.wasDeciedClickCount() ? 5
                                          : 0;
  if (state) {
    Serial.printf("BtnA:%s  count:%d\n", names[state], M5.BtnA.getClickCount());
    if (!M5.Display.displayBusy()) {
      M5.Display.fillRect(0, h * 3, h, h - 1, colors[state]);
      M5.Display.setCursor(0, h * 3);
      M5.Display.printf("%d", M5.BtnA.getClickCount());
    }
  }

  M5.Display.endWrite();

  if (M5.Imu.isEnabled()) {
    int ox = (M5.Display.width() + h) >> 1;
    static int prev_xpos[6];
    int xpos[6];
    float val[6];
    M5.Imu.getAccel(&val[0], &val[1], &val[2]);
    M5.Imu.getGyro(&val[3], &val[4], &val[5]);
    int color[6] = { TFT_RED, TFT_GREEN, TFT_BLUE, TFT_RED, TFT_GREEN, TFT_BLUE };

    for (int i = 0; i < 3; ++i) {
      xpos[i] = val[i] * 50;
      xpos[i + 3] = val[i + 3] / 2;
    }

    M5.Display.startWrite();
    M5.Display.setClipRect(h, h, M5.Display.width(), M5.Display.height());
    M5.Display.waitDisplay();
    for (int i = 0; i < 6; ++i) {
      if (xpos[i] == prev_xpos[i]) continue;

      int px = prev_xpos[i];
      if ((xpos[i] < 0) != (px < 0)) {
        if (px) {
          M5.Display.fillRect(ox, h * (i + 2), px, h, M5.Display.getBaseColor());
        }
        px = 0;
      }
      if (xpos[i] != px) {
        if ((xpos[i] > px) != (xpos[i] < 0)) {
          M5.Display.setColor(color[i]);
        } else {
          M5.Display.setColor(M5.Display.getBaseColor());
        }
        M5.Display.fillRect(xpos[i] + ox, h * (i + 2), px - xpos[i], h);
      }
      prev_xpos[i] = xpos[i];
    }
    M5.Display.clearClipRect();

    M5.Display.endWrite();
  }
  M5.Display.display();
}

上記が「HowToUse」でATOMS3に関連する場所のみ抜き出したもの+ちょっとした修正になります。IMUのグラフを描いている部分がちょっと複雑ですが、シンプルに利用することができます。

Serialだけはちょっと注意が必要で、Serialクラスは「USB CDC On Boot」をEnableにしないとUSBではなくてESP32標準のUARTになります。そしてATOMS3にはUARTのポートがありません。つまりUSB側シリアルを使いたい場合には「USB CDC On Boot」をEnableにしてSerialクラスをUSB側にするか、デフォルトのままの設定の場合にはSerialクラスのかわりに、USBSerialクラスを利用する必要があります。

※USBまわりの設定によりシリアル出力の方法が変わります。別の記事にてまとめる予定です。

JTAGでのデバッグ

ESP32-S3の特徴として、USBシリアルとJTAGを内蔵していることです。チップ自体も無印ESP32よりも一つ新しいシリーズが搭載されています。

そのためArduino IDEの2系とEPS32ボードマネージャーの2.0.6以降を組み合わせることで、ブレイクボイントを利用したデバッグがすぐに使えるようになります。これは便利ですね。いままでは別にJTAGボードを用意して、PlatformIOを使ったりと気軽に使うことができませんでした。

デバッグできない場合

上記の記事を参考にしてください。

まとめ

もう少し確認が必要ですが、ファーストインプレッションとしてここまでにしたいと思います。とりあえず最新のArduino IDEとESP32ボードマネージャーを使って、M5Unifiedライブラリで触るのがおすすめです!

続編

コメント

  1. Aoya-Uta より:

    ありがとうございます。いつも参考にさせていただいています!
    私もM5Stack ATOMS3を購入したのを機に「公式ライブラリ:M5AtomS3.h→M5Unified」へ移行しようと考えいますが、M5Canvasと組合せると上手く動作しないので、質問させて下さい。

    ◆質問
    「M5Unified+M5Canvas」で、ATOMS3でボタンを押した時にスプライトを利用して画面に図形を表示することは可能でしょうか?

    ◆詳細
    参考例を「M5Unified+M5Canvas」で画面表示できたのですが、これに”M5.begin(cfg)を指定”すると、ATOMS3に何も表示されなくなりました。M5.begin(cfg)をコメントにすると表示されます。これは、「M5Unified+M5Canvas」ではプログラムを記載できないということでしょうか?

    ◆テスト例:M5Canvas – 1.簡単な利用サンプル
    https://docs.m5stack.com/ja/api/m5gfx/m5gfx_canvas
    ==ここから==
    //#include // M5Unifiedに変更のためコメント
    //#include // M5Unifiedに変更のためコメント

    #include // M5Unifiedを追加
    M5GFX display;
    M5Canvas canvas(&display);

    int32_t x;
    int32_t y;

    void setup()
    {
    auto cfg = M5.config(); // M5.begin設定を追加すると画面が表示されない
    M5.begin(cfg) // M5.begin設定

    display.begin();
    -以下参考例と同じため省略-
    ==ここまで==

    • たなかまさゆき より:

      M5Unifiedを使う場合にはM5.Lcdがdisplayになります。
      またM5.begin(cfg);で初期化されているのでM5.Lcd.begin()は呼び出す必要がありません。

      #include
      M5Canvas sprite(&M5.Lcd);

      void setup() {
      auto cfg = M5.config();
      M5.begin(cfg);

      M5.Lcd.println(“Test”);

      sprite.createSprite(32, 32);

      みたいな感じになると思います。
      ちなみにM5.LcdでもM5.Displayでも中身は同じなので好きな方を使ってください。

  2. Aoya-Uta より:

    回答ありがとうございます、動作しました!
    M5.Lcdを使うことで、LGFXを使っていた時よりシンプルになりました。

  3. Aoya-Uta より:

    教えていただいた「M5Unified」「M5Canvas 」を使ってAtomS3サーマルカメラを作成しましたので、シェアいたします。ありがとうございました!
    ・【IoT】”AtomS3”でM5StickC Thermal Camera Hat(MLX90640)を動かす
    https://zenn.dev/aoya_uta/articles/1682c3677eafca