汇率说明:本文出现的金额均以人民币计价。涉及外币的原始报价已按 2026 年 10 月固定基准 1 USD ≈ 6.71 元 换算,并在首次出现时标注原币种。公开渠道报价用于量级判断,口径不统一,不构成采购依据。


一个 LoRaWAN 项目做完,传感器装好了,网关上线了,平台里能看到曲线了——然后甲方问一句:这些数据能不能进我们现有的楼控系统?

这句话之后通常是一段谁都不太愿意接的活。楼控是 BACnet,工厂侧是 Modbus,LoRaWAN 这两样都不是。于是要么再买一台协议转换网关,要么找集成商写一段桥接服务,把平台的 MQTT 消息翻译成点位值。这段活不产生新数据,只为了让已经拿到的数据换个说法被读走,但它有报价、有工期、有后续的点表变更流程。

ThinkLink 在 TKE(边缘服务器)和 TKG(网关内置)形态里内置了 BACnet/IP 服务和 Modbus TCP Server,就是冲着这一段来的。本文按实际对接顺序讲清楚:什么情况下能用、字段怎么映射到点位、点表交付前必须核对什么、以及为什么"点上保存成功"不等于"BMS 已经读到值"。


一、先说适用范围:BACnet 与 Modbus 只在 TKE / TKG 上

这是本文最需要先讲清楚的一条,因为它是硬性限制,不是配置问题。

官方文档在三处写了同一句话:BACnet 服务功能仅支持在独立部署的 TKE 设备 / 网关(TKG)上使用,云版本(Cloud Service)暂不提供此功能;Modbus TCP Server 同样"仅支持独立部署的 TKE 或 TKG 设备"。

部署形态 BACnet/IP Modbus TCP Server
Cloud(公有云 SaaS) 不支持 不支持
TKE(本地服务器 / 工控机) 支持 支持
TKG(网关内置,如 GDO51 / GDI51) 支持 支持

这个限制不是功能阉割,而是协议本身决定的。BACnet/IP 默认跑在 UDP 47808(0xBAC0),设备发现靠本地广播的 Who-Is / I-Am:工作站发一个广播 Who-Is,听到它的每台设备回一个 I-Am。这套机制只在同一个广播域里成立,IP 路由器默认不转发广播,跨网段要靠 BBMD(BACnet Broadcast Management Device)。公有云的多租户环境既没有现场广播域,也不会为客户开放一个固定 UDP 端口进内网——这不是"暂时没做",是架构上做不到。

所以销售和售前口径要提前统一:客户如果只用 Cloud,就要走 MQTT 转发器或 REST API,把数据交给集成方自己落地成点位。 承诺"上云也能直接给 BMS 出 BACnet 点"是会在实施阶段翻车的。

还有一条容易被忽略的运行约束:同一个后端进程只运行一个组织的 BACnet 服务。在另一个组织启动 BACnet,会停止之前组织的实例;不能在同一进程内靠不同端口同时跑多个组织。Modbus TCP Server 同理——同一运行实例只为一个组织提供服务,切换服务组织会替换原实例。多租户的边缘部署(例如一台 TKE 服务多个子公司)必须提前设计清楚,不能默认并行。


二、这一段到底值多少钱:按点计价是行业惯例

楼宇和工业集成有个共同的计价单位:点(point)。一个温度、一个阀门状态、一个电表读数,都是一个点。理解这个,才知道内置协议服务省掉的是什么。

公开渠道能查到的两组口径(2026 年):

国内 BA 系统软件公开报价(含软件与调试)

规模 公开报价 折合每点
200 控制点 约 18,000 元 约 90 元/点
500 控制点 约 26,000 元 约 52 元/点
1000 控制点 约 38,000 元 约 38 元/点
2000 控制点 约 46,000 元 约 23 元/点

行业口径里,中等规模项目(约 2000–5000 点)的系统集成单价约 80–200 元/点,包含控制器、传感器、软件授权及调试服务;医院洁净区、数据中心等高端场景可上浮到 300 元/点以上。两组数字口径不同(前者偏软件平台,后者含硬件与施工),只用于量级判断。

