Arduinoでナイトライダー風LED:回路、コード、速度調整
Arduinoでナイトライダー風LEDを作ります。LED 8個と220 Ωの配線、配列とforループのコード、ポテンショメーターでの速度調整、millis、74HC595、WS2812B、ESP32、Picoまで。
Arduinoのナイトライダー回路は、一列に並んだLEDが順番に点灯し、端から端へ流れてはまた戻ってくる定番のLEDエフェクトです。名前は、1980年代の有名なテレビドラマに登場するしゃべる車KITTの、フロントについているスキャナーライトに由来します。必要なのはArduino Uno、LED 6個か8個、LED 1個につき220 Ωの抵抗1本、ジャンパーワイヤー数本だけです。光が一方向にだけ流れて、また最初から始まるものは流れるLED(ランニングライト)やLEDチェイサーと呼ばれます。どちらも初心者が信号機の次に作る定番のプロジェクトで、プログラミングで特に大切な2つの道具、配列とforループを学ぶいちばん楽しい方法です。
このガイドでは、まず回路を組んで流れるLEDのコードを書き、次に往復するナイトライダーのコードを書きます。そのあと、ポテンショメーターで速度を調整し、LEDが片側に積み上がっていく効果を加え、同じ効果をmillis()でコードを一切止めずに動かします。後半では、74HC595チップを使ってわずか3本のピンでLED 8個を動かす方法、WS2812BアドレサブルLEDで1本のピンから尾を引くナイトライダーを作る方法、ESP32版とRaspberry Pi Pico(MicroPython)版、そしてよくある間違いの直し方を紹介します。

ナイトライダー効果とは?流れるLEDとの違いは?
ドラマでは、KITTのフロントバンパーの赤い光が、何かを探る目のように左右に休まず往復します。電子工作ではこの効果を、番組のプロデューサーGlen A. Larsonにちなんでラーソンスキャナーとも呼びます。同じプロデューサーの別のシリーズ『宇宙空母ギャラクティカ』に出てくるロボット、サイロンの目も同じしくみです。本物のスキャナーでは、光っている点の後ろにゆっくり消えていく赤い残像、つまり尾が見えます。シンプルなLED回路では、各LEDは完全に点灯しているか完全に消えているかのどちらかなので、尾をまねるにはPWMでLEDを少しずつ暗くする必要があります。この記事の後半では、WS2812BとESP32でまさにそれを行います。下の表は、この記事でコードを書く効果と、LED 8個で1周が何ステップになるかをまとめたものです。
| 効果 | 動き | LED 8個での1周 | この記事の該当箇所 |
|---|---|---|---|
| 流れるLED | 1つの光が左から右へ流れ、また最初から始まる | 8ステップ | ステップ1 |
| ナイトライダー | 1つの光が両端の間を往復する | 14ステップ | ステップ2と3 |
| 積み上げ | 光が1つずつ右側のいちばん奥の空いた位置まで走り、そこに留まる | 36ステップ | ステップ4 |
| 中央で出会う | 2つの光が両端から中央に寄り、また離れていく | 8ステップ | 74HC595の節 |
| 尾を引くナイトライダー | 先頭のLEDは最大の明るさで、後ろのLEDは徐々に消えていく | 14ステップ | WS2812BとESP32の節 |
部品リストと抵抗の選び方
| 部品 | 数 | メモ |
|---|---|---|
| Arduino Uno(またはNano) | 1 | ESP32やRaspberry Pi Picoでも可 |
| 5 mm LED | 6〜8 | 明るさをそろえるため同じ色・同じロットで |
| 220 Ωの抵抗 | 6〜8 | LED 1個につき1本。Picoでは330 Ω |
| 10 kΩのポテンショメーター(B10K) | 1 | 速度調整版でのみ使用 |
| ブレッドボード | 1 | ハーフサイズ(400穴)で十分 |
| ジャンパーワイヤー | 12〜15 | オス-オス |
| 74HC595、またはLED 8個のWS2812Bスティック | 1 | 任意:ピン節約と尾を引く効果の節で使用 |
LEDは自分で電流を制限しないので、どのLEDにも抵抗を直列に入れます。値はオームの法則で求めます。R=(電源電圧-LEDの順方向電圧)÷ 電流です。赤色LEDの順方向電圧は約2 Vなので、5 Vで220 Ωなら電流は(5 − 2) / 220 ≈ 13.6 mAです。これでLEDは十分明るく光り、Arduinoのピンに推奨される20 mAも下回ります。積み上げ効果で8個のLEDが全部同時に点くと合計電流は約109 mAですが、これもUnoの全ピン合計の上限200 mAに収まります。220 Ωの抵抗をカラーバンドで見分けるには抵抗のカラーコードツールを、ほかの色や電圧の場合はLED抵抗計算ツールを使ってみてください。
配線:LED 8個とポテンショメーター
各LEDの長い脚(アノード)は抵抗を通してボードのデジタルピンへ、短い脚(カソード)はGNDへつなぎます。ブレッドボードでいちばんすっきりするのは、ArduinoのGNDピンをブレッドボードの青い(−)ラインにつなぎ、8個のLEDの短い脚をすべてそのラインに集める方法です。LEDはピンの順に左から右へ等間隔で並べてください。そうすると、コードの配列の順番と光の実際の並びが一致します。ポテンショメーターの外側の2本の脚は5VとGNDへ、中央の脚(ワイパー)はA0へつなぎます。下の表は、この記事のすべてのスケッチで使うピンです。LEDを6個にする場合は、最後の2つのLEDの行を飛ばし、コードの配列から最後の2本のピンを削除してください。ボードのピン配置はピン配置ツールで確認できます。
| 役割 | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| LED 1(左端) | D2 | GPIO 18 | GP8 |
| LED 2 | D3 | GPIO 19 | GP9 |
| LED 3 | D4 | GPIO 21 | GP10 |
| LED 4 | D5 | GPIO 22 | GP11 |
| LED 5 | D6 | GPIO 23 | GP12 |
| LED 6 | D7 | GPIO 25 | GP13 |
| LED 7 | D8 | GPIO 26 | GP14 |
| LED 8(右端) | D9 | GPIO 27 | GP15 |
| ポテンショメーターの中央の脚 | A0 | GPIO 34 | GP26(ADC0) |
| ポテンショメーターの外側の脚 | 5VとGND | 3V3とGND | 3V3(OUT)とAGND |
| LEDのカソード(短い脚) | GND | GND | GND |
| 直列抵抗 | 220 Ω | 220 Ω | 330 Ω |




