Руководства · 13 мин чтения

Бегущие огни Knight Rider на Arduino: схема, код и регулировка скорости

Собираем бегущие огни Knight Rider на Arduino: 8 светодиодов и резисторы 220 Ω, код с массивом и циклом for, регулировка скорости потенциометром, millis, 74HC595, WS2812B, ESP32 и Pico.

Схема Arduino Knight Rider — классический светодиодный эффект: ряд светодиодов загорается один за другим, и огонёк пробегает от одного края к другому и обратно. Название взято от сканера на передней части KITT, говорящей машины из знаменитого сериала 1980-х «Рыцарь дорог» (Knight Rider). Понадобятся только Arduino Uno, 6 или 8 светодиодов, по резистору 220 Ω на каждый светодиод и несколько перемычек. Если огонёк бежит только в одну сторону, а затем начинает снова с начала, это бегущий огонь (LED chaser). Оба проекта — классика, которую новички собирают сразу после светофора, и самый весёлый способ освоить два важнейших инструмента программирования: массивы и цикл for.

В этом руководстве сначала собирается схема и пишется код бегущего огня, затем — код Knight Rider с движением туда и обратно. После этого ты задашь скорость потенциометром, добавишь эффект, в котором светодиоды накапливаются у одного края, и запустишь тот же эффект на millis(), вообще не блокируя код. В последних разделах ты найдёшь, как управлять 8 светодиодами всего с 3 пинов через микросхему 74HC595, как сделать Knight Rider с затухающим хвостом от одного пина на адресных светодиодах WS2812B, версии для ESP32 и Raspberry Pi Pico (MicroPython), а также решения самых частых ошибок.

Красные, жёлтые, синие, зелёные и белые светодиоды 5 мм
Для классического вида Knight Rider возьми 6 или 8 светодиодов 5 мм одного цвета (лучше всего красных); длинная ножка — анод (+), короткая — катод (−).

Что такое эффект Knight Rider и чем он отличается от бегущего огня?

В сериале красный огонь на переднем бампере KITT без остановки бегает влево и вправо, как сканирующий глаз. В электронике этот эффект называют ещё сканером Ларсона (Larson scanner) — по имени продюсера сериала Глена А. Ларсона; глаза роботов-сайлонов из другого его сериала, «Звёздный крейсер „Галактика“» (Battlestar Galactica), устроены так же. У настоящего сканера за светящейся точкой виден медленно гаснущий красный след — хвост. В простой схеме каждый светодиод либо горит полностью, либо полностью погашен; чтобы изобразить хвост, светодиоды нужно постепенно приглушать с помощью ШИМ. Именно это мы и сделаем дальше с WS2812B и ESP32. В таблице ниже собраны эффекты, которые программируются в этой статье, и сколько шагов занимает один круг с 8 светодиодами.

ЭффектЧто делаетОдин круг с 8 светодиодамиВ этой статье
Бегущий огоньОдин огонёк бежит слева направо, затем начинает снова8 шаговШаг 1
Knight RiderОдин огонёк отскакивает между двумя краями14 шаговШаги 2 и 3
НакоплениеКаждый огонёк бежит до крайнего правого свободного места и остаётся там36 шаговШаг 4
Встреча в центреДва огонька идут от краёв к центру, затем расходятся8 шаговРаздел о 74HC595
Knight Rider с хвостомГоловной светодиод горит на полной яркости, следующие за ним гаснут14 шаговРазделы о WS2812B и ESP32

Список деталей и выбор резистора

ДетальКол-воПримечание
Arduino Uno (или Nano)1Подойдёт и ESP32 или Raspberry Pi Pico
Светодиод 5 мм6–8Одного цвета и из одной партии, чтобы яркость была одинаковой
Резистор 220 Ω6–8По одному на светодиод; на Pico — 330 Ω
Потенциометр 10 kΩ (B10K)1Только для версий с регулировкой скорости
Макетная плата1Хватит половинной (400 отверстий)
Провода-перемычки12–15Типа «папа–папа»
74HC595 или линейка WS2812B на 8 светодиодов1Необязательно: для разделов об экономии пинов и эффекте хвоста