海外改造报价(原币种,括号内为按 1 USD ≈ 6.71 元换算的人民币)

改造类型 公开报价 换算
直接接入的既有设备点位 $200–500 / point 约 1,342–3,355 元/点
需要协议网关的点位 $500–1,500 / point 约 3,355–10,065 元/点
超过 20 年、需更换控制器的点位 $1,500–4,000 / point 约 10,065–26,840 元/点

关键在中间那一行:"需要协议网关"这一类点位,单价是直接接入点位的 2.5–3 倍。 英国 2026 年的 BMS 安装口径也给出 £300–600 / connected point 的量级,并明确点位数量是主要成本驱动因素。

这就是内置协议服务的价值所在——它把 LoRaWAN 数据从"需要协议网关才能进 BMS"这一类,挪到了"直接接入"那一类。省下的不是传感器钱,是每个点位后面那 2.5 倍的集成单价,以及每次点表变更都要重新走一次的沟通成本。


三、BACnet 与 Modbus 的差别:交付的是"对象"还是"地址"

两个协议都把平台数据暴露给第三方系统,但交付物完全不同,选型时最容易搞混。

维度 BACnet/IP Modbus TCP Server
数据模型 对象模型:Analog / Binary 的 Input、Output、Value 寄存器模型:地址 + 位/字
自描述性 有对象名称、描述、单位、数据类型 无,全部靠点表 Excel 约定
发现方式 广播 Who-Is / I-Am,BMS 可自动发现 无发现机制,Client 必须预配地址
变化通知 支持 COV 订阅(cov_increment 阈值) 无,Client 只能轮询
写方向 Output / Value 对象写 → 触发绑定 RPC 写寄存器 → 触发绑定 RPC
主要对接对象 楼宇管理系统(BMS)、DDC SCADA、HMI、PLC
传输层 UDP,默认 47808 TCP,默认 502

一句话区分:楼控选 BACnet,工业侧选 Modbus。 两者可以同时开,一台 TKE 既出 BACnet 对象给楼控,又出 Modbus 寄存器给 SCADA,互不影响(但要注意各自的服务权限和端口是两个独立开关)。

判断依据很简单:如果对方系统支持"自动扫描设备、自动列出点位名",那是 BACnet 生态;如果对方工程师开口就问"你给我一份点表,告诉我 40001 是什么",那是 Modbus 生态。


四、BACnet 落地:五个步骤,顺序不能乱

第一步:系统管理员先在组织服务使用权限里放行

2.00.077 起,系统平台 → 系统配置新增了 ModbusTcpServer 和 BACnet 两个独立开关,存于 system_param.service_enable,不是 config 的子字段。

两个要点:

第二步:在物模型里配置 BACnet 字段

启用 BACnet 服务前,必须先在物模型中为需要对外映射的字段配置 BACnet 属性。物模型字段的 unit 会映射到 BACnet 对象的 units。

这一步是最常见的返工点:可交付的物模型包含四块配置——解析脚本、展示字段、Modbus 映射、BACnet 字段。 脚本写完、数据能解析,不等于 BACnet 字段已经配好。第 21 期讲过解码脚本,第 22 期讲过告警,这一块是第三块独立的配置工作。

第三步:在设备上开启 BACnet

设备详情 → 基本信息,开启 BACnet(Modbus 侧对应的是 modbus_tcp 设为 TRUE)。

只有开启了这个开关的设备,数据才会被映射。 按物模型"增量生成"点表时,只覆盖已绑定所选物模型且已开启 BACnet 的设备——不会为全部设备自动生成。

第四步:生成点表

运维管理 → BACnet → 增量生成 → 选择物模型 → 确认。

也可以手动新增对象,此时必填与约束如下:

字段 必填与约束
Eui 必填,选择要映射的设备
data_from 2.00.085 起可自由输入来源,默认 telemetry_data
thing_model 仅遥测来源必填;属性来源无需选择,后端保存为空字符串
field_name 必填;遥测选对应物模型字段,属性来源填写属性路径
object_type 初值 analogOutput,应按实际读写用途选择
object_id 必填,允许 0–4194303
object_name 必填,集成方识别用
default_value 初值 0,输入下限 0
units 初值 noUnits;选物模型字段时从该字段 unit 映射
cov_increment 必填,初值 0,下限 0;变化通知阈值
description、RPC 可选;有写控制需求时关联 RPC

2.00.077 起前端已移除"应用点表"按钮。点表保存、删除、导入、增量生成和重排会自动同步正在运行的服务,不需要额外点击。但反过来:保存点表不会启动一个已停止的服务,启动必须单独在服务配置里操作。

第五步:配置服务参数并重启

系统管理 → 服务器配置 → BACnet,填写网络与设备参数,先"提交",再点"重启"。

配置项 说明 参考值 可修改
IP 地址 TKL 所在设备本地 IP;0.0.0.0 表示绑定所有网口 0.0.0.0 是
Port BACnet 服务监听端口 47808(UDP,即 0xBAC0) 是
Device ID BACnet 设备唯一标识,系统内必须唯一 1 是
Vendor Identifier 厂商标识号 按实际厂商 是
Device Name 在 BMS 中显示的名称 TKE 是
Max APDU Length Accepted 最大可接受 APDU 长度 1024 是
Segmentation Supported 当前版本不通过此项启用分段传输 以实际表单为准 需与 BMS 方现场确认

参考值用于现场配置参考,不表示新增表单会自动填入;应读取已有配置并与 BMS 集成方逐项确认。修改监听参数后仍需提交并重启。


五、点表的三个约束,改起来都带连锁反应

1. object_id 范围 0–4194303,重排会改变集成方手里的标识

列表提供"重排 object id"入口。它看起来只是一个整理功能,但它会改变集成方正在使用的对象标识——BMS 那边按对象实例号取点,你这边重排一次,对方的点全部错位。重排之后必须同步更新已交付的点表,并通知集成方重新配置。

object_id 允许范围是 0–4194303,这是 BACnet 对象实例号 22 位字段的取值上限。另外 BACnet 设备实例号必须在整个 BACnet 互联网络内唯一,重复会导致间歇性故障,而且非常难查——表现是"有时候读不到",不是"一直读不到"。

2. data_from 是自由字符串,不是枚举

2.00.085 起,BACnet 字段增加了 data_from。它的类型是自由字符串(varchar(32)),不是固定枚举。建议值有三个:

data_from 取值与配置
telemetry_data 默认来源;thing_model 必填,从 device.telemetry_data[thing_model] 取字段
server_attrs 从 device.server_attrs 取属性路径;无需物模型,thing_model 保存为 ""
shared_attrs 从 device.shared_attrs 取属性路径;thing_model 保存为 ""
其他字符串 从设备对应的自有属性取路径;来源不存在则使用 default_value

空值回退规则要记牢:未知来源、缺失、null、空字符串或空白值都会回退到 default_value;但数值 0 和布尔 false 会保留。

由此得出一条重要结论:默认值不能作为设备真实状态的证据。 回退到 0 可能是"值为 0",也可能是"来源不存在",两者在 BMS 上显示成同一个数。

3. 唯一键是四元组,不是字段名

对象的业务唯一键是 (eui, data_from, thing_model, field_name)。物模型里的 BACnet 字段用"来源 + 字段名"组合 key。不同来源的同名字段可以共存——所以不要只按字段名去重,那样会误删。

顺带一提:直接新增属性对象不需要物模型;但按物模型"增量生成"仍然只覆盖已绑定该物模型且开启 BACnet 的设备,不会自动为全部设备生成状态对象。想加"在线状态"这种点,必须手动新增。


六、把"离线"做成 BMS 能读的一个点

BMS 需要知道某个传感器是不是还活着。ThinkLink 侧的做法是把在线状态当成一个属性点位暴露出去。

