Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
211 changes: 211 additions & 0 deletions vendors/elsys/codecs/elsys.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
/*
ELSYS payload decoder for ChirpStack v4.
Based on the official Elsys payload decoder.
www.elsys.se
peter@elsys.se

Adapted for chirpstack-device-profiles decodeUplink/encodeDownlink format.
*/

var TYPE_TEMP = 0x01; // temp 2 bytes -3276.8°C to 3276.7°C
var TYPE_RH = 0x02; // Humidity 1 byte 0-100%
var TYPE_ACC = 0x03; // Acceleration 3 bytes X,Y,Z -128 to 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; // Battery mV 2 bytes 0-65535
var TYPE_ANALOG1 = 0x08; // Analog input 1 2 bytes 0-65535 mV
var TYPE_GPS = 0x09; // GPS 6 bytes lat(3),long(3)
var TYPE_PULSE1 = 0x0A; // Pulse input 1 relative 2 bytes
var TYPE_PULSE1_ABS = 0x0B; // Pulse input 1 absolute 4 bytes
var TYPE_EXT_TEMP1 = 0x0C; // External temp 1 2 bytes -3276.8°C to 3276.7°C
var TYPE_EXT_DIGITAL = 0x0D; // Digital input 1 byte
var TYPE_EXT_DISTANCE = 0x0E; // Distance sensor 2 bytes 0-65535 mm
var TYPE_ACC_MOTION = 0x0F; // Acc motion 1 byte 0-255
var TYPE_IR_TEMP = 0x10; // IR temperature 4 bytes internal(2),external(2)
var TYPE_OCCUPANCY = 0x11; // Occupancy 1 byte
var TYPE_WATERLEAK = 0x12; // Water leak 1 byte 0-255
var TYPE_GRIDEYE = 0x13; // Grideye data 65 bytes
var TYPE_PRESSURE = 0x14; // Pressure 4 bytes (hPa)
var TYPE_SOUND = 0x15; // Sound 2 bytes (peak,avg)
var TYPE_PULSE2 = 0x16; // Pulse input 2 relative 2 bytes
var TYPE_PULSE2_ABS = 0x17; // Pulse input 2 absolute 4 bytes
var TYPE_ANALOG2 = 0x18; // Analog input 2 2 bytes 0-65535 mV
var TYPE_EXT_TEMP2 = 0x19; // External temp 2 2 bytes -3276.8°C to 3276.7°C
var TYPE_EXT_DIGITAL2 = 0x1A; // Digital input 2 1 byte
var TYPE_EXT_ANALOG_UV= 0x1B; // Analog UV 4 bytes

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 DecodeElsysPayload(data) {
var obj = {};
for (var i = 0; i < data.length; i++) {
switch (data[i]) {
case TYPE_TEMP:
var temp = (data[i + 1] << 8) | (data[i + 2]);
temp = bin16dec(temp);
obj.temperature = temp / 10;
i += 2;
break;
case TYPE_RH:
obj.humidity = data[i + 1];
i += 1;
break;
case TYPE_ACC:
obj.x = bin8dec(data[i + 1]);
obj.y = bin8dec(data[i + 2]);
obj.z = bin8dec(data[i + 3]);
i += 3;
break;
case TYPE_LIGHT:
obj.light = (data[i + 1] << 8) | (data[i + 2]);
i += 2;
break;
case TYPE_MOTION:
obj.motion = data[i + 1];
i += 1;
break;
case TYPE_CO2:
obj.co2 = (data[i + 1] << 8) | (data[i + 2]);
i += 2;
break;
case TYPE_VDD:
obj.vdd = (data[i + 1] << 8) | (data[i + 2]);
i += 2;
break;
case TYPE_ANALOG1:
obj.analog1 = (data[i + 1] << 8) | (data[i + 2]);
i += 2;
break;
case TYPE_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:
obj.pulse1 = (data[i + 1] << 8) | (data[i + 2]);
i += 2;
break;
case TYPE_PULSE1_ABS:
obj.pulseAbs = (data[i + 1] << 24) | (data[i + 2] << 16) | (data[i + 3] << 8) | (data[i + 4]);
i += 4;
break;
case TYPE_EXT_TEMP1:
var temp = (data[i + 1] << 8) | (data[i + 2]);
temp = bin16dec(temp);
obj.externalTemperature = temp / 10;
i += 2;
break;
case TYPE_EXT_DIGITAL:
obj.digital = data[i + 1];
i += 1;
break;
case TYPE_EXT_DISTANCE:
obj.distance = (data[i + 1] << 8) | (data[i + 2]);
i += 2;
break;
case TYPE_ACC_MOTION:
obj.accMotion = data[i + 1];
i += 1;
break;
case TYPE_IR_TEMP:
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:
obj.occupancy = data[i + 1];
i += 1;
break;
case TYPE_WATERLEAK:
obj.waterleak = data[i + 1];
i += 1;
break;
case TYPE_GRIDEYE:
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:
var press = (data[i + 1] << 24) | (data[i + 2] << 16) | (data[i + 3] << 8) | (data[i + 4]);
obj.pressure = press / 1000;
i += 4;
break;
case TYPE_SOUND:
obj.soundPeak = data[i + 1];
obj.soundAvg = data[i + 2];
i += 2;
break;
case TYPE_PULSE2:
obj.pulse2 = (data[i + 1] << 8) | (data[i + 2]);
i += 2;
break;
case TYPE_PULSE2_ABS:
obj.pulseAbs2 = (data[i + 1] << 24) | (data[i + 2] << 16) | (data[i + 3] << 8) | (data[i + 4]);
i += 4;
break;
case TYPE_ANALOG2:
obj.analog2 = (data[i + 1] << 8) | (data[i + 2]);
i += 2;
break;
case TYPE_EXT_TEMP2:
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:
obj.digital2 = data[i + 1];
i += 1;
break;
case TYPE_EXT_ANALOG_UV:
obj.analogUv = (data[i + 1] << 24) | (data[i + 2] << 16) | (data[i + 3] << 8) | (data[i + 4]);
i += 4;
break;
default:
i = data.length;
break;
}
}
return obj;
}

