【物联网】Modbus 协议及Qinghub物联网平台应用

Modbus 协议简介

QingHub设计器在设计物联网数据采集时不可避免的需要针对Modbus协议的设备做相关数据采集,这里就我们的实际项目经验分享Modbus协议

你可以通过QingHub作业直接体验试用,也可以根据手册开发相应的代码块。 qinghub项目已经全面开源。

源码文件地址: https://gitee.com/qingplus/qingcloud-platform

QingHub设计器体验

简介

Modbus由MODICON公司于1979年开发,是一种工业现场总线协议标准。1996年施耐德公司推出基于以太网TCP/IP的Modbus协议:ModbusTCP。Modbus协议是一项应用层报文传输协议,包括ASCII、RTU、TCP三种报文类型。 标准的Modbus协议物理层接口有RS232、RS422、RS485和以太网接口,采用master/slave方式通信。

功能码

重点介绍常用如下几个功能码:
1: 读线圈寄存器
2: 读离散输入寄存器
3: 读保持寄存器
4: 读输入寄存器
5: 写单个线圈寄存器
6: 写单个保持寄存器
15: 写多个线圈寄存器
16: 写多个保持寄存器

几种继承器介绍

线圈寄存器

实际上就可以类比为开关量,每个bit都对应一个信号的开关状态。所以一个byte就可以同时控制8路的信号。

离散输入寄存器

如果线圈寄存器理解了这个自然也明白了。离散输入寄存器就相当于线圈寄存器的只读模式,他也是每个bit表示一个开关量,而他的开关量只能读取输入的开关信号,是不能够写的。

保持寄存器

这个寄存器的单位不再是bit而是两个byte,也就是可以存放具体的数据量的,并且是可读写的。比如设置时间年月日,不但可以写也可以读出来现在的时间。写分为单个写和多个写。

输入寄存器

只剩下这最后一个了,这个和保持寄存器类似,但是也是只支持读而不能写。一个寄存器也是占据两个byte的空间。

通信协议 (重点看这里就可以了)

Modbus设备可分为主站(poll)和从站(slave)。主站只有一个,从站有多个,主站向各从站发送请求帧,从站给予响应。在使用TCP通信时,主站为client端,主动建立连接;从站为server端,等待连接(当然只要你愿意并足够熟悉,也可以反向操作)。

Mobus 的报文大致分为两类: MBAP+PDU。
MBAP= Modbus Application Protocol Header(Modbus应用协议) 头部
PDU = Protocol Data Unit (数据单元)

MBAP报文

  • MBAP为报文头,长度为7字节,组成如下:
事务处理标识协议标识长度单元标识符
2字节2字节2字节1字节

含义:
事务处理标识:可以理解为报文的序列号,一般每次通信之后就要加1以区别不同的通信数据报文。
协议标识符:00 00表示ModbusTCP协议。
长度:表示接下来的数据长度,单位为字节。
单元标识符:可以理解为设备地址。

PDU报文结构

PDU结构
PDU由功能码+数据组成。功能码为1字节,数据长度不定,由具体功能决定。
主站请求:功能码+数据
从站正常响应:请求功能码+响应数据
从站异常响应:异常功能码+异常码,其中异常功能码即将请求功能码的最高有效位置1,异常码指示差错类型

指令实例

查询(功能码0x03)

基本流程就是:
发送:地址 + 我要查 +(寄存器起始地址+个数)+校验
回复:地址 +(回)我要查 +(数据的字节数+数据) +校验

主机发送: 01 03 00 00 00 01 84 0A
含义:
01-地址
03-功能码,代表查询功能,其他功能后面再说
00 00-代表查询的起始寄存器地址.说明从0x0000开始查询.
00 01-代表查询了一个寄存器.结合前面的00 00,意思就是查询从0开始的1个寄存器值;
84 0A-CRC

