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

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

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

## Структура

```
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` — второй реализации логики мигания нет.

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

```cpp
#include "simple_api.h"

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

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

Полный разбор построчно — в [example_sketch.ino](example_sketch.ino) и
[THEORY.md](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](WIRING.md). Проект собирается PlatformIO под три окружения:

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