'Inteligenty' dom ze sterownikiem PLC

 Language:
Szukanie zaawansowane  

Aktualności:

Powrót do strony głównej: www.edom-plc.pl

Autor Wątek: OpenHAB - komunikacja z WAGO bez modbusa  (Przeczytany 2607 razy)

vakul

  • Full Member
  • ***
  • Wiadomości: 149
    • Zobacz profil
OpenHAB - komunikacja z WAGO bez modbusa
« dnia: Października 09, 2020, 07:40:08 pm »

W innym wątku dotyczącym OpenHABa pisałem o komunikacji z WAGO bez użycia modbusa. Poniżej przedstawiam moje rozwiązanie tego problemu. Być może modbus byłby lepszy ale nigdy go nie sprawdziłem w akcji. To co poniżej działało i działa dobrze od 2 lat.

Środowisko wygląda tak:
- RaspberryPi z uruchomionym serwerem nginx + PHP + MySQL
- Na tym samym urządzeniu RPi działa również OpenHAB

Serwer www zawiera takie skrypty:
/wago/wago_lookup_refresh.php
/wago/wago_get-var-list.php
/wago/wago_set-var.php

Na sterowniku WAGO (750-880) mam dodaną wizualizację o nazwie "openhab", do której dodaję zmienne, które chcę czytać/zapisywać z openhaba. Wystarczy dodać zwykłe pole tekstowe, a w definicji użyć zmiennej, którą chcemy wyświetlić w tym polu. To wystarczy. Sugeruję nie dodawać nic więcej, bo wizualizacja dość szybko rośnie zajmując cenne miejsce na sterowniku.
Wago stworzy z tego plik openhab.xml dostępny przez WWW lub FTP. Plik będzie dostępny pod adresem: http://WAGO_IP/plc/openhab.xml

Zawartość pliku może (ale nie musi) zmieniać się przy kompilacji programu na sterowniku WAGO. To z kolei może powodować, że określone zmienne zmienią swoje adresy. Konieczne jest odświeżanie listy zmiennych (i ich adresów) w sposób cykliczny lub na żądanie (wtedy trzeba to zrobić ręcznie po kompilacji programu na PLC). U mnie dzieje się to cyklicznie co 5 sekund.

W tym celu wywołuję skrypt PHP "wago_lookup_refresh.php":

<?php
/* SETUP */
$log  false;
$wago_ip  '192.168.1.5';


if ($log$time_start microtime(true);

// get a list of visualizations to be parsed
$visu = isset($_GET['visu']) ? $_GET['visu'] : 'plc_visu';
$visu_pages explode (','$visu);
$output = array();

// process each visualization, looking for variable list
foreach ($visu_pages as $xml_page)
{
// get XML description for visu page
$f curl_get('http://'.$wago_ip.'/plc/'.$xml_page.'.xml');
// extract all variable names with memory addresses
preg_match_all("'<variable name=\"(.*?)\">(.*?)</variable>'si"$f$variablelistPREG_SET_ORDER);

// create human readable and serialized output files
foreach ($variablelist as $var)
{
$output[] = $var[1].';'.str_replace(',','|',$var[2].'|'.chr(10));
$serial[$var[1]] = '|'.str_replace(',','|',$var[2].'|');
}
}
// sort the arrays (for quicker lookup)
sort($output);
ksort($serial);

// save the files if different 
if (file_get_contents('wago_variables.csv') != $output)
{
file_put_contents('wago_variables.csv'$output);
file_put_contents('wago_variables.txt'serialize($serial));
}
// if logging enabled - put some data on the screen
if ($log
{
$time_end microtime(true);
$time $time_end $time_start ;
echo "<pre>";
echo 'Execution time : ' $time ' seconds'.chr(10) ;
print_r ($serial);
echo "</pre>";
$f file_put_contents('time.log',file_get_contents ('time.log').chr(10).$time_start);
}

// curl web access
function curl_get($url
{    
    $defaults = array( 
        CURLOPT_URL => $url
        CURLOPT_HEADER => 0
        CURLOPT_RETURNTRANSFER => TRUE
        CURLOPT_TIMEOUT => 
    ); 
    
    $ch curl_init(); 
    curl_setopt_array($ch$defaults); 
    if( ! $result curl_exec($ch)) 
    { 
        trigger_error(curl_error($ch)); 
    } 
    curl_close($ch); 
    return $result

?>


Skrypt tworzy plik tekstowy "wago_variables.txt" i "wago_variables.csv". W pierwszym (TXT) jest zapisana tablica dla PHP do szybkiego wyszukiwania zmiennych dla potrzeb kolejnych skryptów. Natomiast w CSV są wylistowane wszystkie zmienne w czytelnej dla oka postaci (można sobie to wyświetlić w przeglądarce i podejrzeć co tam aktualnie jest).
W obu przypadkach mamy wypisane pary zmienna + adres na sterowniku WAGO. Dobór rozszerzeń TXT/CSV jak widzę jest niefortunny ale tak to mam zrobione, coś pewnie w trakcie modyfikowałem i tak już zostało.

Należy zwrócić uwagę, aby jednorazowo ustawić uprawnienia do plików TXT/CSV, tak aby skrypt php mógł je tworzyć i modyfikować.


Aby lista zmiennych była zawsze aktualna (pliki "wago_variables"), to w OpenHAB ma dodany taki "rule" wywoływany co 5 sekund:
import org.openhab.io.net.actions.HTTP

rule wagorefresh
when
Time cron "0/5 * * * * ?"
then
var String HTTPString = "http://127.0.0.1/wago/wago_lookup_refresh.php?visu=openhab"
sendHttpGetRequest(HTTPString)
end
Podobne wywołanie skryptu może być z CRONa linuxowego, to wcale nie musi być OpenHAB. Może to też być ręczne wejście na link z powyższego skryptu po każdej kompilacji programu na WAGO.

W efekcie mamy 2 lokalne pliki (na serwerze WWW) z zawsze aktualnymi adresami zmiennych na sterowniku WAGO.

Aby pobrać listę wszystkich zmiennych wraz z ich wartościami należy wywołać kolejny skrypt PHP "wago_get-var-list.php"

<?php
$time_start 
microtime(true);

$wago_ip '192.168.1.5';
$output 'json';
$var = isset($_GET['var']) ? $_GET['var'] : 'ALL';

$var_file file_get_contents('wago_variables.txt');
$var_lookup unserialize($var_file);

if (
$var == 'ALL')
$vars array_keys($var_lookup);
else
$vars explode (','$var);

$var_count sizeof($vars);

$post '|0|'.$var_count.'|';

for (
$i=0$i<$var_count$i++)
{
$post .= $i.$var_lookup[$vars[$i]];
}


$wago_response curl_post('http://'.$wago_ip.'/plc/webvisu.htm'$post);
$wago_response explode ('|'$wago_response);

$out '';

if (
$output=='json')
{
$out '{'.chr(10);
for ($i=1$i<=$var_count$i++)
{
// do transformations if needed
if ($vars[$i-1] == '.OH_TEST'$wago_response[$i] = transform_OnOff2Boolean($wago_response[$i]);

// output variables
$out .= '   "'.$vars[$i-1].'": "'.$wago_response[$i].'"';
if ($i<$var_count$out .= ',';

$out .= chr(10);
}
$out .= '}'.chr(10);
}
else
{
for (
$i=1$i<=$var_count$i++)
$out .= $vars[$i-1].'='.$wago_response[$i].chr(10);
}

echo 
$out;

function 
transform_OnOff2Boolean ($val)
{
return ($val==1) ? 'ON' 'OFF';
}
function 
curl_post($url$post

$ch curl_init(); 
curl_setopt($chCURLOPT_URL$url);
curl_setopt($chCURLOPT_POST1);
curl_setopt($chCURLOPT_HEADER0);
curl_setopt($chCURLOPT_RETURNTRANSFER1);
curl_setopt($chCURLOPT_POSTFIELDS$post);

$result curl_exec ($ch);
curl_close ($ch);

return $result


$time_end microtime(true);
$time $time_end $time_start ;

//$f = file_put_contents('time.log',file_get_contents ('time.log').chr(10).$time_start);

//echo chr(10);
//echo 'Execution time : ' . $time . ' seconds' ;
?>

Wywołanie tego skryptu w przeglądarce wyświetli na ekranie plik JSON ze wszystkimi zmiennymi (i wartościami) dostępnymi w wizualizacji openhab.xml (na sterowniku WAGO).

W dalszym kroku mam dodane rozszerzenie (binding) HTTP w OpenHABie, a w http.cfg jest taki wpis:

WagoHttpBinding.url = http://127.0.0.1/wago/wago_get-var-list.php?var=ALL
WagoHttpBinding.updateInterval = 1000

Oznacza to tyle, że mamy do dyspozycji skrót "WagoHttpBinding" z którego możemy korzystać dalej w dowolnym miejscu w OpenHabie. W jego treści będzie zawartość pliku JSON z naszymi zmiennymi. Aby wczytać zmienną do OpenHABa w definicji zmiennych ("items") mam takie wpisy:

Switch Wago_oh_all_lights_off "oh_all_lights_off" <light> {autoupdate="false", http=">[ON:POST:http://127.0.0.1/wago/wago_set-var.php?var=SWIATLA.oh_all_lights_off&val=ON] >[OFF:POST:http://127.0.0.1/wago/wago_set-var.php?var=SWIATLA.oh_all_lights_off&val=OFF] <[WagoHttpBinding:500:JSONPATH($['SWIATLA.oh_all_lights_off'])]" }

Number Wago_oh_light_switch_dol_wiatrolap "oh_light_switch_dol_wiatrolap" <light> {autoupdate="true", http=">[1:POST:http://127.0.0.1/wago/wago_set-var.php?var=SWIATLA.oh_light_switch_dol_wiatrolap&val=1] >[2:POST:http://127.0.0.1/wago/wago_set-var.php?var=SWIATLA.oh_light_switch_dol_wiatrolap&val=2] >[3:POST:http://127.0.0.1/wago/wago_set-var.php?var=SWIATLA.oh_light_switch_dol_wiatrolap&val=3] >[0:POST:http://127.0.0.1/wago/wago_set-var.php?var=SWIATLA.oh_light_switch_dol_wiatrolap&val=0] <[WagoHttpBinding:500:JSONPATH($['SWIATLA.oh_light_switch_dol_wiatrolap'])]" }
Number Wago_oh_light_switch_dol_hol "oh_light_switch_dol_hol" <light> {autoupdate="true", http=">[1:POST:http://127.0.0.1/wago/wago_set-var.php?var=SWIATLA.oh_light_switch_dol_hol&val=1] >[2:POST:http://127.0.0.1/wago/wago_set-var.php?var=SWIATLA.oh_light_switch_dol_hol&val=2] >[3:POST:http://127.0.0.1/wago/wago_set-var.php?var=SWIATLA.oh_light_switch_dol_hol&val=3] >[0:POST:http://127.0.0.1/wago/wago_set-var.php?var=SWIATLA.oh_light_switch_dol_hol&val=0] <[WagoHttpBinding:500:JSONPATH($['SWIATLA.oh_light_switch_dol_hol'])]" }
Number Wago_oh_light_switch_dol_kuchnia "oh_light_switch_dol_kuchnia" <light> {autoupdate="true", http=">[1:POST:http://127.0.0.1/wago/wago_set-var.php?var=SWIATLA.oh_light_switch_dol_kuchnia&val=1] >[2:POST:http://127.0.0.1/wago/wago_set-var.php?var=SWIATLA.oh_light_switch_dol_kuchnia&val=2] >[0:POST:http://127.0.0.1/wago/wago_set-var.php?var=SWIATLA.oh_light_switch_dol_kuchnia&val=0] <[WagoHttpBinding:500:JSONPATH($['SWIATLA.oh_light_switch_dol_kuchnia'])]" }

Ilekroć zawartość zmiennej się zmienia, to OpenHAB wyłuska sobie z pliku JSON odpowiednią wartość i umieści w swoich zmiennych. Dalej możemy sobie robić z nimi co chcemy.

Aby zaktualizować zmienną w sterowniku WAGO należy wywołać skrypt "wago_set-var.php" z odpowiednimi parametrami.
Np.: wago_set-var.php?var=SWIATLA.oh_light_switch_dol_kuchnia&val=2
var to pełna ścieżka do zmiennej widoczna w pliku TXT/CSV, val to wartość jaką chcemy przypisać.

<?php
$time_start 
microtime(true);

$wago_ip '192.168.1.5';
$var = isset($_GET['var']) ? $_GET['var'] : '';
$val = isset($_GET['val']) ? $_GET['val'] : '';
$vars explode (','$var);

$var_file file_get_contents('wago_variables.txt');
$var_lookup unserialize($var_file);
$var_count sizeof($vars);
$var_location = isset($var_lookup[$var]) ? $var_lookup[$var] : '';

if (
$val == 'ON'$val 1// obsługa OpenHab

if ($var_location)
{
// BOOLEAN value, translate ON=>1, OFF=>0
if (substr($var_location,-2) == '0|'$val = ($val>0) ? '1' '0';

$post '|1|1|0'.$var_location.$val.'|';
$wago_response curl_post('http://'.$wago_ip.'/plc/webvisu.htm'$post);
// echo 'Wago response: '.$wago_response;
// echo $post;
}
else
echo 'Unknown variable';


function 
curl_post($url$post

$ch curl_init(); 
curl_setopt($chCURLOPT_URL$url);
curl_setopt($chCURLOPT_POST1);
curl_setopt($chCURLOPT_HEADER0);
curl_setopt($chCURLOPT_RETURNTRANSFER1);
curl_setopt($chCURLOPT_POSTFIELDS$post);

$result curl_exec ($ch);
curl_close ($ch);

return $result


$time_end microtime(true);
$time $time_end $time_start ;
//echo '<br>Execution time : ' . $time . ' seconds' ;
?>

Powyższy kod jest w całości mojego autorstwa, choć pewnych rzeczy już nie pamiętam dlaczego zrobiłem to tak czy inaczej. W każdym razie w obecnej formie działa to dobrze i raczej nie potrzebuję tykać tych fragmentów kodu. Wklejam wszystko tak jak mam, niektóre fragmenty są wykomentowane i służyły do doraźnego debugowania skryptów.

Trochę zabawy na styku OpenHAB/WAGO wymaga ogarnięcie stanów zmiennych OH, które bywają przeróżne np. ON/OFF zamiast 0/1. Trzeba to odpowiednio obsługiwać w skryptach.

Jeszcze słowo odnośnie wydajności. W skryptach widać pozostałości po analizie czasu wykonanywania skryptów. Jak pamiętam udało mi się uzyskać stabilne działanie przy częstotliwości odświeżania wartości zmiennych około 600-700ms (WAGO->OpenHAB). Co było w zupełności wystarczające. Teraz mam to ustawione na 1000ms i szybciej nie potrzeba.
Natomiast wywołanie zmiany OpenHAB->WAGO odbywa się natychmiast bez jakiejkolwiek zauważalnej zwłoki. Kliknięcie przycisku w telefonie natychmiast wyłącza światło, tak samo szybko jak przycisk na ścianie.

Wykorzystanie powyższych skryptów PHP nie ogranicza się oczywiście do OpenHABa, można ich użyć z dowolnym innym narzędziem do wizualizacji. Można pokusić się o dopisanie kodu odpowiedzialnego za aktualizację brokera MQTT i w ten sposób korzystać ze zmiennych w innych aplikacjach. Możliwości nieograniczone.

Zapisane

blackbox

  • Newbie
  • *
  • Wiadomości: 27
    • Zobacz profil
Odp: OpenHAB - komunikacja z WAGO bez modbusa
« Odpowiedź #1 dnia: Listopada 18, 2020, 08:27:02 am »

Ja aktualnie wykorzystuję Modbus do połączenia z OH. W porównaniu ze starą wersją (ver. 1) obecna wersja Modbus Binding daje dużo więcej możliwości konfiguracji i jest bardziej elastyczna. Przetestowałem też Twój sposób (Http Binding) bazujący na tym co wcześniej opisywał Admin (wykorzystuję też jego pliki dashboardu, jako alternatywę do OH, do sterowania i wizualizacji PLC). Zastanawiam się teraz, na który sposób się zdecydować, czy są między nimi jakieś znaczące różnice np. w sposobie obciążenia rpi (OH) czy PLC. Macie jakieś przemyślenia w tej kwestii? 
Zapisane

elst

  • Newbie
  • *
  • Wiadomości: 8
    • Zobacz profil
Odp: OpenHAB - komunikacja z WAGO bez modbusa
« Odpowiedź #2 dnia: Listopada 19, 2020, 01:58:45 pm »

Cześć,
Osobiście zaczynałem z modbus (kompromis pomiedzy częstotliwością odświeżania i obciążeniem kompa z OpenHAB. Następnie próbowałem poprzez MQTT, ale dla mego sterownika nie znalazłem stabilnie pracującej biblioteki. Finalnie przesiadłem się na zmienne sieciowe i spięcie ich z OpenHAB poprzez Node-red i MQTT. Od ładnych kiku miesięcy działa stabilnie. Zalety - małe obciążenie kompa z OpenHAB/node-red/MQTT broker i natychmiastowa aktualizacja zmiennych po obu stronach. Polecam.
Elst.
Zapisane

sobiso

  • Newbie
  • *
  • Wiadomości: 5
    • Zobacz profil
Odp: OpenHAB - komunikacja z WAGO bez modbusa
« Odpowiedź #3 dnia: Stycznia 20, 2021, 02:11:04 pm »

@elst możesz dać jakiś przykład tego rozwiązania ?
Zapisane

dar3k

  • Newbie
  • *
  • Wiadomości: 11
    • Zobacz profil
Odp: OpenHAB - komunikacja z WAGO bez modbusa
« Odpowiedź #4 dnia: Kwietnia 30, 2021, 11:41:58 pm »

A co jeżeli sterowanie świateł u mnie jest oparte na Fb_LatchingRelay (czyli sygnałem wejściowym jest podane 1 dla ON jeżeli wyłączone i 1 dla OFF jeżeli załączone). Wtedy muszę wywołać wartość 1 i 0 z OpenHAB ?
Zapisane

dar3k

  • Newbie
  • *
  • Wiadomości: 11
    • Zobacz profil
Odp: OpenHAB - komunikacja z WAGO bez modbusa
« Odpowiedź #5 dnia: Maja 01, 2021, 11:20:29 am »

Zmodyfikowałem lekko skrypt wago_set-var.php dodając wysyłanie drugiej komendy val=0 po 1s. - myślę, że krótszy czas nie jest wymagany.

Dla potomnych co szukają podobnego rozwiązania.
<?php
$time_start 
microtime(true);

$wago_ip 'WAGO_IP';
$var = isset($_GET['var']) ? $_GET['var'] : '';
$val = isset($_GET['val']) ? $_GET['val'] : '';
echo 
$val;
$vars explode (','$var);

$var_file file_get_contents('wago_variables.txt');
$var_lookup unserialize($var_file);
$var_count sizeof($vars);
$var_location = isset($var_lookup[$var]) ? $var_lookup[$var] : '';

if (
$val == 'ON'$val 1// obsługa OpenHab

if ($var_location)
{
// BOOLEAN value, translate ON=>1, OFF=>0
if (substr($var_location,-2) == '0|'$val = ($val>0) ? '1' '0';

$post '|1|1|0'.$var_location.$val.'|';
$wago_response curl_post('http://'.$wago_ip.'/plc/webvisu.htm'$post);
sleep(1);
$val 0;
$post_0 '|1|1|0'.$var_location.$val.'|';
$wago_response curl_post('http://'.$wago_ip.'/plc/webvisu.htm'$post_0);
}
else
echo 'Unknown variable';


function 
curl_post($url$post

$ch curl_init(); 
curl_setopt($chCURLOPT_URL$url);
curl_setopt($chCURLOPT_POST1);
curl_setopt($chCURLOPT_HEADER0);
curl_setopt($chCURLOPT_RETURNTRANSFER1);
curl_setopt($chCURLOPT_POSTFIELDS$post);

$result curl_exec ($ch);
curl_close ($ch);

return $result


$time_end microtime(true);
$time $time_end $time_start ;
// echo '<br>Execution time : ' . $time . ' seconds' ;
?>

Zapisane