Skip to content

22105-T100 对接文档

传感器简介

T100 工业温度传感器,单寄存器采集,Modbus-RTU 通过 KC21 电池版 DTU 接入,coefficient 0.01

项目
项目名称TECHAI-22105-T100
厂商特弗德 (Techai)
型号T100
业务代码22105
对接模式edgebus-dtu
协议modbus-rtu

模型清单

可使用下列名称或 ID 在 ThinkLink 中搜索对应模型。

模型类型名称id_name平台模型 ID
RPC[T100 GET] parat100_get_2210596540340621676549
RPC[T100 SET] parat100_set_2210596540592598683653
TemplateT100118674122111610880
Thing Model[T100]t100_2210596539321842012165
Thing Model[TECHAI-22105-T100资产] 区域聚合t100_asset_22105119677174411063313
Parameter Thing Model[T100-PARA]t100_para_22105111418029618655232

产品特点与适用范围

本项目面向“温度传感器”场景。能力边界、字段和可部署模型均以当前 descriptor、requirements 与模型源文件为准。

部署与采集信息

| DTU | KC21 | | 供电 | battery | | LoRaWAN Class | A | | 接口 | RS-485 | | 波特率 | 9600 |

遥测与寄存器定义

字段名称类型单位地址/来源说明
temperature温度int16BE0x0001工业温度,coefficient 0.01,2位小数

参数定义

参数名称类型默认值范围单位说明
app_70period_upuint32LE0-4294967295s
app_74period_readuint32LE0-4294967295s
app_110cov_temperatureint16BE-32768-32767
app_150addr_modbusuint811-247

Requirements 证据

以下关键事实由 requirements 源文件提取;原始行号用于复核。

类别来源事实来源
协议/数据- Description: T100 工业温度传感器,单寄存器采集,Modbus-RTU 通过 KC21 电池版 DTU 接入,coefficient 0.01requirement/requirements.md:11
部署- Power: batteryrequirement/requirements.md:17
协议/数据- Protocol: modbus-rturequirement/requirements.md:19
协议/数据- Interface: RS-485requirement/requirements.md:20
协议/数据- Baud rate: 9600requirement/requirements.md:21
协议/数据| Field | Name | Type | Unit | Address/Source | Description |requirement/requirements.md:25
协议/数据| t100_para_22105 | addr_modbus | number | | Modbus Address |requirement/requirements.md:48
协议/数据| t100_set_22105 | addr_modbus | number | Modbus Address (1-247) | — | |requirement/requirements.md:66
部署Validate model IDs, identifiers, payload fields, RPC behavior, triggers, and templates against a real device or platform test asset before production deployment.requirement/requirements.md:89
协议/数据- No unresolved identity or protocol facts were detected from the descriptor.requirement/requirements.md:93
部署Every item above must be checked during deployment. No missing value is promoted to a confirmed fact.requirement/requirements.md:95

EdgeBus 代码

以下为当前使用的 EdgeBus 业务代码。

typescript
import {Buffer} from "buffer";
import {buildOtaFile} from "@EBSDK/run";
import { EBModel } from "@EBSDK/EBCompiler/EBModel/EBModel";
import { EventInfoItem } from "@EBSDK/EBCompiler/plugins/EBHelper";
import type { UserConfUPItem } from "@EBSDK/EBCompiler/plugins/EBHelper";
import {CheckbitEnum, getOtaConfig} from "@EBSDK/otaConfig";
////////////////////////////////////////////////////////////////////////////////////////
const eventInfo:UserConfUPItem[]=[
    {
        name:"t100",port:22, version:"0x86",dataType:"0x05",upPeriodIndex:70,
        quInfo:[
            {
            protocol:"modbus",code:"0x03", periodIndex:74,//addr:"0x01",
            indexAPP:150, indexCMD:0, copySize:1,isLast:false,//period:"900s",payIndex:3,ackAddrIndex:0,
            listVal:[
                { start: "1", end: "1" ,covType:"Int16BE",covAppIndex:110},  //冷藏室温度,refrigerator_temp,coefficient=0.1
                ]
            }
        ]
    }
]
let otaConfig = getOtaConfig({
    SwVersion:31,
    BaudRate: 9600,
    StopBits: 1,
    DataBits: 8,
    Checkbit: CheckbitEnum.NONE,
    Battery: true,
    ConfirmDuty: 60,
    BzType: 22105,
    BzVersion: 11
})
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)
}
buildOtaFile(import.meta.url, otaConfig, MODBUS_TT)

物模型与 RPC 代码

RPC: [T100 GET] para

id_name: t100_get_22105 · ID: 96540340621676549

javascript
import {Buffer} from "buffer";
import { BufferTKL ,PayloadParser,RPCHelper,Utils} from "#tklHelper";
function rpc_script({ device, params, alarms, logger, org_params }) {
    let paraDef = {
        app_70:{name: "period_up", field_name: "period_up", unit:"S", type: "uint32le"},
        app_74:{name: "period_read", field_name: "period_read", unit:"S", type: "uint32le"},
        app_110: {name: "cov_temperature", field_name: "cov_temperature", unit: "℃", type: "int16BE",coefficient:0.1,decimal:1},
        app_150:{name: "addr_modbus", field_name: "addr_modbus", unit:"", type: "uint8"}
    }
    let frames=RPCHelper.buildFrame({
        paraDef:paraDef,
        params:params});
    let msg=RPCHelper.makeMSG({
        msgType:Utils.msgType.paras,
        device:device,
        dnBuffer:frames.readBuffer,
        sleepTime:0,
    })
    return [msg]
}
rpc_script(
    {
        device:"123456",
        params:{
            period_modbus:900,
            addr_modbus:"0x70"
        }
    }
)

RPC: [T100 SET] para

id_name: t100_set_22105 · ID: 96540592598683653

javascript
import {Buffer} from "buffer";
import { BufferTKL ,PayloadParser,RPCHelper,Utils} from "#tklHelper";
function rpc_script({ device, params, alarms, logger, org_params }) {
    let classMode = (device && device.shared_attrs && device.shared_attrs.class_mode) || "ClassA";
    const rpcName="t100_set_22105"
    let paraDef = {
        app_70:{name: "period_up", field_name: "period_up", unit:"S", type: "uint32le"},
        app_74:{name: "period_read", field_name: "period_read", unit:"S", type: "uint32le"},
        app_110: {name: "cov_temperature", field_name: "cov_temperature", unit: "℃", type: "int16BE",coefficient:0.1,decimal:1},
        app_150:{name: "addr_modbus", field_name: "addr_modbus", unit:"", type: "uint8"}
    }
    let frames=RPCHelper.buildFrame({
        paraDef:paraDef,
        params:params});
    let redoBuffer=RPCHelper.redo()
    let dnBuffer=Buffer.alloc(frames.writeBuffer.length+frames.readBuffer.length);
    frames.writeBuffer.copy(dnBuffer,0)
    frames.readBuffer.copy(dnBuffer,frames.writeBuffer.length)
    logger.info("set para")
    let msgQue=Utils.makeParaSetMSG({
        device:device,
        classMode:classMode,
        rpcName:rpcName,
        params:params,
        paraDownBuffer:dnBuffer,
        extraAppBuffer:redoBuffer
    })
    if (msgQue.length==0)  return null
    return msgQue
}

Thing Model: [T100]

id_name: t100_22105 · ID: 96539321842012165

javascript
import {BufferTKL, PayloadParser, RPCHelper} from "#tklHelper";

function payload_parser(device, msg, thingModelId, noticeAttrs, org_params) {
    let    port=msg?.userdata?.port || null;
    if(port!==22) return null
    let frameInfo={
        port:22, dataLen:-1,rssi:true,battery:4,
        tagList:[{   index:0, tag:0x86}, {   index:1, tag:0x05}]
    }
    let appInfo = [
        {   name: "temperature", field_name: "temperature", unit:"℃", index: 6, type: "int16BE",coefficient:0.01,decimal:2},

    ]
    let payParser=new PayloadParser({
        device:device,
        msg:msg,
        frameInfo:frameInfo,
        appInfo:appInfo,
    })
    let tdata= payParser.telemetry()
    if((tdata?.status&0x02)===0x02) { // time out , just update the status.
        const status=tdata.status
        tdata={ ...(device.telemetry_data?.[thingModelId] ?? {})}
        tdata.status=status
    }
    let result = {
        telemetry_data: tdata,
        server_attrs: null,
        shared_attrs: null
    };
    // ── rpcHook (设备对接通用规则) ──
    if (typeof RPCHelper.buildRpcHookAction === "function") {
      const __a = RPCHelper.buildRpcHookAction({ device, msg, thingModelId, idName: "t100_22105", name: "[T100]", parsed: { telemetry_data: result.telemetry_data, server_attrs: result.server_attrs, shared_attrs: result.shared_attrs } });
      if (__a) { result.actions = result.actions || []; result.actions.push(__a); }
    }
    return result;
}
console.log(payload_parser(
    {},
    {
        "if": "loraWAN",
        "gwrx": [
            {
                "eui": "5a5301250103020d",
                "chan": 0,
                "lsnr": 12.8,
                "rfch": 0,
                "rssi": -24,
                "time": "2026-02-27T03:21:37.8216721Z",
                "tmms": 0,
                "tmst": 2799281240,
                "ftime": 0
            }
        ],
        "type": "data",
        "token": 2693015,
        "moteTx": {
            "codr": "4/5",
            "datr": "SF9BW125",
            "freq": 470.3,
            "modu": "LORA",
            "macAck": "",
            "macCmd": ""
        },
        "geoInfo": {
            "type": "gw:wifi",
            "accuracy": 50,
            "altitude": 0,
            "latitude": 34.20012,
            "longitude": 108.86424
        },
        "moteeui": "6353012af10a5685",
        "version": "3.0",
        "userdata": {
            "port": 22,
            "class": "ClassC",
            "seqno": 20001,
            "payload": "gzQAAgAAAAAAAAAAAAA=",
            "confirmed": false
        }
    },
    "1",
    {telemetry_data:true},
    {}
))

Thing Model: [TECHAI-22105-T100资产] 区域聚合

id_name: t100_asset_22105 · ID: 119677174411063313

javascript
import {PayloadParser, RPCHelper} from '#tklHelper';

// 资产(VIRTUAL)聚合物模型 — 通用 agg_spec 驱动聚合器,见 docs/references/workflow/asset-aggregation-model.md
// device=资产, msg=源设备快照, thingModelId=本物模型ID, noticeAttrs=变更标记。
// 聚合规格在资产 server_attrs.agg_spec:min_snr=取最小(snr);min_rssi=取最小(rssi);min_battery=取最小(battery);zone_temperature_avg=空间均值(temperature);online_count=在线数(—)
function payload_parser(device, msg, thingModelId, noticeAttrs) {
    if (!noticeAttrs || !noticeAttrs.telemetry_data) {
        return { telemetry_data: null, server_attrs: null, shared_attrs: null };
    }
    const sa      = device?.server_attrs || {};
    const spec    = sa.agg_spec || {};
    const members = { ...(sa.members || {}) };
    const staleMs = sa.stale_ms != null ? sa.stale_ms : 600000;
    const now     = (typeof Date !== "undefined" && Date.now) ? Date.now() : null;

    // 合并源设备各物模型的最新快照为一个扁平对象
    const srcTd = {};
    const all = msg?.telemetry_data || {};
    for (const k in all) { Object.assign(srcTd, all[k]); }

    // 收集 spec 引用到的全部源字段,把本帧带到的值并进该成员快照
    const want = {};
    for (const out in spec) { if (spec[out].src) want[spec[out].src] = true; }
    const got = {}; let touched = false;
    for (const f in want) { if (srcTd[f] !== undefined && srcTd[f] !== null) { got[f] = srcTd[f]; touched = true; } }
    if (touched && msg && msg.eui) {
        const prev = (members[msg.eui] && members[msg.eui].v) || {};
        const seq  = (members[msg.eui] && members[msg.eui].ts) || 0;
        members[msg.eui] = { v: Object.assign({}, prev, got), ts: now != null ? now : seq + 1 };
    }

    const euis = Object.keys(members);
    const fresh = euis.filter(function (e) {
        if (now == null || staleMs <= 0) return true;
        const ts = members[e].ts;
        return ts != null && ts <= now && (now - ts) <= staleMs;
    });
    const num = function (x) { return (x != null && !isNaN(x)) ? Number(x) : null; };

    const out = {};
    for (const field in spec) {
        const op = spec[field].op, src = spec[field].src;
        if (op === "count_online") { out[field] = fresh.length; continue; }
        const pool = (op === "sum_cumul") ? euis : fresh;
        const vals = pool.map(function (e) { return num(members[e].v && members[e].v[src]); }).filter(function (x) { return x != null; });
        if (op === "avg")        out[field] = vals.length ? Number((vals.reduce(function (a, b) { return a + b; }, 0) / vals.length).toFixed(2)) : null;
        else if (op === "sum_snap" || op === "sum_cumul") out[field] = vals.length ? Number(vals.reduce(function (a, b) { return a + b; }, 0).toFixed(3)) : null;
        else if (op === "max")   out[field] = vals.length ? Math.max.apply(null, vals) : null;
        else if (op === "min")   out[field] = vals.length ? Math.min.apply(null, vals) : null;
        else if (op === "or")    out[field] = pool.some(function (e) { const x = members[e].v && members[e].v[src]; return x != null && x != 0; }) ? 1 : 0;
        else if (op === "count_on") out[field] = pool.filter(function (e) { const x = members[e].v && members[e].v[src]; return x != null && x != 0; }).length;
        else out[field] = null;
    }
    let result = { telemetry_data: out, server_attrs: { members: members }, shared_attrs: null };
    // ── rpcHook (设备对接通用规则) ──
    if (typeof RPCHelper.buildRpcHookAction === "function") {
      const __a = RPCHelper.buildRpcHookAction({ device, msg, thingModelId, idName: "t100_asset_22105", name: "[TECHAI-22105-T100资产] 区域聚合", parsed: { telemetry_data: result.telemetry_data, server_attrs: result.server_attrs, shared_attrs: result.shared_attrs } });
      if (__a) { result.actions = result.actions || []; result.actions.push(__a); }
    }
    return result;
}

// --- local debug ---(两台成员先后上报;平台会把返回的 members 持久化回 device,这里手动模拟)
const agg_spec = {
  "min_snr": {
    "op": "min",
    "src": "snr"
  },
  "min_rssi": {
    "op": "min",
    "src": "rssi"
  },
  "min_battery": {
    "op": "min",
    "src": "battery"
  },
  "zone_temperature_avg": {
    "op": "avg",
    "src": "temperature"
  },
  "online_count": {
    "op": "count_online",
    "src": ""
  }
};
const device = { name: "TECHAI-22105-T100资产-1", server_attrs: { agg_spec, stale_ms: 1800000, members: {} } };
const r1 = payload_parser(device, { eui: "MEMBER_A", telemetry_data: { m1: {"snr":1,"rssi":1,"battery":1,"temperature":1} } }, "tmid", { telemetry_data: true });
device.server_attrs.members = r1.server_attrs.members;            // 平台持久化
console.log("成员A上报后:", JSON.stringify(r1.telemetry_data));
const r2 = payload_parser(device, { eui: "MEMBER_B", telemetry_data: { m1: {"snr":2,"rssi":2,"battery":2,"temperature":2} } }, "tmid", { telemetry_data: true });
device.server_attrs.members = r2.server_attrs.members;
console.log("成员B上报后(2台聚合):", JSON.stringify(r2.telemetry_data));
void PayloadParser;

Parameter Thing Model: [T100-PARA]

id_name: t100_para_22105 · ID: 111418029618655232

javascript
import {PayloadParser, RPCHelper, Utils} from "#tklHelper";

