当前位置:   article > 正文

多协议网关设计架构与实现,支持 RS485/232、CAN、M-Bus、MQTT、TCP 等工业协议接入(附代码示例)_设备网关协议

设备网关协议

一、项目概述

1.1 背景

随着物联网技术的快速发展,越来越多的设备需要接入网络进行数据交互。然而,不同设备往往采用不同的通信协议,例如工业现场常用的Modbus、CAN、电力载波等,以及物联网领域常用的MQTT、TCP/IP等,这给设备的互联互通带来了巨大的挑战。

多协议网关作为连接不同协议设备的桥梁,能够将不同协议的数据进行转换和转发,实现不同设备之间的数据互通,解决物联网碎片化问题,具有重要的应用价值。

1.2 目标

本项目旨在设计并实现一款多协议网关,支持以下功能:

  • 数据采集:
    • 支持RS485、RS232接口的Modbus协议(RTU和TCP)。
    • 支持CAN总线协议。
    • 支持M-Bus总线协议。
    • 支持DL/T645协议。
    • 支持IEC104协议。
    • 支持电力载波协议(待补充具体协议)。
  • 数据上传:
    • 支持UDP、TCP协议。
    • 支持WiFi、以太网等网络接入方式。
    • 支持MQTT协议接入物联网平台。
1.3 应用场景
  • 工业自动化:采集和控制不同厂家的PLC、传感器等设备数据。
  • 智能楼宇:采集和控制楼宇自控系统中的各种设备数据。
  • 智能家居:采集和控制智能家居设备数据。
  • 环境监测:采集各种环境传感器数据,上传至云平台进行分析。

二、系统设计

2.1 硬件架构

本项目采用模块化设计,硬件架构如下图所示:

  • 主控模块: 负责整个系统的控制和数据处理,采用高性能MCU作为主控芯片,配备RAM和Flash用于程序运行和数据存储。
  • 通信模块: 负责与外部网络进行通信,支持WiFi、以太网等多种网络接入方式,可选配4G/5G模块。
  • 接口模块: 负责与各种设备进行通信,支持RS485、RS232、CAN、M-Bus等多种接口,并可根据需求扩展其他接口,如数字量输入输出、模拟量输入输出等。
2.2 软件架构

本项目软件架构采用分层设计,如下图所示:

  • 硬件抽象层(HAL): 对硬件进行抽象,提供统一的接口给上层调用,屏蔽硬件差异。
  • 驱动层: 实现各种硬件模块的驱动程序,例如WiFi驱动、Ethernet驱动、RS485驱动、RS232驱动、CAN驱动等。
  • 应用层: 实现协议转换、数据处理、业务逻辑等功能。
2.3 协议转换

多协议网关的核心功能是协议转换,本项目采用“协议库”的方式实现,如下图所示:

  • 协议库: 包含各种协议的解析和封装函数,例如Modbus协议库、CAN协议库、MQTT协议库等。
  • 协议转换: 根据配置信息,调用相应的协议库函数,将一种协议的数据转换成另一种协议的数据。

三、代码实现

3.1 协议库设计

本项目采用C语言实现,每个协议库包含以下几个部分:

  • 数据结构定义: 定义协议相关的数据结构,例如报文格式、数据类型等。
  • 函数接口: 提供协议解析、数据封装、校验等函数接口。
  • 示例代码: 提供使用示例代码,方便用户快速上手。

