0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

使用协议栈实现Modbus TCP服务器应用

CHANBAEK 来源:木南创智 作者:尹家军 2022-12-13 16:23 次阅读

自从开源了我们自己开发的Modbus协议栈之后,有很多朋友建议我针对性的做几个示例。所以我们就基于平时我们的应用整理了几个简单但可以说明基本的应用方法的示例,这一篇中我们来简述如何使用协议栈实现一个Modbus TCP服务器应用。

1 、何为TCP服务器

Modbus协议是一个主从协议,那肯定就有主站和从站之分,在Modbus TCP中亦称之为客户端与服务器。所谓TCP客户端其功能基本与RTU主站一样,RTU主站会向从站发起数据请求,同样的TCP客户端也会向服务器发起请求。也就是说在Modbus TCP模式下客户端亦是发起通讯的一方。

对于TCP客户端来说,自己并不会产生数据,它的数据均是从服务器获取,为了得到数据就必须向服务器发起数据请求。在Modbus TCP协议中,服务器一般也不会主动向外发送数据,服务器需要根据客户端的数据请求来决定是否发送数据、发送哪些数据。这一过程如下图所示:

从上图我们不难看出,首先客户端要主动发起数据请求,客户端发起的数据请求需要告诉服务器它请求的数据有哪些。服务器收到这个数据请求后,服务器解析客户端的请求并按照客户端的请求返回数据。客户端收到数据响应后解析数据,这样就完成了客户端与服务器之间的一次数据通讯。

需要注意的是,Modbus TCP与Modbus RTU不同的是有一个专用的MBAP报文头来识别Modbus应用数据单元。这一报文头由7个字节组成:

这种MBAP报文头虽然也是用来识别Modbus数据域,但还是与串行链路上使用的MODBUS RTU应用数据单元有一些差别,具体如下:

1 、用MBAP报文头中的单个字节单元标识符取代MODBUS串行链路上通常使用的MODBUS从地址域。这个单元标识符用于设备的通信,这些设备使用单个 IP 地址支持多个独立MODBUS 终端单元,例如:网桥、路由器和网关。

2 、使用接收者可以验证的方式来构造所有MODBUS请求和响应。对于MODBUS PDU有固定长度的功能码来说,仅功能码就足够了。对于在请求或响应中携带一个可变数据的功能码来说,数据域包括字节数。

3 、使用TCP上传送MODBUS数据域时,即使将报文分成多个信息包来传输,可在MBAP报文头上携带附加长度信息,这样接收者就能够识别报文的完整性。

2 、如何实现TCP服务器

我们已经简单的描述了基于TCP/IP的Modbus数据通讯,在此基础上我们将进一步描述基于协议栈的Modbus TCP服务器的实现。

在协议栈中,我们已经实现了Modbus TCP服务器的基本功能,如数据的管理及响应客户端的请求等。Modbus TCP服务器作为数据的生产者,管理者四类数据:线圈量、状态量、输入寄存器和保持寄存器。所以在Modbus TCP服务器中我们要为这四种数据定义相应的地址,以便客户端能够对应的访问。所以设计一个Modbus TCP服务器我们先来设计它的数据地址。在我们的例子中,出于操作方便,我们规定了每类数据类型的数量为10,我们以用的最多的保持寄存器为例,定义寄存器地址为40001到40010。

在我们的协议栈中实现了0x01、0x02、0x03、0x04、0x05、0x06、0x0F以及0x10等功能码。也就是说客户端对象会生成面向这些功能码的Modbus TCP服务器数据请求。Modbus TCP服务器收到请求后,解析请求并根据请求生成响应的数据响应。可以表示为下图所示:

从上图我们明白协议栈中已经实现了对收到的主站数据请求进行解析以及根据解析生成对应的响应的函数。我们使用协议栈时,主要需要做两个方面的事情:解析数据请求和生成数据响应。

在协议栈中定义了一个解析函数,该函数将收到的数据请求消息解析,并根据解析的结果生成返回的数据响应。该函数的原型如下:

/ 解析接收到的信息,返回响应命令的长度 /

uint16_t ParsingClientAccessCommand(uint8_t receivedMessage,uint8_trespondBytes)

这个函数有2个参数:uint8_t receivedMessage是收到的数据请求消息; uint8_trespondBytes是返回的数据响应消息,也是函数需要生成的;而函数的返回值则是生成的数据响应详细的长度。

在解析的过程中,该函数判断消息的完整性,并根据不同的功能码调用不同的回调函数来实现,包括设置本地数据和获取本地数据的相关回调函数,在后续将讨论它们的实现。

3 TCP****服务器编码

