zoukankan      html  css  js  c++  java
  • 【物联网篇】PHP通过Modbus Tcp实时获取设备数据

    前言:

            最近接触了一个关于PLC工控的小项目,大概场景是,对方一个茶叶工厂。已经通过各种设备组成了自动化的工控系统。并且也让我的一个朋友做了茶园监控和茶园天气环境等的web页面展示,但是还没有工控设备的数据显示。

    需求:

            工控设备已经连接到了一台作为上机位的电脑上,所以要获取设备数据。需要在同一局域网上,通过modbus tcp请求对方已经开放的端口。拿到数据储存到数据库,最后web界面只用按时间顺序获取数据库的数据。

            因为访问对方电脑需要他们提供授权,所以这里演示就以modbus的调试工具,以及后面PHP代码请求示例。

    工具:

    1.  Modbus Slave: 从机端模拟软件,这里测试可以把他作为服务端,PHP为客户端就是取该机子的数据。

    2.   Modbus Poll: 主机仿真器,用于测试和调试Modbus从设备,这里测试也只是把他当做客户端使用。

    3.   ModScan32:  主机/从机模拟程序 ,以后介绍。

    4.   MThings:  一个国产免费软件, 既可以模拟主机设备 又可以模拟从机设备,以后介绍。

    以上软件,可以扫描下面二维码,输入“modbus模拟” 获取地址。

    工具操作:

    一.  Modbus Slave

    1. 创建TCP/IP连接。

    (1). 点击connection->connection,弹出参数窗口,可以按下面确认。

    (2). 配置函数,点击setup->slave definition,弹出参数窗口,默认OK就可以。

    (3). 修改某项数据的值,双击对应的框,弹出后修改OK就可以。

    2. 从机参数说明:

    (1). ID, 机子的设备标识,是slave definition的slave ID

    (2). F, 当前节点的函数码,主机获取代码获取设置数据,需要指定的函数。

    3. 查看发送和接收数据明细。

    (1). 点击display,弹出面板。

    4. 注意

    modbus slave每次连接只能维持10分钟,可能是没有激活。

    一.  Modbus Poll

    1.  通过Tcp获取从机上的数据。

    (1).  连接,点击connection->connection, 选择TCP/IP。

    (2). 修改为slave机子对应的IP地址和端口,点击保存。

     

    (3). 连接成功后,查看读写定义,可以按指定slave配置修改。

    (4). 连接失败,Mbpoll面板会提示红色字体。面板文字说明如下。

    Tx = 4表示向主站发送数据帧次数,图中为4次; Error = 0表示通讯错误次数,图中为0次; ID = 1表示模拟的Modbus子设备的设备地址,图中地址为1;F = 03表示所使用的Modbus功能码,图中为03功能码; SR = 1000ms表示扫描周期。红字部分,表示当前的错误状态,“No Connection”表示未连接状态。

    (5). 查看读写数据。

    PHP代码演示:

    1. modbus类库包下载。

    composer require adduc/phpmodbus

    2. 编写请求 "03 Read Holding Registers"函数示例代码。

    <?php
    /**
    * author: bqs
    * desc: 请求modbus地址
    * 公众号: ZERO开发
    */
    
    require_once 'vendor/adduc/phpmodbus/Phpmodbus/ModbusMaster.php';
    
    	// Modbus master UDP
    	$modbus = new ModbusMaster("127.0.0.1", "TCP");
    	
    	// Read multiple registers
    	try {
    		$recData = $modbus->readMultipleRegisters(1, 0, 5);
    	}
    	catch (Exception $e) {
    		// Print error information if any
    		echo $modbus;
    		echo $e;
    		exit;
    	}
    	
    	var_dump($recData);die;
    	
    	// Print data in string format
    	echo PhpType::bytes2string($recData);
    
    
    ?>

    3. 环境要求。

    1. PHP的LAMP环境已经搭建完毕
    2. 可以不用配置虚拟域名,直接localhost访问modubus_tcp_pro.php文件
    3. PHP版本最好是5.5,因为7.0以上运行会对类的构造函数命名报错
    4. PHP5.5扩展开启了php_sockets
    
    5. 运行成功后,返回数据,数组的索引需要计算匹配modbus slave的地址名
    6. 计算方式: (索引-1)/2

    4. readMultipleRegisters说明。

    参数1:unitId, modbus设备ID,参考slave的slave ID
    参数2:reference, 地址号,在设备内存中,数据的地址引用,参考slave配置的地址
    参数3:quantity,线圈,要去设备中读取的数据量,参考slave配置的quantity

    5. 请求异常的几种情况。

    (1). socket_connect() failed

    slave的连接停止了,需要重新开启。

    (2). Modbus response error code: 2 (ILLEGAL DATA ADDRESS)

    从机设备上数据的内容地址不对,可以根据slave的definition的参数,报错可以查看ModbusMaster类的responseCode方法。

    请求的quantity数超过slave定义的quantity数量也会报内容地址错误,请求只能小于定义的数量。

    6.  关于返回的数组。

    如果请求的是5个数据,phpmodbus会返回元素为10的数组。如果是2个,则返回4个元素数组,以此类推。

    7. 关于返回数组与slave的数据块地址数据对应的方式。

    8. 获取设备上指定数据块的实际的数据。

    (1). 枚举某数据块下索引对应的标识。

    // 数据库设备的数据描述
    	$devicesDataBlock = [
    		"0" => "weather",
    		"1" => "water",
    		"2" => "voice",
    		"3" => "electric",
    		"4" => "air"
    	];

    (2).  根据返回数组的过滤出有用的索引,并匹配设备数据标识。

    实际的数据块索引 = (返回数组的索引-1)/2 

    前提处于2的不能有余数,所以只需要对结果做判断,完整代码如下。

    <?php
    /**
    * author: bqs
    * desc: 请求modbus地址
    * 公众号: ZERO开发
    */
    
    require_once 'vendor/adduc/phpmodbus/Phpmodbus/ModbusMaster.php';
    
    	// Modbus master UDP
    	$modbus = new ModbusMaster("127.0.0.1", "TCP");
    	
    	// Read multiple registers
    	try {
    		$recData = $modbus->readMultipleRegisters(1, 0, 5);
    	}
    	catch (Exception $e) {
    		// Print error information if any
    		echo $modbus;
    		echo $e;
    		exit;
    	}
    	
    	// 数据库设备的数据描述
    	$devicesDataBlock = [
    		"0" => "weather",
    		"1" => "water",
    		"2" => "voice",
    		"3" => "electric",
    		"4" => "air"
    	];
    	
    	$realData = [];
    	
    	foreach($recData as $key => $value) {
    		$indexs = ($key-1)/2;
    		if (($key-1)%2 == 0) {
    			$realData[$devicesDataBlock[$indexs]] = $value;
    		}
    	}
    	
    	var_dump($realData);die;
    	
    	// Print data in string format
    	echo PhpType::bytes2string($recData);
    	
    	// 00050000000601030000000A
    
    
    ?>

    个人网站:www.zerofc.cn 公众号:ZEROFC_DEV QQ交流群:515937120 QQ:2652364582 头条号:1637769351151619 B站:286666708 大鱼号:北桥苏
  • 相关阅读:
    vue苦逼自学之路
    第一次博客作业
    u3d学习资料
    leetcode——Divide Two Integers
    leetcode——Swap Nodes in Pairs
    leetcode——Merge k Sorted Lists
    leetcode——Container With Most Water
    leetcode——Regular Expression Matching
    leetcode——Longest Palindromic Substring
    CC_CALLBACK之间的区别
  • 原文地址:https://www.cnblogs.com/zerofc/p/14827916.html
Copyright © 2011-2022 走看看