Светодиод не ограничивает свой ток, поэтому каждому светодиоду нужен последовательный резистор. Номинал находится по закону Ома: R = (напряжение питания − прямое напряжение светодиода) / ток. У красного светодиода прямое напряжение около 2 V, поэтому при 5 V и 220 Ω ток равен (5 − 2) / 220 ≈ 13,6 mA. Светодиод горит ярко, а ток остаётся ниже рекомендуемых для пина Arduino 20 mA. В эффекте накопления, когда одновременно горят все восемь светодиодов, суммарный ток около 109 mA — это всё ещё в пределах лимита Uno в 200 mA на все пины вместе. Чтобы узнать резистор 220 Ω по цветным полосам, воспользуйся утилитой цветового кода резисторов; для других цветов и напряжений попробуй калькулятор резистора для светодиода.

Подключение: 8 светодиодов и потенциометр

Длинная ножка (анод) каждого светодиода идёт через резистор к цифровому пину платы, а короткая (катод) — к GND. На макетной плате аккуратнее всего соединить пин GND Arduino с синей (−) шиной макетки и вывести на эту шину короткие ножки всех восьми светодиодов. Расставь светодиоды на равном расстоянии слева направо в порядке пинов: тогда порядок в массиве кода совпадёт с физическим порядком огонька. Два крайних вывода потенциометра идут к 5V и GND, а средний (движок) — к A0. В таблице ниже указаны пины, которые используют все скетчи этой статьи; если светодиодов 6, пропусти две последние строки со светодиодами и удали два последних пина из массива в коде. Расположение пинов твоей платы можно посмотреть в утилите распиновки.

НазначениеArduino UnoESP32Raspberry Pi Pico
Светодиод 1 (крайний левый)D2GPIO 18GP8
Светодиод 2D3GPIO 19GP9
Светодиод 3D4GPIO 21GP10
Светодиод 4D5GPIO 22GP11
Светодиод 5D6GPIO 23GP12
Светодиод 6D7GPIO 25GP13
Светодиод 7D8GPIO 26GP14
Светодиод 8 (крайний правый)D9GPIO 27GP15
Средний вывод потенциометраA0GPIO 34GP26 (ADC0)
Крайние выводы потенциометра5V и GND3V3 и GND3V3(OUT) и AGND
Катоды светодиодов (короткая ножка)GNDGNDGND
Последовательный резистор220 Ω220 Ω330 Ω
Не подключай светодиоды к пинам D0 и D1: они используются для загрузки кода по USB и для монитора порта; с подключёнными светодиодами загрузка может не пройти, а светодиоды будут хаотично мигать. Поэтому массив начинается с D2. Если не загорается ни один светодиод, сначала проверь, действительно ли синяя шина макетной платы соединена с пином GND Arduino.
Arduino Uno
Arduino Uno
Макетная плата
Макетная плата
R · Ω
R · Ω
Перемычка
Перемычка

Шаг 1: код бегущего огня с массивом и циклом for

Для восьми светодиодов можно написать восемь отдельных строк pinMode и шестнадцать отдельных строк digitalWrite, но код получится длинным, а менять количество светодиодов станет мучением. Вместо этого номера пинов кладутся в массив, и по нему проходит цикл for. Первый элемент массива — LEDS[0], это D2; последний — LEDS[7], это D9. Так один и тот же код без изменений работает с 6, 8 или 12 светодиодами; правится только массив. Основы массивов и циклов подробнее объяснены в статье Основы программирования Arduino.

C++ (Arduino IDE)
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}; (массив пинов)Массив с пинами светодиодов по порядку слева направо; 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; каждый проход обрабатывает один элемент массива
pinMode(LEDS[i], OUTPUT) (режим выхода)Делает все восемь пинов выходами одним циклом
digitalWrite, delay(BEKLE), digitalWrite (включить, ждать, выключить)Включает светодиод, ждёт BEKLE миллисекунд, выключает его и переходит к следующему

При BEKLE (время шага) 100 мс огонёк проходит все восемь светодиодов за 0,8 секунды и сразу начинает снова. Уменьши до 30 мс — и бег станет очень быстрым; подними до 300 мс — и он будет выглядеть медленным, словно лампы загораются одна за другой. Чтобы загрузить код, выбери плату и порт в меню «Инструменты» (Tools) и нажми «Загрузка» (Upload); если загрузка не проходит, пройдись по списку проверок в статье Код не загружается в Arduino.

Шаг 2: код Knight Rider (туда и обратно)

Для эффекта Knight Rider огонёк должен развернуться, дойдя до правого края. Это делается двумя циклами: первый считает слева направо, второй — справа налево. Тонкий момент здесь — крайние светодиоды. Если оба цикла считают полностью, от 0 до 7 и от 7 до 0, крайний правый светодиод загорается один раз в конце прямого цикла и ещё раз в начале обратного; на краях огонёк задерживается вдвое дольше, и движение выглядит рваным. Поэтому прямой цикл останавливается на 6, а обратный — на 1. В итоге каждый круг занимает ровно 14 шагов, и на краях огонёк стоит столько же, сколько в середине. Ещё три строки, которые включают и выключают светодиод, вынесены в маленькую функцию yakSondur() («зажечь и погасить»); код стал и короче, и понятнее.

C++ (Arduino IDE)
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}
Ближе всего к виду из сериала скорость около 60–120 мс на шаг. С 8 светодиодами и 80 мс один круг туда и обратно занимает 14 × 80 = 1,12 секунды. Если опуститься примерно до 20 мс, глаз видит сразу несколько горящих светодиодов, и огонёк превращается в размытую линию.

Шаг 3: регулировка скорости потенциометром

Чтобы не менять код и не загружать его заново каждый раз ради другой скорости, можно использовать потенциометр. Потенциометр — это делитель напряжения: напряжение на среднем выводе меняется от 0 до 5 V в зависимости от положения вала, а analogRead() превращает его в число от 0 до 1023. map(deger, 0, 1023, 400, 20) переводит это число во время шага от 400 до 20 мс; поскольку диапазон записан наоборот, чем больше показание, тем короче шаг и тем быстрее огонёк. В какую сторону крутить вал для ускорения, зависит от того, какой крайний вывод подключён к 5V; если направление кажется обратным, поменяй местами два крайних провода или числа 400 и 20 в map(). Как потенциометр устроен внутри, читай в статье Что такое потенциометр?.

Потенциометр 10 kΩ
Потенциометр 10 kΩ: крайние выводы идут к 5V и GND, средний вывод (движок) — к A0.
C++ (Arduino IDE)
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}

В этой версии время шага считывается заново для каждого светодиода, поэтому скорость меняется сразу, как только поворачивается вал. Однако пока работает delay(), Arduino не может делать ничего другого: даже если повернуть вал посреди медленного шага в 400 мс, новая скорость применится только со следующего шага. Для эффекта с несколькими светодиодами это не проблема, но как только появляются кнопка режимов или чтение датчиков, нужно переходить на метод millis() из шага 5.

Шаг 4: эффект накопления

В эффекте накопления первый огонёк пробегает до самого правого края и остаётся гореть справа. Второй огонёк тоже бежит, но на этот раз останавливается на одну позицию левее; с каждым кругом цель сдвигается на один светодиод влево, пока вся полоса не заполнится. В коде это делается двумя вложенными циклами: внешний считает, сколько светодиодов уже накопилось (dolu, то есть «заполнено»), а внутренний — сколько шагов пробегает огонёк в этом круге. Светодиод, дошедший до своей цели (hedef), не выключается; за это отвечает строка if (i < hedef). С восемью светодиодами заполнение всей полосы занимает 8 + 7 + 6 + ... + 1 = 36 шагов.

C++ (Arduino IDE)
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 секунды, затем светодиоды гаснут по одному справа налево, и эффект начинается заново. Если нужна более простая заполняющаяся шкала, просто убери внутренний цикл и включай светодиоды по порядку, ни разу не выключая; такой вид идеален для индикатора заряда или шкалы уровня звука. Помни, что в этом эффекте все восемь светодиодов горят одновременно: пока у каждого светодиода свой резистор, яркость не упадёт.

