Uplink Payload Formatter
Payload Formatter Script
// Evolion Research Uplink Payload Formatter Version 1.3.4
function decodeUplink(input) {
let addFraction = (n, d) => n / (10 ** d);
let decodeReadingId = (d) => d >> 2;
let decodeStatisticsId = (d) => d & 0x03;
let data = {};
// Events
const events = {
2: "telemetry",
3: "telemetry alert",
4: "telemetry log"
};
// Reading structures (type, signed, fraction)
const readingStructures = {
0: ["null", false, 0],
1: ["temperature", true, 4],
2: ["relative_humidity", false, 4],
3: ["pressure", false, 4],
4: ["gas_estimation", false, 4],
5: ["iaq_index", false, 4],
6: ["voc", false, 4],
7: ["nox_index", false, 2],
8: ["co2_concentration", false, 2],
9: ["particulate_matter", false, 2],
10: ["dew_point", true, 4],
11: ["vpd", false, 4],
12: ["visible_light", false, 0],
13: ["ir_light", false, 0],
14: ["uv_index", false, 4],
15: ["light_custom", false, 0],
16: ["angle", true, 4],
17: ["vibration", false, 4],
18: ["rpm", false, 2],
19: ["speed", false, 4],
20: ["latitude", true, 4],
21: ["longitude", true, 4],
22: ["altitude", true, 2],
23: ["heading", true, 4],
24: ["weight", false, 0],
25: ["distance", false, 0],
26: ["contact", false, 0],
27: ["movement_detection", false, 0],
28: ["wind_speed", false, 4],
29: ["wind_direction", true, 4],
30: ["precipitation", false, 2],
31: ["soil_moisture", false, 4],
32: ["water_flow", false, 0],
33: ["water_conductivity", false, 0],
34: ["ph", false, 4],
35: ["salinity", false, 4],
36: ["tds", false, 4],
37: ["water_custom_1", false, 0],
38: ["water_custom_2", false, 0],
39: ["water_custom_3", false, 0],
40: ["voltage", false, 4],
41: ["current", false, 2],
42: ["frequency", false, 4],
43: ["real_power", false, 2],
44: ["apparent_power", false, 2],
45: ["reactive_power", false, 2],
46: ["power_factor", false, 4],
47: ["phase_angle", true, 4],
48: ["custom_1", false, 0],
49: ["custom_2", false, 0],
50: ["custom_3", false, 0],
51: ["custom_4", false, 0],
52: ["sound_pressure", true, 4],
53: ["solar_radiation", false, 2],
54: ["probe_temperature", true, 3],
55: ["probe_pressure", false, 2],
56: ["uid", false, 0],
57: ["time", false, 0],
58: ["battery_voltage", false, 4],
59: ["solar_voltage", false, 4],
60: ["counter", false, 0],
61: ["duty_cycle", false, 4],
62: ["analog_input", false, 4],
63: ["digital_input", false, 0]
};
// Three-letter statistics type abbreviations
const statistics = {
0: "",
1: "_avr",
2: "_min",
3: "_max"
};
// Decode frame port
data.event = events[input.fPort];
// Sensor data decoding for "telemetry" and "telemetry alert" frame ports
if (input.fPort == 2 || input.fPort == 3) {
for (let i = 0; i < input.bytes.length; i += 4) {
const readingId = decodeReadingId(input.bytes[i]);
// Stop decoding if data type is null
if (readingId == 0)
break;
// Decode digital input channels
else if (readingId == 63) {
for (let j = 0; j < 8; j++) {
const prop = (input.bytes[i + 3] >> j) & 1;
data[`di${j + 1}`] = prop == 1 ? true : false;
}
}
// Decode other data types
else {
// Decode data type
let prop;
// If multiple-read data type, e.g., vlt_1, vlt_2
if ([4,9,16,17,18,40,41,42,43,44,45,46,47,54,55,60,61,62].includes(readingId))
{
for (let j = 1; j <= 64; j++) {
let tempProp = `${readingStructures[readingId][0]}_${j}${statistics[decodeStatisticsId(input.bytes[i])]}`;
if (data[tempProp] == null) {
prop = tempProp;
break;
}
}
}
// If single-read data type, e.g., tmp, pre
else {
prop = `${readingStructures[readingId][0]}${statistics[decodeStatisticsId(input.bytes[i])]}`;
}
// Decode value
const value = (input.bytes[i + 1] << 16) + (input.bytes[i + 2] << 8) + (input.bytes[i + 3]);
// Get the fraction
const fraction = readingStructures[readingId][2];
// Check if the data type is signed
if (readingStructures[readingId][1]) {
// Decode signed data
const negative = ((value >> 23) & 1) == 1 ? true : false;
data[prop] = addFraction(negative ? -((~(value - 1)) & 0xFFFFFF) : value, fraction);
}
else {
// Decode unsigned data
data[prop] = addFraction(value, fraction);
}
}
}
}
return {
data: data
};
}