1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

M5Stack Stopwatch開発キット用にシンプルなアプリを作ってみた際のちょっとしたメモ: VS Code + PlatformIO + M5Unified

1
Last updated at Posted at 2026-08-16

はじめに

以下の「M5Stack Stopwatch開発キット」用に、シンプルなアプリを作ってみた際のちょっとしたメモです。

購入元について

今回、M5Stack Stopwatch開発キットを自分が購入した先は、M5Stack公式のストアでした。

当初は、以下のスイッチサイエンスさんで買おうとしていましたが、初回販売・追加販売時のどちらも、気がついた時には売り切れ状態だったため(どちらのタイミングでも、販売開始から割と短時間で売り切れになっていたようです)、その後に M5Stack公式のストアでの在庫が復活したタイミグで購入しました。

●M5Stack Stopwatch開発キット(ESP32-S3R8) — スイッチサイエンス
 https://www.switch-science.com/products/11200

開発時のメモ

以下、開発時のメモです。

試作したもの

先に、試作したものを動作させた時の様子を掲載します。

アナログ・デジタル表示のストップウォッチ的な内容で、動作としては、ボタンA でスタート・ストップ、ボタンB でリセットとなります。

試した環境

今回のお試し用の環境は、過去にも試して複数の記事を書いている「VS Code + PlatformIO + M5Unified の組み合わせ」です。

iniファイル・cppファイルの内容

今回、細かな手順などは省略します。

以下では、iniファイル・cppファイルの内容を掲載します。

platformio.ini の内容

今回用いた platformio.ini の内容は、以下の公式ページに掲載されているものをそのまま用いました。

●StopWatch
 https://docs.m5stack.com/ja/core/StopWatch

2026-08-16_17-29-21.jpg

以下に、コピペ可能なものを掲載します。

[env:m5stack-stopwatch]
platform = espressif32 @ 6.12.0
board = esp32s3box
framework = arduino
board_build.partitions = default_16MB.csv
board_upload.flash_size = 16MB
board_upload.maximum_size = 16777216
board_build.arduino.memory_type = qio_opi
monitor_speed = 115200
build_flags =
    -DESP32S3
    -DBOARD_HAS_PSRAM
    -DCORE_DEBUG_LEVEL=5
    -DARDUINO_USB_CDC_ON_BOOT=1
    -DARDUINO_USB_MODE=1
lib_deps =
    M5Unified = https://github.com/m5stack/M5Unified
    M5GFX = https://github.com/m5stack/M5GFX
    M5PM1 = https://github.com/m5stack/M5PM1
    M5IOE1 = https://github.com/m5stack/M5IOE1 

main.cpp の内容

main.cpp として実装した内容は、以下のとおりです。

#include <M5Unified.h>
#include <math.h>

static M5Canvas canvas(&M5.Display);

static bool     running  = false;
static uint32_t accumMs  = 0;  // 停止までの累積
static uint32_t startMs  = 0;  // 直近スタート時刻
static uint32_t lastDraw = 0;

static uint32_t elapsedMs()
{
  return running ? accumMs + (millis() - startMs) : accumMs;
}

static void drawHand(int cx, int cy, float a, float len, float w, uint16_t color)
{
  float tx = sinf(a), ty = -cosf(a);
  float px = -ty * w, py = tx * w;
  canvas.fillTriangle(cx + px, cy + py,
                      cx - px, cy - py,
                      cx + tx * len, cy + ty * len, color);
}

static void draw()
{
  const int W  = canvas.width();
  const int H  = canvas.height();
  const int cx = W / 2, cy = H / 2;
  const int R  = (W < H ? W : H) / 2 - 6;

  canvas.fillSprite(TFT_BLACK);

  for (int i = 0; i < 60; ++i) {
    float a   = i * 6 * DEG_TO_RAD;
    int   len = (i % 5 == 0) ? 20 : 8;
    canvas.drawLine(cx + sinf(a) * (R - len), cy - cosf(a) * (R - len),
                    cx + sinf(a) * R,         cy - cosf(a) * R,
                    (i % 5 == 0) ? TFT_WHITE : canvas.color565(90, 90, 90));
  }

  const uint32_t ms = elapsedMs();
  const uint16_t handColor = running ? canvas.color565(0, 230, 140)
                                     : canvas.color565(255, 170, 0);

  float minA = (ms % 3600000UL) / 3600000.0f * TWO_PI;
  float secA = (ms % 60000UL)   / 60000.0f   * TWO_PI;
  drawHand(cx, cy, minA, R * 0.55f, 5, TFT_WHITE);
  drawHand(cx, cy, secA, R * 0.85f, 3, handColor);
  canvas.fillCircle(cx, cy, 8, handColor);

  // --- デジタル表示 ---
  char buf[16];
  snprintf(buf, sizeof(buf), "%02lu:%02lu.%02lu",
           (unsigned long)(ms / 60000 % 100),
           (unsigned long)(ms / 1000 % 60),
           (unsigned long)(ms / 10 % 100));
  canvas.setTextDatum(middle_center);
  canvas.setFont(&fonts::Font7);
  canvas.setTextSize(1);
  canvas.setTextColor(canvas.color565(180, 180, 180));
  canvas.drawString(buf, cx, cy + R / 2);

  canvas.pushSprite(0, 0);
}

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

  M5.Display.setRotation(0);
  M5.Display.setBrightness(200);

  canvas.setPsram(true);
  canvas.setColorDepth(16);
  canvas.createSprite(M5.Display.width(), M5.Display.height());

  draw();
}

void loop()
{
  M5.update();

  bool tap = M5.Touch.getDetail().wasClicked();
  if (M5.BtnA.wasClicked() || tap) {
    if (running) { accumMs += millis() - startMs; running = false; }
    else         { startMs  = millis();           running = true;  }
  }
  if (M5.BtnB.wasClicked()) {
    running = false;
    accumMs = 0;
  }

  if (millis() - lastDraw >= 33) {
    lastDraw = millis();
    draw();
  }
}

ほとんどが画面表示と計算部分になるようなコードです。

その他

ダウンロードモード用の操作について

以下は、ダウンロードモードに関する公式ページの説明のメモです。

2026-08-16_17-39-41.jpg

  • デバイスをケーブルで PC に接続して、電源ボタンを約2秒間長押しすると、緑色のLEDが点灯 ⇒ その後ボタンを離すと、デバイスはダウンロードモードになり、書き込み待機状態になる

デバイスの電源ON/OFF の操作について

以下は、電源ON/OFF に関する公式ページの説明のメモです。

2026-08-16_17-38-12.jpg

  • 電源オン / リセット: 電源ボタンを短く 1回押す
  • 電源オフ: 電源ボタンを 2回連続で押す
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?