# LightHub: Модули управления и конфигурация

> Описание реальных параметров модулей, загружаемых кодом.
> Основано исключительно на анализе исходных файлов C++.

---

## Типы модулей (numbers в JSON)

| Type | Модуль | Класс | Описание |
|------|--------|-------|---------|
| 1 | Реле | out_relay | Управление электромагнитным реле |
| 3 | Мотор | out_Motor | Управление моторами с обратной связью |
| 6 | Счётчик | out_counter | Счётчик импульсов |
| 9 | PWM | out_pwm | Широтно-импульсная модуляция |
| 10 | SPI LED | out_SPILed | Адресуемые светодиоды WS2812/APA102 |
| 11 | DMX | out_dmx | DMX512 управление освещением |
| 14 | Multivent | out_Multivent | Многоканальный кондиционер/вентиляция |
| 17 | PID | out_pid | PID-регулятор |
| 20 | Счётчик v2 | out_counter | Счётчик (улучшенная версия) |
| 21 | Увлажнитель | out_humidifier | Форсуночное увлажнение |
| 22 | Меркурий | out_Mercury | Счётчик электроэнергии Меркурий |
| 50 | Лифт | out_Elevator | Управление лифтом (UART) |
| 55 | UART Bridge | out_UARTbridge | UART-мост между портами |
| 128 | AC | out_AC | Управление кондиционером |
| MBUS | Modbus | out_Modbus | Modbus устройства (general) |

---

## 1. out_counter — Счётчик импульсов

### Конфигурация (Type 20)

```json
"pumpctr": [20, [0.02, 1.2]],
"gasctr": [20, 0]
```

### Параметры массива

| Индекс | Тип | Описание | Пример |
|--------|-----|---------|--------|
| [0] | float | Импульс (накопление за период) | 0.02 л/импульс |
| [1] | float | Период накопления (сек) | 1.2 сек |

### Логика работы

1. Модуль ждёт команду (S_SET или любой CMD)
2. При получении команды с процентом > 0 запускается таймер
3. По истечении периода [1] добавляется значение [0] * TENS_BASE
4. Считанное значение публикуется в MQTT

### Пример использования

```json
{
  "items": {
    "water_meter": [20, [0.01, 5.0]],
    "gas_meter": [20, [0.1, 10.0]]
  }
}
```

---

## 2. out_relay — Электромагнитное реле

### Конфигурация

```json
"pump": [1, [pin, period]]
```

### Параметры

| Индекс | Тип | Описание | Диапазон |
|--------|-----|---------|----------|
| [0] | int | GPIO пин (отрицательное = инвертировать) | 0-54 |
| [1] | float | Период (мс) | любой > 0 |

### Логика

- HIGH (или LOW если инвертировано) = ON
- LOW (или HIGH если инвертировано) = OFF
- Если pin < 0, используется инверсия логики

### Примеры

```json
"relay_normal": [1, [33]],
"relay_inverted": [1, [-33]],
"relay_with_period": [1, [32, 5000]]
```

---

## 3. out_Motor — Управление моторами

### Конфигурация

```json
"roller_blind": [3, [pinUp, pinDown, pinFeedback, feedbackOpen, feedbackClosed, maxOnTime]]
```

### Параметры

| Индекс | Тип | Описание | Диапазон | Значение по умолч. |
|--------|-----|---------|----------|-------------------|
| [0] | int | GPIO пин UP (отрицательное = инвертировать) | 0-54 | 32 |
| [1] | int | GPIO пин DOWN | 0-54 | 33 |
| [2] | int | GPIO пин обратной связи (аналоговый) | 0-54 | 0 |
| [3] | int | Значение обратной связи (открыто) | 0-1024 | 0 |
| [4] | int | Значение обратной связи (закрыто) | 0-1024 | 1024 |
| [5] | int | Максимальное время движения (мс) | > 0 | 10000 |

### Логика работы

1. Модуль сравнивает **targetPos** (из команды 0-255) с **curPos** (из обратной связи)
2. Если разница > POS_ERR (10):
   - Если targetPos < curPos → движение вниз (pinDown)
   - Если targetPos > curPos → движение вверх (pinUp)
3. При достижении targetPos или maxOnTime мотор останавливается
4. Защита от перегрузки: одновременно работают макс. MOTOR_QUOTE моторов (обычно 2)

