多协议网关设计架构与实现,支持 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协议库
// modbus_rtu.h #ifndef MODBUS_RTU_H #define MODBUS_RTU_H #include // Modbus功能码定义 #define MODBUS_FC_READ_COILS 0x01 #define MODBUS_FC_READ_DISCRETE_INPUTS 0x02 #define MODBUS_FC_READ_HOLDING_REGISTERS 0x03 #define MODBUS_FC_READ_INPUT_REGISTERS 0x04 #define MODBUS_FC_WRITE_SINGLE_COIL 0x05 #define MODBUS_FC_WRITE_SINGLE_REGISTER 0x06 #define MODBUS_FC_WRITE_MULTIPLE_COILS 0x0F #define MODBUS_FC_WRITE_MULTIPLE_REGISTERS 0x10 // Modbus异常码定义 #define MODBUS_EXCEPTION_ILLEGAL_FUNCTION 0x01 #define MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS 0x02 #define MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE 0x03 #define MODBUS_EXCEPTION_SLAVE_DEVICE_FAILURE 0x04 // Modbus RTU报文结构体 typedef struct { uint8_t slave_addr; // 从机地址 uint8_t function_code; // 功能码 uint8_t *data; // 数据 uint16_t data_len; // 数据长度 uint16_t crc; // CRC校验码 } modbus_rtu_t; // Modbus RTU协议库函数接口 uint16_t modbus_rtu_calculate_crc(uint8_t *data, uint16_t len); int modbus_rtu_pack(modbus_rtu_t *rtu, uint8_t *buffer, uint16_t buffer_size); int modbus_rtu_unpack(modbus_rtu_t *rtu, uint8_t *buffer, uint16_t buffer_len); int modbus_rtu_check_crc(modbus_rtu_t *rtu); int modbus_rtu_send(uint8_t slave_addr, uint8_t function_code, uint8_t *data, uint16_t data_len); int modbus_rtu_receive(modbus_rtu_t *rtu); #endif
// modbus_rtu.c #include "modbus_rtu.h" // 计算CRC校验码 uint16_t modbus_rtu_calculate_crc(uint8_t *data, uint16_t len) { uint16_t crc = 0xFFFF; uint16_t pos, i; for (pos = 0; pos >= 1; crc ^= 0xA001; } else { crc >>= 1; } } } return crc; } // 打包Modbus RTU报文 int modbus_rtu_pack(modbus_rtu_t *rtu, uint8_t *buffer, uint16_t buffer_size) { uint16_t len = 0; if (buffer_size data_len + 5) { return -1; } buffer[len++] = rtu->slave_addr; buffer[len++] = rtu->function_code; memcpy(buffer + len, rtu->data, rtu->data_len); len += rtu->data_len; rtu->crc = modbus_rtu_calculate_crc(buffer, len); buffer[len++] = (rtu->crc >> 8) & 0xFF; buffer[len++] = rtu->crc & 0xFF; return len; } // 解包Modbus RTU报文 int modbus_rtu_unpack(modbus_rtu_t *rtu, uint8_t *buffer, uint16_t buffer_len) { if (buffer_len slave_addr = buffer[0]; rtu->function_code = buffer[1]; rtu->data_len = buffer_len - 5; rtu->data = buffer + 2; rtu->crc = (buffer[buffer_len - 2] data, rtu->data_len + 2); return (crc == rtu->crc); } // 发送Modbus RTU报文 int modbus_rtu_send(uint8_t slave_addr, uint8_t function_code, uint8_t *data, uint16_t data_len) { modbus_rtu_t rtu; uint8_t buffer[256]; int len; rtu.slave_addr = slave_addr; rtu.function_code = function_code; rtu.data = data; rtu.data_len = data_len; len = modbus_rtu_pack(&rtu, buffer, sizeof(buffer)); if (len
代码说明:
- 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串口驱动程序
// uart.h #ifndef UART_H #define UART_H #include // 初始化串口 int uart_init(uint32_t baudrate); // 发送数据 int uart_send(uint8_t *data, uint16_t len); // 接收数据 int uart_receive(uint8_t *data, uint16_t max_len); #endif
// uart.c #include "uart.h" // TODO: 根据实际使用的MCU和串口进行修改 #define UARTx ... // 初始化串口 int uart_init(uint32_t baudrate) { // 配置串口波特率、数据位、停止位、校验位 // ... return 0; } // 发送数据 int uart_send(uint8_t *data, uint16_t len) { // 通过串口发送数据 // ... return 0; } // 接收数据 int uart_receive(uint8_t *data, uint16_t max_len) { // 通过串口接收数据 // ... return 0; }
代码说明:
- uart_init 函数: 初始化串口参数,包括波特率、数据位、停止位、校验位等。
- uart_send 函数: 通过串口发送数据。
- uart_receive 函数: 通过串口接收数据。
3.3 应用层开发
应用层负责实现协议转换、数据处理、业务逻辑等功能。
示例代码:数据采集和上传
// main.c #include #include "modbus_rtu.h" #include "uart.h" #include "mqtt.h" int main() { // 初始化串口 uart_init(115200); // 初始化MQTT客户端 mqtt_init(); while (1) { // 从Modbus RTU设备读取数据 modbus_rtu_t rtu; rtu.slave_addr = 1; rtu.function_code = MODBUS_FC_READ_HOLDING_REGISTERS; rtu.data = NULL; rtu.data_len = 2; modbus_rtu_send(rtu.slave_addr, rtu.function_code, rtu.data, rtu.data_len); if (modbus_rtu_receive(&rtu) == 0) { // 处理接收到的数据 int16_t temperature = (rtu.data[0]
文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。