KS31 4-20mA 模拟量采集器通过LoRaWAN 接入ThinkLink

1. 传感器简介

KS31 是一款基于 LoRaWAN 通信的 AI 采集器 ,支持 4-20mA0-10V 两种模拟量接口,可用于对接支持上述接口的传统传感器或仪表,实现无线采集与平台接入。

设备内置 4 节 2700mAh 锂亚电池,总电量约 10800mAh,具备低功耗、远距离通信能力,适合智慧工业、智慧楼宇等场景。

2. 产品特点

  • 原生 LoRaWAN 通信,无需额外采集设备
  • 支持 4 路 AI 接口
  • 支持 4-20mA / 0-10V 两种接口型号
  • 电池供电,续航可达 6 年以上(15分钟上报周期条件下)
  • 支持 COV 变化上报
  • 支持通过 NS 下发参数进行远程配置
  • 防护等级 IP65
  • 支持 CN470 / EU433 / EU868 / AS923 / AU915 / US902 等区域标准

3. 适用范围

适用于下列模拟量传感器/设备的无线采集接入:

  • 工业现场 4-20mA 变送器
  • 0-10V 输出类传感器
  • 楼宇自动化模拟量设备
  • 智慧工业、智慧楼宇环境监测场景
  • 需要将传统模拟量设备快速接入 LoRaWAN 平台的项目

4. 采集器信息

4.1 硬件信息

由于 KS31 本身即为 原生 LoRaWAN AI 采集器 ,因此本项目中 无需额外采集器

KS31 自带 LoRaWAN 通信能力与 AI 接口采集能力。

4.2 接线信息

电源与通讯接口
  • 通信方式:LoRaWAN
  • 入网方式:OTAA / ABP,Class A
  • 天线接口:LoRa 天线接口
  • 接线口:PG7 防水接头
  • 供电方式:4 节 2700mAh ER14505 锂亚电池
  • 接线数量:5 根(含 1 根 GND)
传感器接口
  • 支持 4 路 AI 接口
  • 接口类型:
    • 4-20mA
    • 0-10V
  • 型号对应不同接口版本

5. 数据采集

本方案中,KS31 为原生 LoRaWAN 设备,不通过 Modbus 采集寄存器

设备采用 LoRaWAN 应用层协议直接上报采样值、电池电压及状态信息。

5.1 寄存器定义

本设备 无 Modbus 寄存器定义

可关注的 LoRaWAN 上行数据字段如下:

0-10V 接口数据格式
  • 端口号:11
  • 版本号:0x82
  • 控制字:0x25
  • 标识符:0x17
  • 数据内容:
    • 设备状态
    • 通道1采样值
    • 通道2采样值
    • 通道3采样值
    • 通道4采样值
    • 电池电压
4-20mA 接口数据格式
  • 端口号:11
  • 版本号:0x82
  • 控制字:0x25
  • 标识符:0x18
  • 数据内容:
    • 设备状态
    • 通道1采样值
    • 通道2采样值
    • 通道3采样值
    • 通道4采样值
    • 电池电压

5.2 状态位定义

  • 设备状态字段:
    • 0x00:正常
    • 0x01:通信故障

6. EdgeBus 模型

KS31 本身是 LoRaWAN 设备,因此不需要 EB 代码。

文档中仅给出说明即可。

6.1 EB 配置参数

不需要。

6.2 EB 代码

不需要。

6.3 说明

当前 EB 逻辑说明:

  • KS31 为原生 LoRaWAN AI 采集器
  • 设备直接完成模拟量采集与 LoRaWAN 上报
  • 不需要通过 KC 系列采集器或 EdgeBus 做协议转换
  • 平台侧主要完成物模型解析、参数读写与 RPC 配置即可

7. 物模型

7.1 物模型基本信息

数据物模型
  • 名称:MT-KS31
  • id Name:mt_ks31
参数物模型
  • 名称:MT KS31 PARA
  • id Name:mt_ks31_para