到这里其实我们已经很清楚,使用协议栈实现Modbus TCP服务器只需要在TCP/IP收到客户端请求后调用sendLen = ParsingClientAccessCommand(buffer,sendBuf);函数解析收到的请求命令。并根据请求执行相应的操作就可以了。那需要实现哪些操作呢?在协议栈中定义了8个回调函数,分别是获取线圈量、获取状态量、获取输入寄存器和获取保持寄存器,以及预置单个线圈量、预置多个线圈量、预置单个保持寄存器和预置多个保持寄存器。函数原型定义如下:

/*获取想要读取的Coil量的值*/
__weak void GetCoilStatus(uint16_t startAddress,uint16_t quantity,bool*statusList)
{
  //如果需要Modbus TCP Server/RTU Slave应用中实现具体内容
}
 
/*获取想要读取的InputStatus量的值*/
__weak void GetInputStatus(uint16_t startAddress,uint16_t quantity,bool*statusValue)
{
  //如果需要Modbus TCP Server/RTU Slave应用中实现具体内容
}
 
/*获取想要读取的保持寄存器的值*/
__weak void GetHoldingRegister(uint16_t startAddress,uint16_t quantity,uint16_t*registerValue)
{
  //如果需要Modbus TCP Server/RTU Slave应用中实现具体内容
}
 
/*获取想要读取的输入寄存器的值*/
__weak void GetInputRegister(uint16_t startAddress,uint16_tquantity,uint16_t *registerValue)
{
  //如果需要Modbus TCP Server/RTU Slave应用中实现具体内容
}
 
/*设置单个线圈的值*/
__weak void SetSingleCoil(uint16_t coilAddress,bool coilValue)
{
  //如果需要Modbus TCP Server/RTU Slave应用中实现具体内容
}
 
/*设置单个寄存器的值*/
__weak void SetSingleRegister(uint16_t registerAddress,uint16_tregisterValue)
{
  //如果需要Modbus TCP Server/RTU Slave应用中实现具体内容
}
 
/*设置多个线圈的值*/
__weak void SetMultipleCoil(uint16_t startAddress,uint16_t quantity,bool*statusValue)
{
  //如果需要Modbus TCP Server/RTU Slave应用中实现具体内容
}
 
/*设置多个寄存器的值*/
__weak void SetMultipleRegister(uint16_t startAddress,uint16_tquantity,uint16_t *registerValue)
{
  //如果需要Modbus TCP Server/RTU Slave应用中实现具体内容
}

这些函数就是我们要根据我们的Modbus TCP服务器功能设计实现的。对于我们这个测试例子我们只需要实现读取保持寄存器就可以了。具体实现如下:

/*获取想要读取的保持寄存器的值*/
void GetHoldingRegister(uint16_t startAddress,uint16_t quantity, uint16_t* registerValue)
{
   uint16_t start;
   uint16_t count;
   
    /*先判断地址是否处于合法范围*/
    start =(startAddress > 0) ? ((startAddress <= 9) ? startAddress : 9) : 0;
    count =((start + quantity - 1) <= 9) ? quantity : (9 - start);
 
    for(int i = 0; i < count; i++)
    {
       registerValue[i] = holdingRegister[start + i];
    }
}

这个例子中我们实现了读取40001到40010保持寄存器的值。

4 TCP****服务器小结

我们在TCP服务器的基础上使用我们的协议栈实现一个Modbus TCP服务器应用。其实使用协议栈实现Modbus TCP服务器应用是很简单的,我们需要使用如ModPoll这样的软件来测试一下它。

我们读取10个保持寄存器,值分别为对应位固定的1到10,如上图读出的结果与预期一致。我们还可以采用TCP&UDP测试工具来看一下报文,具体如下:

同样的,在同一台设备上只需实现一个Modbus TCP服务器,哪怕是通过不同的网络端口来访问。这一点与客户端是不一样的,原因是Modbus TCP服务器的数据是自己产生,而且只需被动响应客户端的数据请求。

接下来我们来总结一下使用协议栈实现Modbus TCP服务器的工作流程,或者说实现的步骤。首先Modbus TCP服务器要解析从客户端送来的数据请求。在协议栈中已经封装了数据请求的解析函数、所以我们实现Modbus TCP服务器时首先就是调用这一函数来解析接收到的数据请求消息。

然后将解析函数返回的数据响应消息发送到客户端就可以了。也就是说使用协议栈,只需要调用一下这个函数Modbus TCP服务器功能就实现了。这是因为这个函数实现了整个Modbus TCP服务器的响应过程,大致分三个步骤:第一步,解析收到的客户端数据请求消息;第二步,根据解析的结果预置数据或者获取数据,预置和获取数据由8个回调函数实现;第三步,生成Modbus TCP服务器数据响应消息。说到这里我们已经清楚,Modbus TCP服务器必须实现这些回调函数,其它工作则全由协议栈完成。

