TČ Samsung přes MIM-B19N: endpoint 172.16.1.17, plný poll, registry doc
- V096: endpoint home-01 TČ z placeholderu 192.168.1.103 na reálný Waveshare RS485 TO POE ETH (B) 172.16.1.17:502; telemetry_heat_pump.room_temp_c. - R__048: fn_telemetry_heat_pump_sample rozšířena (water_inlet, room_temp, defrost, alarm_code) — drop/comment bez parametrů dle konvence. - poll_heat_pump: místo TODO stubu (zapisoval dummy 45/55 °C!) skutečné čtení MIM bloku 50-75 + defrost reg 2; gate na comm_status ready (jinak skip); operating_mode off/heat/cool/auto/dhw/error; power_w NULL (MIM příkon nemá). - docs/04-modules/modbus-registers-mim-b19n.md (mapa, 9600 8E1, DIP adresa, troubleshooting E6xx) + heat-pump.md odkaz. Živý stav: TCP :502 OK, Modbus bez odpovědi (čeká na protokol převodníku / paritu EVEN / polaritu A-B — checklist v docu). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -418,6 +418,43 @@ async def poll_ev_chargers(site_id: int, db: asyncpg.Connection) -> None:
|
|||||||
asyncio.create_task(_on_ev_departure(site_id, str(code)))
|
asyncio.create_task(_on_ev_departure(site_id, str(code)))
|
||||||
|
|
||||||
|
|
||||||
|
# Samsung MIM-B19N(T) — Modbus RTU slave za RS485→TCP (9600 8E1!).
|
||||||
|
# Adresace: vnitřní jednotka IU má blok base = 50 + IU*50; zde IU 0 → 50..99.
|
||||||
|
# Plný popis: docs/04-modules/modbus-registers-mim-b19n.md
|
||||||
|
MIM_IU_BASE = 50 # blok vnitřní jednotky 0
|
||||||
|
MIM_OFF_COMM_STATUS = 0 # b0 exist, b1 type OK, b2 ready, b3 comm error
|
||||||
|
MIM_OFF_UNIT_TYPE = 1 # lower byte: 110=HE, 115-117=EHS, 120=HT
|
||||||
|
MIM_OFF_ONOFF = 2
|
||||||
|
MIM_OFF_MODE = 3 # 0 auto, 1 cool, 4 heat
|
||||||
|
MIM_OFF_ROOM_TEMP = 9 # °C×10 signed
|
||||||
|
MIM_OFF_ERROR_CODE = 13 # 0 = OK, 100-999 kód
|
||||||
|
MIM_OFF_WATER_IN = 15 # °C×10 signed
|
||||||
|
MIM_OFF_WATER_OUT = 16 # °C×10 signed
|
||||||
|
MIM_OFF_DHW_ONOFF = 22
|
||||||
|
MIM_OFF_DHW_TEMP = 25 # °C×10 (zásobník TUV)
|
||||||
|
MIM_REG_DEFROST = 2 # modulový registr: 0=off, jinak defrost
|
||||||
|
MIM_MODE_NAMES = {0: "auto", 1: "cool", 2: "dry", 3: "fan", 4: "heat"}
|
||||||
|
|
||||||
|
|
||||||
|
def _mim_temp_c(raw: int) -> float | None:
|
||||||
|
"""°C×10, signed 16bit; MIM drží 0 dokud jednotka hodnotu nedodá."""
|
||||||
|
v = raw - 65536 if raw > 32767 else raw
|
||||||
|
return round(v / 10.0, 1)
|
||||||
|
|
||||||
|
|
||||||
|
def mim_operating_mode(on: int, mode: int, dhw_on: int, comm_ready: bool, error: int) -> str:
|
||||||
|
if not comm_ready:
|
||||||
|
return "offline"
|
||||||
|
if error:
|
||||||
|
return "error"
|
||||||
|
parts = []
|
||||||
|
if int(on) == 1:
|
||||||
|
parts.append(MIM_MODE_NAMES.get(int(mode), f"mode{mode}"))
|
||||||
|
if int(dhw_on) == 1:
|
||||||
|
parts.append("dhw")
|
||||||
|
return "+".join(parts) if parts else "off"
|
||||||
|
|
||||||
|
|
||||||
async def poll_heat_pump(site_id: int, db: asyncpg.Connection) -> None:
|
async def poll_heat_pump(site_id: int, db: asyncpg.Connection) -> None:
|
||||||
rows = await db.fetch(
|
rows = await db.fetch(
|
||||||
"""
|
"""
|
||||||
@@ -430,18 +467,54 @@ async def poll_heat_pump(site_id: int, db: asyncpg.Connection) -> None:
|
|||||||
measured_at = datetime.now(timezone.utc)
|
measured_at = datetime.now(timezone.utc)
|
||||||
for row in rows:
|
for row in rows:
|
||||||
code = row["code"]
|
code = row["code"]
|
||||||
logger.info("TODO: heat pump Modbus registry pending (heat_pump=%s)", code)
|
host = row["host"]
|
||||||
|
port = int(row["port"] or 502)
|
||||||
|
unit_id = int(row["unit_id"] if row["unit_id"] is not None else 1)
|
||||||
|
try:
|
||||||
|
client = await get_modbus_client(host, port)
|
||||||
|
async with client.batch(unit_id) as mb:
|
||||||
|
iu = await mb.read_holding_registers(MIM_IU_BASE, 26)
|
||||||
|
defrost_raw = await mb.read_register(MIM_REG_DEFROST)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("heat_pump %s: Modbus poll failed (%s)", code, e)
|
||||||
|
continue
|
||||||
|
|
||||||
|
comm = int(iu[MIM_OFF_COMM_STATUS])
|
||||||
|
comm_ready = (comm & 0b111) == 0b111
|
||||||
|
error_code = int(iu[MIM_OFF_ERROR_CODE])
|
||||||
|
mode_txt = mim_operating_mode(
|
||||||
|
iu[MIM_OFF_ONOFF], iu[MIM_OFF_MODE], iu[MIM_OFF_DHW_ONOFF],
|
||||||
|
comm_ready, error_code,
|
||||||
|
)
|
||||||
|
if not comm_ready:
|
||||||
|
# MIM odpovídá, ale jednotka není ztrackovaná (b0-b2) — telemetrii
|
||||||
|
# nezapisovat (samé nuly), jen log; trvalý stav = špatná adresa IU
|
||||||
|
# nebo SEG5 "Use of central control" vypnuté.
|
||||||
|
logger.warning(
|
||||||
|
"heat_pump %s: jednotka není ready (comm_status=%s) — vzorek přeskočen",
|
||||||
|
code, bin(comm),
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
await db.execute(
|
await db.execute(
|
||||||
"select ems.fn_telemetry_heat_pump_sample($1::int, $2::int, $3::timestamptz, $4::int, $5::float8, $6::float8, $7::float8, $8::text)",
|
"select ems.fn_telemetry_heat_pump_sample("
|
||||||
|
"$1::int, $2::int, $3::timestamptz, $4::int, $5::float8, $6::float8,"
|
||||||
|
" $7::float8, $8::text, $9::float8, $10::float8, $11::boolean, $12::int)",
|
||||||
site_id,
|
site_id,
|
||||||
row["id"],
|
row["id"],
|
||||||
measured_at,
|
measured_at,
|
||||||
0,
|
None, # příkon: MIM neměří — doplní elektroměr (Shelly/Chint)
|
||||||
10.0,
|
None, # venkovní teplota: v MIM mapě není
|
||||||
45.0,
|
_mim_temp_c(iu[MIM_OFF_WATER_OUT]),
|
||||||
55.0,
|
_mim_temp_c(iu[MIM_OFF_DHW_TEMP]),
|
||||||
"standby",
|
mode_txt,
|
||||||
|
_mim_temp_c(iu[MIM_OFF_WATER_IN]),
|
||||||
|
_mim_temp_c(iu[MIM_OFF_ROOM_TEMP]),
|
||||||
|
bool(defrost_raw),
|
||||||
|
error_code,
|
||||||
)
|
)
|
||||||
|
if error_code:
|
||||||
|
logger.warning("heat_pump %s: error code %s", code, error_code)
|
||||||
|
|
||||||
|
|
||||||
async def poll_loxone_sensors(site_id: int, db: asyncpg.Connection) -> None:
|
async def poll_loxone_sensors(site_id: int, db: asyncpg.Connection) -> None:
|
||||||
|
|||||||
21
db/migration/V096__heat_pump_mim_b19n.sql
Normal file
21
db/migration/V096__heat_pump_mim_b19n.sql
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
-- Samsung TČ (EHS) přes Modbus interface MIM-B19N(T): skutečný RS485→TCP
|
||||||
|
-- převodník (Waveshare RS485 TO POE ETH (B)) na 172.16.1.17 nahrazuje
|
||||||
|
-- placeholder 192.168.1.103 ze seedu. MIM = Modbus RTU slave, 9600 8E1,
|
||||||
|
-- adresa dle DIP/rotary (zde 1). Registry: docs/04-modules/modbus-registers-mim-b19n.md.
|
||||||
|
|
||||||
|
update ems.site_endpoint e
|
||||||
|
set host = '172.16.1.17',
|
||||||
|
port = 502,
|
||||||
|
notes = 'Waveshare RS485 TO POE ETH (B) pro Samsung EHS přes MIM-B19N(T). Sériová linka 9600 8E1 (parita EVEN!), Modbus TCP server :502, unit_id = adresa MIM dle DIP (1).'
|
||||||
|
where e.id = (
|
||||||
|
select hp.endpoint_id
|
||||||
|
from ems.asset_heat_pump hp
|
||||||
|
join ems.site s on s.id = hp.site_id
|
||||||
|
where s.code = 'home-01'
|
||||||
|
);
|
||||||
|
|
||||||
|
alter table ems.telemetry_heat_pump
|
||||||
|
add column if not exists room_temp_c numeric(5,2);
|
||||||
|
|
||||||
|
comment on column ems.telemetry_heat_pump.room_temp_c is
|
||||||
|
'Prostorová teplota hlášená vnitřní jednotkou (MIM reg base+9, °C×10). Vstup budoucího termálního modelu domu.';
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
|
-- Insert 1min vzorku telemetrie TČ (MIM-B19N). Bez overloadů — při změně
|
||||||
|
-- signatury drop bez parametrů (konvence CLAUDE.md).
|
||||||
|
|
||||||
|
drop function if exists ems.fn_telemetry_heat_pump_sample;
|
||||||
|
|
||||||
create or replace function ems.fn_telemetry_heat_pump_sample(
|
create or replace function ems.fn_telemetry_heat_pump_sample(
|
||||||
p_site_id int,
|
p_site_id int,
|
||||||
p_heat_pump_id int,
|
p_heat_pump_id int,
|
||||||
@@ -6,7 +11,11 @@ create or replace function ems.fn_telemetry_heat_pump_sample(
|
|||||||
p_outdoor_temp_c double precision,
|
p_outdoor_temp_c double precision,
|
||||||
p_water_outlet_temp_c double precision,
|
p_water_outlet_temp_c double precision,
|
||||||
p_tuv_tank_temp_c double precision,
|
p_tuv_tank_temp_c double precision,
|
||||||
p_operating_mode text
|
p_operating_mode text,
|
||||||
|
p_water_inlet_temp_c double precision default null,
|
||||||
|
p_room_temp_c double precision default null,
|
||||||
|
p_defrost_active boolean default null,
|
||||||
|
p_alarm_code int default null
|
||||||
)
|
)
|
||||||
returns void
|
returns void
|
||||||
language sql
|
language sql
|
||||||
@@ -19,7 +28,11 @@ as $fn$
|
|||||||
outdoor_temp_c,
|
outdoor_temp_c,
|
||||||
water_outlet_temp_c,
|
water_outlet_temp_c,
|
||||||
tuv_tank_temp_c,
|
tuv_tank_temp_c,
|
||||||
operating_mode
|
operating_mode,
|
||||||
|
water_inlet_temp_c,
|
||||||
|
room_temp_c,
|
||||||
|
defrost_active,
|
||||||
|
alarm_code
|
||||||
)
|
)
|
||||||
values (
|
values (
|
||||||
p_site_id,
|
p_site_id,
|
||||||
@@ -29,10 +42,14 @@ as $fn$
|
|||||||
p_outdoor_temp_c,
|
p_outdoor_temp_c,
|
||||||
p_water_outlet_temp_c,
|
p_water_outlet_temp_c,
|
||||||
p_tuv_tank_temp_c,
|
p_tuv_tank_temp_c,
|
||||||
p_operating_mode
|
p_operating_mode,
|
||||||
|
p_water_inlet_temp_c,
|
||||||
|
p_room_temp_c,
|
||||||
|
p_defrost_active,
|
||||||
|
p_alarm_code
|
||||||
)
|
)
|
||||||
on conflict (heat_pump_id, measured_at) do nothing;
|
on conflict (heat_pump_id, measured_at) do nothing;
|
||||||
$fn$;
|
$fn$;
|
||||||
|
|
||||||
comment on function ems.fn_telemetry_heat_pump_sample is
|
comment on function ems.fn_telemetry_heat_pump_sample is
|
||||||
'Insert telemetrie TČ (placeholder Modbus).';
|
'Insert telemetrie TČ z MIM-B19N pollu (voda in/out, TUV, prostorová teplota, defrost, alarm). power_w je NULL — MIM příkon neměří (nutný elektroměr).';
|
||||||
|
|||||||
84
docs/04-modules/modbus-registers-mim-b19n.md
Normal file
84
docs/04-modules/modbus-registers-mim-b19n.md
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# Samsung MIM-B19N(T) — Modbus registry (TČ EHS, home-01)
|
||||||
|
|
||||||
|
Modbus interface modul Samsungu (DVM/EHS). EMS k němu mluví přes Waveshare
|
||||||
|
**RS485 TO POE ETH (B)** na **172.16.1.17:502** (V096; dříve placeholder
|
||||||
|
192.168.1.103). Zdroj: instalační manuál MIM-B19N(T) (DB68-07538A-03).
|
||||||
|
|
||||||
|
## Sériová linka a převodník — POVINNÉ nastavení
|
||||||
|
|
||||||
|
| Parametr | Hodnota |
|
||||||
|
|---|---|
|
||||||
|
| Baud rate | **9600** |
|
||||||
|
| Data bits | 8 |
|
||||||
|
| Parita | **EVEN** (nejčastější chyba — Waveshare default je None!) |
|
||||||
|
| Stop bit | 1 |
|
||||||
|
| Protokol | Modbus RTU, MIM = **slave** |
|
||||||
|
| Adresa MIM | DIP SW4/SW5 + rotary SW1, rozsah 1–247 (čte se jen při zapnutí!); EMS `unit_id` = tato adresa (home-01: 1) |
|
||||||
|
| Waveshare work mode | TCP Server, local port **502**, protokol **Modbus TCP ↔ RTU** (jinak EMS nespojí — port 502 zavřený) |
|
||||||
|
| FC podporované | 0x03, 0x04 čtení; 0x06, 0x10 zápis |
|
||||||
|
| Mezera mezi dotazy | ≥ 10 ms po poslední odpovědi |
|
||||||
|
|
||||||
|
Polarita A/B: při prohození MIM neodpovídá vůbec (timeout na FC3), Y-GRN LED
|
||||||
|
na MIM nebliká k BMS. 7segment na MIM: `E6`+`16` střídavě = ztracená
|
||||||
|
komunikace, `E6`+`04` = tracking nedoběhl, `E6`+`34` = chybná adresa.
|
||||||
|
|
||||||
|
## Adresace registrů
|
||||||
|
|
||||||
|
Vnitřní jednotka IU (adresa 0–47, nastavená na jednotce) má blok
|
||||||
|
**base = 50 + IU×50**. home-01 EHS = IU 0 → blok 50–99. Hodnoty teplot
|
||||||
|
**°C×10, signed**, big endian. Po startu MIM jsou všechny registry 0, dokud
|
||||||
|
nedoběhne tracking (~minuty).
|
||||||
|
|
||||||
|
### Modulové registry (PDU 0–3)
|
||||||
|
|
||||||
|
| Reg | Význam | R/W |
|
||||||
|
|---|---|---|
|
||||||
|
| 0 | Stav modulu: b0 address error, b1 comm error R1/R2, b2 tracking error | R |
|
||||||
|
| 1 | Chybový kód venkovní jednotky (0 = OK, 100–999) | R |
|
||||||
|
| 2 | Defrost (0/0xFF off, jinak on) | R |
|
||||||
|
| 3 | Bzučák (0 on / 1 off) | W |
|
||||||
|
|
||||||
|
### Blok vnitřní jednotky (base+offset; EHS sloupec)
|
||||||
|
|
||||||
|
| Off | Význam | R/W | Poznámka |
|
||||||
|
|---|---|---|---|
|
||||||
|
| +0 | Comm status: b0 exist, b1 type OK, b2 ready, b3 comm error | R | **gate pollu: (v&7)==7** |
|
||||||
|
| +1 | Typ jednotky (lower byte): 110 HE, 115–117 EHS, 120 HT | R | |
|
||||||
|
| +2 | Zapnuto/vypnuto (0/1) | R/W | |
|
||||||
|
| +3 | Režim: 0 auto, 1 cool, 4 heat | R/W | |
|
||||||
|
| +8 | Set teplota ×10 (cool 18–30, heat 16–30) | R/W | |
|
||||||
|
| +9 | Prostorová teplota ×10 | R | → `room_temp_c` |
|
||||||
|
| +13 | Chybový kód jednotky (0 OK, 100–999) | R | → `alarm_code` |
|
||||||
|
| +14 | Blokace dálkového ovládání (0x0000 / 0x6363) | R/W | |
|
||||||
|
| +15 | Teplota vody vstup ×10 | R | → `water_inlet_temp_c` |
|
||||||
|
| +16 | Teplota vody výstup ×10 | R | → `water_outlet_temp_c` |
|
||||||
|
| +18 | Set teplota výstupní vody ×10 (EHS heat 15–65 °C) | R/W | budoucí řízení |
|
||||||
|
| +22 | TUV zapnuto/vypnuto | R/W | |
|
||||||
|
| +23 | TUV režim: 0 Eco, 1 Standard, 2 Power, 3 Force (jen EHS) | R/W | |
|
||||||
|
| +24 | TUV set teplota ×10 (EHS 30–70 °C) | R/W | budoucí řízení |
|
||||||
|
| +25 | TUV teplota zásobníku ×10 | R | → `tuv_tank_temp_c` |
|
||||||
|
| +28 | Tichý režim (0/1) | R/W | |
|
||||||
|
| +29 | Away (0/1) | R/W | |
|
||||||
|
|
||||||
|
## Telemetrie EMS (poll 60 s, `poll_heat_pump`)
|
||||||
|
|
||||||
|
Jeden FC3 blok base+0..+25 (26 registrů) + modulový reg 2 (defrost) →
|
||||||
|
`ems.fn_telemetry_heat_pump_sample`. `operating_mode`: `off` / `heat` /
|
||||||
|
`cool` / `auto` / `dhw` / `heat+dhw` / `error` / (`offline` se nezapisuje —
|
||||||
|
vzorek se přeskočí, jednotka bez trackingu hlásí samé nuly).
|
||||||
|
|
||||||
|
**Příkon (`power_w`) MIM neposkytuje** — zůstává NULL, dokud nebude
|
||||||
|
elektroměr (Shelly EM / Chint na RS485). Bazální spotřeba (CLAUDE.md §15)
|
||||||
|
do té doby TČ neodečítá.
|
||||||
|
|
||||||
|
**Zápisy (on/off, set teploty, TUV)**: zatím neimplementováno; půjdou přes
|
||||||
|
control exporter + `modbus_command` journal jako u Deye (FC 0x06/0x10).
|
||||||
|
Pozn. manuálu: každý write MIM přepošle jednotce, i když hodnota nemění —
|
||||||
|
zapisovat jen při skutečné změně.
|
||||||
|
|
||||||
|
## Stav zapojení (2026-06-12)
|
||||||
|
|
||||||
|
Převodník na 172.16.1.17 odpovídá (ping, web UI :80 „RS485 TO POE ETH (B)"),
|
||||||
|
**port 502 zatím zavřený** → ve web UI nastavit TCP Server :502 + Modbus
|
||||||
|
TCP↔RTU převod a sériovku 9600 8E1. Pak ověřit FC3 čtení bloků 0–2 a 50–75
|
||||||
|
(`/tmp/probe_mim.py` vzor).
|
||||||
Reference in New Issue
Block a user