function payload_parser(device, msg, thingModelId, noticeAttrs, org_params) {
    let    port=msg?.userdata?.port || null;
    const rpcName="t100_set_22105";
    let paraDef = {
        app_70:{name: "period_up", field_name: "period_up", unit:"S", type: "uint32le"},
        app_74:{name: "period_read", field_name: "period_read", unit:"S", type: "uint32le"},
        app_110: {name: "cov_temperature", field_name: "cov_temperature", unit: "℃", type: "int16BE",coefficient:0.1,decimal:1},
        app_150:{name: "addr_modbus", field_name: "addr_modbus", unit:"", type: "uint8"}
    }
    if (port!==214) {
        let checkData=Utils.paraCheck(rpcName,device.server_attrs,device.shared_attrs)
        return {
            server_attrs: checkData.sdata,
            actions:checkData.actions,
        }
    }
    let pdata=(new PayloadParser({
        device:device,
        msg:msg,
        paraInfo:paraDef,
    })).paras()
    let checkData= Utils.paraCheck(rpcName,device.server_attrs,pdata)
    let result = {
        telemetry_data: pdata,
        server_attrs: checkData.sdata,
        shared_attrs: pdata,
        actions: checkData.actions,
    };
    // ── rpcHook (设备对接通用规则) ──
    if (typeof RPCHelper.buildRpcHookAction === "function") {
      const __a = RPCHelper.buildRpcHookAction({ device, msg, thingModelId, idName: "t100_para_22105", name: "[T100-PARA]", parsed: { telemetry_data: result.telemetry_data, server_attrs: result.server_attrs, shared_attrs: result.shared_attrs } });
      if (__a) { result.actions = result.actions || []; result.actions.push(__a); }
    }
    return result;
}
console.log(payload_parser(
    {},
    {
        "if": "loraWAN",
        "gwrx": [
            {
                "eui": "5a5301250103021e",
                "chan": 0,
                "lsnr": 14.2,
                "rfch": 1,
                "rssi": -54,
                "time": "2026-03-25T01:59:11.8596759Z",
                "tmms": 0,
                "tmst": 878152180,
                "ftime": 0
            }
        ],
        "type": "data",
        "token": 132751,
        "moteTx": {
            "codr": "4/5",
            "datr": "SF7BW125",
            "freq": 471.5,
            "modu": "LORA",
            "macAck": " [down] 6097990132204c001136b35e",
            "macCmd": ""
        },
        "geoInfo": {
            "type": "gw:wifi",
            "accuracy": 50,
            "altitude": 0,
            "latitude": 34.19907,
            "longitude": 108.86571
        },
        "moteeui": "6353012af10b4669",
        "version": "3.0",
        "userdata": {
            "adr": false,
            "port": 214,
            "class": "ClassA",
            "seqno": 1196,
            "payload": "LwUEAwAAAC8DCgEBLwpGCBAOAAAQDgAALwOWAQE=",
            "confirmed": true
        }
    },
    "1",
    {telemetry_data:true},
    {}
))

物模型字段定义

物模型字段类型单位名称
t100_22105snrnumberdBSNR
t100_22105rssinumberdBmRSSI
t100_22105rationumberRatio
t100_22105batterynumbervBattery
t100_22105temperaturenumberTemperature
t100_asset_22105min_snrnumberdBZone SNR
t100_asset_22105min_rssinumberdBmZone RSSI
t100_asset_22105min_batterynumbervZone Battery
t100_asset_22105zone_temperature_avgnumberZone Temperature
t100_asset_22105online_countnumberZone Online Count
t100_para_22105snrnumberdBSNR
t100_para_22105rssinumberdBmRSSI
t100_para_22105period_upnumbersUpload Period
t100_para_22105period_readnumbersCollect Period
t100_para_22105cov_temperaturenumberTemperature Change Threshold
t100_para_22105addr_modbusnumberModbus Address

RPC 参数定义

RPC参数类型说明默认值单位
t100_set_22105period_upnumberUpload Period (s)s
t100_set_22105period_readnumberCollect Period (s)s
t100_set_22105cov_temperaturenumberTemperature Change Threshold
t100_set_22105addr_modbusnumberModbus Address (1-247)

触发器定义

无项目专用触发器。

第三方平台数据订阅

MQTT Topic: /v32/{Organization Account}/tkl/up/telemetry/{eui}

json
{
  "eui": "6353012af10a9331",
  "active_time": "2026-07-13T08:35:48.000Z",
  "thingModelId": "Use the platform model ID from the model inventory",
  "thingModelIdName": "Use the current telemetry model id_name",
  "telemetry_data": {
    "snr": 13.5,
    "rssi": -51,
    "battery": 3.37,
    "temperature": 1
  }
}

模板选择与验证

在 ThinkLink 平台按项目名称、业务代码或模型清单中的 id_name、ID 搜索并绑定模型。部署后使用真实上行帧核对字段、单位、RPC 和触发器行为。

补充说明

需求与决策依据:requirement/requirements.md