### Примеры

```json
"blind_motor": [3, [32, 33, 36, 0, 1023, 120000]],
"gate_motor": [3, [-10, -11, 12, 100, 900, 60000]]
```

### Управление

```
Команда: CMD_ON → движение вверх до конца
Команда: CMD_OFF → движение вниз до конца
Значение 0-255 → позиция (0=полностью открыто, 255=полностью закрыто)
```

---

## 4. out_PID — PID-регулятор

### Конфигурация

```json
"heater": [17, [
  [Kp, Ki, Kd, dT, alarmTO, alarmVal, outMin, outMax],
  {"in": {...}, "set": 22.0, "out": {...}}
]]
```

### Параметры массива [0]

| Индекс | Тип | Описание | Обязательный |
|--------|-----|---------|-------------|
| [0] | float | Kp (пропорциональный) | ✓ |
| [1] | float | Ki (интегральный) | ✓ |
| [2] | float | Kd (дифференциальный) | ✓ |
| [3] | float | dT (период сэмплирования, сек) | опционально (5 по умолч.) |
| [4] | int | Timeout будильника (сек) | опционально |
| [5] | float | Порог будильника | опционально |
| [6] | float | Минимум выхода | опционально |
| [7] | float | Максимум выхода | опционально |

### Логика

- **Kp < 0** → режим REVERSE (для охлаждения)
- **Kp > 0** → режим DIRECT (для нагрева)
- Выход ограничивается [outMin, outMax]
- dT = период опроса в секундах

### Примеры

```json
"room_temperature": [
  17,
  [
    [1.0, 0.05, 0.02, 5.0],
    {
      "in": {"emit": "sensors/room/temp"},
      "set": 21.5,
      "out": {"emit": "hvac/heating"}
    }
  ]
]
```

---

## 5. out_PWM — ШИМ управление

### Конфигурация

```json
"led_brightness": [9, [channel, freq, {}]]
```

### Параметры

| Индекс | Тип | Описание |
|--------|-----|---------|
| [0] | int | Канал / GPIO пин |
| [1] | int | Частота PWM (Hz) |

### Примеры

```json
"pwm_led": [9, [5, 1000]],
"pwm_fan": [9, [6, 500]]
```

---

## 6. out_relay (Type 1) — детально

### Загружаемые параметры (getConfig)

```cpp
pin = item->getArg(0);           // основной параметр
if (pin < 0) {
  pin = -pin;
  inverted = true;               // инвертировать логику
}
period = item->getFloatArg(1) * 1000.0;  // период в мс
if (!period) period = 5000UL;    // по умолчанию 5 сек
```

**Вывод**: модуль загружает ровно 2 параметра

---

## 7. out_counter (Type 20) — детально

### Загружаемые параметры (Poll)

```cpp
uint32_t impulse = item->getFloatArg(0) * TENS_BASE;  // [0]
uint32_t period = item->getFloatArg(1) * 1000.0;       // [1] в мс
```

**Логика**:
1. Модуль ждёт активации (getExt() = 0)
2. При получении команды ON/HEAT/COOL/etc запускает таймер
3. По истечении period добавляет impulse к накопленному значению
4. Публикует обновлённое значение

---

## 8. out_Motor (Type 3) — детально

### Загружаемые параметры (getConfig)

```cpp
pinUp = item->getArg(0);                  // [0]
pinDown = item->getArg(1);                // [1]
pinFeedback = item->getArg(2);            // [2]
feedbackOpen = item->getArg(3);           // [3]
feedbackClosed = item->getArg(4);         // [4]
maxOnTime = item->getArg(5);              // [5], по умолчанию 10000 мс
```

### Обработка отрицательных значений

```cpp
if (pinUp < 0) {
  pinUp = -pinUp;
  inverted = true;
}
if (pinDown < 0) {
  pinDown = -pinDown;
  inverted = true;
}
```

---

## 9. out_PID (Type 17) — детально

### Загружаемые параметры (getConfig)

