# Modbus Map Документ составлен по прошивке: - `..\new rev\john103C8T6\Modbus\modbus_config.h` - `..\new rev\john103C8T6\Modbus\modbus_data.h` - `..\new rev\john103C8T6\Modbus\modbus_data.c` - `..\new rev\john103C8T6\Core\Src\main.c` - `..\new rev\john103C8T6\Core\Inc\PROJ_setup.h` Адреса ниже указаны в 0-based формате, как они используются в прошивке. В некоторых Modbus-терминалах эти же адреса отображаются как `30001`, `40001`, `00001` и т.п. В таком терминале к адресу обычно надо прибавить `1` и выбрать нужный тип таблицы. ## Общие параметры | Параметр | Значение | |---|---| | Протокол | Modbus RTU | | Slave ID | `3` | | Slave UART | `USART1`, `PB6 TX`, `PB7 RX` | | Slave UART настройки | `115200`, `8N1` | | Slave таймер | `TIM2` | | Master UART | `USART2`, `PA2 TX`, `PA3 RX` | | Master UART настройки | `115200`, `8N1` | | Master таймер | `TIM4` | | `MAX_SENSE` | `32` | | Включенные таблицы | Coils, Holding Registers, Input Registers | ## Input Registers, function `0x04` Базовый массив: `MB_DATA.InRegs`, адресный диапазон прошивки `0..1999`. | Адрес | Кол-во | Имя | Тип | Описание | |---:|---:|---|---|---| | `0` | `32` | `sens_Temp[0..31]` | `uint16_t` | Температура DS18B20, в коде записывается как `temperature * 10`. Рекомендуется читать как `int16_t / 10.0`, если возможны отрицательные температуры | | `32` | `968` | `reserve` | `uint16_t[]` | Резерв до адреса `999` | | `1000` | `128` | `ID.DevAddr[32][8]` | raw bytes as registers | ROM-коды DS18B20: 32 датчика по 8 байт. Для датчика `N`: базовый регистр `1000 + N * 4` | | `1128` | `72` | `reserve1` | `uint16_t[]` | Резерв до адреса `1199` | | `1200` | `1` | `num_Tsens` | `uint16_t` | Количество найденных датчиков DS18B20 | | `1201` | `1` | `rtc.hours` | `uint16_t` | Текущие часы RTC | | `1202` | `1` | `rtc.minutes` | `uint16_t` | Текущие минуты RTC | | `1203` | `1` | `rtc.seconds` | `uint16_t` | Текущие секунды RTC | | `1204` | `1` | `rtc.date` | `uint16_t` | День месяца | | `1205` | `1` | `rtc.month` | `uint16_t` | Месяц | | `1206` | `1` | `rtc.year` | `uint16_t` | Год в формате RTC проекта | | `1207` | `1` | `rtc.weekday` | `uint16_t` | День недели | | `1208` | `1` | `rtc.apply` | `uint16_t` | Для input-регистров обычно `0` | | `1209` | `1` | `rtc.status` | `uint16_t` | Статус RTC | | `1210` | `790` | reserved | `uint16_t[]` | Адреса доступны в общем диапазоне, прикладного поля нет | ## Holding Registers, functions `0x03`, `0x06`, `0x10` Базовый массив: `MB_DATA.HoldRegs`, адресный диапазон прошивки `0..1999`. | Адрес | Кол-во | Имя | Тип | Описание | |---:|---:|---|---|---| | `0` | `32` | `set_Temp[0..31]` | `uint16_t` | Уставки температуры для датчиков. В текущем коде используются как градусы без умножения на 10 | | `32` | `68` | `reserve` | `uint16_t[]` | Резерв до адреса `99` | | `100` | `32` | `set_hyst[0..31]` | `uint16_t` | Гистерезис температуры для датчиков. В текущем коде используется как градусы | | `132` | `68` | `reserve1` | `uint16_t[]` | Резерв до адреса `199` | | `200` | `1` | `rtc.hours` | `uint16_t` | Часы для установки RTC | | `201` | `1` | `rtc.minutes` | `uint16_t` | Минуты для установки RTC | | `202` | `1` | `rtc.seconds` | `uint16_t` | Секунды для установки RTC | | `203` | `1` | `rtc.date` | `uint16_t` | День месяца | | `204` | `1` | `rtc.month` | `uint16_t` | Месяц | | `205` | `1` | `rtc.year` | `uint16_t` | Год в формате RTC проекта | | `206` | `1` | `rtc.weekday` | `uint16_t` | День недели | | `207` | `1` | `rtc.apply` | `uint16_t` | Записать `1`, чтобы применить время RTC | | `208` | `1` | `rtc.status` | `uint16_t` | Статус установки RTC | | `209` | `1791` | reserved | `uint16_t[]` | Адреса доступны в общем диапазоне, прикладного поля нет | RTC status: | Значение | Имя | Описание | |---:|---|---| | `0` | `MB_RTC_STATUS_IDLE` | Нет операции | | `1` | `MB_RTC_STATUS_SET_OK` | Время установлено | | `2` | `MB_RTC_STATUS_VALUE_ERROR` | Ошибка значения времени/даты | | `3` | `MB_RTC_STATUS_HAL_ERROR` | Ошибка HAL RTC | ## Coils, functions `0x01`, `0x05`, `0x0F` Базовый массив: `MB_DATA.Coils`, адресный диапазон прошивки `0..999`. | Coil address | Кол-во | Имя | Описание | |---:|---:|---|---| | `0` | `48` | `coils[0..2]` | Общие управляющие биты `state_val_01..state_val_16` в трех 16-битных словах | | `20` | `1` | `coils[1].state_val_05` | Управляет `PA10 / Relay_dc5v` в основном цикле | | `48` | `80` | `reserve_coils` | Резерв до coil `127` | | `128` | `32` | `status_tSens[0..1]` | Статусы подключения DS18B20: `Temp1_isConnected..Temp32_isConnected` | | `160` | `96` | `reserve_status_tSens` | Резерв до coil `255` | | `256` | `16` | `relay_struct_on` | Расчетные биты включения реле по датчикам `Temp1..Temp16` | | `272` | `16` | `reserve_relay_struct_on` | Резерв | | `288` | `16` | `relay_struct_off` | Расчетные биты выключения реле по датчикам `Temp1..Temp16` | | `304` | `80` | reserved | Резерв до coil `383` | | `384` | `1` | `init_param` | При записи `1` прошивка применяет `set_Temp[]` и `set_hyst[]`, затем сбрасывает бит | | `385` | `1` | `init_Tsens` | При записи `1` прошивка повторно ищет DS18B20, затем сбрасывает бит | | `386` | `1` | `Save_Param_to_Flash` | Поле объявлено, активного использования в текущем `main.c` не найдено | | `387` | `13` | reserved2 | Резервные биты | | `400` | `600` | reserved | Адреса доступны в общем диапазоне, прикладного поля нет | ## Привязка температур и реле В `value_control()` прошивка сравнивает `sens[i].temperature` с уставкой и гистерезисом: - если `temperature < set_temp - hyst`, выставляется `relay_struct_off` для датчика `i`; - если `temperature > set_temp + hyst`, выставляется `relay_struct_on` для датчика `i`; - если `temperature == set_temp`, оба бита для датчика сбрасываются. Эти расчетные биты лежат в coils `256..271` и `288..303`. Прямая аппаратная привязка этих расчетных битов к GPIO-выходам в текущем `main.c` не найдена. Прямое управление GPIO найдено только для `PA10 / Relay_dc5v` через coil `20`. ## GUI binding to the real STM project Source project checked: `..\new rev\john103C8T6`. The PC GUI bridge is bound to the actual STM Modbus layout from: - `Modbus\modbus_config.h` - `Modbus\modbus_data.h` - `Core\Src\main.c` - `Core\Inc\ds18b20_driver.h` - `Core\Inc\PROJ_setup.h` Active STM settings: | Item | Value | |---|---:| | Default slave ID | `3` | | `MAX_SENSE` in STM | `32` | | GUI channels used | first `16` | | STM protocol on COM | Modbus RTU | | Optional GUI network mode | Modbus TCP gateway/device with the same register map | GUI runtime map: | GUI value | STM source | Modbus function | 0-based address | |---|---|---:|---:| | Current temperature channel `N` | `MB_DATA.InRegs.sens_Temp[N]` | `0x04` | `0 + N` | | DS18B20 ROM ID channel `N` | `MB_DATA.InRegs.ID.DevAddr[N][8]` | `0x04` | `1000 + N * 4` | | Sensor connected channel `N` | `MB_DATA.Coils.status_tSens` | `0x01` | `128 + N` | | Setpoint channel `N` | `MB_DATA.HoldRegs.set_Temp[N]`, value = `degC * 10` | `0x03` / `0x06` | `0 + N` | | Apply setpoints | `MB_DATA.Coils.init_param` | `0x05` | `384` | | Open command/state channel `N` | `MB_DATA.Coils.relay_struct_on` | `0x01` / `0x05` | `256 + N` | | Close command/state channel `N` | `MB_DATA.Coils.relay_struct_off` | `0x01` / `0x05` | `288 + N` | Notes: - GUI channel `1` uses index `N = 0`; GUI channel `16` uses index `N = 15`. - Setpoints are written as tenths of a degree: GUI `28.5°C` -> Modbus holding value `285`; readback `285` -> GUI `28.5°C`. - DS18B20 IDs are stored by STM as `uint8_t DevAddr[32][8]` and exposed through 16-bit input registers. The bridge restores each register in little-endian byte order to display the ROM ID correctly. - The current STM project does not expose analog valve position or opening angle registers. GUI position/angle are therefore derived from binary relay state: open = `100% / 90°`, close = `0% / 0°`. - `value_control()` in `Core\Src\main.c` calculates `relay_struct_on/off` from temperature, setpoint, and hysteresis. Direct manual coil writes from GUI can be overwritten by that firmware logic unless STM firmware adds a manual override register/coil. ## STM room/channel structure added for GUI Added to Keil STM project `..\new rev\john103C8T6`. ### Input registers: room status block Base address: `400`. One room/channel uses `18` registers. Channel `N` uses base `400 + N * 18`, where GUI channel 1 is `N = 0`. | Offset | Field | Scale / meaning | |---:|---|---| | 0 | `channel` | 1-based channel number | | 1 | `location` | numeric location code | | 2..5 | `ds18b20_id[4]` | 8-byte DS18B20 ROM ID, little-endian bytes per register | | 6 | `temperature_x10` | current temperature, degC * 10 | | 7 | `setpoint_x10` | setpoint, degC * 10 | | 8 | `hysteresis_x10` | hysteresis, degC * 10 | | 9 | `valve_position_pct` | valve opening percent, 0..100 | | 10 | `valve_angle_deg` | opening angle in degrees | | 11 | `valve_angle_max_deg` | max opening angle, default 90 | | 12 | `is_connected` | DS18B20 connected flag | | 13 | `valve_open` | open relay/command state | | 14 | `valve_close` | close relay/command state | | 15 | `mode` | 0 auto, 1 manual | | 16 | `command_state` | 0 stop, 1 open, 2 close | | 17 | `reserved` | reserved | ### Holding registers: room control block Base address: `300`. One room/channel uses `8` registers. Channel `N` uses base `300 + N * 8`. | Offset | Field | Scale / meaning | |---:|---|---| | 0 | `setpoint_x10` | writable setpoint, degC * 10 | | 1 | `hysteresis_x10` | writable hysteresis, degC * 10 | | 2 | `valve_position_pct` | writable manual opening percent, 0..100 | | 3 | `valve_angle_max_deg` | max opening angle, default 90 | | 4 | `mode` | 0 auto, 1 manual | | 5 | `command` | 0 stop, 1 open, 2 close | | 6 | `location` | numeric location code | | 7 | `apply` | write 1 to apply room setpoint/hysteresis | Compatibility: - Legacy `set_Temp[0..31]` at holding `0..31` is kept. - Legacy `set_hyst[0..31]` at holding `100..131` is kept. - Legacy coils `256..271` and `288..303` are kept. - The Python bridge writes both the legacy map and the new room control block.