Перейти к содержанию

HMI — Локальный интерфейс

Локальная панель управления: 2 кнопки + 7-сегментный дисплей. Позволяет смотреть и менять пропорцию канала без сети.

Источник: src/hmi.cpp.


1. Железо

Компонент Спецификация
Контроллер LED TM1650, bit-bang протокол через 2 GPIO
Дисплей 2 физические цифры (по 2 LED на сегмент — левый/правый)
Кнопки K1 (down/prev), K2 (up/next) — pull-up, нажатие = LOW

Пины (см. include/config.h)

Сигнал GPIO Направление Назначение
CLK 18 OUT Тактирование TM1650
DAT 23 OUT (OD) Данные TM1650 (нужен внешний pull-up)
KEYM (K1) 33 IN (pull-up) Левая кнопка — «–», предыдущий
KEYP (K2) 32 IN (pull-up) Правая кнопка — «+», следующий

Особенность дисплея

Физически 2 цифры, но TM1650 управляет 4 GRID-выходами. Каждая цифра состоит из двух «полу-цифр»: GRID1+GRID2 → левая, GRID3+GRID4 → правая. В коде это скрыто: showNumber(N, dp_pos) пишет одно и то же значение в обе позиции каждой цифры, плюс точку (dp) ставит в одной из трёх позиций (0, 1, 2) — это служит индикатором номера активного канала.


2. Режимы

Конечный автомат:

stateDiagram-v2
    [*] --> IDLE
    IDLE --> VALUE_EDIT: K2 long (≥500 мс)
    VALUE_EDIT --> IDLE: BOTH short (одновременное нажатие)
    IDLE --> ALARM: hmiSetAlarm() из кода
    ALARM --> IDLE: hmiClearAlarm()

IDLE — основной режим

  • Дисплей: пропорция выбранного канала (00…99).
  • K1 short — переключить на предыдущий канал (с wraparound).
  • K2 short — переключить на следующий канал.
  • K2 long (≥500 мс) — войти в VALUE_EDIT редактировать пропорцию текущего канала.
  • K1 hold ≥1 сек — purge / принудительная продувка выбранного канала на сохранённой пропорции. Дозатор работает пока удерживается кнопка, останавливается при отпускании. Эквивалент DISPENSE-команды по REST API, локально с панели.

Покрытие purge: требует чтобы (1) канал был включён в конфигурации, (2) сохранённая пропорция была > 0, (3) HMI-редактирование не было заблокировано (dosator:hmi_edit_lock = false, флажок «разрешить изменение с панели» в Web UI → Система → Дозаторы). Если хоть одно условие нарушено — purge не стартует, нажатие игнорируется.

Остановка purge триггерится также любым изменением состояния: переход HMI из IDLE в ALARM (например, поднятая внешняя авария), случайное нажатие K2 одновременно с K1, потеря питания. Это защита от «прилипшей» принудительной сессии.

dp-точки (позиция 0/1/2 = канал 0/1/2) ведут себя по-разному в зависимости от того, идёт ли дозирование:

Ситуация Поведение dp
Дозирования нет dp выбранного кнопками канала горит ровно (индикатор выбора)
Идёт дозирование dp каждого дозирующего канала мигает 500/500 мс (индикатор активности); кнопки на dp не влияют

То есть когда канал реально льёт, оператор видит мигающую точку этого канала на железке, независимо от того, какой канал выбран кнопками.

VALUE_EDIT — редактирование пропорции

  • Дисплей: редактируемое значение мигает 500 мс / 500 мс, точка на позиции редактируемого канала.
  • K2 short — +1 (0…99, clamp на верхнем краю).
  • K2 long + auto-repeat (200 мс) — +10.
  • K1 short — -1 (clamp на 0).
  • K1 long + auto-repeat — -10.
  • BOTH short (одновременное короткое) — сохранить в NVS и вернуться в IDLE. После сохранения — три вспышки всего дисплея 100 мс / 100 мс как индикация подтверждения.

ALARM — авария

  • Дисплей: E<код> (например E03), мигает 500/500 мс.
  • Кнопки игнорируются.
  • Активируется вызовом hmiSetAlarm(code) из других модулей.
  • Снимается hmiClearAlarm() (например, после устранения причины или сброса через API).