```cpp
aJsonObject * kPIDObj = aJson.getArrayItem(item->itemArg, 0);
// Обрабатывает случаи от 1 до 8 элементов:
// [Kp]
// [Kp, Ki]
// [Kp, Ki, Kd]
// [Kp, Ki, Kd, dT]
// [Kp, Ki, Kd, dT, alarmTO]
// [Kp, Ki, Kd, dT, alarmTO, alarmVal]
// [Kp, Ki, Kd, dT, alarmTO, alarmVal, outMin]
// [Kp, Ki, Kd, dT, alarmTO, alarmVal, outMin, outMax]

if (kP < 0) {
  kP = -kP;
  direction = REVERSE;  // Отрицательный Kp = режим REVERSE
}
```

### Использование вторых параметров

```cpp
store->setpoint = item->itemVal->valuefloat;  // Установка из JSON value
```

---

## 11. out_humidifier (Type 21) — Форсуночное увлажнение

### Конфигурация

```json
"hum": [21, [
  [pumpPin, flushPin, pressurePin, maxPumpTime, idleTime, capacity, minLevel, startLevel],
  {
    "zone1": {"N": 1, "pin": 24},
    "zone2": {"N": 2, "pin": 33}
  },
  {"item": "pumpctr"},
  255,
  2
]]
```

### Параметры конфигурации [0] (массив переменной длины)

| Индекс | Тип | Описание | По умолч. | Обязателен |
|--------|-----|---------|----------|-----------|
| [0] | int | GPIO пин насоса (отрицательное = инвертировать) | 22 | ✓ |
| [1] | int | GPIO пин очистки (отрицательное = инвертировать) | 23 | опционально |
| [2] | int | GPIO пин датчика давления (аналоговый) | 54 | опционально |
| [3] | int | Макс. время работы насоса (сек) | 255 | опционально |
| [4] | int | Время ожидания между циклами (мс) | 2500 | опционально |
| [5] | int | Максимум одновременно открытых форсунок | 4 | опционально |
| [6] | int | Минимальный уровень (%) для включения форсунки | 10 | опционально |
| [7] | int | Пороговый уровень для запуска цикла (%) | 20 | опционально |

### Параметры зон (объект)

Каждая зона — это объект с полями:

```json
"zone_name": {
  "N": 1,           // номер зоны для балансировки нагрузки
  "pin": 24         // GPIO пин электромагнитного клапана форсунки
}
```

| Поле | Тип | Описание |
|------|-----|---------|
| `N` | int | Номер группы (1-4) для ограничения одновременно открытых клапанов |
| `pin` | int | GPIO пин клапана (отрицательное = инвертировать логику) |
| `set` | float | Требуемый уровень влажности (создаётся автоматически) |
| `cmd` | int | Статус команды (создаётся автоматически) |

### Счётчик импульсов

```json
{"item": "pumpctr"}  // привязка к счётчику для контроля расхода
```

### Пороги и состояния

Из кода определены константы:

```
PREPARATION_TIME = 10000 мс      // подготовка к пуску
PRESSURE_ACHIEVING_TIME = 8000 мс // время достижения давления
MIN_KEEP_TIME = 2500 мс            // минимальное время работы форсунки
FLUSH_PRESSURE = 200               // давление очистки
FLUSH_TIMEOUT = 125000 мс          // таймаут очистки
MIN_OPERATION_PRESSURE = 400       // минимальное рабочее давление
MAX_OPERATION_PRESSURE = 600       // максимальное рабочее давление
```

### Состояния модуля

| Состояние | Код | Описание |
|-----------|-----|---------|
| H_UNKNOWN | 0 | Неизвестное состояние |
| H_READY | 1 | Готов к работе |
| H_PREPARE_FOR_START | 2 | Подготовка к пуску |
| H_PREPARE_VALVES | 3 | Открытие форсунок |
| H_MOTOR_ON | 4 | Включение насоса |
| H_OPERATE | 5 | В режиме увлажнения |
| H_MOTOR_PAUSE | 6 | Пауза насоса |
| H_MOTOR_OFF | 7 | Выключение насоса |
| H_MOTOR_OFF_OK | 10 | Насос остановлен |
| H_FLUSH | 8 | Промывка системы |
| H_IDLE | 9 | Ожидание |
| H_FAULT_PRESSURE | -1 | Ошибка давления |
| H_FAULT_PRESSURE_KEEP | -2 | Потеря давления во время работы |
| H_FAULT_OVERPRESSURE | -3 | Перепад давления |
| H_FAULT_MOTOR_OFF | -4 | Отказ насоса |

### Логика работы