ステップ1:配列とforループで流れるLEDのコード
LED 8個に対して、pinModeを8行、digitalWriteを16行それぞれ書くこともできますが、コードが長くなり、LEDの数を変えるのも面倒になります。そこで、ピン番号を配列に入れ、forループで順にたどります。配列の最初の要素はLEDS[0]でD2、最後はLEDS[7]でD9です。こうすれば、LEDが6個でも8個でも12個でも、同じコードがそのまま動きます。書き換えるのは配列だけです。配列とループの基本はArduinoプログラミングの基本で詳しく説明しています。
1// Yürüyen LED: 8 LED sırayla yanar / Running light: 8 LEDs light up one after another
2const int LEDS[] = {2, 3, 4, 5, 6, 7, 8, 9}; // LED pinleri / LED pins
3const int LED_SAYISI = sizeof(LEDS) / sizeof(LEDS[0]); // dizideki LED sayısı (8) / number of LEDs (8)
4const int BEKLE = 100; // adım süresi (ms) / step time (ms)
5
6void setup() {
7 for (int i = 0; i < LED_SAYISI; i++) {
8 pinMode(LEDS[i], OUTPUT); // tüm LED pinleri çıkış / all LED pins as outputs
9 }
10}
11
12void loop() {
13 for (int i = 0; i < LED_SAYISI; i++) {
14 digitalWrite(LEDS[i], HIGH); // sıradaki LED'i yak / light the next LED
15 delay(BEKLE);
16 digitalWrite(LEDS[i], LOW); // söndür ve devam et / turn it off and move on
17 }
18}| 行 | 働き |
|---|---|
| const int LEDS[] = {2, ..., 9};(配列の宣言) | LEDのピンを左から右の順に入れた配列。LEDS[0] = 2、LEDS[7] = 9 |
| sizeof(LEDS) / sizeof(LEDS[0])(要素数) | 要素の数を求めます。Unoでは16バイト÷2バイト=8。ピンを足したり減らしたりすると自動で更新されます |
| for (int i = 0; i < LED_SAYISI; i++)(ループ) | 変数iを0から7まで数えます。1周ごとに配列の要素を1つ処理します |
| pinMode(LEDS[i], OUTPUT)(出力に設定) | 1つのループで8本のピンをすべて出力にします |
| digitalWrite, delay(BEKLE), digitalWrite(点灯・待機・消灯) | LEDを点灯し、BEKLEミリ秒待ってから消灯し、次のLEDに進みます |
BEKLE(1ステップの時間)が100 msなら、光は0.8秒で8個のLEDを通り過ぎ、すぐにまた始まります。30 msまで下げると流れはとても速くなり、300 msまで上げると、ランプが1つずつ点いていくようなゆっくりした動きに見えます。コードを書き込むには、「ツール」メニューでボードとポートを選び、「書き込み」を押します。書き込みに失敗する場合は、Arduinoにコードが書き込めないときのチェックリストを順に確認してください。
ステップ2:ナイトライダー(往復)のコード
ナイトライダー効果では、光が右端に着いたら折り返す必要があります。これには2つのループを使います。1つ目は左から右へ、2つ目は右から左へ数えます。ここで気をつけたいのが両端のLEDです。両方のループが0から7、7から0まで全部数えると、右端のLEDは往路のループの最後に1回、復路のループの最初にもう1回点きます。光が両端で2倍長く留まり、動きがつっかえたように見えてしまいます。そのため、往路のループは6で、復路のループは1で止めます。その結果、1周がちょうど14ステップになり、光は両端でも中央と同じ時間だけ留まります。また、LEDを点けて消す3行をyakSondur()(トルコ語で「点けて消す」)という小さな関数にまとめました。コードが短くなり、読みやすくもなります。
1// Karaşimşek (Knight Rider): ışık iki uç arasında gidip gelir / light bounces between the two ends
2const int LEDS[] = {2, 3, 4, 5, 6, 7, 8, 9};
3const int LED_SAYISI = sizeof(LEDS) / sizeof(LEDS[0]);
4const int BEKLE = 80; // adım süresi (ms) / step time (ms)
5
6void yakSondur(int i) {
7 digitalWrite(LEDS[i], HIGH);
8 delay(BEKLE);
9 digitalWrite(LEDS[i], LOW);
10}
11
12void setup() {
13 for (int i = 0; i < LED_SAYISI; i++) pinMode(LEDS[i], OUTPUT);
14}
15
16void loop() {
17 for (int i = 0; i < LED_SAYISI - 1; i++) { // soldan sağa: 0..6 / left to right: 0..6
18 yakSondur(i);
19 }
20 for (int i = LED_SAYISI - 1; i > 0; i--) { // sağdan sola: 7..1 / right to left: 7..1
21 yakSondur(i);
22 }
23}ステップ3:ポテンショメーターで速度を調整する
速度を変えたいたびにコードを書き換えて書き込み直す代わりに、ポテンショメーターを使えます。ポテンショメーターは分圧回路で、中央の脚の電圧は軸の位置に応じて0〜5 Vの間で変わり、analogRead()がそれを0〜1023の数値に変えます。map(deger, 0, 1023, 400, 20)はその数値を400〜20 msのステップ時間に変換します。範囲を逆向きに書いているので、読み取った値が大きいほどステップが短くなり、光が速くなります。速くするために軸をどちらに回すかは、外側のどちらの脚を5Vにつないだかで決まります。逆に感じる場合は、外側の2本の線を入れ替えるか、map()の400と20を入れ替えてください。ポテンショメーターの中のしくみはポテンショメーターとは?で読めます。