源码下载:https://download.csdn.net/download/foxclever/12838885

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • MODBUS
    +关注

    关注

    27

    文章

    1439

    浏览量

    75692
  • 服务器
    +关注

    关注

    12

    文章

    8120

    浏览量

    82522
  • TCP
    TCP
    +关注

    关注

    8

    文章

    1272

    浏览量

    78297
  • 协议栈
    +关注

    关注

    2

    文章

    129

    浏览量

    33459
收藏 人收藏

    评论

    相关推荐

    linux平台实现modbus主机协议的动态库libMbpoll

    libMbopll动态库是面向linux平台设计的modbus主机协议,可以运行在x86平台以及各种嵌入式linux平台;协议提供了简单
    发表于 05-28 14:23

    linux平台实现modbus主机协议的动态库libMbpoll

    libMbopll动态库是面向linux平台设计的modbus主机协议,可以运行在x86平台以及各种嵌入式linux平台;协议提供了简单
    发表于 05-28 14:55

    协议介绍--TCP/IP

    ,和SOCKET API。以及DNS,PING等直接调用的函数。5)本协议可以完成客户机,服务器,UDP客户机,UDP服务器。还可以方便完成DNS客户端功能,PING客户端功能。6)
    发表于 09-03 15:03

    Modbus库开发笔记之九:利用协议开发Modbus TCP Server应用

    。接下来我们讨论Modbus TCP Server的实现过程。根据前面对协议的封装,我们需要引用ModbusTCP Server的相关封装
    发表于 08-26 15:56

    Modbus协议中文版(总共127页pdf下载)

    不同类型的总线或网络连接的设备之间的客 户机/服务器通信。 目前,使用下列情况实现 MODBUS: 以太网上的TCP/IP。 各种媒体(有线:EIA/TIA-232-E、EIA
    发表于 07-03 01:10

    Modbus协议中文版

    不同类型的总线或网络连接的设备之间的客 户机/服务器通信。 目前,使用下列情况实现 MODBUS: 以太网上的TCP/IP。 各种媒体(有线:EIA/TIA-232-E、EIA
    发表于 07-09 07:16

    在uIP协议实现基于AJAX和CGI的动态Web服务器

    和应用。为了满足资源有限的嵌入式系统的需要,本文采用微型TCP/IP协议uIP,在协议之上设计实现
    发表于 05-28 05:00

    【HarmonyOS HiSpark Wi-Fi IoT 套件试用连载】MODBUS TCP

    modbus保留的端口号,服务器必须监听这个端口号。modbus帧格式通用的modbus帧格式如下图,从机地址+功能码+数据+CRC校验 基于tc
    发表于 12-07 23:16

    怎么实现的基于TCP/IP协议的简易服务器?

    本文以SPCE061A为主控制,DM9000为以太网MAC控制,配合一定的外围电路而实现的基于TCP/IP协议
    发表于 05-31 06:34

    如何快速实现Modbus RTU和Modbus TCP协议转换?

    整合起来监控管理,目前上位机大部分用的Modbus TCP协议,而现场设备有大批量使用的是Modbus RTU协议,要
    发表于 08-18 18:36

    基于RT-Thread实现的Agile Modbus协议

    基于 RT-Thread 实现的支持 Modbus 固件升级的 Bootloader:HPM6750_Boot  特性  支持 rtu 及 tcp 协议,使用纯 C 开发,不涉及任何硬
    发表于 10-08 15:04

    Modbus通讯协议的几种实现方式

    量避免采用OPC。   OPC协议实现要通过两部分完成。首先是OPC服务器,这是软件程序与不同协议下工业设备通讯的中间件,相当于网关。在OPC服务
    发表于 05-05 16:19

    EasyWeb:微小的TCP/IP协议和Web服务器

    。如果局域网通过路由连接到互联网,数据可以在全世界范围内交换。处理以太网接口的软件比处理序列接口复杂得多。在多数情况下,TCP/IP协议用于数据传输,因为它向局域网其他成员提供了快速可靠的连接。这个小的
    发表于 09-04 07:39

    Modbus TCP通讯协议概述

    ”和“Internet”环境中MODBUS报文的用途。协议的最通用用途是为诸如PLC’s,I/O 模块,以及连接其它简单总线或I/O 模块的网关服务的。 MODBUS/
    的头像 发表于 01-04 17:51 2.9w次阅读
    <b class='flag-5'>Modbus</b> <b class='flag-5'>TCP</b>通讯<b class='flag-5'>协议</b>概述

    Arduino供电的I/O Modbus/TCP设备服务器

    电子发烧友网站提供《Arduino供电的I/O Modbus/TCP设备服务器.zip》资料免费下载
    发表于 11-24 14:27 0次下载
    Arduino供电的I/O <b class='flag-5'>Modbus</b>/<b class='flag-5'>TCP</b>设备<b class='flag-5'>服务器</b>