Sensor
"use strict"; function decodeUplink(payload) { payload = TTNfrom(payload); var uplinkId = payload.substring(0, 2); var content; switch (uplinkId.toUpperCase()) { case "01": content = parseTimeSync(payload.trim()); break; case "04": content = parseTER(payload.trim()); break; case "09": content = parseMetering(payload.trim()); break; case "10": content = parseDigitalData(payload.trim()); break; case "12": content = parseVOC(payload.trim(), true); //true is for new sensor break; case "13": content = parseCo2(payload.trim(), true); //true is for new sensor break; case "0A": content = parseIO(payload.trim()); break; case "0B": content = parseReportData(payload.trim()); break; case "0C": content = parseVOC(payload.trim(), false); //false is for new sensor break; case "0D": content = parseAnalog(payload.trim()); break; case "0E": content = parseCo2(payload.trim(), false); //false is for new sensor break; default: content = null; break; } return TTNto(content); } function TTNfrom(TTNpayload) { var obj; if (typeof TTNpayload === "string") { obj = JSON.parse(TTNpayload); } else { obj = JSON.parse(JSON.stringify(TTNpayload)); } var fPort = obj.fPort; var payload = ""; for (var i = 0; i < obj.bytes.length; i++) { payload += parseInt(obj.bytes[i], 10).toString(16).padStart(2, "0"); } return payload; } function TTNto(content) { var TTNcontent = {}; if (content !== null) { for (var i = 0; i < content.length; i++) { TTNcontent[content[i].variable] = content[i].value; } return { data: TTNcontent }; } else { return { data: content, warnings: [], errors: ["Error on decoding payload"] }; } } function parseTER(payload) { var uplinkId = payload.substring(0, 2); if (uplinkId.toUpperCase() === "04") { var m1 = parseTERMeasurement(payload.substring(2, 22)); var m2 = parseTERMeasurement(payload.substring(22, 42)); var m3 = parseTERMeasurement(payload.substring(42, 62)); var battery = { variable: "battery", value: Number(parseInt(payload.substring(62, 64), 16)).toFixed(), unit: "%" }; var rfu = { variable: "rfu", value: payload.substring(64) }; return [].concat(m1, m2, m3, [battery, rfu]); } else { return null; } } function parseTERMeasurement(payload) { var date = { variable: "date", value: parseDate(payload.substring(0, 8)) }; var temperature = { variable: "temperature", value: getTemperature( parseInt(payload.substring(8, 10), 16), parseInt(payload.substring(10, 12), 16) ), unit: "�C" }; var humidity = { variable: "humidity", value: getHumidity(parseInt(payload.substring(12, 14), 16)), unit: "%" }; var pressure = { variable: "pressure", value: getPressure( parseInt(payload.substring(14, 16), 16), parseInt(payload.substring(16, 18), 16), parseInt(payload.substring(18, 20), 16) ), unit: "hPa" }; return [date, temperature, humidity, pressure]; } function parseVOC(payload, isNew) { var uplinkId = payload.substring(0, 2); if (!isNew) { if (uplinkId.toUpperCase() === "0C") { var m1 = parseVOCMeasurement(payload.substring(2, 30), isNew); var m2 = parseVOCMeasurement(payload.substring(30, 58), isNew); var battery = { variable: "battery", value: Number(parseInt(payload.substring(58, 60), 16)).toFixed(), unit: "%" }; var rfu = { variable: "rfu", value: payload.substring(60) }; return [].concat(m1, m2, [battery, rfu]); } else { return null; } } else { if (uplinkId.toUpperCase() === "12") { var m1 = parseVOCMeasurement(payload.substring(2, 32), isNew); var m2 = parseVOCMeasurement(payload.substring(32, 62), isNew); var battery = { variable: "battery", value: Number(parseInt(payload.substring(62, 64), 16)).toFixed(), unit: "%" }; var rfu = { variable: "rfu", value: payload.substring(64) }; return [].concat(m1, m2, [battery, rfu]); } else { return null; } } } function parseVOCMeasurement(payload, isNew) { if (!isNew) { var date = { variable: "date", value: parseDate(payload.substring(0, 8)) }; var temperature = { variable: "temperature", value: getTemperature( parseInt(payload.substring(8, 10), 16), parseInt(payload.substring(10, 12), 16) ), unit: "�C" }; var humidity = { variable: "humidity", value: getHumidity(parseInt(payload.substring(12, 14), 16)), unit: "%" }; var pressure = { variable: "pressure", value: getPressure( parseInt(payload.substring(14, 16), 16), parseInt(payload.substring(16, 18), 16), parseInt(payload.substring(18, 20), 16) ), unit: "hPa" }; var lux = { variable: "lux", value: Number(parseUnsignedShort(payload.substring(20, 24))).toFixed(), unit: "lx" }; var voc = { variable: "voc", value: Number(parseUnsignedShort(payload.substring(24, 28))).toFixed(), unit: "IAQ/ppb" }; return [date, temperature, humidity, pressure, lux, voc]; } else { var payloadToByteArray = hexStringToByteArray(payload); var date = { variable: "date", value: parseDate(payload.substring(0, 8)) }; var temperature = { variable: "temperature", value: getTemperature( parseInt(payload.substring(8, 10), 16), parseInt(payload.substring(10, 12), 16) ), unit: "�C" }; var humidity = { variable: "humidity", value: getHumidity(parseInt(payload.substring(12, 14), 16)), unit: "%" }; var pressure = { variable: "pressure", value: getPressure( parseInt(payload.substring(14, 16), 16), parseInt(payload.substring(16, 18), 16), parseInt(payload.substring(18, 20), 16) ), unit: "hPa" }; var lux = { variable: "lux", value: Number(parseUnsignedShort(payload.substring(20, 24))).toFixed(), unit: "lx" }; var voc = { variable: "voc", value: Number( 0x00000000 | (payloadToByteArray[12] & 0x000000ff) | ((payloadToByteArray[13] << 8) & 0x0000ff00) | ((payloadToByteArray[14] << 16) & 0x00ff0000) ).toFixed(), unit: "IAQ/ppb" }; return [date, temperature, humidity, pressure, lux, voc]; } } function parseCo2(payload, isNew) { var uplinkId = payload.substring(0, 2); if (!isNew) { if (uplinkId.toUpperCase() === "0E") { var m1 = parseCo2Measurement(payload.substring(2, 34), isNew); var m2 = parseCo2Measurement(payload.substring(34, 66), isNew); var battery = { variable: "battery", value: Number(parseInt(payload.substring(66, 68), 16)).toFixed(), unit: "%" }; var rfu = { variable: "rfu", value: payload.substring(68) }; return [].concat(m1, m2, [battery, rfu]); } else { return null; } } else { if (uplinkId.toUpperCase() === "13") { var m1 = parseCo2Measurement(payload.substring(2, 36), isNew); var m2 = parseCo2Measurement(payload.substring(36, 70), isNew); var battery = { variable: "battery", value: Number(parseInt(payload.substring(70, 72), 16)).toFixed(), unit: "%" }; var rfu = { variable: "rfu", value: payload.substring(72) }; return [].concat(m1, m2, [battery, rfu]); } else { return null; } } } function parseCo2Measurement(payload, isNew) { if (!isNew) { var date = { variable: "date", value: parseDate(payload.substring(0, 8)) }; var temperature = { variable: "temperature", value: getTemperature( parseInt(payload.substring(8, 10), 16), parseInt(payload.substring(10, 12), 16) ), unit: "�C" }; var humidity = { variable: "humidity", value: getHumidity(parseInt(payload.substring(12, 14), 16)), unit: "%" }; var pressure = { variable: "pressure", value: getPressure( parseInt(payload.substring(14, 16), 16), parseInt(payload.substring(16, 18), 16), parseInt(payload.substring(18, 20), 16) ), unit: "hPa" }; var lux = { variable: "lux", value: Number(parseUnsignedShort(payload.substring(20, 24))).toFixed(), unit: "lx" }; var voc = { variable: "voc", value: Number(parseUnsignedShort(payload.substring(24, 28))).toFixed(), unit: "IAQ/ppb" }; var co2 = { variable: "co2", value: Number(parseSignedShort(payload.substring(28, 32))).toFixed(), unit: "ppm" }; return [date, temperature, humidity, pressure, lux, voc, co2]; } else { var payloadToByteArray = hexStringToByteArray(payload); var date = { variable: "date", value: parseDate(payload.substring(0, 8)) }; var temperature = { variable: "temperature", value: getTemperature( parseInt(payload.substring(8, 10), 16), parseInt(payload.substring(10, 12), 16) ), unit: "�C" }; var humidity = { variable: "humidity", value: getHumidity(parseInt(payload.substring(12, 14), 16)), unit: "%" }; var pressure = { variable: "pressure", value: getPressure( parseInt(payload.substring(14, 16), 16), parseInt(payload.substring(16, 18), 16), parseInt(payload.substring(18, 20), 16) ), unit: "hPa" }; var lux = { variable: "lux", value: Number(parseUnsignedShort(payload.substring(20, 24))).toFixed(), unit: "lx" }; var voc = { variable: "voc", value: Number( 0x00000000 | (payloadToByteArray[12] & 0x000000ff) | ((payloadToByteArray[13] << 8) & 0x0000ff00) | ((payloadToByteArray[14] << 16) & 0x00ff0000) ).toFixed(), unit: "IAQ/ppb" }; var co2 = { variable: "co2", value: Number(parseSignedShort(payload.substring(30, 34))).toFixed(), unit: "ppm" }; return [date, temperature, humidity, pressure, lux, voc, co2]; } } function parseReportData(payload) { var uplinkId = payload.substring(0, 2); var content; if (uplinkId.toUpperCase() === "0B") { switch (payload.substring(2, 3)) { case "1": case "4": content = parseModBus(payload); break; case "2": switch (payload.substring(4, 6)) { case "00": content = parseWeather(payload); break; case "01": content = parsePM(payload); break; default: content = null; break; } break; case "3": content = parseTERPM(payload); break; default: content = null; break; } } else { return null; } return content; } function parseModBus(payloads) { var splitted = payloads.split("\n"); //payloads must be on diffrent lines or change the \n split character var m = []; var dP = ""; for (var i = 0; i < splitted.length; i++) { var payload = splitted[i]; var uplinkId = payload.substring(0, 2); if (uplinkId.toUpperCase() === "0B") { var payloadToByteArray = hexStringToByteArray(payload); payloadToByteArray = payloadToByteArray.slice(2); if (payload.substring(2, 3) === "1" || payload.substring(2, 3) === "4") { var id = Number(payloadToByteArray[0]).toFixed(); var frameId = { variable: "frameId", value: id }; if (payload.length === 12) { var e = ""; switch (payload.substring(10, 12).toUpperCase()) { case "05": e = "Configuration error"; break; case "07": e = "Error reading internal configuration"; break; case "7F": e = "Command not implemented"; break; case "CC": e = "Communication error"; break; } var error = { variable: "error", value: e }; m.push.apply(m, [frameId, error]); } else { var d = id === "0" ? payload.substring(10) : payload.substring(6); var data = { variable: "data", value: d }; dP += d; if (id === "0") { //length is declared only on first payload var length = { variable: "length", value: Number( ((payloadToByteArray[2] << 8) & 0x0000ff00) | (payloadToByteArray[1] & 0x000000ff) ).toFixed() }; m.push.apply(m, [frameId, length, data]); } else { m.push.apply(m, [frameId, data]); } } } } } var dataPayload = { variable: "dataPayload", value: dP }; return [].concat(m, [dataPayload]); } function parsePM(payload) { var uplinkId = payload.substring(0, 2); if (uplinkId.toUpperCase() === "0B") { var payloadToByteArray = hexStringToByteArray(payload); payloadToByteArray = payloadToByteArray.slice(3); if (payload.substring(2, 3) === "2" && payload.substring(4, 6) === "01") { var date = { variable: "date", value: parseDate(payload.substring(6, 14)) }; var pm1 = { variable: "pm1", value: Number( (payloadToByteArray[4] & 0xff) + ((payloadToByteArray[5] << 8) & 0xff00) ).toFixed(), unit: "micro g/m3" }; var pm25 = { variable: "pm25", value: Number( (payloadToByteArray[6] & 0xff) + ((payloadToByteArray[7] << 8) & 0xff00) ).toFixed(), unit: "micro g/m3" }; var pm10 = { variable: "pm10", value: Number( (payloadToByteArray[8] & 0xff) + ((payloadToByteArray[9] << 8) & 0xff00) ).toFixed(), unit: "micro g/m3" }; return [date, pm1, pm25, pm10]; } else { return null; } } else { return null; } } function parseTERPM(payload) { var uplinkId = payload.substring(0, 2); if (uplinkId.toUpperCase() === "0B") { var payloadToByteArray = hexStringToByteArray(payload); payloadToByteArray = payloadToByteArray.slice(3); if (payload.substring(2, 3) === "3" && payload.substring(4, 6) === "00") { var date = { variable: "date", value: parseDate(payload.substring(6, 14)) }; var temperature = { variable: "temperature", value: getTemperature(payloadToByteArray[4], payloadToByteArray[5]), unit: "�C" }; var humidity = { variable: "humidity", value: getHumidity(payloadToByteArray[6]), unit: "%" }; var pressure = { variable: "pressure", value: getPressure( payloadToByteArray[7], payloadToByteArray[8], payloadToByteArray[9] ), unit: "hPa" }; var pm1 = { variable: "pm1", value: Number( (payloadToByteArray[10] & 0xff) + ((payloadToByteArray[11] << 8) & 0xff00) ).toFixed(), unit: "micro g/m3" }; var pm25 = { variable: "pm25", value: Number( (payloadToByteArray[12] & 0xff) + ((payloadToByteArray[13] << 8) & 0xff00) ).toFixed(), unit: "micro g/m3" }; var pm10 = { variable: "pm10", value: Number( (payloadToByteArray[14] & 0xff) + ((payloadToByteArray[15] << 8) & 0xff00) ).toFixed(), unit: "micro g/m3" }; if (payload.length <= 38) { return [date, temperature, humidity, pressure, pm1, pm25, pm10]; } else { var battery = { variable: "battery", value: Number(parseInt(payloadToByteArray[16])).toFixed(), unit: "%" }; return [ date, temperature, humidity, pressure, pm1, pm25, pm10, battery ]; } } else { return null; } } else { return null; } } function parseWeather(payload) { var uplinkId = payload.substring(0, 2); if (uplinkId.toUpperCase() === "0B") { var payloadToByteArray = hexStringToByteArray(payload); payloadToByteArray = payloadToByteArray.slice(3); if (payload.substring(2, 3) === "2" && payload.substring(4, 6) === "00") { var pressure = { variable: "pressure", value: getAtmosphericPressure( (((payloadToByteArray[3] << 8) & 0x0000ff00) | (payloadToByteArray[2] & 0x000000ff)) / 1000.0 ), unit: "hPa" }; var outsideTemperature = { variable: "outsideTemperature", value: getFahrenheitToCelsius( (((payloadToByteArray[5] << 8) & 0x0000ff00) | (payloadToByteArray[4] & 0x000000ff)) / 10.0 ), unit: "�C" }; var windSpeed = { variable: "windSpeed", value: getWindSpeed(payloadToByteArray[6] & 0x00ff), unit: "m/s" }; var tenMinutesAvgWindSpeed = { variable: "tenMinutesAvgWindSpeed", value: getWindSpeed(payloadToByteArray[7] & 0x00ff), unit: "m/s" }; var windDirection = { variable: "windDirection", value: Number( ((payloadToByteArray[9] << 8) & 0x0000ff00) | (payloadToByteArray[8] & 0x000000ff) ).toFixed(2), unit: "�" }; var outsideHumidity = { variable: "outsideHumidity", value: Number(payloadToByteArray[10] & 0x00ff).toFixed(2), unit: "%" }; var rainRate = { variable: "rainRate", value: getRainRate( ((payloadToByteArray[12] << 8) & 0x0000ff00) | (payloadToByteArray[11] & 0x000000ff) ), unit: "mm/h" }; var uv = { variable: "uv", value: Number(payloadToByteArray[13] & 0x00ff).toFixed(2) }; var solarRadiation = { variable: "solarRadiation", value: Number( ((payloadToByteArray[15] << 8) & 0x0000ff00) | (payloadToByteArray[14] & 0x000000ff) ).toFixed(2), unit: "W/m�" }; var dayRain = { variable: "dayRain", value: getRainRate( ((payloadToByteArray[17] << 8) & 0x0000ff00) | (payloadToByteArray[16] & 0x000000ff) ) }; var dayET = { variable: "dayET", value: getET( ((payloadToByteArray[19] << 8) & 0x0000ff00) | (payloadToByteArray[18] & 0x000000ff) ), unit: "mm" }; var soilMoistures = []; for (var i = 0; i < 4; i++) { var soilMoisture = { variable: "soilMoisture", value: Number(payloadToByteArray[20 + i]).toFixed(2), unit: "centibar" }; soilMoistures.push(soilMoisture); } var leafWetnesses = []; for (var i = 0; i < 4; i++) { var leafWetness = { variable: "leafWetness", value: Number(payloadToByteArray[24 + i]) }; leafWetnesses.push(leafWetness); } var forecastIcons = { variable: "forecastIcons", value: Number(payloadToByteArray[28]).toFixed() }; var barTrend = { variable: "barTrend", value: Number(payloadToByteArray[29]).toFixed(2) }; return [ pressure, outsideTemperature, windSpeed, tenMinutesAvgWindSpeed, windDirection, outsideHumidity, rainRate, uv, solarRadiation, dayRain, dayET, soilMoistures, leafWetnesses, forecastIcons, barTrend ]; } } else { return null; } } function getAtmosphericPressure(value) { var pressure = value * 33.863886666667; return Number(pressure).toFixed(2); } function getFahrenheitToCelsius(value) { var celsius = (value - 32) / 1.8; return Number(celsius).toFixed(2); } function getWindSpeed(value) { var windSpeed = value * 0.44704; return Number(windSpeed).toFixed(2); } function getRainRate(value) { var rainRate = value * 0.2; return Number(rainRate).toFixed(2); } function getET(value) { var et = (value / 1000) * 25.4; return Number(et).toFixed(2); } function parseDate(payload) { var date = new Date(); var binary = Number(parseInt(reverseBytes(payload), 16)) .toString(2) .padStart(32, "0"); var year = parseInt(binary.substring(0, 7), 2) + 2000; var month = parseInt(binary.substring(7, 11), 2); var day = parseInt(binary.substring(11, 16), 2); var hour = parseInt(binary.substring(16, 21), 2); var minute = parseInt(binary.substring(21, 27), 2); var second = parseInt(binary.substring(27, 32), 2) * 2; date = new Date( year, month - 1, day, hour, minute, second, 0 ).toLocaleString(); return date; } function getHumidity(lo) { var humidity = (((((0 & 0xff) << 8) | (lo & 0xff)) << 16) >> 16) / 2; return Number(humidity).toFixed(2); } function getPressure(lo, mi, hi) { var pressure = String( (lo & 0xff) + ((mi << 8) & 0xff00) + ((hi << 16) & 0xff0000) ).padStart(3); pressure = pressure.substring(0, pressure.length - 2) + "." + pressure.substring(pressure.length - 2); return Number(pressure).toFixed(2); } function getTemperature(lo, hi) { var temperature = String( (((lo & 0xff) + ((hi << 8) & 0xff00)) << 16) >> 16 ).padStart(3); temperature = temperature.substring(0, temperature.length - 2) + "." + temperature.substring(temperature.length - 2); return Number(temperature).toFixed(2); } function parseUnsignedShort(bytes) { bytes = reverseBytes(bytes); var rno = hexStringToByteArray(bytes); var n = 0; if (rno.length === 2) { n = ((rno[0] << 8) & 0x0000ff00) | ((rno[1] << 0) & 0x000000ff); } return n; } function parseSignedShort(bytes) { bytes = reverseBytes(bytes); var rno = hexStringToByteArray(bytes); var n = 0; if (rno.length === 2) { var n = (((rno[0] << 8) | rno[1]) << 16) >> 16; } return n; } function hexStringToByteArray(s) { for (var bytes = [], c = 0; c < s.length; c += 2) { bytes.push(parseInt(s.substr(c, 2), 16)); } return bytes; } function reverseBytes(bytes) { var reversed = bytes; if (bytes.length % 2 === 0) { reversed = ""; for (var starting = 0; starting + 2 <= bytes.length; starting += 2) { reversed = bytes.substring(starting, starting + 2) + reversed; } } return reversed; }
This codec is sourced from The Things Network. All rights belong to The Things Network.
This codec is licensed under the GNU General Public License v3 (GPL v3). Modifications, if any, are clearly marked. You are free to use, modify, and distribute the codec under the terms of GPL v3.