1// Hız ayarlı karaşimşek: potansiyometre adım süresini belirler / Speed-adjustable Knight Rider: the pot sets the step time
2const int LEDS[] = {2, 3, 4, 5, 6, 7, 8, 9};
3const int LED_SAYISI = sizeof(LEDS) / sizeof(LEDS[0]);
4const int POT = A0; // potansiyometrenin orta bacağı / pot wiper
5
6int adimSuresi() {
7 int deger = analogRead(POT); // 0–1023
8 return map(deger, 0, 1023, 400, 20); // değer büyüdükçe hızlanır / faster as the value rises
9}
10
11void yakSondur(int i) {
12 digitalWrite(LEDS[i], HIGH);
13 delay(adimSuresi()); // her adımda yeniden okunur / read again on every step
14 digitalWrite(LEDS[i], LOW);
15}
16
17void setup() {
18 for (int i = 0; i < LED_SAYISI; i++) pinMode(LEDS[i], OUTPUT);
19}
20
21void loop() {
22 for (int i = 0; i < LED_SAYISI - 1; i++) yakSondur(i); // sağa / to the right
23 for (int i = LED_SAYISI - 1; i > 0; i--) yakSondur(i); // sola / to the left
24}この版ではLEDごとにステップ時間を読み直すので、軸を回した瞬間に速度が変わります。ただし、delay()の実行中はArduinoはほかに何もできません。400 msのゆっくりしたステップの途中で軸を回しても、新しい速度が反映されるのは次のステップからです。LEDが数個のエフェクトなら問題ありませんが、モード切り替えボタンやセンサーの読み取りを加えるなら、ステップ5のmillis()の方法に切り替える必要があります。
ステップ4:積み上げ効果
積み上げ効果では、最初の光が右端まで走り、右端で点いたまま留まります。2つ目の光も走りますが、今度は1つ左の位置で止まります。周回ごとに目標が1つずつ左へ移り、最後にバー全体が埋まります。コードでは、2重のループでこれを実現します。外側のループは積み上がったLEDの数(dolu、トルコ語で「満杯」)を数え、内側のループはその周回で光が何ステップ走るかを数えます。目標(hedef)に着いたLEDは消しません。それを担当しているのがif (i < hedef)の行です。LED 8個の場合、バー全体を埋めるには8 + 7 + 6 + ... + 1 = 36ステップかかります。
1// Birikerek dolan efekt: her ışık en boş uca kadar gider ve orada kalır / Stacking effect: each light runs to the last free spot and stays
2const int LEDS[] = {2, 3, 4, 5, 6, 7, 8, 9};
3const int LED_SAYISI = sizeof(LEDS) / sizeof(LEDS[0]);
4const int BEKLE = 60;
5
6void setup() {
7 for (int i = 0; i < LED_SAYISI; i++) pinMode(LEDS[i], OUTPUT);
8}
9
10void loop() {
11 for (int dolu = 0; dolu < LED_SAYISI; dolu++) { // dolu: sağda birikmiş LED sayısı / LEDs already stacked on the right
12 int hedef = LED_SAYISI - 1 - dolu; // bu turda ışığın duracağı yer / where the light stops this round
13 for (int i = 0; i <= hedef; i++) {
14 digitalWrite(LEDS[i], HIGH);
15 delay(BEKLE);
16 if (i < hedef) digitalWrite(LEDS[i], LOW); // hedefte yanık kalır / stays lit at the target
17 }
18 }
19 delay(600); // dolu çubuğu göster / show the full bar
20 for (int i = LED_SAYISI - 1; i >= 0; i--) { // sağdan sola boşalt / empty from right to left
21 digitalWrite(LEDS[i], LOW);
22 delay(BEKLE);
23 }
24 delay(300);
25}スケッチの最後では、満杯のバーを0.6秒表示したあと、LEDを右から左へ1つずつ消し、効果が最初から始まります。もっとシンプルな伸びていくバーにしたい場合は、内側のループを取り除き、LEDを一度も消さずに順番に点けていくだけです。この見た目は、充電インジケーターや音量バーにぴったりです。この効果ではLED 8個が同時に点灯することを忘れないでください。LEDごとに抵抗があれば、明るさは落ちません。
ステップ5:millis()で止まらないナイトライダー
delay()を使ったコードでは、Arduinoはステップごとに指定した時間だけ固まります。その間に誰かがボタンを押しても気づきません。一方、millis()はボードの電源が入ってからの時間をミリ秒で返します。考え方はこうです。ループは休まず回り、毎回、前のステップから十分な時間がたったかを確認し、たっていれば1ステップだけ進めて、すぐにまた抜けます。下のコードでは、ナイトライダーとボード上のLEDが2つの独立したタスクとして同時に動きます。スキャナーがポテンショメーターで決めた速度で往復する間、D13のLEDは自分のペースで1秒に1回点滅します。このテーマについて詳しくはArduinoのmillis()とdelay()の違いを参照してください。
1// millis() ile bekletmesiz karaşimşek + potansiyometre / Non-blocking Knight Rider with millis() + pot
2const int LEDS[] = {2, 3, 4, 5, 6, 7, 8, 9};
3const int LED_SAYISI = sizeof(LEDS) / sizeof(LEDS[0]);
4const int POT = A0;
5
6int konum = 0; // şu an yanan LED / the LED that is lit now
7int yon = 1; // +1 sağa, -1 sola / +1 right, -1 left
8unsigned long sonAdim = 0; // son adımın zamanı / time of the last step
9unsigned long sonBlink = 0; // dahili LED için / for the onboard LED
10
11void setup() {
12 for (int i = 0; i < LED_SAYISI; i++) pinMode(LEDS[i], OUTPUT);
13 pinMode(LED_BUILTIN, OUTPUT); // Uno'da D13 / D13 on the Uno
14 digitalWrite(LEDS[konum], HIGH);
15}
16
17void loop() {
18 unsigned long simdi = millis();
19 unsigned long aralik = map(analogRead(POT), 0, 1023, 400, 20); // adım süresi (ms) / step time (ms)
20
21 // Görev 1: karaşimşek / Task 1: Knight Rider
22 if (simdi - sonAdim >= aralik) { // taşmaya karşı güvenli / overflow-safe
23 sonAdim = simdi;
24 digitalWrite(LEDS[konum], LOW);
25 konum += yon;
26 if (konum == LED_SAYISI - 1 || konum == 0) yon = -yon; // uçta yön değiştir / reverse at the ends
27 digitalWrite(LEDS[konum], HIGH);
28 }
29
30 // Görev 2: dahili LED kendi temposunda yanıp söner / Task 2: the onboard LED blinks at its own pace
31 if (simdi - sonBlink >= 500) {
32 sonBlink = simdi;
33 digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
34 }
35 // Buraya buton, sensör ya da seri port kodu eklenebilir / button, sensor or serial code can go here
36}ここで大事な点が2つあります。1つ目は、時間の変数をunsigned longにすることです。intを使うと、Unoでは約32.7秒で値がオーバーフローしてタイミングが崩れ、効果が止まるか、制御できないほど速くなります。2つ目は、比較をsimdi - sonAdim >= aralik(現在時刻から前回のステップの時刻を引き、間隔と比べる)という引き算の形で行うことです。この方法なら、約49.7日ごとにmillis()がゼロに戻っても正しく動き続けます。光の位置と向きを2つの変数(konumとyon)で持っているので、ループはもうありません。ステップごとに位置が1つ増えるか減り、両端で向きの符号が反転します。
74HC595で3本のピンからLED 8個を動かす
LED 8個でUnoのピンが8本埋まります。プロジェクトにLCDやセンサー、ボタンを加えたくなると、ピンが足りなくなってきます。74HC595シフトレジスタは、Arduinoのわずか3本のピン(データ、クロック、ラッチ)で8つの出力を動かします。各LEDの状態は1バイトのうちの1ビットです。1 << iという式はiビット目だけを1にするので、LEDが1個だけ点きます。ビット演算でパターンを作るのも簡単です。(1 << i) | (0x80 >> i)なら、両端から中央へ向かう2つの光が同時に作れます。チップを2個つなげば、同じ3本のピンでLED 16個のナイトライダーも作れます。縦続接続、SPIでの高速駆動、7セグメント表示器の使い方は74HC595シフトレジスタの記事でステップごとに説明しています。
| 74HC595のピン | 接続先(Arduino Uno) |
|---|---|
| DS(14番) | D11 |
| SH_CP(11番) | D13 |
| ST_CP(12番) | D10 |
| OE(13番) | GND(出力を常に有効) |
| MR(10番) | 5V(リセット無効) |
| VCC(16番)/GND(8番) | 5V/GND、すぐそばに100 nFのコンデンサ |
| Q0(15番)、Q1〜Q7(1〜7番) | それぞれ470 Ωを通してLEDのアノードへ |
| ポテンショメーターの中央の脚 | A0 |
1// 74HC595 ile 3 pinden 8 LED: karaşimşek ve ortada buluşma / 8 LEDs from 3 pins with a 74HC595: Knight Rider and meet-in-the-middle
2const int DATA_PIN = 11; // DS (14)
3const int CLOCK_PIN = 13; // SH_CP (11)
4const int LATCH_PIN = 10; // ST_CP (12)
5const int POT = A0;
6
7void yaz595(byte desen) {
8 digitalWrite(LATCH_PIN, LOW);
9 shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, desen); // 8 biti gönder / send the 8 bits
10 digitalWrite(LATCH_PIN, HIGH); // çıkışlara aktar / copy to the outputs
11}
12
13void setup() {
14 pinMode(DATA_PIN, OUTPUT);
15 pinMode(CLOCK_PIN, OUTPUT);
16 pinMode(LATCH_PIN, OUTPUT);
17 yaz595(0); // açılışta hepsi sönük / all off at startup
18}
19
20void loop() {
21 int bekle = map(analogRead(POT), 0, 1023, 400, 20);
22
23 for (int i = 0; i < 7; i++) { yaz595(1 << i); delay(bekle); } // Q0 -> Q7
24 for (int i = 7; i > 0; i--) { yaz595(1 << i); delay(bekle); } // Q7 -> Q0
25
26 for (int i = 0; i < 4; i++) { // uçlardan ortaya / from the ends to the middle
27 yaz595((1 << i) | (0x80 >> i));
28 delay(bekle);
29 }
30 for (int i = 3; i >= 0; i--) { // ortadan uçlara / from the middle to the ends
31 yaz595((1 << i) | (0x80 >> i));
32 delay(bekle);
33 }
34}74HC595の出力には、220 Ωではなく470 Ωが推奨されます。チップのVCCピンとGNDピンを流れる合計電流は70 mAを超えないようにする必要があるからです。470 ΩならLED 1個あたり約6.4 mAが流れ、8個全部が点いても合計は51 mAに収まります。そもそもナイトライダーでは一度に点くLEDは1個か2個なので、LEDは十分はっきり見えます。
WS2812Bで1本のピンから尾を引くナイトライダー
本物のKITTのスキャナーにいちばん近い版は、WS2812BアドレサブルLEDで作ります。WS2812Bは1個ずつドライバを内蔵していて、テープがどれだけ長くても、1本のデータ線で各LEDの色と明るさを個別に設定できます。尾を作るために配列を1つ用意します。ステップごとに全LEDの明るさを3で割り、先頭のLEDを255にします。後ろのLEDは255、85、28、9と急速に暗くなり、光の後ろに自然な残像が残ります。割る数を3ではなく2にすると尾が長くなり、5にすると短くなります。同じ方法でLEDを8個から60個に増やすには、NUM_LEDSを変えるだけです。

