22116-HCA396L-S2 Dual-axis High-precision Inclinometer
Model catalog
Search for these models in ThinkLink by name or ID.
| Model type | Name | id_name | Platform model 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. Sensor overview
The RION / 瑞芬科技 HCA396L-S2-MB dual-axis high-precision inclinometer connects to a KC21 over RS-485/Modbus RTU. Its business code is 22116 and its ThinkLink template is hca396l_s2_22116.
2. Product features
- Dual-axis ±85° range, 0.001° resolution and 0.01° room-temperature accuracy
- 5Hz bandwidth and -40 to 85℃ operating range
- 100g shock resistance with azimuth and temperature output
3. Application scope
- Foundation-pit, slope and shield-construction monitoring
- Bridge, railway and construction-machine attitude monitoring
- Geological drilling and underground-equipment attitude sensing
4. Collector information
- DTU: KC21
- Interface: RS-485
- Default mode: Battery / Class A
- Serial: 9600, 8N1
- Sensor power-on delay: 2500ms
Pre-deployment power confirmation: Site information was unavailable, so this integration defaults to a battery-powered KC21 in Class A. If the installation is continuously powered, change to Class C, use the MT-EB-POWER base template and review the heartbeat period; otherwise downlinks may be delayed or unavailable and power behaviour may be incorrect.
Connect supply polarity according to the device documentation and nameplate. Connect RS-485 A/B to KC21 485A/485B. Complete wiring with power removed and confirm the actual sensor supply range before deployment.
5. Data acquisition
The integration uses FC04. One query event reads eight consecutive registers from 0x0016 for X/Y inclination, azimuth and temperature. Collection and upload share app_70=900s; COV is disabled.
| Address | Field | Data item | Type | Scale | Unit |
|---|---|---|---|---|---|
| 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 model
- port: 22
- version/dataType:
0x88/0x01 - BzType/BzVersion:
22116/1 - upPeriodIndex: 70
- addr_modbus: app_150
- Uplink length: six common-header bytes plus all selected query-result bytes
- Query logic: One query event reads eight consecutive registers from 0x0016 for X/Y inclination, azimuth and temperature
6.2 EdgeBus code
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. Thing model
- Telemetry:
hca396l_s2_22116 - Parameters:
hca396l_s2_para_22116 - Uplink: port 22, tags
0x88/0x01
7.1 Telemetry parser
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 Parameter parser
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. Third-party MQTT subscription
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
- Set:
hca396l_s2_set_22116 - Get:
hca396l_s2_get_22116
| APP slot | Field | Meaning | Unit | Type | Default |
|---|
| app_20 | TimeOffset | Time offset | | uint32le | 0 | | app_38 | pwron_delay | Power-on delay | ms | uint16le | 2500 | | app_40 | period_heart | Heartbeat period | s | uint32LE | 86400 | | app_70 | period_up | Collect and upload period (≥60) | s | uint32LE | 900 | | app_150 | addr_modbus | Modbus address (1-247) | | uint8 | 1 |
9.1 Set 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 Get 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: {}
});These RPCs configure KC21 application parameters only; they do not write sensor registers.
10. Template selection
Search ThinkLink for template HCA396L-S2, or use business code 22116.
11. Supplementary notes
- This integration is for the HCA396L-S2-MB Modbus variant, not the private-protocol -77 variant.
- Reads during the first approximately two seconds may return an exception, so the power-on delay is 2500ms.
- Configuration writes require a password and may change the measurement reference; no sensor-register writes are exposed.