Achiziție
Master Modbus TCP, RTU, ASCII și UDP, cu citiri grupate, reîncercări și cauze de eroare clare. Grupuri de citire de la 50 ms la 24 h, aplicate fără repornire. Mai mulți slave pe aceeași magistrală, pe rând.
Gateway de protocoale definit software · open source
Multi-Bus Gateway citește echipamentele pe care le ai deja — contoare de energie, invertoare PV, baterii, convertizoare de frecvență, pompe de căldură, senzori — prin Modbus, HTTP sau MQTT și le servește oricui are nevoie: MQTT, InfluxDB și Grafana, Home Assistant, SCADA prin OPC UA, sau un contor Modbus virtual pe care un Victron GX ori un invertor Fronius îl ia drept cel real.
Modernizezi, nu înlocuiești. Un container, pe hardware-ul tău.
Rulează zilnic pe o centrală PV reală din martie 2026: un Janitza UMG 512-PRO la punctul de racordare, citit la 250 ms și servit mai departe ca două contoare virtuale — un EM24 pe care un Victron Ekrano GX îl folosește drept contor de rețea și un Fronius Smart Meter pentru invertoarele Fronius. Patru invertoare Fronius cu reguli de limitare a puterii, un banc de baterii Seplos ascultat fără să transmită, o pompă de căldură Samsung printr-un bridge serial la distanță și alte contoare, pe circuitele lor. Adaugi câte vrei să urmărești: un depozit, o a doua clădire, o cameră frigorifică.
Fiecare echipament e descris de un template. Gateway-ul îl citește, numește fiecare valoare la fel pe orice echipament, o verifică și o trimite mai departe — fiecare echipament cu ieșirile lui.
Tot ce urmează e în gateway-ul open source.
Master Modbus TCP, RTU, ASCII și UDP, cu citiri grupate, reîncercări și cauze de eroare clare. Grupuri de citire de la 50 ms la 24 h, aplicate fără repornire. Mai mulți slave pe aceeași magistrală, pe rând.
Bridge-uri pe pagina Devices: bridge-ul nostru serial pe un Raspberry Pi, un convertor transparent (Waveshare, USR, Elfin) sau un gateway Modbus TCP. Scanezi o magistrală, vezi cât e de încărcată, muți un echipament între bridge-uri.
Tap-ul RTU decodează o magistrală care are deja un master — pachetul master al unui BMS, un datalogger al producătorului — fără să trimită vreodată un octet. Cereri și răspunsuri împerecheate, scrierile urmărite.
Harta de registre ca fișier portabil, cu editor în browser, import CSV/YAML și recunoaștere la scanare. 22 incluse: Janitza UMG 512-PRO, Carlo Gavazzi EM24, Eastron, ABB, Schneider iEM3000, Fronius (SunSpec, Solar API, Smart Meter), Seplos BMS, Delta VFD-EL, pompă de căldură Samsung, Zigbee2MQTT, Tasmota și altele.
Servește orice sursă ca un Carlo Gavazzi EM24, un Fronius Smart Meter sau un contor SunSpec. Contoare compuse din mai multe echipamente. Lipsa unei valori nu e servită niciodată ca 0: eroare, valoare-santinelă sau păstrare — plus un bloc de calitate în aceeași conexiune.
Fiecare echipament și valoare pentru SCADA și panouri HMI, cu calitate și momentul citirii, sub identificatori stabili. Criptat, cu conturile gateway-ului sau token-uri de serviciu. Setpoint-uri și metode cu aceeași siguranță ca orice comandă. Pentru FUXA, UaExpert, Ignition sau un panou Weintek.
Per echipament: la schimbare sau mereu, retain, QoS, TLS. Descoperire automată în Home Assistant, registrele care se pot scrie apar ca entități number și select. InfluxDB cu buffer pe disc: nimic pierdut la o întrerupere.
Trepte, condiții cu histerezis, trim (urmărește un semnal într-o bandă), programări orare. Temporizări, timp minim de funcționare și oprire, porniri pe oră, contoare. Mai multe reguli pe aceeași țintă, cu niveluri și arbitru. Secvențe pe mai multe echipamente, cu anulare. O regulă nouă rulează întâi în shadow: decide și nu scrie nimic până nu o armezi. Regulile se exportă și se importă, iar fiecare decizie ajunge în InfluxDB și MQTT, gata de pus pe grafic.
Un endpoint adună unități citite în mai multe feluri — Solar API și SunSpec, Modbus și MQTT — cu o prioritate pe fiecare câmp, trecere pe sursa următoare când una nu mai răspunde și totaluri pe grup.
Scrierile sunt oprite până le permiți. Limite din template, recitire, lease-uri dead-man care revin la o valoare sigură, protecția echipamentului respectată de oricine scrie — reguli, API, MQTT, OPC UA.
Un motor de expresii sigur, cu valori anterioare, rate, ora din zi și referințe între echipamente. Rezultatele merg la toate ieșirile, ca valorile măsurate.
Asistent de descoperire (scanare rețea, ID-uri de unitate, parcurgere SunSpec, explorare MQTT, Fronius, ESPHome). Monitor de magistrală la nivel de cadru, sonda de registre pentru ordinea octeților și cuvintelor, mostre de payload.
Un snapshot la fiecare modificare, diferențe și revenire, pornire cu ultima configurație bună, backup-uri fără secrete implicit, echipamente șterse care se pot restaura.
Autentificare cu rolurile admin, operator și viewer, passkeys, token-uri de serviciu, jurnal de audit, listă de IP-uri permise, HTTPS și suport pentru reverse proxy. Implicit pentru o rețea locală de încredere, întărit când ai nevoie.
Metrici Prometheus, o pagină de stare cu cauzele erorilor, jurnal de evenimente persistent, alerte de infrastructură prin MQTT și webhook-uri, și un înregistrator de evenimente de calitate a energiei pentru contoarele Janitza.
Un singur contor la punctul de racordare măsoară tot. Gateway-ul îl servește din nou ca un Carlo Gavazzi EM24, așa cum îl așteaptă Victron ESS. Dacă sursa nu mai e proaspătă, contorul virtual tace, iar protecția proprie a GX-ului preia în loc să acționeze pe o valoare înghețată.
Regulile reale de pe centrală: câte una pe invertor, fiecare urmărindu-și cele trei tensiuni de fază și cerând mai puțină putere în patru trepte, până la 50 %, ținând până când tensiunea coboară sub 248 V. Armate din 20 octombrie 2026, după săptămâni în shadow lângă controlerul Node-RED pe care l-au înlocuit, cu aceleași trepte la câteva secunde în zilele cu supratensiune reală.
- id: ov-u1 # one per inverter: ov-u1 … ov-u4
target: { device: pv-u1, command: power_limit }
mode: armed # weeks in shadow first, compared with the old controller
kind: steps
signal: "max(pv-u1.voltage_l1_n, pv-u1.voltage_l2_n, pv-u1.voltage_l3_n)"
signal_valid: { min: 100, max: 350, max_step: 10 } # night zeros, glitches
steps:
- { at: 250.0, value: 80, label: Warning }
- { at: 251.0, value: 70, label: Moderate }
- { at: 252.5, value: 60, label: Severe }
- { at: 253.0, value: 50, label: Emergency, fast: true }
release_below: 248.0
normal: { value: 100 }
params: { revert_s: 0, ramp_s: 10 }
timing: { every_s: 2, debounce: 3, min_interval_s: 30, reassert_s: 120 }
stale_after_s: 90
on_stale: hold # at night the voltages read 0: keep, never curtail a sleeping inverter
on_disable: safe # disarmed: back to 100 %
FUXA, un SCADA open source, e configurat din gateway cu o singură comandă, iar o verificare compară fiecare tag cu gateway-ul: 1199 din 1199 se potrivesc, și după ce gateway-ul repornește.
Magistrala are deja un master. Gateway-ul doar ascultă, împerechează fiecare cerere cu răspunsul ei și publică fiecare pachet — fără niciun cadru propriu pe fir.
Magistrala RS-485 e departe de server. Un bridge mic lângă ea o duce prin rețeaua locală; gateway-ul îi setează viteza și paritatea, iar echipamentele urmează bridge-ul dacă își schimbă adresa.
Fiecare comandă declarată de un template ascultă pe topicul ei. O valoare trimisă la fronius-u1/cmd/power_limit trece prin aceleași porți ca un click în gateway: scrieri permise, scrieri MQTT permise, un broker cu autentificare, limitele din template, protecția echipamentului. Răspunsul revine cu valoarea dinainte și de după și cât a durat; o cerere în afara limitelor e refuzată, cu motivul. În Home Assistant registrele din spatele comenzilor apar ca entități number și select, executate prin aceeași comandă.
Rețete pentru probleme pe care le auzim des. Fiecare folosește ce are deja gateway-ul — echipamente, reguli, MQTT, ieșiri — iar fiecare număr din ele îl schimbi cum vrei.
Racordul permite 45 kW la export. O regulă trim urmărește rezerva până la plafon și coboară limita de putere a invertoarelor cât timp e depășit, apoi o urcă la loc, cu o marjă. Plafonul însuși poate veni prin MQTT — de la operatorul de rețea, un EMS, Node-RED — și se poate schimba în timpul zilei. Testat pe o centrală reală cu patru invertoare și plafon de 20 kW, cu exportul sărind între 10 și 46 kW din cauza norilor: 18 kW în medie, cel mult 21,2 kW, fiecare comandă confirmată.
# 1 · keep the export under 45 kW
- id: export-cap
target: { endpoint: pv, group: inverters, command: power_limit }
kind: trim
# the room left under the cap, in W (export reads negative on this meter):
# exporting 40 kW → 45000 − 40000 = 5000 W of room
# exporting 47 kW → 45000 − 47000 = −2000 W: over the cap
signal: "45000 + umg512.power_active_total"
signal_unit: W under the cap
below: 0 # over 45 kW: lower the inverters' limit
above: 3000 # under 42 kW: raise it back (a 3 kW margin, so it does not hunt)
step: 10 # % per adjustment, at most
gain: 0.002 # % per W past the threshold: 5 kW over → 10 %
min: 0
max: 100
params: { revert_s: 300 } # the inverter's own timer: back to 100 % if the gateway stops…
timing: { every_s: 5, debounce: 2, min_interval_s: 15, reassert_s: 120 } # …refreshed every 2 min
stale_after_s: 30
on_stale: hold # meter lost: keep the last limit
on_disable: safe # disarmed: back to 100 %
# four inverters on one Fronius datamanager: ~5 s a write, so 15 s between
# adjustments keeps it under the ~26 writes a minute it can take
# the cap from MQTT instead of fixed — an MQTT device `site` with
# export_cap_kw on site/limits/export (the grid operator, an EMS, Node-RED):
# signal: "site.export_cap_kw * 1000 + umg512.power_active_total"
# 2 · with the over-voltage rule on the same inverters: the template's
# channel "power" combines with min — the lower limit is the one written
Înainte ca regula de export să taie invertoarele la 45 kW, bateriile preiau ce ar trece peste: Victron ESS prin GX-ul lui, sau încărcătoarele, își măresc încărcarea pe măsură ce exportul se apropie de plafon. Abia când sunt pline sau încarcă deja la maximum, plafonul limitează invertoarele. Două reguli pe două canale, întâi cea a bateriilor — ce se construiește încă e template-ul Victron GX cu comenzile ESS.
Regula urcă frecvența convertizorului cât timp se exportă și o coboară cât timp se importă, între 10:00 și 16:00. O a doua regulă completează ziua: dacă soarele i-a dat pompei mai puțin de patru ore, pornește de la 17:00 până le adună. Protecția convertizorului ține pentru oricine scrie: oprire minimă de 5 s și cel mult 30 de porniri pe oră.
# 1 · follow the solar surplus, 10:00–16:00
- id: vfd-surplus
target: { device: vfd1, command: run_at } # Delta VFD-EL: 0 Hz = stop
kind: trim
layer: optimize
signal: '-site-meter.power_active_total if between("10:00", "16:00") else 0'
above: 500 # exporting more than 500 W: faster
below: 100 # under 100 W (or importing): slower
step: 2 # Hz per adjustment
min: 15
max: 50
start: 15
off_value: 0
timing: { every_s: 5, debounce: 2, min_interval_s: 10, reassert_s: 0 }
stale_after_s: 60
on_stale: safe # meter lost: stop
# 2 · top up the day if the sun did not do it
- id: vfd-quota
target: { device: vfd1, command: run_at }
kind: condition
layer: schedule # above optimize: wins while it has an opinion
# the hours of both rules count: it stops once the day has had four
when: 'between("17:00", "20:00") and run_today_h("vfd-surplus") + run_today_h("vfd-quota") < 4'
then: { params: { value: 30 } }
else: null # otherwise: no opinion
# the drive's protection comes from its template (channel "motor"):
# min stop 5 s, at most 30 starts an hour — for every writer
Când prețul pentru ziua următoare scade sub zero, fiecare kWh exportat costă bani. Citești prețul din API-ul furnizorului sau al bursei ca echipament HTTP/JSON (ori de la un EMS prin MQTT) și limitezi invertoarele cât timp e negativ. Fără preț sau cu un preț învechit: putere maximă, ca un API căzut să nu oprească niciodată producția.
- id: negative-price
target: { endpoint: pv, group: inverters, command: power_limit }
kind: condition
when: "market.price_eur_mwh < 0" # an HTTP/JSON or MQTT device
then: { params: { value: 0 } }
else: { params: { value: 100 } }
timing: { every_s: 30, debounce: 2, min_interval_s: 300 }
stale_after_s: 3600
on_stale: 100 # no price: full power
O instalație cu un contract de 40 kW la import și o pompă de căldură, stații de încărcare sau un compresor. Când contorul de rețea arată peste 40 kW, pompa de căldură se oprește până când importul coboară sub 32 kW. Ca nivel de siguranță are prioritate peste programări și reguli de confort, iar intervalul minim protejează compresorul.
- id: import-cap
target: { device: heat-pump, command: power }
kind: condition
layer: safety # wins over schedules and comfort rules
when: "grid.power_active_total > 40000"
release: "grid.power_active_total < 32000"
then: { params: { value: 0 } }
else: null # otherwise: let the other rules decide
timing: { every_s: 5, debounce: 3, min_interval_s: 300 }
Peste 50,2 Hz invertoarele își reduc puterea în trepte, până la 30 % la 51 Hz, și revin doar sub 50,1 Hz. Citirile în afara 45–55 Hz sau care sar cu mai mult de 1 Hz sunt ignorate ca erori. Același model protejează o centrală reală de supratensiune.
- id: of-derate
target: { endpoint: pv, group: inverters, command: power_limit }
kind: steps
signal: "grid.frequency"
signal_valid: { min: 45, max: 55, max_step: 1 }
steps:
- { at: 50.2, value: 90 }
- { at: 50.5, value: 60 }
- { at: 51.0, value: 30, fast: true }
release_below: 50.1
normal: { value: 100 }
on_stale: 100
Un traductor de nivel citit printr-un modul I/O Modbus, o pompă pe convertizor de frecvență. Pompa pornește sub 30 % și se oprește peste 80 %, cu timp minim de funcționare și de oprire și cel mult 6 porniri pe oră. Dacă se pierde citirea nivelului, pompa se oprește, iar codul de defect al convertizorului devine alarmă.
- id: tank
target: { device: pump-drive, command: run_at }
kind: condition
when: "tank.level_pct < 30"
release: "tank.level_pct > 80"
then: { params: { value: 45 } } # Hz
else: { params: { value: 0 } }
timing: { every_s: 5, debounce: 2, min_on_s: 300, min_off_s: 120,
max_starts_per_h: 6 }
stale_after_s: 30
on_stale: safe # level lost: stop
Un boiler sau o pompă pe program săptămânal: pornit la 23:00 pe tariful de noapte, oprit la 06:00, altă fereastră în weekend. Butonul operatorului „pornește o oră acum”, de pe un HMI sau din Node-RED, e o forțare a aceleiași reguli prin MQTT: după o oră revine singur la program.
- id: boiler-week
target: { device: boiler, command: power }
kind: schedule
schedule:
- { days: Mon-Fri, at: "23:00", value: 1 } # night tariff
- { days: Tue-Sat, at: "06:00", value: 0 } # the morning after each night
- { days: "Sat,Sun", at: "13:00", value: 1, label: weekend }
- { days: "Sat,Sun", at: "17:00", value: 0 }
off_value: 0
# run it for an hour now, from an HMI or Node-RED:
# mbg/rules/boiler-week/set {"force": {"value": 1, "seconds": 3600}}
Cu bateria la 98 % și peste 5 kW exportați, energia nu mai are unde merge ieftin. Invertoarele sunt limitate la 60 % până dispare una dintre condiții, cu 30 de secunde de confirmare înaintea fiecărei schimbări și cel mult o schimbare la două minute.
- id: full-and-exporting
target: { endpoint: pv, group: inverters, command: power_limit }
kind: condition
when: "battery.soc >= 98 and grid.power_active_total < -5000"
then: { params: { value: 60 } }
else: { params: { value: 100 } }
timing: { every_s: 5, debounce: 6, min_interval_s: 120 }
on_stale: hold
Compresoare, contoare de energie și convertizoare stau deja pe linii RS-485 prin toată fabrica. Bridge-urile duc liniile prin rețeaua locală, template-urile numesc fiecare valoare la fel, iar SCADA citește tot prin OPC UA, cu calitate și momentul citirii. Aceleași valori merg în InfluxDB și Grafana pentru echipa de energie.
Un contor de energie Modbus, un invertor și câțiva senzori Zigbee apar singuri în Home Assistant, prin descoperirea MQTT, fiecare ca echipament separat, cu nume stabile. Setările pe care template-ul le permite la scriere, cum ar fi o limită de putere, devin entități number și select.
Un senzor de temperatură prin MQTT (Zigbee sau BLE) și o valoare calculată, „prea cald” peste 8 °C, declarată ca alarmă care trebuie să țină zece minute. Atunci devine alertă: serviciul de alerte o trimite pe telefonul de gardă prin SMS și Telegram, și noaptea, și trimite din nou prin SMS dacă nimeni nu o confirmă în 15 minute.
Pro
Pentru instalațiile care nu au voie să rămână fără date: două gateway-uri pe aceleași echipamente, unul activ și unul în rezervă. Parte din ediția Pro; testată pe banc, testele pe hardware real sunt în desfășurare.
Pompa de căldură raportează eroarea E101. Gateway-ul ridică alarma declarată în template după ce a ținut cât cere alarma — aici 30 de secunde, ca o clipire să nu trezească pe nimeni. Serviciul de alerte o direcționează: limitat ca frecvență, în afara orelor de liniște, pe Telegram, prin SMS de la routerul LTE al instalației și pe e-mail; dacă nimeni nu confirmă o alertă critică în 15 minute, trimite din nou prin SMS. Anunță și când eroarea dispare. În producție, cu 67 de reguli.
Ce construim în continuare. Spune-ne de care are nevoie instalația ta întâi — urcă în listă.
Gateway-ul e open source. Ediția Pro adaugă modulele de care are nevoie o instalație critică, și oamenii care răspund pentru ea.
Gratuit · AGPL-3.0
Preț la cerere
Tot ce e în Community, plus:
Docker cu Compose v2. Stack-ul aduce gateway-ul, un broker MQTT, InfluxDB și Grafana; folosești doar gateway-ul dacă le ai pe ale tale.
mkdir multi-bus-gateway && cd multi-bus-gateway
curl -fsSLO https://raw.githubusercontent.com/sm26449/multi-bus-gateway/main/docker-compose.yml
curl -fsSL https://raw.githubusercontent.com/sm26449/multi-bus-gateway/main/.env.example -o .env
mkdir -p mosquitto/config && curl -fsSL https://raw.githubusercontent.com/sm26449/multi-bus-gateway/main/mosquitto/config/mosquitto.conf -o mosquitto/config/mosquitto.conf
# edit .env: set the four required secrets
docker compose pull && docker compose up -d
docker compose logs multi-bus-gateway | grep -A3 'FIRST RUN' # the admin password, once
# → http://localhost:8080
Spune-ne despre instalația ta: ce ai și ce vrei să facă. Mesajul ajunge direct la noi, iar noi îți răspundem pe e-mail.
Sau scrie direct: [email protected]