22116-HCA396L-S2 双轴高精度测斜传感器
模型清单
可使用下列名称或 ID 在 ThinkLink 中搜索对应模型。
| 模型类型 | 名称 | id_name | 平台模型 ID |
|---|---|---|---|
| RPC | [HCA396L-S2 GET] 22116 | hca396l_s2_get_22116 | 134816007947448320 |
| RPC | [HCA396L-S2 SET] 22116 | hca396l_s2_set_22116 | 134816007255388160 |
| Template | HCA396L-S2 | hca396l_s2_22116 | 134816005137264640 |
| Thing Model | [HCA396L-S2] | hca396l_s2_22116 | 134816005846102016 |
| Parameter Thing Model | [HCA396L-S2-PARA] | hca396l_s2_para_22116 | 134816006567522304 |
1. 传感器简介
RION / 瑞芬科技 HCA396L-S2-MB 双轴高精度测斜传感器通过 RS-485/Modbus RTU 接入 KC21,业务代码为 22116,ThinkLink 模板为 hca396l_s2_22116。
2. 产品特点
- 双轴 ±85° 量程、0.001° 分辨率、常温综合精度 0.01°
- 5Hz 响应带宽,工作温度 -40~85℃
- 支持 100g 抗冲击和方位角/温度输出
3. 适用范围
- 基坑、边坡和盾构施工监测
- 桥梁、铁路和工程机械姿态监测
- 地质钻探和地下设备姿态检测
4. 采集器信息
- DTU:KC21
- 接口:RS-485
- 默认模式:Battery / Class A
- 串口:9600、8N1
- 传感器上电延时:2500ms
部署前供电确认: 本对接在现场信息缺失时默认按 KC21 电池供电(Class A)处理,尚未由客户确认。若实际为常供电,需改为 Class C、改用 MT-EB-POWER 模板并重新核对心跳周期,否则可能出现下行不及时、功耗异常或控制指令不可达。
电源正负极必须按设备资料与实物铭牌连接;RS-485 A/B 分别连接 KC21 485A/485B。部署前断电完成接线,并确认传感器实际供电范围。
5. 数据采集
采用 FC04,1 个查询事件:从 0x0016 连续读取 8 个寄存器,得到 X/Y 倾角、方位角和温度。采集与上报共用 app_70=900s,COV 关闭。
| 地址 | 字段 | 数据项 | 类型 | 系数 | 单位 |
|---|---|---|---|---|---|
| 0x0016-0x0017 | inclination_x | X轴俯仰角 / X inclination | float32ABCD | 1 | ° |
| 0x0018-0x0019 | inclination_y | Y轴横滚角 / Y inclination | float32ABCD | 1 | ° |
| 0x001A-0x001B | azimuth | 方位角 / Azimuth | float32ABCD | 1 | ° |
| 0x001C-0x001D | temperature | 温度 / Temperature | float32ABCD | 1 | ℃ |
6. EdgeBus 模型
- port: 22
- version/dataType:
0x88/0x01 - BzType/BzVersion:
22116/1 - upPeriodIndex: 70
- addr_modbus: app_150
- 上行帧长度:6 字节公共头 + 所有查询结果字节
- 查询逻辑:1 个查询事件:从 0x0016 连续读取 8 个寄存器,得到 X/Y 倾角、方位角和温度
6.2 EdgeBus 代码
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";
// RION HCA396L-S2-MB, RS485/Modbus-RTU FC04.
// 0x0016-0x001D contains X angle, Y angle, azimuth, and temperature as
// four big-endian IEEE754 float32 values.
const eventInfo: UserConfUPItem[] = [
{
name: "rion-hca396l-s2",
port: 22,
version: "0x88",
dataType: "0x01",
upPeriodIndex: 70,
quInfo: [
{
protocol: "modbus",
addr: "0x01",
code: "0x04",
periodIndex: 70,
indexAPP: 150,
indexCMD: 0,
copySize: 1,
isLast: true,
listVal: [{ start: "0x0016", end: "0x001D" }],
},
],
},
];
const otaConfig = getOtaConfig({
BaudRate: 9600,
StopBits: 1,
DataBits: 8,
Checkbit: CheckbitEnum.NONE,
Battery: true,
ConfirmDuty: 60,
BzType: 22116,
BzVersion: 2,
});
const MODBUS_TT = (ebModel: EBModel) => {
for (const info of eventInfo) {
const event = new EventInfoItem(info);
event.upEventSetup();
event.eventInstall();
}
return JSON.stringify(ebModel, null, 2);
};
buildOtaFile(import.meta.url, otaConfig, MODBUS_TT);7. 物模型
- 遥测物模型:
hca396l_s2_22116 - 参数物模型:
hca396l_s2_para_22116 - 上行:port 22,标签
0x88/0x01
7.1 遥测解析器
import {Buffer} from "buffer";
import {PayloadParser, RPCHelper} from "#tklHelper";
function payload_parser(device, msg, thingModelId, noticeAttrs, org_params) {
const port = msg?.userdata?.port ?? null;
if (port !== 22) return null;
const frameInfo = {
port: 22,
dataLen: 22,
rssi: true,
battery: 4,
status: 3,
tagList: [{index: 0, tag: 0x88}, {index: 1, tag: 0x01}],
};
const appInfo = [
{name: "inclination_x", field_name: "inclination_x", unit: "°", index: 6, type: "floatBE", coefficient: 1, decimal: 3},
{name: "inclination_y", field_name: "inclination_y", unit: "°", index: 10, type: "floatBE", coefficient: 1, decimal: 3},
{name: "azimuth", field_name: "azimuth", unit: "°", index: 14, type: "floatBE", coefficient: 1, decimal: 3},
{name: "temperature", field_name: "temperature", unit: "℃", index: 18, type: "floatBE", coefficient: 1, decimal: 2},
];
const parser = new PayloadParser({device, msg, frameInfo, appInfo});
let telemetry = parser.telemetry();
if (telemetry == null) return null;
if ((telemetry.status & 0x02) === 0x02) {
const status = telemetry.status;
telemetry = {...(device.telemetry_data?.[thingModelId] ?? {})};
telemetry.status = status;
}
telemetry._modelName = "hca396l_s2_22116";
telemetry.adr = msg?.userdata?.adr ?? null;
const result = {
telemetry_data: telemetry,
server_attrs: null,
shared_attrs: null,
};
if (typeof RPCHelper.buildRpcHookAction === "function") {
const action = RPCHelper.buildRpcHookAction({
device,
msg,
thingModelId,
idName: "hca396l_s2_22116",
name: "[HCA396L-S2]",
parsed: result,
});
if (action) result.actions = [action];
}
return result;
}
const deviceObj = {telemetry_data: {}};
const msgObj = {
userdata: {
port: 22,
payload: Buffer.from("880100006401c08f126f3efae1484316199a42040000", "hex").toString("base64"),
},
};
const noticeAttrsObj = {telemetry_data: true};
console.log(payload_parser(deviceObj, msgObj, "1", noticeAttrsObj, {}));7.2 参数解析器
import {PayloadParser, RPCHelper, Utils} from "#tklHelper";
function payload_parser(device, msg, thingModelId, noticeAttrs, org_params) {
const port = msg?.userdata?.port ?? null;
const rpcName = "hca396l_s2_set_22116";
const 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_40: {name: "period_heart", field_name: "period_heart", unit: "s", index: 40, type: "uint32LE"},
app_70: {name: "period_up", field_name: "period_up", unit: "s", index: 70, type: "uint32LE"},
app_150: {name: "addr_modbus", field_name: "addr_modbus", unit: "", index: 150, type: "uint8"},
};
if (port !== 214) {
const checkData = Utils.paraCheck(rpcName, device.server_attrs, device.shared_attrs);
return {server_attrs: checkData.sdata, actions: checkData.actions};
}
const pdata = (new PayloadParser({device, msg, paraInfo: paraDef})).paras();
if (pdata == null) return null;
const checkData = Utils.paraCheck(rpcName, device.server_attrs, pdata);
const result = {
telemetry_data: pdata,
server_attrs: checkData.sdata,
shared_attrs: {
TimeOffset: pdata.TimeOffset,
pwron_delay: pdata.pwron_delay,
period_heart: pdata.period_heart,
period_up: pdata.period_up,
addr_modbus: pdata.addr_modbus,
},
actions: checkData.actions,
};
if (typeof RPCHelper.buildRpcHookAction === "function") {
const action = RPCHelper.buildRpcHookAction({
device,
msg,
thingModelId,
idName: "hca396l_s2_para_22116",
name: "[HCA396L-S2-PARA]",
parsed: {
telemetry_data: result.telemetry_data,
server_attrs: result.server_attrs,
shared_attrs: result.shared_attrs,
},
});
if (action) {
result.actions = result.actions || [];
result.actions.push(action);
}
}
return result;
}
const deviceObj = {telemetry_data: {}, server_attrs: {}, shared_attrs: {}};
const msgObj = {userdata: {port: 11, payload: ""}};
const noticeAttrsObj = {telemetry_data: true};
console.log(payload_parser(deviceObj, msgObj, "1", noticeAttrsObj, {}));8. 第三方 MQTT 订阅
Topic: /v32/{Organization Account}/tkl/up/telemetry/{eui}
{
"eui": "6353012af10a9331",
"active_time": "2026-07-29T08:35:48.000Z",
"thingModelId": "22116",
"thingModelIdName": "hca396l_s2_22116",
"telemetry_data": {
"snr": 13.5,
"rssi": -51,
"battery": 3.37,
"inclination_x": -4.471,
"inclination_y": 0.49,
"azimuth": 150.1,
"temperature": 33
}
}9. RPC
- 参数设置:
hca396l_s2_set_22116 - 参数读取:
hca396l_s2_get_22116
| APP 槽位 | 字段 | 含义 | 单位 | 类型 | 默认值 |
|---|
| app_20 | TimeOffset | 时间偏移 | | uint32le | 0 | | app_38 | pwron_delay | 上电延时 | ms | uint16le | 2500 | | app_40 | period_heart | 心跳周期 | s | uint32LE | 86400 | | app_70 | period_up | 采集与上报周期(≥60) | s | uint32LE | 900 | | app_150 | addr_modbus | Modbus 地址(1-247) | | uint8 | 1 |
9.1 参数设置 RPC
import {RPCHelper, Utils} from '#tklHelper';
import {Buffer} from 'buffer';
function rpc_script({device, params, alarms, logger, org_params}) {
const classMode = device?.shared_attrs?.class_mode || "ClassA";
const rpcName = "hca396l_s2_set_22116";
params = params && typeof params === "object" ? {...params} : {};
if (params.period_up != null && params.period_up < 60) {
logger.info("period_up must be at least 60 seconds");
return null;
}
if (params.addr_modbus != null && (params.addr_modbus < 1 || params.addr_modbus > 247)) {
logger.info("addr_modbus must be between 1 and 247");
return null;
}
if (params.period_heart == null || params.period_heart < 3600) {
params.period_heart = 86400;
}
if (params.pwron_delay == null || params.pwron_delay < 2000) {
params.pwron_delay = 2500;
}
const 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_40: {name: "period_heart", field_name: "period_heart", unit: "s", index: 40, type: "uint32LE"},
app_70: {name: "period_up", field_name: "period_up", unit: "s", index: 70, type: "uint32LE"},
app_150: {name: "addr_modbus", field_name: "addr_modbus", unit: "", index: 150, type: "uint8"}
};
const frames = RPCHelper.buildFrame({paraDef, params});
const redoBuffer = RPCHelper.redo();
const dnBuffer = Buffer.alloc(frames.writeBuffer.length + frames.readBuffer.length);
frames.writeBuffer.copy(dnBuffer, 0);
frames.readBuffer.copy(dnBuffer, frames.writeBuffer.length);
logger.info("set HCA396L-S2 KC21 app parameters");
const msgQue = Utils.makeParaSetMSG({
device,
classMode,
rpcName,
params,
paraDownBuffer: dnBuffer,
extraAppBuffer: redoBuffer
});
return msgQue.length == 0 ? null : msgQue;
}
rpc_script({
device: {server_attrs: {}, shared_attrs: {}},
params: {period_up: 900, addr_modbus: 1, period_heart: 86400, pwron_delay: 2500},
alarms: [],
logger: console,
org_params: {}
});9.2 参数读取 RPC
import {RPCHelper, Utils} from '#tklHelper';
function rpc_script({device, params, alarms, logger, org_params}) {
const classMode = device?.shared_attrs?.class_mode || "ClassA";
const sleepMs = classMode === "ClassA" ? 500 : 5000;
const 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_40: {name: "period_heart", field_name: "period_heart", unit: "s", index: 40, type: "uint32LE"},
app_70: {name: "period_up", field_name: "period_up", unit: "s", index: 70, type: "uint32LE"},
app_150: {name: "addr_modbus", field_name: "addr_modbus", unit: "", index: 150, type: "uint8"}
};
const frames = RPCHelper.buildFrame({paraDef, params: params || {}});
const msg = RPCHelper.makeMSG({
msgType: Utils.msgType.paras,
device,
dnBuffer: frames.readBuffer,
sleepTime: sleepMs
});
return [msg];
}
rpc_script({
device: {server_attrs: {}, shared_attrs: {}},
params: {},
alarms: [],
logger: console,
org_params: {}
});以上 RPC 只配置 KC21 应用参数,不写传感器寄存器。
10. 模板选择
在 ThinkLink 中搜索模板 HCA396L-S2,或按业务代码 22116 查找。
11. 补充说明
- 本对接只适用于 HCA396L-S2-MB Modbus 版本,不适用于 -77 私有协议版本。
- 传感器上电约 2 秒内读取会返回异常,因此上电延时设置为 2500ms。
- 设备写配置需要密码且可能改变测量基准,本次不开放设备寄存器写入。