多协议网关设计架构与实现,支持 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 <stdint.h>

// 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 < len; pos++) {
        crc ^= (uint16_t)data[pos];
        for (i = 0; i < 8; i++) {
            if (crc & 0x0001) {
                crc >>= 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 < rtu->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 < 5) {
        return -1;
    }
    rtu->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] << 8) | buffer[buffer_len - 1];
    return 0;
}

// 校验CRC
int modbus_rtu_check_crc(modbus_rtu_t *rtu) {
    uint16_t crc;
    crc = modbus_rtu_calculate_crc(rtu->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 < 0) {
        return -1;
    }
    // TODO: 通过串口发送数据
    // uart_send(buffer, len);
    return 0;
}

// 接收Modbus RTU报文
int modbus_rtu_receive(modbus_rtu_t *rtu) {
    uint8_t buffer[256];
    int len;
    // TODO: 通过串口接收数据
    // len = uart_receive(buffer, sizeof(buffer));
    if (len < 0) {
        return -1;
    }
    if (modbus_rtu_unpack(rtu, buffer, len) < 0) {
        return -1;
    }
    if (!modbus_rtu_check_crc(rtu)) {
        return -1;
    }
    return 0;
}

代码说明:

  • 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 <stdint.h>

// 初始化串口
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 <stdio.h>
#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] << 8) | rtu.data[1];
            printf("Temperature: %d\n", temperature);

            // 通过MQTT协议上传数据
            char topic[] = "sensor/temperature";
            char message[16];
            sprintf(message, "%d", temperature);
            mqtt_publish(topic, message);
        }
    }
    return 0;
}

代码说明:

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

四、项目总结

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

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

注意:

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

 

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

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

相关文章

C++类与对象-基础篇

目录 一、什么是类 1.1 语法定义 1.2 访问限定符 1.3 类域 二、类的实例化 2.1 什么是实例化 2.2 类的大小 三、this指针 3.1 引入 3.2 this指针的使用 一、什么是类 1.1 语法定义 class 类名 {}; 说明 类似于C语言中的结构体&#xff0c;括号后分号不能丢类内成员可…

SSM学习5:AOP

简介 面向切面编程&#xff0c;一种编程范式&#xff0c;指导开发者如何组织程序结构。可以在不经打原始设计的基础上为其进行功能增强。 入门案例 案例&#xff1a;在接口执行前输出当前系统时间 开发模式&#xff1a;XML 或者 注解 思路分析&#xff1a; 导入坐标&#xf…

数学建模·模糊评价法

模糊评价法 一种解决评价问题或者得出最佳方案的方法具体定义 三集&#xff1a;因素集&#xff0c;评语集和权重集&#xff0c;通过模拟矩阵的处理得到最合理的评语具体步骤 因素集 因素集的确定不难&#xff0c;难在对分级评价时&#xff0c;对因素集的分级有技巧评语集 …

TIOBE 7月编程排行榜出炉!Python再次出圈!

又到了周三&#xff0c;本周有过半了&#xff0c;大家好呀&#xff01; 每月的TIOBE编程排行榜都是技术社区关注的焦点&#xff0c;作为编程语言流行度的晴雨表&#xff0c;它反映了行业趋势和技术走向。2024年7月的榜单揭晓了一个重要变化&#xff1a;Python再次登上榜首&…

如何批量更改很多个文件夹里的文件名中包含文件夹名?

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

RedisTemplate 中序列化方式辨析

在Spring Data Redis中&#xff0c;RedisTemplate 是操作Redis的核心类&#xff0c;它提供了丰富的API来与Redis进行交互。由于Redis是一个键值存储系统&#xff0c;它存储的是字节序列&#xff0c;因此在使用RedisTemplate时&#xff0c;需要指定键&#xff08;Key&#xff09…

论文学习_Getafix: learning to fix bugs automatically

1. 引言 研究背景:现代生产代码库极其复杂并且不断更新。静态分析器可以帮助开发人员发现代码中的潜在问题(在本文的其余部分中称为错误),这对于在这些大型代码库中保持高代码质量是必要的。虽然通过静态分析尽早发现错误是有帮助的,但修复这些错误的问题在实践中仍然主要…

“连阳抓妖”,连阳抓主升浪 后市能成妖

指标名 连阳抓妖通达信副图指标 是否收费 免费 格式 源码 注:公式不带有未来函数 ► 图表展示 使用技巧 本指标源自网红博主的精辟讲解&#xff0c;专为捕捉“潜力妖股”设计。其核心逻辑简单而高效&#xff0c;仅需满足四大核心条件&#xff0c;即可锁定前期未大涨个股的…

基于51单片机的五路抢答器Protues仿真设计

目录 一、设计背景 二、实现功能 三、仿真演示 四、源程序&#xff08;部分&#xff09; 一、设计背景 近年来随着科技的飞速发展&#xff0c;单片机的应用正在不断的走向深入。本文阐述了基于51单片机的五路抢答器设计。本设计中&#xff0c;51单片机充当了核心控制器的角…

