Modbus TCP/IP & PHP
Иногда бывают ситуации, когда надо прикрутить промышленное железо к компьютеру.
Совсем недавно мне потребовалось управлять нагрузками и опрашивать датчики на растоянии около 50 метров от компьютера. По счастливой случайности в наличии оказался комуникационный Ethernet-модуль Momentum 170ENT11002 и модуль I/O 170ADM350-10.
Модуль 170ENT11002 поддерживает протокол управления Modbus TCP/IP и я начал искать софт, способный общаться с моим железом, работающий под Linux и естественно бесплатный.
Первое, что я нашел, была утилита Modpoll
- http://www.focus-sw.com/fieldtalk/modpoll.html Версия 2.4
- http://www.modbusdriver.com/modpoll.html Версия 3.1
Эта утилитка работает как под Windows, так и под Linux. Пользоваться ей крайне просто, копируете исполняемый файл из архива на диск и в командной строке набираете (пример для win):
Отключить WD-таймер
C:\>modpoll.exe -m tcp -r 61441 192.168.100.120 0
Записать выходы
C:\>modpoll.exe -m tcp -r 1 192.168.100.120 8888
Надо обратить внимение на то, что адрес регистра WD, по описанию модуля Momentum 461441 в десятичном виде, а для работы Modpoll необходимо указвать 61441. Почему так не знаю, но тоже относится и к другим регистрам. Единственный недостаток Modpoll, на мой взгляд, это закрытость кода.
Так-как планировалось писать скрипт управления на PHP, я счел не совсем удобным пользоваться функцией exec и решил поискать готовые решения на PHP.
Нашлось две реализации протокола Modbus на PHP:
- PhpModbus - http://code.google.com/p/phpmodbus/ , но этот проект не поддерживает Modbus TCP/IP (3.01.2012 вышла новая версия Phpmodbus 0.5.r70. Добавлена поддержка Modbus TCP, об этом ниже)
Проверил работу библиотеки PHP Modbus TCP, работает нармольно.
Единственная загвоздка оказалась с адресацией регистров.
Оказалось, что библиотека использует не адресацию Modbus, а использует адресацию контроллеров Telemecanique TSX.
Для лучшего понимания адресации советую почитать https://sites.google.com/site/fieldbusb ... modbus-rus У меня "просветление" настало после прочтения "Пример 6.3. MODBUS. Модель данных для различных типов устройств".
Вкратце: для регистра 461441 адрес будет равен 461441-400001=61440, а для 400001 адрес 400001-400001=0, но это действительно только для десятичного представления адресов регистров, т.к. четверка в начале не является частью адреса, а указывает на способ адресации (16-битные слова) и ее надо просто откинуть и из адреса вычесть 1 (адреса Modbus начинаются с 1).
Вот пример использования:
<?php
require_once dirname(__FILE__) . '/../Phpmodbus/ModbusMaster.php';
// Create Modbus object
$modbus = new ModbusMaster("192.168.100.120", "TCP");
/**************************************************************************/
// Data to be writen
$data = array(12345);
$dataTypes = array("WORD");
try {
// FC23
// ID-устройства, адрес начального регистра для чтения, кол-во считываемых регистров,
// начальный регистр для записи, данные для записи, тип данных.
$recData = $modbus->readWriteRegisters(0, 0, 1, 0, $data, $dataTypes);
}
/*
try {
// FC16
$modbus->writeMultipleRegister(0, 0, $data, $dataTypes);
}
*/
/*
try {
// FC 3
// read 10 words (20 bytes) from device ID=0, address=12288
$recData = $modbus->readMultipleRegisters(0, 0, 1);
}*/
/*------------------------------------------------------------------------*/
/*
// Data to be writen
$data = array(TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE,
TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE);
try {
// FC15
$modbus->writeMultipleCoils(0, 0, $data);
}
*/
/*
try {
// FC 1
$recData = $modbus->readCoils(0, 0, 12);
}
*/
/**************************************************************************/
catch (Exception $e) {
// Print error information if any
echo $modbus;
echo $e;
exit;
}
// Received data
echo "<h2>Received Data</h2>\n<pre>";
print_r($recData);
echo "</pre>";
// Chunk the data array to set of 4 bytes
$values = array_chunk($recData, 2);
// Get signed integer from INT interpretation
foreach($values as $bytes) echo PhpType::bytes2signedInt($bytes) . "</br>";
// Get unsigned integer from WORD interpretation
foreach($values as $bytes) echo PhpType::bytes2unsignedInt($bytes) . "</br>";
?>
PS Для себя решил использовать эту библиотеку по следующим причинам:
1. Проект живой и в случае каких-то проблем можно связаться с разработчиками.
2. Есть поддержка Modbus UDP. (В дальнейшем планирую использовать)
3. Качество кода Class: ModbusTcp оставляет желать лучшего (сужу по примерам, т.к. до самого класса добраться не успел)
- Class: ModbusTcp - http://www.phpclasses.org/package/377-PHP-Allow-to-read-ModbusTCP-compatible-devices-direct-with-PHP-without-third-package-.html , этот проект удовлетворил все мои потребности.
Для примера, вот небольшой скриптик (на 99% это пример из проекта):
<?php
require_once "Class_ModbusTcp.inc";
$Plc = new ModbusArray;
$Plc->SetAdIpPLC ("192.168.100.120");
/********************************************************/
$Adr = 400001;
$Values = array(3333);
// Записать значения $Writebuffer начиная с адреса $FirstArrAdresse
$result = $Plc->WriteModbus($FirstArrAdresse, $Writebuffer) ;
/********************************************************/
$Plc->print_r_log($result) ;
$Plc->ModClose();
unset($Plc);
?>
PS Ресурсы по Modbus: http://www.modbus.org/tech.php