...
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
/** * 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 // Note that MAX_SAFE_INTEGER is 9007199254740991 function toNumber_BE(bytes, len, signed) { var res = 0; var isNeg = false; if (len == 0) { len = bytes.length; } if (signed) { isNeg = (bytes[0] & 0x80) != 0; } for (var i = 0; i < len ; i++) { if (i == 0 && isNeg) { // Treat most-significant bit as -2^i instead of 2^i res += bytes[i] & 0x7F; res -= 0x80; } else { res *= 256; res += bytes[i]; } } return res; } function int16_BE(bytes, idx) { bytes = bytes.slice(idx || 0); return signed(bytes[0] << 8 | bytes[1] << 0, 2*8); } function int32_BE(bytes, idx) { bytes = bytes.slice(idx || 0); return toNumber_BE(bytes, 4, true); } function uint16_BE(bytes, idx) { bytes = bytes.slice(idx || 0); return signed(bytes[0] << 8 | bytes[1] << 0, 2*8); } function uint16uint32_BE(bytes, idx) { bytes = bytes.slice(idx || 0); return bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[13] << 0; } function uint32_BE// float32([62, 132, 168, 155]) = 0.305068 function float32(bytes, idx) { bytes = bytes.slice(idx || 0); return bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3] << 0; } function float32FromInt(asInt) {= int32_BE(bytes, 0) var sign = (asIntbytes >> 31) == 0 ? 1 : -1;1 : -1; // Comparison with 0x80000000 fails on 32 bit systems! var exponent = ((asIntbytes >> 23) & 0xFF) - 127; var significand = (asIntbytes & ~(-1 << 23)); if (exponent === 128) { // Some systems might have issues with NaN return null;and POSITIVE_INFINITY, e.g. JSON parsing in GoLang // 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 float32FromIntfloat32(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); } |
...