ESP32-S3 で WS2812 リングをドライブしてレインボーフロー回転エフェクトを実装する完全チュートリアル
難易度:⭐⭐☆☆☆(初心者でもOK) 所要時間:30 分 テスト環境:Arduino IDE 2.3.8 + FastLED v3.10.3 + ESP32 Arduino Core 3.3.8
TL;DR(クイックスタート):
- 配線:WS2812 リング
DIN→ ESP32-S3GPIO40、VCC→ 5V、GND→ GND- ライブラリのインストール:Arduino ライブラリマネージャで
FastLED(作者 Daniel Garcia)を検索し、最新版をインストール- コード内の
NUM_LEDS(LED数)とLED_PIN(ピン番号)をお使いの環境に合わせて変更- 書き込んで、電源を入れると、リングが回り始めます
はじめに
家にずっと眠っていた WS2812 リングがあり、「そのうち時間ができたら遊ぼう」と思っていました。しかし、ほこりをかぶっているのを見て、ついでに簡単なサンプルを作ることにしました。
WS2812 シリーズの LED テープ/リング/マトリックスの最大の魅力は、データ線が1本だけで済むことです。電源線を含めても合計3本の配線で駆動でき、各 LED の色を個別に制御できます。これは内蔵ドライバチップのおかげで、デコーダもシフトレジスタも不要、コードも数十行で済みます。
本記事の目標:ESP32-S3 + FastLED ライブラリを使って、レインボーカラーがリングに沿って回転するフローエフェクトを実装します。全程ノンブロッキングで、今後の機能拡張にも影響しません。
実験結果