示例代码:Modbus RTU协议库

  1. // modbus_rtu.h
  2. #ifndef MODBUS_RTU_H
  3. #define MODBUS_RTU_H
  4. #include <stdint.h>
  5. // Modbus功能码定义
  6. #define MODBUS_FC_READ_COILS 0x01
  7. #define MODBUS_FC_READ_DISCRETE_INPUTS 0x02
  8. #define MODBUS_FC_READ_HOLDING_REGISTERS 0x03
  9. #define MODBUS_FC_READ_INPUT_REGISTERS 0x04
  10. #define MODBUS_FC_WRITE_SINGLE_COIL 0x05
  11. #define MODBUS_FC_WRITE_SINGLE_REGISTER 0x06
  12. #define MODBUS_FC_WRITE_MULTIPLE_COILS 0x0F
  13. #define MODBUS_FC_WRITE_MULTIPLE_REGISTERS 0x10
  14. // Modbus异常码定义
  15. #define MODBUS_EXCEPTION_ILLEGAL_FUNCTION 0x01
  16. #define MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS 0x02
  17. #define MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE 0x03
  18. #define MODBUS_EXCEPTION_SLAVE_DEVICE_FAILURE 0x04
  19. // Modbus RTU报文结构体
  20. typedef struct {
  21. uint8_t slave_addr; // 从机地址
  22. uint8_t function_code; // 功能码
  23. uint8_t *data; // 数据
  24. uint16_t data_len; // 数据长度
  25. uint16_t crc; // CRC校验码
  26. } modbus_rtu_t;
  27. // Modbus RTU协议库函数接口
  28. uint16_t modbus_rtu_calculate_crc(uint8_t *data, uint16_t len);
  29. int modbus_rtu_pack(modbus_rtu_t *rtu, uint8_t *buffer, uint16_t buffer_size);
  30. int modbus_rtu_unpack(modbus_rtu_t *rtu, uint8_t *buffer, uint16_t buffer_len);
  31. int modbus_rtu_check_crc(modbus_rtu_t *rtu);
  32. int modbus_rtu_send(uint8_t slave_addr, uint8_t function_code, uint8_t *data, uint16_t data_len);
  33. int modbus_rtu_receive(modbus_rtu_t *rtu);
  34. #endif

  1. // modbus_rtu.c
  2. #include "modbus_rtu.h"
  3. // 计算CRC校验码
  4. uint16_t modbus_rtu_calculate_crc(uint8_t *data, uint16_t len) {
  5. uint16_t crc = 0xFFFF;
  6. uint16_t pos, i;
  7. for (pos = 0; pos < len; pos++) {
  8. crc ^= (uint16_t)data[pos];
  9. for (i = 0; i < 8; i++) {
  10. if (crc & 0x0001) {
  11. crc >>= 1;
  12. crc ^= 0xA001;
  13. } else {
  14. crc >>= 1;
  15. }
  16. }
  17. }
  18. return crc;
  19. }
  20. // 打包Modbus RTU报文
  21. int modbus_rtu_pack(modbus_rtu_t *rtu, uint8_t *buffer, uint16_t buffer_size) {
  22. uint16_t len = 0;
  23. if (buffer_size < rtu->data_len + 5) {
  24. return -1;
  25. }
  26. buffer[len++] = rtu->slave_addr;
  27. buffer[len++] = rtu->function_code;
  28. memcpy(buffer + len, rtu->data, rtu->data_len);
  29. len += rtu->data_len;
  30. rtu->crc = modbus_rtu_calculate_crc(buffer, len);
  31. buffer[len++] = (rtu->crc >> 8) & 0xFF;
  32. buffer[len++] = rtu->crc & 0xFF;
  33. return len;
  34. }
  35. // 解包Modbus RTU报文
  36. int modbus_rtu_unpack(modbus_rtu_t *rtu, uint8_t *buffer, uint16_t buffer_len) {
  37. if (buffer_len < 5) {
  38. return -1;
  39. }
  40. rtu->slave_addr = buffer[0];
  41. rtu->function_code = buffer[1];
  42. rtu->data_len = buffer_len - 5;
  43. rtu->data = buffer + 2;
  44. rtu->crc = (buffer[buffer_len - 2] << 8) | buffer[buffer_len - 1];
  45. return 0;
  46. }
  47. // 校验CRC
  48. int modbus_rtu_check_crc(modbus_rtu_t *rtu) {
  49. uint16_t crc;
  50. crc = modbus_rtu_calculate_crc(rtu->data, rtu->data_len + 2);
  51. return (crc == rtu->crc);
  52. }
  53. // 发送Modbus RTU报文
  54. int modbus_rtu_send(uint8_t slave_addr, uint8_t function_code, uint8_t *data, uint16_t data_len) {
  55. modbus_rtu_t rtu;
  56. uint8_t buffer[256];
  57. int len;
  58. rtu.slave_addr = slave_addr;
  59. rtu.function_code = function_code;
  60. rtu.data = data;
  61. rtu.data_len = data_len;
  62. len = modbus_rtu_pack(&rtu, buffer, sizeof(buffer));
  63. if (len < 0) {
  64. return -1;
  65. }
  66. // TODO: 通过串口发送数据
  67. // uart_send(buffer, len);
  68. return 0;
  69. }
  70. // 接收Modbus RTU报文
  71. int modbus_rtu_receive(modbus_rtu_t *rtu) {
  72. uint8_t buffer[256];
  73. int len;
  74. // TODO: 通过串口接收数据
  75. // len = uart_receive(buffer, sizeof(buffer));
  76. if (len < 0) {
  77. return -1;
  78. }
  79. if (modbus_rtu_unpack(rtu, buffer, len) < 0) {
  80. return -1;
  81. }
  82. if (!modbus_rtu_check_crc(rtu)) {
  83. return -1;
  84. }
  85. return 0;
  86. }