7.2 上行帧结构

  • 端口号为 11 ,并按固定长度 21 字节解析,且 type 字段映射为:
    • "17" -> "4-20mA"
    • "18" -> "0-10V"
上行帧结构
  • Port:11
  • 数据长度:21 字节
  • Tag:
    • Byte0 = 0x82
    • Byte1 = 0x25
  • 解析字段:
    • Byte2:type
    • Byte3:fault
    • Byte4~7:chan1(uint32LE)
    • Byte8~11:chan2(uint32LE)
    • Byte12~15:chan3(uint32LE)
    • Byte16~19:chan4(uint32LE)
    • Byte20:battery/vbat(uint8)
换算逻辑
  • type = 4-20mA,则 powerVal = 10
  • 否则 powerVal = 11
  • 通道换算:
    • chanX = 原始值 * powerVal / 64000
    • 再结合 baseXratioX 做修正
  • 电池换算:
    • battery = (原始值 * 1.6 / 254) + 2.0

7.3 物模型脚本

复制代码
if (!noticeAttrs.telemetry_data) return null
let port = msg?.userdata?.port || null;
if (port !== 11) return null

let frameInfo = {
    port: 11, dataLen: 21, rssi: true,
    tagList: [
        { index: 0, tag: 0x82 },
        { index: 1, tag: 0x25 }
    ]
}

let appInfo = [
    { name: "type", field_name: "type", unit: "", index: 2, type: "hexBE1", options: { "17": "4-20mA", "18": "0-10V" } },
    { name: "fault", field_name: "fault", unit: "", index: 3, type: "uint8" },
    { name: "chan1", field_name: "chan1", unit: "v", index: 4, type: "uint32LE" },
    { name: "chan2", field_name: "chan2", unit: "v", index: 8, type: "uint32LE" },
    { name: "chan3", field_name: "chan3", unit: "v", index: 12, type: "uint32LE" },
    { name: "chan4", field_name: "chan4", unit: "v", index: 16, type: "uint32LE" },
    { name: "vbat", field_name: "vbat", unit: "v", index: 20, type: "uint8", decimal: 2 }
]

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

let tdata = payParser.telemetry()
if (tdata == undefined) return null

let base1 = device?.server_attrs?.base1 ?? 0
let base2 = device?.server_attrs?.base2 ?? 0
let base3 = device?.server_attrs?.base3 ?? 0
let base4 = device?.server_attrs?.base4 ?? 0
let ratio1 = device?.server_attrs?.ratio1 ?? 1
let ratio2 = device?.server_attrs?.ratio2 ?? 1
let ratio3 = device?.server_attrs?.ratio3 ?? 1
let ratio4 = device?.server_attrs?.ratio4 ?? 1

let powerVal = 11
if (tdata.type === "4-20mA") { powerVal = 10 }

let chan1 = tdata.chan1 * powerVal / 64000
let chan2 = tdata.chan2 * powerVal / 64000
let chan3 = tdata.chan3 * powerVal / 64000
let chan4 = tdata.chan4 * powerVal / 64000

tdata.chan1 = Number(((chan1 - base1) * ratio1).toFixed(3))
tdata.chan2 = Number(((chan2 - base2) * ratio2).toFixed(3))
tdata.chan3 = Number(((chan3 - base3) * ratio3).toFixed(3))
tdata.chan4 = Number(((chan4 - base4) * ratio4).toFixed(3))

tdata.battery = Number(((tdata.battery * 1.6) / 254 + 2.0).toFixed(2))

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

8. 第三方平台数据订阅

8.1 MQTT Topic

复制代码
/v32/{Organization Account}/tkl/up/telemetry/{eui}

8.2 上报示例数据

以下示例根据 KS31 的物模型字段进行了对应调整:

复制代码
{
    "eui": "ks31_demo_eui",
    "active_time": "2026-04-12T08:00:00.000Z",
    "thingModelId": "mt_ks31_demo",
    "thingModelIdName": "mt_ks31",
    "telemetry_data": {
        "type": "4-20mA",
        "fault": 0,
        "chan1": 12.345,
        "chan2": 8.765,
        "chan3": 15.120,
        "chan4": 6.540,
        "battery": 3.18
    }
}

说明:

  • type:接口类型
  • fault:设备状态
  • chan1~chan4:4 路模拟量换算后的值
  • battery:电池电压值

9. RPC

9.1 RPC 名称

根据提供资料,KS31 的 RPC 包括:

  1. [MT KS31 SET] para
    id Name:mt_ks31_set_para
  2. [MT KS31 SET] ratio
    id Name:mt_ks31_set_ratio
  3. [MT KS31 GET] para
    id Name:mt_ks31_get_para

9.2 参数定义

常规参数
  • pwron_delay:上电延时,单位 ms,对应地址 38
  • period_up:上报周期,单位 s,对应地址 40
  • cov_enable1~4:4 路通道 COV 使能位,对应地址 150 的 bit0~bit3
  • measurePeriod:采样周期,单位 s,对应地址 152
  • covChan1~4:4 路通道 COV 阈值,对应地址 154 / 158 / 162 / 166
ratio 修正参数
  • base1~base4
  • ratio1~ratio4
    用于平台侧对 4 路通道值做基准偏移与比例修正。

9.3 RPC 代码

配置参数 RPC
复制代码
let classMode = (device && device.shared_attrs && device.shared_attrs.class_mode) || "ClassA";
const rpcName = "mt_ks31_set_para"

let paraDef = {
    app_38: { name: "pwron_delay", field_name: "pwron_delay", unit: "ms", index: 38, type: "uint16le" },
    app_40: { name: "period_up", field_name: "period_up", unit: "s", type: "uint32LE" },
    app_150_1: { name: "cov_enable1", field_name: "cov_enable1", unit: "", type: "bitLE0-0" },
    app_150_2: { name: "cov_enable2", field_name: "cov_enable2", unit: "", type: "bitLE1-1" },
    app_150_3: { name: "cov_enable3", field_name: "cov_enable3", unit: "", type: "bitLE2-2" },
    app_150_4: { name: "cov_enable4", field_name: "cov_enable4", unit: "", type: "bitLE3-3" },
    app_152: { name: "measurePeriod", field_name: "measurePeriod", unit: "s", type: "uint16LE" },
    app_154: { name: "covChan1", field_name: "covChan1", unit: "", type: "floatLE", coefficient: 0.00015625, decimal: 3 },
    app_158: { name: "covChan2", field_name: "covChan2", unit: "", type: "floatLE", coefficient: 0.00015625, decimal: 3 },
    app_162: { name: "covChan3", field_name: "covChan3", unit: "", type: "floatLE", coefficient: 0.00015625, decimal: 3 },
    app_166: { name: "covChan4", field_name: "covChan4", unit: "", type: "floatLE", coefficient: 0.00015625, decimal: 3 }
};

let paraInput = { ...params }
if (params?.cov_enable1) { paraInput.cov_enable1 = 1 } else { paraInput.cov_enable1 = 0 }
if (params?.cov_enable2) { paraInput.cov_enable2 = 1 } else { paraInput.cov_enable2 = 0 }
if (params?.cov_enable3) { paraInput.cov_enable3 = 1 } else { paraInput.cov_enable3 = 0 }
if (params?.cov_enable4) { paraInput.cov_enable4 = 1 } else { paraInput.cov_enable4 = 0 }

let frames = RPCHelper.buildFrame({ paraDef: paraDef, params: paraInput });
let dnBuffer = Buffer.alloc(frames.writeBuffer.length + frames.readBuffer.length);
frames.writeBuffer.copy(dnBuffer, 0);
frames.readBuffer.copy(dnBuffer, frames.writeBuffer.length);