Шаг 5: неблокирующий Knight Rider на millis()

В коде с delay() Arduino на каждом шаге замирает на заданное время; даже если в это время кто-то нажмёт кнопку, плата этого не заметит. millis() же возвращает время в миллисекундах с момента включения платы. Логика такая: loop крутится постоянно, на каждом проходе проверяет, прошло ли достаточно времени с последнего шага, если да — делает ровно один шаг и сразу выходит. В коде ниже Knight Rider и встроенный светодиод работают одновременно как две независимые задачи: пока сканер бегает со скоростью, заданной потенциометром, светодиод на D13 мигает раз в секунду в своём темпе. Подробнее об этом — в статье Arduino: millis() или delay().

C++ (Arduino IDE)
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}

Здесь важны две детали. Во-первых, переменные времени должны быть unsigned long; если взять int, на Uno значение переполнится примерно через 32,7 секунды, и тайминг сломается: эффект либо остановится, либо неконтролируемо ускорится. Во-вторых, сравнение делается вычитанием в форме simdi - sonAdim >= aralik (текущее время минус время последнего шага сравнивается с интервалом); этот способ продолжает работать правильно, даже когда millis() примерно раз в 49,7 дня обнуляется. Поскольку позиция и направление огонька хранятся в двух переменных (konum и yon), циклов больше нет: на каждом шаге позиция увеличивается или уменьшается на единицу, а на краях направление меняет знак.

8 светодиодов от 3 пинов с 74HC595

Восемь светодиодов занимают восемь пинов Uno; как только в проект захочется добавить LCD, датчики и кнопки, пинов начинает не хватать. Сдвиговый регистр 74HC595 управляет своими восемью выходами всего через три пина Arduino (данные, тактовый сигнал и защёлка). Состояние каждого светодиода — один бит байта: выражение 1 << i ставит в 1 только бит i, и загорается один светодиод. Узоры на битовых операциях тоже строятся легко: (1 << i) | (0x80 >> i) даёт два огонька, которые одновременно идут от обоих краёв к центру. Соединив две микросхемы цепочкой, можно сделать и 16-светодиодный Knight Rider на тех же трёх пинах; цепочки, быстрое управление по 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 Ω к аноду светодиода
Средний вывод потенциометраA0
C++ (Arduino IDE)
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 рекомендуется 470 Ω вместо 220 Ω: суммарный ток через выводы VCC и GND микросхемы не должен превышать 70 mA. С 470 Ω через каждый светодиод течёт около 6,4 mA, и даже когда горят все восемь светодиодов, сумма остаётся на уровне 51 mA. В Knight Rider одновременно горят только один-два светодиода, так что они всё равно хорошо видны.

Knight Rider с затухающим хвостом от одного пина на WS2812B

Версия, больше всего похожая на настоящий сканер KITT, делается на адресных светодиодах WS2812B. У каждого WS2812B встроен собственный драйвер; какой бы длинной ни была лента, цвет и яркость каждого светодиода задаются по отдельности по одному проводу данных. Для хвоста хранится массив: на каждом шаге яркость всех светодиодов делится на три, а головному светодиоду ставится 255. Светодиоды позади него быстро гаснут через 255, 85, 28, 9 и оставляют за огоньком естественный след. Возьми делитель 2 вместо 3 — хвост станет длиннее; возьми 5 — короче. Чтобы тем же методом перейти с 8 на 60 светодиодов, достаточно изменить NUM_LEDS.

Адресный RGB-светодиод WS2812B
WS2812B: у каждого светодиода внутри свой драйвер; подключается через DIN, 5V и GND, а DOUT идёт к следующему светодиоду.
C++ (Arduino IDE)
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}
При подключении WS2812B поставь резистор 330 Ω последовательно в линию данных и конденсатор 1000 µF между 5V и GND, а если используется внешний источник питания, соедини его GND с GND Arduino. Каждый светодиод на полном белом может потреблять около 60 mA; линейка на 8 светодиодов работает от USB, но для 30 светодиодов и больше нужен отдельный источник 5 V. Об установке библиотеки, порядке цветов и расчёте питания — в статье Светодиоды WS2812B NeoPixel.

