Añadir lsn50-v2-xkc-y25-v-drainage/config/codec-production.js
This commit is contained in:
parent
70f76ba107
commit
45ced2c42b
106
lsn50-v2-xkc-y25-v-drainage/config/codec-production.js
Normal file
106
lsn50-v2-xkc-y25-v-drainage/config/codec-production.js
Normal file
@ -0,0 +1,106 @@
|
||||
// ChirpStack v4+ Codec (JavaScript)
|
||||
// LSN50 v2.x - Production codec for PA12 + Downlink control (TDC + 5VT)
|
||||
//
|
||||
// Verified mapping (uplink): PA12 = bytes[6] bit1 (0x02) on FPort 2 (MOD=1, 11 bytes)
|
||||
// Downlink commands (Dragino common):
|
||||
// - Set TDC (seconds): 0x01 + 3 bytes (big-endian)
|
||||
// - Set 5VT (ms): 0x07 + 2 bytes (big-endian)
|
||||
|
||||
const CFG = {
|
||||
INVERT_LOGIC: false,
|
||||
EXPECTED_UP_FPORT: 2,
|
||||
EXPECTED_UP_LEN: 11,
|
||||
CMD_FPORT: 2,
|
||||
LIMITS: {
|
||||
TDC_SEC_MIN: 5,
|
||||
TDC_SEC_MAX: 86400,
|
||||
VT_MS_MIN: 0,
|
||||
VT_MS_MAX: 60000
|
||||
}
|
||||
};
|
||||
|
||||
function decodeUplink(input) {
|
||||
const b = input.bytes || [];
|
||||
const fPort = input.fPort;
|
||||
const rawHex = b.map(x => x.toString(16).padStart(2, "0")).join("");
|
||||
|
||||
const byte6 = (b.length > 6) ? b[6] : 0x00;
|
||||
const pa12 = (byte6 & 0x02) ? 1 : 0;
|
||||
const drenaje = CFG.INVERT_LOGIC ? (pa12 ? 0 : 1) : pa12;
|
||||
|
||||
const warnings = [];
|
||||
if (fPort !== CFG.EXPECTED_UP_FPORT) warnings.push(`Unexpected fPort=${fPort}, expected ${CFG.EXPECTED_UP_FPORT}`);
|
||||
if (b.length !== CFG.EXPECTED_UP_LEN) warnings.push(`Unexpected payload length=${b.length}, expected ${CFG.EXPECTED_UP_LEN}`);
|
||||
|
||||
return {
|
||||
data: {
|
||||
drenaje,
|
||||
pa12,
|
||||
byte6,
|
||||
payload_len: b.length,
|
||||
payload_hex: rawHex,
|
||||
warnings: warnings.length ? warnings : undefined
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function u16be(n) {
|
||||
return [(n >> 8) & 0xff, n & 0xff];
|
||||
}
|
||||
|
||||
function u24be(n) {
|
||||
return [(n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff];
|
||||
}
|
||||
|
||||
function assertInt(name, v) {
|
||||
if (typeof v !== "number" || !isFinite(v) || Math.floor(v) !== v) {
|
||||
throw new Error(`${name} must be an integer`);
|
||||
}
|
||||
}
|
||||
|
||||
function clampInt(name, v, min, max) {
|
||||
assertInt(name, v);
|
||||
if (v < min || v > max) throw new Error(`${name} out of range [${min}, ${max}]: ${v}`);
|
||||
return v;
|
||||
}
|
||||
|
||||
function encodeDownlink(input) {
|
||||
const data = (input && input.data) ? input.data : {};
|
||||
const cmd = data.cmd;
|
||||
|
||||
if (!cmd) {
|
||||
return { bytes: [] };
|
||||
}
|
||||
|
||||
if (cmd === "set_tdc") {
|
||||
const tdc = clampInt("tdc_sec", data.tdc_sec, CFG.LIMITS.TDC_SEC_MIN, CFG.LIMITS.TDC_SEC_MAX);
|
||||
const bytes = [0x01].concat(u24be(tdc));
|
||||
return {
|
||||
fPort: CFG.CMD_FPORT,
|
||||
bytes
|
||||
};
|
||||
}
|
||||
|
||||
if (cmd === "set_5vt") {
|
||||
const vt = clampInt("vt_ms", data.vt_ms, CFG.LIMITS.VT_MS_MIN, CFG.LIMITS.VT_MS_MAX);
|
||||
const bytes = [0x07].concat(u16be(vt));
|
||||
return {
|
||||
fPort: CFG.CMD_FPORT,
|
||||
bytes
|
||||
};
|
||||
}
|
||||
|
||||
if (cmd === "profile_active") {
|
||||
const vt = clampInt("vt_ms", (data.vt_ms ?? 2000), CFG.LIMITS.VT_MS_MIN, CFG.LIMITS.VT_MS_MAX);
|
||||
const bytes = [0x07].concat(u16be(vt));
|
||||
return { fPort: CFG.CMD_FPORT, bytes };
|
||||
}
|
||||
|
||||
if (cmd === "profile_sleep") {
|
||||
const vt = clampInt("vt_ms", (data.vt_ms ?? 0), CFG.LIMITS.VT_MS_MIN, CFG.LIMITS.VT_MS_MAX);
|
||||
const bytes = [0x07].concat(u16be(vt));
|
||||
return { fPort: CFG.CMD_FPORT, bytes };
|
||||
}
|
||||
|
||||
throw new Error(`Unknown cmd: ${cmd}`);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user