Skip to content

SWMx DIDO 控制器接入 ThinkLink 对接文档

1. 传感器简介

SWMx 是竞速电子 JSDZ 的 DIDO 控制器,主要用于数字量输入状态采集和继电器输出控制。

本方案中,SWMx 通过 RS-485 接入 KC11 采集器,由 KC11 内置 EdgeBus 执行 Modbus RTU 采集逻辑,并通过 LoRaWAN 将数据上传到 ThinkLink 平台。

设备基本信息如下:

项目内容
设备型号SWMx
厂商竞速电子
英文名JSDZ
设备类型DIDO 控制器
业务代码22108
ThinkLink 模板SWMx-22108

2. 产品特点

SWMx DIDO 控制器具备以下特点:

  1. 支持数字量输入状态采集
  2. 支持继电器输出控制
  3. 支持 RS-485 / Modbus RTU 通讯
  4. 可通过 KC11 + EdgeBus 接入 LoRaWAN 网络
  5. 支持通过 ThinkLink 进行状态解析、数据展示和远程控制
  6. 支持上报 RY / DI 的完整状态字节
  7. 支持将 RY、DI 前 5 路状态解析为独立字段
  8. 支持通过 RPC 修改采集周期、上传周期和 Modbus 地址
  9. 支持通过 RPC 控制指定继电器开关状态

3. 适用范围

本方案适用于以下场景:

  • 远程继电器控制
  • 数字量输入状态监测
  • 设备运行状态采集
  • 门禁、门磁、开关量状态监测
  • 工业现场 DIDO 控制器无线化改造
  • 传统 RS-485 控制器接入 LoRaWAN / ThinkLink 平台
  • 需要通过 MQTT / RPC 实现第三方平台远程控制的场景

4. 采集器信息

4.1 硬件信息

本方案采用 KC11 作为采集器。

项目内容
采集器型号KC11
采集接口RS-485
通讯协议Modbus RTU
上行方式LoRaWAN
供电方式220V / 12V 供电
边缘计算能力支持 EdgeBus
平台接入ThinkLink

KC11 负责通过 RS-485 读取 SWMx 的 Modbus 数据,并将采集结果通过 LoRaWAN 上传到网关,最终进入 ThinkLink 平台。


4.2 接线信息

电源与通讯接口

接线项说明
KC11 电源220V / 12V 供电
SWMx 电源按 SWMx 控制器实际供电要求接入
RS-485 AKC11 RS-485 A 接 SWMx A
RS-485 BKC11 RS-485 B 接 SWMx B
GND如现场需要,可连接通讯参考地

传感器接口

SWMx 本身为 DIDO 控制器,传感器或现场设备接入 SWMx 的 DI 输入端,控制对象接入 SWMx 的 RY / 继电器输出端。

本方案中,KC11 不直接接入 DI 或继电器负载,而是通过 RS-485 读取和控制 SWMx。


5. 数据采集

本方案中,通过 Modbus 读取以下数据:

数据类型Modbus 功能码说明
RY 状态0x01读取线圈状态
DI 状态0x02读取离散输入状态

本次采集使用的是 Modbus 01 和 02 功能码

  • 0x01:读取 RY / Coil 状态
  • 0x02:读取 DI / Discrete Input 状态

需要注意的是,01 和 02 功能码本身是按 bit 读取,而不是像 03 / 04 那样直接按寄存器读取。但是在本方案中,EBHelper 已经在底层完成了处理,会自动将 bit 数据组合为字节。

因此,EB 代码中配置:

typescript
{ start: "0", end: "15", covType: "HEX" }

表示读取 0 到 15 共 16 个 bit,底层会自动组合为 2 字节数据上传。


5.1 寄存器定义

RY 状态采集

项目内容
协议Modbus RTU
功能码0x01
读取对象Coil / 继电器状态
起始地址0
结束地址15
数据长度16 bit,底层组合为 2 字节
上传格式HEX
变化触发支持,covType: "HEX"

对应 EB 配置:

typescript
{
  protocol: "modbus",
  code: "0x01",
  periodIndex: 74,
  indexAPP: 150,
  indexCMD: 0,
  copySize: 1,
  isLast: false,
  listVal: [
    { start: "0", end: "15", covType: "HEX" }
  ]
}

DI 状态采集

