0
0

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 で“ロボットの目と自然な瞬き”を作る 【最小コード & 30 分で動く】

Last updated at Posted at 2025-05-05

M5Stack で“自然な瞬き”を作る 【最小コード & 30 分で動く】

M5Stack Basic v2.7Arduino IDE だけで、
目パーツがランダムに瞬きしつつチラ見もするアニメーションを実装します。
この記事だけで 30 分以内 に動作確認できる“最小構成”を書きました。

完成デモ(30 秒)👇


TL;DR(先に要点)

  • Ticker ライブラリを使って 非同期タイマー → ランダム間隔で自然な瞬き
  • Sprite でオフスクリーン描画 → 画面チラつき激減 / FPS 14→30 に改善
  • コードは 130 行、外付けパーツは不要(M5Stack Core 内蔵 TFT のみ)

必要なもの(ハード)

パーツ 型番 / バージョン 備考
M5Stack Basic V2.7 Core 本体だけで OK
USB Type‑C ケーブル 書き込み & 電源
(任意) 220 Ω + LED デバッグ用(動作確認に便利)

開発環境

  • Arduino IDE 2.3.6
  • Board Manager: esp32 by Espressif Systems 2.0.6
  • ライブラリ: M5Unified, Ticker

コード全文(コピペで動く)

#include <M5Unified.h>
#include <Ticker.h>

// LovyanGFX Sprite クラス
lgfx::LGFX_Sprite sprite(&M5.Display);
Ticker blinkTicker;

constexpr uint16_t EYE_W = 60;
constexpr uint16_t EYE_H = 60;
constexpr uint16_t LEFT_X  = 90;
constexpr uint16_t RIGHT_X = LEFT_X + EYE_W + 20;
constexpr uint16_t Y0 = 100;
bool eyesClosed = false;

void drawEyes(bool closed) {
  sprite.fillSprite(TFT_BLACK);
  if (closed) {
    sprite.fillRect(0, 0, EYE_W, EYE_H / 3, TFT_WHITE);
    sprite.fillRect(EYE_W + 20, 0, EYE_W, EYE_H / 3, TFT_WHITE);
  } else {
    sprite.fillCircle(EYE_W / 2, EYE_H / 2, 25, TFT_WHITE);
    sprite.fillCircle(EYE_W / 2 + EYE_W + 20, EYE_H / 2, 25, TFT_WHITE);
    sprite.fillCircle(EYE_W / 2, EYE_H / 2, 10, TFT_BLACK);
    sprite.fillCircle(EYE_W / 2 + EYE_W + 20, EYE_H / 2, 10, TFT_BLACK);
  }
  sprite.pushSprite(LEFT_X, Y0);
}

// 次の瞬きをスケジュール
void scheduleBlink() {
  uint32_t interval = eyesClosed ? random(80, 120) : random(800, 2500);
  blinkTicker.attach_ms(interval, []() {
    eyesClosed = !eyesClosed;
    drawEyes(eyesClosed);
    scheduleBlink();
  });
}

void setup() {
  auto cfg = M5.config();
  M5.begin(cfg);
  sprite.setColorDepth(8);
  sprite.createSprite(EYE_W * 2 + 20, EYE_H);
  drawEyes(false);

  randomSeed(micros());
  scheduleBlink();  // 初回の瞬きを予約
}

void loop() {
  M5.update(); // 他の処理を入れても瞬きは止まらない
}

コードのポイント解説

  1. 非同期タイマーTicker を二段構えで呼び出し、random() で毎回インターバルを再設定。これだけで“不規則な瞬き”が実現できます。
  2. Sprite 描画fillSprite()pushSprite() でダブルバッファ。全画面 fillScreen() は NG(チラつき大)。
  3. randomSeedmicros() でシードを与えて起動毎にパターンを変化。

ハマりポイント & 解決策

症状 原因 ワンポイント解決
画面が真っ白 M5.begin() 前に TFT を触っている M5Unified を使えば内部で初期化済み
瞬きがカクつく delay() を使った同期処理 TickerTaskManager で非同期化
FPS が上がらない setColorDepth(16) で転送量大 8bit ColorDepth で十分キレイ

次にやると面白い遊びアイデア

  • ランダム表情 5 種:怒り・困り目などをビットマップで差し替え
  • チャリーン音と同期:SDカードに wavファイル を置き、瞬きに合わせて再生
  • 口パーツ連動:シリアル or I2C で別モジュールと同期させ、喋るロボ顔に

🔽 ここまでは無料で完結。さらに“目が動く&瞬き + 背景色変更 ”をセットにした 完全版コード は note にまとめています👇


💡 深掘りしたい人向け(完全版リンク)

  • 完全版コード(目が動く&瞬き / 背景色変更)

👉 https://note.com/yokoyan_pws/n/n12bc925fdef0

記事価格 1,000 円 なのですが、下記クーポンにより記事を半額で購入可能です!

🎁 note×X(旧Twitter) 連動半額クーポン(500円OFF)
記事ページで [拡散を応援して購入] を押すと
通常 1,000 円 → 500 円 で購入できます


ライセンス & 免責

  • 使用・改変は自由ですが、損害等は自己責任でお願いします。

ご質問・改善案があればコメント欄へどうぞ。
楽しんでいただけたら LGTM👍 やシェアをお願いします!

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?