1// WS2812B ile kuyruklu karaşimşek (tek veri pini) / Knight Rider with a fading tail on WS2812B (one data pin)
2#include <Adafruit_NeoPixel.h>
3
4const int DATA_PIN = 6; // DIN, 330 Ω üzerinden / DIN through 330 Ω
5const int NUM_LEDS = 8; // şerit ya da çubuktaki LED sayısı / LEDs on the strip or stick
6const int POT = A0;
7
8Adafruit_NeoPixel strip(NUM_LEDS, DATA_PIN, NEO_GRB + NEO_KHZ800);
9
10uint8_t seviye[NUM_LEDS]; // her LED'in kırmızı parlaklığı / red brightness of each LED
11int konum = 0;
12int yon = 1;
13
14void setup() {
15 strip.begin();
16 strip.setBrightness(80); // 0–255; USB için düşük tut / keep it low on USB power
17 strip.show(); // hepsini söndür / all off
18}
19
20void loop() {
21 for (int i = 0; i < NUM_LEDS; i++) seviye[i] = seviye[i] / 3; // kuyruk söner / the tail fades
22 seviye[konum] = 255; // baştaki LED tam parlak / the head is full brightness
23
24 for (int i = 0; i < NUM_LEDS; i++) {
25 strip.setPixelColor(i, strip.Color(seviye[i], 0, 0)); // yalnız kırmızı / red only
26 }
27 strip.show();
28
29 konum += yon;
30 if (konum == NUM_LEDS - 1 || konum == 0) yon = -yon; // uçta yön değiştir / reverse at the ends
31 delay(map(analogRead(POT), 0, 1023, 150, 20));
32}ESP32のナイトライダー:すべてのピンでPWMの尾
Unoでは、普通のLEDで尾を引くナイトライダーを作るのは難しいです。PWMが使えるのは3、5、6、9、10、11番ピンだけで、D2〜D9の配列にはそのうち4本しか含まれないからです。ESP32ではLEDCハードウェアのおかげでほぼすべての出力ピンでPWMを出せ、無印ESP32には16個のPWMチャンネルがあります。現在のESP32 ArduinoコアではanalogWrite()がそのまま使えるので、WS2812Bの尾のロジックを普通のLEDに応用できます。表のピンは起動に影響しない安全なGPIOです。ポテンショメーターはWi-Fiがオンでも確実に読めるADC1チャンネルのGPIO 34につなぎます。ESP32のADCは12ビットなので、値は0〜4095の範囲になります。ESP32のピンの出力は3.3 Vなので、赤色LEDに220 Ωで流れる電流は約6 mAです。LEDはUnoのときより少し暗く見えます。ボードの準備についてはESP32とは?を参照してください。
1// ESP32: her pinde PWM ile kuyruklu karaşimşek / ESP32: Knight Rider with a PWM tail on every pin
2const int LEDS[] = {18, 19, 21, 22, 23, 25, 26, 27};
3const int LED_SAYISI = sizeof(LEDS) / sizeof(LEDS[0]);
4const int POT = 34; // ADC1, yalnızca giriş / ADC1, input only
5
6int parlaklik[LED_SAYISI]; // 0–255
7int konum = 0;
8int yon = 1;
9unsigned long sonAdim = 0;
10
11void setup() {
12 // analogWrite() pini PWM için kendisi hazırlar / analogWrite() sets the pin up for PWM itself
13}
14
15void loop() {
16 unsigned long aralik = map(analogRead(POT), 0, 4095, 400, 20); // ESP32 ADC 12 bit: 0–4095
17 if (millis() - sonAdim >= aralik) {
18 sonAdim = millis();
19 for (int i = 0; i < LED_SAYISI; i++) parlaklik[i] /= 3; // kuyruk söner / the tail fades
20 parlaklik[konum] = 255;
21 for (int i = 0; i < LED_SAYISI; i++) analogWrite(LEDS[i], parlaklik[i]);
22 konum += yon;
23 if (konum == LED_SAYISI - 1 || konum == 0) yon = -yon;
24 }
25}MicroPythonでRaspberry Pi Picoのナイトライダー
Raspberry Pi Picoなら、同じ効果をMicroPythonの数行で書けます。Pinオブジェクトをリストに入れると、Arduinoの配列と同じ役割を果たします。time.ticks_ms()とtime.ticks_diff()は、millis()とオーバーフローに強い引き算に相当するMicroPythonの関数です。ポテンショメーターはGP26(ADC0)につなぎます。read_u16()は0〜65535の値を返し、コードがそれを400〜20 msに変換します。PicoのすべてのGPIOから取り出す合計電流は50 mA未満に抑えることが推奨されています。表で330 Ωを選んだのはそのためで、LED 1個あたり約4 mAが流れ、8個全部が点いても合計は約32 mAです。BlueGraysのPicoナイトライダーのプロジェクトでは、GP9〜GP14の6本のピンにLED 6個を使います。その回路を組んだ場合は、PINLERリストを[9, 10, 11, 12, 13, 14]に変えるだけです。セットアップとThonnyでのコードの書き込みについてはRaspberry Pi Picoとは?を参照してください。
1# Raspberry Pi Pico: potansiyometreyle hız ayarlı karaşimşek / Pico: Knight Rider with pot speed control
2from machine import Pin, ADC
3import time
4
5PINLER = [8, 9, 10, 11, 12, 13, 14, 15] # GP8–GP15
6leds = [Pin(p, Pin.OUT) for p in PINLER]
7pot = ADC(26) # GP26 = ADC0
8
9def adim_suresi():
10 # read_u16: 0–65535 -> 400–20 ms
11 return 400 - pot.read_u16() * 380 // 65535
12
13konum, yon = 0, 1
14leds[konum].on()
15son = time.ticks_ms()
16
17while True:
18 if time.ticks_diff(time.ticks_ms(), son) >= adim_suresi(): # millis() benzeri / like millis()
19 son = time.ticks_ms()
20 leds[konum].off()
21 konum += yon
22 if konum == len(leds) - 1 or konum == 0:
23 yon = -yon # uçta yön değiştir / reverse at the ends
24 leds[konum].on()PicoもすべてのGPIOでPWMを出せます。machine.PWMを使い、LEDごとにduty_u16()の値を設定すれば、ESP32の尾の効果をPicoに移せます。WS2812B LEDを使いたい場合は、MicroPythonに組み込まれたneopixelモジュールで1本のGPIOからテープを動かせます。ただし、5 Vで給電したWS2812BはPicoの3.3 Vのデータ信号を必ずしも確実に読めるとは限らないので、色が乱れる場合は間にレベル変換モジュールを入れてください。
ナイトライダーのプロジェクトアイデア
- 音量メーター(VUメーター):KY-038サウンドセンサーで測った音の大きさで、伸びていくバーのLEDをいくつ点けるかを決めます。
- 駐車センサーのLEDバー:HC-SR04で測った距離が縮むほど多くのLEDを点け、とても近づいたら全部を点滅させます。
- 光をつかまえるゲーム:スキャナーが速く往復する中、中央のLEDが点いた瞬間にボタンを押すゲームです。成功するたびにステップ時間を短くします。
- 夜の自動イルミネーション:LDRで暗くなってきたことを検出したら、効果を自動で始めます。
- 模型の車や自転車のテールライト:WS2812Bスティックで尾を引く赤いナイトライダーを作り、ブレーキボタンを押したら全LEDを最大の明るさで点けます。
- レベルインジケーター:ポテンショメーターの位置や電池の電圧を、伸びていくバーで表示します。
- もっと大きな表示:同じロジックをMAX7219 LEDマトリクスやLEDキューブに広げます。
よくある間違いと直し方
| 症状 | 原因 | 対策 |
|---|---|---|
| LEDがとても暗い | pinMode(OUTPUT)がなく、digitalWriteは内部プルアップ抵抗をオンにしているだけ | setup()のループですべてのピンをOUTPUTにする |
| 1個のLEDだけ一度も光らない | LEDが逆向き、または抵抗や線が1列ずれている | 長い脚を抵抗とピンへ、短い脚をGNDへ。LED単体を5Vと抵抗でテストする |
| 両端のLEDが2倍長く点いている | 両方のループに両端のLEDが含まれている | 往路のループではi < LED_SAYISI − 1、復路のループではi > 0を使う |
| 関係ないピンがちらつく、またはプログラムがおかしな動きをする | 配列の終わりを越えて読んでいる:i <= LED_SAYISI | 条件をi < LED_SAYISIに変える。LEDS[8]という要素は存在しない |
| コードが書き込めない、または書き込み中にLEDがちらつく | LEDがD0とD1につながっている | 配列をD2から始める |
| ポテンショメーターで速度が変わらない | 中央の脚ではなく外側の脚がA0につながっている、またはループの外で値を1回しか読んでいない | 中央の脚をA0につなぎ、ステップごとにanalogReadを呼ぶ |
| 約32秒後に効果がおかしくなる | millis()の値をint型の変数に入れている | 時間の変数をunsigned longにする |
| WS2812Bがまったく光らない、またはでたらめな色になる | GNDが共通になっていない、データ線の抵抗がない、またはテープの逆の端(DOUT)につないでいる | GNDをつなぎ、330 Ωを入れ、矢印が出ていく側のDIN端につなぐ |
ナイトライダーはシンプルに見えますが、この先あらゆるプロジェクトで使う基本の考え方が詰まっています。配列、ループ、アナログ値の読み取り、map()、止まらないタイミング処理、ビット演算、アドレサブルLEDです。次のステップとして、Arduinoの信号機のプロジェクトやArduinoのプロジェクトアイデアの一覧を見てみてください。BlueGraysアプリには、流れるLED、ナイトライダーとその速度調整版、WS2812Bの積み上げ効果とナイトライダー効果、ESP32の流れるLEDとPicoのナイトライダーのプロジェクトがあり、どれもステップごとの回路図とすぐに使えるコードつきで、自分のボードで試せます。
よくある質問
Arduino UnoではLEDを何個制御できますか?
Unoにはデジタル出力として使えるピンがD0〜D13とA0〜A5の20本あります。D0とD1をシリアル通信用に空けておけば、LED 18個を直接動かせます。LEDごとに抵抗を入れ、合計電流を200 mA未満に抑えてください。18個全部を同時に点けるなら、330 Ω以上を選びます。もっと多くのLEDには、74HC595シフトレジスタか、1本のピンで数百個のLEDを動かせるWS2812Bテープを使ってください。
LEDチェイサーにはどんな抵抗が必要ですか?
5 VのArduino Unoでは、赤色LEDには220 Ωが合い、LED 1個あたり約13.6 mAが流れます。330 Ωでも動きますが、LEDは少し暗くなります。3.3 VのESP32では220 Ω、Picoでは330 Ωが推奨です。LEDが1個しか点かない効果なら共通の抵抗1本でも動きますが、複数のLEDが点くと明るさが落ちるので、LEDごとに抵抗を1本入れるのが正解です。
Arduinoなしでナイトライダー回路は作れますか?
はい。定番の方法は555タイマーとCD4017カウンターICです。555がクロックパルスを作り、4017が10本の出力を順番にHIGHにするので、それだけで流れるLEDになります。往復の動きにするには、出力をダイオードでまとめます。10本の出力を1-2-3-4-5-6-5-4-3-2の順でLED 6個につなぎ、速度は555につないだポテンショメーターで調整します。
流れるLEDとナイトライダー効果の違いは何ですか?
流れるLEDでは光が一方向に流れ、最後のLEDのあとでまた始まります。LED 8個なら1周は8ステップです。ナイトライダーでは光が端で折り返し、両端の間を往復します。LED 8個なら1周は14ステップです。コードの違いもわずかで、ナイトライダーでは逆向きに数える2つ目のループを加えるだけです。
LEDチェイサーでdelay()ではなくmillis()を使うのはなぜですか?
delay()の実行中、Arduinoはほかに何もできません。ボタンの押下を見逃し、センサーも読めません。millis()を使えば、ループは時間が来たときだけ1ステップ進み、残りの時間でほかのタスクを実行します。そのため、スキャナーが往復している間も、ボタン、ポテンショメーター、シリアルコマンドにすぐ反応します。
ナイトライダー効果に消えていく尾を加えるには?
尾を作るには、先頭の後ろのLEDを少しずつ暗くする必要があり、それにはPWMが必要です。UnoでPWMが使えるのは3、5、6、9、10、11番ピンだけなので、そのピンを使えば尾を引くLED 6個のナイトライダーを作れます。もっと簡単なのは、LEDを1個ずつ設定できるWS2812Bテープか、すべてのピンでPWMが使えるESP32やPicoを使う方法です。
この記事を読んだ後に作れるプロジェクト
全プロジェクトを見る
Knight Rider風LED
Knight Riderでおなじみの、光が左右に往復する動きです。6個のLEDの上を光の点が端から端へ進み、折り返します。Arduinoが各LEDを10分の1秒ずつ点灯させ、端に着くたびに向きを変えて往復し続けます。装飾用の照明や警告灯作りの第一歩にぴったりです。

流れるLED
LEDが次々に点灯し、光が走るように進みます。Arduinoが6個のLEDを1個ずつ短く点灯して消灯させることで、光が端から端へと移動し、また最初に戻ります。この簡単なプロジェクトで、1つのパターンで複数の光を制御する方法を学びます。

速度調整できるKnight Rider風LED
6個のLEDの上を光が端から端へ走り、折り返して戻ってきます。ポテンショメーターのつまみを回すと、その速さをすぐに調整できます。つまみを一方の端にすると光が飛ぶように進み、反対の端にするとゆっくり進みます。1つのつまみで動作中のアニメーションを操る楽しさを体験できます。

速度調整できる流れるLED
6個のLEDが順番に点滅し、光が端から端へ走るように見えます。ポテンショメーターのつまみを回すと、その速さがすぐに変わります。一方に回すと光が素早く進み、反対に回すと各LEDが1秒近く点灯します。つまみで動きの速さを調整する仕組みを目で確かめられます。

WS2812Bのナイトライダー風ライト
スマートLEDテープの赤い光が端から端へ移動して戻り、往復を繰り返し続けます。Arduinoは各ステップで1個のLEDだけを点灯し、0.1秒待ってから隣へ移ります。6個の個別のLEDと何十本もの配線を使う代わりに、1本のテープと1本のデータ線で同じ流れる光を実現できます。