let msgQue = Utils.makeParaSetMSG({
    device: device,
    classMode: classMode,
    rpcName: rpcName,
    params: params,
    paraDownBuffer: dnBuffer,
})

if (msgQue.length == 0) return null
return msgQue
配置 ratio 参数 RPC
复制代码
logger.info("set ratio para");
return [{
    sleepTimeMs: 0,
    type: "modifyAttrs",
    dnMsg: {
        server_attrs: params
    }
}]
读取参数 RPC
复制代码
let paraDef = {
    app_38: { name: "pwron_delay", field_name: "pwron_delay", unit: "ms", index: 38, type: "uint16le" },
    app_40: { name: "period_up", field_name: "period_up", unit: "s", type: "uint32LE" },
    app_150_1: { name: "cov_enable1", field_name: "cov_enable1", unit: "", type: "bitLE0-0" },
    app_150_2: { name: "cov_enable2", field_name: "cov_enable2", unit: "", type: "bitLE1-1" },
    app_150_3: { name: "cov_enable3", field_name: "cov_enable3", unit: "", type: "bitLE2-2" },
    app_150_4: { name: "cov_enable4", field_name: "cov_enable4", unit: "", type: "bitLE3-3" },
    app_152: { name: "measurePeriod", field_name: "measurePeriod", unit: "s", type: "uint16LE" },
    app_154: { name: "covChan1", field_name: "covChan1", unit: "", type: "floatLE", coefficient: 0.00015625, decimal: 3 },
    app_158: { name: "covChan2", field_name: "covChan2", unit: "", type: "floatLE", coefficient: 0.00015625, decimal: 3 },
    app_162: { name: "covChan3", field_name: "covChan3", unit: "", type: "floatLE", coefficient: 0.00015625, decimal: 3 },
    app_166: { name: "covChan4", field_name: "covChan4", unit: "", type: "floatLE", coefficient: 0.00015625, decimal: 3 }
}
let frames = RPCHelper.buildFrame({ paraDef: paraDef });
let msg = RPCHelper.makeMSG({
    msgType: Utils.msgType.paras,
    device: device,
    dnBuffer: frames.readBuffer,
    sleepTime: 0,
});
return [msg];

10. 模板选择

在 ThinkLink 平台中搜索模板:

  • KS31
  • MT-KS31

或按业务类型查找:

  • LoRaWAN 原生 AI 采集器
  • 4-20mA 采集设备
  • 0-10V 采集设备
  • 模拟量采集类设备
相关推荐
Zzj_tju1 小时前
大语言模型部署实战:生产环境怎么做高并发、监控、限流与故障恢复?
人工智能·语言模型·自然语言处理
weixin_509138341 小时前
《智能体认知动力学导论》与OT-SGN引擎:投资研究的真实案例——当理论“导航”现实,跨域测地线产生惊人洞察
人工智能
agicall.com1 小时前
信电助-智能双轨电话业务系统部署方案详解
人工智能·语音识别·座机语音转文字·固话录音转文字
隔壁大炮1 小时前
Day02-04.张量点乘和矩阵乘法
人工智能·pytorch·深度学习·线性代数·算法·矩阵
jedi-knight2 小时前
大模型本地部署指南
人工智能
ai产品老杨2 小时前
深度解析:基于异构计算的工业级AI视频中台架构,如何实现GB28181/RTSP跨平台部署与源码交付?
人工智能·架构·音视频
Rubin智造社2 小时前
04月25日AI每日参考:谷歌豪掷400亿押注Anthropic,DeepSeek V4横空出世
大数据·人工智能·物联网·comfyui·deepseek v4·谷歌anthropic投资·meta亚马逊芯片
geneculture2 小时前
本真信息观:基于序位守恒的融智学理论框架——人类认知第二次大飞跃的基础
人工智能·算法·机器学习·数据挖掘·融智学的重要应用·哲学与科学统一性·融智时代(杂志)
俊哥V2 小时前
每日 AI 研究简报 · 2026-04-25
人工智能·ai