Tasmota parsowanie danych z Modbus

Witam.

Układ z ESP32 mierzy temperatury. Chciałbym, żeby układ reagował na wartości tych temperatur sterowaniem pomp bezpośrednio, bez pośrednictwa HA, NodeRed itp. Na razie robię to w oparciu o DS18x20 , ale wieczne problemy z transmisją (czujniki oddalone o kilkadziesiąt metrów) sprawiły, że chciałbym przejść na PT100 i Modbus. Na razie mam tak:

mem3 25
Rule1
  ON DS18S20-1#temperature DO event t1=%value% ENDON
  ON DS18B20-2#temperature DO event t2=%value% ENDON
  ON event#t2>%mem3% DO var1 1 ENDON
  ON event#t2<=%mem3% DO var1 0 ENDON
  ON event#t1 DO Backlog var2 %value%; add2 1 ENDON
  ON event#t1 DO Backlog var3 %value%; add3 2 ENDON
  ON event#t2>%var3% DO Power1 %var1% ENDON
  ON event#t2<%var2% DO Power1 0 ENDON

po nowemu, odpytanie o temperatury z PT100 wygląda tak:

ModBusSend {"deviceaddress": 1, "functioncode": 3, "startaddress": 0, "type":"uint16", "count":1}  

a odpowiedź (przykładowa):

tele/ESP32_Cam_test/RESULT = {"ModbusReceived":{"DeviceAddress":1,"FunctionCode":3,"StartAddress":0,"Length":7,"Count":1,"Values":[191]}}

i nie umiem sobie poradzić z wyłuskaniem wartości 191 (to temperatura 19.1 stopnia), aby użyć podobnej reguły, gdzie dotychczas mam DS1820

czy ktoś mógłby pomóc? pozdrawiam

Wytłumacz po koleii zapis obecnego zestawu reguł. Co dzieje się w każdym wierszu?

3 zasady:

Pompa powinna się włączyć, gdy temperatura kolektorów jest wyższa więcej niż 2 stopnie niż woda w zbiorniku (środek).
Pompa powinna się zatrzymać, gdy temperatura kolektorów jest niższa o 1 stopień niż woda w zbiorniku (środek).
Pompa nie powinna się uruchamiać, jeśli temperatura kolektorów jest niższa niż 25 stopni Celsjusza (mem3).
t1: temperatura zbiornik środek
t2: temperatura kolektory
var1: w prawidłowym zakresie temperatur kolektora?
var2: temperatura progowa dla kolektorów
var3: w temperaturze progowej dla kolektorów
mem3: najniższa ważna temperatura kolektorów

nie jestem pewien, czy wszystko dobrze zrozumiałem (podpatrzyłem i zaadaptowałem dla siebie “gotowca” z netu),ale na tyle, ile rozumiem:
dwie pierwsze linijki przypisują wartość pomiarów z czujników do zmiennych t1 i t2
dwie następne ustawiają flagi uwzględniające mem3, czyli minimalną temperaturę kolektorów.
dwie kolejne ustawiają zdarzenia dla poleceń w ostatnich dwóch linijkach
dwie ostatnie to polecenia do modułu przekaźników na “Modbus” - wartość 512 wyłącza przekaźnik, 256 włącza

i tak na przykład:

w zmiennej t1 widzę temperaturę z czujnika DS18S20-1

chciałbym użyć czegoś na kształt:
ON ModbusReceived DO event t1=Values: ENDON

gdzie Values byłoby wartością 191 po odczytaniu odpowiedzi z Modbus:
tele/ESP32_Cam_test/RESULT = {“ModbusReceived”:{“DeviceAddress”:1,“FunctionCode”:3,“StartAddress”:0,“Length”:7,“Count”:1,“Values”:[191]}}

Wieczorem zobaczę jak mogę Ci pomóc.
A teraz prośba, nie rób kolejnych wpisów pod własnymi. Można je edytować bez problemu.

Przeczytaj też te podpowiedzi i popraw, scal swoje wpisy w tym temacie:

I tak na początek to:

wydaje mi się to błędnym założeniem, zmienne w regułach Tasmota to tylko i wyłącznie var lub mem i nie mogę to być dowolne inne zapisy jak np Twoje t1 t2.
Rule Variables

EDIT:
Przykład z książki kucharskiej, na którym się pewnie wzorowałeś - Solar heater control

Dzięki.

