P1Q LoRaWAN packet format¶
This document describes uplinks produced by the qp-patrol LoRaWAN firmware variant
p1q-nrf52811-s112-lorawan (RN2483/RN2903).
Other P1Q/P1QT/P1QP firmware variants may use different FPorts and/or payload formats.
Measurement packet FPort == 1 WITHOUT alarm functionality¶
| byte no. | example (HEX) | description |
|---|---|---|
| 0 | 03 | unit of measure (megapascal - see table below) |
| 1-4 | E5B8B441 | MIN value: float - little endian (~22.590) |
| 5-8 | 0000C741 | MAX value: float - little endian (24.875) |
| 9-12 | E5B8A441 | Average value: float - little endian (~20.590) |
| 13-16 | 0000C841 | Last value: float - little endian (25) |
Measurement packet FPort == 1 WITH alarm functionality¶
| byte no. | example (HEX) | description |
|---|---|---|
| 0 | 03 | unit of measure (megapascal - see table below) |
| 1-4 | E5B8B441 | MIN value: float - little endian (~22.590) |
| 5-8 | 0000C741 | MAX value: float - little endian (24.875) |
| 9-12 | E5B8A441 | Average value: float - little endian (~20.590) |
| 13-16 | 0000C841 | Last value: float - little endian (25) |
| 17-20 | 3EA3D843 | Meta data - please ignore |
| 21 | 02 | Alarm status - see table below |
Alarm byte interpretation¶
| Packet value | Alarm state |
|---|---|
| 0x00 | no alarm |
| 0x01 | measured value too low |
| 0x02 | measured value too high |
Units of measurement¶
#define UNIT_TYPE_C 0 /**< C = celsius */ #define UNIT_TYPE_PA 1 /**< Pa = pascal */ #define UNIT_TYPE_KPA 2 /**< kPa = kilopascal */ #define UNIT_TYPE_MPA 3 /**< MPa = megapascal */
System / control packets FPort == 2¶
Warning
Devices shipped before Jan 2025 used unconfirmed packet uplink for the boot packet. After this date confirmed packets are used.
Battery packet FPort == 3¶
| byte no. | example (HEX) | description |
|---|---|---|
| 0 | 0a | raw battery indicator (0-255, implementation-defined). Do not interpret as voltage or percentage. |
Example parsers¶
Python3¶
``` python
!/usr/bin/env python3¶
import struct import argparse from pprint import pprint
def parse(packet, fport): out = {"packet": "".join("{:02X}".format(x) for x in packet)}
out["fport"] = fport
if fport == 1:
out["info"] = "This is a standard measurement packet."
elif fport == 2:
out["info"] = "This is a boot / test packet."
return out
elif fport == 3:
out["info"] = "This is a BATTERY STATUS packet."
out["battery_level_adc"] = int(packet[0])
return out
elif fport == 0x43:
out[
"info"
] = "This is a START packet. This packet is only sent once after device boots!"
return out
else:
out["info"] = "Packet FPort unknown type -> 0x{:02x}! Aborting parsing.".format(
fport
)
return out
out["unit of measure"] = packet[0]
out["MIN (float)"] = struct.unpack("<f", packet[1:5])[0]
out["MAX (float)"] = struct.unpack("<f", packet[5:9])[0]
out["AVG (float)"] = struct.unpack("<f", packet[9:13])[0]
out["LAST (float)"] = struct.unpack("<f", packet[13:17])[0]
return out
if name == "main":
parser = argparse.ArgumentParser(description="LoRaWAN packet parser")
parser.add_argument(
"--pl",
default="03E5B8B4410000C741E5B8A4410000C841",
action="store",
help="Payload in hex",
)
parser.add_argument(
"--fport",
default=1,
action="store",
type=int,
help="LoRaWAN FPort",
)
args = parser.parse_args()
packet = bytes.fromhex(args.pl)
print("Packet in HEX: 0x", end="")
for p in packet:
print("{:02X} ".format(p), end="")
print()
print("Packet in DEC: ", end="")
for p in packet:
print("{} ".format(int(p)), end="")
print()
data = parse(packet, args.fport)
pprint(data)
```
Java¶
``` java class PacketParser { public static float reverse_endianness(float x) { return java.nio.ByteBuffer.allocate(8) .order(java.nio.ByteOrder.BIG_ENDIAN).putFloat(x) .order(java.nio.ByteOrder.LITTLE_ENDIAN).getFloat(0); }
public static void main(String[] args) { System.out.println("P1AP pressure sensor LoRaWAN packet parser");
String packet = "03E5B8B4410000C741E5B8A4410000C841";
final int unit_of_measure = Integer.parseInt(packet.substring(0, 2), 16);
Long i = Long.parseLong(packet.substring(2, 10), 16);
final Float min_value = reverse_endianness(Float.intBitsToFloat(i.intValue()));
i = Long.parseLong(packet.substring(10, 18), 16);
final Float max_value = reverse_endianness(Float.intBitsToFloat(i.intValue()));
i = Long.parseLong(packet.substring(18, 26), 16);
final Float average_value = reverse_endianness(Float.intBitsToFloat(i.intValue()));
i = Long.parseLong(packet.substring(26, 34), 16);
final Float last_value = reverse_endianness(Float.intBitsToFloat(i.intValue()));
System.out.println("Example packet in HEX: " + packet);
System.out.print("Unit of measure index: " + unit_of_measure);
if (unit_of_measure == 3) {
System.out.println(" MPa");
} else {
System.out.println("");
}
System.out.println("MIN value: " + min_value);
System.out.println("MAX value: " + max_value);
System.out.println("AVERAGE value: " + average_value);
System.out.println("LAST value: " + last_value);
} }
```
ChirpStack / JavaScript¶
``` js var units = ['C', 'Pa', 'kPa', 'MPa'];
function bytesToFloat(bytes) { "use strict"; var bits = bytes[3]<<24 | bytes[2]<<16 | bytes[1]<<8 | bytes[0]; 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); return f; }
function Decode(fPort, bytes, variables) { "use strict"; switch (fPort) { case 1: return { unit: units[bytes[0]], min_value: bytesToFloat(bytes.slice(1, 5)), max_value: bytesToFloat(bytes.slice(5, 9)), average_value: bytesToFloat(bytes.slice(9, 13)), last_value: bytesToFloat(bytes.slice(13, 17)) }; default: return { errors: ['Unknown FPort - see device manual!'], }; } }
```
TTN / JavaScript¶
``` js var units = ['C', 'Pa', 'kPa', 'MPa'];
function bytesToFloat(bytes) { "use strict"; var bits = bytes[3]<<24 | bytes[2]<<16 | bytes[1]<<8 | bytes[0]; 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); return f; }
function decodeUplink(input) { "use strict"; switch (input.fPort) { case 1: return { data: { unit: units[input.bytes[0]], min_value: +bytesToFloat(input.bytes.slice(1, 5)).toFixed(4), max_value: +bytesToFloat(input.bytes.slice(5, 9)).toFixed(4), average_value: +bytesToFloat(input.bytes.slice(9, 13)).toFixed(4), last_value: +bytesToFloat(input.bytes.slice(13, 17)).toFixed(4) } }; default: return { errors: ['Unknown FPort - see device manual!'], }; } }
```
Support¶
For support please contact your distributor or manufacturer directly via www.moirelabs.com