项目内容
协议Modbus RTU
功能码0x02
读取对象Discrete Input / 数字量输入状态
起始地址0
结束地址15
数据长度16 bit,底层组合为 2 字节
上传格式HEX
变化触发支持,covType: "HEX"

对应 EB 配置:

typescript
{
  protocol: "modbus",
  code: "0x02",
  periodIndex: 74,
  indexAPP: 150,
  indexCMD: 0,
  copySize: 1,
  isLast: false,
  listVal: [
    { start: "0", end: "15", covType: "HEX" }
  ]
}

5.2 状态位定义

本方案中,物模型同时解析完整状态字和前 5 路通道状态。

RY 状态位定义

字段名称field_name数据位置类型说明
RYry_status_hexindex 6hexbe2RY 2 字节状态值,HEX 字符串
RY1ry1index 6 bit0bitLE0-0第 1 路继电器状态
RY2ry2index 6 bit1bitLE1-1第 2 路继电器状态
RY3ry3index 6 bit2bitLE2-2第 3 路继电器状态
RY4ry4index 6 bit3bitLE3-3第 4 路继电器状态
RY5ry5index 6 bit4bitLE4-4第 5 路继电器状态

DI 状态位定义

字段名称field_name数据位置类型说明
DIdi_status_hexindex 8hexbe2DI 2 字节状态值,HEX 字符串
DI1di1index 8 bit0bitLE0-0第 1 路 DI 状态
DI2di2index 8 bit1bitLE1-1第 2 路 DI 状态
DI3di3index 8 bit2bitLE2-2第 3 路 DI 状态
DI4di4index 8 bit3bitLE3-3第 4 路 DI 状态
DI5di5index 8 bit4bitLE4-4第 5 路 DI 状态

6. EdgeBus 模型

本方案中,SWMx 是 RS-485 / Modbus RTU 设备,不是原生 LoRaWAN 设备,因此需要通过 KC11 内置 EdgeBus 执行采集逻辑。

EdgeBus 负责:

  1. 定时读取 SWMx 的 RY 状态
  2. 定时读取 SWMx 的 DI 状态
  3. 使用 Modbus 01 / 02 功能码完成 bit 状态读取
  4. 将读取结果封装为 LoRaWAN 上行数据
  5. 支持变化上传逻辑
  6. 支持通过平台 RPC 修改参数
  7. 支持通过平台 RPC 控制继电器输出

6.1 EB 配置参数

参数说明
port22
dataType0x86
version0x08
upPeriodIndex70
readPeriodIndex74
Modbus 地址参数 index150
Modbus 波特率9600
数据位8
停止位1
校验位NONE
业务类型22108
业务版本13
EB 软件版本31
是否电池设备false

参数说明:

参数 index字段说明
70period_up上传周期,单位秒
74period_read采集周期,单位秒
150addr_modbusSWMx 的 Modbus 地址

6.2 EB 代码

typescript
import { Buffer } from "buffer";
import { buildOtaFile } from "@EBSDK/run";
import {
  ActionAfertExpr, CalcData,
  CrcMode,
  CvtRule,
  EBBuffer,
  EBModel,
  ExprCondition,
  LoraUpEvent,
  QueryEvent, SetUpCovDataType, QuItemBase,
  UserConfUPItem, EventInfoItem, QuItemModBus, UserConfQueryItem, EventConfig, Utils
} from "@EBSDK/EBCompiler/all_variable";
import { CheckbitEnum, getOtaConfig, HwTypeEnum, UpgrdTypeEnum } from "@EBSDK/otaConfig";

////////////////////////////////////////////////////////////////////////////////////////
// 01、02 是按 bit 读取的,EBHelper 会自动组合成字节。
// start: "0", end: "15" 实际读取 16 bit,上传时为 2 字节。
// covType 如果是 HEX,会将采集到的数据与上次数据做对比,如果有差异则触发上传。