从机回复: 01 03 02 12 34 B5 33
含义:
01-地址
03-功能码
02-代表后面数据的字节数,因为上面说到,一个寄存器有2个字节,所以后面的字节数肯定是2*查询的寄存器个数;
12 34-寄存器的值是12 34,结合发送的数据看出,01这个寄存器的值为12 34
B5 33-CRC校验码

修改单个寄存器(功能码0x06)

主机送: 01 06 00 00 00 01 48 0A
01-从机地址
06-功能码:修改单个寄存器功能,修改有些不同,有修改一个寄存器和修改多个寄存器;
00 00-修改的起始寄存器地址.说明从0x0000开始.
00 01-修改的值为00 01.结合前面的00 00,意思就是修改0号寄存器值为00 01;
48 0A-CRC

从机回复: 01 06 00 00 00 01 48 0A
01-从机地址
06-功能码:修改单个寄存器功能;
00 00-修改的起始寄存器地址.说明是0x0000.
00 01-修改的值为00 01.结合前面的00 00,意思就是修改0号寄存器值为00 01;
48 0A-CRC

修改多个寄存器(功能码0x10)

主机发送: 01 10 00 00 00 02 04 11 22 33 44 42 5A
01-从机地址
10-功能码,代表修改多个寄存器功能;
00 00-代表修改的起始寄存器地址.说明从0x0000开始.
00 02代表修改的寄存器数量
04 -表示修改的总字节数,由于只修改了1个寄存器,所以数据要有两个字节;
11 22 33 44-表示修改的值,结合上面,就是从第0000寄存器开始修改2寄存器值为11 22 33 44,就是把0000寄存器改为11 22,0001为33 44,
42 5A -循环冗余校验,是modbus的校验公式,从首个字节开始到22前面为止;

从机回复: 01 10 00 00 00 02 41 C8
01-从机地址
10-功能码
00 00-代表修改的起始寄存器地址.说明是0x0000.
00 02-代表修改的寄存器数量,只需要回复这么多久足够了,从机告诉主机修改了哪几个寄存器就足够了;
41 C8-循环冗余校验;

java 开发

maven 依赖
<!-- Modbus -->
<dependency>
    <groupId>com.infiniteautomation</groupId>
    <artifactId>modbus4j</artifactId>
    <version>3.1.0</version>
</dependency>
<dependency>
    <groupId>com.digitalpetri.modbus</groupId>
    <artifactId>modbus-master-tcp</artifactId>
    <version>1.2.0</version>
</dependency>
API 实例

第一:建立连接

/**
 * 获取 Modbus Master
 * @return ModbusMaster
 * @throws ModbusInitException ModbusInitException
 */
public ModbusMaster getMaster(ModbusConfig modbusConfig) throws ModbusInitException {
    log.debug("Modbus Tcp Connection Info {},{}", modbusConfig.getHostName(),modbusConfig.getPort());
    ModbusMaster modbusMaster = masterMap.get(modbusConfig.getEquipmentId());
    if (null == modbusMaster) {
        IpParameters params = new IpParameters();
        params.setHost(modbusConfig.getHostName());
        params.setPort(modbusConfig.getPort());
        **params.setEncapsulated(true);**
        modbusMaster = modbusFactory.createTcpMaster(params, true);
        modbusMaster.init();
        masterMap.put(modbusConfig.getEquipmentId(), modbusMaster);
    }
    return modbusMaster;
}

注意
这里有一个需要特别关注的地方,params.setEncapsulated(true)

如果encapsulated=true时:API 在构建消息是会自动加上CRC 校验码。

public byte[] getMessageData() {
    ByteQueue msgQueue = new ByteQueue();
    modbusMessage.write(msgQueue);
    ModbusUtils.pushShort(msgQueue, ModbusUtils.calculateCRC(modbusMessage));
    return msgQueue.popAll();
}

如果encapsulated=false时:消息会加上事务处理标识,协议标识6个字节:

public byte[] getMessageData() {
    ByteQueue msgQueue = new ByteQueue();
    modbusMessage.write(msgQueue);
    ByteQueue xaQueue = new ByteQueue();
    ModbusUtils.pushShort(xaQueue, transactionId);
    ModbusUtils.pushShort(xaQueue, ModbusUtils.IP_PROTOCOL_ID);
    ModbusUtils.pushShort(xaQueue, msgQueue.size());
    xaQueue.push(msgQueue);
    return xaQueue.popAll();
}

第二: API实例

/**
 * 读线圈寄存器
 * @param modbusMaster
 * @param slaveId
 * @param initOffset
 * @param count
 * @return
 * @throws ModbusTransportException
 */
private static String batchCoilStatus01(ModbusMaster modbusMaster,Integer slaveId,Integer initOffset,Integer count) throws ModbusTransportException {
    ReadCoilsRequest request= new ReadCoilsRequest(slaveId,initOffset,count);
    ReadCoilsResponse response = (ReadCoilsResponse) modbusMaster.send(request);
    byte[] data  = response.getData();
    String bytes = ByteUtils.toHexAscii(data);
    return bytes;
}

/**
 * 读离散输入寄存器
 * @param modbusMaster
 * @param slaveId
 * @param initOffset
 * @param count
 * @return
 * @throws ModbusTransportException
 */
private static String batchInputStatus02(ModbusMaster modbusMaster,Integer slaveId,Integer initOffset,Integer count) throws ModbusTransportException {
    ReadDiscreteInputsRequest request= new ReadDiscreteInputsRequest(slaveId,initOffset,count);
    ReadDiscreteInputsResponse response = (ReadDiscreteInputsResponse) modbusMaster.send(request);
    byte[] data  = response.getData();
    String bytes = ByteUtils.toHexAscii(data);
    return bytes;
}
/**
 *批量读取保持继承器
 * @param modbusMaster
 * @param slaveId
 * @param initOffset
 * @param count
 * @return
 * @throws ModbusTransportException
 */
private static String batchRead03(ModbusMaster modbusMaster,Integer slaveId,Integer initOffset,Integer count) throws  ModbusTransportException {
    ReadHoldingRegistersRequest request= new ReadHoldingRegistersRequest(slaveId,initOffset,count);
    ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) modbusMaster.send(request);
    byte[] data  = response.getData();
    String bytes = ByteUtils.toHexAscii(data);
    return bytes;
}


/**
 * 批量读取输入继承器
 * @param modbusMaster
 * @param slaveId
 * @param initOffset
 * @param count
 * @return
 * @throws ModbusTransportException
 */
private static String batchRead04(ModbusMaster modbusMaster ,Integer slaveId,Integer initOffset, Integer count) throws  ModbusTransportException {
    ReadInputRegistersRequest request= new ReadInputRegistersRequest(slaveId,initOffset,count);
    ReadInputRegistersResponse response = (ReadInputRegistersResponse) modbusMaster.send(request);
    byte[] data  = response.getData();
    String bytes = ByteUtils.toHexAscii(data);
    return bytes;
}

轻云UC设计器实例

基础信息

组件名称 : modbus-connector
组件版本: 1.0.0
组件类型: 系统默认
状 态: 正式发布
组件描述:通过MODBUS 连接网关,采集或下发相关指令到设备端。

配置文件:

配置文件作为MODBUS配置界面元素的基础,MODBUS所有高级配置均可以通过重构该配置文件体现在前端界面上。配置参数分为三组: 基础配置,连接配置,高级配置,一般情况可以随意扩展高级配置。

注: 配置文件仅供修改升级组件式利用,一般情况下对用户透明。无需做任何更改,除非用户需要手动维护组件心跳或通信端口时,一般情况下禁止修改。

