Skip to content

Бинарный формат UART протокола iDryer

Все структуры упакованы с #pragma pack(1) — выравнивание отсутствует, байты идут подряд. Все многобайтовые числа — little-endian (младший байт первым).


Кадр (Frame)

[SOF 1B][VER 1B][FLAGS 1B][KIND 1B][SEQ 1B][LEN 1B][PAYLOAD 0-200B][CRClo 1B][CRChi 1B]
Offset Size Type Поле Значение / Описание
0 1 uint8 sof Всегда 0xAA
1 1 uint8 version Всегда 1
2 1 uint8 flags Битовые флаги (см. ниже)
3 1 uint8 kind Тип сообщения MessageKind
4 1 uint8 sequence Счётчик 0–255, инкремент при каждом кадре
5 1 uint8 payloadLength Длина payload (0–200)
6 0-200 bytes payload Данные (структура зависит от kind)
6+LEN 2 uint16 LE crc CRC16-CCITT poly=0x1021 init=0xFFFF, little-endian

Флаги (byte 2):

Бит Маска Константа Описание
0 0x01 ACK_REQUIRED Требует подтверждения (ACK)
1 0x02 IS_ACK Это кадр ACK
2 0x04 ERROR Payload содержит ErrorPayload
3 0x08 FRAGMENTED Это промежуточный фрагмент
4 0x10 LAST_FRAGMENT Это последний фрагмент

HelloPayload (0x01) — 86 байт

Offset Size Type Поле Описание
0 1 uint8 role 0x01=MCU, 0x02=ESP, 0xFF=HelloRequest (триггер)
1 1 uint8 deviceType Тип продукта (DeviceType). См. таблицу ниже. 0=Unknown/legacy
2 2 _pad1 Выравнивание
4 4 uint32 LE firmwareVersion MAJOR<<16 | MINOR<<8 | PATCH
8 4 uint32 LE workTimeCounter Наработка, секунды
12 8 char[8] hardwareVersion ASCII строка ("v1.0\0...")
20 1 uint8 unitsCount Количество юнитов (0–4)
21 48 UnitConfig[4] units Конфигурация юнитов (4 × 12 байт)
69 17 char[17] mcuSerial Flash ID RP2040, HEX-строка + \0

UnitConfig (12 байт, вложена в HelloPayload):

Offset Size Type Поле Описание
0 1 uint8 unitId 0–3
1 1 _pad1 Выравнивание
2 2 uint16 LE capabilities Битовые флаги hardware
4 4 uint8[4] scales Индексы датчиков весов, 0xFF=не используется
8 4 uint8[4] rfid Индексы RFID-ридеров, 0xFF=не используется

Значения deviceType:

Значение Имя Описание
0x00 Unknown Legacy / поле не заполнено. Портал трактует как dryer
0x01 Dryer iDryer. Кол-во камер портал вычисляет из unitsCount
0x02 Heater iHeater
0x03 Telemetry Модуль телеметрии
0x04 Link Универсальный LINK (standalone)
0x05 LinkII Специализированный LINK для iHeater
0x06..0xFF Резерв

Флаги capabilities:

Бит Маска Описание
0 0x0001 HEATER
1 0x0002 FAN
2 0x0004 SERVO
3 0x0008 RH_AIR_SENSOR
4 0x0010 TEMP_AIR_SENSOR
5 0x0020 TEMP_HEATER_SENSOR
6–15 Зарезервировано

HelloAckPayload (0x02) — 37 байт

Offset Size Type Поле Описание
0 4 uint32 LE ipAddress IP адрес (little-endian), 0=нет подключения
4 33 char[33] ssid Имя WiFi сети, null-terminated, ""=нет

ipAddress в little-endian: для IP 192.168.1.5 → байты 05 01 A8 C0.


TelemetryPayload (0x10) — 1 + N×7 байт

Offset Size Type Поле Описание
0 1 uint8 count Количество юнитов (1–4)
1 N×7 TelemetryEntry[N] units Данные юнитов

TelemetryEntry (7 байт):

Offset Size Type Поле Описание
0 1 uint8 unitId 0–3
1 2 int16 LE temperatureC10 Температура × 10 (553 → 55.3°C)
3 2 uint16 LE humidityPct10 Влажность × 10 (452 → 45.2%)
5 1 uint8 heaterPowerPct Мощность нагревателя 0–100%
6 1 uint8 fanOn 0=выкл, 1=вкл

WeightsPayload (0x12) — 1 + N×4 байт

Offset Size Type Поле Описание
0 1 uint8 count Количество датчиков (1–4)
1 N×4 WeightEntry[N] weights Данные весов

WeightEntry (4 байт):

Offset Size Type Поле Описание
0 1 uint8 sensorId 0–3 (W1=0 ... W4=3)
1 1 uint8 unitId 0–3
2 2 uint16 LE weightGramsC10 Вес × 10 (1234 → 123.4 г)

StatusPayload (0x13) — 133 байт