const eventInfo: UserConfUPItem[] = [
  {
    name: "swm",
    dataType: "0x86",
    port: 22,
    version: "0x08",
    upPeriodIndex: 70,
    quInfo: [
      {
        protocol: "modbus",
        code: "0x01",
        periodIndex: 74,
        indexAPP: 150,
        indexCMD: 0,
        copySize: 1,
        isLast: false,
        listVal: [
          { start: "0", end: "15", covType: "HEX" }
        ]
      },
      {
        protocol: "modbus",
        code: "0x02",
        periodIndex: 74,
        indexAPP: 150,
        indexCMD: 0,
        copySize: 1,
        isLast: false,
        listVal: [
          { start: "0", end: "15", covType: "HEX" }
        ]
      }
    ]
  }
];

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

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.3 说明

当前 EB 逻辑说明:

  1. KC11 通过 RS-485 连接 SWMx。
  2. EdgeBus 按照配置周期读取 SWMx 数据。
  3. 读取 RY 状态时使用 Modbus 0x01 功能码。
  4. 读取 DI 状态时使用 Modbus 0x02 功能码。
  5. start: "0", end: "15" 表示读取 16 个 bit。
  6. EBHelper 底层会自动将 16 bit 结果组合为 2 字节。
  7. 物模型中将 RY 状态作为 ry_status_hex 上传。
  8. 物模型中将 DI 状态作为 di_status_hex 上传。
  9. 同时解析 RY1~RY5、DI1~DI5 作为独立状态字段。
  10. covType 设置为 HEX 时,采集结果会与上一次数据对比,数据发生变化时触发上传。
  11. RPC 可用于配置采集周期、上传周期和 Modbus 地址。
  12. RPC 可用于控制指定继电器开关。

7. 物模型

7.1 物模型基本信息

项目内容
物模型名称SWMx-22108
idNameswm_22108
业务代码22108
数据上报端口22
参数端口214
透传端口51
数据长度9
是否解析 RSSI
电池字段 index4
数据标识0x86
版本标识0x08

7.2 上行帧结构

本方案中,SWMx 数据通过 LoRaWAN port 22 上报。

javascript
let frameInfo = {
    port: 22,
    dataLen: 9,
    rssi: true,
    battery: 4,
    tagList: [
        { index: 0, tag: 0x86 },
        { index: 1, tag: 0x08 }
    ]
};

上行数据结构说明:

index内容说明
00x86数据类型
10x08协议版本
4battery电池或供电状态字段
6~7RY 状态2 字节 HEX
8~9DI 状态2 字节 HEX

注:根据提供的物模型脚本,dataLen 配置为 9,RY 从 index 6 解析 2 字节,DI 从 index 8 解析 2 字节。实际部署时建议结合真实上行 payload 再确认帧长度是否需要调整。


7.3 物模型脚本

javascript
let port = msg?.userdata?.port || null;

if (port === 51) {
    let retval = Utils.paraCheck(Utils.msgType.transParent, device.server_attrs, { device: device, msg: msg });
    return {
        telemetry_data: retval.tdata,
        server_attrs: retval.sdata,
        shared_attrs: retval.pdata,
        actions: retval.actions
    };
}

if (port !== 22) return null;

let frameInfo = {
    port: 22,
    dataLen: 9,
    rssi: true,
    battery: 4,
    tagList: [
        { index: 0, tag: 0x86 },
        { index: 1, tag: 0x08 }
    ]
};

let appInfo = [
    { name: "RY", field_name: "ry_status_hex", unit: "", index: 6, type: "hexbe2" },
    { name: "DI", field_name: "di_status_hex", unit: "", index: 8, type: "hexbe2" },

    { name: "RY1", field_name: "ry1", unit: "", index: 6, type: "bitLE0-0" },
    { name: "RY2", field_name: "ry2", unit: "", index: 6, type: "bitLE1-1" },
    { name: "RY3", field_name: "ry3", unit: "", index: 6, type: "bitLE2-2" },
    { name: "RY4", field_name: "ry4", unit: "", index: 6, type: "bitLE3-3" },
    { name: "RY5", field_name: "ry5", unit: "", index: 6, type: "bitLE4-4" },

    { name: "DI1", field_name: "di1", unit: "", index: 8, type: "bitLE0-0" },
    { name: "DI2", field_name: "di2", unit: "", index: 8, type: "bitLE1-1" },
    { name: "DI3", field_name: "di3", unit: "", index: 8, type: "bitLE2-2" },
    { name: "DI4", field_name: "di4", unit: "", index: 8, type: "bitLE3-3" },
    { name: "DI5", field_name: "di5", unit: "", index: 8, type: "bitLE4-4" }
];