Knight Rider на ESP32: ШИМ-хвост на каждом пине

На Uno Knight Rider с хвостом на обычных светодиодах сделать трудно: ШИМ есть только на пинах 3, 5, 6, 9, 10 и 11, а в нашем массиве D2–D9 из них только четыре. На ESP32 благодаря аппаратному блоку LEDC ШИМ может выдавать почти любой выходной пин, а у классической ESP32 16 каналов ШИМ. Поскольку в актуальном ядре ESP32 для Arduino analogWrite() работает напрямую, логику хвоста от WS2812B можно применить к обычным светодиодам. Пины в таблице — безопасные GPIO, которые не влияют на загрузку платы; потенциометр подключается к GPIO 34 на канале ADC1, который надёжно читает даже при включённом Wi-Fi, а поскольку АЦП ESP32 12-битный, значение меняется от 0 до 4095. Пины ESP32 выдают 3,3 V, поэтому через красный светодиод с 220 Ω течёт около 6 mA; светодиоды выглядят чуть тусклее, чем на Uno. О настройке платы — в статье Что такое ESP32?.

C++ (Arduino IDE)
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}

Knight Rider на Raspberry Pi Pico с MicroPython

На Raspberry Pi Pico тот же эффект пишется в несколько строк MicroPython. Список объектов Pin выполняет ту же роль, что массив на Arduino. time.ticks_ms() и time.ticks_diff() — аналоги millis() и защищённого от переполнения вычитания в MicroPython. Потенциометр подключается к GP26 (ADC0); read_u16() возвращает значение от 0 до 65535, и код переводит его в 400–20 мс. Суммарный ток со всех GPIO Pico рекомендуется держать ниже 50 mA; поэтому в таблице выбрано 330 Ω: через каждый светодиод течёт около 4 mA, и даже когда горят все восемь, сумма остаётся около 32 mA. В проекте BlueGrays «Огни Knight Rider на Pico» используются 6 светодиодов на пинах GP9–GP14; если собрана именно эта схема, просто замени список PINLER на [9, 10, 11, 12, 13, 14]. Об установке и загрузке кода через Thonny — в статье Что такое Raspberry Pi Pico?.

MicroPython
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()

Каждый GPIO на Pico тоже умеет выдавать ШИМ; с machine.PWM и значением duty_u16() для каждого светодиода эффект хвоста с ESP32 переносится на Pico. Если нужны светодиоды WS2812B, встроенный в MicroPython модуль neopixel управляет лентой от одного GPIO; однако WS2812B с питанием 5 V не всегда надёжно читает сигнал данных 3,3 V от Pico, поэтому если цвета сбоят, поставь между ними преобразователь уровней.

Идеи проектов с Knight Rider

  • Индикатор уровня звука (VU-метр): громкость, измеренная датчиком звука KY-038, определяет, сколько светодиодов заполняющейся шкалы загорится.
  • Светодиодная шкала парктроника: зажигай больше светодиодов по мере того, как уменьшается расстояние, измеренное HC-SR04, а при сильном приближении мигай всеми сразу.
  • Игра «Поймай огонёк»: пока сканер быстро бегает, старайся нажать кнопку ровно в тот момент, когда горит средний светодиод; после каждого успеха сокращай время шага.
  • Автоматическое ночное украшение: эффект запускается автоматически, когда фоторезистор (LDR) замечает, что темнеет.
  • Задний фонарь для модели машины или велосипеда: сделай красный Knight Rider с хвостом на линейке WS2812B, а при нажатии кнопки тормоза зажигай все светодиоды на полную яркость.
  • Индикатор уровня: показывай положение потенциометра или напряжение батареи заполняющейся шкалой.
  • Дисплеи побольше: перенеси ту же логику на светодиодную матрицу MAX7219 или светодиодный куб.

Частые ошибки и как их исправить

