从原理到代码:C# 解析 BACnet 协议通信机制

张开发
2026/4/21 15:22:29 15 分钟阅读
从原理到代码:C# 解析 BACnet 协议通信机制
威哥最近在做一个老旧楼宇的暖通空调智能化改造甲方指定要用 BACnet 协议对接现有的西门子、江森自控设备网上找的资料要么太老要么太零散能不能给我系统讲下 BACnet 的通信机制再给点 C# 实现的核心思路没问题BACnet 是楼宇自控领域的事实标准协议从 1995 年发布到现在已经迭代了 20 多个版本核心设计思想就是“开放、互操作、分层”刚好适合你这种多品牌设备对接的场景。今天咱们从原理到核心代码实现一步步拆解。一、先搞懂 BACnet 的分层架构很多人学 BACnet 一开始就啃 ASHRAE 135 标准厚厚的一本根本啃不动其实只要先搞懂它的分层架构后面的通信机制就顺理成章了。数据链路层/物理层BACnet/IPBACnet MS/TPBACnet EthernetBACnet PTPBACnet LonTalk应用层 APDU网络层 NPDU数据链路层/物理层BACnet 采用 OSI 七层模型的简化版只保留了三层核心应用层 APDU负责定义具体的业务操作比如读设备温度、写阀门开度、报警通知等是我们开发者最需要关注的一层。网络层 NPDU负责跨网络的设备寻址和路由比如把 APDU 从 IP 网络转发到 MS/TP 总线网络。数据链路层/物理层负责底层的物理传输目前工业和楼宇改造中最常用的是BACnet/IP以太网/WiFi和BACnet MS/TPRS485 总线老旧设备可能还有 LonTalk。二、再深入 BACnet/IP 的通信机制既然你做的是老旧楼宇改造大概率会用 BACnet/IP 网关把 MS/TP 总线设备转成 IP 网络所以咱们重点讲BACnet/IP的通信机制MS/TP 只是底层传输不同上层 APDU/NPDU 完全一样。2.1 BACnet/IP 的核心概念在讲通信流程之前先记住几个 BACnet/IP 的核心概念这些是理解后续代码的基础BACnet 设备对象每个 BACnet 设备都有一个唯一的设备对象包含设备 ID、设备名称、厂商信息等基本属性设备 ID 是跨网络寻址的关键。BACnet 对象除了设备对象还有模拟输入AI、模拟输出AO、二进制输入BI、二进制输出BO、多状态输入MSI、多状态输出MSO等常用对象每个对象都有唯一的对象类型和对象实例号。BACnet 属性每个对象都有多个属性比如 AI 对象有“当前值”“单位”“报警上限”“报警下限”等属性属性有唯一的属性 ID。BACnet/IP 端口默认是 47808十六进制 0xBAC0刚好对应 BACnet 的缩写也可以自定义。BACnet/IP 广播地址用于发现网络中的所有 BACnet 设备比如 255.255.255.255全局广播或者子网广播地址。2.2 BACnet/IP 的通信流程BACnet/IP 的通信流程主要分为设备发现和数据读写/报警两部分咱们用 UML 时序图来展示2.2.1 设备发现流程江森BO设备西门子AI设备BACnet/IP网关C江森BO设备西门子AI设备BACnet/IP网关C发送Who-Is广播NPDUAPDU转发Who-Is到MS/TP总线转发Who-Is到MS/TP总线回复I-Am设备ID1001回复I-Am设备ID1002转发I-Am到IP网络保存设备列表设备发现的核心是Who-Is和I-Am两个 APDUWho-Is上位机发送的广播请求询问网络中所有 BACnet 设备的存在也可以指定设备 ID 范围。I-Am设备收到 Who-Is 后回复自己的设备对象信息包括设备 ID、设备名称、厂商信息等。2.2.2 数据读写流程西门子AI设备BACnet/IP网关C西门子AI设备BACnet/IP网关C发送ReadProperty请求设备ID1001, AI1, 属性ID85转发ReadProperty到MS/TP总线读取AI1的当前值回复ReadPropertyAck当前值22.5℃转发ReadPropertyAck到IP网络显示温度值数据读写的核心是ReadProperty/ReadPropertyMultiple和WriteProperty/WritePropertyMultiple四个 APDUReadProperty读取单个对象的单个属性。ReadPropertyMultiple读取多个对象的多个属性比 ReadProperty 效率高很多工业场景推荐用这个。WriteProperty写入单个对象的单个属性。WritePropertyMultiple写入多个对象的多个属性同样推荐用这个。三、最后讲 C# 实现的核心思路现在原理搞懂了咱们来讲 C# 实现的核心思路不用第三方收费库用 .NET 8 的System.Net.Sockets就能实现基础的 BACnet/IP 通信。3.1 核心模块划分咱们把 BACnet/IP 通信系统分成三个核心模块分层解耦方便后续扩展传输层模块UDP Socket通信广播/单播处理网络层模块NPDU封装NPDU解析应用层模块Who-Is/I-Am封装ReadProperty/WriteProperty封装APDU解析应用层模块网络层模块传输层模块3.2 传输层模块的核心实现传输层模块用 UDP Socket 实现因为 BACnet/IP 主要用 UDP 传输只有大数据包比如 ReadPropertyMultiple 读取大量属性才会用 TCP 分片传输咱们先实现基础的 UDP 通信usingSystem.Net;usingSystem.Net.Sockets;publicclassBacnetIpTransport{privateUdpClient_udpClient;privateIPEndPoint_localEndPoint;privateIPEndPoint_broadcastEndPoint;publicBacnetIpTransport(intlocalPort47808){_localEndPointnewIPEndPoint(IPAddress.Any,localPort);_broadcastEndPointnewIPEndPoint(IPAddress.Broadcast,47808);_udpClientnewUdpClient(_localEndPoint);_udpClient.EnableBroadcasttrue;}// 发送广播数据publicvoidSendBroadcast(byte[]data){_udpClient.Send(data,data.Length,_broadcastEndPoint);}// 发送单播数据publicvoidSendUnicast(byte[]data,IPEndPointremoteEndPoint){_udpClient.Send(data,data.Length,remoteEndPoint);}// 接收数据publicbyte[]Receive(outIPEndPointremoteEndPoint){remoteEndPointnull;try{return_udpClient.Receive(refremoteEndPoint);}catch{returnnull;}}// 释放资源publicvoidDispose(){_udpClient?.Close();_udpClient?.Dispose();}}3.3 网络层模块的核心实现网络层模块负责封装和解析 NPDUNPDU 的结构比较简单主要包含版本号目前是 0x01。控制字节定义 NPDU 的类型比如是否是广播、是否需要路由等。目标网络号跨网络路由时用本地网络用 0xFFFF。目标地址跨网络路由时用本地网络用 0x00。源网络号跨网络路由时用本地网络用 0xFFFF。源地址跨网络路由时用本地网络用 0x00。APDU 长度可选只有大数据包才用。APDU 数据应用层的 APDU。3.4 应用层模块的核心实现应用层模块是最复杂的负责封装和解析 APDUAPDU 的类型很多咱们先实现最常用的Who-Is和ReadPropertyWho-Is APDU结构非常简单只有一个 APDU 类型字节0x0A如果指定设备 ID 范围后面会加两个 32 位的设备 ID。ReadProperty APDU结构稍微复杂一点包含 APDU 类型字节0x0C、调用 ID、设备对象标识符、目标对象标识符、目标属性标识符、可选的数组索引。四、实战中的避坑经验最后给你讲几个我在 BACnet 项目中踩过的坑这些坑网上很少有人提但非常致命设备 ID 冲突老旧楼宇的设备可能没有统一规划设备 ID导致多个设备 ID 相同Who-Is 会收到多个 I-Am数据读写会混乱。解决方法是用 BACnet 调试工具比如 YABE先扫描网络修改冲突的设备 ID。BACnet/IP 网关的路由配置如果用 BACnet/IP 网关转 MS/TP 总线一定要配置网关的路由表否则上位机无法访问 MS/TP 总线上的设备。属性 ID 的差异不同厂商的设备某些属性的 ID 可能不同比如有些厂商的 AI 对象“当前值”属性 ID 是 85有些是 86。解决方法是用 YABE 先读取设备的所有属性确认属性 ID。UDP 数据包的大小限制BACnet/IP 的 UDP 数据包最大是 1472 字节以太网 MTU 1500 字节减去 IP 头 20 字节和 UDP 头 8 字节如果 ReadPropertyMultiple 读取的属性太多超过了这个限制设备会拒绝请求。解决方法是分多次读取或者用 TCP 分片传输。超时和重试机制BACnet 设备的响应速度可能很慢尤其是 MS/TP 总线上的设备一定要设置合理的超时时间比如 5 秒和重试次数比如 3 次。好的今天咱们从原理到核心代码实现系统讲了 BACnet 协议的通信机制实战中的避坑经验也给你了。接下来你可以先实现基础的 Who-Is 和 ReadProperty用 YABE 测试一下没问题再扩展 ReadPropertyMultiple 和 WriteProperty。 点击我的头像进入主页关注专栏第一时间收到更新提醒有问题评论区交流看到都会回。

更多文章