let payParser = new PayloadParser({
    device: device,
    msg: msg,
    frameInfo: frameInfo,
    appInfo: appInfo,
});

let tdata = payParser.telemetry();

if ((tdata?.status & 0x02) === 0x02) {
    // time out,只更新 status
    const status = tdata.status;
    tdata = { ...(device.telemetry_data?.[thingModelId] ?? {}) };
    tdata.status = status;
}

return {
    telemetry_data: tdata,
    server_attrs: null,
    shared_attrs: null
};

8. 第三方平台数据订阅

8.1 MQTT Topic

第三方平台可通过以下 Topic 订阅 ThinkLink 上报数据:

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

其中:

参数说明
Organization AccountThinkLink 组织账号
euiLoRaWAN 设备 EUI

8.2 上报示例数据

json
{
    "eui": "6353012af10a9331",
    "active_time": "2026-02-05T08:35:48.000Z",
    "thingModelId": "88304368717139973",
    "thingModelIdName": "swm_22108",
    "telemetry_data": {
        "snr": 13.5,
        "rssi": -51,
        "battery": 3.37,
        "ry_status_hex": "001F",
        "di_status_hex": "000B",
        "ry1": 1,
        "ry2": 1,
        "ry3": 1,
        "ry4": 1,
        "ry5": 1,
        "di1": 1,
        "di2": 1,
        "di3": 0,
        "di4": 1,
        "di5": 0,
        "status": 0
    }
}

字段说明:

字段说明
ry_status_hexRY 状态 2 字节 HEX 字符串
di_status_hexDI 状态 2 字节 HEX 字符串
ry1~ry5第 1~5 路继电器状态
di1~di5第 1~5 路 DI 输入状态
battery电池或供电状态字段
rssiLoRaWAN RSSI
snrLoRaWAN SNR
status数据状态

9. RPC

本方案支持以下 RPC:

  1. 配置参数
  2. 读取参数
  3. 继电器控制

9.1 RPC 名称

RPC 功能RPC 名称
配置参数swm_set_22108
读取参数swm_get_22108
继电器控制swm_action_22108

9.2 参数定义

配置参数定义

javascript
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_150: {
        name: "addr_modbus",
        field_name: "addr_modbus",
        unit: "",
        type: "uint8"
    }
};

参数表:

参数field_name类型单位说明
app_70period_upuint32LEs上传周期
app_74period_readuint32LEs采集周期
app_150addr_modbusuint8-SWMx Modbus 地址

继电器控制参数

每一路继电器对应一个独立的布尔参数,可在一次调用中控制任意一路或多路。表单打开时各字段会从设备共享属性(ry1~ry5)自动回填上次的设定状态。

参数类型说明
switch_ry1boolean第 1 路继电器:true 闭合 / 打开,false 断开 / 关闭
switch_ry2boolean第 2 路继电器
switch_ry3boolean第 3 路继电器
switch_ry4boolean第 4 路继电器
switch_ry5boolean第 5 路继电器

仅传入的字段会下发控制(对应一帧 Modbus FC05,线圈地址 = 路号 − 1);未传入的路不动作。设置成功后,对应路状态会同步写回遥测 ryN 与共享属性 ryN

示例(控制第 1 路闭合、第 3 路断开):

json
{
    "switch_ry1": true,
    "switch_ry3": false
}

9.3 RPC 代码

9.3.1 配置参数 RPC

javascript
let classMode = (device && device.shared_attrs && device.shared_attrs.class_mode) || "ClassA";
let intervalms = classMode === "ClassA" ? 0 : 2000;

const rpcName = "swm_set_22108";

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_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;

9.3.2 读取参数 RPC

javascript
let classMode = (device && device.shared_attrs && device.shared_attrs.class_mode) || "ClassA";
let sleepMs = classMode === "ClassA" ? 500 : 5000;

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_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: sleepMs,
});

return [msg];

9.3.3 继电器控制 RPC

表单提供 switch_ry1~switch_ry5 五个布尔参数,每个对应一路继电器。脚本遍历本次传入的字段,每路下发一帧 Modbus 05 功能码(线圈地址 = 路号 − 1),多帧拼接后一次性下行;通过设备回显逐帧校验线圈值,成功后把每路状态同步写回遥测 ryN 与共享属性 ryN(用于下次打开表单自动回填)。