СимптомПричинаРешение
Светодиоды светят очень тусклоНет pinMode(OUTPUT); digitalWrite лишь включает встроенный подтягивающий резисторСделай все пины OUTPUT циклом в setup()
Один светодиод никогда не загораетсяСветодиод перевёрнут, или резистор либо провод сдвинут на один рядДлинная ножка — к резистору и пину, короткая — к GND; проверь светодиод отдельно с 5V и резистором
Крайние светодиоды горят вдвое дольшеОба цикла включают крайние светодиодыВ прямом цикле используй i < LED_SAYISI − 1, в обратном — i > 0
Случайный пин мигает или программа ведёт себя странноЧтение за пределами массива: i <= LED_SAYISIИзмени условие на i < LED_SAYISI; элемента LEDS[8] не существует
Код не загружается, или светодиоды мигают во время загрузкиСветодиоды подключены к D0 и D1Начни массив с D2
Потенциометр не меняет скоростьК A0 подключён крайний вывод вместо среднего, или значение читается только один раз вне циклаПодключи средний вывод к A0; вызывай analogRead на каждом шаге
Эффект сбивается примерно через 32 секундыЗначение millis() хранится в переменной intСделай переменные времени unsigned long
WS2812B вообще не горят или показывают случайные цветаНет общей земли, нет резистора в линии данных, или лента подключена не с того конца (DOUT)Соедини GND, добавь 330 Ω, подключай к концу DIN, от которого указывают стрелки на ленте
Перед сборкой схемы код можно опробовать в Tinkercad с 8 светодиодами и потенциометром. Если в реальной схеме что-то не так, загрузи короткий скетч, который на несколько секунд ставит все пины в HIGH, и проверь светодиоды по очереди; сразу станет видно, какой из них не загорается.

Knight Rider выглядит просто, но в нём есть базовые идеи, которые пригодятся потом в каждом проекте: массивы, циклы, аналоговое чтение, map(), неблокирующие задержки, битовые операции и адресные светодиоды. Для следующего шага загляни в проект Светофор на Arduino или в список идей проектов на Arduino. В приложении BlueGrays есть проекты «Бегущий огонь», «Огни Knight Rider» и «Огни Knight Rider с регулировкой скорости», эффекты накопления и Knight Rider на WS2812B, а также «Бегущий огонь на ESP32» и «Огни Knight Rider на Pico» — каждый с пошаговой схемой подключения и готовым кодом, чтобы попробовать на своей плате.

Частые вопросы

Сколькими светодиодами может управлять Arduino Uno?

У Uno 20 пинов, которые можно использовать как цифровые выходы: D0–D13 и A0–A5; если оставить D0 и D1 свободными для последовательной связи, напрямую можно управлять 18 светодиодами. Дай каждому светодиоду свой резистор и держи суммарный ток ниже 200 mA; если все 18 будут гореть одновременно, выбери 330 Ω или больше. Для большего количества светодиодов используй сдвиговый регистр 74HC595 или ленту WS2812B, которая управляет сотнями светодиодов от одного пина.

Какой резистор нужен для бегущих огней?

На Arduino Uno с 5 V для красных светодиодов подходит 220 Ω, это около 13,6 mA на светодиод; 330 Ω тоже подойдёт, но светодиоды будут чуть тусклее. На ESP32 с 3,3 V рекомендуется 220 Ω, на Pico — 330 Ω. В эффектах, где горит только один светодиод, работает и один общий резистор, но когда загораются несколько светодиодов, яркость падает, поэтому правильный выбор — по резистору на каждый светодиод.

Можно ли собрать схему Knight Rider без Arduino?

Да, классический способ — таймер 555 и микросхема-счётчик CD4017. 555 выдаёт тактовые импульсы, а 4017 по очереди ставит в HIGH свои 10 выходов, что уже даёт бегущий огонь. Для движения туда и обратно выходы объединяются диодами: 10 выходов подключаются к 6 светодиодам в порядке 1-2-3-4-5-6-5-4-3-2, а скорость задаётся потенциометром на 555.

Чем бегущий огонь отличается от эффекта Knight Rider?

