TRP-12 Water
Last updated
Last updated
"cmd": "gw",
"seqno": 147729,
"EUI": "BE7A000000000027",
"ts": 1661434039877,
"fcnt": 198,
"port": 3,
"freq": 868500000,
"toa": 1810,
"dr": "SF12 BW125 4/5",
"ack": false,
"gws": [{
"rssi": -87,
"snr": 7.2,
"ts": 1661434039877,
"time": "2022-08-25T13:27:19.763929Z",
"gweui": "9C65F9FFFF386BD3",
"ant": 0,
"lat": 47.3855578,
"lon": 8.5384989
}, {
"rssi": -112,
"snr": -17.8,
"ts": 1661434039898,
"time": "2022-08-25T13:27:19.776447Z",
"gweui": "9C65F9FFFF38674D",
"ant": 0,
"lat": 47.385558700000004,
"lon": 8.5385182
}],
"bat": 255,
"data": "01800002b14d6000820093be028545efe800",
"_id": "630778b83184c84733e8971e"
}n
{
"msg": {
"batteryVoltage": 1.8,
"waterMeter": 7677,
"ts": 1661262701281,
"rssi": -80,
"snr": 9.8,
"toa": 61,
"frequency": 867700000,
"dr": "SF7 BW125 4/5",
"bat": "External power source",
"hex": "01800002b14d6000820093be028545efe800"
},
"metadata": {
"deviceType": "default",
"deviceName": "Test Device",
"ts": "1661434329768"
},
"msgType": "POST_TELEMETRY_REQUEST"
}
// V1.0, 09.08.2021, DS
// V1.1, 11.03.2022, DS
if (msg.data) {
var decoded = decodeFromHex(parseHexString(msg.data));
if(decoded.register0801 != null && decoded.register0802 != null){
decoded.electricityMeterEnergy = decoded.register0801 + decoded.register0802;
}
decoded.ts = msg.ts;
decoded.rssi = msg.rssi;
decoded.snr = msg.snr;
decoded.toa = msg.toa;
decoded.frequency = msg.frequency;
decoded.dr = msg.dr;
decoded.bat = decodeBattery(msg.bat);
decoded.hex = msg.data;
return {
msg: decoded,
metadata: metadata,
msgType: msgType
};
} else {
return {
msg: msg,
metadata: metadata,
msgType: msgType
};
}
function decodeFromHex(bytes) {
// Decode an uplink message from a buffer
// (array) of bytes to an object of fields.
var telemetry = {};
// Get data length, main_header and initiate the counter
var sensorDataLength = bytes.length;
var counter = 1;
var main_header = bytes[0];
// Loop through the data and decode the chuncks. Add them to the telemetry
// immediatly
while (counter < sensorDataLength) {
var identifier = bytes[counter];
var chunkLength = 0;
var obj = {};
var subbytes = [];
if (identifier >= 01 && identifier <= 95) {
//identifierChunch = "ChunkTypeA";
chunkLength =
3; // Type A Chunk. Fixed size. 3 bytes
subbytes = bytes.slice(counter, counter +
chunkLength);
obj = parseChunkTypeA(subbytes);
} else if (identifier >= 96 && identifier <= 127) {
//identifierChunch = "ChunkTypeD";
chunkLength =
2; // Type D Chunk. Fixed size. 2 bytes
subbytes = bytes.slice(counter, counter +
chunkLength);
obj = parseChunkTypeD(subbytes);
} else if (identifier >= 128 && identifier <= 191) {
//identifierChunch = "ChunkTypeB";
chunkLength =
5; // Type B Chunk. Fixed size. 5 bytes
subbytes = bytes.slice(counter, counter +
chunkLength);
obj = parseChunkTypeB(subbytes);
} else if (identifier >= 192 && identifier <= 254) {
//identifierChunch = "ChunkTypeC";
chunkLength = bytes[counter + 1] +
2; // Type C Chunk. Variable size. Size + 2 (+ leading 2 bytes)
if (counter + chunkLength > sensorDataLength) {
chunkLength = sensorDataLength - counter;
}
// check if there already is a timesatmp present
var timestamp = msg.ts;
// if ('ts' in telemetry) {
// timestamp = telemetry[0].ts;
// }
subbytes = bytes.slice(counter, counter +
chunkLength);
// check if the payload already contained a timestamp
obj = parseChunkTypeC(subbytes, timestamp);
} else {
continue;
}
// Push the parsed data to the telemetry array and increment counter
// Check if it is an array, push the data if yes, otherwise concatenate
if (Array.isArray(obj)) {
telemetry.push(obj);
} else {
telemetry = Object.assign(telemetry, obj);
}
counter += chunkLength;
}
// check if the timestamp has been filled by the payload, otherwise use
// the timestamp of the uplink to loriot
if (!('ts' in telemetry)) {
telemetry.ts = msg.ts;
}
return telemetry;
}
function decodeBattery(byte) {
if (byte == 0) {
return 'External power source';
} else if (byte > 0 && byte < 255) {
return byte / 254 * 100;
} else {
return 'Unknown battery state'
}
}
function parseHexString(hex) {
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return bytes;
}
function decodeToString(payload) {
return String.fromCharCode.apply(String, payload);
}
function decodeToJson(payload) {
// covert payload to string.
var str = decodeToString(payload);
// parse string to JSON
var data = JSON.parse(str);
return data;
}
function twos_complement(input_value, num_bits) {
var mask = Math.pow(2, num_bits - 1);
return -(input_value & mask) + (input_value & ~mask);
}
function get_int(bytes) {
var length = bytes.length;
var int = 0;
var i;
for (i = 0; i < length; i++) {
int += bytes[length - i - 1] << (i * 8);
}
return int;
}
function get_float(bytes) {
var length = bytes.length;
var float = 0;
var int = get_int(bytes);
if (length == 4) {
var sign = (int & 0x80000000) ? -1 : 1;
var exponent = ((int >> 23) & 0xFF) - 127;
var significand = (int & ~(-1 << 23));
if (exponent == 128) {
float = sign * ((significand) ? Number.NaN :
Number.POSITIVE_INFINITY);
} else if (exponent == -127) {
if (significand == 0) {
float = sign * 0.0;
} else {
exponent = -126;
significand /= (1 << 22);
float = sign * significand * Math.pow(2,
exponent);
}
} else {
significand = (significand | (1 << 23)) / (1 <<
23);
float = sign * significand * Math.pow(2,
exponent);
}
} else if (length == 2) {
var left = (int >> 14) & 0x03;
var value0 = int & 0x3FFF;
float = value0;
if (left == 0) {
float = float * 0.001;
} else if (left == 1) {
float = float * 0.02;
float = float + 16.38;
} else if (left == 3) {
float = float * 5;
float = float + 16725;
} else if (left == 2) {
float = float + 344;
}
} else {
throw "Undefined floating point size";
}
return float;
}
function parseChunkTypeA(bytes) {
var telemetry = {};
var dataHeader = bytes[0];
var dataValue = get_int(bytes.slice(1));
if (dataHeader ==
1
) { // 1 = 0x01 Temperature. signed integer 16bits. 1 LSB = 0.01°C. 0: 0°C; 1: +0.01°C; 0xFFFF : -0.01°C
telemetry.temperature = twos_complement(dataValue,
16) / 100;
} else if (dataHeader ==
2
) { // 2 = 0x02 Relative Humidity. unsigned integer 16bits. 1 LSB = 0.01°RH
telemetry.humidity = twos_complement(dataValue,
16) / 100;
} else if (dataHeader ==
3
) { // 3 = 0x03 Oxygen concentration. 1 LSB = 0.001%
telemetry.oxygen = dataValue / 1000;
} else if (dataHeader ==
4) { // 4 = 0x04 CO2 concentration. 1 LSB = 0.001%
telemetry.co2 = dataValue / 1000;
} else if (dataHeader ==
5
) { // 5 = 0x05 Second temperature. signed integer 16bits. 1 LSB = 0.01°C. 0: 0°C; 1: +0.01°C; 0xFFFF : -0.01°C
telemetry.temperature2 = twos_complement(dataValue,
16) / 100;
} else if (dataHeader ==
6
) { // 6 = 0x06 Pressure. unsigned integer 16bits. 1 LSB = 0.5mbar
telemetry.pressure = dataValue / 2;
} else if (dataHeader ==
7) { // 7 = 0x07 analog channel #0. 1 LSB = 1 uA
telemetry.analogCurrent0 = dataValue;
} else if (dataHeader ==
8) { // 8 = 0x08 analog channel #1. 1 LSB = 1 uA
telemetry.analogCurrent1 = dataValue;
} else if (dataHeader ==
9) { // 9 = 0x09 analog channel #2. 1 LSB = 1 uA
telemetry.analogCurrent2 = dataValue;
} else if (dataHeader ==
10) { // 10 = 0x0A analog channel #3. 1 LSB = 1 uA
telemetry.analogCurrent3 = dataValue;
} else if (dataHeader ==
11) { // 11 = 0x0B digital inputs state
telemetry.digitalInputsState = dataValue;
} else if (dataHeader ==
12) { // 12 = 0x0C relative pulse counter 0
telemetry.relativePulseCounter0 = dataValue;
} else if (dataHeader ==
13) { // 13 = 0x0D relative pulse counter 1
telemetry.relativePulseCounter1 = dataValue;
} else if (dataHeader ==
14) { // 14 = 0x0E relative pulse counter 2
telemetry.relativePulseCounter2 = dataValue;
} else if (dataHeader ==
16) { // 16 = 0x10 analog channel #0. 1 LSB = 1 mV
telemetry.analogVoltage0 = dataValue;
} else if (dataHeader ==
17) { // 17 = 0x11 analog channel #1. 1 LSB = 1 mV
telemetry.analogVoltage1 = dataValue;
} else if (dataHeader ==
18) { // 18 = 0x12 analog channel #2. 1 LSB = 1 mV
telemetry.analogVoltage2 = dataValue;
} else if (dataHeader ==
19) { // 19 = 0x12 analog channel #3. 1 LSB = 1 mV
telemetry.analogVoltage3 = dataValue;
}
return telemetry;
}
function parseChunkTypeB(bytes) {
var telemetry = {};
var dataHeader = bytes[0];
var dataValue = bytes.slice(1);
// console.log(dataHeader)
if (dataHeader ==
128
) { // 128 = 0x80 timestamp of the measurement. unsigned integer 32 bits. UNIX timestamp
// console.log(dataValue)
// console.log(get_int(dataValue))
//telemetry.ts = twos_complement(get_int(dataValue), 32) *1000; // calculate it in miliseconds
} else if (dataHeader ==
129
) { // 129 = 0x81 Main energy index. IEEE754 floating point. Unit : kWh for electricity meters (register 1.8.0)
telemetry.electricityMeterEnergy = get_float(
dataValue);
} else if (dataHeader ==
131
) { // 129 = 0x81 Main energy index. IEEE754 floating point. Unit : kWh for electricity meters (register 1.8.0)
telemetry.register0801 = get_float(
dataValue);
} else if (dataHeader ==
132
) { // 129 = 0x81 Main energy index. IEEE754 floating point. Unit : kWh for electricity meters (register 1.8.0)
telemetry.register0802 = get_float(
dataValue);
} else if (dataHeader ==
133
) { // 133 = 0x85 Main energy index. IEEE754 floating point. Unit : m3 for water meter
telemetry.waterMeter = get_float(dataValue);
} else if (dataHeader ==
134
) { // 134 = 0x86 Main energy index. IEEE754 floating point. Unit : m3 for uncorrected gas meter
telemetry.gasMeterVolume = get_float(dataValue);
} else if (dataHeader ==
135
) { // 135 = 0x87 Flow temperature. IEEE754 floating point. Unit : °C
telemetry.flowTemperature = get_float(dataValue);
} else if (dataHeader ==
138
) { // 138 = 0x8a Power register. IEEE754 floating point. Unit W.
telemetry.power = get_float(dataValue);
} else if (dataHeader ==
139
) { // 139 = 0x8b Volume index. IEEE754 floating point. Unit: m3 for heat meter
telemetry.heatMeterVolume = get_float(dataValue);
} else if (dataHeader ==
140
) { // 140 = 0x8c Return flow temperature. IEEE754 floating point. Unit: °C
telemetry.returnFlowTemperature = get_float(
dataValue);
} else if (dataHeader ==
141
) { // 141 = 0x8d VolumeFlow = flowRate. IEEE754 floating point. Unit: m3/h
telemetry.flowRate = get_float(dataValue);
} else if (dataHeader ==
154
) { // 154 = 0x9A IEEE754 floating point. Unit : kWh for heat meters (register 1.8.0)
telemetry.heatMeterEnergy = get_float(dataValue);
}
return telemetry;
}
function parseChunkTypeC(bytes, timestamp) {
var telemetry = [];
var size = bytes.length;
var dataHeader = bytes[0];
var snr = get_int(bytes.slice(1, 5));
var status = bytes.slice(5, 6);
var interval =
900000; // accumulation interval in milliseconds
var lastReadIndex = get_float(bytes.slice(6, 10));
var idx = 10;
var counter = 1;
// Get the interval from the status
if ((status & 0x1c) == 0) {
interval = 3600000;
} else if ((status & 0x1c) == 0x4) {
interval = 900000;
} else if ((status & 0x1c) == 0x8) {
interval = 38400000;
}
// Loop through the deltas and change the timestamp accordingly
if (dataHeader ==
201
) { // 140 = 0x8c Return flow temperature. IEEE754 floating point. Unit: °C
telemetry.push({
//'ts': timestamp,
'waterMeterSNR': snr,
'waterMeterCounter': lastReadIndex,
'batteryError': Boolean(status &
0x2),
'otherError': Boolean(status & 1)
})
while (idx <= size) {
var slice = bytes.slice(idx, idx + 2);
if (!(get_int(slice) == 0xffff)) {
telemetry.push({
//'ts': timestamp - counter * interval,
'waterMeterCounter': lastReadIndex +
get_float(slice)
})
}
idx += 2;
counter++;
}
} else if (dataHeader ==
202
) { // 141 = 0x8d Volume flow. IEEE754 floating point. Unit: m3/h
telemetry.push({
//'ts': timestamp,
'gasMeterSNR': snr,
'gasMeterCounter': lastReadIndex,
'batteryError': Boolean(status &
0x2),
'otherError': Boolean(status & 1)
})
while (idx <= size) {
var slice = bytes.slice(idx, idx + 2);
if (!(get_int(slice) == 0xffff)) {
telemetry.push({
//'ts': timestamp - counter * interval,
'gasMeterCounter': lastReadIndex +
get_float(slice)
})
}
idx += 2;
counter++;
}
}
return telemetry;
}
function parseChunkTypeD(bytes) {
var telemetry = {};
var dataHeader = bytes[0];
var dataValue = get_int(bytes.slice(1));
if (dataHeader == 96) { // 96 = 0x60 Battery voltage
var volt = 0;
if (dataValue >= 81) {
volt = 4.2 + (dataValue - 80) * 0.1
} else {
volt = 1.8 + dataValue * 0.03
}
telemetry.batteryVoltage = volt;
} else if (dataHeader ==
97) { // 97 = 0x61 M-BUS meter status byte
telemetry.mBusMeterStatus = dataValue;
}
return telemetry;
}
function decodeBattery(byte) {
if (byte == 0) {
return 'External power source';
} else if (byte > 0 && byte < 255) {
return byte / 254 * 100;
} else {
return 'Unknown battery state';
}
}