[M5Stack で“ロボットの目と自然な瞬き”を作る 【最小コード & 30 分で動く】

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

> **M5Stack Basic v2.7** と **Arduino IDE** だけで、
目パーツがランダムに瞬きしつつチラ見もするアニメーションを実装します。
この記事だけで **30 分以内** に動作確認できる“最小構成”を書きました。
>
> 完成デモ（30 秒）👇
>
> <iframe width="320" height="320" src="https://www.youtube.com/embed/20d5RszOMFQ" loading="lazy" allowfullscreen></iframe>

---

## TL;DR（先に要点）

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

---

## 必要なもの（ハード）

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

https://ssci.to/9010

開発環境

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

---

## コード全文（コピペで動く）

```cpp
#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. **randomSeed** — `micros()` でシードを与えて起動毎にパターンを変化。

---

## ハマりポイント & 解決策

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

---

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

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

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

---

## 💡 深掘りしたい人向け（完全版リンク）

* 完全版コード（目が動く&瞬き / 背景色変更）

👉 [https://note.com/yokoyan\_pws/n/n12bc925fdef0](https://note.com/yokoyan_pws/n/n12bc925fdef0)

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

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


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

---

### ライセンス & 免責

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

---


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