SEEED - Sensecap S2120 8 In 1 Weather Sensor

Sensor

Codec Description

Codec for SEEED - Sensecap-S2120-8-In-1-Weather-Sensor (v1.0.0).

Codec Preview

/* eslint-disable no-throw-literal */ /* eslint-disable vars-on-top */ /* eslint-disable no-var */ /* eslint-disable radix */ /* eslint-disable no-case-declarations */ /* eslint-disable no-unused-vars */ /* eslint-disable no-plusplus */ /* eslint-disable no-use-before-define */ /** * Entry, decoder.js */ function decodeUplink(input, port) { // init var bytes = bytes2HexString(input).toLocaleUpperCase(); const result = { err: 0, payload: bytes, valid: true, messages: [], }; const splitArray = dataSplit(bytes); // data decoder const decoderArray = []; for (let i = 0; i < splitArray.length; i++) { const item = splitArray[i]; const { dataId } = item; const { dataValue } = item; const messages = dataIdAndDataValueJudge(dataId, dataValue); decoderArray.push(messages); } result.messages = decoderArray; console.log(JSON.stringify(result.messages)); return { data: result }; } /** * data splits * @param bytes * @returns {*[]} */ function dataSplit(bytes) { const frameArray = []; for (let i = 0; i < bytes.length; i++) { const remainingValue = bytes; const dataId = remainingValue.substring(0, 2); let dataValue; let dataObj = {}; switch (dataId) { case "01": case "20": case "21": case "30": case "31": case "33": case "40": case "41": case "42": case "43": case "44": case "45": dataValue = remainingValue.substring(2, 22); bytes = remainingValue.substring(22); dataObj = { dataId, dataValue, }; break; case "02": dataValue = remainingValue.substring(2, 18); bytes = remainingValue.substring(18); dataObj = { dataId: "02", dataValue, }; break; case "03": case "06": dataValue = remainingValue.substring(2, 4); bytes = remainingValue.substring(4); dataObj = { dataId, dataValue, }; break; case "05": case "34": dataValue = bytes.substring(2, 10); bytes = remainingValue.substring(10); dataObj = { dataId, dataValue, }; break; case "04": case "10": case "32": case "35": case "36": case "37": case "38": case "39": dataValue = bytes.substring(2, 20); bytes = remainingValue.substring(20); dataObj = { dataId, dataValue, }; break; default: dataValue = "9"; break; } if (dataValue.length < 2) { break; } frameArray.push(dataObj); } return frameArray; } function dataIdAndDataValueJudge(dataId, dataValue) { let messages = []; switch (dataId) { case "01": const temperature = dataValue.substring(0, 4); const humidity = dataValue.substring(4, 6); const illumination = dataValue.substring(6, 14); const uv = dataValue.substring(14, 16); const windSpeed = dataValue.substring(16, 20); messages = [ { measurementValue: loraWANV2DataFormat(temperature, 10), measurementId: "4097", type: "Air Temperature", }, { measurementValue: loraWANV2DataFormat(humidity), measurementId: "4098", type: "Air Humidity", }, { measurementValue: loraWANV2DataFormat(illumination), measurementId: "4099", type: "Light Intensity", }, { measurementValue: loraWANV2DataFormat(uv, 10), measurementId: "4190", type: "UV Index", }, { measurementValue: loraWANV2DataFormat(windSpeed, 10), measurementId: "4105", type: "Wind Speed", }, ]; break; case "02": const windDirection = dataValue.substring(0, 4); const rainfall = dataValue.substring(4, 12); const airPressure = dataValue.substring(12, 16); messages = [ { measurementValue: loraWANV2DataFormat(windDirection), measurementId: "4104", type: "Wind Direction Sensor", }, { measurementValue: loraWANV2DataFormat(rainfall, 1000), measurementId: "4113", type: "Rain Gauge", }, { measurementValue: loraWANV2DataFormat(airPressure, 0.1), measurementId: "4101", type: "Barometric Pressure", }, ]; break; case "03": const Electricity = dataValue; messages = [ { "Battery(%)": loraWANV2DataFormat(Electricity), }, ]; break; case "04": const electricityWhether = dataValue.substring(0, 2); const hwv = dataValue.substring(2, 6); const bdv = dataValue.substring(6, 10); const sensorAcquisitionInterval = dataValue.substring(10, 14); const gpsAcquisitionInterval = dataValue.substring(14, 18); messages = [ { "Battery(%)": loraWANV2DataFormat(electricityWhether), "Hardware Version": `${loraWANV2DataFormat( hwv.substring(0, 2) )}.${loraWANV2DataFormat(hwv.substring(2, 4))}`, "Firmware Version": `${loraWANV2DataFormat( bdv.substring(0, 2) )}.${loraWANV2DataFormat(bdv.substring(2, 4))}`, measureInterval: parseInt(loraWANV2DataFormat(sensorAcquisitionInterval)) * 60, gpsInterval: parseInt(loraWANV2DataFormat(gpsAcquisitionInterval)) * 60, }, ]; break; case "05": const sensorAcquisitionIntervalFive = dataValue.substring(0, 4); const gpsAcquisitionIntervalFive = dataValue.substring(4, 8); messages = [ { measureInterval: parseInt(loraWANV2DataFormat(sensorAcquisitionIntervalFive)) * 60, gpsInterval: parseInt(loraWANV2DataFormat(gpsAcquisitionIntervalFive)) * 60, }, ]; break; case "06": const errorCode = dataValue; let descZh; switch (errorCode) { case "00": descZh = "CCL_SENSOR_ERROR_NONE"; break; case "01": descZh = "CCL_SENSOR_NOT_FOUND"; break; case "02": descZh = "CCL_SENSOR_WAKEUP_ERROR"; break; case "03": descZh = "CCL_SENSOR_NOT_RESPONSE"; break; case "04": descZh = "CCL_SENSOR_DATA_EMPTY"; break; case "05": descZh = "CCL_SENSOR_DATA_HEAD_ERROR"; break; case "06": descZh = "CCL_SENSOR_DATA_CRC_ERROR"; break; case "07": descZh = "CCL_SENSOR_DATA_B1_NO_VALID"; break; case "08": descZh = "CCL_SENSOR_DATA_B2_NO_VALID"; break; case "09": descZh = "CCL_SENSOR_RANDOM_NOT_MATCH"; break; case "0A": descZh = "CCL_SENSOR_PUBKEY_SIGN_VERIFY_FAILED"; break; case "0B": descZh = "CCL_SENSOR_DATA_SIGN_VERIFY_FAILED"; break; case "0C": descZh = "CCL_SENSOR_DATA_VALUE_HI"; break; case "0D": descZh = "CCL_SENSOR_DATA_VALUE_LOW"; break; case "0E": descZh = "CCL_SENSOR_DATA_VALUE_MISSED"; break; case "0F": descZh = "CCL_SENSOR_ARG_INVAILD"; break; case "10": descZh = "CCL_SENSOR_RS485_MASTER_BUSY"; break; case "11": descZh = "CCL_SENSOR_RS485_REV_DATA_ERROR"; break; case "12": descZh = "CCL_SENSOR_RS485_REG_MISSED"; break; case "13": descZh = "CCL_SENSOR_RS485_FUN_EXE_ERROR"; break; case "14": descZh = "CCL_SENSOR_RS485_WRITE_STRATEGY_ERROR"; break; case "15": descZh = "CCL_SENSOR_CONFIG_ERROR"; break; case "FF": descZh = "CCL_SENSOR_DATA_ERROR_UNKONW"; break; default: descZh = "CC_OTHER_FAILED"; break; } messages = [ { measurementId: "4101", type: "sensor_error_event", errCode: errorCode, descZh, }, ]; break; case "10": const statusValue = dataValue.substring(0, 2); const { status, type } = loraWANV2BitDataFormat(statusValue); const sensecapId = dataValue.substring(2); messages = [ { status, channelType: type, sensorEui: sensecapId, }, ]; break; default: break; } return messages; } /** * * data formatting * @param str * @param divisor * @returns {string|number} */ function loraWANV2DataFormat(str, divisor = 1) { const strReverse = bigEndianTransform(str); let str2 = toBinary(strReverse); if (str2.substring(0, 1) === "1") { const arr = str2.split(""); const reverseArr = arr.map((item) => { if (parseInt(item) === 1) { return 0; } return 1; }); str2 = parseInt(reverseArr.join(""), 2) + 1; return `-${str2 / divisor}`; } return parseInt(str2, 2) / divisor; } /** * Handling big-endian data formats * @param data * @returns {*[]} */ function bigEndianTransform(data) { const dataArray = []; for (let i = 0; i < data.length; i += 2) { dataArray.push(data.substring(i, i + 2)); } // array of hex return dataArray; } /** * Convert to an 8-digit binary number with 0s in front of the number * @param arr * @returns {string} */ function toBinary(arr) { const binaryData = arr.map((item) => { let data = parseInt(item, 16).toString(2); const dataLength = data.length; if (data.length !== 8) { for (let i = 0; i < 8 - dataLength; i++) { data = `0${data}`; } } return data; }); const ret = binaryData.toString().replace(/,/g, ""); return ret; } /** * sensor * @param str * @returns {{channel: number, type: number, status: number}} */ function loraWANV2BitDataFormat(str) { const strReverse = bigEndianTransform(str); const str2 = toBinary(strReverse); const channel = parseInt(str2.substring(0, 4), 2); const status = parseInt(str2.substring(4, 5), 2); const type = parseInt(str2.substring(5), 2); return { channel, status, type }; } /** * channel info * @param str * @returns {{channelTwo: number, channelOne: number}} */ function loraWANV2ChannelBitFormat(str) { const strReverse = bigEndianTransform(str); const str2 = toBinary(strReverse); const one = parseInt(str2.substring(0, 4), 2); const two = parseInt(str2.substring(4, 8), 2); const resultInfo = { one, two, }; return resultInfo; } /** * data log status bit * @param str * @returns {{total: number, level: number, isTH: number}} */ function loraWANV2DataLogBitFormat(str) { const strReverse = bigEndianTransform(str); const str2 = toBinary(strReverse); const isTH = parseInt(str2.substring(0, 1), 2); const total = parseInt(str2.substring(1, 5), 2); const left = parseInt(str2.substring(5), 2); const resultInfo = { isTH, total, left, }; return resultInfo; } function bytes2HexString(arrBytes) { var str = ""; for (var i = 0; i < arrBytes.length; i++) { var tmp; var num = arrBytes[i]; if (num < 0) { tmp = (255 + num + 1).toString(16); } else { tmp = num.toString(16); } if (tmp.length === 1) { tmp = `0${tmp}`; } str += tmp; } return str; } function toTagoFormat(result) { if (!result.data.messages.length) { return "Payload is not valid"; } const group = String(new Date().getTime()); const arrayToTago = []; console.log(result.data); for (const messages of result.data.messages) { for (const x of messages) { console.log(x); arrayToTago.push({ variable: String(x.type).toLowerCase().replace(/\s/g, "_"), value: x.measurementValue, group, }); } } return arrayToTago; } const payload_raw = payload.find( (x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data" ); if (payload_raw) { try { // Convert the data from Hex to Javascript Buffer. const buffer = Buffer.from(payload_raw.value, "hex"); const payload_aux = toTagoFormat(decodeUplink(buffer)); payload = payload.concat(payload_aux.map((x) => ({ ...x }))); } catch (e) { // Print the error to the Live Inspector. console.error(e); // Return the variable parse_error for debugging. payload = [{ variable: "parse_error", value: e.message }]; } } // console.log(JSON.stringify(payload)); 

This codec is sourced from TagoIO Github. All rights belong to TagoIO Github.

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.

Community Feedback