Nie wiem, czy nie namieszałem coś z tym scalaniem - teraz widać dwa posty jako usunięte przez autora (w nich były treści, przeniesione do drugiego postu).

Faktycznie zmienne w regułach Tasmoty to var lub mem (tak, z tej “książki kucharskiej” korzystałem).
Próbowałem się wgryźć w te reguły i chyba jest tak, że “t1” i “t2” są składowymi nazwy eventów, na które reguła reaguje poleceniami, zmienne to: mem3, var1, var2, var3. Ale nadal nie wiem, dlaczego dla “DS” t1 przyjmuje prawdziwą wartość temperatury, a dla modbusa już nie?

W każdym razie jeśli stworzę regułę:

ON DS18S20-1#temperature DO event t1=%value% ENDON

w konsoli Tasmoty widzę odpowiedź:

RUL: DS18S20-1#TEMPERATURE performs "backlog event t1=24.9"

natomiast jeśli wpiszę:

ON  ModbusReceived#Values DO event t1=%value% ENDON

w odpowiedzi dostaję:

RUL: MODBUSRECEIVED#VALUES performs "backlog event t1="

czyli chyba reguła zareagowała na przyjście danych z modbusa, tylko nie umiem wyłuskać wartości. Wartość przychodzi w nawiasach kwadratowych - czy to nie jest problem?

przypomnę, jaka pełna wiadomość przychodzi po wysłaniu żądania pomiaru temperatury:

11:20:21.975 MQT: tele/ESP32_Cam_test/RESULT = {"ModbusReceived":{"DeviceAddress":1,"FunctionCode":3,"StartAddress":0,"Length":7,"Count":1,"Values":[198]}}```

Format danych, w jakim otrzymujesz wartość w wiadomości ModbusReceived jest przekazywana jako tablica wartości (array), może tak można ją przetworzyć (gdzie #0 to pierwszy element z tej tablicy).

ON ModbusReceived#Values DO event t1=[Values#0] ENDON

Dzięki. To może być dobry trop. Zwłaszcza, że jeśli wpiszę ModbusReceived#lenght albo ModbusReceived#Count, otrzymuję właściwe odpowiedzi. Poćwiczę z wartością pierwszą (zerową) w tablicy…

Wszystko jest OK, usunięte posty znikną automatycznie po pewnym czasie.
Ale nadal wklejasz kod bez jego formatowania jak to opisał @szopen w linkowanym powyżej poście. Kod wówczas jest nieczytelny i często ze zmienionymi znakami, automatycznie przy wklejaniu.

Wracając do tematu.

Użył bym wyzwalacza ModbusReceived#Values do zapisania zmiennej var - tylko nie wiem czy nie będą przeszkodą te dziwne nawiasy kwadratowe.
Następnie zmienna var może już być wykorzystywana w logice regulacji temperatury.

chyba są przeszkodą - w żaden sposób nie mogę tego odczytać. Dodatkowo, zmienna var1 po takich zabiegach przyjmuje zawsze wartość 0.000 (float?). Czy w regułach deklaruje się jakoś zmienne?

Arithmetic commands used with VAR

Syntax

Zadziałało. Dzięki za pomoc, nie zamykam jeszcze tematu, może się coś urodzić.

Należało zażądać pobrania pierwszego elementu z tablicy już w pierwszej komendzie:

Rule2 ON  ModbusReceived#Values[0] DO  var1 %value%  ENDON

ps. przy okazji nauczyłem się wklejać kod :slight_smile:

2 Likes

Widzę, że przechodzisz do rule2 czyli wykorzystujesz drugi zestaw reguł. Wyjaśniam, w celach edukacyjnych dla innych, którzy kiedyś przeczytają ten temat.
Rule1; Rule2; Rule3 to zestawy reguł mogące składać się z wielu, a podstawą składni każdej jest:
ON <trigger> DO <command> [ENDON | BREAK]

Liczymy na podzielenie się gotowym kodem na forum, bo przypadek jest podejrzewam nawet nie tak odosobniony i funkcjonalność jaką planujesz uzyskać pewnie interesuje jaszcze parę innych osób.

OK, jak skończę podzielę się efektem.

A jednak utknąłem. W przypadku “DS” miałem dwa czujniki, które od razu zgłaszały się z innymi nazwami .

ON DS18S20-1#temperature DO event t1=%value% ENDON
ON DS18B20-2#temperature DO event t2=%value% ENDON

W przypadku Modbusa czujniki zgłaszają się jako ModbusReceived# i po tym “#” określamy, jaki parametr chcemy czytać (Values, albo DeviceAddress, albo coś innego). Wyzwalacz reguły chyba nie pozwala na zapis w rodzaju " ModbusReceived#DeviceAddress AND ModbusReceived#Values". Myślałem, żeby w regule odpytać pierwszy czujnik pod adresem 1, odebrać dane i zapisać do var1, potem odczekać, odpytać drugi pod adresem 2, zapisać do var2. Mniej więcej, coś takiego:

ON Time#Minute DO ModBusSend {"deviceaddress": 1, "functioncode": 3, "startaddress": 0, "type":"uint16", "count":1}  ENDON
ON ModbusReceived#Values[0] var1 = %value%/10; event=nastepny ENDON 
ON event#nastepny DO ModBusSend {"deviceaddress": 2, "functioncode": 3, "startaddress": 0, "type":"uint16", "count":1}  ENDON
ON ModbusReceived#Values[0] DO  var2 = %value%/10;  ENDON
itd

ale wszystko wykonuje się w “mgnieniu oka” i w efekcie mam w var1 i var2 te same wartości z czujnika pierwszego. Jak sprawić, żeby trzecia linijka w powyższym kodzie “zaczekała” kilkanaście sekund z wykonaniem. Myślałem, że działa to tak: wykonuje się pierwsza linijka, program czeka na dane z Modbus, zapisuje do zmiennej, ustawia “event”. Trzecia linijka reaguje na “event” z poprzedniej (czyli niejako czeka na wykonanie pop[rzedniej), odpytuje drugi czujnik itd. Ale coś nie tak, dzieje się coś takiego:

17:41:00.924 RUL: TIME#MINUTE performs "ModBusSend {"deviceaddress": 1, "functioncode": 3, "startaddress": 0, "type":"uint16", "count":1}"
17:41:00.939 MQT: stat/ESP32_Cam_test/RESULT = {"ModbusSend":"Done"}
17:41:01.027 MQT: tele/ESP32_Cam_test/RESULT = {"ModbusReceived":{"DeviceAddress":1,"FunctionCode":3,"StartAddress":0,"Length":7,"Count":1,"Values":[187]}}
17:41:01.041 RUL: MODBUSRECEIVED#VALUES[0] performs "Backlog var1 = 187/10; RuleTimer1 10"
17:41:01.052 RUL: MODBUSRECEIVED#VALUES[0] performs "var2 = 187/10;"
17:41:01.060 MQT: stat/ESP32_Cam_test/RESULT = {"Var2":"18.700"}
17:41:01.285 MQT: stat/ESP32_Cam_test/RESULT = {"Var1":"18.700"}

?

Hasło klucz RuleTimer - może być ich 8.

Rules#Timer=<x> when countdown RuleTimer<x> expires (x = 1..8).

Posłużę się przykładem z książki, jak się tego używa:

Time-delayed Auto-off Switch

Koniecznie zapoznaj się z funkcją Backlog, która umożliwia pisanie kolejno każdej komendy jedna po drugiej, przedzielonych średnikiem.

Dziękuję. W poprzednim poście coś napisałem (na co Ty odpowiedziałeś), po czym próbowałem coś zmienić i wywaliłem niechcący całą treść. Przepraszam.
Popróbuję z timerami i jak napisałem, jeśli do czegoś dojdę, napiszę.

próbuję:

Rule3
ON Time#Minute DO Backlog ModBusSend {"deviceaddress": 1, "functioncode": 3, "startaddress": 0, "type":"uint16", "count":1}; RuleTimer1 10 ENDON
ON ModbusReceived#Values[0] DO var1 = %value%/10 ENDON 
ON Rules#Timer=1 DO Backlog ModBusSend {"deviceaddress": 2, "functioncode": 3, "startaddress": 0, "type":"uint16", "count":1}; RuleTimer1 0 ENDON 
ON ModbusReceived#Values[0] DO  var2 = %value%/10 ENDON

ale efekt jest taki:

19:01:00.947 RUL: TIME#MINUTE performs "Backlog ModBusSend {"deviceaddress": 1, "functioncode": 3, "startaddress": 0, "type":"uint16", "count":1}; RuleTimer1 10"
19:01:00.993 MQT: stat/ESP32_Cam_test/RESULT = {"ModbusSend":"Done"}
19:01:01.054 MQT: tele/ESP32_Cam_test/RESULT = {"ModbusReceived":{"DeviceAddress":1,"FunctionCode":3,"StartAddress":0,"Length":7,"Count":1,"Values":[185]}}
19:01:01.065 RUL: MODBUSRECEIVED#VALUES[0] performs "var1 = 185/10"
19:01:01.073 MQT: stat/ESP32_Cam_test/RESULT = {"Var1":"18.500"}
19:01:01.084 RUL: MODBUSRECEIVED#VALUES[0] performs "var2 = 185/10"
19:01:01.092 MQT: stat/ESP32_Cam_test/RESULT = {"Var2":"18.500"}
19:01:11.940 RUL: RULES#TIMER=1 performs "Backlog ModBusSend {"deviceaddress": 2, "functioncode": 3, "startaddress": 0, "type":"uint16", "count":1}; RuleTimer1 0"
19:01:11.986 MQT: stat/ESP32_Cam_test/RESULT = {"ModbusSend":"Done"}
19:01:12.047 MQT: tele/ESP32_Cam_test/RESULT = {"ModbusReceived":{"DeviceAddress":2,"FunctionCode":3,"StartAddress":0,"Length":7,"Count":1,"Values":[1]}}
19:01:12.057 RUL: MODBUSRECEIVED#VALUES[0] performs "var1 = 1/10"
19:01:12.065 MQT: stat/ESP32_Cam_test/RESULT = {"Var1":"0.100"}
19:01:12.074 RUL: MODBUSRECEIVED#VALUES[0] performs "var2 = 1/10"
19:01:12.083 MQT: stat/ESP32_Cam_test/RESULT = {"Var2":"0.100"}

czyli pierwsza linijka wykonuje się prawidłowo, trzecia też (po zadanym czasie 10 s), ale obie linijki odpowiadające za odbiór danych działają “poza czasem”, czyli wartości są przypisywane do obu zmiennych przy każdej komendzie odpytującej czujniki.
Chodziłoby o to, żeby czwarta linijka wykonała się o kilkanaście sekund poźniej, niż pierwsza. Ale wywołuję ją komendą ON ModbusReceived#Values i nie wiem jak połączyć z ON Rules#Timer

=======================================

Strasznie namotałem, ale sezon na słońce lada chwila, chciałem mieć to już za sobą. Poniżej nieco kulawy (ale działający) kod:

Rule1
ON Rules#Timer=2 DO Backlog ModBusSend {"deviceaddress": 1, "functioncode": 3, "startaddress": 0, "type":"uint16", "count":1}; RuleTimer1 10 ENDON
ON ModbusReceived#Values[0] DO var1 = %value%/10 ENDON  
ON Rules#Timer=1  DO Backlog Rule2 1 ; Rule1 0; RuleTimer1 5; RuleTimer2 15   ENDON

Rule2
ON Rules#Timer=1 DO ModBusSend {"deviceaddress": 2, "functioncode": 3, "startaddress": 0, "type":"uint16", "count":1} ENDON
ON ModbusReceived#Values[0] DO Backlog var2 = %value%/10;  Rule2 0 ENDON 

Rule3
ON System#Boot DO Backlog RuleTimer2 15; Rule1 1 ENDON   
ON var6#State<200 DO var1=%value% ENDON
ON var5#State<200 DO var2=%value% ENDON
ON var1#State>=%var2% DO Backlog ModBusSend {"deviceaddress": 3, "functioncode": 6, "startaddress": 0, "type":"uint16", "count":1, "values":[512]};RuleTimer2 15; Rule1 1 ENDON
ON var1#State<%var2% DO Backlog ModBusSend {"deviceaddress": 3, "functioncode": 6, "startaddress": 0, "type":"uint16", "count":1, "values":[256]};RuleTimer2 15; Rule1 1 ENDON

Nie umiałem napisać wyzwalacza, który reagowałby na dane przychodzące od czujnika o konkretnym ID.
Po każdym zapytaniu na magistrali, czujniki odpowiadają (czujniki temperatur wartością temperatury, a moduł z przekaźnikami m.in. komendą, jaką odebrał). Robiło się zamieszanie - na przykład wartość 256, która jest poleceniem włączenia przekaźnika, była interpretowana jako temperatura 25,6 stopnia. W tym celu użyłem aż trzech reguł oraz timerów, które wyłączają się nawzajem uniemożliwiając w ten sposób interpretowania “nie swoich” poleceń.

1 Like