Skip to content

22105-T100 Integration Guide

Sensor Overview

T100 is integrated with ThinkLink through modbus-rtu in edgebus-dtu mode. This guide documents the current deployable artifacts and their source-traceable versions.

ItemValue
ProjectTECHAI-22105-T100
VendorTECHAI
ModelT100
Business Code22105
Integration Modeedgebus-dtu
Protocolmodbus-rtu

Model catalog

Search for these models in ThinkLink by name or ID.

Model typeNameid_namePlatform model 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

Features and Application Scope

This project covers T100 through modbus-rtu. Its capability boundary, fields, and deployable models follow the current descriptor, requirements, and model sources.

Deployment and Data Acquisition

| DTU | KC21 | | Power | battery | | LoRaWAN Class | A | | Interface | RS-485 | | Baud Rate | 9600 |

Telemetry and Register Definitions

FieldNameTypeUnitAddress/SourceDescription
temperaturetemperatureint16BE0x0001Source field: temperature

Parameter Definitions

ParameterNameTypeDefaultRangeUnitDescription
app_70period_upuint32LE0-4294967295sSource parameter: app_70
app_74period_readuint32LE0-4294967295sSource parameter: app_74
app_110cov_temperatureint16BE-32768-32767Source parameter: app_110
app_150addr_modbusuint811-247Source parameter: app_150

Requirements Evidence

The following key facts were extracted from the requirements source; line references support verification.

CategorySource FactSource
Protocol/Data- Description: T100 Modbus-RTU KC21 DTU coefficient 0.01requirement/requirements.md:11
Deployment- Power: batteryrequirement/requirements.md:17
Protocol/Data- Protocol: modbus-rturequirement/requirements.md:19
Protocol/Data- Interface: RS-485requirement/requirements.md:20
Protocol/Data- Baud rate: 9600requirement/requirements.md:21
Protocol/DataField Name Type Unit Address/Source Descriptionrequirement/requirements.md:25
Protocol/Datat100_para_22105 addr_modbus number Modbus Addressrequirement/requirements.md:48
Protocol/Datat100_set_22105 addr_modbus number Modbus Address (1-247) —requirement/requirements.md:66
DeploymentValidate model IDs, identifiers, payload fields, RPC behavior, triggers, and templates against a real device or platform test asset before production deployment.requirement/requirements.md:91
Protocol/Data- No unresolved identity or protocol facts were detected from the descriptor.requirement/requirements.md:95
DeploymentEvery item above must be checked during deployment. No missing value is promoted to a confirmed fact.requirement/requirements.md:97

EdgeBus Code

The following is the current EdgeBus business code.

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)

Thing Model and RPC Code

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: t100_asset_22105

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},
    {}
))

Thing Model Field Definitions

Thing ModelFieldTypeUnitName
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 Parameter Definitions

RPCParameterTypeDescriptionDefaultUnit
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)

Trigger Definitions

No project-specific triggers are defined.

Third-Party Platform Subscription

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
  }
}

Template Selection and Verification

Search ThinkLink by project name, business code, or the id_name and ID values in the model inventory. After deployment, use real uplinks to verify fields, units, RPCs, and trigger behavior.

Supplementary Notes

Requirements and decisions: requirement/requirements.md.