三步:

  1. 在目标设备基础信息开启 notify_offline = "TRUE",并设置实际心跳(确认检测状态来源)。
  2. 手动新增 BACnet 对象:选真实设备 EUI、未冲突的对象 ID/名称,来源填 server_attrs,字段填 _online,类型选 binaryInput,单位选 noUnits。
  3. 保存点表,确认运行服务同步成功,再用 BMS 检查上线、超时离线和恢复。

新增时的关键字段(还需填写 EUI、对象标识等必填项):

"data_from": "server_attrs", "thing_model": "", "field_name": "_online", "object_type": "binaryInput", "units": "noUnits", "default_value": 0

这里有一个必须写进交付文档的警告:default_value 为 0 不能证明检测结果是离线,属性缺失也可能返回 0。关闭离线通知后,_online 可能冻结为最后状态。

另外,在线状态在平台上有三个口径,彼此不能互换(第 22 期讲过):设备列表的活动时间、server_attrs.online(检测器维护的数字 1/0)、以及 MQTT 通知里的布尔 data.online。notify_offline 是设备/模板上的字符串开关 "TRUE"/"FALSE",默认关闭——未启用或缺失时不能擅自当作离线。


七、Modbus TCP:四类寄存器与十种编码

配置三步

  1. 系统管理员在组织服务使用权限允许 ModbusTcpServer。
  2. 系统管理 → 服务器配置 → Modbus TCP Server,配置监听端口(通常 502),提交后重启。
  3. 设备详情 → 基本信息,把 modbus_tcp 设为 TRUE。

寄存器映射条目

进入运维管理 → Modbus TCP → 寄存器列表,为已启用设备添加映射:

配置项 说明
设备 EUI 选择要映射数据的目标设备
寄存器类型 coils(1 bit 读写)/ discrete_inputs(1 bit 只读)/ holding_registers(16 bit 读写)/ input_registers(16 bit 只读)
寄存器地址 十六进制;平台自动检测冲突,同一类型内地址不可重复
数据来源 telemetry_data / shared_attrs / server_attrs
字段路径(name_path) 具体字段名,支持多级路径,如 temperature、sensor.value
数值类型 string、boolean、Int16LE、Int16BE、Int32LE、Int32BE、FloatLE、FloatBE、DoubleLE、DoubleBE;界面没有通用 number 选项
RPC 绑定 可选。Client 写该寄存器时自动触发指定 RPC

三个填表单的坑

地址用十六进制,个数用十进制。 两者不要混用。

"寄存器个数"由类型联动计算,不可手工修改。 例如选 FloatBE 后自动显示 2。分配后续地址时必须考虑整个占用长度,不能只核对起始地址——一个 FloatBE 占了 2 个 16 位寄存器,下一个点的起始地址要跳过 2。

LE / BE 是编码选择,必须与读取方的字节解释方式一致。 平台侧选了 FloatBE,SCADA 那边按 LE 解,读出来的是完全错误的数值,而且不会报错。

读写行为

读方向:Client 读寄存器时,ThinkLink 返回映射字段的当前值,值在设备每次上报后自动更新,不需要额外配置轮询。

写方向:Client 写寄存器,该寄存器必须绑定了 RPC;ThinkLink 收到写操作后以写入值作为参数调用绑定 RPC,参数形式为 { _eui, [field_name]: value },调用来源标记为 bacnet_server(Modbus 侧同机制)。

典型用途:SCADA 写 holding_registers 修改采样间隔,ThinkLink 触发"设置采样间隔" RPC 下发到设备。


八、写方向:三件事必须分开验收

这是整个对接里最容易出责任纠纷的地方。官方文档的表述很克制:

BACnet 写入成功不等于设备已执行,应核对设备回复或后续遥测。 写入被接受、设备执行成功和属性持久化完成必须分别验收。

三个阶段的差别:

  1. 写入被接受——平台收到了 BMS 的写请求,点表里的对象值可能已经更新。这一步秒级完成。
  2. 设备执行成功——RPC 下发到设备,设备真正改了参数。如果设备是 Class A 的 LoRaWAN 终端,下行只能在每次上行之后的接收窗口发出(RX1 约 1 秒、RX2 约 2 秒)。设备十分钟上报一次,那么这条指令最坏情况下要等十分钟才落到设备。
  3. 属性持久化完成——设备回传的新值写回属性,BMS 下一次读才能看到真实结果。

中间有任何一环失败,表现都是"BMS 上显示写入成功,但设备没变"。RPC 失败或设备反馈原值时,后续的属性通知可以把真实缓存值回推——但这个回推要等到下一次属性更新,不是即时的。

所以在验收单上,写控制这一项要写成"写后 N 个上报周期内回读确认",而不是"写操作返回成功"。


九、网络与端口:47808 不是配上就能通

BACnet/IP 这一段的网络要求,比大多数人预期的要具体。

UDP 47808,双向。 防火墙规则必须放行 UDP 47808 的两个方向。只放出站的规则,结果是工作站能发出 Who-Is,但永远收不到 I-Am 回应。

广播只在同一个广播域内有效。 工作站发 Who-Is 广播,同一个二层网段内的设备才会回应。一旦中间有 IP 路由器,路由器默认不转发广播,发现就静默失败——工作站只会报告"什么都没发现",不会报"网络不通"。跨网段的标准解法是部署 BBMD:每个有设备的子网放一台 BBMD,各自维护一张广播分发表(Broadcast Distribution Table),广播以单播发给对端 BBMD,再由它在本网段重新广播。

配置 BBMD 时有几条硬规则:广播分发表在所有 BBMD 上必须一致,只在一台表上列了某子网,那个子网就是单向可达;BBMD 自身的地址必须是静态的,因为外部设备注册和 peer 表都按地址指向它;同一子网放两台 BBMD 是配置错误,不是冗余,会产生重复流量和重复 I-Am 应答。

NAT 会破坏 BACnet/IP。 BACnet/IP 把发起方地址写在消息载荷里(BBMD 转发的 Forwarded-NPDU、外部设备注册都如此),NAT 只改写 IP 头不动载荷,结果是请求能出去、应答回不来。可行路径是路由或 VPN,不是地址转换。

跨站点的 VPN 和无线网桥经常丢广播。 单播通不代表广播通,这类链路上的监管通常要用外部设备注册,而不是靠普通发现。

另外提醒一件部署层面的事:TKE 的 Docker 部署手册给出的宿主机入站端口清单(80/8080、443、18083、18084、1883、8883、8083、8084、1700 UDP、9000、9001)里没有包含 47808 和 502。安装器不会为这两个服务设置映射或放行规则,需要部署方自行确认容器端口映射、宿主机防火墙和安全组。这一条如果漏了,现象是"平台里显示服务在运行,BMS 就是扫不到"。

最后一条是安全建议,不是可选:BACnet 最初是为隔离的控制网络设计的,存量设备普遍缺少认证机制。 一旦控制网与业务网打通,这个前提就不成立了。UDP 47808 和 TCP 502 都应该限制在楼控 / SCADA 所在的网段内可达,不要对公网开放。


十、YABE 验证与点表交付

用 YABE 做五分钟闭环

YABE(Yet Another BACnet Explorer)是常用的 BACnet 设备浏览与调试工具。配置完成后,用 YABE 连接 ThinkLink,可以发现并查看已发布的 BACnet 设备及其对象属性,验证数据是否正常发布。

建议按这个顺序核对,每一步都对应一类可能的问题:

检查项 通过标准 不通过时优先怀疑
发现设备 YABE 能扫到 Device ID 与 Device Name UDP 47808 未放行 / 跨网段无 BBMD / 服务未重启
对象列表 对象数量与点表一致,object_id 对得上 设备未开启 BACnet / 物模型未配 BACnet 字段
读值 presentValue 与平台遥测一致 字段名或来源配错 / 值回退到 default_value
单位 units 显示为预期单位(如 degreesCelsius) 物模型字段未填 unit
COV 改变化超过 cov_increment 时收到通知 cov_increment 填了 0
写控制 写 Output/Value 后设备回读有变化 未绑定 RPC / Class A 下行窗口未到

Modbus 侧没有 YABE 这样的通用浏览器,通常用 Modbus 主站工具(如 Modbus Poll 一类)按点表逐个地址读值,重点核对两点:地址口径(十六进制/十进制)和字节序(LE/BE)。

点表交付

BACnet 与 Modbus 的点表都支持导出为 Excel,也可以从 Excel 导入批量配置。导出文件包含对象类型、地址、名称、描述、数据类型、单位、读写权限等完整信息,正是集成方配置 Client 点位所需要的。

导入时有一条要注意:已存在 ID 的条目会被覆盖更新。 这与第 23 期讲过的 Excel 迁移陷阱是同一类风险——导入前先导出一份备份,覆盖不可回滚。

两条交付建议:

  1. 点表版本化。 点表、物模型版本、平台版本三者一起记录。BACnet 对象标识一变,BMS 侧的点位就全错位,事后追查成本远高于事前留档。
  2. 先交付再联调。 把点表发给集成方配置 Client,再一起联调,比边调边改点表快得多。点表保存虽然会自动同步运行中的服务,但每改一次对方就要重新配一次。

十一、验收清单

# 项目 通过标准
1 部署形态 确认使用 TKE 或 TKG;Cloud 不出 BACnet / Modbus
2 服务权限 系统配置中 BACnet 与 ModbusTcpServer 两个开关均为显式 true
3 物模型配置 解析脚本、展示字段、Modbus 映射、BACnet 字段四块都已完成
4 设备开关 目标设备 BACnet / modbus_tcp 已开启
5 服务参数 Device ID 全网唯一;Port 与 BMS 方确认一致;已提交并重启
6 端口可达 UDP 47808 双向放行(跨网段已部署 BBMD);TCP 502 放行且仅对控制网开放
7 点表完整性 对象/地址、名称、数据类型、单位、读写权限齐备;写控制点已绑定 RPC
8 读值正确 YABE / Modbus 主站读到的值与平台遥测一致,单位正确
9 状态点位 _online 点位存在,且已实测上线、超时离线、恢复三种情况
10 COV cov_increment 已按实际精度填写,不为 0
11 写控制 写入后按上报周期回读确认,不是只看写操作返回成功
12 运行监控 点表同步无错误重试;服务异常退出能自动重试,手动停止不会自动复活
13 交付物 点表 Excel 已导出并版本化;导入前已备份

十二、结语

把 LoRaWAN 数据接进 BMS 或 SCADA,本质上不是技术难题——协议都是公开的,桥接总能写出来。真正的成本在于:每一个点位都要有人配置、有人核对、有人在项目周期里反复变更。行业按点计价,需要协议网关的点位单价是直接接入点位的 2.5 到 3 倍,这笔账算的是集成工作量,不是软件功能。

ThinkLink 在 TKE / TKG 上内置 BACnet/IP 与 Modbus TCP,省掉的就是这一段。但它没有省掉的是工程纪律:BACnet 只在本地部署形态可用,同一个进程只服务一个组织,点表重排会打乱集成方的配置,写入成功不等于设备已执行,UDP 47808 需要双向放行且跨网段要 BBMD。

这些限制在文档里都写着,只是分散在不同页面。把它们集中到验收清单里,是让对接一次做对的最省事的办法。


相关阅读

本文基于 ThinkLink 官方文档(版本 2.00.077–2.00.085)撰写,配置路径与参数以实际平台版本为准。官方手册: https://www.think-link.net/docs/zh/Maintenance/BACnet.html https://www.think-link.net/docs/zh/Maintenance/ModbusTCP.html https://www.think-link.net/docs/zh/QuickStart/SystemManagement/ServerConfiguration.html