Sensor
function decodeUplink(input) { /** * @reference https://github.com/myDevicesIoT/cayenne-docs/blob/master/docs/LORA.md * * * Type IPSO LPP Hex Data Size Data Resolution per bit * Digital Input 3200 0 0 1 1 * Digital Output 3201 1 1 1 1 * Analog Input 3202 2 2 2 0.01 Signed * Analog Output 3203 3 3 2 0.01 Signed * Counter Input 5501 4 4 2 1 event Unsigned MSB * Illuminance Sensor 3301 101 65 2 1 Lux Unsigned MSB * Presence Sensor 3302 102 66 1 1 * Temperature Sensor 3303 103 67 2 0.1 °C Signed MSB * Humidity Sensor 3304 104 68 1 0.5 % Unsigned * Accelerometer 3313 113 71 6 0.001 G Signed MSB per axis * Barometer 3315 115 73 2 0.1 hPa Unsigned MSB * Gyrometer 3334 134 86 6 0.01 °/s Signed MSB per axis * GPS Location 3336 136 88 9 Latitude : 0.0001 ° Signed MSB * Longitude : 0.0001 ° Signed MSB * Altitude : 0.01 meter Signed MSB SEZO specific types * Loudness - 200 C8 2 0.01 Signed * Particle Matter PM1 - 201 C9 1 1 * Particle Matter PM2.5 - 202 CA 1 1 * Particle Matter PM4 - 203 CB 1 1 * Particle Matter PM10 - 204 CC 1 1 * Index air quality - 205 CD 2 1 */ var sensor_types = { 0 : {'size': 1, 'name': 'digital_input', 'readable_name': 'Digital Input', 'signed': false, 'divisor': 1,}, 1 : {'size': 1, 'name': 'digital_output', 'readable_name': 'Digital Output', 'signed': false, 'divisor': 1,}, 2 : {'size': 2, 'name': 'analog_input', 'readable_name': 'Analog Input', 'signed': true , 'divisor': 100,}, 3 : {'size': 2, 'name': 'analog_output', 'readable_name': 'Analog Output', 'signed': true , 'divisor': 100,}, 4 : {'size': 2, 'name': 'counter_input', 'readable_name': 'Counter Input', 'signed': false, 'divisor': 1,}, 101: {'size': 2, 'name': 'luminosity', 'readable_name': 'Illuminance Sensor', 'signed': false, 'divisor': 1,}, 102: {'size': 1, 'name': 'presence', 'readable_name': 'Presence Sensor', 'signed': false, 'divisor': 1,}, 103: {'size': 2, 'name': 'temperature', 'readable_name': 'Temperature Sensor', 'signed': true , 'divisor': 10,}, 104: {'size': 1, 'name': 'humidity', 'readable_name': 'Humidity Sensor', 'signed': false, 'divisor': 2,}, 113: {'size': 6, 'name': 'accelerometer', 'readable_name': 'Accelerometer', 'signed': true , 'divisor': [1000,1000,1000],}, 114: {'size': 6, 'name': 'magnetometer', 'readable_name': 'Magnetometer', 'signed': true , 'divisor': [1000,1000,1000],}, 115: {'size': 2, 'name': 'barometer', 'readable_name': 'Barometer', 'signed': false, 'divisor': 10,}, 134: {'size': 6, 'name': 'gyrometer', 'readable_name': 'Gyrometer', 'signed': true , 'divisor': 100,}, 136: {'size': 9, 'name': 'gps_location', 'readable_name': 'GPS Location', 'signed': false, 'divisor': [10000,10000,100],}, //SEZO specific types 200: {'size': 2, 'name': 'battery', 'readable_name': 'Battery', 'signed': false , 'divisor': 100,}, 201: {'size': 2, 'name': 'loudness', 'readable_name': 'Loudness', 'signed': true , 'divisor': 100,}, 202: {'size': 1, 'name': 'PM1', 'readable_name': 'PM1', 'signed': false, 'divisor': 1,}, 203: {'size': 1, 'name': 'PM2_5', 'readable_name': 'PM2_5', 'signed': false, 'divisor': 1,}, 204: {'size': 1, 'name': 'PM4', 'readable_name': 'PM4', 'signed': false, 'divisor': 1,}, 205: {'size': 1, 'name': 'PM10', 'readable_name': 'PM10', 'signed': false, 'divisor': 1,}, 206: {'size': 2, 'name': 'IAQ', 'readable_name': 'IAQ', 'signed': false, 'divisor': 1,}, 207: {'size': 2, 'name': 'UV', 'readable_name': 'UV Sensor', 'signed': false, 'divisor': 1,}, 208: {'size': 2, 'name': 'EXT_temperature', 'readable_name': 'External Temperature Sensor', 'signed': true , 'divisor': 10,}, 209: {'size': 1, 'name': 'EXT_humidity', 'readable_name': 'External Humidity Sensor', 'signed': false, 'divisor': 2,}, 210: {'size': 2, 'name': 'EXT_barometer', 'readable_name': 'Barometer', 'signed': false, 'divisor': 10,}, 211: {'size': 1, 'name': 'IAQ_accuracy', 'readable_name': 'IAQ accuracy', 'signed': false, 'divisor': 1,} }; function arrayToDecimal(stream, is_signed, divisor) { var value = 0; for (var i = 0; i < stream.length; i++) { if (stream[i] > 0xFF) throw 'Byte value overflow!'; value = (value << 8) | stream[i]; } if (is_signed) { var edge = 1 << (stream.length) * 8; // 0x1000.. var max = (edge - 1) >> 1; // 0x0FFF.. >> 1 value = (value > max) ? value - edge : value; } value /= divisor; return value; } var bytes = input.bytes; var sensors = {}; var i = 0; while (i < bytes.length) { // console.log(i); // console.log(typeof bytes[i]) // console.log(bytes[i].toString()) var s_no = bytes[i++]; var s_type = bytes[i++]; if (typeof sensor_types[s_type] == 'undefined') throw 'Sensor type error!: ' + s_type; var s_size = sensor_types[s_type].size; var s_name = sensor_types[s_type].name; switch (s_type) { case 0 : // Digital Input case 1 : // Digital Output case 2 : // Analog Input case 3 : // Analog Output case 4 : // Counter input case 101: // Illuminance Sensor case 102: // Presence Sensor case 103: // Temperature Sensor case 104: // Humidity Sensor case 115: // Barometer case 134: // Gyrometer case 200: // Battery case 201: // Loudness case 202: // PM1 case 203: // PM2.5 case 204: // PM4 case 205: // PM10 case 206: // IAQ case 207: // UV radiation case 208: // External temperature case 209: // External Relative Humidity case 210: // External Pressure case 211: // External Pressure var s_value = arrayToDecimal(bytes.slice(i, i+s_size), is_signed = sensor_types[s_type].signed, divisor = sensor_types[s_type].divisor); break; case 113: // Accelerometer case 114: // Magnetometer var s_value = { 'x': arrayToDecimal(bytes.slice(i+0, i+2), is_signed=sensor_types[s_type].signed, divisor=sensor_types[s_type].divisor[0]), 'y': arrayToDecimal(bytes.slice(i+2, i+4), is_signed=sensor_types[s_type].signed, divisor=sensor_types[s_type].divisor[1]), 'z': arrayToDecimal(bytes.slice(i+4, i+6), is_signed=sensor_types[s_type].signed, divisor=sensor_types[s_type].divisor[2])}; break; case 136: // GPS Location var s_value = { 'latitude': arrayToDecimal(bytes.slice(i+0, i+3), is_signed=sensor_types[s_type].signed, divisor=sensor_types[s_type].divisor[0]), 'longitude': arrayToDecimal(bytes.slice(i+3, i+6), is_signed=sensor_types[s_type].signed, divisor=sensor_types[s_type].divisor[1]), 'altitude': arrayToDecimal(bytes.slice(i+6, i+9), is_signed=sensor_types[s_type].signed, divisor=sensor_types[s_type].divisor[2])}; break; } if (typeof(sensors[s_name]) === "undefined") { sensors[s_name] = {}; } sensors[s_name] = s_value; // sensors[s_no] = {'type': s_type, 'type_name': s_name, 'value': s_value }; i += s_size; } return {data: sensors}; } function encodeDownlink(input) { var i = colors.indexOf(input.data.led); if (i === -1) { return { errors: ['invalid LED color'], }; } return { // LoRaWAN FPort used for the downlink message fPort: 2, // Encoded bytes bytes: [i], }; } function decodeDownlink(input) { switch (input.fPort) { case 2: return { // Decoded downlink (must be symmetric with encodeDownlink) data: { led: colors[input.bytes[0]], }, }; default: return { errors: ['invalid FPort'], }; } }
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.