Indeklimasensor ERS CO2

Fra IoTwiki
Version fra 23. okt 2020, 12:44 af Mortenra (diskussion | bidrag) Mortenra (diskussion | bidrag) (Brugeroplevelse: Erfaring og konklusion på ERSC02)

(forskel) ←Ældre version | Nuværende version (forskel) | Nyere version→ (forskel)
Skift til: navigering, søgning
Info
Produktnavn Indeklimasensor ERS CO2
Sensortype
Modelnavn Indeklima Elsys ERS CO2
Producent Elsys
Kort beskrivelse Indeklimasensor med bl.a. temperatur, co2, bevægelse.
Netværk LoRaWAN
Dokumentation https://www.elsys.se/en/ers/
Producentens hjemmeside https://www.elsys.se/en/
Anvendes i projekterne

Brugeroplevelse: Erfaring og konklusion på ERSC02

Ballerup Kommune har testet sensorer i forskellige lokaler hvoraf ERSCO2 indgår i rækken af test-devices. Devicet er i 2020 også testet jævnfør SAnD´s køleskabscase, primært med henblik på temperaturmåling.
Konklusion maj 2020 er:

  • devicet kører stabilt
  • kører langt på batteriet
  • dokumentation(dekodning af hex-data i database) er rigtig god
  • dekodningscript (javascript) af hex-data til anvendelse på netværksserver er tilgængelig
  • kommunikation og assistance med producent er god

Devicet er i den prisdyre ende og bør derfor anvendes på de klassiske indendørscases, hvor der måles på alle parametre som devicet understøtter. Ballerup Kommune har i 2019/2020 testet dem i kommunens køleskabe. Der kan være udfordring med at data kan opsamles af udendørs gateways, da signalstyrken er noget mindre ved data-transfer inde fra køleskab. Derfor har vi suppleret med indendørs gateways (eks. Kerlinks IfemtoCell) i de akutelle bygninger hvor devicet er placeret.

Kommentar fra HTK: HTK har gennem længere tid haft 30-40 Elsys ERSCO2 sensorer opsat på skole. Alle sensorer virker efter hensigten både mht. batteridrift, payload data og generel stabilitet. Det eneste punkt der kan være en udfordring er selve kassens/chassiets lidt skrøbelige plastik, låsemekanisme og beslag som ikke tåler alt for meget fysisk påvirkning. - Dette kan være problematisk i institutioner og skoler.

Tilslutning og konfiguration af device til netværksserver

Guiden her anvender Chirpstack som eksempel, men kan også anvendes på andre platforme. Elsys har lanceret deres egen mobil-app "Sensor Settings"(android & iOS) til konfiguration og opkobling til brugerens netværksserver.
Kræver at brugerens tablet/smartpohone understøtter NFC.

Med appen kan brugeren:

  • aflæse devicets tilslutningsnøgler
  • definere frekvens for upload af data - dvs. hvor ofte skal data transmitteres fra device
  • kalibrere sensor
  • styre den infrarøde sensors følsomhed
  • bestemme OTAA-tilslutning (foretrukket)


Workflow: Konfiguration ved brug af NFC og app til Chirpstack:

  1. aflæs DevEUI på device (står også bagpå)
  2. aktiver OTAA (Under "Sensor Keys"), notér AppKey og skriv("Write") til device
  3. Indtast AppKey på Chirpstack under "Application Server" --> "Keys (OTAA)"
  4. IKKE udfylde "Gen Appliation Key" ud
  5. I Chirpstack - hold øje med fanen "Activation" og at devicet faktisk er aktiveret.

Dekodning af payload på netværksserver - Chirpstack

Dekodnings-scriptet fra Elsys tilføjes under "Device-profiles" ---> "Codec" under "Payload codec".
Husk at tilknytte dit device til denne "Device-profile". Verificér at payload er dekodet og kommer ud i "oversat" format under dit device("LORAWAN FRAMES")


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_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
                i+=65;
            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);
                obj.externalTemperature2=temp/10;
                i+=2;
            break
            case TYPE_EXT_DIGITAL2: //Digital input 2 
                obj.digital2=(data[i+1]); 
                i+=1; 
             break
            default: //somthing is wrong with data
                i=data.length;
            break
        }
    }
    return obj;
}

function Decoder(bytes, port) {
  return DecodeElsysPayload(bytes);
}

function Decode(fPort, bytes) {
  return DecodeElsysPayload(bytes);
}

Dekodning af payload i database

Se dokumentation af dekodning

CREATE OR REPLACE VIEW loriot.indeklima_dekodet AS
select "eui",
 round(hex_to_int(substring("data", 3, 4))/10::decimal,1) as temp,
 hex_to_int(substring("data", 9, 2))::int as humidity,
 hex_to_int(substring("data", 13, 4))::int as light,
 hex_to_int(substring("data", 19, 2))::int as motion,
 hex_to_int(substring("data", 23, 4))::int as co2,
 round(hex_to_int(substring("data", 29, 4))/1000::decimal,3) as voltage,
 timezone('Europe/Berlin'::text, to_timestamp((date_part('epoch'::text,ts) / 1000::double precision)::integer::double precision)) AS ts
from loriot.indeklimasensor
where cmd = 'rx' ;(substring(pl, 23, 4))::decimal

Erfaringer, tips & tricks