df:
  component:
    name: modbus-connector
    type: 2   #1:采集器;2:接收器;3:转换器;4:存储器;5:解析器;6:状态解析器:7:同步器;8:消息通知
    transportPort: 49096    #内部akka通信端口
    heartbeatCron: 0/30 * * ? * * *       #网关心跳数据上报时间
    #########本地环境用##########
    instance-id: 998
    topic: DEMO-TOPIC-998
    response-topic: DEMO-TOPIC-RESPONSE-198
    executor:
      akka-address: 192.168.3.195:49095
      rest-address: 192.168.3.195:49090
    #############结束##############

    parameter:
      connection[0]:                      #组件连接参数数组
        name: Host                        #参数名称,自定义
        key: hostName                     #参数key,自定义
        required: true                    #是否必传 true或false
        value-type: string                #参数值类型,支持 string, int, float
        default-value: 127.0.0.1          #参数默认值,自定义
        input-type: input                 #参数输入类型,支持 input-输入框,select-下拉框
        description: 数据采集端TCP 连接地址                 #参数值描述,自定义
      connection[1]:
        name: Port
        key: port
        required: true
        value-type: int
        input-type: input
        default-value: 502
        description: Modbus数据采集端TCP连接端口号
      connection[2]:
        name: 设备编号
        key: equipmentId
        required: true
        value-type: string
        input-type: input
        default-value: 80100012
        description: 设备编号(平台分配得设备编号)
      base[0]:
        name: 从站编号
        key: slaveId
        required: true
        value-type: int
        input-type: input
        description: 采集设备序号(slaveId即设备地址)
      base[1]:
        name: 功能码
        key: functionCode
        required: false
        value-type: int
        input-type: select
        select-option: 01|1,02|2,03|3,04|4,05|5,15|15,6|6,16|16
        description: 命名空间功能码 [1:读线圈状态、2:读离散输入状态、3:读保持寄存器、4:读输入寄存器,5:写单个线圈,15:写多个线圈,6:写单个保持寄存器,16:写多个保持寄存器]
      base[2]:
        name: 偏移量
        key: offset
        required: false
        value-type: int
        input-type: input
        description: 偏移量
      base[3]:
        name: 数量
        key: numberOfRegisters
        required: true
        value-type: int
        input-type: input
        default-value: 1
        description: 继承器数量(从偏移量开始读取多少个继承器)
      base[4]:
        name: Encapsulated
        key: encapsulated
        required: false
        value-type: int
        input-type: select
        select-option: true|1,false|2
        default-value: 1
        description: Master发送指令到slave时得消息格式为EncapMessage或者XaMessage
      advance[0]:
        name: CRON
        key: cron
        required: true
        value-type: string
        input-type: input
        default-value: 1/10 * * ? * * *
        description: 网关采集器定时任务

组件测试

  1. 配置网关

从组件列表中选择modbus-connector并拖动到作业设计器。
image.png

  1. 参数说明

修改右侧配置参数并保存,参数使用说明可以通过每个参数旁边的?查看。
image.png
参数含义可以对照Modbus Slave 软件中的相关信息。 Modbus Slave 下载
image.png

  1. 启动网关

注意观察执行日志,确保网关正常运行,状态status 字段为1时表示正常运行中
image.png

  1. 测试验证

1)前提准备:
下载并启动 Modbus Slave 调试工具
image.png

连接成功后记录相关信息:
IP: 192.168.3.45
Port : 502
Slave ID: 1
function code : 3
偏移量: 0 (我们以0位作为测试数据)

2)进入到轻云UC通用设计中心,进入到设计器,并拖动modbus-connector组件到画板。
按规上面modbus slave 中的配置信息,设定好modbus-connector配置信息:
image.png
image.png
注意配置中的功能码,偏移量,从站编号,IP ,端口,需要与测试工具中的配置信息一致。
3)切换到UC设计器,重启modbus-connector网关,可以从执行日志板块看到数据。
这里的数据时定时获取的,通过配置参照中的表达式。如: 1/10 * * ? * * * 每10秒获取一次数据。
image.png

