Elsys decoding - Chirpstack v4

Hi all,

I’m running Chirpstack v4 from the docker install, which works perfectly.

I am trying to decode the payload from my Elsys ERS VOC sensors, but can’t seem to figure it out.
I have setup the codec with the JavaScript decoder from Elsys as follows:

const TYPE_TEMP = 0x01; //temp 2 bytes -3276.8°C -->3276.7°C
const TYPE_RH = 0x02; //Humidity 1 byte 0-100%
const TYPE_ACC = 0x03; //acceleration 3 bytes X,Y,Z -128 → 127 +/-63=1G
const TYPE_LIGHT = 0x04; //Light 2 bytes 0–>65535 Lux
const TYPE_MOTION = 0x05; //No of motion 1 byte 0-255
const TYPE_CO2 = 0x06; //Co2 2 bytes 0-65535 ppm
const TYPE_VDD = 0x07; //VDD 2byte 0-65535mV
const TYPE_ANALOG1 = 0x08; //VDD 2byte 0-65535mV
const TYPE_GPS = 0x09; //3bytes lat 3bytes long binary
const TYPE_PULSE1 = 0x0A; //2bytes relative pulse count
const TYPE_PULSE1_ABS = 0x0B; //4bytes no 0->0xFFFFFFFF
const TYPE_EXT_TEMP1 = 0x0C; //2bytes -3276.5C–>3276.5C
const TYPE_EXT_DIGITAL = 0x0D; //1bytes value 1 or 0
const TYPE_EXT_DISTANCE = 0x0E; //2bytes distance in mm
const TYPE_ACC_MOTION = 0x0F; //1byte number of vibration/motion
const TYPE_IR_TEMP = 0x10; //2bytes internal temp 2bytes external temp -3276.5C–>3276.5C
const TYPE_OCCUPANCY = 0x11; //1byte data
const TYPE_WATERLEAK = 0x12; //1byte data 0-255
const TYPE_GRIDEYE = 0x13; //65byte temperature data 1byte ref+64byte external temp
const TYPE_PRESSURE = 0x14; //4byte pressure data (hPa)
const TYPE_SOUND = 0x15; //2byte sound data (peak/avg)
const TYPE_PULSE2 = 0x16; //2bytes 0–>0xFFFF
const TYPE_PULSE2_ABS = 0x17; //4bytes no 0->0xFFFFFFFF
const TYPE_ANALOG2 = 0x18; //2bytes voltage in mV
const TYPE_EXT_TEMP2 = 0x19; //2bytes -3276.5C–>3276.5C
const TYPE_EXT_DIGITAL2 = 0x1A; // 1bytes value 1 or 0
const TYPE_EXT_ANALOG_UV = 0x1B; // 4 bytes signed int (uV)
const TYPE_TVOC = 0x1C; // 2 bytes (ppb)
const TYPE_DEBUG = 0x3D; // 4bytes debug

function bin16dec(bin) {
var num = bin & 0xFFFF;
if (0x8000 & num)
num = -(0x010000 - num);
return num;
}

function bin8dec(bin) {
var num = bin & 0xFF;
if (0x80 & num)
num = -(0x0100 - num);
return num;
}

function hexToBytes(hex) {
for (var bytes = , c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return bytes;
}

function DecodeElsysPayload(data) {
var obj = new Object();
for (i = 0; i < data.length; i++) {
//console.log(data[i]);
switch (data[i]) {
case TYPE_TEMP: //Temperature
var temp = (data[i + 1] << 8) | (data[i + 2]);
temp = bin16dec(temp);
obj.temperature = temp / 10;
i += 2;
break
case TYPE_RH: //Humidity
var rh = (data[i + 1]);
obj.humidity = rh;
i += 1;
break
case TYPE_ACC: //Acceleration
obj.x = bin8dec(data[i + 1]);
obj.y = bin8dec(data[i + 2]);
obj.z = bin8dec(data[i + 3]);
i += 3;
break
case TYPE_LIGHT: //Light
obj.light = (data[i + 1] << 8) | (data[i + 2]);
i += 2;
break
case TYPE_MOTION: //Motion sensor(PIR)
obj.motion = (data[i + 1]);
i += 1;
break
case TYPE_CO2: //CO2
obj.co2 = (data[i + 1] << 8) | (data[i + 2]);
i += 2;
break
case TYPE_VDD: //Battery level
obj.vdd = (data[i + 1] << 8) | (data[i + 2]);
i += 2;
break
case TYPE_ANALOG1: //Analog input 1
obj.analog1 = (data[i + 1] << 8) | (data[i + 2]);
i += 2;
break
case TYPE_GPS: //gps
i++;
obj.lat = (data[i + 0] | data[i + 1] << 8 | data[i + 2] << 16 | (data[i + 2] & 0x80 ? 0xFF << 24 : 0)) / 10000;
obj.long = (data[i + 3] | data[i + 4] << 8 | data[i + 5] << 16 | (data[i + 5] & 0x80 ? 0xFF << 24 : 0)) / 10000;
i += 5;
break
case TYPE_PULSE1: //Pulse input 1
obj.pulse1 = (data[i + 1] << 8) | (data[i + 2]);
i += 2;
break
case TYPE_PULSE1_ABS: //Pulse input 1 absolute value
var pulseAbs = (data[i + 1] << 24) | (data[i + 2] << 16) | (data[i + 3] << 8) | (data[i + 4]);
obj.pulseAbs = pulseAbs;
i += 4;
break
case TYPE_EXT_TEMP1: //External temp
var temp = (data[i + 1] << 8) | (data[i + 2]);
temp = bin16dec(temp);
obj.externalTemperature = temp / 10;
i += 2;
break
case TYPE_EXT_DIGITAL: //Digital input
obj.digital = (data[i + 1]);
i += 1;
break
case TYPE_EXT_DISTANCE: //Distance sensor input
obj.distance = (data[i + 1] << 8) | (data[i + 2]);
i += 2;
break
case TYPE_ACC_MOTION: //Acc motion
obj.accMotion = (data[i + 1]);
i += 1;
break
case TYPE_IR_TEMP: //IR temperature
var iTemp = (data[i + 1] << 8) | (data[i + 2]);
iTemp = bin16dec(iTemp);
var eTemp = (data[i + 3] << 8) | (data[i + 4]);
eTemp = bin16dec(eTemp);
obj.irInternalTemperature = iTemp / 10;
obj.irExternalTemperature = eTemp / 10;
i += 4;
break
case TYPE_OCCUPANCY: //Body occupancy
obj.occupancy = (data[i + 1]);
i += 1;
break
case TYPE_WATERLEAK: //Water leak
obj.waterleak = (data[i + 1]);
i += 1;
break
case TYPE_GRIDEYE: //Grideye data
var ref = data[i+1];
i++;
obj.grideye = ;
for(var j = 0; j < 64; j++) {
obj.grideye[j] = ref + (data[1+i+j] / 10.0);
}
i += 64;
break
case TYPE_PRESSURE: //External Pressure
var temp = (data[i + 1] << 24) | (data[i + 2] << 16) | (data[i + 3] << 8) | (data[i + 4]);
obj.pressure = temp / 1000;
i += 4;
break
case TYPE_SOUND: //Sound
obj.soundPeak = data[i + 1];
obj.soundAvg = data[i + 2];
i += 2;
break
case TYPE_PULSE2: //Pulse 2
obj.pulse2 = (data[i + 1] << 8) | (data[i + 2]);
i += 2;
break
case TYPE_PULSE2_ABS: //Pulse input 2 absolute value
obj.pulseAbs2 = (data[i + 1] << 24) | (data[i + 2] << 16) | (data[i + 3] << 8) | (data[i + 4]);
i += 4;
break
case TYPE_ANALOG2: //Analog input 2
obj.analog2 = (data[i + 1] << 8) | (data[i + 2]);
i += 2;
break
case TYPE_EXT_TEMP2: //External temp 2
var temp = (data[i + 1] << 8) | (data[i + 2]);
temp = bin16dec(temp);
if(typeof obj.externalTemperature2 === “number”) {
obj.externalTemperature2 = [obj.externalTemperature2];
}
if(typeof obj.externalTemperature2 === “object”) {
obj.externalTemperature2.push(temp / 10);
} else {
obj.externalTemperature2 = temp / 10;
}
i += 2;
break
case TYPE_EXT_DIGITAL2: //Digital input 2
obj.digital2 = (data[i + 1]);
i += 1;
break
case TYPE_EXT_ANALOG_UV: //Load cell analog uV
obj.analogUv = (data[i + 1] << 24) | (data[i + 2] << 16) | (data[i + 3] << 8) | (data[i + 4]);
i += 4;
break
case TYPE_TVOC:
obj.tvoc = (data[i + 1] << 8) | (data[i + 2]);
i += 2;
break
default: //somthing is wrong with data
i = data.length;
break
}
}
return obj;
}

This seems not to work, since i get nothing in the Measurements tab.
On the device i receive data correctly as such

However i get an error also
image

I am not proficient at all in JS, so im kind of lost.

I fixed the issue by using the script Elsys supplies for use with LORIOT.

var TYPE_TEMP = 0x01; //temp 2 bytes -3276.8°C -->3276.7°C
var TYPE_RH = 0x02; //Humidity 1 byte 0-100%
var TYPE_ACC = 0x03; //acceleration 3 bytes X,Y,Z -128 → 127 +/-63=1G
var TYPE_LIGHT = 0x04; //Light 2 bytes 0–>65535 Lux
var TYPE_MOTION = 0x05; //No of motion 1 byte 0-255
var TYPE_CO2 = 0x06; //Co2 2 bytes 0-65535 ppm
var TYPE_VDD = 0x07; //VDD 2byte 0-65535mV
var TYPE_ANALOG1 = 0x08; //VDD 2byte 0-65535mV
var TYPE_GPS = 0x09; //3bytes lat 3bytes long binary
var TYPE_PULSE1 = 0x0A; //2bytes relative pulse count
var TYPE_PULSE1_ABS = 0x0B; //4bytes no 0->0xFFFFFFFF
var TYPE_EXT_TEMP1 = 0x0C; //2bytes -3276.5C–>3276.5C
var TYPE_EXT_DIGITAL = 0x0D; //1bytes value 1 or 0
var TYPE_EXT_DISTANCE = 0x0E; //2bytes distance in mm
var TYPE_ACC_MOTION = 0x0F; //1byte number of vibration/motion
var TYPE_IR_TEMP = 0x10; //2bytes internal temp 2bytes external temp -3276.5C–>3276.5C
var TYPE_OCCUPANCY = 0x11; //1byte data
var TYPE_WATERLEAK = 0x12; //1byte data 0-255
var TYPE_GRIDEYE = 0x13; //65byte temperature data 1byte ref+64byte external temp
var TYPE_PRESSURE = 0x14; //4byte pressure data (hPa)
var TYPE_SOUND = 0x15; //2byte sound data (peak/avg)
var TYPE_PULSE2 = 0x16; //2bytes 0–>0xFFFF
var TYPE_PULSE2_ABS = 0x17; //4bytes no 0->0xFFFFFFFF
var TYPE_ANALOG2 = 0x18; //2bytes voltage in mV
var TYPE_EXT_TEMP2 = 0x19; //2bytes -3276.5C–>3276.5C
var TYPE_EXT_DIGITAL2 = 0x1A; // 1bytes value 1 or 0
var TYPE_EXT_ANALOG_UV= 0x1B; // 4 bytes signed int (uV)
var TYPE_TVOC = 0x1C; // 2 bytes (ppb)
var TYPE_DEBUG = 0x3D; // 4bytes debug
function bin16dec(bin) {
var num=bin&0xFFFF;
if (0x8000 & num)
num = - (0x010000 - num);
return num;
}
function bin8dec(bin) {
var num=bin&0xFF;
if (0x80 & num)
num = - (0x0100 - num);
return num;
}
function hexToBytes(hex) {
for (var bytes = , c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return bytes;
}
function DecodeElsysPayload(data){
var obj = new Object();
for(i=0;i<data.length;i++){
//console.log(data[i]);
switch(data[i]){
case TYPE_TEMP: //Temperature
var temp=(data[i+1]<<8)|(data[i+2]);
temp=bin16dec(temp);
obj.temperature=temp/10;
i+=2;
break
case TYPE_RH: //Humidity
var rh=(data[i+1]);
obj.humidity=rh;
i+=1;
break
case TYPE_ACC: //Acceleration
obj.x=bin8dec(data[i+1]);
obj.y=bin8dec(data[i+2]);
obj.z=bin8dec(data[i+3]);
i+=3;
break
case TYPE_LIGHT: //Light
obj.light=(data[i+1]<<8)|(data[i+2]);
i+=2;
break
case TYPE_MOTION: //Motion sensor(PIR)
obj.motion=(data[i+1]);
i+=1;
break
case TYPE_CO2: //CO2
obj.co2=(data[i+1]<<8)|(data[i+2]);
i+=2;
break
case TYPE_VDD: //Battery level
obj.vdd=(data[i+1]<<8)|(data[i+2]);
i+=2;
break
case TYPE_ANALOG1: //Analog input 1
obj.analog1=(data[i+1]<<8)|(data[i+2]);
i+=2;
break
case TYPE_GPS: //gps
obj.lat=(data[i+1]<<16)|(data[i+2]<<8)|(data[i+3]);
obj.long=(data[i+4]<<16)|(data[i+5]<<8)|(data[i+6]);
i+=6;
break
case TYPE_PULSE1: //Pulse input 1
obj.pulse1=(data[i+1]<<8)|(data[i+2]);
i+=2;
break
case TYPE_PULSE1_ABS: //Pulse input 1 absolute value
var pulseAbs=(data[i+1]<<24)|(data[i+2]<<16)|(data[i+3]<<8)|(data[i+4]);
obj.pulseAbs=pulseAbs;
i+=4;
break
case TYPE_EXT_TEMP1: //External temp
var temp=(data[i+1]<<8)|(data[i+2]);
temp=bin16dec(temp);
obj.externalTemperature=temp/10;
i+=2;
break
case TYPE_EXT_DIGITAL: //Digital input
obj.digital=(data[i+1]);
i+=1;
break
case TYPE_EXT_DISTANCE: //Distance sensor input
obj.distance=(data[i+1]<<8)|(data[i+2]);
i+=2;
break
case TYPE_ACC_MOTION: //Acc motion
obj.accMotion=(data[i+1]);
i+=1;
break
case TYPE_IR_TEMP: //IR temperature
var iTemp=(data[i+1]<<8)|(data[i+2]);
iTemp=bin16dec(iTemp);
var eTemp=(data[i+3]<<8)|(data[i+4]);
eTemp=bin16dec(eTemp);
obj.irInternalTemperature=iTemp/10;
obj.irExternalTemperature=eTemp/10;
i+=4;
break
case TYPE_OCCUPANCY: //Body occupancy
obj.occupancy=(data[i+1]);
i+=1;
break
case TYPE_WATERLEAK: //Water leak
obj.waterleak=(data[i+1]);
i+=1;
break
case TYPE_GRIDEYE: //Grideye data
var ref = data[i+1];
i++;
obj.grideye = ;
for(var j = 0; j < 64; j++) {
obj.grideye[j] = ref + (data[1+i+j] / 10.0);
}
i += 64;
break
case TYPE_PRESSURE: //External Pressure
var temp=(data[i+1]<<24)|(data[i+2]<<16)|(data[i+3]<<8)|(data[i+4]);
obj.pressure=temp/1000;
i+=4;
break
case TYPE_SOUND: //Sound
obj.soundPeak=data[i+1];
obj.soundAvg=data[i+2];
i+=2;
break
case TYPE_PULSE2: //Pulse 2
obj.pulse2=(data[i+1]<<8)|(data[i+2]);
i+=2;
break
case TYPE_PULSE2_ABS: //Pulse input 2 absolute value
obj.pulseAbs2=(data[i+1]<<24)|(data[i+2]<<16)|(data[i+3]<<8)|(data[i+4]);
i+=4;
break
case TYPE_ANALOG2: //Analog input 2
obj.analog2=(data[i+1]<<8)|(data[i+2]);
i+=2;
break
case TYPE_EXT_TEMP2: //External temp 2
var temp=(data[i+1]<<8)|(data[i+2]);
temp=bin16dec(temp);
if(typeof obj.externalTemperature2 === “number”) {
obj.externalTemperature2 = [obj.externalTemperature2];
}
if(typeof obj.externalTemperature2 === “object”) {
obj.externalTemperature2.push(temp / 10);
} else {
obj.externalTemperature2 = temp / 10;
}
i+=2;
break
case TYPE_EXT_DIGITAL2: //Digital input 2
obj.digital2=(data[i+1]);
i+=1;
break
case TYPE_EXT_ANALOG_UV: //Load cell analog uV
obj.analogUv = (data[i + 1] << 24) | (data[i + 2] << 16) | (data[i + 3] << 8) | (data[i + 4]);
i += 4;
break
case TYPE_TVOC:
obj.tvoc = (data[i + 1] << 8) | (data[i + 2]);
i += 2;
break
default: //somthing is wrong with data
i=data.length;
break
}
}
return obj;
}
// TTN V3 function
function decodeUplink(input) {
d=DecodeElsysPayload(input.bytes);
return {
data: d,
warnings: ,
errors:
};
}

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.