Hi there, Well almost.
// Mapping measurement IDs to human-readable field names
var measurementIdToFieldName = {
‘4097’: ‘AIR_TEMPERATURE’,
‘4098’: ‘AIR_HUMIDITY’,
‘4099’: ‘LIGHT_INTENSITY’,
‘4190’: ‘UV_INDEX’,
‘4105’: ‘WIND_SPEED’,
‘4104’: ‘WIND_DIRECTION’,
‘4113’: ‘RAIN_GAUGE’,
‘4101’: ‘BAROMETRIC_PRESSURE’,
‘4191’: ‘PEAK_WIND_GUST’,
‘4213’: ‘RAIN_ACCUMULATION’,
‘4120’: ‘BATTERY’,
};
// Function to decode the SenseCAP payload and extract the data
function decodeSensecapPayload(data) {
if (!data || data.length === 0) {
console.log(“Input data is empty or undefined.”);
return { err: 1, messages: [], valid: false };
}
// Convert byte array to hex string
data = data2HexString(data).toUpperCase();
console.log("Payload in hex format: " + data);
var result = {
err: 0,
payload: data,
valid: true,
messages: [],
};
var splitArray = dataSplit(data);
console.log("Split data array: " + JSON.stringify(splitArray));
var decoderArray = [];
for (var i = 0; i < splitArray.length; i++) {
var item = splitArray[i];
var dataId = item.dataId;
var dataValue = item.dataValue;
console.log("Processing dataId: " + dataId + " with dataValue: " + dataValue);
var messages = dataIdAndDataValueJudge(dataId, dataValue);
console.log("Decoded messages: " + JSON.stringify(messages));
if (messages.length > 0) {
decoderArray.push(messages);
}
}
result.messages = decoderArray;
return result;
}
// Function to split the incoming data into manageable pieces
function dataSplit(data) {
var frameArray = [];
console.log("Splitting data: " + data);
if (!data || data.length === 0) {
console.log(“No data to split.”);
return frameArray;
}
while (data.length > 2) {
var dataId = data.substring(0, 2);
var dataValue = ‘’;
var dataObj = {};
console.log("Processing dataId: " + dataId);
switch (dataId) {
case '01':
case '4A':
dataValue = data.substring(2, 22);
data = data.substring(22);
break;
case '02':
case '4B':
dataValue = data.substring(2, 18);
data = data.substring(18);
break;
case '03':
dataValue = data.substring(2, 4);
data = data.substring(4);
break;
case '4C':
dataValue = data.substring(2, 14);
data = data.substring(14);
break;
default:
console.log("Unknown dataId: " + dataId);
dataValue = '';
data = '';
break;
}
if (dataValue) {
dataObj = {
dataId: dataId,
dataValue: dataValue,
};
frameArray.push(dataObj);
}
}
console.log("Frame array after splitting: " + JSON.stringify(frameArray));
return frameArray;
}
// Function to match dataId and dataValue to their corresponding measurements
function dataIdAndDataValueJudge(dataId, dataValue) {
var messages = [];
switch (dataId) {
case ‘01’:
case ‘4A’:
var temperature = loraWANV2DataFormat(dataValue.substring(0, 4), 10);
var humidity = loraWANV2DataFormat(dataValue.substring(4, 6));
var illumination = loraWANV2DataFormat(dataValue.substring(6, 14));
var uvIndex = loraWANV2DataFormat(dataValue.substring(14, 16), 10);
var windSpeed = loraWANV2DataFormat(dataValue.substring(16, 20), 10);
messages.push({
measurementValue: temperature,
measurementId: '4097',
type: 'Air Temperature',
});
messages.push({
measurementValue: humidity,
measurementId: '4098',
type: 'Air Humidity',
});
messages.push({
measurementValue: illumination,
measurementId: '4099',
type: 'Light Intensity',
});
messages.push({
measurementValue: uvIndex,
measurementId: '4190',
type: 'UV Index',
});
messages.push({
measurementValue: windSpeed,
measurementId: '4105',
type: 'Wind Speed',
});
break;
case '02':
case '4B':
var windDirection = loraWANV2DataFormat(dataValue.substring(0, 4));
var rainfall = loraWANV2DataFormat(dataValue.substring(4, 12), 1000);
var airPressure = loraWANV2DataFormat(dataValue.substring(12, 16), 0.1);
messages.push({
measurementValue: windDirection,
measurementId: '4104',
type: 'Wind Direction Sensor',
});
messages.push({
measurementValue: rainfall,
measurementId: '4113',
type: 'Rain Gauge',
});
messages.push({
measurementValue: airPressure,
measurementId: '4101',
type: 'Barometric Pressure',
});
break;
case '4C':
var peakWind = loraWANV2DataFormat(dataValue.substring(0, 4), 10);
var rainAccumulation = loraWANV2DataFormat(dataValue.substring(4, 12), 1000);
messages.push({
measurementValue: peakWind,
measurementId: '4191',
type: 'Peak Wind Gust',
});
messages.push({
measurementValue: rainAccumulation,
measurementId: '4213',
type: 'Rain Accumulation',
});
break;
case '03':
var battery = loraWANV2DataFormat(dataValue);
messages.push({
measurementValue: battery,
measurementId: '4120',
type: 'Battery',
});
break;
default:
console.log("Unrecognized dataId: " + dataId);
break;
}
return messages;
}
// Helper functions for data formatting
function loraWANV2DataFormat(str, divisor) {
divisor = divisor || 1;
var strReverse = bigEndianTransform(str);
var str2 = toBinary(strReverse);
if (str2[0] === ‘1’) {
var arr = str2.split(’’).map(function (bit) {
return bit === ‘1’ ? ‘0’ : ‘1’;
});
str2 = (parseInt(arr.join(’’), 2) + 1).toString();
return parseFloat(’-’ + str2 / divisor);
}
return parseInt(str2, 2) / divisor;
}
function bigEndianTransform(data) {
var arr = [];
for (var i = 0; i < data.length; i += 2) {
arr.push(data.substring(i, i + 2));
}
return arr;
}
function toBinary(arr) {
return arr.map(function (byte) {
var binaryString = parseInt(byte, 16).toString(2);
while (binaryString.length < 8) {
binaryString = ‘0’ + binaryString;
}
return binaryString;
}).join(’’);
}
function data2HexString(arrBytes) {
if (!arrBytes || arrBytes.length === 0) {
console.log(“No data in arrBytes to convert.”);
return “”;
}
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;
}
console.log("Hex string after conversion: " + str);
return str;
}
// Main Decoder function
function Decoder(bytes, port) {
console.log("Input bytes: " + bytes);
// Check if the input bytes are empty and use a test payload if they are
if (!bytes || bytes.length === 0) {
console.log(“Input bytes are empty. Using a test payload.”);
// Use a sample payload for testing
bytes = [0x4A, 0xFF, 0xE4, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4B, 0x00, 0xD8, 0x00, 0x00, 0x00, 0x23, 0xDD, 0x4C, 0x00, 0x00, 0x00, 0x00];
console.log("Test payload: " + bytes);
}
// Proceed with decoding the payload
var decodedSensecapFields = decodeSensecapPayload(bytes).messages;
var datacakeFields = [];
if (decodedSensecapFields.length === 0) {
console.log(“No decoded fields available, returning empty array.”);
return datacakeFields;
}
for (var i = 0; i < decodedSensecapFields.length; i++) {
var arrayEntry = decodedSensecapFields[i];
arrayEntry.forEach(function (entry) {
// Map measurementId to a human-readable field name
var fieldName = measurementIdToFieldName[entry.measurementId] || ‘MEASUREMENT_ID_’ + entry.measurementId;
datacakeFields.push({
field: fieldName,
value: entry.measurementValue,
});
});
}
console.log("Decoded fields to return to Datacake: " + JSON.stringify(datacakeFields));
return datacakeFields;
}
WOW, That looks better
Cool,
GL PJ