1. **Инициализация**: модуль ждёт команду включения или когда уровень влажности > startLevel
2. **Подготовка**: открывает клапаны форсунок, ждёт достижения давления
3. **Работа**: включает насос, поддерживает давление в диапазоне [MIN_OPERATION_PRESSURE, MAX_OPERATION_PRESSURE]
4. **Балансировка**: одновременно открыто не более `capacity` форсунок из группы `N`
5. **Очистка**: периодическая промывка при давлении FLUSH_PRESSURE
6. **Контроль**: отключение при падении давления ниже MIN_OPERATION_PRESSURE

### Примеры конфигурации

#### Простая система увлажнения

```json
{
  "items": {
    "humidifier": [21, [
      [22, 23, 54, 255, 2500, 4, 10, 20],
      {
        "bedroom": {"N": 1, "pin": 24},
        "living": {"N": 2, "pin": 25}
      },
      {"item": "waterctr"}
    ]]
  }
}
```

#### Многозональная система (из официального примера)

```json
{
  "items": {
    "hum": [21, [
      [22, 15, 30, 4, 10, 20],
      {
        "bedr1": {"N": 1, "pin": 24},
        "ktc1": {"N": 1, "pin": 25},
        "dinner1": {"N": 1, "pin": 9},
        "bedr2m": {"N": 1, "pin": 8},
        "bedr21": {"N": 1, "pin": 11},
        "bedr22": {"N": 1, "pin": 12},
        "zal2": {"N": 2, "pin": 33},
        "cab3": {"N": 1, "pin": 32},
        "zal3": {"N": 4, "pin": 31},
        "bedr3": {"N": 1, "pin": 30}
      },
      {"item": "pumpctr"},
      255,
      2
    ]]
  }
}
```

---

## 12. out_Mercury (Type 22) — Счётчик электроэнергии

### Конфигурация

```json
"merc": [22, [
  0,                    // [0] адрес Modbus (обычно на выходе item)
  9600,                 // [1] скорость UART (baud)
  "8N1",                // [2] параметры кадра
  2,                    // [3] уровень доступа
  [2,2,2,2,2,2],       // [4] пароль (массив из 6 чисел)
  10000,                // [5] период опроса (мс)
  {}                    // [6] параметры (обычно пусто)
]]
```

### Параметры массива

| Индекс | Тип | Назначение |
|--------|-----|-----------|
| [0] | int | Адрес устройства (из item->getArg(0)) |
| [1] | int | Скорость UART |
| [2] | string | Параметры (8N1, 8E1, 8O1) |
| [3] | int | Уровень доступа (обычно 2) |
| [4] | array/string | Пароль (6 символов или массив 6 чисел) |
| [5] | int | Интервал опроса (мс) |
| [6] | object | Дополнительные параметры |

### Примеры

```json
"electricity_meter": [22, [
  1,
  9600,
  "8N1",
  2,
  [0x01, 0x02, 0x03, 0x04, 0x05, 0x06],
  60000,
  {}
]]
```

---

## 13. out_AC (Type 128) — Управление кондиционером

### Конфигурация

```json
"ac": [128, [
  portNum,          // [0] номер UART порта (опционально)
  {parameters}      // параметры управления
]]
```

### Загружаемые параметры

```cpp
portNum = item->getArg(0);  // 0=Serial, 1=Serial1, 2=Serial2, 3=Serial3
```

### Поддерживаемые команды

Модуль работает с 37-байтовым протоколом:

| Байт | Назначение |
|------|-----------|
| 13 | Текущая температура |
| 17 | Статус команды |
| 23 | Режим (04=DRY, 01=COOL, 02=HEAT, 00=AUTO, 03=FAN) |
| 25 | Скорость вентилятора |
| 27 | SWING |
| 28 | Блокировка пульта |
| 29 | Питание |
| 31 | Fresh |
| 35 | Установка температуры |

---

## 14. out_Modbus (тип MBUS) — Modbus устройства

### Конфигурация общая

```json
"modbus": {
  "device_name": {
    "baud": 9600,
    "serial": "8N1",
    "poll": {
      "regs": [[0, 40], [100, 20]],
      "irs": [[0, 10]],
      "coils": [0, 1],
      "dins": [0],
      "delay": 5000
    },
    "par": {
      "parameter_name": {
        "reg": 0,
        "type": "i16",
        "id": 1,
        "map": {}
      }
    }
  }
}
```

