Heltec HRI-3621 sensor with ChirpStack

I’m working on integrating the Heltec HRI-3621 sensor with ChirpStack (Version: v4.9.0) and I’ve run into some difficulties. I’m hoping someone here can help me out with a few questions:

  1. Codec for HRI-362: I’ve read through the Heltec data format documentation, and I’m trying to determine the correct codec to use in ChirpStack for decoding the payloads sent by the HRI-3621. I’ve tried a few different approaches based on the examples provided in the Heltec documentation, but so far, I haven’t been able to successfully decode the temperature and pressure readings in ChirpStack. Could someone provide guidance or an example of the correct codec to use with this sensor?

  2. MQTT Message Handling: I’ve set up ChirpStack to forward decoded sensor data over MQTT. However, when I attempt to process the MQTT messages in Node-RED, the decoded values (temperature and pressure) are showing up as 0.00. Has anyone successfully processed the MQTT messages from this sensor in Node-RED, and if so, could you share the steps or a sample flow that worked for you?

Any help on these topics would be greatly appreciated! I’ve been stuck for a while now and would love to get this sensor working with ChirpStack.

Thanks in advance for your help!

Hello everyone,

After reviewing the Heltec documentation in detail and going through several iterations of testing, I was able to develop my own solution for decoding the data from the Heltec HRI-3621 sensor. Rather than relying on the Heltec-provided example code, I built a custom decoder that works well for me.

For those who are interested, I’ve created a Node-Red function that decodes and displays the Base64 data, and I’ve also built a custom codec for ChirpStack that handles the sensor’s data correctly.

Here is an example of the Node-Red function that decodes the Base64 data:

// Ensure the payload is a string
if (typeof msg.payload.data === 'string') {
// Decode Base64 data to a byte array
let bytes = Buffer.from(msg.payload.data, 'base64');
// Function to convert 4 bytes to a float (Little Endian)
function bytesToFloat(by) {
    var buffer = Buffer.from(by);
    return buffer.readFloatLE(0);
}

// Initialize variables for pressure and temperature
let temperature = null;
let pressure = null;

// Identify sensor sub-IDs and assign the correct data types
let sensorDataIndex = 0;

while (sensorDataIndex < bytes.length) {
    let subId = bytes[sensorDataIndex] >> 4; // Upper 4 bits for Sub-ID
    let dataType = bytes[sensorDataIndex] & 0x0F; // Lower 4 bits for data type

    // Shift index after reading the first byte
    sensorDataIndex++;

    // Decode based on Sub-ID and data type
    if (subId === 0x00 && dataType === 0b0010) {
        // Sub-ID 0x00 -> Pressure, data type: float (4 bytes)
        pressure = bytesToFloat(bytes.slice(sensorDataIndex, sensorDataIndex + 4));
        sensorDataIndex += 4; // Shift past float data
    } else if (subId === 0x01 && dataType === 0b0010) {
        // Sub-ID 0x01 -> Temperature, data type: float (4 bytes)
        temperature = bytesToFloat(bytes.slice(sensorDataIndex, sensorDataIndex + 4));
        sensorDataIndex += 4; // Shift past float data
    } else {
        // Skip unknown data types or Sub-IDs
        sensorDataIndex++;
    }
}

// Format and output decoded data
msg.payload = {
    temperature: temperature !== null ? temperature.toFixed(2) + " °C" : "N/A",
    pressure: pressure !== null ? pressure.toFixed(2) + " hPa" : "N/A",
    rawBytes: Buffer.from(bytes).toString('hex')  // Raw bytes as hex string
};

return msg; } 
else {
// Error handling for invalid data types
node.error("Received data is not a Base64 string.");
return null;
}

Additionally, I’ve written a custom codec for ChirpStack that works with the Heltec HRI-3621 sensor. If anyone is interested, I’m happy to share this codec and provide further explanations.

function decodeUplink(input) {
// Base64 data as a byte array
let bytes = input.bytes;

// Function to convert 4 bytes into a float (Little Endian)
function bytesToFloat(by) {
    // Convert bytes into an integer
    let bits = (by[0]) | (by[1] << 8) | (by[2] << 16) | (by[3] << 24);
    let sign = (bits >>> 31 === 0) ? 1.0 : -1.0;
    let e = (bits >>> 23) & 0xff;
    let m = (e === 0) ? (bits & 0x7fffff) << 1 : (bits & 0x7fffff) | 0x800000;
    let floatVal = sign * m * Math.pow(2, e - 150);
    return floatVal;
}

// Initialize variables for pressure and temperature
let temperature = null;
let pressure = null;

// Identify sensor sub-IDs and assign the corresponding data types
// Sub-ID for pressure (0x00) and temperature (0x01)
let sensorDataIndex = 0;

while (sensorDataIndex < bytes.length) {
    // Extract the sub-ID and data type from the first byte
    let subId = bytes[sensorDataIndex] >> 4; // Upper 4 bits for sub-ID
    let dataType = bytes[sensorDataIndex] & 0x0F; // Lower 4 bits for data type

    // Determine the start index of the sensor data
    sensorDataIndex++;

    // Process based on sub-ID and data type
    if (subId === 0x00 && dataType === 0b0010) {
        // Sub-ID: 0x00 -> Pressure, Data type: Float (4 bytes)
        pressure = bytesToFloat(bytes.slice(sensorDataIndex, sensorDataIndex + 4));
        sensorDataIndex += 4; // Move forward after the float data type
    } else if (subId === 0x01 && dataType === 0b0010) {
        // Sub-ID: 0x01 -> Temperature, Data type: Float (4 bytes)
        temperature = bytesToFloat(bytes.slice(sensorDataIndex, sensorDataIndex + 4));
        sensorDataIndex += 4; // Move forward after the float data type
    } else {
        // Skip if an unknown data type or sub-ID is found
        sensorDataIndex++; // Skip 1 byte to continue
    }
}

// Return the decoded data to ChirpStack
return {
    data: {
        temperature: temperature !== null ? temperature.toFixed(2) + " °C" : "N/A",
        pressure: pressure !== null ? pressure.toFixed(2) + " hPa" : "N/A"
    }
};
}
Summary

This text will be hidden

Best regards,
Niox