Gdy zrozumie się już jak wygląda pamięć sterownika i jak można adresować zmienne, by ich wartości były przechowywane w konkretnie określonym miejscu (-->poprzedni artykuł), można zabrać się za poznanie protokołu MODBUS. MODBUS jest obecnie przemysłowym standardem wykorzystywanym do komunikowania się z najróżniejszymi urządzeniami. Potencjalne wykorzystania to np. połączenie sterownika z pulpitem HMI, połączenie z modułem 1-wire, połączenie sterownika z komputerem PC itd.
UWAGA! Wszystko, co tu znajdziecie jest amatorską próbą objęcia tematu prostym umysłem. Znawców rzeczy proszę o wyrozumiałość. Niestety żaden 'profesjonalny' opis nie był dla mnie do przebrnięcia dopóki sam wszystkiego nie poskładałem z wielu przypadkowych kawałków.
Komunikacja w MODBUSie odbywa się poprzez wykonywanie tzw. funkcji. Funkcje wymiany danych można dla uproszczenia podzielić wg dwóch kryteriów: czy dotyczą zmiennych typu BIT (BOOL) lub WORD oraz czy można zmieniać ich wartość lub jedynie odczytywać.
Poniższa tabela przedstawia główne funkcje (FC_) wg powyższego podziału:
Read/Write | Read only | |
BIT/BOOL |
FC1 - Read Coil |
FC2 - Read Discrete Input |
WORD |
FC3 - Read Holding Register |
FC4 - Read Input Registers |
Powiem szczerze, nie bardzo przemawiają do mnie nazwy. Nie wiem, czemu coil, discrete input, register... Najważniejsze jest dla mnie, że by odczytać pojedynczy bit mogę wykorzystać FC1 lub FC2, aby zapisać bit - FC5 lub FC15; dla zmiennych typu WORD wykorzystać trzeba FC3, FC4 lub FC23 do odczytu i FC6, FC16 lub FC23 do zapisu. Kropka.
Dla podkreślenia - MODBUS służy do odczytywania i ew. modyfikacji konkretnych obszarów pamięci sterownika. Nie obsługuje poleceń typu "wyłącz światło" lub "podaj temperaturę". Aby zrobić powyższe, trzeba zmodyfikować lub odczytać jakąś konkretną komórkę pamięci. Jaką? O tym poniżej.
Pozostaje teraz zrozumieć, jak adresy protokołu MODBUS mają się do adresów w pamięci sterownika. W instrukcji sterownika znajduje się cały dział zatytułowany "MODBUS Register Mapping". Można tam znaleźć tabele mapujące adresy: oddzielnie dla funkcji odczytujących pojedyncze bity i osobno dla tych, odczytujące słowa:
Oto tabela do odczytywania/zapisu pojedynczych bitów (funkcje FC1 i FC2). Dla wejść i wyjść - trzeba znać ich kolejny numer, a nie adres w pamięci:
Adres MODBUS [dec] | Zakres pamięci | Opis |
0...511 |
Obszar fizycznych wejść |
Pierwsze 512 wejść cyfrowych |
512...1023 |
Obszar fizycznych wyjść |
Pierwsze 512 wyjść cyfrowych |
12288...32767 |
%MX0...%MX1279.15 |
NOVRAM |
Dla odczytywania danych typu WORD (funkcje FC3 i FC4):
Adres MODBUS [dec] | Zakres pamięci | Opis |
0...255 |
%IW0...%IW255 |
Obszar fizycznych wejść |
512...767 |
%QW0...%QW255 |
Obszar fizycznych wyjść |
12288...24575 |
%MW0...%MW12287 |
NOVRAM |
A więc:
Z zapisywaniem (Funkcje FC5 i FC6) jest bardzo podobnie, z tym że wejścia sterownika są read-only.
Omówmy więc konkretny przykład. Oto stan mojego sterownika:
Konieczne jest zaznaczenie, że wejście IN4 jest 4. fizycznym wejściem sterownika a OUT 3 3. fizycznym wyjściem. Tak odległe adresy w pamięci (%IX11.2 i QX3.2) są konsekwencją występowania innych modułów zajmujących pierwsze obszary pamięci (czujniki analogowe, moduł RS232).
Przykładowe zapytania:
MODBUS | WAGO WebServer | Odpowiedź |
FC2 3 (czwarte wejście) |
READPI?ADR=IX11.3&FORMAT=%x |
TRUE |
FC1 514 (trzecie wyjście) |
READPI?ADR=QX3.2&FORMAT=%x |
TRUE |
FC4 11 |
READPI?ADR1=IW11&FORMAT=%d |
8 |
FC5 515 |
READPI?ADR1=QW3&FORMAT=%d |
204 |
Jak to wszystko sprawdzić? Jedną z możliwości jest wykorzystanie biblioteki phpmodbus dostępnej pod https://code.google.com/p/phpmodbus/. Integruje ona funkcje MODBUSa w języku PHP.
Bez znajomości samego protokołu można pobierać/wysyłać dane do sterownika. Oto przykład odczytujące dane z adresu 515 (ostatni przykład z powyższej tabeli):
<?php require_once dirname(__FILE__) . '/Phpmodbus/ModbusMaster.php'; // Create Modbus object $modbus = new ModbusMaster("192.168.1.3", "UDP"); try { // FC 1 $recData = $modbus->readMultipleRegisters(0, 515, 1); } catch (Exception $e) { // Print error information if any echo $modbus; echo $e; exit; } // Print read data echo "Data:"; var_dump($recData);
Wykonanie skryptu zwraca:
Data: array(2) { [0]=> int(0) [1]=> int(204) }
Co oznaczają te liczby? Odczytaliśmy słowo składające się z dwój bajtów. Pierwszy równy jest 0, drugi to 204. 204 w systemie dwójkowym to 11001100. Czytając od tyłu mamy więc: 2 pierwsze bity false, 2 kolejne TRUE (czyli %QX3.2 i %QX3.3), 2 kolejne false i 2 kolejne true. Wszystko się zgadza.
Teraz pozostaje mi znaleźć jakieś urządzenie do integracji i przygotować skrypty PHP, które posłużyłyby jako pośrednik między stroną www a sterownikiem. Warto by sprawdzić, czy odpytywanie sterownika przez wbudowany webserwer (komendami READPI) jest szybsze niż komunikacja z pośrednictwem serwera na RaspberryPi, który odpytywałby sterownik po MODBUSie.