Files
2026-10-31 08:06:35 +03:00

446 lines
34 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Описание компонента zh_avr_bme280_sensor
## Общие сведения
**zh_avr_bme280_sensor** — прошивка для AVR-микроконтроллеров ATMega168/328, реализующая беспроводной датчик климата на базе датчика BME280 и трансивера nRF24L01+.
Компонент работает в режиме глубокого сна (Power-down), просыпается по таймеру (watchdog), измеряет температуру, влажность и давление на датчике BME280 (интерфейс I2C) и передаёт на шлюз пакет с идентификатором устройства и уровнем заряда батареи. Идентификатор устройства получается автоматически по воздуху в процедуре сопряжения со шлюзом и сохраняется в EEPROM.
### Основные возможности
1. **Сверхнизкое энергопотребление** — около 5 мкА в режиме сна.
2. **Измерение климата** — температура, влажность и атмосферное давление датчиком BME280 по шине I2C.
3. **Автоматическое сопряжение (pairing)** — запрос идентификатора устройства у шлюза по воздуху, хранение ID в EEPROM.
4. **Передача уровня заряда батареи** — измерение напряжения через внутренний АЦП с опорным напряжением 1,1 В.
5. **Работа по протоколу nRF24L01+** — передача и приём через SPI, адресная доставка на шлюз, динамическая длина пакета.
6. **Поддержка нескольких МК** — ATMega168/168P/168PB/328/328P/328PB.
7. **Пробуждение по watchdog-таймеру** — WDT с интервалом ~8 секунд, цикл работы каждые `WDT_WAKE_COUNT` (38) прерываний ≈ 5 минут.
8. **Автономная работа** — не требует внешних подтягивающих резисторов (используются внутренние pull-up).
---
## Внутреннее устройство
### Структура данных
Компонент не использует динамическое выделение памяти. Сырые показания хранятся в статических переменных, калибровочные коэффициенты BME280 — в массиве `_bme280_calib[33]`. Перед передачей данные сериализуются функцией `zh_nrf24_sensor_build_payload()` из внешней библиотеки `zh_nrf24_config` в буфер `uint8_t[ZH_NRF24_MAX_PAYLOAD_SIZE]` (32 байта) в формате little-endian.
#### Формат передаваемого пакета
Данные формируются в глобальной переменной `_transmitted_data` типа `zh_nrf24_sensor_transmitted_data_t` из библиотеки `zh_nrf24_config`, затем сериализуются в полезную нагрузку. Для типа `ZH_NRF24_SENSOR_BME280` полный размер пакета — 12 байт (заголовок id + type + battery и три поля измерений, каждое `int16_t` в little-endian).
| Поле | Тип | Описание |
| ------ | ----- | ---------- |
| `id` | `int16_t` | Идентификатор устройства, назначенный шлюзом при сопряжении |
| `type` | `int16_t` | Тип устройства (`ZH_NRF24_SENSOR_BME280`) |
| `battery` | `int16_t` | Напряжение батареи × 100 (например, 330 = 3.30 В) |
| `value_1` | `int16_t` | Влажность (% × 10, например 453 = 45.3 %) |
| `value_2` | `int16_t` | Температура (°C × 10, например 253 = 25.3 °C) |
| `value_3` | `int16_t` | Давление (мм рт. ст. × 10) |
Тип устройства задаётся значением перечисления `ZH_NRF24_SENSOR_BME280` из библиотеки `zh_nrf24_config`.
#### Формат пакета сопряжения
Пакет сопряжения — структура `zh_nrf24_sensor_pairing_data_t` (5 байт, `ZH_NRF24_PAIRING_PACKET_SIZE`):
| Поле | Тип | Описание |
| ------ | ----- | ---------- |
| `type` | `uint8_t` | Тип датчика (код `ZH_NRF24_SENSOR_BME280`) |
| `magic` | `uint8_t` | Маркер направления: `ZH_NRF24_PAIRING_REQ_MAGIC` (0xA5) в запросе, `ZH_NRF24_PAIRING_ACK_MAGIC` (0x5A) в ответе |
| `token_hi` | `uint8_t` | Старший байт псевдослучайного токена |
| `token_lo` | `uint8_t` | Младший байт псевдослучайного токена |
| `id` | `uint8_t` | Идентификатор, назначенный шлюзом (в ответе) |
### Конфигурационные макросы
| Макрос | Значение | Описание |
| -------- | ---------- | ---------- |
| `GATEWAY_CHANNEL` | `120` | Радиоканал шлюза |
| `GATEWAY_ADDRESS` | `0xDDEEFF` | 3-байтовый адрес шлюза |
| `EEPROM_DEVICE_ID_ADDR` | `(uint8_t *)0x00` | Адрес ячейки EEPROM для хранения ID устройства |
| `DEVICE_ID_UNASSIGNED` | `0xFF` | Признак отсутствующего (несопряжённого) идентификатора |
| `PAIRING_TIMEOUT_ATTEMPTS` | `7` | Максимальное число попыток сопряжения |
| `WDT_WAKE_COUNT` | `38` | Количество прерываний WDT до цикла измерения (≈ 5 минут) |
| `TWI_TWBR` | `32` | Значение регистра TWBR для задания скорости аппаратного TWI |
| `BME280_ADDR` | `0x76` | 7-битный адрес датчика BME280 на шине I2C |
### Управление памятью
Компонент не использует динамическую память. Все данные хранятся в статической области и регистрах микроконтроллера. Глобальная переменная `_transmitted_data` заполняется при старте (тип устройства, затем идентификатор из EEPROM или из сопряжения). В цикле обновляются поля `battery`, `value_1` (влажность), `value_2` (температура), `value_3` (давление). Идентификатор устройства сохраняется в EEPROM адресом `0x00` (`eeprom_update_byte`).
---
## API (интерфейс)
Все функции являются `static` и доступны только внутри модуля `zh_avr_bme280_sensor.c`.
### Инициализация
| Функция | Описание |
| --------- | ---------- |
| `_zh_wdt_start()` | Запуск watchdog-таймера в режиме прерывания с указанным таймаутом (биты WDP3:0) |
| `_zh_wdt_stop()` | Остановка watchdog-таймера |
| `_zh_i2c_init()` | Инициализация аппаратного TWI (I2C) на пинах PC4 (SDA), PC5 (SCL), TWBR = 32 |
| `_zh_bme280_init()` | Инициализация датчика BME280: проверка CHIP_ID, сброс, чтение калибровки, настройка режима |
| `_zh_spi_init()` | Инициализация аппаратного SPI в режиме мастера (Fosc/16) |
| `_zh_nrf24_init()` | Инициализация трансивера nRF24L01+: адреса TX/RX, канал, ретрансмиты, динамические пакеты, CRC |
| `_zh_nrf24_interrupt_init()` | Настройка внешнего прерывания INT0 (PD2, падающий фронт) для сигнала IRQ трансивера |
### Идентификатор устройства и сопряжение
| Функция | Описание |
| --------- | ---------- |
| `_zh_load_device_id()` | Чтение ID устройства из EEPROM (возвращает `DEVICE_ID_UNASSIGNED` при 0x00/0xFF) |
| `_zh_save_device_id()` | Сохранение ID устройства в EEPROM через `eeprom_update_byte` |
| `_zh_get_random_token()` | Генерация 16-битного псевдослучайного токена из MCUSR и шума АЦП (никогда не 0) |
| `_zh_do_pairing()` | Неблокирующая процедура сопряжения: запрос → ожидание ACK в RX-режиме → backoff; возвращает назначенный ID |
| `_zh_enter_final_sleep()` | Переход в постоянный сон Power-down до сброса (отключение прерываний, WDT, радио, тактов периферии) |
### Измерение
| Функция | Описание |
| --------- | ---------- |
| `_zh_get_battery_voltage()` | Измерение напряжения батареи через внутренний АЦП с опорой 1,1 В (две конверсии, усреднение) |
### Чтение данных BME280
| Функция | Описание |
| --------- | ---------- |
| `_zh_bme280_read()` | Перевод BME280 в forced mode, чтение 8 сырых байт и распаковка ADC значений температуры, давления, влажности |
| `_zh_bme280_compensate_temperature()` | Компенсация сырой температуры по калибровочным коэффициентам (результат в °C) |
| `_zh_bme280_compensate_pressure()` | Компенсация сырого давления по калибровочным коэффициентам (результат в Па) |
| `_zh_bme280_compensate_humidity()` | Компенсация сырой влажности по калибровочным коэффициентам (результат в %) |
### Передача данных nRF24L01+
| Функция | Описание |
| --------- | ---------- |
| `_zh_nrf24_send()` | Передача буфера: Power Up, `FLUSH_TX`, `W_TX_PAYLOAD`, импульс CE, ожидание IRQ с таймаутом ~100 мс, сброс статуса |
| `_zh_nrf24_send_no_wait()` | Запуск передачи без ожидания завершения (импульс CE 15 мкс), завершение сигнализируется IRQ |
| `_zh_nrf24_enter_rx()` | Переход в режим приёма на pipe 0 (PRIM_RX, очистка FIFO, CE) |
| `_zh_nrf24_enter_power_down()` | Перевод трансивера в режим Power Down |
### Низкоуровневые SPI-операции
| Функция | Описание |
| --------- | ---------- |
| `_zh_spi_transfer()` | Обмен одного байта по SPI (полный дуплекс) |
| `_zh_nrf24_write_register()` | Запись одного байта в регистр nRF24L01+ |
| `_zh_nrf24_write_buff_register()` | Запись многобайтового буфера в регистр nRF24L01+ |
| `_zh_nrf24_write_buffer()` | Запись буфера данных с указанной командой (CSN управляется вручную через PB2) |
| `_zh_nrf24_send_command()` | Отправка однобайтовой команды и чтение статуса |
| `_zh_nrf24_read_payload()` | Чтение принятой полезной нагрузки из RX FIFO (`R_RX_PAYLOAD`) |
| `_zh_nrf24_read_payload_width()` | Чтение длины пакета в RX FIFO (`R_RX_PL_WID`) |
### Низкоуровневые TWI-операции (аппаратный I2C)
| Функция | Описание |
| --------- | ---------- |
| `_zh_i2c_wait()` | Ожидание флага `TWINT` с таймаутом 65535 итераций (возвращает false при зависании шины) |
| `_zh_i2c_start()` | Генерация стартового (или повторного стартового) условия, проверка статусов `0x08`/`0x10` |
| `_zh_i2c_stop()` | Генерация стоп-условия с ограниченным ожиданием сброса бита `TWSTO` |
| `_zh_i2c_write()` | Передача одного байта через `TWDR`, проверка статусов ACK (`0x18`, `0x28`, `0x40`) |
| `_zh_i2c_read_ack()` | Чтение байта из `TWDR` с подтверждением ACK (возвращает `0xFF` при таймауте) |
| `_zh_i2c_read_nack()` | Чтение последнего байта из `TWDR` без подтверждения NACK (возвращает `0xFF` при таймауте) |
### Служебные TWI-функции BME280
| Функция | Описание |
| --------- | ---------- |
| `_zh_bme280_write_reg()` | Запись одного байта в регистр BME280 (start, адрес, регистр, данные, stop, задержка 2 мс) |
| `_zh_bme280_read_reg()` | Чтение нескольких байт из регистров BME280 (repeated-start, ACK/NACK) |
### Статические переменные модуля
Все переменные объявлены `static` и доступны только внутри модуля.
| Переменная | Объявление | Описание |
| --------- | ---------- | ---------- |
| `_wdt_wake_count` | `static volatile uint8_t` | Счётчик прерываний WDT, инкрементируется в `ISR(WDT_vect)` до предела `WDT_WAKE_COUNT` |
| `_nrf_event` | `static volatile bool` | Флаг события трансивера, выставляется в `ISR(INT0_vect)` и сбрасывается в машине состояний сопряжения |
| `_bme280_calib` | `static uint8_t [33]` | Калибровочные коэффициенты BME280, загружаются при инициализации датчика |
| `_bme280_temp_fine` | `static int32_t` | Промежуточное значение `t_fine`, общее для компенсаторов давления и влажности |
| `_bme280_present` | `static bool` | Признак доступности датчика BME280 (сбрасывается при ошибке чтения, восстанавливается повторной инициализацией) |
| `_transmitted_data` | `zh_nrf24_sensor_transmitted_data_t` | Глобальная (в пределах модуля) структура формируемого пакета |
---
## Потокобезопасность
Компонент работает на bare-metal AVR без RTOS. Точки синхронизации — два ISR:
- `ISR(WDT_vect)` — увеличивает `_wdt_wake_count` (объявлен как `static volatile uint8_t`, инкремент защищён проверкой верхнего предела), заново включает бит `WDIE`.
- `ISR(INT0_vect)` — выставляет флаг `_nrf_event` (`static volatile bool`) при фронте на пине IRQ трансивера.
Главный цикл и машина состояний сопряжения проверяют эти флаги после выхода из сна. Атомарная запись одного байта на AVR гарантирует отсутствие состояний гонки. Весь остальной обмен с периферией (I2C, SPI, АЦП) выполняется последовательно в главном цикле.
---
## Алгоритмы
### Алгоритм загрузки идентификатора и сопряжения
- При старте читается ID из EEPROM (`eeprom_read_byte`, адрес `0x00`); значения `0x00` и `0xFF` считаются признаком несопряжённого устройства.
- При отсутствии ID выполняется `_zh_do_pairing()` — неблокирующая машина состояний (`ZH_NRF24_PAIR_SEND` → `ZH_NRF24_PAIR_TX_WAIT` → `ZH_NRF24_PAIR_RX_WAIT` → `ZH_NRF24_PAIR_BACKOFF`):
- генерируется токен (`_zh_get_random_token()` из MCUSR и шума АЦП);
- отправляется запрос `zh_nrf24_sensor_pairing_data_t` (magic `0xA5`) без ожидания (`_zh_nrf24_send_no_wait`);
- после TX_DS трансивер переводится в RX-режим, ожидание ответа со сном и WDT-таймаутом 250 мс;
- ответ валидируется по magic `0x5A`, типу, токену и ненулевому ID; ширина пакета проверяется через `R_RX_PL_WID`;
- при неудаче — backoff-пауза ~4 с (WDT), не более `PAIRING_TIMEOUT_ATTEMPTS` (7) попыток.
- Назначенный ID сохраняется в EEPROM и записывается в `_transmitted_data.id`.
- После инициализации выполняется пробная сборка пакета `zh_nrf24_sensor_build_payload()`; при нулевом результате (неизвестный тип) — переход в постоянный сон.
### Алгоритм инициализации BME280
- Задержка 100 мс после включения.
- Проверка `CHIP_ID` (регистр `BME280_REG_ID`, ожидается `0x60`).
- Сброс записью `0xB6` в регистр `BME280_REG_RESET`, задержка 10 мс.
- Чтение калибровочных коэффициентов: 26 байт из `BME280_REG_CALIB00` и 7 байт из `BME280_REG_CALIB26` (всего 33 байта, массив `_bme280_calib`).
- Конфигурация влажности: `ctrl_hum = 0x01` (oversampling x1).
- Конфигурация режима: `config = 0x00` (фильтр off, standby off).
- Запуск измерения: `ctrl_meas = 0x25` (temp/pressure oversampling x16, forced mode) в `_zh_bme280_read()` перед каждым чтением.
### Алгоритм компенсации BME280
Сырые ADC-значения пересчитываются в физические величины по алгоритмам производителя из datasheet BME280.
- **Температура**: `_zh_bme280_compensate_temperature()` — вычисляет `_bme280_temp_fine` (используется для давления и влажности), возвращает результат в °C.
- **Давление**: `_zh_bme280_compensate_pressure()` — возвращает результат в Па. При `var1 == 0` (знаменатель ноль) возвращает 0.0.
- **Влажность**: `_zh_bme280_compensate_humidity()` — результат ограничивается диапазоном [0, 419430400] на промежуточном шаге, затем делится на 1024.0 и возвращается в процентах.
### Алгоритм аппаратного TWI
- Инициализация: `TWSR = 0x00` (предделитель 1), `TWBR = TWI_TWBR` (32), включение модуля битом `TWEN`. Скорость: `F_CPU / (16 + 2·TWBR·prescaler)` ≈ 100 кГц при 8 МГц.
- Стартовое/повторное стартовое условие: установка `TWSTA`, ожидание `TWINT`, проверка статуса (`0x08` — START, `0x10` — REPEATED START).
- Передача байта: запись в `TWDR`, сброс `TWINT`, ожидание завершения, проверка статусов подтверждения (`0x18`/`0x28` — данные, `0x40` — адрес чтения).
- Приём байта: установка `TWEA` для всех байт кроме последнего, чтение `TWDR` после `TWINT`.
- Останов: установка `TWSTO`, ограниченное ожидание (1000 итераций) сброса бита аппаратно.
- Обработка ошибок: каждая операция возвращает `false` при таймауте ожидания или неверном статусе, вызывающая сторона выполняет `_zh_i2c_stop()`.
### Алгоритм передачи через nRF24L01+
- Переход в Power Up (бит `PWR_UP` в CONFIG).
- Ожидание 5 мс (время стартования осциллятора).
- Очистка TX FIFO командой `FLUSH_TX`.
- Загрузка полезной нагрузки командой `W_TX_PAYLOAD` (размер пакета определяет `zh_nrf24_sensor_build_payload()`, динамические пакеты включены: `FEATURE = EN_DPL`, `DYNPD = DPL_P0`).
- Импульс CE для запуска передачи.
- Ожидание завершения по пину IRQ (PD2) с таймаутом ~100 мс.
- Сброс флагов статуса, очистка FIFO, переход в Power Down.
Для процедуры сопряжения используется вариант без блокировки (`_zh_nrf24_send_no_wait`, импульс CE 15 мкс) с завершением по прерыванию INT0.
### Алгоритм сна и пробуждения
- Инициализация WDT: прерывание по таймауту ~8 секунд (`WDT_T_8S`), без сброса MCU.
- ISR `WDT_vect`: увеличивает `_wdt_wake_count` (до предела `WDT_WAKE_COUNT`), заново включает бит `WDIE`.
- Главный цикл: переход в Power-down (`sleep_cpu()`). После пробуждения проверяется `_wdt_wake_count >= WDT_WAKE_COUNT` (38 × 8 с ≈ 5 минут).
- При достижении порога: обнуление счётчика, повторная инициализация BME280 (если датчик недоступен), чтение показаний, сериализация пакета и передача через nRF24L01+.
- Первое измерение выполняется сразу после инициализации и сопряжения, до входа в цикл.
- `_zh_enter_final_sleep()` — аварийный постоянный сон: отключение прерываний, WDT, радио, АЦП, TWI, SPI и тактов всей неиспользуемой периферии (PRR).
---
## Примеры использования
### Типовой цикл работы (main)
```c
int main(void)
{
_transmitted_data.type = ZH_NRF24_SENSOR_BME280;
MCUSR &= ~(1 << WDRF);
wdt_disable();
cli();
_zh_i2c_init();
_bme280_present = _zh_bme280_init();
_zh_spi_init();
_zh_nrf24_init();
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sei();
uint8_t device_id = _zh_load_device_id();
if (device_id == DEVICE_ID_UNASSIGNED) {
device_id = _zh_do_pairing();
}
_transmitted_data.id = (int16_t)device_id;
// Пробная сборка пакета: неизвестный тип — переход в постоянный сон
{
uint8_t probe[ZH_NRF24_MAX_PAYLOAD_SIZE];
if (zh_nrf24_sensor_build_payload(&_transmitted_data, probe,
sizeof(probe)) == 0) {
_zh_enter_final_sleep();
}
}
_zh_wdt_start(WDT_T_8S);
int32_t adc_t = 0, adc_p = 0, adc_h = 0;
// Первое измерение сразу после инициализации и сопряжения
if (_bme280_present == true) {
if (_zh_bme280_read(&adc_t, &adc_p, &adc_h) == true) {
_transmitted_data.battery =
(int16_t)lroundf(_zh_get_battery_voltage() * 100.0);
_transmitted_data.value_2 =
(int16_t)lroundf(_zh_bme280_compensate_temperature(adc_t) * 10.0);
_transmitted_data.value_3 =
(int16_t)lroundf(_zh_bme280_compensate_pressure(adc_p) * 0.00750062 * 10.0);
_transmitted_data.value_1 =
(int16_t)lroundf(_zh_bme280_compensate_humidity(adc_h) * 10.0);
uint8_t payload[ZH_NRF24_MAX_PAYLOAD_SIZE];
uint8_t size = zh_nrf24_sensor_build_payload(&_transmitted_data,
payload, sizeof(payload));
if (size > 0) {
_zh_nrf24_send(payload, size);
}
} else {
_bme280_present = false;
}
}
for (;;)
{
sleep_enable();
sleep_cpu();
sleep_disable();
if (_wdt_wake_count >= WDT_WAKE_COUNT) {
_wdt_wake_count = 0;
if (_bme280_present == false) {
_bme280_present = _zh_bme280_init();
}
if (_bme280_present == true) {
if (_zh_bme280_read(&adc_t, &adc_p, &adc_h) == true) {
_transmitted_data.battery =
(int16_t)lroundf(_zh_get_battery_voltage() * 100.0);
_transmitted_data.value_2 =
(int16_t)lroundf(_zh_bme280_compensate_temperature(adc_t));
_transmitted_data.value_3 =
(int16_t)lroundf(_zh_bme280_compensate_pressure(adc_p) * 0.00750062);
_transmitted_data.value_1 =
(int16_t)lroundf(_zh_bme280_compensate_humidity(adc_h));
uint8_t payload[ZH_NRF24_MAX_PAYLOAD_SIZE];
uint8_t size = zh_nrf24_sensor_build_payload(&_transmitted_data,
payload, sizeof(payload));
if (size > 0) {
_zh_nrf24_send(payload, size);
}
} else {
_bme280_present = false;
}
}
}
}
}
```
### Изменение конфигурации под другое устройство
```c
// Идентификатор устройства назначается шлюзом при сопряжении
// и хранится в EEPROM (адрес 0x00). Для повторного сопряжения
// обнулите ячейку EEPROM.
#define GATEWAY_CHANNEL 120
#define GATEWAY_ADDRESS 0xDDEEFF
// Тип устройства задаётся перечислением ZH_NRF24_SENSOR_BME280
```
### Подключение по SPI и I2C (схема выводов)
```c
// Назначение пинов AVR:
// nRF24L01+ (SPI):
// PB2 -> CSN (управление SPI-селектом, выход)
// PB3 -> MOSI (назначается аппаратным SPI)
// PB5 -> SCK (назначается аппаратным SPI)
// PB1 -> CE (управление передачей, выход)
// PD2 -> IRQ (вход, активный низкий, INT0, с подтяжкой к VCC)
// BME280 (I2C, аппаратный TWI):
// PC4 -> SDA (аппаратный TWI, внутренний pull-up)
// PC5 -> SCL (аппаратный TWI, внутренний pull-up)
```
---
## Логирование
Логирование отсутствует.
---
## Ошибки
Компонент не возвращает коды ошибок. Функции `_zh_bme280_init`, `_zh_bme280_read`, `_zh_bme280_write_reg`, `_zh_bme280_read_reg` возвращают `bool` (true при успехе). `_zh_load_device_id` возвращает `DEVICE_ID_UNASSIGNED` при отсутствии сохранённого ID. Остальные функции имеют тип `void` или возвращают однобайтовый статус SPI (`uint8_t`).
| Условие | Поведение |
| --------- | ---------- |
| Нулевое показание АЦП | `_zh_get_battery_voltage()` возвращает 0.0 |
| Неверный `CHIP_ID` BME280 | `_zh_bme280_init()` возвращает false, флаг `_bme280_present = false`, при следующем цикле предпринимается повторная попытка инициализации |
| Таймаут ожидания `TWINT` | `_zh_i2c_wait()` возвращает false, операция TWI прерывается |
| Неверный статус TWI (NACK, ошибка шины) | `_zh_i2c_start()` / `_zh_i2c_write()` возвращают false, вызывающая функция выполняет `_zh_i2c_stop()` |
| Таймаут при чтении TWI | `_zh_i2c_read_ack()` / `_zh_i2c_read_nack()` возвращают `0xFF` |
| Ошибка чтения BME280 | `_zh_bme280_read()` возвращает false, пакет не формируется, `_bme280_present = false` |
| `var1 == 0` при компенсации давления | `_zh_bme280_compensate_pressure()` возвращает 0.0 |
| Превышение таймаута передачи (~100 мс) | Цикл ожидания IRQ прерывается, передача может быть неполной |
| Нет ответа шлюза при сопряжении | После 7 попыток (`PAIRING_TIMEOUT_ATTEMPTS`) — переход в постоянный сон до сброса |
| Ширина принятого пакета ≠ 5 байт / > 32 байт | FIFO очищается, состояние сопряжения переходит в backoff |
| `zh_nrf24_sensor_build_payload()` вернул 0 | Неизвестный тип датчика или слишком мал буфер — переход в постоянный сон |
---
## Ограничения
1. **Широковещательная передача данных** — приём используется только на этапе сопряжения; шлюз не подтверждает пакеты измерений на уровне приложения.
2. **Отсутствие шифрования** — CRC включён, но AES-шифрование не используется. Пакеты могут быть перехвачены.
3. **Жёстко зашитый адрес шлюза** — смена адреса или канала требует перепрошивки (ID устройства при этом хранится в EEPROM).
4. **Таймаут передачи ~100 мс** — если шлюз недоступен, передача прерывается. Аппаратные ретрансмиты nRF24L01+ (`SETUP_RETR = 0xFF`: 15 попыток с интервалом до 500 мс) включены.
5. **Точность измерения батареи** — зависит от стабильности внутреннего опорного напряжения 1,1 В.
6. **Программные задержки в TWI** — обмен с BME280 выполняется на аппаратном TWI, но ожидание `TWINT` реализовано поллингом с таймаутом; прерывания TWI не используются.
7. **Задержка между измерениями ~5 минут** — определяется произведением `WDT_WAKE_COUNT` (38) на интервал WDT (~8 секунд). В течение этого времени пакет не передаётся.
8. **Давление в мм рт. ст.** — `_zh_bme280_compensate_pressure()` возвращает давление в Паскалях, для перевода в пакет умножается на `0.00750062` (коэффициент пересчёта Па → мм рт. ст.).
9. **Масштабирование значений ×10 только в первом цикле** — при первом измерении температура, влажность и давление умножаются на 10 (одна дробная цифра); в цикле пробуждения значения записываются без масштабирования (см. `main`).
10. **Сопряжение требует шлюза** — без ответившего шлюза устройство после 7 попыток уходит в постоянный сон до сброса/перепрошивки.
---
## Требования для сборки
- PlatformIO с плагином `platform-atmelavr`
- AVR-GCC (входит в состав PlatformIO)
- Библиотека `zh_nrf24_config` (ветка `dev`, подключается через `lib_deps` в `platformio.ini`)
- Программатор USBasp (или совместимый)
- Фьюзы: LFUSE=`0xE2`, HFUSE=`0xDF`, EFUSE=`0x07` (8 МГц, без делителя)
---
## Установка
### Сборка и прошивка через PlatformIO
```bash
pio run -e ATmega328P -t upload
```
### Подключение в проекте
Компонент является самодостаточной прошивкой — не подключается как библиотека. Для интеграции в другой проект скопируйте `src/zh_avr_bme280_sensor.c`, добавьте зависимость `zh_nrf24_config` и адаптируйте макросы конфигурации.
```ini
; platformio.ini
lib_deps =
https://github.com/aZholtikov/zh_nrf24_config#dev
```
```c
// Зависимости: стандартные заголовки AVR-libc, математика и конфигурация протокола
#include "avr/io.h"
#include "avr/interrupt.h"
#include "avr/sleep.h"
#include "avr/eeprom.h"
#include "avr/wdt.h"
#include "util/delay.h"
#include "stdbool.h"
#include "math.h"
#include "zh_nrf24_config.h"
```