### Items использование Modbus

```json
"device_instance": [14, [
  "device_name",
  {
    "power": {"emit": "home/ac/power"},
    "temp": {"emit": "home/ac/temp"}
  }
]]
```

### Загружаемые параметры

```cpp
// From template:
baud = baudObj->valueint;              // скорость (по умолчанию 9600)
serialParam = str2SerialParam(...);    // формат (по умолчанию 8N1)

// Poll strategy:
pollingRegisters = aJson.getObjectItem(pollObj, "regs");   // блоки Holding Reg
pollingIrs = aJson.getObjectItem(pollObj, "irs");           // Input Reg
pollingCoils = aJson.getObjectItem(pollObj, "coils");       // Coil Reg
poolingDiscreteIns = aJson.getObjectItem(pollObj, "dins");  // Discrete In
pollingInterval = delayObj->valueint;  // по умолчанию 1000 мс
```

### Типы данных в параметрах

| type | Описание |
|------|---------|
| i16 | int16 |
| i32 | int32 |
| u16 | uint16 |
| u32 | uint32 |
| i8h | старший байт |
| i8l | младший байт |
| u8h | старший байт unsigned |
| u8l | младший байт unsigned |
| x10 | умножить на 10 |
| 100 | умножить на 100 |

### Параметры в "par"

```json
"parameter": {
  "reg": 0,              // Holding Register номер
  "ir": 0,               // Input Register номер
  "coil": 0,             // Coil номер
  "type": "i16",         // тип данных
  "id": 1,               // ID команды
  "map": {               // маппинг значений
    "cmd": [["OFF",0], ["ON",1]],
    "val": null,
    "def": "acmode"
  }
}
```

---

## 15. out_Multivent (Type 14) — Многоканальный кондиционер

### Конфигурация

```json
"vac": [14, [
  "haier",           // имя шаблона Modbus
  {
    "": {            // центральная установка
      "val": {"emit": "home/ac/temp"},
      "mode": {"emit": "home/ac/mode"}
    },
    "zone1": {       // зона 1
      "pid": [490, 100, 9879, 40],
      "set": 21.0,
      "fan": 0,
      "cmd": 14,
      "item": "ac_zone1/set"
    }
  }
]]
```

### Параметры зоны

| Параметр | Тип | Описание |
|----------|-----|---------|
| `pid` | [Kp, Ki, Kd, dT] | Коэффициенты PID |
| `set` | float | Требуемая температура |
| `fan` | int (0-255) | Текущий процент открытия |
| `cmd` | int | Текущая команда |
| `item` | string | Привязка к другому item |
| `V` | int | Номинальный объём (для балансировки) |

---

## Практические примеры конфигурации

### Система со счётчиками

```json
{
  "items": {
    "pumpctr": [20, [0.02, 1.2]],
    "gasctr": [20, 0.1],
    "waterctr": [20, 0]
  },
  "in": [
    {"#": 41, "item": "waterctr/set", "scmd": "%0.01", "rcmd": ""},
    {"#": 39, "item": "gasctr/set", "scmd": "%0.1", "rcmd": ""}
  ]
}
```

### Система с реле и мотором

```json
{
  "items": {
    "pump": [1, [33]],
    "blind": [3, [32, 33, 36, 0, 1023, 120000]]
  }
}
```

### PID регулятор

```json
{
  "items": {
    "heater": [17, [
      [1.0, 0.05, 0.02, 5.0],
      {
        "in": {"emit": "sensors/temp"},
        "set": 22.0,
        "out": {"emit": "heating/power"}
      }
    ]]
  }
}
```

---

## Ключевые правила конфигурации

1. **Инверсия логики**: отрицательное значение пина = инвертировать логику
2. **Единицы времени**: 
   - counter/relay: float конвертируется в миллисекунды (* 1000)
   - mercury: миллисекунды напрямую
3. **Параметры по умолчанию**:
   - relay period = 5000 мс
   - motor maxOnTime = 10000 мс
   - modbus baud = 9600
   - modbus polling = 1000 мс
4. **Массивы переменной длины**: PID и Mercury поддерживают опциональные параметры

---

**Документ основан на анализе C++ кода без домыслов**  
**Версия: 1.0**  
**Дата: 2026-01-21**
1. * 1. 