Python实现傅里叶级数可视化工具

Python实现傅里叶级数可视化工具 flyfish 有matlab实现&#xff0c;我没matlab&#xff0c;我有Python&#xff0c;所以我用Python实现。 整个工具的实现代码放在最后,界面使用PyQt5开发 起源 傅里叶级数&#xff08;Fourier Series&#xff09;由法国数学家和物理学家让-巴…

[激光原理与应用-106]:南京科耐激光-激光焊接-焊中检测-智能制程监测系统IPM介绍 - 9 - 图解常见的焊接缺陷/缺欠分类

目录 前言&#xff1a; 1、焊接缺陷的类型 2、焊接缺陷的危害 3、结论 一、功能性缺陷 1.1 虚焊&#xff1a;最重要的非视觉检测的缺陷 1.虚焊的定义 2.虚焊的成因 3.虚焊的危害 4.虚焊的检测与解决 二、成型性缺陷 2.1 黑爆缺陷 1、黑爆缺陷的定义与外观 2、黑爆…

深度优先搜索(所有可达路径)

参考题目&#xff1a;所有可达路径 题目描述 给定一个有 n 个节点的有向无环图&#xff0c;节点编号从 1 到 n。请编写一个函数&#xff0c;找出并返回所有从节点 1 到节点 n 的路径。每条路径应以节点编号的列表形式表示。 输入描述 第一行包含两个整数 N&#xff0c;M&…

红日靶场----(三)2.漏洞利用

上期的通过一句话木马实现对目标主机的持久后门 我使用的是蚁剑&#xff0c;蚁剑安装及使用参考&#xff1a; 下载地址&#xff1a; GitHub - AntSwordProject/AntSword-Loader: AntSword 加载器 安装即使用&#xff1a; 1. 快速入门 语雀 通过YXCMS的后台GETSHELL 利用…

C++第四弹 -- 类与对象(中上) (构造函数 析构函数 拷贝构造函数)

目录 前言构造函数1. 概念2. 特征 析构函数1. 概念2. 特征 拷贝构造函数1. 概念2. 特征 总结 前言 让我们一起揭开 C 对象生命周期管理的神秘面纱&#xff0c;掌握构造函数、析构函数和拷贝构造函数的精髓&#xff01; 博客主页: 酷酷学!!! 期待更多好文, 点击关注~ 构造函…

Linux系统中磁盘管理LVM与挂载

Linux系统中磁盘管理LVM与挂载 本文以属于Linux系统基本概念&#xff0c;如果以查找教程教程&#xff0c;解决问题为主&#xff0c;只需要查看本文后半部分。如需要系统性学习请查看本文前半部分。 本文操作极容易导致主机无法自动重启&#xff0c;请慎重操作。操作前务必要进…

新手教学系列——crontab 使用不当引发的服务器性能问题

起因及症状 最近,我们的一台服务器随着运行时间的增加,逐渐出现了压力过大的问题。具体表现为数据库连接数飙升至 4000+,Redis 频繁超时,系统报错文件打开数过多等。针对这些问题,我们逐一检查了数据库连接池、Redis 连接池以及系统的 ulimit 配置,但都未能找到问题的根…

ROS服务通信自定义srv

服务通信自定义srv 流程:创建ROS功能包按照固定格式创建srv文件编译配置文件编译生成中间文件 流程: srv 文件内的可用数据类型与 msg 文件一致&#xff0c;且定义 srv 实现流程与自定义 msg 实现流程类似&#xff0c;需查阅msg文件的可以浏览ROS话题通信流程自定义数据msg格式…

7月报名 | 海克斯康CAEfatigue疲劳分析培训

您好&#xff01;感谢您长期以来对优飞迪科技与海克斯康的关注与支持。我们诚邀您参加海克斯康CAEfatigue疲劳分析培训&#xff0c;特邀海克斯康原厂讲师将通过培训帮助您了解CAEfatigue的功能并使用其进行疲劳分析的过程、参数设置以及软件操作方法和技巧&#xff0c;学会使用…

VS2019使用C#写窗体程序技巧(1)

1、打开串口 private void button1_Click(object sender, EventArgs e){myPort cmb1.Text;mybaud Convert.ToInt32(cmb2.Text, 10);databit 8;parity Parity.None;stopBit StopBits.One;textBox9.Text "2";try{sp new SerialPort(myPort, mybaud, parity, dat…

Linux:进程池制作(基于匿名管道和命名管道两个版本)

Linux&#xff1a;进程池制作 & 匿名管道 & 命名管道 前言一、匿名管道制作进程池一、进程池框架二、创建管道、创建进程、工作进程执行任务2.1 创建管道、创建进程 2.2 工作进程执行任务三、主进程向子进程发送任务3.1 任务封装3.2 主进程向子进程发送任务 四、回收资…