前几篇文章我们已经介绍了MQTT和UART这两个概念,那么把它们结合起来又会得到什么呢?作为工程师,相信大家经常会遇到以下场景:在现场需要通过串口将电脑与PLC或控制设备连接起来,而电脑需要通过某种协议(比如Modbus、Profibus-DP等)与现场设备进行数据读取或写入。在进行调试时,往往需要前往现场进行操作:
由于现在疫情管控,很多公司提出了非必要不出差的政策(当然也有一部分经济考虑),为了避免频繁的现场调试,比较常见的方法就是通过在现场的PC上安装向日葵或者Teamview软件来进行远程调试,但是有时出于知识产权的保护,很难直接在现场PC上安装调试软件或存留调试代码;或者现场甲方无法提供合适的PC,这就要求调试人员不得不去现场调试。针对这个问题,是否有好的解决办法,我们今天就来讨论这个话题。
1.需求分析
首先如果需要远程连接,那我们必须依托于网络,不管是NB, CAT1或者Wifi,现场必须有一个可以联网的设备。其次,这个设备必须支持UART功能,这是因为它需要与现场设备相连。本质上讲,我们需要的就是一个UART到网络的转换器,实现方案可以有以下几种:
-
仅需要1个转换器,用户把调试的串口通讯协议包重新封装为以太网包,将其作为Server部署在云端,Converter和PC都作为Client与云连接,并且还需要设计本地PC与云的网络协议
-
仅需1个转换器,用户把调试的串口通讯协议包重新封装为以太网包,将其作为Server部署在本地,Converter作为Client与本地相连,这就需要本地的PC有公网IP,如果PC是内部局域网还需要做端口映射
-
需要两个转换器,由于PC和现场设备都使用的UART,故完全不需要修改通讯协议,仅需要绑定两个转换器,并且选择合适的网络协议做数据交互即可。
从工作量上看,第三种方式是最简单,最便捷的,所以今天就重点研究这个方案。
2.方案选型
方案3看似很美好,目前市面上也有一些成熟产品(如有需要可以联系本人),但在实际应用过程中,也有一些限制和难点。
-
先谈方案限制,我们先来看一下原先不加转换器的通讯时序,以Modbus为例
Modbus主机通过t1时长发送请求帧数据给到PLC并开启Ttimeout的超时时间,PLC在接收到数据后,需要经过t2时间进行处理并通过t3时间发送应答帧回PC,这里要求下面公式必须成立
如果加入两个UART转换器,情况会稍微复杂一些,时序会变的较长且有可能会出一些不确定性
同样,如果要稳定可靠的通讯则必须满足如下条件
-
由于方案的时序限制,本方案的难点在于如何尽可能的满足上述公式。有以下几思路:
-
-
选择轻量级,开销小的网络协议,以减小网络延时
-
增大Ttimeout值,对于某些软件或者协议支持对Ttimeout值的修改,这样就可以根据自身网络情况,选择合适的Ttimeout值即可。但并不是所有的协议都适合使用该方法,例如Profibus-DP中虽然可以根据GSD文件在组态软件中修改各种延时,但是这会影响现场设备的通讯周期,这种协议还是建议通过现场上层的工业网关或者通讯模块来获取信息
-
将PLC的从站串口协议做在PC侧的Converter中,将PC主站的串口协议做在现场端的Converter中,通过网络共享访问数据,这样做可以将限制条件变为如下两个公式:
在Converter中做协议的难点在于很多协议是不开放的,比如西门子 Step7所使用的PPI协议,后期适配也是比较麻烦的事情。
-
3. 方案确定
在不考虑做串口协议的前提下,使用MQTT做网络数据交互是一个非常好的选择
-
协议简单,开销小 -
Converter配对简单,只要将本设备的订阅对端设备的发布Topic即可 -
有很多免费的broker可以直接运行,或者撸个号称永久免费的主机自己搭建,可以参考《从零开始建立MQTT服务器》。如果本文阅读量高的话,下篇可以写个免费撸主机的教程
硬件资源 | 所用模块 |
---|---|
MIMXRT1170 EVK x 2 | UARTx1, 1G ENETx1,CMSIS DAP |
千兆网线 x2 | |
USB线 x2 |
软件资源 | 描述 |
---|---|
IAR | 编译环境,要求高于V9.10版本 |
MCUXpresso SDK | NXP提供SDK 基于V2.10 |
Lwip_mqtt | 已经集成在SDK中 |
NXP已经提供了基于lwip+freertos+mqtt的示例,具体路径在\boards\evkmimxrt1170\lwip_examples\lwip_mqtt_enet_qos中,该示例使用公开免费的Broker(broker.hivemq.com)为例,演示了订阅/发布的流程,下载完程序后,需要将网线接入Internet路由器即可在虚拟串口中看到如下的日志:
Initializing PHY...
************************************************
MQTT client example
************************************************
Getting IP address from DHCP...
IPv4 Address : 192.168.0.102
IPv4 Subnet mask : 255.255.255.0
IPv4 Gateway : 192.168.0.100
Resolving "broker.hivemq.com"...
Connecting to MQTT broker at 18.185.216.165...
MQTT client "nxp_f50003c25757bd58aa6d0ce50102f020" connected.
Subscribing to the topic "lwip_topic/#" with QoS 0...
Subscribing to the topic "lwip_other/#" with QoS 1...
Subscribed to the topic "lwip_topic/#".
Subscribed to the topic "lwip_other/#".
Going to publish to the topic "lwip_topic/100"...
Published to the topic "lwip_topic/100".
Received 18 bytes from the topic "lwip_topic/100": "message from board"
Going to publish to the topic "lwip_topic/100"...
Published to the topic "lwip_topic/100".
Received 18 bytes from the topic "lwip_topic/100": "message from board"
Going to publish to the topic "lwip_topic/100"...
Published to the topic "lwip_topic/100".
Received 18 bytes from the topic "lwip_topic/100": "message from board"
Going to publish to the topic "lwip_topic/100"...
Published to the topic "lwip_topic/100".
Received 18 bytes from the topic "lwip_topic/100": "message from board"
Going to publish to the topic "lwip_topic/100"...
Published to the topic "lwip_topic/100".
Received 18 bytes from the topic "lwip_topic/100": "message from board"
下面我们基于这个示例来完成Converter的全部功能
-
Clone SDK中提供的lwip_mqtt_enet_qos示例并重起名为GatewayUart2Net_RT1170_IAR
-
Copy《今天聊聊UART》相关的uart代码到新工程
-
修改app_thread中for (i = 0; i
while(1) { if(connected) { if(g_rxBuffer.flag == 1) { err = tcpip_callback(publish_message, NULL); if (err != ERR_OK) { PRINTF("Failed to invoke publishing of a message on the tcpip_thread: %d.\r\n", err); } g_rxBuffer.flag = 0; } } //Restart to connect else { } }
-
修改mqtt_incoming_data_cb函数如下:
static void mqtt_incoming_data_cb(void *arg, const u8_t *data, u16_t len, u8_t flags) { int i; LWIP_UNUSED_ARG(arg); g_MQTTRxBuffer.uCount = len; g_MQTTRxBuffer.flag = 1; memcpy((void *)&g_txBuffer.byData[0], (void *)data, len); uart_sendDMA(len); if (flags & MQTT_DATA_FLAG_LAST) { PRINTF("\"\r\n"); } }
-
在串口接收ISR中添加如下函数
if ((kLPUART_IdleLineFlag) & status) { LPUART_ClearStatusFlags(DEMO_LPUART, kLPUART_IdleLineFlag); g_rxBuffer.uCount = GetRingBufferLengthDMA(); memcpy((void *)&g_MQTTTxBuffer.byData[0], (void *)&g_rxBuffer.byData, g_rxBuffer.uCount); g_MQTTTxBuffer.uCount = g_rxBuffer.uCount; //Continue to receive the next frame uart_receiveDMA(); g_rxBuffer.flag = 1; }
-
由于UART和默认的打印函数冲突了,所以需要禁止DbgConsole,在Fsl_debug_console.h中修改宏
#ifndef SDK_DEBUGCONSOLE #define SDK_DEBUGCONSOLE DEBUGCONSOLE_DISABLE #endif
-
在mqtt_subscribe_topics修改订阅的Topic,两个板子互相订阅对方的发布即可,这里不做演示
-
修改MAC地址,保证两个evk的MAC地址不能相同,否则路由器会傻掉的
为了测试网络延时,可以先订阅本机发送的Topic,然后串口发送数据,Broker在接收到数据后会发送回本地,这样可以大致计算网络的延时,从下图可以看到,本地发送数据到broker.hivemq.com服务器然后返回本地最大有700ms的延时
4.安全分析
工业现场设备通过远程进行控制,本身还是有一定安全风险的,如果确实有比较强烈的需求,建议使用MQTT+TLS进行加密传输,服务器主机也最好由自己搭建,或者购买比较成熟的产品,本示例仅仅用于展示,代码后续会上传到我的Gitee(https://gitee.com/hudiekaxp),请您注意查收
以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !