Skip to content

真兰水表对接

本文档描述 ZENNER 水表(DN25 / DN40) 通过 KC21 采集设备,基于 Modbus-RTU + EB 协议 接入 ThinkLink 平台 的整体方案。
内容包括:

  • 设备接线与通信参数
  • ThinkLink 平台使用方法
  • 数据上报与第三方订阅方式
  • EB 高级对接实现(EBHelper / 物模型 / RPC)

第 1 部分:用户使用说明


2. 对接设备信息(被采集设备)

2.1 水表基本信息

  • 品牌:ZENNER
  • 设备类型:水表
  • 规格型号:DN25 / DN40
  • 供电方式:外部供电
  • 供电电压:DC 12–24V

2.2 通信参数(RS485 / Modbus-RTU)

参数项配置
通信协议Modbus-RTU
波特率9600 bps
数据位8
校验位偶校验(EVEN)
停止位1
默认从站地址可配置(通过 ThinkLink RPC 修改)

2.3 水表接线说明

线缆颜色含义
红色电源 +
黑色电源 −
黄色RS485 A
绿色 / 蓝色RS485 B

2.4 官方说明书

2.5 注意事项

​ 光电直读的大口径水表DN40以上的,其标识的485地址为 16进制,否则为10进制。 使用ThinkLink配置表地址时,如果是16进制,则需要在地址前加0x前缀以标识其为16进制格式。

3. 采集设备信息(KC21)

3.1 基本信息

  • 设备型号:KC21 (A1 版本,可对外 15V供电,内置10800mA电池,例如KC218-A1-AS923-N)
  • 供电方式:内置电池
  • 对外供电能力:15V(可为水表供电)

3.2 KC21 接线说明

线缆颜色含义
红色 / 棕色电源 +
黑色电源 −
蓝色RS485 A
白色RS485 B

⚠️ 注意:
KC21 的 RS485 A/B 需与 ZENNER 水表 A/B 对应连接,避免反接。


4.1 添加设备

  1. 登录 ThinkLink 平台
  2. 进入 设备管理 → 新增设备
  3. 搜索并选择模板
  4. 完成设备创建

4.2 参数配置(抄表周期 & 地址)

路径:

选择 RPC:

参数说明

参数名含义说明
period抄表周期单位:秒
addr水表 Modbus 地址RS485 从站地址
如果用16进制那么就用0x开头
否则为十进制数字。
光电直读的大口径水表DN40以上的,其标识的485地址为 16进制,否则为10进制。

参数修改后,设备会自动保存并生效。


4.3 第三方平台数据订阅

MQTT Topic

plain
/v32/{Organization Account}/tkl/up/telemetry/{eui}

上报示例数据

json
{
    "eui": "6353012af10a9331",
    "active_time": "2026-02-05T08:35:48.000Z",
    "thingModelId": "71719731542888453",
    "thingModelIdName": "zenner_21121",
    "telemetry_data": {
        "snr": 13.5,
        "rssi": -51,
        "battery": 3.37,
        "total_flow": 1,
        "update_time": 1770280548000
    }
}

协议说明文档


第 2 部分:高级对接说明


5. 数据采集实现(EBHelper)

5.1 通信协议

  • 协议类型:Modbus-RTU
  • 实现方式:EBHelper
  • BzType:21121

5.2 采集寄存器说明

寄存器地址含义数据类型
0x0007EDC 电压状态Uint16BE
0x0008EDC 报警信息Uint16BE
0x0004–0x0005总用水量FloatBE

5.3 OTA 配置参数

javascript
let otaConfig = getOtaConfig({
    BaudRate: 9600,
    StopBits: 1,
    DataBits: 8,
    Checkbit: CheckbitEnum.EVEN,
    Battery: true,
    ConfirmDuty: 60,
    BzType: 21121,
    BzVersion: 16
})

5.4 EB 编译核心逻辑

  • 使用 EBHelper
  • 定义 eventInfo
  • 支持周期性 Modbus 采集与 LoRa 上报

5.5 EB代码

typescript
import { Buffer } from "buffer";
import { buildOtaFile } from "@EBSDK/run";
import {
    ActionAfertExpr, CalcData,
    CrcMode,
    CvtRule,
    EBBuffer,
    EBModel,
    ExprCondition,
    LoraUpEvent,
    QueryEvent, SetUpCovDataType,
    UserConfUPItem,EventInfoItem
} from "@EBSDK/EBCompiler/all_variable";
import { CheckbitEnum, getOtaConfig, HwTypeEnum, UpgrdTypeEnum } from "@EBSDK/otaConfig";
////////////////////////////////////////////////////////////////////////////////////////
const eventInfo:UserConfUPItem[]=[
    {
        name:"water",port:22,version:"0x83",dataType:"0x32",upPeriod:"10y",
        quInfo:[
            {
                protocol:"modbus",addr:"0x02",code:"0x03", periodIndex:70,
                indexAPP:150, indexCMD:0, copySize:1,payIndex:3,ackAddrIndex:0,
                listVal:[
                    { start: "0x0007", end: "0x0007",  },// EDC 电压状态 2字节:0x0007(低字节含状态码:FF/01/02 等,按手册解释)type: "Uint16BE"
                ]
            },
            {
                protocol:"modbus",addr:"0x02",code:"0x03", periodIndex:70,
                indexAPP:150, indexCMD:0, copySize:1,payIndex:3,ackAddrIndex:0,
                listVal:[
                    { start: "0x0008", end: "0x0008",  },//EDC 报警信息 2字节:0x0008,type: "Uint16BE"
                ]
            },
            {
                protocol:"modbus",addr:"0x02",code:"0x03", periodIndex:70,
                indexAPP:150, indexCMD:0, copySize:1,payIndex:3,ackAddrIndex:0,isLast:true,
                listVal:[
                    { start: "0x0004", end: "0x0005"   }, // type: "FloatBE"
                ]
            }
        ]
    }
]
let otaConfig = getOtaConfig({
    BaudRate: 9600,
    StopBits: 1,
    DataBits: 8,
    Checkbit: CheckbitEnum.EVEN,
    Battery: true,
    ConfirmDuty: 60,
    BzType: 21121,
    BzVersion: 16
})
const MODBUS_TT = (ebModel: EBModel) => {
    for (let i=0; i<eventInfo.length; i++){
        let event=new EventInfoItem(eventInfo[i]);
        event.upEventSetup()
        event.eventInstall()
    }
    return JSON.stringify(ebModel, null, 2)
}

6. 物模型解析说明

6.1 上行帧结构

端口号:

采用EBHelper标准帧结构:

字段名偏移长度 (Bytes)含义说明
version01协议版本号,固定为0x83
dataType11固定为 0x32
covStatus21保留字节(供内部 COV 处理)
status31查询事件状态
battery41电池电量
addr51子设备地址
EDC电压62大端格式,Uint16BE
EDC报警82大端格式,Uint16BE
总流量104FloatBE,单位m³

6.2 物模型信息

项目内容
物模型名称ZENNER
ThingModel ID Namezenner_21121
事件名称water

6.3 字段映射

字段名含义
total_flow总用水量
battery电池电压
rssi信号强度
snr信噪比

6.4 Telemetry 数据解析

  • LoRa 端口:22

  • 数据类型:FloatBE

  • 精度:3 位小数

    物模型使用 tklHelper实现

javascript
let    port=msg?.userdata?.port || null;
    let frameInfo={
        port:22, dataLen:14,rssi:true,battery:4,
        tagList:[{   index:0, tag:0x83}, {   index:1, tag:0x32}]
    }
    let appInfo = [
        {   name: "totalFlow", field_name: "total_flow", unit:"", index: 10, type: "floatBE",decimal:3},
        ]
    let paraInfo= {
        app_70: {name: "period", field_name: "period_modbus", unit: "s",  type: "uint32LE"},
        app_150: {name: "addr", field_name: "addr_modbus", unit: "", type: "uint8"},
    }
    let pdata={}
    let tdata={}
    let payParser=new PayloadParser({
        device:device,
        msg:msg,
        frameInfo:frameInfo,
        appInfo:appInfo,
        paraInfo:paraInfo,
    })
    if (port===214) {
        pdata=payParser.paras()
        tdata=null
    }else  {
        tdata= payParser.telemetry()
        pdata=null
    }
    return {
            telemetry_data: tdata,
            server_attrs: null,
            shared_attrs: pdata
    }

6.5 RPC 参数修改

  • RPC 名称[EB SET] easy para
  • ID Nameeb_set_easy_para
地址含义
app_70抄读周期(period)
app_150水表 Modbus 地址(addr)

周期和地址配置采用的是系统内置的标准RPC :eb_set_easy_para, 代码如下:

javascript
    let    port=msg?.userdata?.port || null;
    let frameInfo={
        port:22, dataLen:14,rssi:true,battery:4,
        tagList:[{   index:0, tag:0x83}, {   index:1, tag:0x32}]
    }
    let appInfo = [
        {   name: "totalFlow", field_name: "total_flow", unit:"", index: 10, type: "floatBE",decimal:3},
        ]
    let paraInfo= {
        app_70: {name: "period", field_name: "period_modbus", unit: "s",  type: "uint32LE"},
        app_150: {name: "addr", field_name: "addr_modbus", unit: "", type: "uint8"},
    }
    let pdata={}
    let tdata={}
    let payParser=new PayloadParser({
        device:device,
        msg:msg,
        frameInfo:frameInfo,
        appInfo:appInfo,
        paraInfo:paraInfo,
    })
    if (port===214) {
        pdata=payParser.paras()
        tdata=null
    }else  {
        tdata= payParser.telemetry()
        pdata=null
    }
    return {
            telemetry_data: tdata,
            server_attrs: null,
            shared_attrs: pdata
    }

. 总结

  • 本方案实现了 ZENNER 水表 → KC21 → EB → ThinkLink 的完整闭环
  • 支持:
    • 远程参数配置
    • 周期性数据采集
    • 标准 MQTT 对接第三方平台
  • 物模型采用 :zenner_21121
  • RPC参数修改:eb_set_easy_para
  • 模板 :ZENNER-BATTERY-21121
  • 架构稳定、低功耗,适用于水务与灌溉场景