代码说明:

  • modbus_rtu_calculate_crc 函数: 实现CRC16校验码计算算法。
  • modbus_rtu_pack 函数: 将 modbus_rtu_t 结构体数据打包成Modbus RTU报文,包括添加从机地址、功能码、数据和CRC校验码。
  • modbus_rtu_unpack 函数: 从接收到的报文中解析出Modbus RTU数据结构,包括提取从机地址、功能码、数据和CRC校验码。
  • modbus_rtu_check_crc 函数: 校验接收到的报文的CRC校验码是否正确。
  • modbus_rtu_send 函数: 发送Modbus RTU报文,需要根据实际使用的串口进行修改,调用串口发送函数发送数据。
  • modbus_rtu_receive 函数: 接收Modbus RTU报文,需要根据实际使用的串口进行修改,调用串口接收函数接收数据。
3.2 驱动程序开发

驱动程序负责与硬件模块进行交互,例如初始化硬件、发送数据、接收数据等。

示例代码:RS485串口驱动程序

  1. // uart.h
  2. #ifndef UART_H
  3. #define UART_H
  4. #include <stdint.h>
  5. // 初始化串口
  6. int uart_init(uint32_t baudrate);
  7. // 发送数据
  8. int uart_send(uint8_t *data, uint16_t len);
  9. // 接收数据
  10. int uart_receive(uint8_t *data, uint16_t max_len);
  11. #endif
  1. // uart.c
  2. #include "uart.h"
  3. // TODO: 根据实际使用的MCU和串口进行修改
  4. #define UARTx ...
  5. // 初始化串口
  6. int uart_init(uint32_t baudrate) {
  7. // 配置串口波特率、数据位、停止位、校验位
  8. // ...
  9. return 0;
  10. }
  11. // 发送数据
  12. int uart_send(uint8_t *data, uint16_t len) {
  13. // 通过串口发送数据
  14. // ...
  15. return 0;
  16. }
  17. // 接收数据
  18. int uart_receive(uint8_t *data, uint16_t max_len) {
  19. // 通过串口接收数据
  20. // ...
  21. return 0;
  22. }

代码说明:

  • uart_init 函数: 初始化串口参数,包括波特率、数据位、停止位、校验位等。
  • uart_send 函数: 通过串口发送数据。
  • uart_receive 函数: 通过串口接收数据。
3.3 应用层开发

应用层负责实现协议转换、数据处理、业务逻辑等功能。

示例代码:数据采集和上传

  1. // main.c
  2. #include <stdio.h>
  3. #include "modbus_rtu.h"
  4. #include "uart.h"
  5. #include "mqtt.h"
  6. int main() {
  7. // 初始化串口
  8. uart_init(115200);
  9. // 初始化MQTT客户端
  10. mqtt_init();
  11. while (1) {
  12. // 从Modbus RTU设备读取数据
  13. modbus_rtu_t rtu;
  14. rtu.slave_addr = 1;
  15. rtu.function_code = MODBUS_FC_READ_HOLDING_REGISTERS;
  16. rtu.data = NULL;
  17. rtu.data_len = 2;
  18. modbus_rtu_send(rtu.slave_addr, rtu.function_code, rtu.data, rtu.data_len);
  19. if (modbus_rtu_receive(&rtu) == 0) {
  20. // 处理接收到的数据
  21. int16_t temperature = (rtu.data[0] << 8) | rtu.data[1];
  22. printf("Temperature: %d\n", temperature);
  23. // 通过MQTT协议上传数据
  24. char topic[] = "sensor/temperature";
  25. char message[16];
  26. sprintf(message, "%d", temperature);
  27. mqtt_publish(topic, message);
  28. }
  29. }
  30. return 0;
  31. }

代码说明:

  • 程序首先初始化串口和MQTT客户端。
  • 在主循环中,程序从Modbus RTU设备读取数据,并将数据上传
  • 在主循环中,程序从Modbus RTU设备读取数据,并将接收到的数据解析出来。
  • 然后,程序将解析出来的温度值通过MQTT协议发布到主题 "sensor/temperature" 上。

四、项目总结

本项目设计并实现了一款多协议网关,能够实现不同协议设备之间的数据互通,为物联网应用提供了一种有效的解决方案。

4.1 优势
  • 模块化设计: 采用模块化设计,方便用户根据需求进行扩展。
  • 支持多种协议: 支持多种工业现场和物联网常用协议,方便用户接入不同类型的设备。
  • 易于使用: 提供了详细的文档和示例代码,方便用户快速上手。

注意:

        不懂的可以评论或者私信我,整个流程的思路我都可以提供!!!

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/在线问答5/article/detail/835538
推荐阅读
相关标签
  

闽ICP备14008679号