Decoder for Chirpstack v4?

Hi,

I’m using a Wisblock dev kit to build an environment monitor as per the guide/sketch published here:

It’s working fine, and from the serial monitor I can see the measurements being taken.

I’m passing the data to a Chirpstack server, but I’m having trouble decoding the data, I’m not much of a programmer and I’m struggling to get my head around the code for it.

I’ve been trying to use this code block:

function decodeUplink(fPort, bytes) {
  var data = {};

  if (fPort === 2) {
    // Decode temperature
    var temp = bytes[0] << 8 | bytes[1];
    data.temperature = temp / 100.0;

    // Decode humidity
    var hum = bytes[2] << 8 | bytes[3];
    data.humidity = hum / 100.0;

    // Decode pressure
    var pres = bytes[4] << 8 | bytes[5];
    data.pressure = pres / 100.0;

    // Decode gas resistance
    var gas = bytes[6] << 24 | bytes[7] << 16 | bytes[8] << 8 | bytes[9];
    data.gasResistance = gas;
  }

  return data;
}

in Chirpstack I just see an error:

“decodeUplink did not return ‘data’”

It seems the code is returning an empty object maybe? Not sure how to troubleshoot this one.

Hi @Wedgie32
I think there are some minor mistakes in your decoder. The data (temperature first), doesn’t start at bytes[0], because byted[0] is the data type:

Byte 0 ==> Marker for the data type, for environment sensor it is alway 1
Byte 1 - 2 ==> temperature MSB first, 2 bytes length
Byte 3 - 4 ==> humidity MSB first, 2 bytes lenght
Byte 5 - 8 ==> barometric pressure, MSB first, 4 bytes length
Byte 9 - 14 ==> gas resistance, MSB first, 4 bytes length

Try this decoder, that should work on TTN and Chirpstack V3 and V4:

// Decode decodes an array of bytes into an object.
//  - fPort contains the LoRaWAN fPort number
//  - bytes is an array of bytes, e.g. [225, 230, 255, 0]
//  - variables contains the device variables e.g. {"calibration": "3.5"} (both the key / value are of type string)
// The function must return an object, e.g. {"temperature": 22.5}
function DoDecode(fPort, bytes) {
	var decoded = {};
	switch (bytes[0])
	{
		case 0x01: // Environment sensor data
			decoded.temperature = (bytes[1] << 8 | bytes[2]) / 100;
			decoded.humidity = (bytes[3] << 8 | + bytes[4]) / 100;
			decoded.pressure = (bytes[8] | (bytes[7] << 8) | (bytes[6] << 16) | (bytes[5] << 24)) / 100;
			decoded.gas = bytes[12] | (bytes[11] << 8) | (bytes[10] << 16) | (bytes[9] << 24);
			decoded.batt = (bytes[13] << 8 | bytes[14]) / 100;
			break;
		case 0x02: // Temperature and humidity sensor data
			decoded.temperature = (bytes[1] << 8 | bytes[2]) / 100;
			decoded.humidity = (bytes[3] << 8 | + bytes[4]) / 100;
			decoded.batt = (bytes[5] << 8 | bytes[6]) / 100;
			break;
		case 0x03: // Ambient light sensor data
			decoded.light = (bytes[4] | (bytes[3] << 8) | (bytes[2] << 16) | (bytes[1] << 24)) / 10;
			decoded.batt = (bytes[5] << 8 | bytes[6]) / 100;
			break;
		case 0x04: // No sensor data, just counter
			decoded.cnt = bytes[4] | (bytes[3] << 8) | (bytes[2] << 16) | (bytes[1] << 24);
			break;
		case 0x20: // Solar panel data
			decoded.current = (bytes[1] << 8 | bytes[2]);
			decoded.today = (bytes[3] << 8 | + bytes[4]);
			break;
		case 0x30: // Accelerometer sensor
        	if (bytes[1] == 0) {
				decoded.x_move = "no";
            } else {
				decoded.x_move = "yes";
            }
        	if (bytes[2] == 0) {
				decoded.y_move = "no";
            } else {
				decoded.y_move = "yes";
            }
        	if (bytes[3] == 0) {
				decoded.z_move = "no";
            } else {
				decoded.z_move = "yes";
            }
			break;
		default:
			decoded.unknown = "Unknown data format";
			break;
	}
	return decoded;
}

// For TTN
function Decoder(bytes, fPort)
{
	return DoDecode(fPort, bytes);
}

// For Chirpstack
function Decode(fPort, bytes, variables)
{
	return DoDecode(fPort, bytes);
}


// Chirpstack v3 to v4 compatibility wrapper
function decodeUplink(input) {
	return {
		data: Decode(input.fPort, input.bytes, input.variables)
	};
}

WisBlock-Example-Decoder.zip (945 Bytes)

Thanks Beegee, you’re a legend. This is the first explanation that’s actually ‘clicked’ in my brain. I’m not a natural with this stuff :smiling_face: