Skip to content

1. Overview

The Acrel DTSD1352 is a three-phase rail-mount multifunction energy meter with RS-485 Modbus RTU output. It measures three-phase voltage, current, active/reactive/apparent power, power factor, frequency, per-phase energy, voltage/current unbalance, zero-sequence current, and DIDO status — 37 electrical parameters in total. Active energy accuracy is Class 0.5S (IEC 62053-22), meeting both trade settlement and industrial metering requirements.

This integration connects the DTSD1352 to ThinkLink via a KC11 LoRaWAN collector over RS-485. The KC11's built-in EdgeBus script handles Modbus RTU polling, data framing, and LoRaWAN uplink, delivering three-phase power data from distribution panels wirelessly to ThinkLink.

Business code: 23112. Template name: DTSD1352.


2. Features

  • Reads all 37 electrical parameters from DTSD1352 — voltage, current, power, power factor, energy, frequency, unbalance, and DIDO status
  • Connects via RS-485 / Modbus RTU
  • KC11 provides wireless LoRaWAN uplink without modifying distribution panel wiring
  • Active energy accuracy Class 0.5S (IEC 62053-22); reactive energy Class 2 — suitable for trade settlement
  • DIN-rail mounting (7-module DIN, 126×91×74 mm), fits standard distribution enclosures
  • Remote CT/PT ratio write via transparent Modbus FC=10 — supports high-voltage/high-current transformer wiring
  • Remote configuration of Modbus slave address and collection/upload period — supports multi-meter RS-485 bus
  • Class C always-on mode via KC11 (AC mains powered); minimum collection interval 1 second
  • Built-in voltage-loss/overvoltage/phase-sequence fault detection; DIDO status readable remotely

3. Use Cases

This integration is suited for any scenario that requires connecting DTSD1352 to ThinkLink or a LoRaWAN network:

  • Three-phase power monitoring in industrial plant distribution panels with zonal energy sub-metering
  • Remote meter reading in commercial building distribution rooms, replacing manual inspection
  • Enterprise energy management and power quality analysis (unbalance, power factor, zero-sequence current)
  • Multi-floor / multi-circuit metering with multiple meters on a single RS-485 bus
  • Bidirectional metering for sites with distributed solar (four-quadrant active and reactive energy)
  • Wireless retrofit of existing power meters without changing original wiring
  • MQTT data feed to PLC / SCADA / energy management systems

4. Collector Information

4.1 Hardware

ItemDetails
Collector modelKC11
Communication interfaceRS-485
Power supplyAC 85–270 V mains (DIN-rail mount)
UplinkLoRaWAN Class C (always-on)
EdgeBusSupported
ProtocolModbus RTU
Business code23112
Template nameDTSD1352

4.2 Wiring

Power and Communication

ConnectionDescription
KC11 powerAC 85–270 V, tap from the 220 V circuit inside the distribution panel
RS-485 AConnect to DTSD1352 Pin 7 (RS485 A+)
RS-485 BConnect to DTSD1352 Pin 8 (RS485 😎
TerminationKC11 has a built-in 120 Ω resistor; no external terminator needed at the bus end; do not add resistors at mid-bus nodes

Meter-side Terminals

DTSD1352 rear terminal assignments (RS-485 relevant):

DTSD1352 terminals (rear)       KC11 DTU terminals
─────────────────────────       ─────────────────────
Pin 7 (RS485 A+)         ───→   RS485 A
Pin 8 (RS485 B-)         ───→   RS485 B
Voltage inputs (L1/L2/L3/N) ─── Connect to metered circuit (meter self-powers from here)

Note: The DTSD1352 self-powers from the metered voltage (L1/N or L1/L2/L3). The KC11 uses a separate AC mains supply; the two power circuits are independent. When multiple meters share one RS-485 bus, each meter must be configured with a unique slave address (1–247) via its front-panel menu.


5. Data Collection

This integration reads the following Modbus register groups from DTSD1352 using FC=03 (Read Holding Registers):

GroupStart addressContent
G10x000AHForward active total energy (ep_fwd)
G20x0014HReverse active total energy (ep_rev)
G30x0028HForward reactive total energy (eq_fwd)
G40x0032HReverse reactive total energy (eq_rev)
G50x0061HPhase A/B/C voltage + Phase A/B/C current (6 items)
G60x0077HFrequency + Line voltage AB/CB/AC (4 items)
G70x0087HPer-phase forward active energy ep_a/ep_b/ep_c
G8a0x008FHDIDO/voltage-loss status (dido_status)
G8b0x0092HZero-sequence current + voltage unbalance + current unbalance
G90x0164HThree-phase active/reactive/apparent power + power factor (28 registers; triggers uplink)

CT/PT ratios (0x008DH/0x008EH) are written via Modbus FC=10 (write multiple registers) transparent pass-through and are not polled periodically.

5.1 Register Definitions

Energy Registers (uint32BE, × 0.01, kWh / kVarh)

FieldAddressBytesTypeCoefficientUnit
Forward active total energy0x000AH4uint32BE0.01kWh
Reverse active total energy0x0014H4uint32BE0.01kWh
Forward reactive total energy0x0028H4uint32BE0.01kVarh
Reverse reactive total energy0x0032H4uint32BE0.01kVarh
Phase A forward active energy0x0087H4uint32BE0.01kWh
Phase B forward active energy0x0089H4uint32BE0.01kWh
Phase C forward active energy0x008BH4uint32BE0.01kWh

Voltage / Current / Frequency Registers (uint16BE)

FieldAddressBytesTypeCoefficientUnit
Phase A voltage0x0061H2uint16BE0.1V
Phase B voltage0x0062H2uint16BE0.1V
Phase C voltage0x0063H2uint16BE0.1V
Phase A current0x0064H2uint16BE0.01A
Phase B current0x0065H2uint16BE0.01A
Phase C current0x0066H2uint16BE0.01A
Frequency0x0077H2uint16BE0.01Hz
Line voltage AB0x0078H2uint16BE0.1V
Line voltage CB0x0079H2uint16BE0.1V
Line voltage AC0x007AH2uint16BE0.1V

Power Registers (int32BE two's complement, × 0.001, 3 decimal places)

FieldAddressBytesTypeCoefficientUnit
Phase A active power0x0164H4int32BE0.001kW
Phase B active power0x0166H4int32BE0.001kW
Phase C active power0x0168H4int32BE0.001kW
Total active power0x016AH4int32BE0.001kW
Phase A reactive power0x016CH4int32BE0.001kVar
Phase B reactive power0x016EH4int32BE0.001kVar
Phase C reactive power0x0170H4int32BE0.001kVar
Total reactive power0x0172H4int32BE0.001kVar
Phase A apparent power0x0174H4int32BE0.001kVA
Phase B apparent power0x0176H4int32BE0.001kVA
Phase C apparent power0x0178H4int32BE0.001kVA
Total apparent power0x017AH4int32BE0.001kVA
Phase A power factor0x017CH2int16BE0.001
Phase B power factor0x017DH2int16BE0.001
Phase C power factor0x017EH2int16BE0.001
Total power factor0x017FH2int16BE0.001

Status / Diagnostic Registers

FieldAddressBytesTypeCoefficientUnit
DIDO/voltage-loss status0x008FH2uint16BE
Zero-sequence current0x0092H2uint16BE0.01A
Voltage unbalance0x0093H2uint16BE0.1%
Current unbalance0x0094H2uint16BE0.1%

Configuration Registers (R/W, written via RPC)

FieldAddressBytesTypeDescription
PT ratio (voltage transformer)0x008DH2uint16BE1–9999, default 1
CT ratio (current transformer)0x008EH2uint16BE1–9999, default 1

5.2 Status Bit Definitions (DIDO/Voltage-loss Register 0x008FH)

BitMeaning
Bit0Phase A overvoltage
Bit1Phase B overvoltage
Bit2Phase C overvoltage
Bit3Phase A voltage loss
Bit4Phase B voltage loss
Bit5Phase C voltage loss
Bit6Phase A reverse (phase-sequence fault)
Bit7Phase B reverse
Bit8Phase C reverse
Bit9DI status
Bit10DO status

6. EdgeBus Model

6.1 EB Configuration

ParameterValue
Uplink port22
Frame version tag0x87
Data type tag0x31
Upload/collection period slot (upPeriodIndex)70
Baud rate9600 bps
Data bits8
Stop bits1
ParityNone
Power modeAC mains (Battery=false, Class C)
Business code (BzType)23112
Business version (BzVersion)1
Firmware version (SwVersion)31

6.2 EB 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: "dtsd1352",
        port: 22,
        version: "0x87",
        dataType: "0x31",
        upPeriodIndex: 70,
        quInfo: [
            // G1: Forward active total energy ep_fwd (0x000A, uint32BE)
            {
                protocol: "modbus", addr: "0x01", code: "0x03", periodIndex: 70,
                indexAPP: 150, indexCMD: 0, copySize: 1,
                listVal: [
                    { start: "0x000A", end: "0x000B" }
                ]
            },
            // G2: Reverse active total energy ep_rev (0x0014, uint32BE)
            {
                protocol: "modbus", addr: "0x01", code: "0x03", periodIndex: 70,
                indexAPP: 150, indexCMD: 0, copySize: 1,
                listVal: [
                    { start: "0x0014", end: "0x0015" }
                ]
            },
            // G3: Forward reactive total energy eq_fwd (0x0028, uint32BE)
            {
                protocol: "modbus", addr: "0x01", code: "0x03", periodIndex: 70,
                indexAPP: 150, indexCMD: 0, copySize: 1,
                listVal: [
                    { start: "0x0028", end: "0x0029" }
                ]
            },
            // G4: Reverse reactive total energy eq_rev (0x0032, uint32BE)
            {
                protocol: "modbus", addr: "0x01", code: "0x03", periodIndex: 70,
                indexAPP: 150, indexCMD: 0, copySize: 1,
                listVal: [
                    { start: "0x0032", end: "0x0033" }
                ]
            },
            // G5: Three-phase voltage + current (0x0061-0x0066, 6×uint16BE)
            {
                protocol: "modbus", addr: "0x01", code: "0x03", periodIndex: 70,
                indexAPP: 150, indexCMD: 0, copySize: 1,
                listVal: [
                    { start: "0x0061", end: "0x0061" }, // ua
                    { start: "0x0062", end: "0x0062" }, // ub
                    { start: "0x0063", end: "0x0063" }, // uc
                    { start: "0x0064", end: "0x0064" }, // ia
                    { start: "0x0065", end: "0x0065" }, // ib
                    { start: "0x0066", end: "0x0066" }  // ic
                ]
            },
            // G6: Frequency + line voltages (0x0077-0x007A, 4×uint16BE)
            {
                protocol: "modbus", addr: "0x01", code: "0x03", periodIndex: 70,
                indexAPP: 150, indexCMD: 0, copySize: 1,
                listVal: [
                    { start: "0x0077", end: "0x0077" }, // freq
                    { start: "0x0078", end: "0x0078" }, // uab
                    { start: "0x0079", end: "0x0079" }, // ucb
                    { start: "0x007A", end: "0x007A" }  // uac
                ]
            },
            // G7: Per-phase energy (0x0087-0x008C, 3×uint32BE)
            {
                protocol: "modbus", addr: "0x01", code: "0x03", periodIndex: 70,
                indexAPP: 150, indexCMD: 0, copySize: 1,
                listVal: [
                    { start: "0x0087", end: "0x0088" }, // ep_a (uint32BE)
                    { start: "0x0089", end: "0x008A" }, // ep_b (uint32BE)
                    { start: "0x008B", end: "0x008C" }  // ep_c (uint32BE)
                ]
            },
            // G8a: Voltage-loss / DIDO status (0x008F, uint16BE)
            {
                protocol: "modbus", addr: "0x01", code: "0x03", periodIndex: 70,
                indexAPP: 150, indexCMD: 0, copySize: 1,
                listVal: [
                    { start: "0x008F", end: "0x008F" }  // dido_status
                ]
            },
            // G8b: Zero-sequence current + unbalance (0x0092-0x0094, 3×uint16BE)
            {
                protocol: "modbus", addr: "0x01", code: "0x03", periodIndex: 70,
                indexAPP: 150, indexCMD: 0, copySize: 1,
                listVal: [
                    { start: "0x0092", end: "0x0092" }, // i0
                    { start: "0x0093", end: "0x0093" }, // unbl_u
                    { start: "0x0094", end: "0x0094" }  // unbl_i
                ]
            },
            // G9: Power + power factor (0x0164-0x017F, 12×int32BE + 4×int16BE); triggers uplink
            {
                protocol: "modbus", addr: "0x01", code: "0x03", periodIndex: 70,
                indexAPP: 150, indexCMD: 0, copySize: 1,
                isLast: true,
                listVal: [
                    { start: "0x0164", end: "0x0165" }, // pa (int32BE)
                    { start: "0x0166", end: "0x0167" }, // pb (int32BE)
                    { start: "0x0168", end: "0x0169" }, // pc (int32BE)
                    { start: "0x016A", end: "0x016B" }, // pt (int32BE)
                    { start: "0x016C", end: "0x016D" }, // qa (int32BE)
                    { start: "0x016E", end: "0x016F" }, // qb (int32BE)
                    { start: "0x0170", end: "0x0171" }, // qc (int32BE)
                    { start: "0x0172", end: "0x0173" }, // qt (int32BE)
                    { start: "0x0174", end: "0x0175" }, // sa (int32BE)
                    { start: "0x0176", end: "0x0177" }, // sb (int32BE)
                    { start: "0x0178", end: "0x0179" }, // sc (int32BE)
                    { start: "0x017A", end: "0x017B" }, // st (int32BE)
                    { start: "0x017C", end: "0x017C" }, // pfa (int16BE)
                    { start: "0x017D", end: "0x017D" }, // pfb (int16BE)
                    { start: "0x017E", end: "0x017E" }, // pfc (int16BE)
                    { start: "0x017F", end: "0x017F" }  // pft (int16BE)
                ]
            }
        ]
    }
]

let otaConfig = getOtaConfig({
    BaudRate: 9600,
    StopBits: 1,
    DataBits: 8,
    Checkbit: CheckbitEnum.NONE,
    Battery: false,
    ConfirmDuty: 60,
    BzType: 23112,
    BzVersion: 1
})

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(__filename, otaConfig, MODBUS_TT)

6.3 Description

The EB code contains 9 Modbus query events (G1–G9). After all queries complete, G9 (isLast: true) triggers a single uplink:

  • G1–G4: Energy registers at scattered addresses — each reads 2 registers (uint32BE, 4 bytes per group)
  • G5: Phase A/B/C voltage (0x0061–0x0063) + phase A/B/C current (0x0064–0x0066), 6 uint16BE registers
  • G6: Frequency (0x0077) + line voltages AB/CB/AC (0x0078–0x007A), 4 uint16BE registers
  • G7: Per-phase forward active energy ep_a/ep_b/ep_c (0x0087/0x0089/0x008B), 2 registers each (uint32BE)
  • G8a: DIDO/voltage-loss status (0x008F), 1 uint16BE register
  • G8b: Zero-sequence current (0x0092) + voltage unbalance (0x0093) + current unbalance (0x0094), 3 uint16BE
  • G9: Power group, reads 28 consecutive registers at 0x0164–0x017F (12×int32BE + 4×int16BE), triggers uplink

Uplink frame layout (dataLen = 118 bytes):

Byte offsetContent
0Version tag (0x87)
1Data type (0x31)
2Status byte
3Reserved
4Battery voltage
5Reserved
6–9ep_fwd (uint32BE)
10–13ep_rev (uint32BE)
14–17eq_fwd (uint32BE)
18–21eq_rev (uint32BE)
22–23ua (uint16BE)
24–25ub (uint16BE)
26–27uc (uint16BE)
28–29ia (uint16BE)
30–31ib (uint16BE)
32–33ic (uint16BE)
34–35freq (uint16BE)
36–37uab (uint16BE)
38–39ucb (uint16BE)
40–41uac (uint16BE)
42–45ep_a (uint32BE)
46–49ep_b (uint32BE)
50–53ep_c (uint32BE)
54–55dido_status (uint16BE)
56–57i0 (uint16BE)
58–59unbl_u (uint16BE)
60–61unbl_i (uint16BE)
62–65pa (int32BE)
66–69pb (int32BE)
70–73pc (int32BE)
74–77pt (int32BE)
78–81qa (int32BE)
82–85qb (int32BE)
86–89qc (int32BE)
90–93qt (int32BE)
94–97sa (int32BE)
98–101sb (int32BE)
102–105sc (int32BE)
106–109st (int32BE)
110–111pfa (int16BE)
112–113pfb (int16BE)
114–115pfc (int16BE)
116–117pft (int16BE)

upPeriodIndex = periodIndex = 70: COV is disabled. The upload and collection period share one parameter slot (app_70), defaulting to 60 seconds. The Modbus slave address is injected dynamically into the query frame via app_150 (indexAPP=150, indexCMD=0, copySize=1).


7. Thing Model

7.1 Thing Model Overview

TypeNameid_name
Telemetry thing model[DTSD1352]dtsd1352_23112
Parameter thing model[DTSD1352-PARA]dtsd1352_para_23112
javascript
let frameInfo = {
    port: 22,
    dataLen: 118,
    rssi: true,
    battery: 4,
    tagList: [
        { index: 0, tag: 0x87 },
        { index: 1, tag: 0x31 }
    ]
};

7.3 Thing Model Scripts

Telemetry Thing Model (dtsd1352_23112)

javascript
import {BufferTKL, PayloadParser, Utils} from '#tklHelper';

function payload_parser({device, msg, thingModelId, noticeAttrs}) {
    let port = msg?.userdata?.port || null;
    if (port !== 22) return null;
    let frameInfo = {
        port: 22,
        dataLen: 118,
        rssi: true,
        battery: 4,
        tagList: [
            { index: 0, tag: 0x87 },
            { index: 1, tag: 0x31 }
        ]
    };
    let appInfo = [
        { name: "Forward active total energy", field_name: "ep_fwd",      unit: "kWh",   index: 6,   type: "uint32BE", coefficient: 0.01,  decimal: 2 },
        { name: "Reverse active total energy", field_name: "ep_rev",      unit: "kWh",   index: 10,  type: "uint32BE", coefficient: 0.01,  decimal: 2 },
        { name: "Forward reactive total energy", field_name: "eq_fwd",    unit: "kVarh", index: 14,  type: "uint32BE", coefficient: 0.01,  decimal: 2 },
        { name: "Reverse reactive total energy", field_name: "eq_rev",    unit: "kVarh", index: 18,  type: "uint32BE", coefficient: 0.01,  decimal: 2 },
        { name: "Phase A voltage",  field_name: "ua",          unit: "V",     index: 22,  type: "uint16BE", coefficient: 0.1,   decimal: 1 },
        { name: "Phase B voltage",  field_name: "ub",          unit: "V",     index: 24,  type: "uint16BE", coefficient: 0.1,   decimal: 1 },
        { name: "Phase C voltage",  field_name: "uc",          unit: "V",     index: 26,  type: "uint16BE", coefficient: 0.1,   decimal: 1 },
        { name: "Phase A current",  field_name: "ia",          unit: "A",     index: 28,  type: "uint16BE", coefficient: 0.01,  decimal: 2 },
        { name: "Phase B current",  field_name: "ib",          unit: "A",     index: 30,  type: "uint16BE", coefficient: 0.01,  decimal: 2 },
        { name: "Phase C current",  field_name: "ic",          unit: "A",     index: 32,  type: "uint16BE", coefficient: 0.01,  decimal: 2 },
        { name: "Frequency",        field_name: "freq",        unit: "Hz",    index: 34,  type: "uint16BE", coefficient: 0.01,  decimal: 2 },
        { name: "Line voltage AB",  field_name: "uab",         unit: "V",     index: 36,  type: "uint16BE", coefficient: 0.1,   decimal: 1 },
        { name: "Line voltage CB",  field_name: "ucb",         unit: "V",     index: 38,  type: "uint16BE", coefficient: 0.1,   decimal: 1 },
        { name: "Line voltage AC",  field_name: "uac",         unit: "V",     index: 40,  type: "uint16BE", coefficient: 0.1,   decimal: 1 },
        { name: "Phase A fwd active energy", field_name: "ep_a", unit: "kWh", index: 42,  type: "uint32BE", coefficient: 0.01,  decimal: 2 },
        { name: "Phase B fwd active energy", field_name: "ep_b", unit: "kWh", index: 46,  type: "uint32BE", coefficient: 0.01,  decimal: 2 },
        { name: "Phase C fwd active energy", field_name: "ep_c", unit: "kWh", index: 50,  type: "uint32BE", coefficient: 0.01,  decimal: 2 },
        { name: "DIDO/voltage-loss status", field_name: "dido_status", unit: "", index: 54, type: "uint16BE" },
        { name: "Zero-seq current", field_name: "i0",          unit: "A",     index: 56,  type: "uint16BE", coefficient: 0.01,  decimal: 2 },
        { name: "Voltage unbalance",field_name: "unbl_u",      unit: "%",     index: 58,  type: "uint16BE", coefficient: 0.1,   decimal: 1 },
        { name: "Current unbalance",field_name: "unbl_i",      unit: "%",     index: 60,  type: "uint16BE", coefficient: 0.1,   decimal: 1 },
        { name: "Phase A active power",   field_name: "pa",    unit: "kW",    index: 62,  type: "int32BE",  coefficient: 0.001, decimal: 3 },
        { name: "Phase B active power",   field_name: "pb",    unit: "kW",    index: 66,  type: "int32BE",  coefficient: 0.001, decimal: 3 },
        { name: "Phase C active power",   field_name: "pc",    unit: "kW",    index: 70,  type: "int32BE",  coefficient: 0.001, decimal: 3 },
        { name: "Total active power",     field_name: "pt",    unit: "kW",    index: 74,  type: "int32BE",  coefficient: 0.001, decimal: 3 },
        { name: "Phase A reactive power", field_name: "qa",    unit: "kVar",  index: 78,  type: "int32BE",  coefficient: 0.001, decimal: 3 },
        { name: "Phase B reactive power", field_name: "qb",    unit: "kVar",  index: 82,  type: "int32BE",  coefficient: 0.001, decimal: 3 },
        { name: "Phase C reactive power", field_name: "qc",    unit: "kVar",  index: 86,  type: "int32BE",  coefficient: 0.001, decimal: 3 },
        { name: "Total reactive power",   field_name: "qt",    unit: "kVar",  index: 90,  type: "int32BE",  coefficient: 0.001, decimal: 3 },
        { name: "Phase A apparent power", field_name: "sa",    unit: "kVA",   index: 94,  type: "int32BE",  coefficient: 0.001, decimal: 3 },
        { name: "Phase B apparent power", field_name: "sb",    unit: "kVA",   index: 98,  type: "int32BE",  coefficient: 0.001, decimal: 3 },
        { name: "Phase C apparent power", field_name: "sc",    unit: "kVA",   index: 102, type: "int32BE",  coefficient: 0.001, decimal: 3 },
        { name: "Total apparent power",   field_name: "st",    unit: "kVA",   index: 106, type: "int32BE",  coefficient: 0.001, decimal: 3 },
        { name: "Phase A power factor",   field_name: "pfa",   unit: "",      index: 110, type: "int16BE",  coefficient: 0.001, decimal: 3 },
        { name: "Phase B power factor",   field_name: "pfb",   unit: "",      index: 112, type: "int16BE",  coefficient: 0.001, decimal: 3 },
        { name: "Phase C power factor",   field_name: "pfc",   unit: "",      index: 114, type: "int16BE",  coefficient: 0.001, decimal: 3 },
        { name: "Total power factor",     field_name: "pft",   unit: "",      index: 116, type: "int16BE",  coefficient: 0.001, decimal: 3 },
    ];
    let payParser = new PayloadParser({ device, msg, frameInfo, appInfo });
    let tdata = payParser.telemetry();
    if ((tdata?.status & 0x02) === 0x02) {
        const status = tdata.status;
        tdata = { ...(device.telemetry_data?.[thingModelId] ?? {}) };
        tdata.status = status;
    }
    tdata._modelName = "dtsd1352_23112";
    return { telemetry_data: tdata, server_attrs: null, shared_attrs: null };
}

Parameter Thing Model (dtsd1352_para_23112)

javascript
import {BufferTKL, PayloadParser, Utils} from '#tklHelper';

function payload_parser({device, msg, thingModelId, noticeAttrs}) {
    let port = msg?.userdata?.port || null;
    const rpcName = "dtsd1352_set_23112";
    let paraDef = {
        app_20:  { name: "TimeOffset",  field_name: "TimeOffset",  unit: "",   index: 20, type: "uint32le" },
        app_38:  { name: "pwron_delay", field_name: "pwron_delay", unit: "ms", index: 38, type: "uint16le" },
        app_70:  { name: "period_up",   field_name: "period_up",   unit: "s",  type: "uint32le" },
        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, msg, paraInfo: paraDef })).paras();
    let checkData = Utils.paraCheck(rpcName, device.server_attrs, pdata);
    return {
        telemetry_data: pdata,
        server_attrs: checkData.sdata,
        shared_attrs: pdata,
        actions: checkData.actions
    };
}

8. Third-party Platform Data Subscription

8.1 MQTT Topic

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

8.2 Sample Payload

json
{
    "eui": "aabbccddeeff0011",
    "active_time": "2026-05-24T02:00:00.000Z",
    "thingModelId": "111065443795365888",
    "thingModelIdName": "dtsd1352_23112",
    "telemetry_data": {
        "snr": 9.5,
        "rssi": -65,
        "battery": 3.6,
        "status": 0,
        "ep_fwd": 12345.67,
        "ep_rev": 0.00,
        "eq_fwd": 3218.44,
        "eq_rev": 0.00,
        "ua": 220.3,
        "ub": 219.8,
        "uc": 221.1,
        "ia": 18.52,
        "ib": 17.91,
        "ic": 19.05,
        "freq": 50.01,
        "uab": 381.2,
        "ucb": 380.7,
        "uac": 381.9,
        "ep_a": 4115.22,
        "ep_b": 4112.89,
        "ep_c": 4117.56,
        "dido_status": 0,
        "i0": 0.15,
        "unbl_u": 0.3,
        "unbl_i": 1.2,
        "pa": 4.076,
        "pb": 3.941,
        "pc": 4.199,
        "pt": 12.216,
        "qa": 1.312,
        "qb": 1.288,
        "qc": 1.356,
        "qt": 3.956,
        "sa": 4.284,
        "sb": 4.146,
        "sc": 4.420,
        "st": 12.850,
        "pfa": 0.951,
        "pfb": 0.950,
        "pfc": 0.950,
        "pft": 0.951
    }
}

9. RPC

9.1 RPC Names

TypeNameid_name
Parameter set[DTSD1352 SET] 23112dtsd1352_set_23112
Parameter get[DTSD1352 GET] 23112dtsd1352_get_23112

9.2 Parameter Definitions

Slotfield_nameDescriptionUnitTypeRangeNotes
app_70period_upCollection / upload periodsuint32le1–3600Controls how often data is collected and pushed to the cloud; collection and upload share one period slot because COV is disabled
app_150addr_modbusModbus slave addressuint81–247Slave address the DTU targets when polling the meter
0x008EH (device reg)ct_ratioCT ratiouint16BE1–9999Current transformer ratio; written directly to meter via FC=10 transparent pass-through
0x008DH (device reg)vt_ratioVT ratiouint16BE1–9999Voltage transformer ratio; written directly to meter via FC=10 transparent pass-through

period_up and addr_modbus are written to DTU parameter slots via the PTL-D01 protocol (port 214) with full read-back confirmation. ct_ratio and vt_ratio are fire-and-forget writes via transparent Modbus FC=10 (port 51) directly to the meter's configuration registers.

9.3 RPC Scripts

Parameter Set RPC (dtsd1352_set_23112)

javascript
import {BufferTKL, RPCHelper, Utils} from '#tklHelper'

function rpc_script({device, params, alarms, logger}) {
    let classMode = (device?.shared_attrs?.class_mode) || "ClassC";
    const rpcName = "dtsd1352_set_23112";

    let paraDef = {
        app_20:  { name: "TimeOffset",  field_name: "TimeOffset",  unit: "",   index: 20, type: "uint32le" },
        app_38:  { name: "pwron_delay", field_name: "pwron_delay", unit: "ms", index: 38, type: "uint16le" },
        app_70:  { name: "period_up",   field_name: "period_up",   unit: "s",  type: "uint32le" },
        app_150: { name: "addr_modbus", field_name: "addr_modbus", unit: "",   type: "uint8" }
    };

    // ct_ratio/vt_ratio go via transparent Modbus — exclude from PTL-D01 state machine
    const ptlFields = ["TimeOffset", "pwron_delay", "period_up", "addr_modbus"];
    let ptlParams = {};
    ptlFields.forEach(f => { if (params[f] !== undefined) ptlParams[f] = params[f]; });

    let msgQue = [];
    if (Object.keys(ptlParams).length > 0) {
        let frames = RPCHelper.buildFrame({ paraDef, params: ptlParams });
        let dnBuffer = Buffer.alloc(frames.writeBuffer.length + frames.readBuffer.length);
        frames.writeBuffer.copy(dnBuffer, 0);
        frames.readBuffer.copy(dnBuffer, frames.writeBuffer.length);
        msgQue = Utils.makeParaSetMSG({
            device, classMode, rpcName, params: ptlParams,
            paraDownBuffer: dnBuffer.length > 0 ? dnBuffer : undefined,
        });
    }

    let modbusAddr = device?.shared_attrs?.addr_modbus ?? 1;

    if (params.ct_ratio !== undefined) {
        let ctFrames = RPCHelper.buildmodbusFrame10({
            paraDef: {
                addr: modbusAddr,
                paraList: [{ index: 0x008E, field_name: "ct_ratio", name: "CT ratio", type: "uint16BE" }]
            },
            params
        });
        if (ctFrames.writeBuffer.length > 0) {
            msgQue.push(RPCHelper.makeMSG({
                msgType: Utils.msgType.transParent,
                device,
                dnBuffer: ctFrames.writeBuffer,
            }));
        }
    }

    if (params.vt_ratio !== undefined) {
        let vtFrames = RPCHelper.buildmodbusFrame10({
            paraDef: {
                addr: modbusAddr,
                paraList: [{ index: 0x008D, field_name: "vt_ratio", name: "VT ratio", type: "uint16BE" }]
            },
            params
        });
        if (vtFrames.writeBuffer.length > 0) {
            msgQue.push(RPCHelper.makeMSG({
                msgType: Utils.msgType.transParent,
                device,
                dnBuffer: vtFrames.writeBuffer,
            }));
        }
    }

    if (msgQue.length == 0) return null;
    return msgQue;
}