WS2812Bで流れるLED(積み上げ点灯)
コップに水を注ぐように、光が満ちては減っていくLEDテープです。Arduinoが10個のカラーLEDをオレンジ色に1個ずつ点灯し、点灯したままにします。すべて点灯すると、同じ順番で1個ずつ消灯し、最初から繰り返します。このシンプルなアニメーションで、1本の配線から各LEDを個別に制御する方法がわかります。

ESP32の流れるLED
6個のLEDを左から右へ、右から左へ順番に点灯させます(Knight Rider風)。forループと配列を使う基本例で、進む向きを切り替える仕組みを学べます。

Raspberry Pi Picoでナイトライダー風LED(6個が往復)
Picoで定番の「ナイトライダー」風LED点灯パターンを作ります!6個のLEDが左から右へ、次に右から左へと、点灯を繰り返します。1980年代の人気ドラマに登場する車、KITTのフロントグリルのような動きです。このプロジェクトは「流れるLED」プロジェクトの発展版です。6個のLEDを使う回路(GP9-GP14)は同じで、コードのパターンを変えます。reversed()関数でリストを逆順にたどる方法、2つのforループで往復させる方法、同じ回路で複数のアニメーションを作る方法を学びます。同じハードウェア(LED6個、220Ωの抵抗6個)を使います。
ほかの記事
BlueGraysアプリ
この回路をスマホでステップごとに作る
回路図、コード、部品リストを1つの画面で確認できます。Arduino、ESP32、Picoのプロジェクトを200件以上用意しています。