Offset Size Type Поле Описание
0 1 uint8 count Количество юнитов (1–4)
1 N×32 StatusEntry[N] units Статус юнитов
1+N×32 4 uint32 LE uptime Аптайм устройства, секунды

StatusEntry (32 байт):

Offset Size Type Поле Описание
0 1 uint8 unitId 0–3
1 1 uint8 mode DryerMode: 0=Idle, 1=Drying, 2=Storage, 3=Profile, 4=Fault
2 4 uint32 LE sessionNum Номер сессии (0=Idle/Fault)
6 2 int16 LE targetTempC10 Целевая температура × 10
8 2 uint16 LE targetHumidityPct Целевая влажность % (0=не используется)
10 2 uint16 LE durationMinutes Длительность, мин (0=бесконечно)
12 4 uint32 LE elapsedSeconds Секунд с начала режима
16 4 uint32 LE stageElapsedSeconds Секунд на текущем этапе (PROFILE)
20 4 uint32 LE stageRemainingSeconds Секунд до конца этапа (PROFILE)
24 4 uint32 LE totalRemainingSeconds Секунд до конца программы
28 1 uint8 currentStage Текущий этап (PROFILE, 0-based)
29 1 uint8 totalStages Всего этапов (PROFILE)
30 1 uint8 stagePhase StagePhase: 0=Ramp, 1=Hold
31 1 _pad Выравнивание

RfidPayload (0x14) — 37 байт

Offset Size Type Поле Описание
0 1 uint8 event RfidEvent: 1=TagDetected, 2=TagRemoved
1 1 uint8 readerId 0–3
2 32 char[32] tag HEX ID метки, null-terminated (пусто для TagRemoved)
34 1 uint8 unitId 0–3
35 2 _pad[2] Выравнивание

CommandPayload (0x20) — 13 байт

Offset Size Type Поле Описание
0 1 uint8 command CommandCode (см. ниже)
1 1 uint8 targetState Используется для Start (тип режима)
2 1 uint8 unitId 0–3 или 0xFF=все юниты
3 2 reserved[2] Зарезервировано
5 4 uint32 LE arg0 Аргумент (Start: температура × 10)
9 4 uint32 LE arg1 Аргумент (Start: минуты)

CommandCode:

Код Имя Описание
0x01 Start Запуск (arg0=temp×10, arg1=minutes, targetState=DryerMode)
0x02 Stop Остановка
0x03 Find Поиск (мигание)
0x05 GetConfig Запрос JSON конфига
0x06 SetConfig Применить настройки
0x07 ReadRfid Прочитать метку
0x08 WriteRfid Не реализовано
0x10 ResetFault Сброс ошибки
0x11 WifiStatus Запрос IP (MCU → LINK)
0x12 ClearErrors Очистка EEPROM лога

ProfilePayload внутри Command (0x20) — 64 байта

Один и тот же MessageKind::Command (0x20) используется для обычной команды и для профильной сушки. Приёмник (MCU) различает формат по длине payload: 13 байт → CommandPayload, 64 байтаProfilePayload. Логика в uart_bridge.cpp.

Offset Size Type Поле Описание
0 1 uint8 unitId 0–3 (U1–U4) или согласно прошивке
1 1 uint8 totalStages Число используемых этапов (1–10)
2 1 uint8 startStage Индекс старта (0-based)
3 1 _pad Выравнивание
4 60 ProfileStage[10] stages До 10 этапов по 6 байт

ProfileStage (6 байт):

Offset Size Type Поле Описание
0 2 uint16 LE temp Целевая температура ×10 (60°C → 600)
2 2 uint16 LE ramp Разгон до цели, секунды
4 2 uint16 LE hold Удержание, секунды

JSON-команда MQTT и семантика этапов: 07-profile-mode.md.


ConfigChunkPayload (0x30) — 200 байт

Offset Size Type Поле Описание
0 2 uint16 LE transferId ID передачи (для разделения сессий)
2 2 uint16 LE totalSize Полный размер JSON (только в первом фрагменте, chunkIndex==0)
4 2 uint16 LE chunkIndex Индекс фрагмента: 0, 1, 2, ... N-1
6 194 bytes data JSON-данные (до 194 байт)

Если JSON ≤ 194 байт — отправляется одним кадром с FLAG_LAST_FRAGMENT. Если JSON > 194 байт — фрагментируется: промежуточные с FLAG_FRAGMENTED, последний с FLAG_LAST_FRAGMENT.

Форматы JSON в data:

  • MCU→LINK, полный конфиг: {"v":8,"units":3,"active":0,"lang":"en","menu":[{"id":3,"t":"val","val":[50,65,85]},...]}
  • MCU→LINK, delta: {"d":{"3":[55,60,55],"81":3}}
  • LINK→MCU, set: {"cmd":"set","id":3,"unit":0,"val":55}
  • LINK→MCU, invoke: {"cmd":"invoke","id":5}

HeartbeatPayload (0x40) — 9 байт

