В прошлой статье писал о подключении и чтении данных с прибора "ОВЕН" МВ-110-8А по ModBUS RTU RS485 с использованием преобразователя ОВЕН АС4 https://dzen.ru/a/aCXqApd28zMRehU6. В этой статье будет использоваться протокол ModBUS-TCP, то есть подключение через Ethernet интерфейс. Будем использовать модуль дискретных вводов-выводов "ОВЕН" МК210-311. Обмен можно настроить с любым прибором с поддержкой ModBUS-TCP, просто данные приборы у меня есть под рукой. Здесь сразу же опишем, как можно обмениваясь пакетом в 16 бит, расшифровывать и управлять дискретными выходами побитно.
Установку Apache, PHP, modbus-cli опишу еще раз, чтобы не пришлось читать другие статьи, у кого уже установлено и настроено, то можете сразу переходить к Настройке прибора.
Установка web-сервера Apache 2 на Linux
В терминале вводим команду
sudo apt-get install apache2
После установки нам нет необходимости можно проверить установку. Вводим в браузере localhost, Должна появится страница приветствия. Настраивать подключения нет необходимости, потому что будем работать на одном компьютере.
Для программирования нам понадобится скриптовый язык PHP. Установка осуществляется командой:
sudo apt-get install php
После удачной установки нужно разобраться с правми пользователей. Опишу как сделать проще, без заботы о безопасности.
1.
Удобнее, когда apache запускается от основного пользователя (по умолчанию www-data). Для изменения из терминала открываем файл конфигурации
sudo nano /etc/apache2/envvars
Меняем переменные
export APACHE_RUN_USER=(имя пользователя)
export APACHE_RUN_GROUP=(имя пользователя)
сохраняем файл.
2. Заходим в папку /var. Папку www открыть как root с контекстного
меню. В открытой папке находим папку html. Открываем свойства из
контекстного меню. В правах задаем полный доступ к папкам и файлам для пользователя от которого работаем (для начала можно и для всех задать). Закрываем окно папки открытое от администратора. Теперь нужно перезапустить сервисы, легче перезагрузить компьютер.
Теперь можно написать первую программу. Открываем папку /var/www/html. В ней создаем папку Primer. Заходим в эту папку, в ней созаем файл index.php. В файле можете написать
<?php echo 'Привет, МИР!'; ?>
и сохранить его. Затем в браузере набираем адрес localhost/Primer, и мы должны увидеть на странице браузера "Привет, МИР!".
Мы убедились, что apache и php работают.
Подключение приборов
В прошлой статье https://dzen.ru/a/aCLZIGBtABZip2Gz описывал как подключал и настраивал приборы. Здесь опишу в общих чертах.
Для работы с устройствами по протоколу Modbus используем консольную python-утилиту modbus-cli
Установка:
sudo apt install python3-pip
sudo pip3 install modbus_cli
Настройка прибора
По умолчанию IP-адрес МК210-311 - 192.168.1.99 и маска подсети
255.255.0.0. Настраиваем сетевой адаптер Linux компьютера, чтобы он был участником подсети: задаем адрес 192.168.1.XXX, маску подсети 255.255.0.0.
Номера регистров МК210-311: для чтения входов - 51, для чтения и записи выходов - 470 (Номера узнаем из инструкции на модуль МК210-311).
Теперь можем в терминале вводить команды для обмена
modbus -s 1 -v 192.168.1.99:502 51
-s 1 - это адрес слэйва
502 - порт ModBUS TCP
51 адрес регистра, (правильнее вводить i@51 для чтения).
Для включения выходов команда
modbus -s 1 -v 192.168.1.99:502 h@470=1
включится первый выход, для побитной передачи использую преобразование от двоичного кода в dec, нужно учесть что передается старшим байтом вперед, то есть если хотим включить 1 выход то передаем данные 00000001, если хотим последний, то передаем 10000000 то есть число 128
modbus -s 1 -v 192.168.1.99:502 h@470=128
для чтения состояния выходов используем команду:
modbus -s 1 -v 192.168.1.99:502 i@470
За один такт по ModBUS передается 16 бит информации, это нужно учитывать при обмене с модулями. Самый простой и однозначный обмен - передача и чтение целочисленных данных.
Программа обмена
В папке html создаем еще один файл opros.php. В него записываем скрипт опроса, который будет происходить фоново на открытой странице.
<?php
echo "Входы от МК210-311:<br>";
$Chtenie = shell_exec("modbus -s 1 -v 192.168.1.99 i@51");
$KonProbel = strripos($Chtenie, ' ');
$Chtenie = substr($Chtenie, $KonProbel, 10);
//$Chislo = hexdec($Chtenie); // Вариант с промежуточным преобразованием в dec
//$ChisloBin = decbin($Chislo);
//echo "$ChisloBin<br>";
$ChisloBin = base_convert($Chtenie, 16, 2); // Преобразуем ответ в побитную форму
//echo "$ChisloBin<br>";
echo " 1 2 3 4 5 6";
$Vhody = " 0 0 0 0 0 0";
$DlinaChisloBin = strlen($ChisloBin);
for ($i=1; $i <= $DlinaChisloBin; $i++){
$VhBit = substr($ChisloBin, -$i, 1);
//echo $VhBit;
$Vhody = substr_replace($Vhody, $VhBit, $i*2-1, 1); // Указываем состояния входов
}
echo "<br>$Vhody<br><br>";
echo "Выходы МК210-311:<br><br>";
$Chtenie = shell_exec("modbus -s 1 -v 192.168.1.99:502 i@470");
$KonProbel = strripos($Chtenie, ' ');
$Chtenie = substr($Chtenie, $KonProbel, 10);
$ChisloBin = base_convert($Chtenie, 16, 2);
$Dlina=strlen( $ChisloBin );
$ChisloBin = substr_replace('00000000', $ChisloBin, -$Dlina); // Чтобы увидеть полный код, при 1 (будет 00000001)
//echo "$ChisloBin<br>";
$VyhodBin=strrev($ChisloBin);
echo '<table border="1" cellpadding="0" cellspacing="0">';
echo '<tr>';
for($nomer=1; $nomer <= 8; $nomer++){
$VyhBit = substr($VyhodBin, $nomer-1, 1);
$bgcolor = '#FFFFFF';
if ($VyhBit== '1') { $bgcolor = '#66FF00'; }
echo "<td align=\"center\" style =\"background-color:$bgcolor; width:80px; height:10px; border: '#FFFFFF';\"></td>";
}
echo '</tr>';
echo '</table>';
?>
В файле index.php напишем скрипт для запуска фоновой задачи opros.php и в нем добавим обработку нажатия кнопок для управления выходами
<div class="container" >
<div class="row">
<div class="col-12">
<h2>
<div id="content"></div>
</h2>
</div>
</div>
</div>
<script type="text/javascript"
src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script>
function timer(){ // функция, которая запрашивает данные с сервера
$.ajax({ // вызываем встроенную функцию, которая поможет нам получить данные с сервера
url: "opros.php", // какой скрипт серверу нужно выполнить
cache: false, // предыдущие ответы не сохраняем
success: function(html){ // если всё хорошо, отправляем ответ от сервера на страницу в блок content
$("#content").html(html);
}
});
}
$(document).ready(function(){ // как только страница полностью загрузилась
timer();
setInterval('timer()',1000); // начинаем каждую секунду запрашивать новые данные
});
</script>
<?php
echo "<form method=\"post\" onkeypress=\"if(event.keyCode == 13) return false;\">"; // Ввод без нажатия энтер
echo '<table border="1" cellpadding="0" cellspacing="0">';
echo '<tr>';
for($nomer=1; $nomer <= 8; $nomer++){
echo "<td align=\"center\" width=\"80\"><input type = \"submit\" style =\"width:78px; height:40px;\" name = \"but$nomer\" value = \"$nomer\"/></td>";
}
echo '</tr>';
echo '</table>';
// Передача данных из чекбокса без учета нажатых кнопок
echo "<br><br><br><br>";
echo '<table border="1" cellpadding="0" cellspacing="0">';
echo '<tr>';
for($nomer=1; $nomer <= 8; $nomer++){
$VyhBit = substr($VyhodZadan, $nomer-1, 1);
if ($VyhBit== '1') {
echo "<td align=\"center\" width=\"60\" style =\"background-color: $bgcolor\">$nomer<br><input type=\"checkbox\" name = \"activ1$nomer\" style =\"width:31px; height:40px;\" checked></td>";
}
else{
echo "<td align=\"center\" width=\"60\" style =\"background-color: $bgcolor\">$nomer<br><input type=\"checkbox\" name = \"activ1$nomer\" style =\"width:31px; height:40px;\"></td>";
}
if (isset($_POST["activ1$nomer"])){$activ1V[$nomer] = '1';}
else {$activ1V[$nomer] = '0';}
$VyhodZadan=$VyhodZadan.$activ1V[$nomer] ;
}
echo '</tr>';
echo '</table>';
//echo "<br>$VyhodZadan";
echo "<br><input type = \"submit\" name = \"button1\" value = \"ЗАПИСАТЬ\"/><br>";
echo '</form>';
// обработка нажатий кнопок
for($nomer=1; $nomer <= 8; $nomer++){
if ($_POST["but$nomer"]){
$Chtenie = shell_exec("modbus -s 1 -v 192.168.1.99 i@470"); // Читаем текущее состояние выходов
$KonProbel = strripos($Chtenie, ' ');
$Chtenie = substr($Chtenie, $KonProbel, 10);
$ChisloBin = base_convert($Chtenie, 16, 2);
$Dlina=strlen( $ChisloBin );
$ChisloBin = substr_replace('00000000', $ChisloBin, -$Dlina); // Чтобы увидеть полный код, при 1 (будет 00000001)
$VyhodBin=strrev($ChisloBin);
$VyhodSost = substr($VyhodBin, $nomer-1, 1);
if ($VyhodSost == '1' ){ // Условие для инверсии переключения
$perekluch = 0;
} else {
$perekluch = 1;
}
$VyhodBin = substr_replace($VyhodBin, $perekluch, $nomer-1, 1); // Вставляем нужное состояние при нажатии кнопки в текщую комбинацию
$VyhodyMK=strrev($VyhodBin);
$Vyhody = bindec($VyhodyMK);
shell_exec("modbus -s 1 -v 192.168.1.99 h@470=$Vyhody"); // Записываем в прибор в виде dec числа
}
}
// Передача из чекбокса
if ($_POST['button1']){
$VyhodMK=strrev($VyhodZadan);
$Vyhody = bindec($VyhodMK);
shell_exec("modbus -s 1 -v 192.168.1.99:502 h@470=$Vyhody");
}
?>
Сохраняем и обновляем страницу в браузере localhost/Primer.
Должно получиться:
Обработка входов не прорисована, выведены состояния входов. После чтения происходит преобразование в бинарный код, он переворачивается, так как происходит передача старшим битом вперед, и выводится на экран. Выходы прорисованы в виде закрашиваемых ячеек таблицы.
Чтение входов и выходов происходит фоново - файл opros.php
Управление выходами происходит в основном скрипте файл - index.php. Кнопки под соответствующими индикаторами включения каналов запускают скрипт проверки состояния выходов на данный момент, изменения состояния соответствующего бита и отправки необходимого сочетания включенных и выключенных выходов в виде числа. При изменении состояния происходит перезагрузка страницы. Можно заморочиться и сделать нажатия кнопок без перезагрузки страницы, но здесь заморачиваться не стал
Ячейки с чекбоксами формируют необходимую комбинацию и отправляют команду на прибор, без учета текущего состояния.
Читать данные побитно и передавть их, у меня не получилось, если кто-то знает как, буду рад прочитать в комментариях.
Таким образом получилось управлять модулем дискретных вводов-выводов через Ethernet порт. Это уже дает возможность сделать контроллер управления каким либо устройством с одноплатных компьютеров вроде Raspberry Pi. При этом основную программу создавать без дополнительных программ типа CodeSys, Beremiz. Вообще, конечно же у Raspberry Pi есть свои GPIO выходы и можно управлять с них. Но гораздо надежней использовать модули ввода-вывода. А если использовать программируемые реле, то одноплатник можно использовать как устройство верхнего уровня. Распределенными тех процессами будут управлять программируемые реле, функцию согласования, сбора данных, интерфейса пользователя будет выполнять одноплатный компьютер с сенсорным дисплеем.