3. Тайминги (config.h)

Параметр Значение Описание
HMI_TASK_TICK_MS 10 мс Период task'а HMI
HMI_BTN_DEBOUNCE_MS 20 мс Антидребезг кнопок
HMI_BTN_LONG_MS 500 мс Порог short vs long
HMI_BTN_BOTH_LONG_MS 3000 мс Долгое нажатие обоих кнопок (BOTH_LONG)
HMI_BTN_AUTOREPEAT_MS 200 мс Автоповтор в long-режиме
HMI_BTN_PURGE_MS 1000 мс K1-hold для purge (принудительная продувка)
HMI_BLINK_ON_MS 500 мс Мигание VALUE_EDIT/ALARM, фаза ON
HMI_BLINK_OFF_MS 500 мс Мигание, фаза OFF
HMI_FLASH_ON_MS 100 мс Вспышка подтверждения, ON
HMI_FLASH_OFF_MS 100 мс Вспышка подтверждения, OFF
HMI_FLASH_COUNT 3 Количество вспышек

4. События

HMI публикует и слушает события через esp_event:

Публикует (HMI_EVENTS)

Событие Когда Payload
HMI_EVT_CHANNEL_CHANGE Сменили канал кнопками {ch}
HMI_EVT_STATE_CHANGE IDLE / VALUE_EDIT / ALARM {state}
HMI_EVT_PROPORTION_CHANGE Сохранили новую пропорцию (BOTH short) {ch, value}

Слушает (DOSING_EVENTS)

Событие Эффект
DOSING_EVT_PROPORTION_CHANGE Обновляет s_proportion[ch] → дисплей моментально перерисовывается

Это даёт двунаправленную синхронизацию HMI и Web: меняешь пропорцию кнопкой — Web подхватит через GET /api/dosator/status; меняешь через POST /api/dosator/config — HMI обновит дисплей по событию.


5. Яркость дисплея

Яркость TM1650 (1…7) хранится в NVS, но не настраивается в runtime — управляется только boot-логикой:

  1. hmiPreInit() вызывается сразу после Serial.begin() — гасит TM1650 (setupDisplay(false) + clearDisplay). Это критично при warm-reboot: чип сохраняет состояние с прошлой сессии, без гашения дал бы пик тока.
  2. hmiInit() поднимает дисплей на минимальной яркости (1).
  3. Через HMI_BOOT_RAMP_DELAY_MS (2 с) задача HMI поднимает яркость до сохранённого в NVS значения.

Цель — ограничить пиковый ток при включении. HMI_DEFAULT_BRIGHTNESS = 1.

Runtime-API яркости и UI-элемент сознательно убраны — менять яркость оператору незачем, а dim-on-boot важнее.


6. NVS

Namespace hmi:

Ключ Тип Default Описание
active_ch uchar 0 Канал, который показывается при старте (последний выбранный)
hmi_bright uchar 1 Яркость дисплея (1…7), применяется через 2с после boot

Подробнее — nvs-reference.md.


7. Шпаргалка оператора

Хочу… Кнопки
Посмотреть пропорцию канала Короткое K1 или K2 — листать каналы
Изменить пропорцию текущего Долго K2 → войти, K1/K2 ±1, K2 long ±10
Сохранить изменения Одновременно K1+K2 коротко (трижды мигнёт)
Отменить редактирование (нет отмены — выйти можно только сохранением; чтобы вернуть старое значение — выставь его снова)
Продуть дозатор K1 удерживать ≥1 сек в IDLE на нужном канале — пока держишь, льёт; отпустил — стоп. Требует hmi_edit_lock = false, пропорцию > 0, канал включён.

Точки на дисплее (позиция 0/1/2 = канал 0/1/2):

  • дозирования нет — ровно горит точка выбранного канала (где сейчас стоит «листалка» каналов);
  • идёт дозирование — мигают точки тех каналов, что реально льют (можно несколько разом), независимо от выбора кнопками.

Двузначное число — пропорция выбранного канала.