Offset Size Type Поле Описание
0 4 uint32 LE uptimeSeconds Аптайм, секунды
4 2 int16 LE wifiRssiDbm RSSI (ESP→MCU) или температура MCU (MCU→ESP)
6 2 uint16 LE errorsSinceBoot Количество ошибок с перезапуска
8 1 uint8 cloudState LinkCloudState (0–7, только ESP→MCU)

LinkCloudState:

Значение Имя
0 Idle
1 WifiConnecting
2 Provisioning
3 Registering
4 AwaitingClaim
5 Ready
6 MqttConnecting
7 Online

errorsSinceBoot: uint16, счётчик с момента сброса питания/рестарта на стороне отправителя кадра. Семантика (ошибки приложения MCU, сбои линка UART и т.д.) задаётся прошивкой отправителя; у референсного Link значение в кадре LINK→MCU не совпадает с полем в кадре MCU→LINK — см. 01-uart.md и 03-mqtt/01-mqtt.md.

wifiRssiDbm: при LINK→MCU — RSSI WiFi в dBm (типично отрицательное число). При MCU→LINK — по соглашению прошивки часто температура MCU или иной показатель; точное значение не задаётся одним числом для всех продуктов.


LogPayload (0x60) — 164 байта

Кадр MessageKind::Log. Типичное направление: MCU→LINK (структурированные события/ошибки приложения контроллера). Строки — C-строки с завершающим \0 в пределах поля; неиспользуемый хвост заполняют нулями.

Offset Size Type Поле Описание
0 10 char[10] severity Уровень: например critical, error, warning, info
10 20 char[20] source Источник: THERMISTOR, HEATER, …
30 32 char[32] event Код события: SENSOR_SHORT, OVER_MAX, …
62 100 char[100] message Текст для человека
162 1 uint8 unitId 0–3 (юнит U1–U4)
163 1 uint8 _pad Выравнивание

В uart_bridge.cpp для Log допускается payloadLength от 0 до MAX_PAYLOAD_SIZE (200); референсный Link в IdryerDevice обрабатывает пакет как LogPayload только если длина не меньше sizeof(LogPayload) (164).

Публикация в MQTT events из бинарного Log не выполняется внутри UartBridge; нужен колбэк setLogHandler и вызов MqttClient::publishEvent в прошивке продукта (в референсном Link — handleLog). См. 01-mqtt.md.


AckPayload (0x11, 0x21, 0x31) — 2 байт

Offset Size Type Поле Описание
0 1 uint8 ackSequence SEQ подтверждаемого кадра
1 1 uint8 status ErrorCode: 0=OK

ErrorPayload (0x50) — 4 байт

Offset Size Type Поле Описание
0 1 uint8 code ErrorCode (см. ниже)
1 1 uint8 lastSequence SEQ кадра, вызвавшего ошибку
2 2 uint16 LE detail Доп. информация (ожидаемая/фактическая длина)

ErrorCode:

Код Имя Описание
0x00 None OK
0x01 CrcMismatch CRC не совпал
0x02 UnknownMessage Неизвестный MessageKind
0x03 InvalidPayload Неверный размер/формат payload
0x04 Busy Устройство занято
0x05 Timeout ACK не получен за 700ms × 3
0x06 SequenceMismatch Неожиданный номер последовательности

ClaimStatusPayload (0x71) — 18 байт

Offset Size Type Поле Описание
0 1 uint8 status ClaimingStatus: 0=Idle, 1=Provisioning, 2=WaitingClaim, 3=Claimed, 4=Error
1 9 char[9] pin PIN-код 8 цифр + \0 (пусто если не WaitingClaim)
10 4 uint32 LE expiresAt Unix timestamp истечения PIN
14 4 uint32 LE remainingSeconds Секунд до истечения PIN

ClaimCompletePayload (0x72) — 38 байт

Offset Size Type Поле Описание
0 1 uint8 success 1=успех, 0=ошибка/таймаут
1 37 char[37] deviceId UUID устройства (только для отображения)

WsEnablePayload (0x73) — 4 байт

Offset Size Type Поле Описание
0 1 uint8 enable 1=включить WS, 0=выключить
1 1 reserved
2 2 uint16 LE pin PIN 0–9999 (4 цифры, генерируется на MCU)

WsStatusPayload (0x74) — 6 байт

Offset Size Type Поле Описание
0 1 uint8 state WsState: 0=Disabled, 1=Listening, 2=Connected
1 2 uint16 LE pin PIN 0–9999
3 1 uint8 pairedCount Привязанных клиентов (0–5)
4 1 uint8 maxClients Максимум клиентов (5)
5 1 reserved

Примечания

  • Все структуры объявлены с #pragma pack(push, 1) / #pragma pack(pop) — нет неявного выравнивания компилятора.
  • static_assert в uart_protocol.h гарантируют соответствие размеров структур.
  • При добавлении новых полей — соблюдать pack(1) и обновлять static_assert.