LoRaWAN协议
1.协议概述
此文档规范了 LoRaWAN 节点与 NS 服务器之间的通讯协议数据格式。节点可以是采集设备数据的网关,也可以是直连表。设备使用 LoRaWan 无线通讯将设备数据逐层上报到 NS 服务器,同时节点还支持 NS 服务器进行透传下发。数据报文格式是 HEX 十六进制格式(HEX Format)。
┌────────┐ ┌─────────┐ ┌────────────────┐ ┌───────────┐
│ Device │ -- │ Gateway │ -- │ Network Server │ --MQTT-- │ Acrel IoT │
└────────┘ └─────────┘ └────────────────┘ └───────────┘
如上图所示,设备通过LoRaWAN网关(如AWT200-LW)接入NS平台(Network Server),然后需要在NS平台维护JS脚本,将HEX报文解析中台所需JSON格式 ,再由NS平台转发给数据中台进行数据处理。
由于LoRaWAN报文长度受限,报文中仅包含电参量值,而不包含参量名,所以对于中台来说,不同型号、不同版本的报文格式不相同,定制开发的成本太高,所以设备研发完成后,还需要编写解析报文的Javascript脚本。
2.解析脚本
以下为解析脚本模板,其中标注了需要修改的用于解析数据内容的主要逻辑部分。
/**
* 上行解码器脚本
*/
function uplinkDeviceDecode(bytes, input) {
var decoded = {};
decoded.devEUI = LoRaObject.devEUI;
decoded.rssi = LoRaObject.rxInfo[0].rssi;
decoded.snr = LoRaObject.rxInfo[0].loRaSNR;
decoded.fPort = LoRaObject.fPort;
//原始数据,0x90透传报文需要用到
decoded.data = LoRaObject.data;
//中台以此区分报文是否是经过脚本解析,默认置为true
// true-未解析过的16进制报文
// false-解析为JSON报文
decoded.original = true;
//中台产品型号productKey,联系中台研发获取
decoded.productKey = "ODAzMTU1MDA1ODcyMDc0NzUy";
//中台产品名称,联系中台研发获取
decoded.productName = "ADL200-NK";
var payload = {};
// 命令字91代表实时数据,
if (bytes[3] === 0x91) {
// 91报文是解析过的,这里修改标识
decoded.original = false;
/* === START 需要修改的主要逻辑,依次解析电参量值 START === */
// 仪表地址,中台根据此地址生成拓扑
decoded.addr = bytes[4];
// modbus地址
decoded.MeterAddr = bytes[4];
// 仪表状态,0-offline 1-online
decoded.MeterState = bytes[6];
decoded.EPI = bytesToFloat(bytes.slice(7, 11),2);
decoded.EPIJ = bytesToFloat(bytes.slice(11, 15),2);
decoded.EPIF = bytesToFloat(bytes.slice(15, 19),2);
decoded.EPIP = bytesToFloat(bytes.slice(19, 23),2);
decoded.EPIG = bytesToFloat(bytes.slice(23, 27),2);
decoded.U = bytesToFloat(bytes.slice(27, 31),1);
decoded.I = bytesToFloat(bytes.slice(31, 35),1);
decoded.P = bytesToFloat(bytes.slice(35, 39),3);
decoded.Q = bytesToFloat(bytes.slice(39, 43),1);
decoded.S = bytesToFloat(bytes.slice(43, 47),1);
decoded.PF = bytesToFloat(bytes.slice(47, 51),1);
decoded.EPE = bytesToFloat(bytes.slice(51, 55),2);
decoded.AlarmA = bytesToFloat(bytes.slice(55, 59),1);
decoded.AlarmB = bytesToFloat(bytes.slice(59, 63),1);
decoded.PowerLimit = bytesToFloat(bytes.slice(63, 67),2);
decoded.BuyTimes = bytesToFloat(bytes.slice(67, 71),0);
decoded.Balance = bytesToFloat(bytes.slice(71, 75),2);
decoded.PriceSharp = bytesToFloat(bytes.slice(75, 79),2);
decoded.PricePeak = bytesToFloat(bytes.slice(79, 83),2);
decoded.PriceFlat = bytesToFloat(bytes.slice(83, 87),2);
decoded.PriceVally = bytesToFloat(bytes.slice(87, 91),2);
decoded.AlarmPower = bytesToFloat(bytes.slice(91, 95),2);
decoded.PRESTATE = bytesToFloat(bytes.slice(95, 99),0);
/* === END 需要修改的主要逻辑,依次解析电参量值 END === */
payload.data = decoded;
return payload;
}
}
// Chirpstack v4 使用的函数
function decodeUplink(input) {
var decoded = uplinkDeviceDecode(input.bytes, input);
return {
data: decoded
};
}
// Chirpstack v3 使用的函数
function Decode(fPort, bytes) {
return uplinkDeviceDecode(bytes);
}
// The Things Network 使用的函数
function Decoder(bytes, port) {
return uplinkDeviceDecode(bytes);
}
/*
******************************************
* bytes to number
*******************************************
*/
function bytesToFloat(bytes, id, scale) {
id = (id - 1) * 4;
var buff = bytes.slice(7 + id, 7 + id + 4);
var bits = (buff[0] << 24) | (buff[1] << 16) | (buff[2] << 8) | buff[3];
var sign = bits >>> 31 === 0 ? 1.0 : -1.0;
var e = (bits >>> 23) & 0xff;
var m = e === 0 ? (bits & 0x7fffff) << 1 : (bits & 0x7fffff) | 0x800000;
var f = sign * m * Math.pow(2, e - 150);
f = parseFloat(f.toFixed(scale));
return f;
}
3.1 实时数据
字段 | 数值(HEX) | 类型 | 长度 |
|---|
包头 | {{ | ASCII | 2 |
包长 | 包头到结束符的包长 | uint8 | 1 |
功能码 | 0x91 | uint8 | 1 |
表地址 | 仪表modbus地址1~255 | uint8 | 1 |
数据组号 | 网关模板内的组号1~255 | uint8 | 1 |
在线状态 | 0=离线 1=在线 | uint8 | 1 |
数据体 | 根据模板内参数序号依次排列 | | N<=4*X |
CRC | 包长~数据体的CRC | uint16 | 2 |
结束符 | }} | ASCII | 2 |
总包长 | | | 11+N |
注意:上述字段<数据体>的长度一栏,X 代表上报参数数量,其根据 LoRaWAN 发送速率有所不同 ,见下表:
LoRaWAN 速率 | 单包上传最大参数数量(个) |
|---|
自动 | 10 |
SF10~SF12 | 10 |
SF9 | 20 |
SF8/SF7 | 40 |
3.2 命令下发
注:下发端口为 FPort:10
字段 | 数值(HEX) | 类型 | 长度 |
|---|
包头 | {{ | ASCII | 2 |
包长 | 包头到结束符的包长 | uint8 | 1 |
功能码 | 0x90 | uint8 | 1 |
数据体 | 自定义输入(透传内容) | uint8 | N<=41 |
CRC | 包长~数据体的CRC | uint16 | 2 |
结束符 | }} | ASCII | 2 |
总包长 | | | 8+N |
报文示例:
透传下发:7b 7b 10 90 01 03 00 00 00 10 44 06 9D 9F 7d 7d
包头:7b 7b
包长:10(总字节数,此处为 7b 7b 10 90 01 03 00 00 00 10 44 06 9D 9F 7d 7d 的字节数,共 16 字节,转换为 16 进制为 0x10)
功能码:90
数据体: 01 03 00 00 00 10 44 06(自定义输入,内容是通过 485 发送给电表的实际报文)
CRC:9D 9F(10 90 01 03 00 00 00 10 44 06 部分做 CRC 校验,此处结果为 9D 9F)
固定包尾:7d 7d
字段 | 数值(HEX) | 类型 | 长度 |
|---|
包头 | {{ | ASCII | 2 |
包长 | 包头到结束符的包长 | uint8 | 1 |
功能码 | 0x90 | uint8 | 1 |
数据体 | 回包数据体(透传内容) | uint8 | N<=41 |
CRC | 包长~数据体的CRC | uint16 | 2 |
结束符 | }} | ASCII | 2 |
总包长 | | | 8+N |
25 十二月 2025