Мой дом.

Modbus TCP/IP & PHP

 

Иногда бывают ситуации, когда надо прикрутить промышленное железо к компьютеру.

Совсем недавно мне потребовалось управлять нагрузками и опрашивать датчики на растоянии около 50 метров от компьютера. По счастливой случайности в наличии оказался комуникационный Ethernet-модуль Momentum 170ENT11002 и модуль I/O 170ADM350-10.

Модуль 170ENT11002 поддерживает протокол управления Modbus TCP/IP и я начал искать софт, способный общаться с моим железом, работающий под Linux и естественно бесплатный.

Первое, что я нашел, была утилита Modpoll 

Эта утилитка работает как под 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 оставляет желать лучшего (сужу по примерам, т.к. до самого класса добраться не успел)

 

 

Для примера, вот небольшой скриптик (на 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

Сделать бесплатный сайт с uCoz