你可以通过Qinghub作业直接体验试用,也可以根据手册开发相应的代码块。 qinghub项目已经全面开源。
源码文件地址: https://gitee.com/qingplus/qingcloud-platform
qinghub作业配置

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/460703.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MC78L05ACDR2G线性稳压器芯片中文资料规格书PDF数据手册引脚图参数图片价格

产品概述&#xff1a; MC78L00A系列线性稳压器价格便宜&#xff0c;易于使用&#xff0c;适用于各种需要最高100mA的调节电源的应用。与大功率MC7800和MC78M00系列一样&#xff0c;这款稳压器也提供内部电流限制和高温关断&#xff0c;因此非常坚固耐用。在很多应用中&#xf…

MediaBox音视频终端SDK已适配鸿蒙星河版(HarmonyOS NEXT)

2024年1月&#xff0c;HarmonyOS NEXT 鸿蒙星河版系统开发者预览版开放申请&#xff0c;该系统将只能安装为鸿蒙开发的原生应用&#xff0c;而不再兼容安卓应用。对此&#xff0c;阿里云MediaBox音视频终端SDK产品已实现功能的鸿蒙化迁移和重构&#xff0c;全面适配鸿蒙系统Har…

王勇:硬科技的下一站 | 演讲嘉宾公布

一、智能耳机与可穿戴专题论坛 智能耳机与可穿戴专题论坛将于3月27日同期举办&#xff01; 智能耳机、可穿戴设备已经逐渐融入我们的生活&#xff0c;它们不仅带来了便捷与舒适&#xff0c;更在悄然改变着我们的生活方式和工作模式。在这里&#xff0c;我们将分享最新的研究成果…

前端基础——HTML傻瓜式入门(2)

该文章Github地址&#xff1a;https://github.com/AntonyCheng/html-notes 在此介绍一下作者开源的SpringBoot项目初始化模板&#xff08;Github仓库地址&#xff1a;https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址&#xff1a;https://blog.c…

误分区酿苦果,数据恢复有妙方

一、误操作引发分区混乱 在数字化时代的浪潮中&#xff0c;硬盘分区成为我们管理和存储数据的重要手段。然而&#xff0c;误分区这一操作失误&#xff0c;却时常给许多用户带来不小的困扰。误分区&#xff0c;简单来说&#xff0c;就是在对硬盘进行分区操作时&#xff0c;由于…

P6安装:安装P6提示1433端口无效

错误描述 尝试运行 Microsoft SQL Server 2005 的 Primavera P6 数据库时&#xff0c;遇到以下错误&#xff1a; SQLServerException: The TCP/IP connection to the host [name], port 1433 has failed. Error: “Connection refused: connect. Verify the connection prope…

LCR144翻转二叉树(力扣简单题,Java,递归+非递归)

目录 题目描述&#xff1a; 递归代码1&#xff1a; 递归代码2&#xff1a; 非递归代码&#xff08;层次遍历&#xff09;&#xff1a; 题目描述&#xff1a; 给定一棵二叉树的根节点 root&#xff0c;请左右翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a;…

vs2022安装番茄助手后无法使用

1.安装番茄助手 兼容性-win7-管理员启动 2.破解 下载附件“VA_X64.dll”、“PiaoYun64.dll”破解文件&#xff0c;使用Everything找到C盘对应的“VA_X64.dll”路径&#xff0c;将两个破解文件拷贝到此路径。 3.命令行键入类似命令&#xff1a;D:\OfficeSoftware\VisualStudi…

mybatis实现动态sql和关联映射以及延迟加载策略

一、动态sql的简述 什么是动态sql:在不同条件下拼接不同的sql Mybatis框架的动态sql技术是一种根据特定条件动态拼接SQl语句的功能&#xff0c;他存在的意义是为了解决拼接SQL语句字符串时的痛点问题。比如我们在用淘宝之类的软件在进行商品属性选择的时候&#xff0c;我们会发…

JAVA22 FFM实战之HelloWorld

前言 JDK22即将发布&#xff0c;Java Foreign Function & Memory API将会退出预览&#xff0c;是时候开始学习一波了。 FFM API介绍 FFM API由两大部分组成&#xff0c;一个是Foreign Function Interface&#xff0c;另一个是Memory API。前者是外部函数接口&#xff0c…

Java双非大二找实习记录

先说结论&#xff1a;2.22→3.6线上线下面了七家&#xff0c;最后oc两家小公司&#xff0c;接了其中一个。 本人bg&#xff1a; 真名不经传双非一本&#xff0c;无绩点无竞赛无奖项无实习&#xff0c;23年12月开始学java。若非要说一点相关的经历&#xff0c;就是有java基础&…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:PluginComponent)

提供外部应用组件嵌入式显示功能&#xff0c;即外部应用提供的UI可在本应用内显示。 说明&#xff1a; 该组件从API Version 9开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。本组件为系统接口。 子组件 无 接口 PluginComponent(value:…

数据预处理|数据清洗|使用Pandas进行异常值清洗

数据预处理|数据清洗|使用Pandas进行异常值清洗 使用Pandas进行异常值清洗1. 异常值检测1.1 简单统计分析1.2 散点图方法1.3 3σ原则1.4 箱线图 2. 异常值处理2.1 直接删除2.2 视为缺失值2.3 平均值修正2.4 盖帽法2.5 分箱平滑法2.6 回归插补2.7 多重插补2.8 不处理 使用Pandas…

《Ubuntu20.04环境下的ROS进阶学习5》

一、Hector_Mapping构建二维地图 在前面我们已经介绍了如何使用激光雷达来扫描地图&#xff0c;如何用激光雷达来建造地图&#xff0c;本节我们将两者结合起来&#xff0c;通过Hector_Mapping功能包实现SLAM。 二、在仿真环境中进行2D SLAM 1、下载Hector_Mapping sudo apt i…

软考高级:数据库、数据仓库和数据湖概念和例题

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

丈夫竞被妻子打的不敢回家

妻子对丈夫大打出手&#xff0c;公主岭法院发出首份男性人身安全保护令 近日&#xff0c;公主岭市人民法院公主岭人民法庭作出了一项具有历史意义的裁定&#xff0c;首次为一位男性受害者发出了人身安全保护令。 据悉&#xff0c;受害者张某与妻子李某结婚多年&#xff0c;但婚…

组合逻辑电路(四)

目录 采用MSI的组合逻辑电路的分析与设计 采用MSI的组合逻辑电路的设计 用具有n个地址输入端的中规模集成器件实现n变量逻辑函数 用译码器设计组合逻辑电路 例 用数据选择器设计组合逻辑电路 例 用具有n个地址输入端的数据选择器实现m变量逻辑函数&#xff08;m>n&#…

工具类实现导出复杂excel、word

1、加入准备的工具类 package com.ly.cloud.utils.exportUtil;import java.util.Map;public interface TemplateRenderer {Writable render(Map<String, Object> dataSource) throws Throwable;}package com.ly.cloud.utils.exportUtil;import java.util.Map;public int…

LabVIEW多表位数字温湿度计图像识别系统

LabVIEW多表位数字温湿度计图像识别系统 解决数字温湿度计校准过程中存在的大量需求和长时间校准问题&#xff0c;通过LabVIEW开发平台设计了一套适用于20多个表位的数字温度计图像识别系统。该系统能够通过图像采集、提取和处理&#xff0c;进行字符训练&#xff0c;从而实现…

树形结构 一篇文章梳理

树形结构是一种非常重要的非线性数据结构&#xff0c;它模拟了具有层次关系的数据模型。在树形结构中&#xff0c; 目录 一、组成元素&#xff1a; 二、树的属性&#xff1a; 深度或高度 度 路径 路径长度 三、树的类型 1 二叉树 2 多叉树 3 完全二叉树 4 满二叉树…