はじめに
以下の「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
以下に、コピペ可能なものを掲載します。
[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();
}
}
ほとんどが画面表示と計算部分になるようなコードです。
その他
ダウンロードモード用の操作について
以下は、ダウンロードモードに関する公式ページの説明のメモです。
- デバイスをケーブルで PC に接続して、電源ボタンを約2秒間長押しすると、緑色のLEDが点灯 ⇒ その後ボタンを離すと、デバイスはダウンロードモードになり、書き込み待機状態になる
デバイスの電源ON/OFF の操作について
以下は、電源ON/OFF に関する公式ページの説明のメモです。
- 電源オン / リセット: 電源ボタンを短く 1回押す
- 電源オフ: 電源ボタンを 2回連続で押す


