TTN Smart Sensor (Volley-Boast)

Sensor

Codec Description

Codec for Volley-Boast

Codec Preview

/* istanbul ignore next */ function customDecoder(bytes, fport) { var decoded = Decoder(bytes, fport); //======================================================= // Insert your customization here //======================================================= return decoded; } //=========================================================== // Volley Boast Decoder // // Volley Boast maintains the below code, therefore // DO NOT EDIT BELOW THIS POINT!! //=========================================================== const DECODER_MAJOR_VERSION = 2; const DECODER_MINOR_VERSION = 2; const DECODER_PATCH_VERSION = 1; function decodeUplink(input) { const decoded = Decoder(input.bytes, input.fPort); return decoded; } function Decoder(bytes, fport) { var decoded = {}; var decodedData = {}; var warnings = []; var errors = []; if (fport == 1) { decodedData = parseStandardPayload(bytes); } else if((fport >= 2) && (fport <= 9)) { decodedData = parseModbusStandardPayload(bytes, fport); } else if (fport == 10) { decodedData = parseHeartbeat1p0Payload(bytes); } else if((fport >= 20) && (fport <= 29)) { decodedData = parseHeartbeat2p0Payload(bytes); } else if((fport >= 30) && (fport <= 39)) { decodedData = parseOneAnalogSensorPayload(bytes); } else if((fport >= 40) && (fport <= 49)) { decodedData = parseTwoAnalogSensorsPayload(bytes); } else if((fport >= 50) && (fport <= 59)) { decodedData = parseDigitalSensorsPayload(bytes); } else if((fport >= 60) && (fport <= 69)) { decodedData = parseEventLogPayload(bytes); } else if((fport >= 70) && (fport <= 79)) { decodedData = parseConfigurationPayload(bytes, fport); } else if((fport >= 80) && (fport <= 89)) { decodedData = parseEventLogPayload(bytes); } else if((fport >= 100) && (fport <= 109)) { decodedData = parseModbusGenericPayload(bytes, fport); } else if((fport >= 110) && (fport <= 119)) { decodedData = parseAnalogInputVariableLengthPayload(bytes, fport); } else if((fport >= 120) && (fport <= 129)) { decodedData = parseModbusStandardVariableLengthPayload(bytes, fport); } else { errors.push('unknown FPort') } decoded = addVoboMetadata(decodedData, fport); decoded.warnings = warnings; decoded.errors = errors; return decoded; } function addVoboMetadata(decodedData, fport) { var payload = {}; payload.data = decodedData; var voboType = ""; var payloadType = ""; var portLastDigit = fport % 10; if((portLastDigit == 0) || ((fport >= 1) && (fport < 10))) voboType = "VoBoXX"; else if(portLastDigit == 1) voboType = "VoBoTC"; else if(portLastDigit == 2) voboType = "VoBoXP"; if(fport == 1) payloadType = "Standard"; else if ((fport >= 2) && (fport <= 9)) { payloadType = "Modbus Standard"; } else if(fport == 10) payloadType = "Heartbeat 1.0"; else if((fport >= 20) && (fport <= 29)) { payloadType = "Heartbeat 2.0"; } else if((fport >= 30) && (fport <= 39)) { payloadType = "One Analog Input"; payload.data.analogSensorString0 = lookupAnalogSensorName(voboType, payload.data.sensorNum0); payload.data.engUnitsString0 = lookupUnits(1, payload.data.sensorUnits0); } else if((fport >= 40) && (fport <= 49)) { payloadType = "Two Analog Inputs"; payload.data.analogSensorString0 = lookupAnalogSensorName(voboType, payload.data.sensorNum0); payload.data.engUnitsString0 = lookupUnits(1, payload.data.sensorUnits0); payload.data.analogSensorString1 = lookupAnalogSensorName(voboType, payload.data.sensorNum1); payload.data.engUnitsString1 = lookupUnits(1, payload.data.sensorUnits1); } else if((fport >= 50) && (fport <= 59)) { payloadType = "Digital Inputs"; payload.data.digitalSensorStrings = []; payload.data.digitalSensorData = []; for (let i = 0; i < 16; i++) { let valid = (payload.data.sensorValid0 >> i) & 1; if(valid == 1) { payload.data.digitalSensorStrings.push(lookupDigitalSensorName(voboType, i)); let data = (payload.data.sensorData0 >> i) & 1; payload.data.digitalSensorData.push(data); } } } else if((fport >= 60) && (fport <= 69)) payloadType = "Event Log"; else if((fport >= 70) && (fport <= 79)) payloadType = "Configuration"; else if((fport >= 80) && (fport <= 89)) payloadType = "Notification"; else if((fport >= 100) && (fport <= 109)) payloadType = "Modbus Generic"; else if((fport >= 110) && (fport <= 119)) { payloadType = "Analog Input Variable Length"; for (let i = 0; i < payload.data.numOfAinPayloads; i++) { payload.data["analogSensorString" + i] = lookupAnalogSensorName(voboType, payload.data.ainPayloads[i].sensorNum0); payload.data["engUnitsString" + i] = lookupUnits(1, payload.data.ainPayloads[i].sensorUnits0); } } else if((fport >= 120) && (fport <= 129)) payloadType = "Modbus Standard Variable Length"; payload.data.fport = fport; payload.data.voboType = voboType; payload.data.payloadType = payloadType; return payload; } function parseStandardPayload(bytes) { var decoded = {}; decoded.DIN1 = bytes[0] & 0x01; // Discrete digital 1 (1-bit) decoded.DIN2 = bytes[0] >> 1 & 0x01; // Discrete digital 2 (1-bit) decoded.DIN3 = bytes[0] >> 2 & 0x01; // Discrete digital 3 (1-bit) decoded.WKUP = bytes[0] >> 3 & 0x01; // Discrete digital wakeup (1-bit) decoded.ADC1 = ((bytes[0] & 0xf0) >> 4) | (bytes[1] << 4); // ADC 1 (12-bit) decoded.ADC2 = (bytes[3] & 0x0f) << 8 | bytes[2]; // ADC 2 (12-bit) decoded.ADC3 = ((bytes[3] & 0xf0) >> 4) | (bytes[4] << 4); // ADC 3 (12-bit) decoded.Battery = ((bytes[6] & 0x0f) << 8 | bytes[5]) * 4; // ADC battery (12-bit) if ((bytes[7] >> 7 & 0x01) == 0) { decoded.Temperature = (((bytes[6] & 0xf0) >> 4) | (bytes[7] << 4)) * 0.125; // ADC temperature (12-bit) above 0 degrees C } else { decoded.Temperature = (4096 - (((bytes[6] & 0xf0) >> 4) | (bytes[7] << 4))) * 0.125 * (-1); // ADC temperature (12-bit) below 0 degrees C } decoded.Modbus0 = bytes[9] << 8 | bytes[8]; // Modbus-RS485 (16-bit) return decoded; } function parseModbusStandardPayload(bytes, fport) { var startIdx = (fport - 1) * 5 - 4; var decoded = {}; decoded["Modbus"+startIdx++] = (bytes[1] << 8) | bytes[0]; // Modbus-RS485 (16-bit) decoded["Modbus"+startIdx++] = (bytes[3] << 8) | bytes[2]; // Modbus-RS485 (16-bit) decoded["Modbus"+startIdx++] = (bytes[5] << 8) | bytes[4]; // Modbus-RS485 (16-bit) decoded["Modbus"+startIdx++] = (bytes[7] << 8) | bytes[6]; // Modbus-RS485 (16-bit) decoded["Modbus"+startIdx] = (bytes[9] << 8) | bytes[8]; // Modbus-RS485 (16-bit) return decoded; } function parseHeartbeat1p0Payload(bytes) { var decoded = {}; decoded.batteryLevelMV = ((bytes[1] & 0x0f) << 8 | bytes[0]) * 4; // ADC battery (12-bit) decoded.fwVersionMajor = (bytes[2] & 0x0f) | (bytes[1] & 0xf0) >> 4; // FW Version Major (8-bit) decoded.fwVersionMinor = (bytes[3] & 0x0f) | (bytes[2] & 0xf0) >> 4; // FW Version Minor (8-bit) decoded.fwVersionPatch = (bytes[4] & 0x0f) | (bytes[3] & 0xf0) >> 4; // FW Version Patch (8-bit) decoded.fwVersionCustom = (bytes[4] & 0xf0) >> 4; // FW Version Custom (4-bit) decoded.recSignalLevels = bytes[5] * -1; // Last RSSI (8 bits) decoded.analogVoltageConfig = (bytes[6] & 0x1f); // Analog Voltage Config (5 bits) decoded.analogPowerTimeConfig = parseFloat((((bytes[7] & 0x1F) << 3 | (bytes[6] & 0xE0) >> 5) / 10).toFixed(1)); // Analog Power Time Config (8 bits) decoded.failedTransmissionBeforeRejoinConfig = (bytes[8] & 0x01) << 3 | (bytes[7] & 0xE0) >> 5; // Failed Transmissions Before Rejoin Config (4 bits) decoded.cycleThroughFSB = (bytes[8] & 0x02) >> 1; // Cycle Through FSB (1 bit) decoded.ackEnable = (bytes[8] & 0x04) >> 2; // ACK Enable (1 bit) decoded.ackFreq = (bytes[8] & 0x78) >> 3; // ACK Frequency (4 bits) decoded.ackReq = (bytes[9] & 0x07) << 1 | (bytes[8] & 0x80) >> 7; // ACK Request (4 bits) decoded.batteryLevelThreshold = (((bytes[10] & 0x7F) << 5) | (bytes[9] & 0xF8) >> 3) * 4; // Battery Level Threshold (12-bit) return decoded; } function parseHeartbeat2p0Payload(bytes) { var decoded = {}; decoded.batteryLevel = (bytes[1] & 0x0F) << 8 | bytes[0]; // Current Battery level in mV decoded.fatalErrorsTotal = (bytes[1] & 0xF0) >> 4; // Total number of Fatal Error detected by the handler (triggering reboot) for the last HeartBeat interval decoded.rssiAvg = bytes[2]; // RSSI of received signal from LoRaWAN GW - average for the last HeartBeat interval decoded.failedJoinAttemptsTotal = bytes[3] & 0x7F; // Total Number of Failed Join Attempts to LoRaWAN network for the last HeartBeat interval decoded.configUpdateOccurred = (bytes[3] & 0x80) >> 7; // Indicates if one or more config updates occurred for the last HeartBeat interval decoded.firmwareRevision = (bytes[5] & 0x03) << 8 | bytes[4]; // Firmware revision encoding. decoded.rebootsTotal = (bytes[5] & 0x1C) >> 2; // Total number of VoBo reboots not including Fatal Error for the last HeartBeat interval decoded.failedTransmitsTotal = (bytes[5] & 0xE0) >> 5; // Total Number of Failed LoRaWAN Transmits for the last HeartBeat interval decoded.errorEventLogsTotal = bytes[6]; // Total number of Error Event Logs written for the last HeartBeat interval decoded.warningEventLogsTotal = bytes[7]; // Total number of Warning Event Logs written for the last HeartBeat interval decoded.infoEventLogsTotal = bytes[8]; // Total number of Info Event Logs written for the last HeartBeat interval decoded.measurementPacketsTotal = (bytes[10]) << 8 | bytes[9]; // Number of Measurements Packets successfully sent for the last HeartBeat interval return decoded; } function bytesToFloat32(bytesArray) { var buffer = new ArrayBuffer(4); var bytesRaw = new Uint8Array(buffer); bytesRaw[0] = bytesArray[0]; bytesRaw[1] = bytesArray[1]; bytesRaw[2] = bytesArray[2]; bytesRaw[3] = bytesArray[3]; var float32Res = new DataView(buffer).getFloat32(0, false); return float32Res; } function parseOneAnalogSensorPayload(bytes) { var decoded = {}; decoded.sensorNum0 = bytes[0]; // Sensor Num 0 (8-bit) decoded.sensorUnits0 = bytes[1]; // Sensor Units 0 (8-bit) decoded.sensorData0 = bytesToFloat32(bytes.slice(2,6)); // Sensor Data 0 (32-bit) return decoded; } function parseTwoAnalogSensorsPayload(bytes) { var decoded = {}; decoded.sensorNum0 = (bytes[0] & 0xF0) >> 4; // Sensor Num 0 (4-bit) decoded.sensorUnits0 = bytes[1]; // Sensor Units 0 (8-bit) decoded.sensorData0 = bytesToFloat32(bytes.slice(2,6)); // Sensor Data 0 (32-bit) decoded.sensorNum1 = bytes[0] & 0x0F; // Sensor Num 1 (4-bit) decoded.sensorUnits1 = bytes[6]; // Sensor Units 1 (8-bit) decoded.sensorData1 = bytesToFloat32(bytes.slice(7,11)); // Sensor Data 1 (32-bit) return decoded; } function parseDigitalSensorsPayload(bytes) { var decoded = {}; decoded.sensorValid0 = (bytes[1] << 8) | bytes[0]; // Sensor Valid 0 (16-bit) decoded.sensorData0 = (bytes[3] << 8) | bytes[2]; // Sensor Data 0 (16-bit) return decoded; } function parseEventLogPayload(bytes) { var decoded = {}; decoded.eventTimestamp = bytes[0] + bytes[1]*Math.pow(2, 8) + bytes[2]*Math.pow(2, 16) + bytes[3]*Math.pow(2, 24); decoded.eventCode = bytes[4] + bytes[5]*Math.pow(2, 8); decoded.metadata = Array.prototype.slice.call(bytes.slice(6, 11), 0); return decoded; } function parseVoboLibGeneralConfigurationPayload(bytes, fport) { var decoded = {}; decoded.subgroupID = bytes[0] & 0x0F; // Sub-Group ID (4-bit) decoded.sequenceNumber = (bytes[0] & 0xF0) >> 4; // Sequence Number (4-bit) if (decoded.sequenceNumber == 0) { decoded.transRejoin = bytes[1] & 0x0F; // Transmission Rejoin (4-bit) decoded.ackFrequency = (bytes[1] & 0xF0) >> 4; // Acknowledgement Frequency for Data (4-bit) if ((fport == 70) || (fport == 71)) decoded.lowBattery = parseFloat(((bytes[2] & 0x0F) / 10.0 + 2.5).toFixed(1)); // Low Battery Threshold (4-bit) else if (fport == 72) decoded.lowVoltThreshold = parseFloat(((bytes[2] & 0x0F) / 10.0 + 2.5).toFixed(1)); // Low Voltage Threshold (4-bit) decoded.reserved1 = (bytes[2] & 0x10) >> 4; // Reserved 1 Field (1-bit) decoded.heartbeatAckEnable = Boolean((bytes[2] & 0x20) >> 5); // Acknowledgement Enable for Heartbeat (1-bit) decoded.operationMode = (bytes[2] & 0x40) >> 6; // Operation Mode (1-bit) decoded.cycleSubBands = Boolean((bytes[2] & 0x80) >> 7); // Cycle Sub Bands Enable (1-bit) decoded.ackRetries = (bytes[3] & 0x07); // Acknowledgement Retries (3-bit) decoded.reservedLL = (bytes[3] & 0x38) >> 3; // Reserved LL (3-bit) decoded.ackEnable = Boolean((bytes[3] & 0x40) >> 6); // Acknowledgement Enable for Data (1-bit) decoded.heartbeatEnable = Boolean((bytes[3] & 0x80) >> 7); // Heartbeat Enable (1-bit) decoded.cycleTime = ((bytes[6] & 0x03) << 16) | (bytes[5] << 8) | bytes[4]; // Cycle Time (18-bit) decoded.backOffReset = (bytes[6] & 0xFC) >> 2; // BackOff Reset (6-bit) decoded.reservedRD = bytes[7] & 0x3F; // Reserved RD (6-bit) decoded.reserved2 = (bytes[7] & 0xC0) >> 6; // Reserved 2 Field (2-bit) decoded.resendAttempts = bytes[8] & 0x0F; // Resend Attempts (4-bit) decoded.freqSubBand = (bytes[8] & 0xF0) >> 4; // Frequency Sub Band (4-bit) } if (decoded.sequenceNumber == 1) { decoded.timeSyncInterval = bytes[1]; // Time Sync Interval in 30 minutes quantities (8-bit) decoded.joinEUI = ""; // LoRaWAN Join EUI (64-bit) for(let i = 9; i >= 2; i--) { decoded.joinEUI += bytes[i].toString(16).toUpperCase().padStart(2,0); if(i != 2) decoded.joinEUI += "-"; } decoded.joinNonceResetEnable = Boolean(bytes[10] & 0x01); // Join Nonce Reset Enable (1-bit) decoded.timeSyncWakeupEnable = Boolean((bytes[10] & 0x02) >> 1); // Time Sync Wakeup Enable (1-bit) decoded.reserved1 = (bytes[10] & 0xFC) >> 2; // Reserved 1 Field (1-bit) } return decoded; } function parseVoboLibVoboSyncConfigurationPayload(bytes) { var decoded = {}; decoded.subgroupID = bytes[0] & 0x0F; // Sub-Group ID (4-bit) decoded.sequenceNumber = (bytes[0] & 0xF0) >> 4; // Sequence Number (4-bit) if (decoded.sequenceNumber == 0) { decoded.vbsNodeNumber = (bytes[2] << 8) | bytes[1]; // VoboSync Node Number (16-bit) decoded.vbsTimeReference = bytes.slice(3, 7).readUInt32LE(); // VoboSync Time Reference (32-bit) decoded.reservedVCPDS = bytes[7] & 0x0F; // Reserved VCPDS (4-bit) decoded.reservedVMPDS = (bytes[7] & 0xF0) >> 4; // Reserved VMPDS (4-bit) decoded.reservedVUDS = bytes[8] & 0x0F; // Reserved VUDS (4-bit) decoded.reserved1 = (bytes[8] & 0x30) >> 4; // Reserved 1 Field (2-bit) decoded.reservedVSAE = Boolean((bytes[8] & 0x40) >> 6); // Reserved VSAE (1-bit) decoded.vbsEnable = Boolean((bytes[8] & 0x80) >> 7); // VoboSync Enable (1-bit) decoded.reserved3 = bytes[9] & 0x3F; // Reserved 3 (6-bit) decoded.reserved2 = (bytes[9] & 0xC0) >> 6; // Reserved 2 Field (2-bit) } if (decoded.sequenceNumber == 1) { decoded.vbsMeasurementDelaySec = bytes[1] & 0x7F; // VoboSync Measurement Delay (7-bit) decoded.reserved1 = (bytes[1] & 0x80) >> 7; // Reserved 1 Field (1-bit) decoded.vbsUplinkDelaySec = bytes[2] & 0x7F; // VoboSync Uplink Delay (7-bit) decoded.reserved2 = (bytes[2] & 0x80) >> 7; // Reserved 2 Field (1-bit) } return decoded; } function parseVoboXXGeneralConfigurationPayload(bytes) { var decoded = {}; decoded.subgroupID = bytes[0] & 0x0F; // Sub-Group ID (4-bit) decoded.sequenceNumber = (bytes[0] & 0xF0) >> 4; // Sequence Number (4-bit) decoded.analogVoltage = parseFloat((bytes[1] / 10).toFixed(1)); // Analog Voltage (8-bit) decoded.powerTime = parseFloat((bytes[2] / 10).toFixed(1)); // Analog Power Time (8-bit) decoded.mbEnable = Boolean(bytes[3] & 0x01); // Modbus Enable (1-bit) decoded.engUnitsEnable = Boolean((bytes[3] >> 1) & 0x01); // Engineering Units Enable (1-bit) decoded.din1TransmitEnable = Boolean((bytes[3] >> 2) & 0x01); // DIN1 Transmit Enable (1-bit) decoded.din2TransmitEnable = Boolean((bytes[3] >> 3) & 0x01); // DIN2 Transmit Enable (1-bit) decoded.din3TransmitEnable = Boolean((bytes[3] >> 4) & 0x01); // DIN3 Transmit Enable (1-bit) decoded.wkupTransmitEnable = Boolean((bytes[3] >> 5) & 0x01); // WKUP Transmit Enable (1-bit) decoded.ain1TransmitEnable = Boolean((bytes[3] >> 6) & 0x01); // AIN1 Transmit Enable (1-bit) decoded.ain2TransmitEnable = Boolean((bytes[3] >> 7) & 0x01); // AIN2 Transmit Enable (1-bit) decoded.ain3TransmitEnable = Boolean(bytes[4] & 0x01); // AIN3 Transmit Enable (1-bit) decoded.batteryLevelTransmitEnable = Boolean((bytes[4] >> 1) & 0x01); // Battery Level Transmit Enable (1-bit) decoded.adcTemperatureTransmitEnable = Boolean((bytes[4] >> 2) & 0x01); // ADC Temperature Transmit Enable (1-bit) decoded.mbTransmitEnable = Boolean((bytes[4] >> 3) & 0x01); // Modbus Transmit Enable (1-bit) decoded.ainPayloadType = (bytes[4] >> 4) & 0x01; // Ain Payload Type (1-bit) decoded.reservedMAWE = Boolean((bytes[4] >> 5) & 0x01); // Reserved MAWE Field (1-bit) decoded.reservedMADE = Boolean((bytes[4] >> 6) & 0x01); // Reserved MADE Field (1-bit) decoded.reservedMAME = Boolean((bytes[4] >> 7) & 0x01); // Reserved MAME Field (1-bit) return decoded; } function parseVoboXXModbusGeneralConfigurationPayload(bytes) { var decoded = {}; decoded.subgroupID = bytes[0] & 0x0F; // Sub-Group ID (4-bit) decoded.sequenceNumber = (bytes[0] & 0xF0) >> 4; // Sequence Number (4-bit) decoded.mbTimeout = (bytes[2] << 8) | bytes[1]; // Modbus Timeout in milliseconds (16-bit) decoded.mbBaud = bytes[3] & 0x0F; // Modbus Baud Rate - encoded (4-bit) decoded.mbStopBits = (bytes[3] >> 4) & 0x03; // Modbus Stop Bits (2-bit) decoded.mbParity = (bytes[3] >> 6) & 0x03; // Modbus Parity (2-bit) decoded.mbPayloadType = bytes[4] & 0x03; // Modbus Payload Type (2-bit) decoded.reserved1 = (bytes[4] >> 2) & 0x3F; // Reserved 1 Field (6-bit) return decoded; } function parseVoboXXModbusGroupsEnableConfigurationPayload(bytes) { var decoded = {}; decoded.subgroupID = bytes[0] & 0x0F; // Sub-Group ID (4-bit) decoded.sequenceNumber = (bytes[0] & 0xF0) >> 4; // Sequence Number (4-bit) if (decoded.subgroupID == 6) { decoded.mbFirstCycleG1 = Boolean(bytes[1] & 0x01); // Group 1 First Cycle Enable decoded.mbFirstCycleG2 = Boolean((bytes[1] >> 1) & 0x01); // Group 2 First Cycle Enable decoded.mbFirstCycleG3 = Boolean((bytes[1] >> 2) & 0x01); // Group 3 First Cycle Enable decoded.mbFirstCycleG4 = Boolean((bytes[1] >> 3) & 0x01); // Group 4 First Cycle Enable decoded.mbFirstCycleG5 = Boolean((bytes[1] >> 4) & 0x01); // Group 5 First Cycle Enable decoded.mbFirstCycleG6 = Boolean((bytes[1] >> 5) & 0x01); // Group 6 First Cycle Enable decoded.mbFirstCycleG7 = Boolean((bytes[1] >> 6) & 0x01); // Group 7 First Cycle Enable decoded.mbFirstCycleG8 = Boolean((bytes[1] >> 7) & 0x01); // Group 8 First Cycle Enable decoded.mbFirstCycleG9 = Boolean(bytes[2] & 0x01); // Group 9 First Cycle Enable decoded.mbFirstCycleG10 = Boolean((bytes[2] >> 1) & 0x01); // Group 10 First Cycle Enable decoded.mbFirstCycleG11 = Boolean((bytes[2] >> 2) & 0x01); // Group 11 First Cycle Enable decoded.mbFirstCycleG12 = Boolean((bytes[2] >> 3) & 0x01); // Group 12 First Cycle Enable decoded.mbFirstCycleG13 = Boolean((bytes[2] >> 4) & 0x01); // Group 13 First Cycle Enable decoded.mbFirstCycleG14 = Boolean((bytes[2] >> 5) & 0x01); // Group 14 First Cycle Enable decoded.mbFirstCycleG15 = Boolean((bytes[2] >> 6) & 0x01); // Group 15 First Cycle Enable decoded.mbFirstCycleG16 = Boolean((bytes[2] >> 7) & 0x01); // Group 16 First Cycle Enable decoded.mbFirstCycleG17 = Boolean(bytes[3] & 0x01); // Group 17 First Cycle Enable decoded.mbFirstCycleG18 = Boolean((bytes[3] >> 1) & 0x01); // Group 18 First Cycle Enable decoded.mbFirstCycleG19 = Boolean((bytes[3] >> 2) & 0x01); // Group 19 First Cycle Enable decoded.mbFirstCycleG20 = Boolean((bytes[3] >> 3) & 0x01); // Group 20 First Cycle Enable decoded.mbFirstCycleG21 = Boolean((bytes[3] >> 4) & 0x01); // Group 21 First Cycle Enable decoded.mbFirstCycleG22 = Boolean((bytes[3] >> 5) & 0x01); // Group 22 First Cycle Enable decoded.mbFirstCycleG23 = Boolean((bytes[3] >> 6) & 0x01); // Group 23 First Cycle Enable decoded.mbFirstCycleG24 = Boolean((bytes[3] >> 7) & 0x01); // Group 24 First Cycle Enable decoded.mbFirstCycleG25 = Boolean(bytes[4] & 0x01); // Group 25 First Cycle Enable decoded.mbFirstCycleG26 = Boolean((bytes[4] >> 1) & 0x01); // Group 26 First Cycle Enable decoded.mbFirstCycleG27 = Boolean((bytes[4] >> 2) & 0x01); // Group 27 First Cycle Enable decoded.mbFirstCycleG28 = Boolean((bytes[4] >> 3) & 0x01); // Group 28 First Cycle Enable decoded.mbFirstCycleG29 = Boolean((bytes[4] >> 4) & 0x01); // Group 29 First Cycle Enable decoded.mbFirstCycleG30 = Boolean((bytes[4] >> 5) & 0x01); // Group 30 First Cycle Enable decoded.mbFirstCycleG31 = Boolean((bytes[4] >> 6) & 0x01); // Group 31 First Cycle Enable decoded.mbFirstCycleG32 = Boolean((bytes[4] >> 7) & 0x01); // Group 32 First Cycle Enable decoded.mbFirstCycleG33 = Boolean(bytes[5] & 0x01); // Group 33 First Cycle Enable decoded.mbFirstCycleG34 = Boolean((bytes[5] >> 1) & 0x01); // Group 34 First Cycle Enable decoded.mbFirstCycleG35 = Boolean((bytes[5] >> 2) & 0x01); // Group 35 First Cycle Enable decoded.mbFirstCycleG36 = Boolean((bytes[5] >> 3) & 0x01); // Group 36 First Cycle Enable decoded.mbFirstCycleG37 = Boolean((bytes[5] >> 4) & 0x01); // Group 37 First Cycle Enable decoded.mbFirstCycleG38 = Boolean((bytes[5] >> 5) & 0x01); // Group 38 First Cycle Enable decoded.mbFirstCycleG39 = Boolean((bytes[5] >> 6) & 0x01); // Group 39 First Cycle Enable decoded.mbFirstCycleG40 = Boolean((bytes[5] >> 7) & 0x01); // Group 40 First Cycle Enable decoded.mbFirstCycleG41 = Boolean(bytes[6] & 0x01); // Group 41 First Cycle Enable decoded.reserved1 = (bytes[6] >> 1) & 0x7F; // Reserved 1 Field (7-bit) } if (decoded.subgroupID == 7) { decoded.mbSubseqCyclesG1 = Boolean(bytes[1] & 0x01); // Group 1 Subsequent Cycle Enable decoded.mbSubseqCyclesG2 = Boolean((bytes[1] >> 1) & 0x01); // Group 2 Subsequent Cycle Enable decoded.mbSubseqCyclesG3 = Boolean((bytes[1] >> 2) & 0x01); // Group 3 Subsequent Cycle Enable decoded.mbSubseqCyclesG4 = Boolean((bytes[1] >> 3) & 0x01); // Group 4 Subsequent Cycle Enable decoded.mbSubseqCyclesG5 = Boolean((bytes[1] >> 4) & 0x01); // Group 5 Subsequent Cycle Enable decoded.mbSubseqCyclesG6 = Boolean((bytes[1] >> 5) & 0x01); // Group 6 Subsequent Cycle Enable decoded.mbSubseqCyclesG7 = Boolean((bytes[1] >> 6) & 0x01); // Group 7 Subsequent Cycle Enable decoded.mbSubseqCyclesG8 = Boolean((bytes[1] >> 7) & 0x01); // Group 8 Subsequent Cycle Enable decoded.mbSubseqCyclesG9 = Boolean(bytes[2] & 0x01); // Group 9 Subsequent Cycle Enable decoded.mbSubseqCyclesG10 = Boolean((bytes[2] >> 1) & 0x01); // Group 10 Subsequent Cycle Enable decoded.mbSubseqCyclesG11 = Boolean((bytes[2] >> 2) & 0x01); // Group 11 Subsequent Cycle Enable decoded.mbSubseqCyclesG12 = Boolean((bytes[2] >> 3) & 0x01); // Group 12 Subsequent Cycle Enable decoded.mbSubseqCyclesG13 = Boolean((bytes[2] >> 4) & 0x01); // Group 13 Subsequent Cycle Enable decoded.mbSubseqCyclesG14 = Boolean((bytes[2] >> 5) & 0x01); // Group 14 Subsequent Cycle Enable decoded.mbSubseqCyclesG15 = Boolean((bytes[2] >> 6) & 0x01); // Group 15 Subsequent Cycle Enable decoded.mbSubseqCyclesG16 = Boolean((bytes[2] >> 7) & 0x01); // Group 16 Subsequent Cycle Enable decoded.mbSubseqCyclesG17 = Boolean(bytes[3] & 0x01); // Group 17 Subsequent Cycle Enable decoded.mbSubseqCyclesG18 = Boolean((bytes[3] >> 1) & 0x01); // Group 18 Subsequent Cycle Enable decoded.mbSubseqCyclesG19 = Boolean((bytes[3] >> 2) & 0x01); // Group 19 Subsequent Cycle Enable decoded.mbSubseqCyclesG20 = Boolean((bytes[3] >> 3) & 0x01); // Group 20 Subsequent Cycle Enable decoded.mbSubseqCyclesG21 = Boolean((bytes[3] >> 4) & 0x01); // Group 21 Subsequent Cycle Enable decoded.mbSubseqCyclesG22 = Boolean((bytes[3] >> 5) & 0x01); // Group 22 Subsequent Cycle Enable decoded.mbSubseqCyclesG23 = Boolean((bytes[3] >> 6) & 0x01); // Group 23 Subsequent Cycle Enable decoded.mbSubseqCyclesG24 = Boolean((bytes[3] >> 7) & 0x01); // Group 24 Subsequent Cycle Enable decoded.mbSubseqCyclesG25 = Boolean(bytes[4] & 0x01); // Group 25 Subsequent Cycle Enable decoded.mbSubseqCyclesG26 = Boolean((bytes[4] >> 1) & 0x01); // Group 26 Subsequent Cycle Enable decoded.mbSubseqCyclesG27 = Boolean((bytes[4] >> 2) & 0x01); // Group 27 Subsequent Cycle Enable decoded.mbSubseqCyclesG28 = Boolean((bytes[4] >> 3) & 0x01); // Group 28 Subsequent Cycle Enable decoded.mbSubseqCyclesG29 = Boolean((bytes[4] >> 4) & 0x01); // Group 29 Subsequent Cycle Enable decoded.mbSubseqCyclesG30 = Boolean((bytes[4] >> 5) & 0x01); // Group 30 Subsequent Cycle Enable decoded.mbSubseqCyclesG31 = Boolean((bytes[4] >> 6) & 0x01); // Group 31 Subsequent Cycle Enable decoded.mbSubseqCyclesG32 = Boolean((bytes[4] >> 7) & 0x01); // Group 32 Subsequent Cycle Enable decoded.mbSubseqCyclesG33 = Boolean(bytes[5] & 0x01); // Group 33 Subsequent Cycle Enable decoded.mbSubseqCyclesG34 = Boolean((bytes[5] >> 1) & 0x01); // Group 34 Subsequent Cycle Enable decoded.mbSubseqCyclesG35 = Boolean((bytes[5] >> 2) & 0x01); // Group 35 Subsequent Cycle Enable decoded.mbSubseqCyclesG36 = Boolean((bytes[5] >> 3) & 0x01); // Group 36 Subsequent Cycle Enable decoded.mbSubseqCyclesG37 = Boolean((bytes[5] >> 4) & 0x01); // Group 37 Subsequent Cycle Enable decoded.mbSubseqCyclesG38 = Boolean((bytes[5] >> 5) & 0x01); // Group 38 Subsequent Cycle Enable decoded.mbSubseqCyclesG39 = Boolean((bytes[5] >> 6) & 0x01); // Group 39 Subsequent Cycle Enable decoded.mbSubseqCyclesG40 = Boolean((bytes[5] >> 7) & 0x01); // Group 40 Subsequent Cycle Enable decoded.mbSubseqCyclesG41 = Boolean(bytes[6] & 0x01); // Group 41 Subsequent Cycle Enable decoded.reserved1 = (bytes[6] >> 1) & 0x7F; // Reserved 1 Field (7-bit) } return decoded; } function parseVoboXXModbusGroupsConfigurationPayload(bytes) { var decoded = {}; decoded.subgroupID = bytes[0] & 0x0F; // Sub-Group ID (4-bit) decoded.sequenceNumber = (bytes[0] & 0xF0) >> 4; // Sequence Number (4-bit) var groupIdx = ((decoded.subgroupID - 8) * 16) + decoded.sequenceNumber + 1; // Sub-Groups 8, 9 and 10 carry the groups configurations. Group index is determined by Sub-Group ID and Sequence Number. var groupIdxStr = (groupIdx).toString(); decoded["mbFirstCycleG" + groupIdxStr] = Boolean(bytes[1] & 0x01); // Modbus Group First Cycle Enable (1-bit) decoded["mbSubseqCyclesG" + groupIdxStr] = Boolean((bytes[1] >> 1) & 0x01); // Modbus Group Subsequent Cycle Enable (1-bit) decoded["mbByteSwapG" + groupIdxStr] = Boolean((bytes[1] >> 2) & 0x01); // Modbus Group Byte Swap Enable (1-bit) decoded["mbWordSwapG" + groupIdxStr] = Boolean((bytes[1] >> 3) & 0x01); // Modbus Group Word Swap Enable (1-bit) decoded["mbNumTypeG" + groupIdxStr] = (bytes[1] >> 4) & 0x03; // Modbus Group Numerical Type (2-bit) decoded["reserved1"] = (bytes[1] >> 6) & 0x03; // Reserved 1 Field (2-bit) decoded["mbSlaveAddrG" + groupIdxStr] = bytes[2]; // Modbus Group Slave Address (8-bit) decoded["mbStartAddrG" + groupIdxStr] = (bytes[4] << 8) | bytes[3]; // Modbus Group Start Address (16-bit) decoded["mbNumRegsG" + groupIdxStr] = bytes[5]; // Modbus Group Number of Registers (8-bit) decoded["mbWdataG" + groupIdxStr] = (bytes[7] << 8) | bytes[6]; // Modbus Group Write Data (16-bit) decoded["mbGrpDelayG" + groupIdxStr] = (bytes[9] << 8) | bytes[8]; // Modbus Group Delay in milliseconds (16-bit) decoded["mbFuncCodeG" + groupIdxStr] = bytes[10] & 0x3F; // Modbus Group Function Code (6-bit) decoded["reserved2"] = (bytes[10] >> 6) & 0x03; // Reserved 2 Field (2-bit) return decoded; } function parseVoboXXModbusPayloadsSlotsConfigurationPayload(bytes) { var decoded = {}; decoded.subgroupID = bytes[0] & 0x0F; // Sub-Group ID (4-bit) decoded.sequenceNumber = (bytes[0] & 0xF0) >> 4; // Sequence Number (4-bit) if (decoded.sequenceNumber == 0) { decoded.mbGroupPaySlot0 = bytes[1]; // Modbus Payload Slot 0 Group Idx (8-bit) decoded.mbRegPaySlot0 = bytes[2]; // Modbus Payload Slot 0 Register Idx (8-bit) decoded.mbGroupPaySlot1 = bytes[3]; // Modbus Payload Slot 1 Group Idx (8-bit) decoded.mbRegPaySlot1 = bytes[4]; // Modbus Payload Slot 1 Register Idx (8-bit) decoded.mbGroupPaySlot2 = bytes[5]; // Modbus Payload Slot 2 Group Idx (8-bit) decoded.mbRegPaySlot2 = bytes[6]; // Modbus Payload Slot 2 Register Idx (8-bit) decoded.mbGroupPaySlot3 = bytes[7]; // Modbus Payload Slot 3 Group Idx (8-bit) decoded.mbRegPaySlot3 = bytes[8]; // Modbus Payload Slot 3 Register Idx (8-bit) decoded.mbGroupPaySlot4 = bytes[9]; // Modbus Payload Slot 4 Group Idx (8-bit) decoded.mbRegPaySlot4 = bytes[10]; // Modbus Payload Slot 4 Register Idx (8-bit) } if (decoded.sequenceNumber == 1) { decoded.mbGroupPaySlot5 = bytes[1]; // Modbus Payload Slot 5 Group Idx (8-bit) decoded.mbRegPaySlot5 = bytes[2]; // Modbus Payload Slot 5 Register Idx (8-bit) decoded.mbGroupPaySlot6 = bytes[3]; // Modbus Payload Slot 6 Group Idx (8-bit) decoded.mbRegPaySlot6 = bytes[4]; // Modbus Payload Slot 6 Register Idx (8-bit) decoded.mbGroupPaySlot7 = bytes[5]; // Modbus Payload Slot 7 Group Idx (8-bit) decoded.mbRegPaySlot7 = bytes[6]; // Modbus Payload Slot 7 Register Idx (8-bit) decoded.mbGroupPaySlot8 = bytes[7]; // Modbus Payload Slot 8 Group Idx (8-bit) decoded.mbRegPaySlot8 = bytes[8]; // Modbus Payload Slot 8 Register Idx (8-bit) decoded.mbGroupPaySlot9 = bytes[9]; // Modbus Payload Slot 9 Group Idx (8-bit) decoded.mbRegPaySlot9 = bytes[10]; // Modbus Payload Slot 9 Register Idx (8-bit) } if (decoded.sequenceNumber == 2) { decoded.mbGroupPaySlot10 = bytes[1]; // Modbus Payload Slot 10 Group Idx (8-bit) decoded.mbRegPaySlot10 = bytes[2]; // Modbus Payload Slot 10 Register Idx (8-bit) decoded.mbGroupPaySlot11 = bytes[3]; // Modbus Payload Slot 11 Group Idx (8-bit) decoded.mbRegPaySlot11 = bytes[4]; // Modbus Payload Slot 11 Register Idx (8-bit) decoded.mbGroupPaySlot12 = bytes[5]; // Modbus Payload Slot 12 Group Idx (8-bit) decoded.mbRegPaySlot12 = bytes[6]; // Modbus Payload Slot 12 Register Idx (8-bit) decoded.mbGroupPaySlot13 = bytes[7]; // Modbus Payload Slot 13 Group Idx (8-bit) decoded.mbRegPaySlot13 = bytes[8]; // Modbus Payload Slot 13 Register Idx (8-bit) decoded.mbGroupPaySlot14 = bytes[9]; // Modbus Payload Slot 14 Group Idx (8-bit) decoded.mbRegPaySlot14 = bytes[10]; // Modbus Payload Slot 14 Register Idx (8-bit) } if (decoded.sequenceNumber == 3) { decoded.mbGroupPaySlot15 = bytes[1]; // Modbus Payload Slot 15 Group Idx (8-bit) decoded.mbRegPaySlot15 = bytes[2]; // Modbus Payload Slot 15 Register Idx (8-bit) decoded.mbGroupPaySlot16 = bytes[3]; // Modbus Payload Slot 16 Group Idx (8-bit) decoded.mbRegPaySlot16 = bytes[4]; // Modbus Payload Slot 16 Register Idx (8-bit) decoded.mbGroupPaySlot17 = bytes[5]; // Modbus Payload Slot 17 Group Idx (8-bit) decoded.mbRegPaySlot17 = bytes[6]; // Modbus Payload Slot 17 Register Idx (8-bit) decoded.mbGroupPaySlot18 = bytes[7]; // Modbus Payload Slot 18 Group Idx (8-bit) decoded.mbRegPaySlot18 = bytes[8]; // Modbus Payload Slot 18 Register Idx (8-bit) decoded.mbGroupPaySlot19 = bytes[9]; // Modbus Payload Slot 19 Group Idx (8-bit) decoded.mbRegPaySlot19 = bytes[10]; // Modbus Payload Slot 19 Register Idx (8-bit) } if (decoded.sequenceNumber == 4) { decoded.mbGroupPaySlot20 = bytes[1]; // Modbus Payload Slot 20 Group Idx (8-bit) decoded.mbRegPaySlot20 = bytes[2]; // Modbus Payload Slot 20 Register Idx (8-bit) decoded.mbGroupPaySlot21 = bytes[3]; // Modbus Payload Slot 21 Group Idx (8-bit) decoded.mbRegPaySlot21 = bytes[4]; // Modbus Payload Slot 21 Register Idx (8-bit) decoded.mbGroupPaySlot22 = bytes[5]; // Modbus Payload Slot 22 Group Idx (8-bit) decoded.mbRegPaySlot22 = bytes[6]; // Modbus Payload Slot 22 Register Idx (8-bit) decoded.mbGroupPaySlot23 = bytes[7]; // Modbus Payload Slot 23 Group Idx (8-bit) decoded.mbRegPaySlot23 = bytes[8]; // Modbus Payload Slot 23 Register Idx (8-bit) decoded.mbGroupPaySlot24 = bytes[9]; // Modbus Payload Slot 24 Group Idx (8-bit) decoded.mbRegPaySlot24 = bytes[10]; // Modbus Payload Slot 24 Register Idx (8-bit) } if (decoded.sequenceNumber == 5) { decoded.mbGroupPaySlot25 = bytes[1]; // Modbus Payload Slot 25 Group Idx (8-bit) decoded.mbRegPaySlot25 = bytes[2]; // Modbus Payload Slot 25 Register Idx (8-bit) decoded.mbGroupPaySlot26 = bytes[3]; // Modbus Payload Slot 26 Group Idx (8-bit) decoded.mbRegPaySlot26 = bytes[4]; // Modbus Payload Slot 26 Register Idx (8-bit) decoded.mbGroupPaySlot27 = bytes[5]; // Modbus Payload Slot 27 Group Idx (8-bit) decoded.mbRegPaySlot27 = bytes[6]; // Modbus Payload Slot 27 Register Idx (8-bit) decoded.mbGroupPaySlot28 = bytes[7]; // Modbus Payload Slot 28 Group Idx (8-bit) decoded.mbRegPaySlot28 = bytes[8]; // Modbus Payload Slot 28 Register Idx (8-bit) decoded.mbGroupPaySlot29 = bytes[9]; // Modbus Payload Slot 29 Group Idx (8-bit) decoded.mbRegPaySlot29 = bytes[10]; // Modbus Payload Slot 29 Register Idx (8-bit) } if (decoded.sequenceNumber == 6) { decoded.mbGroupPaySlot30 = bytes[1]; // Modbus Payload Slot 30 Group Idx (8-bit) decoded.mbRegPaySlot30 = bytes[2]; // Modbus Payload Slot 30 Register Idx (8-bit) decoded.mbGroupPaySlot31 = bytes[3]; // Modbus Payload Slot 31 Group Idx (8-bit) decoded.mbRegPaySlot31 = bytes[4]; // Modbus Payload Slot 31 Register Idx (8-bit) decoded.mbGroupPaySlot32 = bytes[5]; // Modbus Payload Slot 32 Group Idx (8-bit) decoded.mbRegPaySlot32 = bytes[6]; // Modbus Payload Slot 32 Register Idx (8-bit) decoded.mbGroupPaySlot33 = bytes[7]; // Modbus Payload Slot 33 Group Idx (8-bit) decoded.mbRegPaySlot33 = bytes[8]; // Modbus Payload Slot 33 Register Idx (8-bit) decoded.mbGroupPaySlot34 = bytes[9]; // Modbus Payload Slot 34 Group Idx (8-bit) decoded.mbRegPaySlot34 = bytes[10]; // Modbus Payload Slot 34 Register Idx (8-bit) } if (decoded.sequenceNumber == 7) { decoded.mbGroupPaySlot35 = bytes[1]; // Modbus Payload Slot 35 Group Idx (8-bit) decoded.mbRegPaySlot35 = bytes[2]; // Modbus Payload Slot 35 Register Idx (8-bit) decoded.mbGroupPaySlot36 = bytes[3]; // Modbus Payload Slot 36 Group Idx (8-bit) decoded.mbRegPaySlot36 = bytes[4]; // Modbus Payload Slot 36 Register Idx (8-bit) decoded.mbGroupPaySlot37 = bytes[5]; // Modbus Payload Slot 37 Group Idx (8-bit) decoded.mbRegPaySlot37 = bytes[6]; // Modbus Payload Slot 37 Register Idx (8-bit) decoded.mbGroupPaySlot38 = bytes[7]; // Modbus Payload Slot 38 Group Idx (8-bit) decoded.mbRegPaySlot38 = bytes[8]; // Modbus Payload Slot 38 Register Idx (8-bit) decoded.mbGroupPaySlot39 = bytes[9]; // Modbus Payload Slot 39 Group Idx (8-bit) decoded.mbRegPaySlot39 = bytes[10]; // Modbus Payload Slot 39 Register Idx (8-bit) } if (decoded.sequenceNumber == 8) { decoded.mbGroupPaySlot40 = bytes[1]; // Modbus Payload Slot 40 Group Idx (8-bit) decoded.mbRegPaySlot40 = bytes[2]; // Modbus Payload Slot 40 Register Idx (8-bit) } return decoded; } function parseVoboXXEngineeringUnitsConfigurationPayload(bytes) { var decoded = {}; decoded.subgroupID = bytes[0] & 0x0F; // Sub-Group ID (4-bit) decoded.sequenceNumber = (bytes[0] & 0xF0) >> 4; // Sequence Number (4-bit) // AIN1 General if (decoded.sequenceNumber == 0) { decoded.ain1UnitsCode = bytes[1]; // AIN1 Units Code (8-bit) decoded.ain1MinValue = bytesToFloat32(bytes.slice(2,6)); // AIN1 Minimum Value (32-bit) decoded.ain1MaxValue = bytesToFloat32(bytes.slice(6,10)); // AIN1 Maximum Value (32-bit) decoded.ain1Type = bytes[10] & 0x03; // AIN1 Type (2-bit) decoded.reserved1 = (bytes[10] >> 2) & 0x3F // Reserved 1 Field (6-bit) } // AIN1 Series Resistance if (decoded.sequenceNumber == 1) { decoded.ain1SeriesResistance = bytesToFloat32(bytes.slice(1,5)); // AIN1 Series Resistance (32-bit) } // AIN1 Calibration if (decoded.sequenceNumber == 2) { decoded.ain1Gain = bytesToFloat32(bytes.slice(1,5)); // AIN1 Calibration Gain (32-bit) decoded.ain1Offset = bytesToFloat32(bytes.slice(5,9)); // AIN1 Calibration Offset (32-bit) } // AIN2 General if (decoded.sequenceNumber == 3) { decoded.ain2UnitsCode = bytes[1]; // AIN2 Units Code (8-bit) decoded.ain2MinValue = bytesToFloat32(bytes.slice(2,6)); // AIN2 Minimum Value (32-bit) decoded.ain2MaxValue = bytesToFloat32(bytes.slice(6,10)); // AIN2 Maximum Value (32-bit) decoded.ain2Type = bytes[10] & 0x03; // AIN2 Type (2-bit) decoded.reserved1 = (bytes[10] >> 2) & 0x3F // Reserved 1 Field (6-bit) } // AIN2 Series Resistance if (decoded.sequenceNumber == 4) { decoded.ain2SeriesResistance = bytesToFloat32(bytes.slice(1,5)); // AIN2 Series Resistance (32-bit) } // AIN2 Calibration if (decoded.sequenceNumber == 5) { decoded.ain2Gain = bytesToFloat32(bytes.slice(1,5)); // AIN2 Calibration Gain (32-bit) decoded.ain2Offset = bytesToFloat32(bytes.slice(5,9)); // AIN2 Calibration Offset (32-bit) } // AIN3 General if (decoded.sequenceNumber == 6) { decoded.ain3UnitsCode = bytes[1]; // AIN3 Units Code (8-bit) decoded.ain3MinValue = bytesToFloat32(bytes.slice(2,6)); // AIN3 Minimum Value (32-bit) decoded.ain3MaxValue = bytesToFloat32(bytes.slice(6,10)); // AIN3 Maximum Value (32-bit) decoded.ain3Type = bytes[10] & 0x03; // AIN3 Type (2-bit) decoded.reserved1 = (bytes[10] >> 2) & 0x3F // Reserved 1 Field (6-bit) } // AIN3 Series Resistance if (decoded.sequenceNumber == 7) { decoded.ain3SeriesResistance = bytesToFloat32(bytes.slice(1,5)); // AIN3 Series Resistance (32-bit) } // AIN3 Calibration if (decoded.sequenceNumber == 8) { decoded.ain3Gain = bytesToFloat32(bytes.slice(1,5)); // AIN3 Calibration Gain (32-bit) decoded.ain3Offset = bytesToFloat32(bytes.slice(5,9)); // AIN3 Calibration Offset (32-bit) } return decoded; } function parseVoboTCGeneralConfigurationPayload(bytes) { var decoded = {}; decoded.subgroupID = bytes[0] & 0x0F; // Sub-Group ID (4-bit) decoded.sequenceNumber = (bytes[0] & 0xF0) >> 4; // Sequence Number (4-bit) if (decoded.sequenceNumber == 0) { decoded.deviceType1 = bytes[1] & 0x07; // Device Type 1 (3-bit) decoded.enable1 = Boolean((bytes[1] & 0x08) >> 3); // Enable 1 (1-bit) decoded.deviceType2 = (bytes[1] & 0x70) >> 4; // Device Type 2 (3-bit) decoded.enable2 = Boolean((bytes[1] & 0x80) >> 7); // Enable 2 (1-bit) decoded.deviceType3 = bytes[2] & 0x07; // Device Type 3 (3-bit) decoded.enable3 = Boolean((bytes[2] & 0x08) >> 3); // Enable 3 (1-bit) decoded.deviceType4 = (bytes[2] & 0x70) >> 4; // Device Type 4 (3-bit) decoded.enable4 = Boolean((bytes[2] & 0x80) >> 7); // Enable 4 (1-bit) decoded.deviceType5 = bytes[3] & 0x07; // Device Type 5 (3-bit) decoded.enable5 = Boolean((bytes[3] & 0x08) >> 3); // Enable 5 (1-bit) decoded.deviceType6 = (bytes[3] & 0x70) >> 4; // Device Type 6 (3-bit) decoded.enable6 = Boolean((bytes[3] & 0x80) >> 7); // Enable 6 (1-bit) decoded.deviceType7 = bytes[4] & 0x07; // Device Type 7 (3-bit) decoded.enable7 = Boolean((bytes[4] & 0x08) >> 3); // Enable 7 (1-bit) decoded.deviceType8 = (bytes[4] & 0x70) >> 4; // Device Type 8 (3-bit) decoded.enable8 = Boolean((bytes[4] & 0x80) >> 7); // Enable 8 (1-bit) decoded.deviceType9 = bytes[5] & 0x07; // Device Type 9 (3-bit) decoded.enable9 = Boolean((bytes[5] & 0x08) >> 3); // Enable 9 (1-bit) decoded.deviceType10 = (bytes[5] & 0x70) >> 4; // Device Type 10 (3-bit) decoded.enable10 = Boolean((bytes[5] & 0x80) >> 7); // Enable 10 (1-bit) decoded.deviceType11 = bytes[6] & 0x07; // Device Type 11 (3-bit) decoded.enable11 = Boolean((bytes[6] & 0x08) >> 3); // Enable 11 (1-bit) decoded.deviceType12 = (bytes[6] & 0x70) >> 4; // Device Type 12 (3-bit) decoded.enable12 = Boolean((bytes[6] & 0x80) >> 7); // Enable 12 (1-bit) decoded.deviceUnits1 = bytes[7] & 0x01; // Device Units 1 (1-bit) decoded.deviceUnits2 = (bytes[7] & 0x02) >> 1; // Device Units 2 (1-bit) decoded.deviceUnits3 = (bytes[7] & 0x04) >> 2; // Device Units 3 (1-bit) decoded.deviceUnits4 = (bytes[7] & 0x08) >> 3; // Device Units 4 (1-bit) decoded.deviceUnits5 = (bytes[7] & 0x10) >> 4; // Device Units 5 (1-bit) decoded.deviceUnits6 = (bytes[7] & 0x20) >> 5; // Device Units 6 (1-bit) decoded.deviceUnits7 = (bytes[7] & 0x40) >> 6; // Device Units 7 (1-bit) decoded.deviceUnits8 = (bytes[7] & 0x80) >> 7; // Device Units 8 (1-bit) decoded.deviceUnits9 = bytes[8] & 0x01; // Device Units 9 (1-bit) decoded.deviceUnits10 = (bytes[8] & 0x02) >> 1; // Device Units 10 (1-bit) decoded.deviceUnits11 = (bytes[8] & 0x04) >> 2; // Device Units 11 (1-bit) decoded.deviceUnits12 = (bytes[8] & 0x08) >> 3; // Device Units 12 (1-bit) decoded.wkupSensorTransmitEnable = Boolean((bytes[8] & 0x10) >> 4); // WKUP Sensor Transmit Enable (1-bit) decoded.batterySensorTransmitEnable = Boolean((bytes[8] & 0x20) >> 5); // Battery Sensor Transmit Enable (1-bit) decoded.coldJointSensorTransmitEnable = Boolean((bytes[8] & 0x40) >> 6); // Cold Joint Sensor Transmit Enable (1-bit) decoded.reserved1 = (bytes[8] & 0x80) >> 7; // Reserved 1 Field (1-bit) } else if (decoded.sequenceNumber == 1) { decoded.deviceType1 = bytes[1] & 0x07; // Device Type 1 (3-bit) decoded.enable1 = Boolean((bytes[1] & 0x08) >> 3); // Enable 1 (1-bit) decoded.deviceType2 = (bytes[1] & 0x70) >> 4; // Device Type 2 (3-bit) decoded.enable2 = Boolean((bytes[1] & 0x80) >> 7); // Enable 2 (1-bit) decoded.deviceType3 = bytes[2] & 0x07; // Device Type 3 (3-bit) decoded.enable3 = Boolean((bytes[2] & 0x08) >> 3); // Enable 3 (1-bit) decoded.deviceType4 = (bytes[2] & 0x70) >> 4; // Device Type 4 (3-bit) decoded.enable4 = Boolean((bytes[2] & 0x80) >> 7); // Enable 4 (1-bit) decoded.deviceType5 = bytes[3] & 0x07; // Device Type 5 (3-bit) decoded.enable5 = Boolean((bytes[3] & 0x08) >> 3); // Enable 5 (1-bit) decoded.deviceType6 = (bytes[3] & 0x70) >> 4; // Device Type 6 (3-bit) decoded.enable6 = Boolean((bytes[3] & 0x80) >> 7); // Enable 6 (1-bit) decoded.deviceType7 = bytes[4] & 0x07; // Device Type 7 (3-bit) decoded.enable7 = Boolean((bytes[4] & 0x08) >> 3); // Enable 7 (1-bit) decoded.deviceType8 = (bytes[4] & 0x70) >> 4; // Device Type 8 (3-bit) decoded.enable8 = Boolean((bytes[4] & 0x80) >> 7); // Enable 8 (1-bit) decoded.deviceType9 = bytes[5] & 0x07; // Device Type 9 (3-bit) decoded.enable9 = Boolean((bytes[5] & 0x08) >> 3); // Enable 9 (1-bit) decoded.deviceType10 = (bytes[5] & 0x70) >> 4; // Device Type 10 (3-bit) decoded.enable10 = Boolean((bytes[5] & 0x80) >> 7); // Enable 10 (1-bit) decoded.deviceType11 = bytes[6] & 0x07; // Device Type 11 (3-bit) decoded.enable11 = Boolean((bytes[6] & 0x08) >> 3); // Enable 11 (1-bit) decoded.deviceType12 = (bytes[6] & 0x70) >> 4; // Device Type 12 (3-bit) decoded.enable12 = Boolean((bytes[6] & 0x80) >> 7); // Enable 12 (1-bit) decoded.deviceUnits1 = bytes[7] & 0x03; // Device Units 1 (2-bit) decoded.deviceUnits2 = (bytes[7] & 0x0C) >> 2; // Device Units 2 (2-bit) decoded.deviceUnits3 = (bytes[7] & 0x30) >> 4; // Device Units 3 (2-bit) decoded.deviceUnits4 = (bytes[7] & 0xC0) >> 6; // Device Units 4 (2-bit) decoded.deviceUnits5 = bytes[8] & 0x03; // Device Units 5 (2-bit) decoded.deviceUnits6 = (bytes[8] & 0x0C) >> 2; // Device Units 6 (2-bit) decoded.deviceUnits7 = (bytes[8] & 0x30) >> 4; // Device Units 7 (2-bit) decoded.deviceUnits8 = (bytes[8] & 0xC0) >> 6; // Device Units 8 (2-bit) decoded.deviceUnits9 = bytes[9] & 0x03; // Device Units 9 (2-bit) decoded.deviceUnits10 = (bytes[9] & 0x0C) >> 2; // Device Units 10 (2-bit) decoded.deviceUnits11 = (bytes[9] & 0x30) >> 4; // Device Units 11 (2-bit) decoded.deviceUnits12 = (bytes[9] & 0xC0) >> 6; // Device Units 12 (2-bit) decoded.coldJointSensorUnits = bytes[10] & 0x03; // Cold Joint Sensor Units (2-bit) decoded.wkupSensorTransmitEnable = Boolean((bytes[10] & 0x04) >> 2); // WKUP Sensor Transmit Enable (1-bit) decoded.batterySensorTransmitEnable = Boolean((bytes[10] & 0x08) >> 3); // Battery Sensor Transmit Enable (1-bit) decoded.coldJointSensorTransmitEnable = Boolean((bytes[10] & 0x10) >> 4); // Cold Joint Sensor Transmit Enable (1-bit) decoded.tcSensorsTransmitEnable = Boolean((bytes[10] & 0x20) >> 5);; // TC Sensors Transmit Enable (1-bit) decoded.ainPayloadType = (bytes[10] & 0x40) >> 6; // Ain Payload Type (1-bit) decoded.reserved1 = (bytes[10] & 0x80) >> 7; // Reserved 1 Field (1-bit) } return decoded; } function parseVoboTCCalibrationConfigurationPayload(bytes) { var decoded = {}; decoded.subgroupID = bytes[0] & 0x0F; // Sub-Group ID (4-bit) decoded.sequenceNumber = (bytes[0] & 0xF0) >> 4; // Sequence Number (4-bit) if (decoded.sequenceNumber == 0) { decoded.gain1 = parseFloat(((((bytes[2] & 0x07) << 8) | bytes[1]) / 1000.0).toFixed(3)); // Gain 1 (11-bit) decoded.reserved1 = (bytes[2] & 0xF8) >> 3; // Reserved 1 Field (5-bit) decoded.offset1 = parseFloat(((((bytes[4] & 0x7F) << 8) | bytes[3]) / 1000.0 - 10.000).toFixed(3)); // Offset 1 (15-bit) decoded.reserved2 = (bytes[4] & 0x80) >> 7; // Reserved 2 Field (1-bit) decoded.gain2 = parseFloat(((((bytes[6] & 0x07) << 8) | bytes[5]) / 1000.0).toFixed(3)); // Gain 2 (11-bit) decoded.reserved3 = (bytes[6] & 0xF8) >> 3; // Reserved 3 Field (5-bit) decoded.offset2 = parseFloat(((((bytes[8] & 0x7F) << 8) | bytes[7]) / 1000.0 - 10.000).toFixed(3)); // Offset 2 (15-bit) decoded.reserved4 = (bytes[8] & 0x80) >> 7; // Reserved 4 Field (1-bit) } if (decoded.sequenceNumber == 1) { decoded.gain3 = parseFloat(((((bytes[2] & 0x07) << 8) | bytes[1]) / 1000.0).toFixed(3)); // Gain 3 (11-bit) decoded.reserved1 = (bytes[2] & 0xF8) >> 3; // Reserved 1 Field (5-bit) decoded.offset3 = parseFloat(((((bytes[4] & 0x7F) << 8) | bytes[3]) / 1000.0 - 10.000).toFixed(3)); // Offset 3 (15-bit) decoded.reserved2 = (bytes[4] & 0x80) >> 7; // Reserved 2 Field (1-bit) decoded.gain4 = parseFloat(((((bytes[6] & 0x07) << 8) | bytes[5]) / 1000.0).toFixed(3)); // Gain 4 (11-bit) decoded.reserved3 = (bytes[6] & 0xF8) >> 3; // Reserved 3 Field (5-bit) decoded.offset4 = parseFloat(((((bytes[8] & 0x7F) << 8) | bytes[7]) / 1000.0 - 10.000).toFixed(3)); // Offset 4 (15-bit) decoded.reserved4 = (bytes[8] & 0x80) >> 7; // Reserved 4 Field (1-bit) } if (decoded.sequenceNumber == 2) { decoded.gain5 = parseFloat(((((bytes[2] & 0x07) << 8) | bytes[1]) / 1000.0).toFixed(3)); // Gain 5 (11-bit) decoded.reserved1 = (bytes[2] & 0xF8) >> 3; // Reserved 1 Field (5-bit) decoded.offset5 = parseFloat(((((bytes[4] & 0x7F) << 8) | bytes[3]) / 1000.0 - 10.000).toFixed(3)); // Offset 5 (15-bit) decoded.reserved2 = (bytes[4] & 0x80) >> 7; // Reserved 2 Field (1-bit) decoded.gain6 = parseFloat(((((bytes[6] & 0x07) << 8) | bytes[5]) / 1000.0).toFixed(3)); // Gain 6 (11-bit) decoded.reserved3 = (bytes[6] & 0xF8) >> 3; // Reserved 3 Field (5-bit) decoded.offset6 = parseFloat(((((bytes[8] & 0x7F) << 8) | bytes[7]) / 1000.0 - 10.000).toFixed(3)); // Offset 6 (15-bit) decoded.reserved4 = (bytes[8] & 0x80) >> 7; // Reserved 4 Field (1-bit) } if (decoded.sequenceNumber == 3) { decoded.gain7 = parseFloat(((((bytes[2] & 0x07) << 8) | bytes[1]) / 1000.0).toFixed(3)); // Gain 7 (11-bit) decoded.reserved1 = (bytes[2] & 0xF8) >> 3; // Reserved 1 Field (5-bit) decoded.offset7 = parseFloat(((((bytes[4] & 0x7F) << 8) | bytes[3]) / 1000.0 - 10.000).toFixed(3)); // Offset 7 (15-bit) decoded.reserved2 = (bytes[4] & 0x80) >> 7; // Reserved 2 Field (1-bit) decoded.gain8 = parseFloat(((((bytes[6] & 0x07) << 8) | bytes[5]) / 1000.0).toFixed(3)); // Gain 8 (11-bit) decoded.reserved3 = (bytes[6] & 0xF8) >> 3; // Reserved 3 Field (5-bit) decoded.offset8 = parseFloat(((((bytes[8] & 0x7F) << 8) | bytes[7]) / 1000.0 - 10.000).toFixed(3)); // Offset 8 (15-bit) decoded.reserved4 = (bytes[8] & 0x80) >> 7; // Reserved 4 Field (1-bit) } if (decoded.sequenceNumber == 4) { decoded.gain9 = parseFloat(((((bytes[2] & 0x07) << 8) | bytes[1]) / 1000.0).toFixed(3)); // Gain 9 (11-bit) decoded.reserved1 = (bytes[2] & 0xF8) >> 3; // Reserved 1 Field (5-bit) decoded.offset9 = parseFloat(((((bytes[4] & 0x7F) << 8) | bytes[3]) / 1000.0 - 10.000).toFixed(3)); // Offset 9 (15-bit) decoded.reserved2 = (bytes[4] & 0x80) >> 7; // Reserved 2 Field (1-bit) decoded.gain10 = parseFloat(((((bytes[6] & 0x07) << 8) | bytes[5]) / 1000.0).toFixed(3)); // Gain 10 (11-bit) decoded.reserved3 = (bytes[6] & 0xF8) >> 3; // Reserved 3 Field (5-bit) decoded.offset10 = parseFloat(((((bytes[8] & 0x7F) << 8) | bytes[7]) / 1000.0 - 10.000).toFixed(3));// Offset 10 (15-bit) decoded.reserved4 = (bytes[8] & 0x80) >> 7; // Reserved 4 Field (1-bit) } if (decoded.sequenceNumber == 5) { decoded.gain11 = parseFloat(((((bytes[2] & 0x07) << 8) | bytes[1]) / 1000.0).toFixed(3)); // Gain 11 (11-bit) decoded.reserved1 = (bytes[2] & 0xF8) >> 3; // Reserved 1 Field (5-bit) decoded.offset11 = parseFloat(((((bytes[4] & 0x7F) << 8) | bytes[3]) / 1000.0 - 10.000).toFixed(3));// Offset 11 (15-bit) decoded.reserved2 = (bytes[4] & 0x80) >> 7; // Reserved 2 Field (1-bit) decoded.gain12 = parseFloat(((((bytes[6] & 0x07) << 8) | bytes[5]) / 1000.0).toFixed(3)); // Gain 12 (11-bit) decoded.reserved3 = (bytes[6] & 0xF8) >> 3; // Reserved 3 Field (5-bit) decoded.offset12 = parseFloat(((((bytes[8] & 0x7F) << 8) | bytes[7]) / 1000.0 - 10.000).toFixed(3));// Offset 12 (15-bit) decoded.reserved4 = (bytes[8] & 0x80) >> 7; // Reserved 4 Field (1-bit) } return decoded; } function parseVoboXPGeneralConfigurationPayload(bytes) { var decoded = {}; decoded.subgroupID = bytes[0] & 0x0F; // Sub-Group ID (4-bit) decoded.sequenceNumber = (bytes[0] & 0xF0) >> 4; // Sequence Number (4-bit) if (decoded.sequenceNumber == 0) { decoded.analogVoltage = parseFloat((bytes[1] / 10).toFixed(1)); // Analog Voltage (8-bit) decoded.powerTime = parseFloat((bytes[2] / 10).toFixed(1)); // Analog Power Time (8-bit) decoded.mbEnable = Boolean(bytes[3] & 0x01); // Modbus Enable (1-bit) decoded.reserved1 = (bytes[3] >> 1) & 0x7F; // Reserved 1 Field (7-bit) decoded.reserved2 = bytes[4] & 0x07; // Reserved 2 Field (3-bit) decoded.mbTransmitEnable = Boolean((bytes[4] >> 3) & 0x01); // Modbus Transmit Enable (1-bit) decoded.ainPayloadType = (bytes[4] >> 4) & 0x01; // Ain Payload Type Field (1-bit) decoded.reservedMAWE = Boolean((bytes[4] >> 5) & 0x01); // Reserved MAWE Field (1-bit) decoded.reservedMADE = Boolean((bytes[4] >> 6) & 0x01); // Reserved MADE Field (1-bit) decoded.reservedMAME = Boolean((bytes[4] >> 7) & 0x01); // Reserved MAME Field (1-bit) decoded.reservedDTC = (bytes[6] << 8) | bytes[5]; // Reserved DTC Field (16-bit) } if (decoded.sequenceNumber == 1) { decoded.stateRLY1 = bytes[1] & 0x01; // RLY1 Driver State (1-bit) decoded.stateRLY2 = (bytes[1] >> 1) & 0x01; // RLY2 Driver State (1-bit) decoded.stateRLY3 = (bytes[1] >> 2) & 0x01; // RLY3 Driver State (1-bit) decoded.stateRLY4 = (bytes[1] >> 3) & 0x01; // RLY4 Driver State (1-bit) decoded.reserved1 = (bytes[1] >> 4) & 0x0F; // Reserved 1 Field (4-bit) decoded.lorawanClass = bytes[2] & 0x01; // LoRaWAN Class (1-bit) decoded.contMeasEnable = Boolean((bytes[2] >> 1) & 0x01); // Continuous Measurement Enable (1-bit) decoded.reservedARDE = Boolean((bytes[2] >> 2) & 0x01); // Reserved ARDE Field (1-bit) decoded.pFailWarnEnable = Boolean((bytes[2] >> 3) & 0x01); // Power Fail Warning Enable (1-bit) decoded.pcntDin1Enable = Boolean((bytes[2] >> 4) & 0x01); // Pulse Count DIN1 Enable (1-bit) decoded.pcntDin2Enable = Boolean((bytes[2] >> 5) & 0x01); // Pulse Count DIN2 Enable (1-bit) decoded.reserved2 = (bytes[2] >> 6) & 0x03; // Reserved 2 Field (2-bit) decoded.pcntDin1Type = bytes[3] & 0x03; // Pulse Count DIN1 Type (2-bit) decoded.pcntDin2Type = (bytes[3] >> 2) & 0x03; // Pulse Count DIN2 Type (2-bit) decoded.reserved3 = (bytes[3] >> 4) & 0x0F; // Reserved 3 Field (2-bit) decoded.pcntPeriod = bytes[4] & 0x3F; // Pulse Count Period (6-bit) decoded.reserved4 = (bytes[4] >> 6) & 0x03; // Reserved 4 Field (2-bit) decoded.contMeasCycleTime = bytes[5] & 0x3F; // Continuous Measurement Cycle Time (6-bit) decoded.reserved5 = (bytes[5] >> 6) & 0x03; // Reserved 5 Field (2-bit) decoded.pFailThreshold = parseFloat((bytes[6] / 10).toFixed(1)); // Power Fail Warning Threshold Voltage (8-bit) decoded.serialPHY = bytes[7] & 0x01; // Serial Physical Layer (1-bit) decoded.reserved6 = (bytes[7] >> 1) & 0x07; // Reserved 6 Field (3-bit) decoded.serialProtoRS485 = (bytes[7] >> 4) & 0x01; // Serial Protocol for RS485 (1-bit) decoded.reserved7 = (bytes[7] >> 5) & 0x01; // Reserved 7 Field (1-bit) decoded.serialProtoRS232 = (bytes[7] >> 6) & 0x01; // Serial Protocol for RS232 (1-bit) decoded.reserved8 = (bytes[7] >> 7) & 0x01; // Reserved 8 Field (1-bit) } return decoded; } function parseVoboXPTransmitEncodingConfigurationPayload(bytes) { var decoded = {}; decoded.subgroupID = bytes[0] & 0x0F; // Sub-Group ID (4-bit) decoded.sequenceNumber = (bytes[0] & 0xF0) >> 4; // Sequence Number (4-bit) decoded.txAin1 = bytes[1] & 0x0F; // AIN1 Transmit Encoding (4-bit) decoded.txAin2 = (bytes[1] >> 4) & 0x0F; // AIN2 Transmit Encoding (4-bit) decoded.txAin3 = bytes[2] & 0x0F; // AIN3 Transmit Encoding (4-bit) decoded.txDin1 = (bytes[2] >> 4) & 0x0F; // DIN1 Transmit Encoding (4-bit) decoded.txDin2 = bytes[3] & 0x0F; // DIN2 Transmit Encoding (4-bit) decoded.txPcntDin1 = (bytes[3] >> 4) & 0x0F; // DIN1 Pulse Count Transmit Encoding (4-bit) decoded.txPcntDin2 = bytes[4] & 0x0F; // DIN2 Pulse Count Transmit Encoding (4-bit) decoded.txWKUP = (bytes[4] >> 4) & 0x0F; // WKUP Transmit Encoding (4-bit) decoded.txTemp = bytes[5] & 0x0F; // ADC Temperature Transmit Encoding (4-bit) decoded.txVoltRLY1 = (bytes[5] >> 4) & 0x0F; // RLY1 Voltage Transmit Encoding (4-bit) decoded.txVoltRLY2 = bytes[6] & 0x0F; // RLY2 Voltage Transmit Encoding (4-bit) decoded.txVoltRLY3 = (bytes[6] >> 4) & 0x0F; // RLY3 Voltage Transmit Encoding (4-bit) decoded.txVoltRLY4 = bytes[7] & 0x0F; // RLY4 Voltage Transmit Encoding (4-bit) decoded.txVoltVIN = (bytes[7] >> 4) & 0x0F; // VIN Voltage Transmit Encoding (4-bit) decoded.txVolt3V3 = bytes[8] & 0x0F; // 3V3 Voltage Transmit Encoding (4-bit) decoded.txVoltVPP = (bytes[8] >> 4) & 0x0F; // VPP Voltage Transmit Encoding (4-bit) decoded.txContMeasPeriod = bytes[9] & 0x0F; // Continuous Measurement Period Transmit Encoding (4-bit) decoded.txContMeasCnt = (bytes[9] >> 4) & 0x01; // Continuous Measurement Count Transmit Encoding (1-bit) decoded.reserved1 = (bytes[9] >> 5) & 0x07; // Reserved 1 Field (3-bit) return decoded; } function parseVoboXPRelayPulseConfigurationPayload(bytes) { var decoded = {}; decoded.subgroupID = bytes[0] & 0x0F; // Sub-Group ID (4-bit) decoded.sequenceNumber = (bytes[0] & 0xF0) >> 4; // Sequence Number (4-bit) if (decoded.sequenceNumber == 0) { decoded.pulseDelayRLY1 = (bytes[2] << 8) | bytes[1]; // RLY1 Driver Pulse Delay (16-bit) decoded.pulsePeriodRLY1 = (bytes[4] << 8) | bytes[3]; // RLY1 Driver Pulse Period (16-bit) decoded.pulseDelayRLY2 = (bytes[6] << 8) | bytes[5]; // RLY2 Driver Pulse Delay (16-bit) decoded.pulsePeriodRLY2 = (bytes[8] << 8) | bytes[7]; // RLY2 Driver Pulse Period (16-bit) } else if (decoded.sequenceNumber == 1) { decoded.pulseDelayRLY3 = (bytes[2] << 8) | bytes[1]; // RLY3 Driver Pulse Delay (16-bit) decoded.pulsePeriodRLY3 = (bytes[4] << 8) | bytes[3]; // RLY3 Driver Pulse Period (16-bit) decoded.pulseDelayRLY4 = (bytes[6] << 8) | bytes[5]; // RLY4 Driver Pulse Delay (16-bit) decoded.pulsePeriodRLY4 = (bytes[8] << 8) | bytes[7]; // RLY4 Driver Pulse Period (16-bit) } return decoded; } function parseConfigurationPayload(bytes, fport) { var decoded = {}; var subgroupID = bytes[0] & 0x0F; // VoBo Lib Configuration Payloads if(subgroupID == 0) decoded = parseVoboLibGeneralConfigurationPayload(bytes, fport); else if(subgroupID == 1) decoded = parseVoboLibVoboSyncConfigurationPayload(bytes); // VoBo XX Configuration Payloads else if(fport == 70 && subgroupID == 4) decoded = parseVoboXXGeneralConfigurationPayload(bytes); else if(fport == 70 && subgroupID == 5) decoded = parseVoboXXModbusGeneralConfigurationPayload(bytes); else if(fport == 70 && (subgroupID == 6 || subgroupID == 7)) decoded = parseVoboXXModbusGroupsEnableConfigurationPayload(bytes); else if(fport == 70 && (subgroupID == 8 || subgroupID == 9 || subgroupID == 10)) decoded = parseVoboXXModbusGroupsConfigurationPayload(bytes); else if(fport == 70 && subgroupID == 11) decoded = parseVoboXXModbusPayloadsSlotsConfigurationPayload(bytes); else if(fport == 70 && subgroupID == 12) decoded = parseVoboXXEngineeringUnitsConfigurationPayload(bytes); // VoBo TC Configuration Payloads else if(fport == 71 && subgroupID == 4) decoded = parseVoboTCGeneralConfigurationPayload(bytes); else if(fport == 71 && subgroupID == 5) decoded = parseVoboTCCalibrationConfigurationPayload(bytes); // VoBo XP Configuration Payloads else if(fport == 72 && subgroupID == 4) decoded = parseVoboXPGeneralConfigurationPayload(bytes); else if(fport == 72 && subgroupID == 5) decoded = parseVoboXXModbusGeneralConfigurationPayload(bytes); else if(fport == 72 && (subgroupID == 6 || subgroupID == 7)) decoded = parseVoboXXModbusGroupsEnableConfigurationPayload(bytes); else if(fport == 72 && (subgroupID == 8 || subgroupID == 9 || subgroupID == 10)) decoded = parseVoboXXModbusGroupsConfigurationPayload(bytes); else if(fport == 72 && subgroupID == 11) decoded = parseVoboXXModbusPayloadsSlotsConfigurationPayload(bytes); else if(fport == 72 && subgroupID == 12) decoded = parseVoboXXEngineeringUnitsConfigurationPayload(bytes); else if(fport == 72 && subgroupID == 13) decoded = parseVoboXPTransmitEncodingConfigurationPayload(bytes); else if(fport == 72 && subgroupID == 14) decoded = parseVoboXPRelayPulseConfigurationPayload(bytes); return decoded; } function parseModbusGenericPayload(bytes, fport) { var decoded = {}; var payloadSize = bytes.length; var byteIdx = 0; while(byteIdx < (payloadSize - 1)) { var groupIdx = bytes[byteIdx] & 0x3F; if(groupIdx > 0) { var blockType = (bytes[byteIdx] >> 6) & 0x03; byteIdx++; if(blockType == 0x00) // One register block { var registerValue = (bytes[byteIdx + 1] << 8) | (bytes[byteIdx]); decoded["group" + groupIdx + "register1"] = registerValue; byteIdx += 2; } else if(blockType == 0x01) // Two registers block { var register1Value = (bytes[byteIdx + 1] << 8) | (bytes[byteIdx]); decoded["group" + groupIdx + "register1"] = register1Value; var register2Value = (bytes[byteIdx + 3] << 8) | (bytes[byteIdx + 2]); decoded["group" + groupIdx + "register2"] = register2Value; byteIdx += (2 * 2); } else if(blockType == 0x02) // Non fragmented block { var registersValues = []; var numOfRegisters = bytes[byteIdx] & 0x7F; byteIdx++; for (let regIdx = 0; regIdx < numOfRegisters; regIdx++) { var registerValue = (bytes[byteIdx + 1] << 8) | (bytes[byteIdx]); decoded["group" + groupIdx + "register" + (regIdx + 1)] = registerValue; registersValues.push(registerValue); byteIdx += 2; } } else if(blockType == 0x03) // Fragmented block { var registersValues = []; var numOfRegisters = bytes[byteIdx] & 0x7F; byteIdx++; var registerOffset = bytes[byteIdx] & 0x7F; byteIdx++; var numOfRegToRead = numOfRegisters - registerOffset; var freeRegToRead = Math.floor((payloadSize - byteIdx) / 2); if(numOfRegToRead > freeRegToRead) numOfRegToRead = freeRegToRead; for (let regIdx = 0; regIdx < numOfRegToRead; regIdx++) { var registerValue = (bytes[byteIdx + 1] << 8) | (bytes[byteIdx]); decoded["group" + groupIdx + "register" + (registerOffset + regIdx + 1)] = registerValue; registersValues.push(registerValue); byteIdx += 2; } } } else break; } return decoded; } function parseAnalogInputVariableLengthPayload(bytes, fport) { var decoded = {}; var decodedAinPayloads = []; const AIN_PAYLOAD_SIZE = 6; var payloadLength = bytes.length var byteIdx = 0; while(byteIdx < payloadLength) { var ainPayload = bytes.slice(byteIdx, byteIdx + AIN_PAYLOAD_SIZE) var decodedAinPayload = parseOneAnalogSensorPayload(ainPayload) decodedAinPayloads.push(decodedAinPayload) byteIdx += AIN_PAYLOAD_SIZE; } decoded.ainPayloads = decodedAinPayloads; decoded.numOfAinPayloads = decodedAinPayloads.length return decoded; } function parseModbusStandardVariableLengthPayload(bytes, fport) { var decoded = {}; var decodedModbusSlots = {}; var payloadLength = bytes.length var firstSlotNum = bytes[payloadLength - 1] const REGISTER_SIZE = 2 var byteIdx = 0; while(byteIdx < payloadLength - 1) { var registerIdx = byteIdx / 2; var slotIdx = firstSlotNum + registerIdx; decodedModbusSlots["Modbus"+ slotIdx] = (bytes[byteIdx + 1] << 8) | bytes[byteIdx]; byteIdx += REGISTER_SIZE; } decoded.modbusSlots = decodedModbusSlots; decoded.firstSlotNum = firstSlotNum; decoded.numModbusSlots = (payloadLength - 1) / 2; return decoded; } function lookupAnalogSensorName(voboType, sensorNum) { const analogSensorsTableXX = [ {"Analog Sensor Number":"0","Analog Sensor Name":"Battery Voltage"}, {"Analog Sensor Number":"1","Analog Sensor Name":"AIN1"}, {"Analog Sensor Number":"2","Analog Sensor Name":"AIN2"}, {"Analog Sensor Number":"3","Analog Sensor Name":"AIN3"}, {"Analog Sensor Number":"15","Analog Sensor Name":"ADC Temperature"} ]; const analogSensorsTableTC = [ {"Analog Sensor Number":"0","Analog Sensor Name":"Battery Voltage"}, {"Analog Sensor Number":"1","Analog Sensor Name":"TC1"}, {"Analog Sensor Number":"2","Analog Sensor Name":"TC2"}, {"Analog Sensor Number":"3","Analog Sensor Name":"TC3"}, {"Analog Sensor Number":"4","Analog Sensor Name":"TC4"}, {"Analog Sensor Number":"5","Analog Sensor Name":"TC5"}, {"Analog Sensor Number":"6","Analog Sensor Name":"TC6"}, {"Analog Sensor Number":"7","Analog Sensor Name":"TC7"}, {"Analog Sensor Number":"8","Analog Sensor Name":"TC8"}, {"Analog Sensor Number":"9","Analog Sensor Name":"TC9"}, {"Analog Sensor Number":"10","Analog Sensor Name":"TC10"}, {"Analog Sensor Number":"11","Analog Sensor Name":"TC11"}, {"Analog Sensor Number":"12","Analog Sensor Name":"TC12"}, {"Analog Sensor Number":"13","Analog Sensor Name":"Cold Joint Temperature"} ]; const analogSensorsTableXP = [ {"Analog Sensor Number":"0","Analog Sensor Name":"3.3V Supply Voltage"}, {"Analog Sensor Number":"1","Analog Sensor Name":"AIN1"}, {"Analog Sensor Number":"2","Analog Sensor Name":"AIN2"}, {"Analog Sensor Number":"3","Analog Sensor Name":"AIN3"}, {"Analog Sensor Number":"4","Analog Sensor Name":"DIN1"}, {"Analog Sensor Number":"5","Analog Sensor Name":"DIN1 Pulse Count"}, {"Analog Sensor Number":"6","Analog Sensor Name":"DIN2"}, {"Analog Sensor Number":"7","Analog Sensor Name":"DIN2 Pulse Count"}, {"Analog Sensor Number":"8","Analog Sensor Name":"RLY1 Voltage"}, {"Analog Sensor Number":"9","Analog Sensor Name":"RLY2 Voltage"}, {"Analog Sensor Number":"10","Analog Sensor Name":"RLY3 Voltage"}, {"Analog Sensor Number":"11","Analog Sensor Name":"RLY4 Voltage"}, {"Analog Sensor Number":"12","Analog Sensor Name":"WKUP"}, {"Analog Sensor Number":"13","Analog Sensor Name":"VPP Voltage"}, {"Analog Sensor Number":"14","Analog Sensor Name":"VIN Voltage"}, {"Analog Sensor Number":"15","Analog Sensor Name":"ADC Temperature"}, {"Analog Sensor Number":"16","Analog Sensor Name":"Cont Meas Period"}, {"Analog Sensor Number":"17","Analog Sensor Name":"Cont Meas Count"} ]; var analogSensorsTable = []; var analogSensorNameSuffix = ""; if(voboType == "VoBoXX") { analogSensorsTable = analogSensorsTableXX; } else if(voboType == "VoBoTC") { analogSensorsTable = analogSensorsTableTC; } else if(voboType == "VoBoXP") { analogSensorsTable = analogSensorsTableXP; var sensorNumEncoding = (sensorNum & 0xC0) >> 6; if(sensorNumEncoding == 0x01) analogSensorNameSuffix = " (Max)"; else if(sensorNumEncoding == 0x02) analogSensorNameSuffix = " (Min)"; else if(sensorNumEncoding == 0x03) analogSensorNameSuffix = " (Avg)"; sensorNum = sensorNum & 0x3F; } else { errorMsg = voboType + " -- " + "Invalid VoBo Type. Use \"VoBoXX\", \"VoBoTC\" or \"VoBoXP\""; throw new Error(errorMsg); } var analogSensorRow = analogSensorsTable.find(element => element["Analog Sensor Number"] == sensorNum.toString()); if(typeof analogSensorRow == 'undefined') { var genericAnalogSensorName = "AnalogSensor" + sensorNum.toString(); return genericAnalogSensorName; } var analogSensorName = analogSensorRow["Analog Sensor Name"]; if(voboType == "VoBoXP") { analogSensorName = analogSensorName + analogSensorNameSuffix; } return analogSensorName; } function lookupDigitalSensorName(voboType, sensorNum) { const digitalSensorsTableXX = [ {"Digital Sensor Number":"0","Digital Sensor Name":"WKUP"}, {"Digital Sensor Number":"1","Digital Sensor Name":"DIN1"}, {"Digital Sensor Number":"2","Digital Sensor Name":"DIN2"}, {"Digital Sensor Number":"3","Digital Sensor Name":"DIN3"} ]; const digitalSensorsTableTC = [ {"Digital Sensor Number":"0","Digital Sensor Name":"WKUP"} ]; const digitalSensorsTableXP = [ {"Digital Sensor Number":"0","Digital Sensor Name":"WKUP"}, {"Digital Sensor Number":"1","Digital Sensor Name":"DIN1"}, {"Digital Sensor Number":"2","Digital Sensor Name":"DIN2"} ]; var digitalSensorsTable = []; if(voboType == "VoBoXX") { digitalSensorsTable = digitalSensorsTableXX; } else if(voboType == "VoBoTC") { digitalSensorsTable = digitalSensorsTableTC; } else if(voboType == "VoBoXP") { digitalSensorsTable = digitalSensorsTableXP; } else { errorMsg = voboType + " -- " + "Invalid VoBo Type. Use \"VoBoXX\", \"VoBoTC\" or \"VoBoXP\""; throw new Error(errorMsg); } var digitalSensorRow = digitalSensorsTable.find(element => element["Digital Sensor Number"] == sensorNum.toString()); if(typeof digitalSensorRow == 'undefined') { var genericDigitalSensorName = "DigitalSensor" + sensorNum.toString(); return genericDigitalSensorName; } var digitalSensorName = digitalSensorRow["Digital Sensor Name"]; return digitalSensorName; } function lookupUnits(outputType, unitCode) { const engUnitsTable = [ {"Units Code":"1","Description":"inches of water at 20 degC (68 degF)","Abbreviated Units":"inH2O (20 degC or 68 degF)"}, {"Units Code":"2","Description":"inches of mercury at 0 degC (32 degF)","Abbreviated Units":"inHg (20 degC or 68 degF)"}, {"Units Code":"3","Description":"feet of water at 20 degC (68 degF)","Abbreviated Units":"ftH2O (20 degC or 68 degF)"}, {"Units Code":"4","Description":"millimeters of water at 20 degC (68 degF)","Abbreviated Units":"mmH2O (20 degC or 68 degF)"}, {"Units Code":"5","Description":"millimeters of mercury at 0 degC (32 degF)","Abbreviated Units":"mmHg (0 degC or 32 degF)"}, {"Units Code":"6","Description":"pounds per square inch","Abbreviated Units":"psi"}, {"Units Code":"7","Description":"bars","Abbreviated Units":"bar"}, {"Units Code":"8","Description":"millibars","Abbreviated Units":"mbar"}, {"Units Code":"9","Description":"grams per square centimeter","Abbreviated Units":"g/cm^2"}, {"Units Code":"10","Description":"kilograms per square centimeter","Abbreviated Units":"kg/cm^2"}, {"Units Code":"11","Description":"pascals","Abbreviated Units":"Pa"}, {"Units Code":"12","Description":"kilopascals","Abbreviated Units":"kPa"}, {"Units Code":"13","Description":"torr","Abbreviated Units":"torr"}, {"Units Code":"14","Description":"atmospheres","Abbreviated Units":"atm"}, {"Units Code":"15","Description":"cubic feet per minute","Abbreviated Units":"ft^3/min"}, {"Units Code":"16","Description":"gallons per minute","Abbreviated Units":"usg/min"}, {"Units Code":"17","Description":"liters per minute","Abbreviated Units":"L/min"}, {"Units Code":"18","Description":"imperial gallons per minute","Abbreviated Units":"impgal/min"}, {"Units Code":"19","Description":"cubic meter per hour","Abbreviated Units":"m^3/h"}, {"Units Code":"20","Description":"feet per second","Abbreviated Units":"ft/s"}, {"Units Code":"21","Description":"meters per seond","Abbreviated Units":"m/s"}, {"Units Code":"22","Description":"gallons per second","Abbreviated Units":"usg/s"}, {"Units Code":"23","Description":"million gallons per day","Abbreviated Units":"Musg/d"}, {"Units Code":"24","Description":"liters per second","Abbreviated Units":"L/s"}, {"Units Code":"25","Description":"million liters per day","Abbreviated Units":"ML/d"}, {"Units Code":"26","Description":"cubic feet per second","Abbreviated Units":"ft^3/s"}, {"Units Code":"27","Description":"cubic feet per day","Abbreviated Units":"ft^3/d"}, {"Units Code":"28","Description":"cubic meters per second","Abbreviated Units":"m^3/s"}, {"Units Code":"29","Description":"cubic meters per day","Abbreviated Units":"m^3/d"}, {"Units Code":"30","Description":"imperial gallons per hour","Abbreviated Units":"impgal/h"}, {"Units Code":"31","Description":"imperial gallons per day","Abbreviated Units":"impgal/d"}, {"Units Code":"32","Description":"Degrees Celsius","Abbreviated Units":"C"}, {"Units Code":"33","Description":"Degrees Fahrenheit","Abbreviated Units":"F"}, {"Units Code":"34","Description":"Degrees Rankine","Abbreviated Units":"R"}, {"Units Code":"35","Description":"Kelvin","Abbreviated Units":"K"}, {"Units Code":"36","Description":"millivolts","Abbreviated Units":"mV"}, {"Units Code":"37","Description":"ohms","Abbreviated Units":"ohm"}, {"Units Code":"38","Description":"hertz","Abbreviated Units":"hz"}, {"Units Code":"39","Description":"milliamperes","Abbreviated Units":"mA"}, {"Units Code":"40","Description":"gallons","Abbreviated Units":"usg"}, {"Units Code":"41","Description":"liters","Abbreviated Units":"L"}, {"Units Code":"42","Description":"imperial gallons","Abbreviated Units":"impgal"}, {"Units Code":"43","Description":"cubic meters","Abbreviated Units":"m^3"}, {"Units Code":"44","Description":"feet","Abbreviated Units":"ft"}, {"Units Code":"45","Description":"meters","Abbreviated Units":"m"}, {"Units Code":"46","Description":"barrels","Abbreviated Units":"bbl"}, {"Units Code":"47","Description":"inches","Abbreviated Units":"in"}, {"Units Code":"48","Description":"centimeters","Abbreviated Units":"cm"}, {"Units Code":"49","Description":"millimeters","Abbreviated Units":"mm"}, {"Units Code":"50","Description":"minutes","Abbreviated Units":"min"}, {"Units Code":"51","Description":"seconds","Abbreviated Units":"s"}, {"Units Code":"52","Description":"hours","Abbreviated Units":"h"}, {"Units Code":"53","Description":"days","Abbreviated Units":"d"}, {"Units Code":"54","Description":"centistokes","Abbreviated Units":"centistokes"}, {"Units Code":"55","Description":"centipoise","Abbreviated Units":"cP"}, {"Units Code":"56","Description":"microsiemens","Abbreviated Units":"microsiemens"}, {"Units Code":"57","Description":"percent","Abbreviated Units":"%"}, {"Units Code":"58","Description":"volts","Abbreviated Units":"V"}, {"Units Code":"59","Description":"pH","Abbreviated Units":"pH"}, {"Units Code":"60","Description":"grams","Abbreviated Units":"g"}, {"Units Code":"61","Description":"kilograms","Abbreviated Units":"kg"}, {"Units Code":"62","Description":"metric tons","Abbreviated Units":"t"}, {"Units Code":"63","Description":"pounds","Abbreviated Units":"lb"}, {"Units Code":"64","Description":"short tons","Abbreviated Units":"short ton"}, {"Units Code":"65","Description":"long tons","Abbreviated Units":"long ton"}, {"Units Code":"66","Description":"millisiemens per centimeter","Abbreviated Units":"millisiemens/cm"}, {"Units Code":"67","Description":"microsiemens per centimeter","Abbreviated Units":"microsiemens/cm"}, {"Units Code":"68","Description":"newton","Abbreviated Units":"N"}, {"Units Code":"69","Description":"newton meter","Abbreviated Units":"N m"}, {"Units Code":"70","Description":"grams per second","Abbreviated Units":"g/s"}, {"Units Code":"71","Description":"grams per minute","Abbreviated Units":"g/min"}, {"Units Code":"72","Description":"grams per hour","Abbreviated Units":"g/h"}, {"Units Code":"73","Description":"kilograms per second","Abbreviated Units":"kg/s"}, {"Units Code":"74","Description":"kilograms per minute","Abbreviated Units":"kg/min"}, {"Units Code":"75","Description":"kilograms per hour","Abbreviated Units":"kg/h"}, {"Units Code":"76","Description":"kilograms per day","Abbreviated Units":"kg/d"}, {"Units Code":"77","Description":"metric tons per minute","Abbreviated Units":"t/min"}, {"Units Code":"78","Description":"metric tons per hour","Abbreviated Units":"t/h"}, {"Units Code":"79","Description":"metric tons per day","Abbreviated Units":"t/d"}, {"Units Code":"80","Description":"pounds per second","Abbreviated Units":"lb/s"}, {"Units Code":"81","Description":"pounds per minute","Abbreviated Units":"lb/min"}, {"Units Code":"82","Description":"pounds per hour","Abbreviated Units":"lb/h"}, {"Units Code":"83","Description":"pounds per day","Abbreviated Units":"lb/d"}, {"Units Code":"84","Description":"short tons per minute","Abbreviated Units":"short ton/min"}, {"Units Code":"85","Description":"short tons per hour","Abbreviated Units":"short ton/h"}, {"Units Code":"86","Description":"short tons per day","Abbreviated Units":"short ton/d"}, {"Units Code":"87","Description":"long tons per hour","Abbreviated Units":"long ton/h"}, {"Units Code":"88","Description":"long tons per day","Abbreviated Units":"long ton/d"}, {"Units Code":"89","Description":"deka therm","Abbreviated Units":"Dth"}, {"Units Code":"90","Description":"specific gravity units","Abbreviated Units":"specific gravity units"}, {"Units Code":"91","Description":"grams per cubic centimeter","Abbreviated Units":"g/cm^3"}, {"Units Code":"92","Description":"kilograms per cubic meter","Abbreviated Units":"kg/m^3"}, {"Units Code":"93","Description":"pounds per gallon","Abbreviated Units":"lb/usg"}, {"Units Code":"94","Description":"pounds per cubic feet","Abbreviated Units":"lb/ft^3"}, {"Units Code":"95","Description":"grams per milliliter","Abbreviated Units":"g/mL"}, {"Units Code":"96","Description":"kilograms per liter","Abbreviated Units":"kg/L"}, {"Units Code":"97","Description":"grams per liter","Abbreviated Units":"g/L"}, {"Units Code":"98","Description":"pounds per cubic inch","Abbreviated Units":"lb/in^3"}, {"Units Code":"99","Description":"short tons per cubic yard","Abbreviated Units":"short ton/yd^3"}, {"Units Code":"100","Description":"degrees twaddell","Abbreviated Units":"degTw"}, {"Units Code":"101","Description":"degrees brix","Abbreviated Units":"degBx"}, {"Units Code":"102","Description":"degrees baume heavy","Abbreviated Units":"BH"}, {"Units Code":"103","Description":"degrees baume light","Abbreviated Units":"BL"}, {"Units Code":"104","Description":"degrees API","Abbreviated Units":"degAPI"}, {"Units Code":"105","Description":"percent solids per weight","Abbreviated Units":"% solid/weight"}, {"Units Code":"106","Description":"percent solids per volume","Abbreviated Units":"% solid/volume"}, {"Units Code":"107","Description":"degrees balling","Abbreviated Units":"degrees balling"}, {"Units Code":"108","Description":"proof per volume","Abbreviated Units":"proof/volume"}, {"Units Code":"109","Description":"proof per mass","Abbreviated Units":"proof/mass"}, {"Units Code":"110","Description":"bushels","Abbreviated Units":"bushel"}, {"Units Code":"111","Description":"cubic yards","Abbreviated Units":"yd^3"}, {"Units Code":"112","Description":"cubic feet","Abbreviated Units":"ft^3"}, {"Units Code":"113","Description":"cubic inches","Abbreviated Units":"in^3"}, {"Units Code":"114","Description":"inches per second","Abbreviated Units":"in/s"}, {"Units Code":"115","Description":"inches per minute","Abbreviated Units":"in/min"}, {"Units Code":"116","Description":"feet per minute","Abbreviated Units":"ft/min"}, {"Units Code":"117","Description":"degrees per second","Abbreviated Units":"deg/s"}, {"Units Code":"118","Description":"revolutions per second","Abbreviated Units":"rev/s"}, {"Units Code":"119","Description":"revolutions per minute","Abbreviated Units":"rpm"}, {"Units Code":"120","Description":"meters per hour","Abbreviated Units":"m/hr"}, {"Units Code":"121","Description":"normal cubic meters per hour","Abbreviated Units":"normal m^3/h"}, {"Units Code":"122","Description":"normal liter per hour","Abbreviated Units":"normal L/h"}, {"Units Code":"123","Description":"standard cubic feet per minute","Abbreviated Units":"standard ft^3/min"}, {"Units Code":"124","Description":"bbl liq","Abbreviated Units":"bbl liq"}, {"Units Code":"125","Description":"ounce","Abbreviated Units":"oz"}, {"Units Code":"126","Description":"foot pound force","Abbreviated Units":"ft lb force"}, {"Units Code":"127","Description":"kilowatt","Abbreviated Units":"kW"}, {"Units Code":"128","Description":"kilowatt hour","Abbreviated Units":"KWh"}, {"Units Code":"129","Description":"horsepower","Abbreviated Units":"hp"}, {"Units Code":"130","Description":"cubic feet per hour","Abbreviated Units":"ft^3/h"}, {"Units Code":"131","Description":"cubic meters per minute","Abbreviated Units":"m^3/min"}, {"Units Code":"132","Description":"barrels per second","Abbreviated Units":"bbl/s"}, {"Units Code":"133","Description":"barrels per minute","Abbreviated Units":"bbl/min"}, {"Units Code":"134","Description":"barrels per hour","Abbreviated Units":"bbl/h"}, {"Units Code":"135","Description":"barrels per day","Abbreviated Units":"bbl/d"}, {"Units Code":"136","Description":"gallons per hour","Abbreviated Units":"usg/h"}, {"Units Code":"137","Description":"imperial gallons per second","Abbreviated Units":"impgal/s"}, {"Units Code":"138","Description":"liters per hour","Abbreviated Units":"L/h"}, {"Units Code":"139","Description":"parts per million","Abbreviated Units":"ppm"}, {"Units Code":"140","Description":"megacalorie per hour","Abbreviated Units":"Mcal/h"}, {"Units Code":"141","Description":"megajoule per hour","Abbreviated Units":"MJ/h"}, {"Units Code":"142","Description":"British thermal unit per hour","Abbreviated Units":"BTU/h"}, {"Units Code":"143","Description":"degrees","Abbreviated Units":"degrees"}, {"Units Code":"144","Description":"radian","Abbreviated Units":"rad"}, {"Units Code":"145","Description":"inches of water at 15.6 degC (60 degF)","Abbreviated Units":"inH20 (15.6 degC or 60 degF)"}, {"Units Code":"146","Description":"micrograms per liter","Abbreviated Units":"micrograms/L"}, {"Units Code":"147","Description":"micrograms per cubic meter","Abbreviated Units":"micrograms/m^3"}, {"Units Code":"148","Description":"percent consitency","Abbreviated Units":"% consistency"}, {"Units Code":"149","Description":"volume percent","Abbreviated Units":"volume %"}, {"Units Code":"150","Description":"percent steam quality","Abbreviated Units":"% steam quality"}, {"Units Code":"151","Description":"feet in sixteenths","Abbreviated Units":"ft in sixteenths"}, {"Units Code":"152","Description":"cubic feet per pound","Abbreviated Units":"ft^3/lb"}, {"Units Code":"153","Description":"picofarads","Abbreviated Units":"pF"}, {"Units Code":"154","Description":"milliliters per liter","Abbreviated Units":"mL/L"}, {"Units Code":"155","Description":"microliters per liter","Abbreviated Units":"microliters/L"}, {"Units Code":"156","Description":"percent plato","Abbreviated Units":"% plato"}, {"Units Code":"157","Description":"percent lower explosion level","Abbreviated Units":"% lower explosion level"}, {"Units Code":"158","Description":"mega calorie","Abbreviated Units":"Mcal"}, {"Units Code":"159","Description":"kiloohms","Abbreviated Units":"kohm"}, {"Units Code":"160","Description":"megajoule","Abbreviated Units":"MJ"}, {"Units Code":"161","Description":"British thermal unit","Abbreviated Units":"BTU"}, {"Units Code":"162","Description":"normal cubic meter","Abbreviated Units":"normal m^3"}, {"Units Code":"163","Description":"normal liter","Abbreviated Units":"normal L"}, {"Units Code":"164","Description":"standard cubic feet","Abbreviated Units":"normal ft^3"}, {"Units Code":"165","Description":"parts per billion","Abbreviated Units":"parts/billion"}, {"Units Code":"166","Description":"ampere","Abbreviated Units":"A"}, {"Units Code":"167","Description":"millimeters per meter","Abbreviated Units":"mm/m"}, {"Units Code":"168","Description":"seconds since epoch","Abbreviated Units":"epoch sec"}, {"Units Code":"169","Description":"no units","Abbreviated Units":"no units"}, {"Units Code":"170","Description":"percentage","Abbreviated Units":"%"}, {"Units Code":"171","Description":"kilohertz","Abbreviated Units":"khz"}, {"Units Code":"235","Description":"gallons per day","Abbreviated Units":"usg/d"}, {"Units Code":"236","Description":"hectoliters","Abbreviated Units":"hL"}, {"Units Code":"237","Description":"megapascals","Abbreviated Units":"MPa"}, {"Units Code":"238","Description":"inches of water at 4 degC (39.2 degF)","Abbreviated Units":"inH2O (4 degC or 39.2 degF)"}, {"Units Code":"239","Description":"millimeters of water at 4 degC (39.2 degF)","Abbreviated Units":"mmH2O (4 degC or 39.2 degF)"}, {"Units Code":"253","Description":"edge count or pulse count","Abbreviated Units":"counts"}, {"Units Code":"254","Description":"raw ADC code","Abbreviated Units":"ADC code"}, {"Units Code":"255","Description":"data not valid","Abbreviated Units":"data not valid"} ]; var outputString = ""; var engUnitsRow = engUnitsTable.find(element => element["Units Code"] == unitCode.toString()); if(typeof engUnitsRow == 'undefined') { return "Unknown Units"; } if(outputType == 0) { outputString = engUnitsRow["Description"]; } else if(outputType == 1) { outputString = engUnitsRow["Abbreviated Units"]; } else { errorMsg = outputType + " -- " + "Invalid Output Type. Use 0 for Description or 1 for Abbreviated Units."; throw new Error(errorMsg); } return outputString; } //=========================================================== // Utility functions // Decoder doesn't call these functions // Functions provided for customer usage // // List of functions: // hexToFloat - Conversion from Hex to Float Number // modbusToFloat_AB_CD_to_ABCD - Takes two Modbus registers, does no swapping, and converts them to 32 bit floating point number // modbusToFloat_AB_CD_to_DCBA - Takes two Modbus registers, does byte and word swapping, and converts them to 32 bit floating point number // modbusToFloat_AB_CD_to_BADC - Takes two Modbus registers, does byte swapping, and converts them to 32bit floating point number // modbusToFloat_AB_CD_to_CDAB - Takes two Modbus registers, does word swapping, and converts them to 32bit floating point number // printPayload - Prints Decoded payloads in formatted fashion //=========================================================== /* istanbul ignore next */ function hexToFloat(hex) { var s = hex >> 31 ? -1 : 1; var e = (hex >> 23) & 0xFF; return s * (hex & 0x7fffff | 0x800000) * 1.0 / Math.pow(2, 23) * Math.pow(2, (e - 127)) } /* istanbul ignore next */ function modbusToFloat_AB_CD_to_ABCD(modbusReg0, modbusReg1) { var floatVal = 0; var bytes = Buffer.alloc(4); bytes[0] = (modbusReg1 & 0xFF00) >> 8; bytes[1] = modbusReg1 & 0xFF; bytes[2] = (modbusReg0 & 0xFF00) >> 8; bytes[3] = modbusReg0 & 0xFF; floatVal = bytes.readFloatBE(0); return floatVal; } /* istanbul ignore next */ function modbusToFloat_AB_CD_to_DCBA(modbusReg0, modbusReg1) { var floatVal = 0; var bytes = Buffer.alloc(4); bytes[0] = modbusReg0 & 0xFF; bytes[1] = (modbusReg0 & 0xFF00) >> 8; bytes[2] = modbusReg1 & 0xFF; bytes[3] = (modbusReg1 & 0xFF00) >> 8; floatVal = bytes.readFloatBE(0); return floatVal; } /* istanbul ignore next */ function modbusToFloat_AB_CD_to_BADC(modbusReg0, modbusReg1) { var floatVal = 0; var bytes = Buffer.alloc(4); bytes[0] = modbusReg1 & 0xFF; bytes[1] = (modbusReg1 & 0xFF00) >> 8; bytes[2] = modbusReg0 & 0xFF; bytes[3] = (modbusReg0 & 0xFF00) >> 8; floatVal = bytes.readFloatBE(0); return floatVal; } /* istanbul ignore next */ function modbusToFloat_AB_CD_to_CDAB(modbusReg0, modbusReg1) { var floatVal = 0; var bytes = Buffer.alloc(4); bytes[0] = (modbusReg0 & 0xFF00) >> 8; bytes[1] = modbusReg0 & 0xFF; bytes[2] = (modbusReg1 & 0xFF00) >> 8; bytes[3] = modbusReg1 & 0xFF; floatVal = bytes.readFloatBE(0); return floatVal; } /* istanbul ignore next */ function printPayload(payload) { if(payload.data.payloadType == "Standard") { let modbusSlot0HexString = payload.data.Modbus0.toString(16).toUpperCase().padStart(4,0); console.log("%s | %s | %s | %s | " + "DIN1 = %d | DIN2 = %d | DIN3 = %d | WKUP = %d | ADC1 = %d | ADC2 = %d | ADC3 = %d | " + "Battery = %d | Temperature = %f | Modbus0 = 0x%s |\n", payload.timestamp, payload.deveui, payload.data.voboType, payload.data.payloadType, payload.data.DIN1, payload.data.DIN2, payload.data.DIN3, payload.data.WKUP, payload.data.ADC1, payload.data.ADC2, payload.data.ADC3, payload.data.Battery, payload.data.Temperature, modbusSlot0HexString); return; } if(payload.data.payloadType == "Modbus Standard") { dataKeys = Object.keys(payload.data); let stringToPrint = payload.timestamp + " | " + payload.deveui + " | " + payload.data.voboType + " | " + payload.data.payloadType + " | "; for (let idx = 0; idx < dataKeys.length; idx++) { if((dataKeys[idx] != "fport") && (dataKeys[idx] != "voboType") && (dataKeys[idx] != "payloadType")) { let fieldName = dataKeys[idx]; let valueHex = "0x" + payload.data[fieldName].toString(16).toUpperCase().padStart(4,0); stringToPrint += fieldName + " = " + valueHex + " | "; } } console.log("%s\n", stringToPrint); return; } if(payload.data.payloadType == "Heartbeat 1.0") { let firmwareRevisionHexString = payload.data.fwVersionMajor.toString(10) + "." + payload.data.fwVersionMinor.toString(10) + "." + payload.data.fwVersionPatch.toString(10) + "." + payload.data.fwVersionCustom.toString(10); console.log("%s | %s | %s | %s | " + "Battery Level MV = %d | FW Version = %s | Last RSSI = %d | Analog Voltage Config = %d | Analog Power Time Config = %d | Failed Transmissions Before Rejoin Config = %d | " + "Cycle Through FSB Config = %d | ACK Enable Config = %d | ACK Frequency Config = %d | ACK Request Config = %d | Battery Level Threshold Config = %d |\n", payload.timestamp, payload.deveui, payload.data.voboType, payload.data.payloadType, payload.data.batteryLevelMV, firmwareRevisionHexString, payload.data.recSignalLevels, payload.data.analogVoltageConfig, payload.data.analogPowerTimeConfig, payload.data.failedTransmissionBeforeRejoinConfig, payload.data.cycleThroughFSB, payload.data.ackEnable, payload.data.ackFreq, payload.data.ackReq, payload.data.batteryLevelThreshold); return; } if(payload.data.payloadType == "Heartbeat 2.0") { let firmwareRevisionHexString = payload.data.firmwareRevision.toString(16).toUpperCase().padStart(3,0); console.log("%s | %s | %s | %s | " + "Battery Level = %d | Fatal Errors Total = %d | RSSI Avg = %d | Failed Join Attempts Total = %d | Config Update Occurred = %d | Firmware Revision = 0x%s | " + "Reboots Total = %d | Failed Transmits Total = %d | Error Event Logs Total = %d | Warning Event Logs Total = %d | Info Event Logs Total = %d | Measurement Packets Total = %d |\n", payload.timestamp, payload.deveui, payload.data.voboType, payload.data.payloadType, payload.data.batteryLevel, payload.data.fatalErrorsTotal, payload.data.rssiAvg, payload.data.failedJoinAttemptsTotal, payload.data.configUpdateOccurred, firmwareRevisionHexString, payload.data.rebootsTotal, payload.data.failedTransmitsTotal, payload.data.errorEventLogsTotal, payload.data.warningEventLogsTotal, payload.data.infoEventLogsTotal, payload.data.measurementPacketsTotal); return; } if(payload.data.payloadType == "One Analog Input") { let fractionDigitsData0 = 1; if(payload.data.engUnitsString0 == "mV" || payload.data.engUnitsString0 == "V") fractionDigitsData0 = 3; if(payload.data.engUnitsString0 == "ADC code") fractionDigitsData0 = 0; console.log("%s | %s | %s | %s | %s = %f %s |\n", payload.timestamp, payload.deveui, payload.data.voboType, payload.data.payloadType, payload.data.analogSensorString0, parseFloat(payload.data.sensorData0).toFixed(fractionDigitsData0), payload.data.engUnitsString0); return; } if(payload.data.payloadType == "Two Analog Inputs") { let fractionDigitsData0 = 1; if(payload.data.engUnitsString0 == "mV" || payload.data.engUnitsString0 == "V" || payload.data.engUnitsString0 == "mA" || payload.data.engUnitsString0 == "A") fractionDigitsData0 = 3; if(payload.data.engUnitsString0 == "ADC code") fractionDigitsData0 = 0; let fractionDigitsData1 = 1; if(payload.data.engUnitsString1 == "mV" || payload.data.engUnitsString1 == "V" || payload.data.engUnitsString1 == "mA" || payload.data.engUnitsString1 == "A") fractionDigitsData1 = 3; if(payload.data.engUnitsString1 == "ADC code") fractionDigitsData1 = 0; console.log("%s | %s | %s | %s | %s = %f %s | %s = %f %s |\n", payload.timestamp, payload.deveui, payload.data.voboType, payload.data.payloadType, payload.data.analogSensorString0, parseFloat(payload.data.sensorData0).toFixed(fractionDigitsData0), payload.data.engUnitsString0, payload.data.analogSensorString1, parseFloat(payload.data.sensorData1).toFixed(fractionDigitsData1), payload.data.engUnitsString1); return; } if (payload.data.payloadType == "Analog Input Variable Length") { var stringToPrint = ""; for (let i = 0; i < payload.data.numOfAinPayloads; i++) { analogSensorString = payload.data["analogSensorString" + i]; engUnitsString = payload.data["engUnitsString" + i]; sensorData = payload.data.ainPayloads[i].sensorData0; let fractionDigitsData = 1; if(engUnitsString == "mV" || engUnitsString == "V" || engUnitsString == "mA" || engUnitsString == "A") fractionDigitsData = 3; if(engUnitsString == "ADC code") fractionDigitsData = 0; stringToPrint = stringToPrint + analogSensorString + " = " + parseFloat(sensorData).toFixed(fractionDigitsData) + " " + engUnitsString + " | "; } console.log("%s | %s | %s | %s | %s\n", payload.timestamp, payload.deveui, payload.data.voboType, payload.data.payloadType, stringToPrint); return; } if(payload.data.payloadType == "Modbus Standard Variable Length") { dataKeys = Object.keys(payload.data.modbusSlots); let stringToPrint = payload.timestamp + " | " + payload.deveui + " | " + payload.data.voboType + " | " + payload.data.payloadType + " | "; for (let idx = 0; idx < dataKeys.length; idx++) { let fieldName = dataKeys[idx]; let valueHex = "0x" + payload.data.modbusSlots[fieldName].toString(16).toUpperCase().padStart(4,0); stringToPrint += fieldName + " = " + valueHex + " | "; } console.log("%s\n", stringToPrint); return; } if(payload.data.payloadType == "Digital Inputs") { var stringToPrint = "" for (let i = 0; i < payload.data.digitalSensorData.length; i++) { stringToPrint = stringToPrint + payload.data.digitalSensorStrings[i] + " = " + payload.data.digitalSensorData[i].toString() + " | "; } console.log("%s | %s | %s | %s | %s\n", payload.timestamp, payload.deveui, payload.data.voboType, payload.data.payloadType, stringToPrint); return; } if((payload.data.payloadType == "Event Log") || (payload.data.payloadType == "Notification")) { let eventTimestampDateTime = new Date(payload.data.eventTimestamp * 1000).toISOString(); let eventCodeHexString = payload.data.eventCode.toString(16).toUpperCase().padStart(4,0); let metadataHexString = Buffer.from(payload.data.metadata).toString("hex").toUpperCase(); console.log("%s | %s | %s | %s | Timestamp = %s | Code = 0x%s | Metadata = 0x%s |\n", payload.timestamp, payload.deveui, payload.data.voboType, payload.data.payloadType, eventTimestampDateTime, eventCodeHexString, metadataHexString); return; } if(payload.data.payloadType == "Modbus Generic") { let dataKeys = Object.keys(payload.data); let stringToPrint = payload.timestamp + " | " + payload.deveui + " | " + payload.data.voboType + " | " + payload.data.payloadType + " | "; for (let idx = 0; idx < dataKeys.length; idx++) { if((dataKeys[idx] != "fport") && (dataKeys[idx] != "voboType") && (dataKeys[idx] != "payloadType")) { let fieldName = dataKeys[idx]; let regValueHexStr = "0x" + payload.data[fieldName].toString(16).toUpperCase().padStart(4,0); stringToPrint += fieldName + " = " + regValueHexStr + " | "; } } console.log("%s\n", stringToPrint); return; } console.log(JSON.stringify(payload, null, 4)); console.log(""); } // module.exports = {customDecoder, decodeUplink, Decoder, addVoboMetadata, lookupAnalogSensorName, lookupDigitalSensorName, lookupUnits, // hexToFloat, modbusToFloat_AB_CD_to_ABCD, modbusToFloat_AB_CD_to_DCBA, modbusToFloat_AB_CD_to_BADC, modbusToFloat_AB_CD_to_CDAB, printPayload}

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.

Community Feedback