/**
* Parser for Lobaro Pressure Probe via LoRaWAN (hybrid gateway).
* Usable for Pressure Probe as or with Presure+Temperature Probe.
* Works with TTN, ChirpStack, or the Lobaro Platform.
*/
function signed(val, bits) {
// max positive value possible for signed int with bits:
var mx = Math.pow(2, bits-1);
if (val < mx) {
// is positive value, just return
return val;
} else {
// is negative value, convert to neg:
return val - (2 * mx);
}
}
function int16_BE(bytes, idx) {
bytes = bytes.slice(idx || 0);
return signed(bytes[0] << 8 | bytes[1] << 0, 2*8);
}
function uint16_BE(bytes, idx) {
bytes = bytes.slice(idx || 0);
return bytes[0] << 8 | bytes[1] << 0;
}
function uint32_BE(bytes, idx) {
bytes = bytes.slice(idx || 0);
return bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3] << 0;
}
function float32FromInt(asInt) {
var sign = (asInt >> 31) == 0 ? 1 : -1;
var exponent = ((asInt >> 23) & 0xFF) - 127;
var significand = (asInt & ~(-1 << 23));
if (exponent === 128)
return null;
// return sign * ((significand) ? Number.NaN : Number.POSITIVE_INFINITY);
if (exponent === -127) {
if (significand === 0) return sign * 0.0;
exponent = -126;
significand /= (1 << 22);
} else {
significand = (significand | (1 << 23)) / (1 << 23);
}
return sign * significand * Math.pow(2, exponent);
}
function float32_BE(bytes, idx) { return float32FromInt(uint32_BE(bytes, idx)); }
/**
* TTN decoder function.
*/
function Decoder(bytes, port) {
var vals = {};
if( port == 20 ){
if (bytes.length==5) {
// Pressure Probe without temperature sensor and Bridges internal Temperature
vals["error"] = !!(bytes[0]&0x80);
vals["pressure"] = int16_BE(bytes, 1)/1000;
vals["temperature"] = int16_BE(bytes, 3);
} else if (bytes.length==7) {
vals["error"] = !!(bytes[0]&0x80);
vals["pressure"] = int16_BE(bytes, 1)/1000;
vals["temperature"] = int16_BE(bytes, 3);
vals["voltage"] = uint16_BE(bytes, 5) / 1000;
} else if (bytes.length==9) {
vals["error"] = !!(bytes[0]&0x80);
// pressure in mH2O
vals["pressure"] = float32_BE(bytes, 1);
// temperature in Degree Celsius
vals["temperature"] = float32_BE(bytes, 5);
} else if (bytes.length==11) {
vals["error"] = !!(bytes[0]&0x80);
// pressure in mH2O
vals["pressure"] = float32_BE(bytes, 1);
// temperature in Degree Celsius
vals["temperature"] = float32_BE(bytes, 5);
vals["voltage"] = uint16_BE(bytes, 9) / 1000;
}
}
if (port === 64 && bytes.length == 13) { // status packet
vals["Firmware Identifier"] = String.fromCharCode(bytes[0]) + String.fromCharCode(bytes[1]) + String.fromCharCode(bytes[2]);
vals["FirmwareVersion"] = bytes[3] + '.' + bytes[4] + '.' + bytes[5];
vals["status"] = bytes[6];
vals["reboot reason"] = bytes[7];
vals["final words"] = bytes[8];
vals["voltage"] = uint16_BE(bytes,9)/1000.0
vals["temperature"] = int16_BE(bytes,11)/10.0;
}
return vals;
}
function NB_ParseModbusQuery(input){
vals = {};
for( var i = 0; i< input.d.batch.length; i++ ){
if (input.d.batch[i].cmd == "AQMAFgAC"){
vals["pressure"] = float32_BE(bytes(atob(input.d.batch[i].rsp)),3);
}
if (input.d.batch[i].cmd == "AQMAJgAC"){
vals["temperature"] = float32_BE(bytes(atob(input.d.batch[i].rsp)),3);
}
// else: keller
if (input.d.batch[i].cmd == "AQMAAgAC"){
// convert to mH2O
vals["pressure"] = float32_BE(bytes(atob(input.d.batch[i].rsp)),3)*10.197442889221;
}
if (input.d.batch[i].cmd == "AQMACAAC"){
vals["temperature"] = float32_BE(bytes(atob(input.d.batch[i].rsp)),3);
}
// vbat
if (input.d.batch[i].cmd == "+gQABQAB"){
vals["vBat"] = int16_BE(bytes(atob(input.d.batch[i].rsp)),3)/1000.0;
}
// internal temperature
if (input.d.batch[i].cmd == "+gQABAAB"){
vals["temperatureInt"] = int16_BE(bytes(atob(input.d.batch[i].rsp)),3);
}
}
return vals;
}
/**
* TTN V3 Wrapper
*/
function decodeUplink(input) {
return {
data: {
values: Decoder(input.bytes, input.fPort)
},
warnings: [],
errors: []
};
}
function NB_ParseDeviceQuery(input) {
for (var key in input.d) {
var v = input.d[key];
switch (key) {
case "temperature":
v = v / 10.0;
Device.setProperty("device.temperature", v);
continue;
case "vbat":
v = v / 1000.0;
Device.setProperty("device.voltage", v);
continue;
}
Device.setProperty("device." + key, v);
}
return null;
}
function NB_ParseConfigQuery(input) {
for (var key in input.d) {
Device.setConfig(key, input.d[key]);
}
return null;
}
function NB_ParseStatusQuery(input) {
NB_ParseDeviceQuery(input);
return null;
}
/**
* ChirpStack decoder function.
*/
function Decode(fPort, bytes) {
// wrap TTN Decoder:
return Decoder(bytes, fPort);
}
/**
* Lobaro Platform decoder function.
*/
function Parse(input) {
if (input.i && input.d) {
// NB-IoT
var decoded = {};
decoded = input.d;
decoded.address = input.i;
decoded.fCnt = input.n;
var query = input.q || "data";
switch (query) {
case "config":
return NB_ParseConfigQuery(input);
case "device":
return NB_ParseDeviceQuery(input);
case "modbus":
return NB_ParseModbusQuery(input);
case "status":
return NB_ParseStatusQuery(input);
default:
}
return decoded;
}
var data = bytes(atob(input.data));
var port = input.fPort;
return Decoder(data, port);
} |