Witam!

Zapraszam do zapoznaniu się z najważniejszymi funkcjami ‘inteligentnego’ systemu, który instalowałem w moim nowo wybudowanym domu. Całość oparta jest o sterownik programowalny PLC firmy WAGO, pozwalała w tym czasie sterować 100 obwodami elektrycznymi i wykonywać zdarzenia inicjowane poprzez 90 wejść cyfrowych.

System jest cały czas rozwijany i usprawniany. Na dziś steruje oświetleniem, roletami, zaworami w rozdzielni c.o., podlewaniem ogrodu, bramą wjazdową, piecem i rekuperatorem.

Zapraszam zainteresowanych do przejrzenia opisu kroków projektowania i wykonania oraz elementów składowych systemu. Miejcie jednak dużo dystansu. Wszystko, co tu znajdziecie jest dziełem finansisty, nie elektryka. Moja przygoda z niezaizolowanymi kablami 220V zaczęła się w dniu zakupu sterownika PLC…

Wizualizacja Image 01 NEW Rolety

Wprowadzenie

Codesys oferuje środowisko programistyczne dla sterowników PLC. Autorzy programu przygotowali dla użytkowników wiele ułatwień pozwalających zaoszczędzić czas… pozostawili jednak wiele ‘rozwiązań specyficznych’, które potrafią spowodować wypadanie włosów z głowy. Oto krótka prezentacja głównych funkcji.

Dlaczego Raspberry Pi

Kryteria wyboru rozwiązania umożliwiającego komunikację pomiędzy PLC a siecią 1-wire były następujące: ma być w miarę tanio. Mój dom działa i wszelka rozbudowa jest motywowana bardziej kaprysem i ciekawością niż potrzebą. Rozwiązania oparte o WIRE-CHIP firmy SolidChip (http://www.solidchip.eu/index.php?menu=24) są sprawdzone i działają poprawnie. Wymagają jednak modułu WIRE-CHIP

Czujniki DS18B20 i rozległa sieć 1-Wire

1wire sensor3Nadszedł czas na przetestowanie tego, co moduły "wzmacniające" sieć 1-wire potrafią.  Zakupiłem sporą paczkę czujników i zabrałem sie do pracy. Jako że montaż czujników w obudowach łączników ściennych miałem już przećwiczony, wszystko poszło całkiem sprawnie.  Oto kilka zdjęć i szczegółów...

Pomiar poziomu cieczy

Tytuł brzmi wspaniale by odwrócić uwagę czytelników od faktu, że chodzi o czujnik poziomu nieczystości w szambie. Zdarzyło mi się całkiem niedawno zapomnieć o konieczności sprawdzenia, czy już nie pora wezwać zaprzyjaźnionego szambelana. O zaniedbaniu dowiedziałem się z nienacka, po wejściu do łazienki w piwnicy, gdzie w obniżeniu podłogi pod prysznicem wdzierała się do domu ciecz, rozsiewając zabójczą woń. Był to piątek wieczór, sytuacja fatalna... Taki ze mnie magik od inteligentnych domów, a mam szambo w łazience.

Jeszcze jeden sposób komunikacji z PLC

Przed paroma dniami otrzymałem maila od Sergio, który zwrócił moją uwagę na projekt WebVisu rozwijany przez Franka Benkerta w ramach SourceForge.net (https://sourceforge.net/projects/webvisu/). Autorowi udało się rozszyfrować zasady komunikacji pomiędzy sterownikiem, a standardowymi wizualizacjami webowymi opartymi o aplety Javy. Gdy już rozpracował protokół, przygotował kod, który odtwarza wizualizacje zrobione w CoDeSysie w dowolnej przeglądarce przy wykorzystaniu WYŁĄCZNIE JavaScriptu. Brzmi fascynująco? Bo jest!

Oto krótka ilustracja, o co w ogóle chodzi:

webvisu

Aby zobaczyć, jak to wszystko działa należy:

  1. Włączyć obsługę wizualizacji webowych w Target Settings->Visualization->Web visualization
  2. Przygotować wizualizację w CoDeSys, wgrać ją wraz z programem na sterownik,
  3. Pobrać ze strony sourceforge plik WebVisu.html
  4. Wgrać przez FTP ww. plik na sterownik do katalogu ‘webserv’
  5. W przeglądarce wpisać adres: „http://IPsterownika/webserv/webvisu.html"

Dokonania Franka Benkerta są dla mnie niesamowite z 2 powodów:

  1. Pokazał sposób komunikacji ze sterownikiem PLC, o którym nie wspomina żadna dokumentacja,
  2. Pokazał możliwość otrzymania ze sterownika informacji o miejscu umieszczenia w pamięci (adresie) zmiennych, którym nie przypisano adresu w kodzie programu.

 

 

Zacznijmy od protokołu

Komunikacja ze sterownikiem odbywa się przez zapytania POST wysyłane na adres /plc/webvisu.htm.

Aby odczytywać zmienne (czyli również stany wejść/wyjść) zapytanie powinno być ustrukturyzowane w następujący sposób:

| 0 | liczba-adresów-do-odczytania
| nr-zmiennej-licząc-od-zera | adres | adres | ilość-bajtów | rodzaj-zmiennej |

Odczytanie stanów 2 pierwszych wyjść na moim sterowniku odbywa się w następujący sposób:

|0|2|
0|2|48|0|0|
1|2|29|0|0|

Odpowiedź sterownika to|0|0|

Adresu, ilości bajtów i rodzaju zmiennej wcale nie trzeba znać. Dane te publikowane są przez sterownik w pliku plc_visu.xml. Piszę o tym w drugiej części artykułu.

Zmiana wartości zmiennej odbywa się komendą:

|1| liczba-adresów-do-zmiany
|nr-zmiennej-licząc-od-zera| adres | adres | ilość-bajtów | rodzaj-zmiennej | wartość |

Zmiana wartości zmiennej typu BYTE zaadresowanej przeze mnie na %MB6 na 7 wykonana zostanie komendą
|1|1|0|0|6|1|2|7|

Dla takiego amatora, jak ja, który nie lubi MODBUSA, takie zasady komunikacji są przejrzyste i proste. Do odczytania stanów 100 wyjść wystarczy prosty program w PYTHONie:

#!/usr/bin/python
import requests 	#you might need to install requests separatelly
req = "|0|100"
for num in range (0,99):
        req+= "|"+str(num)+"|2|"+str(num)+"|1|2"
req+= "|"
r = requests.post('http://192.168.1.3/PLC/webvisu.htm', data=req)
print r.text

Odpowiedź sterownika to:

|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|….

Czas wykonania takiego skryptu to 0.2 sek.

W javascriptcie (z jQuery) odczytanie danych wyglądałby następująco:

<script>
req = "|0|100";
for (var i=0; i<100; i++) {
req+="|"+i+"|2|"+i+"|1|2";
}
req += "|"
console.log(req);
$.ajax({
	type: "POST",
	url: "http://192.168.1.3/plc/webvisu.htm",
	data: req,
	success: function (data) {
		console.log(data);
	}
});
</script>

Przewaga tego sposobu nad wykorzystywanym przeze mnie dotychczas READPI/WRITEPI jest dość oczywista. Można bardzo szybko odczytywać dużą ilość zmiennych. To duże uproszczenie wobec konieczności każdorazowego konstruowania zapytania „READPI?ADR=…..&FORMAT=%d”. Tak naprawdę powinienem teraz przepisać całą moją wtyczkę i wszystkie programy…

Adresowanie

Poniższe 4 paragrafy są teorią dla dociekliwych. Niecierpliwi mogą od razu przewinąć do słów „Nie trzeba jednak tego wszystkiego wiedzieć”

Każda z odczytywanych lub zapisywanych zmiennych przedstawiana jest jako:

| adres | adres | ilość-bajtów | rodzaj-zmiennej |

Wygląda na to, że pierwsza część adresu to grupa. Na moim sterowniku zmienne, którym przypisałem adres (np. przez AT %MB0 : BYTE) umieszczone są w grupie 0, wejścia – 1, wyjścia – 2, inne zmienne użyte w programie – 4.

Pierwsze wyjście DO ma u mnie adres %QX3.0, jako że pierwsze trzy ‘słowa’ zajęte zostały przez moduł komunikacji RS232. 3 słowa to 3x16 bitów = 48 bitów, licząc od 0 to 0-47. Stąd też adres mojego %QX3.0 to 2,48.

Ilość bajtów zależy od rodzaju zmiennej. Dla fizycznych wejść i wyjść jest to 0 (?), dla BOOL i BYTE – 1, WORD – 2, DWORD – 4, STRING – o 1 więcej niż długość.

Rodzaje zmiennej określone są następująco: BOOL – 0, INT – 1, BYTE – 2, WORD – 3, DINT – 4, DWORD – 5, REAL – 6, TIME – 7, STRING – 8, ARRAY – 9. Choć określenie rodzaju nie jest krytyczne, tj. można odczytywać całą pamięć kawałkami 1-bajtowymi określając rodzaj jako 2. Właściwe podanie rodzaju zapewnia jednak odpowiednio sformatowaną odpowiedź. 4-znakowy string, może wrócić jako „abcd”, gdy podamy rodzaj 8, lub 47821, gdy podamy rodzaj 5.

Nie trzeba jednak tego wszystkiego wiedzieć. Kolejna rzecz, którą opisuje Frank Benkert dotyczy publikowania przez sterownik adresów zmiennych użytych w wizualizacji.

Wyobraźmy sobie prosty program z następującymi zmiennymi:

PROGRAM PLC_PRG
VAR
	AddressedVariable  AT %MB0 : BOOL;
	StringVariable	: STRING(5);
	ByteVariable : BYTE;
	WordVariable: WORD;
END_VAR

W programie takim tworzymy wizualizację „PLC_VISU” z 4 elementami wyświetlającymi wartości ww. zmiennych, oraz z piątym, który prezentuje wartość pierwszego wyjścia w sterowniku. Gdy wgramy taki program do sterownika (zakładając, że opcja Target Settings->Visualization->Web visualization jest zaznaczona), w katalogu PLC wygenerowany zostanie plik „plc_visu.xml”.

W dolnej części tego pliku znajdziemy:

<variablelist>
	<variable name="PLC_PRG.AddressedVariable">0,0,1,0</variable>
	<variable name="PLC_PRG.StringVariable">4,0,6,8</variable>
	<variable name="PLC_PRG.ByteVariable">4,6,1,2</variable>
	<variable name="PLC_PRG.WordVariable">4,7,2,3</variable>
	<variable name=".OUT0">2,0,0,0</variable>
</variablelist>

Oto adresy, których szukaliśmy! Możemy teraz odczytać ich wartości – przy pomocy zapytań “|0|…”, READPI czy też MODBUSA. Dla mnie jest to odkrycie przełomowe. Oto mam dostęp do danych bez konieczności wcześniejszego żmudnego adresowania w sterowniku.  Możemy odczytać wartość StringVariable wysyłając "|0|1|0|4|0|6|8|" lub wszystkich ww. adresów poprzez"|0|5|0|0|0|1|0|1|4|0|6|8|2|4|6|1|2|3|4|7|2|3|4|2|0|0|0|"

Zamiast więc przypisywać każdej zmiennej, którą chcemy odczytywać i zmieniać z zewnątrz, adresu poprzez „VAR variable AT %M….”, możemy umieścić na wizualizacji element, prezentujący jej wartość. Po wgraniu wizualizacji na sterownik, adres takiej zmiennej będzie do znalezienia w pliku nazwa_vizualizacji.xml.

Ważna uwaga: umieszczenie zmiennych i bloków funkcyjnych w pamięci sterownika ustalane jest przez kompilator w czasie generowania kodu programu. Każda zmiana w samym programie może prowadzić do przypisania innych adresów. Wszelkie programy integrujące powinny więc prowadzić komunikację w oparciu o adresy pozyskane każdorazowo z pliku xml… Dla programistów to jednak nic trudnego ;)

Wielkie podziękowania dla Sergio za podrzucenie mi tematu ;)