javascript
let classMode = (device?.shared_attrs?.class_mode) || "ClassA";
let addr_modbus = device.shared_attrs?.addr_modbus ?? 1;
let rpcName = "swm_action_22108";

// 5 路继电器:表单字段 switch_ry1..switch_ry5,各自从 shared_attrs ry1..ry5 回填。
// 一次提交可含任意子集;每个出现的字段发一帧 Modbus FC05(线圈地址 = 路号-1)。
let frames = [];
let appInfo = [];
let telemetry_data = {};
let shared_attrs = {};
for (let n = 1; n <= 5; n++) {
    let key = "switch_ry" + n;
    if (params[key] === undefined) continue;
    let on = params[key] ? 1 : 0;
    let checkVal = params[key] ? 0xFF00 : 0x0000;
    frames.push(RPCHelper.modbusAction(addr_modbus, 5, n - 1, checkVal));
    let tdataKey = "ry" + n;
    // 每帧回显 8 字节 FC05: [addr][05][coilH][coilL][valH][valL][crcL][crcH]
    // 回显线圈值 = uint16BE @ (帧首+4);帧首+6 是 CRC,切勿用作校验。
    appInfo.push({field_name: tdataKey, index: appInfo.length * 8 + 4, type: "uint16BE", val: checkVal});
    telemetry_data[tdataKey] = on;
    shared_attrs[tdataKey] = on;
}
if (frames.length === 0) return null;

let dnBuffer = Buffer.alloc(frames.length * 8);
frames.forEach((f, i) => f.copy(dnBuffer, i * 8));

let checkInfo = {
    frameInfo: {port: 51, dataLen: dnBuffer.length},
    appInfo: appInfo,
    telemetry_data: telemetry_data,
    server_attrs: null,
    shared_attrs: shared_attrs
};
let msgQue = Utils.makeParaSetMSG({
    msgType: Utils.msgType.transParent,
    checkInfo: checkInfo,
    device: device,
    classMode: classMode,
    rpcName: rpcName,
    params: params,
    paraDownBuffer: dnBuffer,
    timeout: 3000,
    maxRetries: 3
});
if (msgQue.length === 0) return null;
return msgQue;

控制逻辑说明:

项目说明
Modbus 功能码05(每路一帧)
控制对象任意一路或多路继电器(一次调用)
打开值0xFF00
关闭值0x0000
表单字段switch_ry1 ~ switch_ry5(布尔)
线圈地址路号 − 1(switch_ry1→0 … switch_ry5→4)
成功回写遥测 ryN + 共享属性 ryN(用于表单回填)

10. 模板选择

在 ThinkLink 平台中搜索模板:

latex
SWMx-22108

或按业务类型查找:

latex
22108

模板建议信息:

项目内容
模板名称SWMx-22108
设备类型DIDO 控制器
厂商竞速电子 / JSDZ
业务代码22108
接入方式KC11 + RS-485 + EdgeBus + LoRaWAN
数据上报RY 状态、DI 状态
控制能力支持继电器 RPC 控制
参数配置支持上传周期、采集周期、Modbus 地址配置

11. 对接总结

本方案通过 KC11 + EdgeBus + LoRaWAN + ThinkLink 将 SWMx DIDO 控制器接入物联网平台。

核心特点是:

  1. 使用 KC11 通过 RS-485 连接 SWMx。
  2. EdgeBus 使用 Modbus 01 读取 RY 状态。
  3. EdgeBus 使用 Modbus 02 读取 DI 状态。
  4. 01/02 功能码按 bit 读取,EBHelper 已在底层完成与 03/04 的差异处理。
  5. RY、DI 状态分别以 2 字节 HEX 字符串上传。
  6. 平台侧同时解析 RY1~RY5、DI1~DI5 状态。
  7. 支持 RPC 修改上传周期、采集周期和 Modbus 地址。
  8. 支持 RPC 通过 Modbus 05 功能码控制任意一路或多路继电器(switch_ry1~switch_ry5),并将状态同步回写共享属性供表单回填。

该方案适合将传统 RS-485 DIDO 控制器快速改造成 LoRaWAN 无线设备,并接入 ThinkLink 实现远程监测、远程控制和第三方平台数据订阅。