// ChirpStack v4 / LoRa Alliance TS013 codec API
function decodeUplink(input) {
return {
data: DecodeElsysPayload(input.bytes),
};
}

function encodeDownlink(input) {
return {
bytes: [],
};
}
105 changes: 105 additions & 0 deletions vendors/elsys/codecs/test_decode_elsys.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
[
{
"name": "Test decode temperature and humidity",
"input": {
"fPort": 5,
"bytes": [1, 0, 234, 2, 55]
},
"expected": {
"data": {
"temperature": 23.4,
"humidity": 55
}
}
},
{
"name": "Test decode temperature, humidity and VDD",
"input": {
"fPort": 5,
"bytes": [1, 0, 198, 2, 42, 7, 14, 10]
},
"expected": {
"data": {
"temperature": 19.8,
"humidity": 42,
"vdd": 3594
}
}
},
{
"name": "Test decode negative temperature",
"input": {
"fPort": 5,
"bytes": [1, 255, 206, 2, 80]
},
"expected": {
"data": {
"temperature": -5.0,
"humidity": 80
}
}
},
{
"name": "Test decode external temperature (DS18B20)",
"input": {
"fPort": 5,
"bytes": [1, 0, 200, 12, 0, 150]
},
"expected": {
"data": {
"temperature": 20.0,
"externalTemperature": 15.0
}
}
},
{
"name": "Test decode analog input",
"input": {
"fPort": 5,
"bytes": [1, 0, 180, 8, 19, 136]
},
"expected": {
"data": {
"temperature": 18.0,
"analog1": 5000
}
}
},
{
"name": "Test decode pressure",
"input": {
"fPort": 5,
"bytes": [20, 0, 15, 92, 168]
},
"expected": {
"data": {
"pressure": 1006.76
}
}
},
{
"name": "Test decode digital input",
"input": {
"fPort": 5,
"bytes": [1, 0, 210, 13, 1]
},
"expected": {
"data": {
"temperature": 21.0,
"digital": 1
}
}
},
{
"name": "Test decode pulse counter",
"input": {
"fPort": 5,
"bytes": [10, 0, 42]
},
"expected": {
"data": {
"pulse1": 42
}
}
}
]
11 changes: 11 additions & 0 deletions vendors/elsys/codecs/test_encode_elsys.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[
{
"name": "Test encode empty (no downlink encoding supported)",
"input": {
"data": {}
},
"expected": {
"bytes": []
}
}
]
15 changes: 15 additions & 0 deletions vendors/elsys/devices/elt-2-hp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[device]
id = "b2c3d4e5-6f7a-8b9c-0d1e-f2a3b4c5d6e7"
name = "ELT-2 HP"
description = "Multi-sensor with 2 external sensor inputs. Internal temperature, humidity, accelerometer and barometric pressure. External inputs support analog 0-10V, digital, pulse counter, DS18B20, ultrasonic distance, and water leak sensors. IP67 rated with external antenna."

[[device.firmware]]
version = "1.0"
profiles = [
"EU868-1_0_3.toml",
]
codec = "elsys.js"

[device.metadata]
product_url = "https://www.elsys.se/shop/product/elt2hp/"
documentation_url = "https://elsys.se/public/documents/Sensor_payload.pdf"
25 changes: 25 additions & 0 deletions vendors/elsys/profiles/EU868-1_0_3.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[profile]
id = "c3d4e5f6-7a8b-9c0d-1e2f-a3b4c5d6e7f8"
vendor_profile_id = 0
region = "EU868"
mac_version = "1.0.3"
reg_params_revision = "A"
supports_otaa = true
supports_class_b = false
supports_class_c = false
max_eirp = 14

[profile.abp]
rx1_delay = 0
rx1_dr_offset = 0
rx2_dr = 0
rx2_freq = 0

[profile.class_b]
timeout_secs = 0
ping_slot_nb_k = 0
ping_slot_dr = 0
ping_slot_freq = 0

[profile.class_c]
timeout_secs = 0
10 changes: 10 additions & 0 deletions vendors/elsys/vendor.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[vendor]
id = "a1b2c3d4-5e6f-7a8b-9c0d-e1f2a3b4c5d6"
name = "Elsys"
ouis = ["fc0fee"]
devices = [
"elt-2-hp.toml",
]

[vendor.metadata]
homepage = "https://www.elsys.se/"
Loading