# Теория: как две ESP32 разговаривают по радио

## 1. Диапазон 2.4 ГГц: общий эфир для всех

nRF24L01+ работает в том же диапазоне частот (2.4 ГГц), что Wi-Fi и Bluetooth —
это не совпадение, а стандарт: этот диапазон выделен для нелицензируемого
использования почти во всём мире, поэтому в нём одновременно "разговаривают"
роутеры, наушники, беспроводные мыши и наш модуль. Чтобы не мешать друг другу,
устройства делят диапазон на **каналы** — узкие полосы частот.

Формула канала у nRF24L01+ простая: `частота = 2400 + номер_канала` МГц. В
этом компоненте выбран канал 110 ([main.cpp:13](src/main.cpp#L13)), то есть
частота 2510 МГц — она находится **выше** типового диапазона Wi-Fi
(2412-2472 МГц, каналы 1-13) и Bluetooth, поэтому меньше шансов, что домашний
роутер или чей-то телефон будут "перебивать" наш сигнал.

## 2. Auto-ACK: как понять, что пакет дошёл

В радиоэфире нет гарантии, что отправленный пакет долетел — сигнал может
потеряться из-за помех, расстояния или препятствия. Библиотека `RF24`
использует аппаратный механизм **Auto-ACK** (automatic acknowledgement):
после каждого отправленного пакета приёмник автоматически, без участия кода,
отправляет короткое подтверждение "получил". Если передатчик за отведённое
время не дождался этого подтверждения — он считает пакет потерянным.

Именно это возвращает `radio.write()` внутри `RF24`, и именно на этом
построен весь смысл `NrfLink::sendTelemetry()`
([NrfLink.cpp:29-37](src/NrfLink.cpp#L29-L37)): `true` — ACK получен, пакет
точно дошёл, `false` — ACK не пришёл, пакет считается потерянным (или потерялось
само подтверждение — со стороны передатчика это неотличимо, и это нормальное
ограничение протокола).

## 3. Почему "качество связи" — это не RSSI

У Wi-Fi и Bluetooth чипов обычно есть прямое измерение силы принятого сигнала
(RSSI, Received Signal Strength Indicator) — число в дБм, которое можно
прочитать из регистра. **У nRF24L01+ такого измерения нет.**

Поэтому в этом компоненте "качество связи" — это не сила сигнала, а **доля
успешных подтверждений (Auto-ACK success rate)**:

```
ackRate = packetsAcked / packetsSent
```

реализовано в [NrfLink::getAckSuccessRate()](src/NrfLink.cpp#L47-L50). Это
косвенная, но честная метрика: чем хуже физическая связь (дальше расстояние,
больше помех, экранирована антенна), тем чаще Auto-ACK не успевает дойти, и
тем ниже становится `ackRate`. Это не "сила сигнала в дБм", а "процент
успешных попыток" — этот компонент нигде не выдаёт одно за другое.

## 4. Payload: сколько данных влезает в один пакет

nRF24L01+ аппаратно ограничивает размер одного пакета данных (payload) 32
байтами — это физическое ограничение чипа, а не настройка, которую можно
изменить в коде. `TelemetryPacket` ([NrfLink.h:16-20](include/NrfLink.h#L16-L20))
занимает всего 13 байт (4+4+4 плюс выравнивание), что оставляет запас для
будущих компонентов, которым потребуется передавать больше полей. Соответствие
лимиту проверяется не "на глаз", а автоматически на этапе компиляции:

```cpp
static_assert(sizeof(TelemetryPacket) <= NrfLink::kMaxPayloadBytes, ...);
```

([NrfLink.cpp:15-16](src/NrfLink.cpp#L15-L16)) — если кто-то добавит в
структуру слишком много полей, прошивка просто не соберётся, а не будет
загадочно "терять" часть данных на реальном железе.

## 5. Скорость эфира и дальность — компромисс

`RADIO_SPEED` можно выставить в 250 kbps, 1 Mbps или 2 Mbps. Более низкая
скорость означает, что каждый бит "звучит" в эфире дольше — приёмнику легче
отличить сигнал от шума, поэтому дальность и устойчивость к помехам выше.
Более высокая скорость — обратный компромисс: меньше задержка, но короче
надёжная дальность. В этом компоненте выбрана `RF24_250KBPS`
([main.cpp:14](src/main.cpp#L14)) — приоритет отдан устойчивости связи, а не
скорости передачи, что разумно для учебного демонстрационного проекта.

## 6. Почему у драйвера есть свой класс поверх RF24

Библиотека `RF24` уже сама по себе полноценный низкоуровневый драйвер чипа —
она умеет говорить с ним по SPI, читать/писать регистры, включать Auto-ACK.
Класс `NrfLink` не переписывает эту работу заново — он **не наследуется** от
`RF24` и не оборачивает каждый её метод один в один. Вместо этого он
добавляет прикладной уровень поверх готового транспорта: типизированный
`TelemetryPacket` вместо голых байт, счётчики `packetsSent`/`packetsAcked`,
единый `isAlive()` для обеих ролей. Это тот же принцип, что у I2C-компонентов
этого репозитория, где `Wire` — транспорт, а класс-драйвер — прикладная логика.