В бегущем огне огонёк движется в одну сторону и после последнего светодиода начинает заново; с 8 светодиодами один круг — 8 шагов. В Knight Rider огонёк на краю разворачивается и отскакивает между двумя краями; с 8 светодиодами один круг — 14 шагов. Разница в коде тоже небольшая: для Knight Rider добавляется второй цикл, который считает в обратную сторону.

Зачем в бегущих огнях использовать millis() вместо delay()?

Пока работает delay(), Arduino не может делать ничего другого: она пропускает нажатия кнопок и не может читать датчики. С millis() каждый проход loop делает шаг, только когда подошло время, а в оставшееся время выполняются другие задачи. Поэтому, пока сканер бегает, кнопки, потенциометры или команды по последовательному порту срабатывают мгновенно.

Как добавить затухающий хвост к эффекту Knight Rider?

Для хвоста светодиоды за головным нужно постепенно приглушать, а для этого нужен ШИМ. На Uno ШИМ есть только на пинах 3, 5, 6, 9, 10 и 11, так что Knight Rider с хвостом на 6 светодиодах можно собрать на этих пинах. Путь проще — лента WS2812B, где каждый светодиод задаётся отдельно, или ESP32 либо Pico, у которых ШИМ есть на каждом пине.

Все проекты
Светодиодный эффект Knight Rider
#6Средний

Светодиодный эффект Knight Rider

Классический бегущий огонь-сканер: светящаяся точка скользит от одного края шести светодиодов к другому и возвращается обратно. Arduino зажигает каждый светодиод на десятую долю секунды и переходит к

4.8(106)
202
Бегущие огни на светодиодах
#5Средний

Бегущие огни на светодиодах

Огни, которые загораются друг за другом, будто бегут. Arduino зажигает шесть светодиодов по одному; каждый светится короткое мгновение и гаснет, так что свет проходит от начала до конца и начинает сно

4.6(85)
207
Светодиодный эффект Knight Rider с регулируемой скоростью
#8Средний

Светодиодный эффект Knight Rider с регулируемой скоростью

Свет бежит от одного края шести светодиодов к другому, разворачивается и бежит обратно. Поверни ручку потенциометра, и скорость этого бега сразу изменится: в одном крайнем положении свет почти летит,

4.5(60)
120
Бегущие огни с регулируемой скоростью
#7Средний

Бегущие огни с регулируемой скоростью

Шесть светодиодов загораются друг за другом, и кажется, что свет бежит от одного края к другому. Поверни ручку потенциометра, и скорость этого бега сразу меняется: в одну сторону свет мчится, в другую

4.6(72)
146
Эффект Knight Rider на WS2812B
#73Средний
Premium

Эффект Knight Rider на WS2812B

Красная светящаяся точка скользит от одного края умной светодиодной ленты к другому, затем возвращается и бегает туда-обратно без остановки. На каждом шаге Arduino зажигает только один светодиод, ждёт

4.9(73)
94
Бегущий огонь на WS2812B (эффект заполнения)
#72Средний
Premium

Бегущий огонь на WS2812B (эффект заполнения)

Световая лента, которая наполняется и пустеет, как стакан с водой. Arduino по одному зажигает десять цветных светодиодов оранжевым и оставляет их гореть; когда лента заполнена, выключает их по одному

4.7(50)
65
Светодиоды Knight Rider на ESP32
#108Новичок

Светодиоды Knight Rider на ESP32

6 светодиодов по очереди бегают слева направо и справа налево (в стиле Knight Rider). Базовый пример использования циклов for и массивов. Учит логике смены направления.

4.5(19)
32
Сканер Knight Rider на Pico (6 светодиодов туда-обратно)
#154Новичок

Сканер Knight Rider на Pico (6 светодиодов туда-обратно)

Классический эффект светодиодного сканера Knight Rider на Pico! 6 светодиодов бегут слева направо, затем справа налево, и так бесконечно. Как передняя решётка машины KITT из культового сериала 80-х. Этот проект — доработка проекта «Бегущие огни»: та же схема из 6 светодиодов (GP9–GP14), но другой узор в коде. Изучи функцию reversed() для разворота списка, два отдельных цикла for для движения туда-обратно и несколько анимаций на одной схеме.

0.0(0)
13