Parameter Get RPC (dtsd1352_get_23112)

javascript
import {BufferTKL, RPCHelper, Utils} from '#tklHelper'

function rpc_script({device, params, alarms, logger}) {
    let classMode = (device?.shared_attrs?.class_mode) || "ClassC";
    let sleepMs = classMode === "ClassA" ? 500 : 5000;
    let paraDef = {
        app_20:  { name: "TimeOffset",  field_name: "TimeOffset",  unit: "",   index: 20, type: "uint32le" },
        app_38:  { name: "pwron_delay", field_name: "pwron_delay", unit: "ms", index: 38, type: "uint16le" },
        app_70:  { name: "period_up",   field_name: "period_up",   unit: "s",  type: "uint32le" },
        app_150: { name: "addr_modbus", field_name: "addr_modbus", unit: "",   type: "uint8" }
    };
    let frames = RPCHelper.buildFrame({ paraDef, params });
    let msg = RPCHelper.makeMSG({
        msgType: Utils.msgType.paras,
        device,
        dnBuffer: frames.readBuffer,
        sleepTime: sleepMs,
    });
    return [msg];
}

10. Template Selection

Search the ThinkLink platform template library for:

  • DTSD1352

Or search by business code:

  • 23112 / Three-phase rail-mount multifunction energy meter

11. Notes

Modbus Communication Settings

The DTSD1352 ships with default communication parameters: 9600 bps, 8 data bits, 1 stop bit, Even parity (DL/T645 mode). This integration uses Modbus RTU with no parity, so parity must be changed via the front-panel menu before connecting:

Menu path: BUS → Parity → None

Restart the meter after changing the setting. Configure KC11 to match: 9600 bps, 8N1.

Writing CT/PT Ratios

CT and PT ratios are written to the meter via the SET RPC (dtsd1352_set_23112):

  • ct_ratio → meter register 0x008EH (Modbus FC=10)
  • vt_ratio → meter register 0x008DH (Modbus FC=10)

These are fire-and-forget writes — the platform does not read back the values to confirm. After writing, it is recommended to run the GET RPC (dtsd1352_get_23112) to verify that period_up / addr_modbus are correct, and to use the ThinkLink transparent pass-through RPC (Modbus debug) to read back registers 0x008D/0x008EH and confirm the ratios took effect.

Multi-meter Bus Wiring

Multiple DTSD1352 meters can share one RS-485 bus, each assigned a unique Modbus address (1–247). Configure addr_modbus independently for each device on the ThinkLink platform. The KC11 injects the slave address dynamically into each query frame (indexAPP=150). A 120 Ω termination resistor is required at the far end of the bus; KC11 has one built-in.

Modbus Communication Examples

Read Phase A voltage (FC=03):

Request:  01 03 00 61 00 01 D4 34
Response: 01 03 02 08 9A C0 2A
          → 0x089A = 2202 → 2202 × 0.1 = 220.2 V

Read total active power (4-byte int32BE, FC=03):

Request:  01 03 01 6A 00 02 A3 E7
Response: 01 03 04 00 00 04 B0 [CRC]
          → 0x000004B0 = 1200 → 1200 × 0.001 = 1.200 kW