リング上の40個の LED が同時に点灯し、色がレインボーグラデーションで分布し、全体の色調が回転し続け、まるで一筋のカラフルな光が流れているように見えます。
コンポーネント説明
WS2812 LED リング
WS2812 リングは伝言ゲームのような仕組みです。すべてのデータを最初の LED に送信すると、自分の色情報を取り出し、残りを次の LED に渡します。次の LED も自分の色を取り出して残りを次へ渡す、という具合にデイジーチェーン(daisy-chain)方式でつながっていきます。たった1本のデータ線で、数十個(さらには数百個)の LED を個別に制御できます。
例:データコマンド列: [赤,青,緑,黄]
↓
LED ¹ 「赤」を取り出して赤く点灯 → [青,緑,黄] を次へ渡す
↓
LED ² 「青」を取り出して青く点灯 → [緑,黄] を次へ渡す
↓
LED ³ 「緑」を取り出して緑に点灯 → [黄] を次へ渡す
...
...
...
| パラメータ | 数値 |
|---|---|
| 駆動電圧 | 5V |
| LED 1個あたりの最大電流 | 60mA(R/G/B 各20mA 全点灯時) |
| データ信号レベル | 3.3V ロジック互換(レベル変換不要) |
| 通信プロトコル | 単線 NZR(帰零コード) |
| カラー順序 | GRB |
| リフレッシュレート | 400Hz / 800Hz(モデルによる) |
選んだ理由:配線が極めてシンプル(データ線1本)、FastLED がネイティブ対応、コミュニティの資料が豊富で、初心者がハマりにくい。
WS2812B の1本のデータ線(単一 GPIO)で、理論上および実際に何個の LED を駆動できるか?
理論上の上限
厳密な制限はほぼありません(数千〜数万個を駆動可能)。WS2812B は**デイジーチェーン(daisy-chain)**方式を採用しており、後ろの LED の DO ポートが次の LED の DI ポートに接続され、データが1つずつ下流に伝わります。マイクロコントローラがタイムリーに完全なデータフレームを送信できれば、理論上は無限にカスケード接続可能です。
実際の推奨数(1本のデータ線あたり)
| 使用シーン | 推奨最大数 | 説明 |
|---|---|---|
| スムーズなアニメーション/ゲーム(高リフレッシュレート) | 300〜600 個 | 推奨範囲、リフレッシュレートは 30〜60fps 以上を維持 |
| 一般的なエフェクト/雰囲気照明 | 800〜1200 個 | よく使われる上限、リフレッシュレートは約 15〜30fps |
| 限界ケース | 2000〜4000+ 個 | 可能だが、リフレッシュレートが低く(<10fps)、信号の問題が起きやすい |
| プロフェッショナル/大型プロジェクト | 数千〜数万個 | 複数のデータ線で並列駆動が必須(ESP32 に最適) |
主な制限要因
- リフレッシュレート(最も重要) 1個あたり約 30μs(24bit)のデータが必要。
- 1000 個 ≈ 30ms → 約 33fps
- 2000 個 ≈ 60ms → 約 16fps(明らかにカクつく)
- 信号品質
- データ線が長すぎる(>10〜15m)場合や LED 数が多すぎる場合、後方の LED でノイズ、色ずれ、チラつきが発生しやすくなります。
- 500〜1000個程度ごとに信号アンプ(74HCT245 / SN74AHCT125 など)やリピーターモジュールを追加することを推奨します。
- 電源(データ線の制限ではありませんが、必ず解決が必要)
- 各 LED が白色全点灯時の最大消費電流は約 60mA(通常は平均 20〜30mA)。
- 複数ポイントからの給電が必須(1〜2mごとに給電ポイントを設ける)。そうしないと電圧降下により後端が暗くなったり変色したりします。
BOM(部品表)
| 部品 | スペック | 数量 |
|---|---|---|
| ESP32-S3 開発ボード | GPIO 搭載の任意のバージョン | ×1 |
| WS2812 LED リング | 40 LED(その他の数量でも可、コードで1行変更) | ×1 |
| ジャンパーワイヤー | オス-メス / オス-オス、実際の必要に応じて | 適量 |
ピン配置説明
WS2812 リングには通常、以下の4つのピンがあります。
| ピンラベル | 説明 |
|---|---|
| VCC / 5V | 電源正極、5V に接続 |
| GND | 電源負極、GND に接続 |
| DIN / Data In | データ入力、ESP32-S3 の GPIO に接続 |
| DOUT / Data Out | データ出力、複数のリングをカスケード接続する際に使用。本プロジェクトでは未使用 |
⚠️ 一部のリングには
+、-、Dataとだけ記載されている場合がありますが、対応関係は同じなので慌てなくて大丈夫です。
配線方法
| WS2812 リングピン | ESP32-S3 |
|---|---|
| VCC / 5V | 5V(ボードの 5V ピンまたは外部 5V 電源) |
| GND | GND |
| DIN | GPIO40 |
💡 配線が終わったら、必ず一つずつ確認することをお勧めします。 トラブルシューティングの時間を80%削減できます。特に VCC を 3.3V に接続しないように注意してください。LED は点灯しますが、色がずれたり明るさが低下したりして、無駄にデバッグ時間を消費することになります。
必要なライブラリ
Arduino IDE のライブラリマネージャで FastLED を検索してください。作者は Daniel Garcia です。最新版をインストールします(本記事のテストバージョン:v3.10.3)。
インストール方法:ツール → ライブラリを管理 → FastLED を検索 → インストール
完全コード
/*
* ESP32-S3 WS2812 レインボーフロー回転リング
* FastLED ノンブロッキング版 —— loop() をブロックしない、ボタンやセンサーの追加が容易
*/
#include <FastLED.h>
// ===== ここをお使いの環境に合わせて変更してください =====
#define LED_PIN 40 // データ線を接続する GPIO ピン
#define NUM_LEDS 40 // リング上の LED 数
#define BRIGHTNESS 204 // グローバル輝度、範囲 0(全消灯)〜 255(全点灯)
// ====================================
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB // WS2812 のカラー順序は GRB、RGB ではないので注意
CRGB leds[NUM_LEDS]; // 各 LED のカラー配列
uint8_t gHue = 0; // レインボー開始色相、毎フレーム増加させて「回転」を実現
void setup() {
// ステップ1:ハードウェアに1秒の起動時間を与え、電源投入時の突入電流による LED のチラつきを防止
delay(1000);
// ステップ2:FastLED を初期化、使用するピン、LED タイプ、数量を指定
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS)
.setCorrection(TypicalLEDStrip); // 色温度を自動補正、白色をより白く見せる
// ステップ3:グローバル輝度を設定(RGB 値を調整するより簡単)
FastLED.setBrightness(BRIGHTNESS);
}
void loop() {
// ステップ4:レインボーグラデーションでリング全体を埋める
// gHue は開始色相、255/NUM_LEDS は各 LED 間の色相間隔
fill_rainbow(leds, NUM_LEDS, gHue, 255 / NUM_LEDS);
// ステップ5:カラーデータをリングに送信
FastLED.show();
// ステップ6:10ms ごとに色相を +1、値を小さくするほど回転が速く、大きくするほど遅くなる
EVERY_N_MILLISECONDS(10) {
gHue++;
}
}
コード解説
| 重要な行 | 処理内容 |
|---|---|
fill_rainbow(...) | FastLED の組み込み関数。レインボーグラデーションカラーを自動計算して配列に格納。HSV 計算を手書きする必要なし |
FastLED.show() | leds[] 配列のカラーデータを GPIO40 経由で送信。この行を呼ぶまで LED は変化しない |
EVERY_N_MILLISECONDS(10) | FastLED の組み込みノンブロッキングタイマー。「10ms ごとに1回実行」と同等。loop() をブロックしない |
gHue++ | 色相を +1 するごとに、次のフレームの fill_rainbow の開始色がずれ、回転しているように見える |
setCorrection(TypicalLEDStrip) | LED の色温度を自動補正。混色後の白色が緑寄りにならないようにする。WS2812 向け |
回転速度を変えたい場合:
EVERY_N_MILLISECONDS(10)の数値を調整。10 → 5 で2倍速く、10 → 20 で半分の速さになる。
よくある問題のトラブルシューティング
慌てないでください。90% の問題は以下の箇所にあります。
問題1:電源を入れても LED が全く点灯しない
- DIN が
GPIO40に接続されているか確認(コード内のLED_PINで定義したピン) - VCC が 5V に接続されており、3.3V ではないことを確認
- GND が接続されているか確認。GND が共通でないとデータ信号が送信されません
問題2:一部の LED しか点灯しない、または色が乱れてチカチカする
- ほぼ間違いなく電力不足です。40個の LED が白色全点灯時には 2.4A の電流を消費し、USB ポートの 500mA では不足します。外部の 5V 2A 以上の電源の使用を推奨します
問題3:色がおかしい、赤を指定しているのに緑色になる
COLOR_ORDERの定義が間違っています。WS2812B は GRB 順序です。コード内のGRBをRGBに変更して試すか、その逆を試してください
問題4:コンパイルエラー FastLED.h: No such file が出る
- ライブラリがインストールされていません。ライブラリマネージャを再度開き、FastLED のステータスが「インストール済み」であることを確認し、Arduino IDE を再起動してください
問題5:書き込みは成功するが LED が動かない
NUM_LEDSがお使いのリングの実際の LED 数と一致しているか確認してください。数が合わないと表示が異常になります
FAQ
Q:WS2812 と WS2812B の違いは何ですか?コードは共通で使えますか?
A:WS2812B は WS2812 のアップグレード版で、パッケージが小型化され、タイミングが微調整されていますが、FastLED は両方に対応しています。LED_TYPE に WS2812B を指定すれば、他のコードの変更は不要です。
Q:リングの LED 数が 12/16/24 個しかありません。コードはどう変更すればいいですか?
A:1行だけ変更してください:#define NUM_LEDS 24 をお使いの実際の LED 数に変更するだけで、他はそのままで大丈夫です。
Q:GPIO40 を別のピンに変更できますか?
A:はい、ESP32-S3 のほとんどの GPIO が使用可能です(0、3、45、46 など起動関連ピンは避けてください)。#define LED_PIN 40 の数字を変更し、配線もそのピンに変更してください。
Q:複数のリングを同時に駆動できますか?
A:はい。各リングを独立した GPIO に接続し、コード内で addLeds をもう一度呼び出して、別の leds[] 配列セグメントを割り当てることで可能です。
Q:リングに独立した電源は必要ですか? A:LED 数が8個以下で輝度を上げきっていなければ、開発ボードの 5V ピンで駆動可能です。8個を超える場合や白色全点灯が必要な場合は、5V 2A 以上の外部電源の使用を強く推奨します。外部電源の GND と開発ボードの GND を共通にしてください。
Q:EVERY_N_MILLISECONDS とは何ですか?なぜ delay() をそのまま使わないのですか?
A:EVERY_N_MILLISECONDS は FastLED の組み込みノンブロッキングタイマーで、loop() は通常通り実行され、指定した間隔で中のコードが実行されます。delay() を使うとプログラム全体がそこで停止し、ボタンやシリアル通信などを同時に処理できなくなります。
Q:レインボーの回転方向を逆にできますか?
A:はい、gHue++ を gHue-- に変更するだけで逆回転になります。
応用アイデア
- ボタンを追加してエフェクト切り替え(呼吸灯 / マーキー / レインボーフローを自在に切り替え)
- マイクモジュールを接続して、オーディオレスポンシブ LED スペクトラムエフェクトを作成
- 複数のリングをカスケード接続、最初のリングの DOUT を次の DIN に接続して、より長い LED テープエフェクトを実現
- OLED ディスプレイを接続して、現在のエフェクト名と輝度値を表示