Skip to content

LED Middle — мигание без delay()

Управление несколькими светодиодами одновременно без delay() — так, чтобы мигание одного не мешало другому и не блокировало остальную программу (например, чтение датчика или работу по Wi-Fi).

Components/2. LED_middle — управление светодиодами (мигание, бегущий огонь)

Модуль учит управлять несколькими светодиодами одновременно без delay() — то есть так, чтобы мигание одного светодиода не мешало миганию другого и не блокировало остальную программу (например, чтение показаний с датчика или работу по Wi-Fi).

Это компонент-актуатор (микроконтроллер только включает/выключает пины), а не датчик: здесь нет обмена данными по шине (I2C/UART), поэтому нет и классического “показания датчика”. Диагностика в этом модуле отвечает на другой вопрос: “не забыл ли я вызывать код обновления в loop()?” — подробнее в THEORY.md.

Структура

Components/2. LED_middle/
├── include/LedController.h   ← продвинутый слой: класс (ООП)
├── src/LedController.cpp     ← продвинутый слой: реализация на millis()
├── include/simple_api.h      ← педагогический слой: фасад из функций
├── src/simple_api.cpp        ← педагогический слой: прячет указатели/массивы
├── src/main.cpp              ← тестовый стенд продвинутого слоя (диагностика)
├── example_sketch.ino        ← пример для ученика (использует simple_api)
├── README.md                 ← этот файл
├── THEORY.md                 ← физика светодиода, math резистора, разбор ошибок
├── WIRING.md                 ← какие пины безопасны на каких платах, схема
└── PREZ.md                   ← краткая презентация модуля (5 слайдов-тезисов)

Два слоя API — и почему их два

СлойКто используетЧто видно
LedController (класс)продвинутые ученики, Combined-Projectsконструкторы, указатели, .update() вызывается вручную на каждом объекте
simple_api (фасад из функций)новички, 5 класстолько процедурные вызовы вроде blinkSingle(13, 200, 200), без единого указателя на виду

Общее архитектурное правило репозитория — один класс-драйвер без второго слоя-фасада (см. firmware/COMPONENT_STANDARD.md). Здесь это правило нарушено осознанно: simple_api — не техническая надстройка “на всякий случай”, а часть учебной программы для самых начинающих учеников, которые ещё не проходили классы и указатели. Внутри фасада по-прежнему работает единственный класс LedController — второй реализации логики мигания нет.

Быстрый старт (педагогический слой)

#include "simple_api.h"

void setup() {
  initLed(2);
  blinkSingle(2, 200, 200);   // мигает каждые 200 мс
}

void loop() {
  updateLeds();   // обязательно один раз за итерацию loop()
}

Полный разбор построчно — в example_sketch.ino и THEORY.md.

API фасада (simple_api.h)

ФункцияЧто делаетПример вызова
initLed(pin)подготовить один светодиодinitLed(2);
blinkSingle(pin, onMs, offMs)мигание одного светодиодаblinkSingle(2, 200, 200);
stopLed(pin)выключить и остановить миганиеstopLed(2);
isLedAlive(pin)вызывается ли updateLeds() достаточно часто для этого пинаisLedAlive(2);
initLedGroup(groupId, pins[], count)создать группу из нескольких светодиодовinitLedGroup(0, pins, 5);
blinkArray(groupId, onMs, offMs)одновременное мигание всей группойblinkArray(0, 300, 300);
runningLights(groupId, stepMs)эффект “бегущий огонь”runningLights(0, 100);
stopGroup(groupId)остановить группуstopGroup(0);
isGroupAlive(groupId)вызывается ли updateLeds() достаточно часто для группыisGroupAlive(0);
updateLeds()обязательно вызывать 1 раз в loop()updateLeds();

Лимиты (можно менять в simple_api.cpp)

  • MAX_SINGLE = 6 — сколько разных одиночных пинов можно завести одновременно.
  • MAX_GROUPS = 4 — сколько разных групп (бегущих огней) можно завести одновременно.

Это не аппаратное ограничение платы, а разумный потолок для учебного стенда (столько отдельных светодиодов реально развести на одной макетке на занятии).

Подключение и сборка

Схема подключения, конкретные номера пинов и обоснование их выбора — в WIRING.md. Проект собирается PlatformIO под три окружения:

cd "Components/2. LED_middle"
pio run -e esp32dev -e esp32s3 -e esp32c6