# nRF24L01+ — беспроводной радиомост между двумя ESP32

Компонент реализует радиомост на паре модулей nRF24L01+: одна ESP32 в роли
передатчика (Transmitter) шлёт пакеты телеметрии, вторая в роли приёмника
(Receiver) их принимает. Обмен идёт через готовую библиотеку `RF24`, поверх
которой компонент даёт собственный прикладной класс `NrfLink` — подробнее о
том, зачем нужен ещё один слой поверх уже готового драйвера чипа, см.
[THEORY.md](THEORY.md).

## Файлы

- [include/NrfLink.h](include/NrfLink.h) / [src/NrfLink.cpp](src/NrfLink.cpp) — драйвер.
- [src/main.cpp](src/main.cpp) — тестовый стенд с тремя ролями (передатчик/приёмник/тест железа).
- [tools/link_quality_view.py](tools/link_quality_view.py) — живой график качества связи (см. раздел "Визуализация" ниже).
- [THEORY.md](THEORY.md) — физика радиоэфира 2.4 ГГц, Auto-ACK, лимиты payload.
- [WIRING.md](WIRING.md) — схема подключения (SPI).
- [Lesson.md](Lesson.md) — методический план урока для учителя.

## Подключение (кратко)

| nRF24L01+ | ESP32 (по умолчанию в `main.cpp`) |
|-----------|-------------------------------------|
| VCC       | 3.3V                                 |
| GND       | GND                                   |
| CE        | GPIO4                                 |
| CSN       | GPIO27                                |
| SCK       | GPIO18                                |
| MISO      | GPIO19                                |
| MOSI      | GPIO23                                |

Полная схема, включая важное примечание про конденсатор питания, — в [WIRING.md](WIRING.md).

## Быстрый старт

Нужны **две** платы ESP32 с двумя модулями nRF24L01+.

1. Собрать схему по [WIRING.md](WIRING.md) на обеих платах.
2. Прошить первую плату ролью передатчика: `pio run -e transmitter -t upload`.
3. Прошить вторую плату ролью приёмника: `pio run -e receiver -t upload`.
4. Открыть Serial Monitor на каждой (115200 бод) — передатчик покажет долю
   успешных подтверждений (`AckRate`), приёмник — принятые пакеты и оценочную
   задержку.

Если нужно просто проверить, что чип отвечает по SPI (без второй платы) —
окружение по умолчанию: `pio run -e test_hw -t upload`.

## Роли и окружения PlatformIO

| Окружение | Роль | Плата |
|-----------|------|-------|
| `test_hw` | Проверка присутствия чипа по SPI, без радиообмена | esp32dev |
| `transmitter` / `transmitter_s3` / `transmitter_c6` | Передатчик | esp32dev / esp32-s3 / esp32-c6 |
| `receiver` / `receiver_s3` / `receiver_c6` | Приёмник | esp32dev / esp32-s3 / esp32-c6 |

Если прошиваете обе платы с одного компьютера, PlatformIO может перепутать
порты при автоопределении — как найти и закрепить свой порт, см. комментарий
в [platformio.ini](platformio.ini).

## Визуализация

Serial Monitor с текстовыми числами — не самый наглядный формат для группы
учеников. `tools/link_quality_view.py` рисует живой график:

```bash
cd tools
pip install -r requirements.txt
python3 link_quality_view.py /dev/ttyUSB0 --role tx   # для передатчика
python3 link_quality_view.py /dev/ttyUSB0 --role rx   # для приёмника
```

На стороне передатчика хорошо видно, как разносится плата с приёмником
(закройте антенну ладонью, отойдите подальше) — `AckRate` падает в реальном
времени. Serial Monitor прошивки на момент запуска скрипта должен быть закрыт.

## API драйвера

| Метод | Назначение |
|-------|------------|
| `begin(role, address, channel, dataRate, paLevel)` | Инициализация SPI и настройка радиоэфира под выбранную роль. |
| `sendTelemetry(TelemetryPacket)` | Отправка пакета (только Transmitter). `false` — Auto-ACK не получен. |
| `receiveTelemetry(TelemetryPacket&)` | Приём пакета, если он есть (только Receiver). |
| `getAckSuccessRate()` | Доля успешных подтверждений с момента `begin()`. |
| `isChipConnected()` | Прямая проверка присутствия чипа по SPI. |
| `isAlive(timeoutMs)` | Была ли недавняя протокольная активность (отправка/приём). |

## Известные ограничения

- У nRF24L01+ нет измерения настоящей силы сигнала (RSSI) — метрика качества
  связи здесь построена на проценте успешных Auto-ACK, см. [THEORY.md](THEORY.md).
- Оценка задержки (`DelayMs`) на приёмнике не учитывает рассинхронизацию
  часов `millis()` двух независимых плат — годится для сравнения "быстрее/медленнее",
  не для точного измерения времени в миллисекундах.
- Максимальный размер пакета — 32 байта (аппаратный лимит чипа), проверяется
  на этапе компиляции через `static_assert` в [NrfLink.cpp:15-16](src/NrfLink.cpp#L15-L16).
