Vai al contenuto

MlsDevice, Milesight device manager

Vai all indice del manuale di programmazione
Tipo: Blocco Funzione
Libreria LogicLab: eLLabMQTTLib
Libreria Codesys: Non disponibile

Questo blocco funzione da eseguire in task Back, esegue la gestione dei dispositivi Milesight. Il FB si connette al gateway LoRaWAN tramite il FB MQTTClient.

Descrizione

Enable (BOOL) Attivandolo viene abilitata l’esecuzione del FB.
SpyOn (BOOL) Se attivo permette di spiare il funzionamento del FB (Vedi articolo).
MQTT (@MQTTClient_v3) Indirizzo allocazione FB MQTTClient di supporto.
DTopic (@STRING) Definizionme topic a cui il device appartiene (Es. application/1/device).
EUI (@STRING]) Definizione 64-bit (Extended Unique Identifier), identificativo univoco del dispositivo.
Timeout (TIME) Tempo controllo dispositivo attivo, se non si ricevono dati in questo tempo viene segnalato errore (Deve essere impostato almeno al doppio del valore di report interval).
Fault (BOOL) Attivo per un loop se errore esecuzione.
DInit (BOOL)Si attiva per un loop alla inizializzazione del dispositivo. E’ possibile eseguire l’invio di comandi in downlink di inizializzazione.
Active (BOOL) Attivo se dispositivo linkato.
OnError (BOOL) Attivo se dispositivo in errore.
RxOTrig (BOOL) Si attiva per un loop alla ricezione di un uplink dal dispositivo.
RSSI (REAL) (Received Signal Strength Indicator), indica la qualità del segnale ricevuto dal dispositivo (dBm).
SNR (REAL) (Signal-to-Noise Ratio), indica il livello del segnale rispetto al rumore di ambiente (dB).
Battery (USINT) Livello batteria (%).
ETime (TIME) Tempo trascorso da ultima ricezione uplink da dispositivo.
RxObject (@STRING) Se attivo RxOTrig ritorna dato ricevuto in uplink da dispositivo.
RxOLength (UDINT) Se attivo RxOTrig ritorna dimensione dato ricevuto in uplink da dispositivo.
RxOTrig (BOOL) Si attiva per un loop alla ricezione di un uplink dal dispositivo.
RxPkts (UDINT) Counter pacchetti dati ricevuti (Uplink) dal dispositivo.
TxPkts (UDINT) Counter pacchetti dati trasmessi (Downlink) al dispositivo.
Errors (UDINT) Counter errori comunicazione con dispositivo.

Immagine FB MlsDevice
RSSI

Il Received Signal Strength Indicator RSSI indica la potenza del segnale ricevuto in milliwatt e viene misurata in dBm. Questo valore è utilizzato come misura della capacità di un ricevitore di “sentire” un segnale proveniente da un mittente. L’RSSI è un valore negativo e quanto più vicino allo 0, migliore è il segnale, i valori RSSI LoRa tipici sono:

  • RSSI minimo = -120 dBm.
  • Se RSSI=-30dBm: il segnale è forte.
  • Se RSSI=-120dBm: il segnale è debole.
SNR

Il Signal-to-Noise Ratio SNR è il rapporto tra il segnale ricevuto ed il livello del rumore di fondo. Il rumore di fondo è costituito da tutte le sorgenti di segnale indesiderate che possono corrompere il segnale trasmesso che ne provocano la ritrasmissione. Se SNR è maggiore di 0, il segnale ricevuto opera al di sopra del rumore di fondo. Se SNR è inferiore a 0, il segnale ricevuto opera al di sotto del rumore di fondo. Anche se normalmente nelle trasmissioni radio il rumore di fondo è il limite fisico della sensibilità, LoRa funziona al di sotto del livello di rumore. I valori SNR LoRa tipici sono:

  • Valore ottimali: SNR > 0 dB
  • Valori in media: -5dB > SNR <= 0dB
  • Valori che indicano un livello di segnale al di sotto del rumore di fondo: SNR < -5dB

Metodi disponibili

DeviceError

Questo metodo permette di segnalare al FB MlsDevice una condizione di errore.

Il metodo ritorna un (BOOL) TRUE.

Immagine metodo MlsDevice.DeviceError
DLinkValue

Questo metodo permette di inviare un payload al dispositivo vedi comandi di downlink. Nei dispositivi in classe A il comando verrà inviato solo in seguito alla ricezione di un uplink, nei dispositivi in classe C il comando viene subito inviato. Il comando definito viene inserito nel registro FIFO di supporto del FB MQTTClien e inviato in publish verso il broker.

Name (@STRING) Definizione nome variabile da inserire nel payload.
VType (VR_TYPE) Tipo variabile da inserire nel payload (Vedi definizione).
VAddress (PVOID) Indirizzo variabile da inserire nel payload.

Il Il metodo ritorna un (BOOL) TRUE.

Immagine metodo MlsDevice.DLinkValue
DLinkJSON

Questo metodo permette di inviare un payload al dispositivo vedi comandi di downlink. Nei dispositivi in classe A il comando verrà inviato solo in seguito alla ricezione di un uplink, nei dispositivi in classe C il comando viene subito inviato. Il comando definito viene inserito nel registro FIFO di supporto del FB MQTTClient e inviato in publish verso il broker.

Payload (@STRING) Definizione payload in formato JSON da inviare in downlink al dispositivo.

Il Il metodo ritorna un (BOOL) TRUE.

Immagine metodo MlsDevice.DLinkJSON

Milesight payload codec

Il Milesight payload codec (Vedi articolo) è un sistema per codificare e decodificare i dati scambiati con i sensori Milesight. Molti codecs sono preinstallati di default nel gateway, è comunque possibile reperirne altri in rete. Il codec presente sul gateway esegue

  • Decodifica (Decoder): Il sensore raccoglie i valori (temperatura, umidità, ecc), li codifica in formato binario e li invia in uplink al gateway. Il gateway li decodifica ritornandoli al programma applicativo in formato JSON.
  • Codifica (Encoder): Il programma applicativo invia i comandi al gateway in formato JSON, il gateway codifica i dati in formato binario e li invia in downlink al sensore che esegue il comando.

Installazione codec Elsist su gateway

Per l’utilizzo con i ns FB consigliamo di utilizzare il codec riportato sotto da installare come codec custom per tutti i dispositivi, per il test utilizzare l’utility MilesightEDTest. Per installare il codec dal menù Network Server->Payload Codec del gateway con il tasto [+] definire un nuovo codec copiando le funzioni sottoriportate nelle rispettive caselle.

Screenshot definizione custom payload encoder/decoder
Sfw111, ePayloadtDecoder.js
// =============================================================================
// Project: SFR115B000
// Milesight payload decoder
// Copyright 2025 Elsist Srl
// =============================================================================
// https://github.com/Milesight-IoT/SensorDecoders
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// MAIN FUNCTION
// -----------------------------------------------------------------------------
// Chirpstack v3, v4

function decodeUplink(input) {var decoded = milesightDeviceDecode(input.bytes); return { data: decoded };}
function Decode(fPort, bytes) {return milesightDeviceDecode(bytes);}

// The Things Network

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

// =============================================================================
// FUNZIONE "milesightDeviceDecode(bytes)"
// =============================================================================
// Decodifica dati ricevuti dal sensore.
// -----------------------------------------------------------------------------

function milesightDeviceDecode(bytes)
{
    var decoded={}; //Return value
    for (var i=0; i < bytes.length; )
    {
        var IDType=(bytes[i++]*0x100)+bytes[i++]; //Channel ID/Type
        switch (IDType)
        {
            // -----------------------------------------------------------------
            // TUTTI I DISPOSITIVI
            // -----------------------------------------------------------------

            case 0xFE03: decoded.report_interval=readUInt16LE(bytes.slice(i, i+2)); i+=2; break; //[FE031204]={"report_interval":1042}
            case 0xFF01: decoded.protocol_version="V"+bytes[i++].toString(); break; //[FF0101]={"protocol_version":"V1"}
            case 0xFF09: decoded.hardware_version="V"+bytes[i++].toString()+bytes[i++].toString(16); break; //[FF090140]={"hardware_version":"V140"}
            case 0xFF0A: decoded.software_version="V"+bytes[i++].toString()+bytes[i++].toString(16); break; //[FF0A0114]={"software_version":"V114"}
            case 0xFF0F: decoded.lorawan_class=readLoRaWANClass(bytes[i++]); break; //[FF0F01]={"lorawan_class":"Class B"}
            //case 0xFF16: decoded.device_serial=bytes.slice(i, i+8).map(byte => byte.toString(16).padStart(2, '0')).join(''); i+=8; break; //[ff166136c40091605408]={"device_serial":"6136c40091605408"}

            // -----------------------------------------------------------------
            // EM300
            // -----------------------------------------------------------------
            // battery (%)
            // temperature (°C)

            case 0x0175: decoded.battery=bytes[i++]; break; //[017550]={"battery":80}
            case 0x0367: decoded.temperature=readInt16LE(bytes.slice(i, i+2))/10; i+=2; break; //[03671001]={"temperature":27.2}

            // humidity (%RH)
            // EM500-SMTC Moisture (old resolution 0.5).

            case 0x0468:
            decoded.humidity = bytes[i] / 2; //[046864]={"humidity":50}
            decoded.moisture = bytes[i] / 2; //[046864]={"moisture":50}
            i++; break;

            // -----------------------------------------------------------------
            // EM300-DI: Lettura stato ingresso ed impulsi
            // -----------------------------------------------------------------
            // M300-MLD: leakage 0x0500, vedi "gpio_input_3" in UC300
            // EM300-MCS: magnet 0x0600, vedi "gpio_input_4" in UC300

            case 0x8500: decoded.gpio_alarm=(bytes[i++] == 0)?"release":"alarm"; break; //EM300-DI:[850000]={"gpio_alarm":"release"}

            // 0x05C8, per versioni software fino alla 1.2
            // Gestito in modello UC300

            // 0x05E1, per versioni software successive
            // 8 Bytes, water_conv(2B)+pulse_conv(2B)+Water consumption(4B)
            // [05e10a000a0000005b43]={"water_conv":1,"pulse_conv":1,"water":219}

            case 0x05E1:
            decoded.water_conv=readUInt16LE(bytes.slice(i, i+2))/10; i+=2;
            decoded.pulse_conv=readUInt16LE(bytes.slice(i, i+2))/10; i+=2;
            decoded.water=readFloatLE(bytes.slice(i, i+4)); i+=4;
            break;

            // -----------------------------------------------------------------
            // UC300
            // -----------------------------------------------------------------
            // 0x0701 gpio_output_1, utilizzato in UC51x
            // 0x0801 gpio_output_2, utilizzato in UC51x

            case 0x0300: decoded.gpio_input_1=bytes[i++]; break; //[030000]={"gpio_input_1":0}
            case 0x0400: decoded.gpio_input_2=bytes[i++]; break; //[040000]={"gpio_input_2":0}
            case 0x0500: decoded.gpio_input_3=bytes[i++]; break; //[050000]={"gpio_input_3":0}
            case 0x0600: decoded.gpio_input_4=bytes[i++]; break; //[050000]={"gpio_input_4":0}

            case 0x0701: decoded.gpio_output_1=bytes[i++]; break; //[070100]={"gpio_output_1":0}
            case 0x0801: decoded.gpio_output_2=bytes[i++]; break; //[080100]={"gpio_output_2":0}

            case 0x03C8: decoded.gpio_counter_1=readUInt32LE(bytes.slice(i, i+4)); i+=4; break; //[03C810200000]={"gpio_counter_1":8208}
            case 0x04C8: decoded.gpio_counter_1=readUInt32LE(bytes.slice(i, i+4)); i+=4; break; //[04C810200000]={"gpio_counter_2":8208}
            case 0x05C8: decoded.gpio_counter_1=readUInt32LE(bytes.slice(i, i+4)); i+=4; break; //[05C810200000]={"gpio_counter_3":8208}
            case 0x06C8: decoded.gpio_counter_1=readUInt32LE(bytes.slice(i, i+4)); i+=4; break; //[06C810200000]={"gpio_counter_4":8208}

            // -----------------------------------------------------------------
            // UC51x
            // -----------------------------------------------------------------
            // 0x04C8 valve_1_pulse, gestito in UC300 [04C810200000]={"gpio_counter_2":8208}
            // 0x06C8 valve_2_pulse, gestito in UC300 [06C810200000]={"gpio_counter_4":8208}
            // 0x0701 gpio_1, gestito in UC300 [070101]={"gpio_output_1":1}
            // 0x0801 gpio_2, gestito in UC300 [080101]={"gpio_output_2":1}

            case 0x0301: decoded.valve_1 = bytes[i++]; break; //[030100]={"valve_1":0}
            case 0x0501: decoded.valve_1 = bytes[i++]; break; //[050100]={"valve_2":0}

            // -----------------------------------------------------------------
            // EM500-SMTC
            // -----------------------------------------------------------------
            // Moisture (old resolution 0.5), stesso codice e valenza di umidità
            // Gestito in EM300 [046864]={"moisture":50}
            // Moisture (%RH)(new resolution 0.01)
            // ec (µs/cm)

            case 0x04CA: decoded.moisture=readUInt16LE(bytes.slice(i, i+2))/100; i+=2; break; //[04CA1020]={"moisture":82.08}
            case 0x057F: decoded.ec=readUInt16LE(bytes.slice(i, i+2)); i+=2; break; //[057F2010]={"ec":4128}

            // Temperature change alarm.
            // [83D71001200001]={"temperature":27.2,"temperature_change":3.2,"temperature_alarm":"threshold alarm release"}

            case 0x83D7:
            decoded.temperature=readInt16LE(bytes.slice(i, i+2))/10; i+=2;
            decoded.temperature_change=readInt16LE(bytes.slice(i, i+2))/10; i+=2;
            decoded.temperature_alarm=readTemperatureAlarm(bytes[i++]);
            break;

            // -----------------------------------------------------------------
            // CODICE NON GESTITO
            // -----------------------------------------------------------------
            // Se codice non gestito esco non posso proseguire nella decodifica.

            default: decoded.code_not_managed=IDType.toString(16).toUpperCase().padStart(2, '0'); return decoded;
        }
    }
    return decoded;
}

// -----------------------------------------------------------------------------
// Bytes to number
// -----------------------------------------------------------------------------

function readUInt16LE(bytes) {var value=(bytes[1] << 8)+bytes[0]; return(value&0xffff);}
function readInt16LE(bytes) {var ref=readUInt16LE(bytes); return(ref > 0x7fff ? ref-0x10000 : ref);}
function readUInt32LE(bytes) {var value=(bytes[3] << 24)+(bytes[2] << 16)+(bytes[1] << 8)+bytes[0]; return(value&0xffffffff);}

// JavaScript bitwise operators yield a 32 bits integer, not a float.
// Assume LSB (least significant byte first).

function readFloatLE(bytes) {
    var bits = (bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | bytes[0];
    var sign = bits >>> 31 === 0 ? 1.0 : -1.0;
    var e = (bits >>> 23) & 0xff;
    var m = e === 0 ? (bits & 0x7fffff) << 1 : (bits & 0x7fffff) | 0x800000;
    var f = sign * m * Math.pow(2, e - 150);
    var n = Number(f.toFixed(2));
    return n;
}

// -----------------------------------------------------------------------------
// Local decoder functions
// -----------------------------------------------------------------------------

// Funzione che ritorna lo stato del GPIO.
function readGPIOStatus(bytes){switch (bytes){case 0: return "low"; case 1: return "high"; default: return "unknown";}}

// Funzione che ritorna la classe LoRaWAN.
function readLoRaWANClass(bytes){switch (bytes){case 0: return "Class A"; case 1: return "Class B"; case 2: return "Class C"; default: return "unknown";}}

// Funzione che ritorna la condizione di allarme.
function readTemperatureAlarm(type){switch (type){case 0: return "threshold alarm"; case 1: return "threshold alarm release"; case 2:return "mutation alarm"; default: return "unkown";}}

// [End of file]
Sfw111, ePayloadtEncoder.js
// =============================================================================
// Project: SFR115B000
// Milesight payload encoder
// Copyright 2025 Elsist Srl
// =============================================================================
// https://github.com/Milesight-IoT/SensorDecoders
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// MAIN FUNCTION
// -----------------------------------------------------------------------------
// The Things Network

function Encode(fPort, obj){var encoded = milesightDeviceEncoder(obj); return encoded;}

// =============================================================================
// FUNZIONE "milesightDeviceEncoder(payload)"
// =============================================================================
// Codifica i dati da inviare al sensore.
// -----------------------------------------------------------------------------

function milesightDeviceEncoder(payload)
{
    var encoded=[]; //Return value

    // -------------------------------------------------------------------------
    // DEVICE REBOOT
    // -------------------------------------------------------------------------
    // Valore "0" o superiori a "1" non ritornano nulla
    // @param {number} reboot values: (0: no, 1: yes)
    // @example payload: {"reboot": 1} -> [FF 10 ff]

    if ("reboot" in payload)
    {
        if (payload.reboot == 1) encoded=encoded.concat([0xFF, 0x10, 0xFF]);
    }

    // -------------------------------------------------------------------------
    // REPORT INTERVAL
    // -------------------------------------------------------------------------
    // @param {number} report_interval uint: second
    // @example payload: {"report_interval":600} -> [FF 03 58 02]

    if ("report_interval" in payload)
    {
        if ((typeof payload.report_interval === "number") && (payload.report_interval > 1))
        {
            encoded=encoded.concat([0xFF, 0x03]);
            encoded=encoded.concat(CCUInt16LE(payload.report_interval));
        }
    }

    // -------------------------------------------------------------------------
    // RESPONSE TIME
    // -------------------------------------------------------------------------
    // @param {number} response_time udint: second
    // @example payload: {"response_time":600} -> [FF1E58020000]

    if ("response_time" in payload)
    {
        if ((typeof payload.response_time === "number") && (payload.response_time > 1))
        {
            encoded=encoded.concat([0xFF, 0x1E]);
            encoded=encoded.concat(CCUInt32LE(payload.response_time));
        }
    }

    // -------------------------------------------------------------------------
    // UC51x, SET VALVE 1
    // -------------------------------------------------------------------------
    // @param {number} valve_1 (0: open, 1: close)
    // @example payload: {"valve_1":0} -> [0xFF, 0x1D, 0x00, 0x00]

    if ("valve_1" in payload)
    {
        switch (payload.valve_1)
        {
            case 0: encoded=encoded.concat([0xFF, 0x1D, 0x00, 0x00]); break;
            case 1: encoded=encoded.concat([0xFF, 0x1D, 0x20, 0x00]); break;
        }
    }

    // -------------------------------------------------------------------------
    // UC51x, SET VALVE 2
    // -------------------------------------------------------------------------
    // @param {number} valve_2 (0: open, 1: close)
    // @example payload: {"valve_2":0} -> [0xFF, 0x1D, 0x01, 0x00]

    if ("valve_2" in payload)
    {
        switch (payload.valve_2)
        {
            case 0: encoded=encoded.concat([0xFF, 0x1D, 0x01, 0x00]); break;
            case 1: encoded=encoded.concat([0xFF, 0x1D, 0x21, 0x00]); break;
        }
    }

    // -------------------------------------------------------------------------
    // UC300, SET GPIO 1
    // -------------------------------------------------------------------------
    // @param {number} gpio_output_1 values: (0: low, 1: high)
    // Example payload: {"gpio_output_1":0} -> [0x07, 0x01, 0xFF]

    if ("gpio_output_1" in payload)
    {
        switch (payload.gpio_output_1)
        {
            case 0: encoded=encoded.concat([0x07, 0x00, 0xFF]); break;
            case 1: encoded=encoded.concat([0x07, 0x01, 0xFF]); break;
        }
    }

    // -------------------------------------------------------------------------
    // UC300, SET GPIO 2
    // -------------------------------------------------------------------------
    // @param {number} gpio_output_2 values: (0: low, 1: high)
    // Example payload: {"gpio_output_2":0} -> [0x08, 0x01, 0xFF]

    if ("gpio_output_2" in payload)
    {
        switch (payload.gpio_output_2)
        {
            case 0: encoded=encoded.concat([0x08, 0x00, 0xFF]); break;
            case 1: encoded=encoded.concat([0x08, 0x01, 0xFF]); break;
        }
    }
    return encoded;
}

// -----------------------------------------------------------------------------
// SUPPORT FUNCTIONS
// -----------------------------------------------------------------------------
// Concatena un numero UInt16 (2 Bytes) in little endian.

function CCUInt16LE(DValue)
{
    var bArray=[0x00, 0x00]; //Byte array
    for (var i=(2-1); i >= 0; i--) {bArray[(2-1)-i]=DValue&0xFF; DValue>>=8;}
    return(bArray);
}

// Concatena un numero UInt32 (4 Bytes) in little endian.

function CCUInt32LE(DValue)
{
    var bArray=[0x00, 0x00, 0x00, 0x00]; //Byte array
    for (var i=(3-1); i >= 0; i--) {bArray[(3-1)-i]=DValue&0xFF; DValue>>=8;}
    return(bArray);
}

// [End of file]

Trigger di spy

Se SpyOn attivo è possibile utilizzare utilizzare la console di spionaggio per verificare il funzionamento della FB. Sono previsti vari livelli di triggers.

Livelli di trigger
TriggerDescrizione
16#00000001Rx: Dato ricevuto in uplink.
16#00000002Tx: Dato trasmesso in downlink.
16#10000000Lg: Log di esecuzione.
16#40000000Er: Errore di esecuzione.

Esempi

Come utilizzare gli esempi.

Gestione EM300-TH sensore temperatura ed umidità LoRaWAN della Milesight.

LogicLab (Ptp208, ST_MlsDevice)
PROGRAM ST_MlsDevice
VAR
    Temperature:REAL; (* Temperature (°C) *)
    Humidity:REAL; (* Humidity (%) *)
    SPData : MQTT_TS_DATA; (* MQTT topic subscribe data *)
    TCPClient : SysTCPClient; (* TCP client management *)
    FIFO : FIFOFile_v1; (* FIFO on file *)
    MQTT : MQTTClient_v3; (* MQTT client FB *)
    MDev : MlsDevice; (* Milesight device manager *)
END_VAR

// *****************************************************************************
// PROGRAM "ST_MlsDevice"
// *****************************************************************************
// Is managed a EM300-TH temperature and humidity sensor.
// -----------------------------------------------------------------------------

    // -------------------------------------------------------------------------
    // INITIALIZATION
    // -------------------------------------------------------------------------
    // Initialize the FBs parameters.

    IF (SysFirstLoop) THEN

        // Set FIFO parameters.

        FIFO.FIFOFilename:=eNULL; //Path and name of FIFO file
        FIFO.FIFOSize:=2048; //FIFO file size
        FIFO.FIFOIDx:=eNULL; //FIFO indexes

        // Set TCPClient parameters.

        TCPClient.PeerAdd:=ADR('192.168.1.150'); //Peer address
        TCPClient.PeerPort:=1883; //Peer port
        TCPClient.LocalAdd:=ADR('0.0.0.0'); //Local address
        TCPClient.LocalPort:=0; //Local port
        TCPClient.FlushTm:=0; //Flush time (mS)
        TCPClient.LifeTm:=90; //Life time (S)
        TCPClient.RxSize:=1512; //Rx buffer size
        TCPClient.TxSize:=512; //Tx buffer size

        // Set MQTTClient parameters.

        MQTT.SpyOn:=FALSE; //Spy active
        MQTT.FIFOFile:=ADR(FIFO); //FIFO on file
        MQTT.CFlags:=16#02; //Clean session
        MQTT.Username:=ADR('loraadm'); //Broker username
        MQTT.Password:=ADR('URloraadm123456'); //Broker password
        MQTT.ClientID:=eNULL; //Client identifier
        MQTT.KeepAlive:=T#90s; //Keep alive time
        MQTT.Delay:=T#1s; //Send delay time
        MQTT.Timeout:=T#5s; //Execution timeout

        // Topic subscribe definitions.
        // Subscribe to all applications and all devices rx data.

        MQTT.TSData:=ADR(SPData); //Topic subscribe data
        MQTT.TSNumber:=1; //Topic subscribe number
        eTO_JUNK(MQTT.Subscribe(0, ADR('application/+/device/+/rx'), eNULL, 0, 0));

        // Milesight device definitions.

        MDev.Enable:=TRUE; //FB enable
        MDev.SpyOn:=TRUE; //Spy On
        MDev.MQTT:=ADR(MQTT); //MQTT client
        MDev.DTopic:=ADR('application/1/device'); //Device topic
        MDev.EUI:=ADR('24e124136e313438'); //Device EUI
        MDev.Timeout:=T#10m; //Timeout
    END_IF;

    // -------------------------------------------------------------------------
    // FBs EXECUTION
    // -------------------------------------------------------------------------
    // FBs execution.

    TCPClient(Connect:=MQTT.Connect); //TCPClient management
    MQTT(Enable:=TRUE, File:=TCPClient.File); //MQTTClient management

    // Device management, send the init downlink settings.

    MDev(); //Milesight device manager
    IF (MDev.DInit) THEN
        eTO_JUNK(MDev.DLinkJSON(ADR('{"report_interval":120}'))); //Set reporting interval, 120 (S)
    END_IF;

    // Not needed, is just to show how to spy the complete uplink message.

    // IF (MDev.RxOTrig) THEN eTO_JUNK(SysWrSpyData(SPY_ASCII, 0, 16#00000001, ADR('----'), MQTT.RxValue)); END_IF;

    // The managed device is a EM300-TH so acquire its measures.

    IF NOT(MDev.RxOTrig) THEN RETURN; END_IF;
    eTO_JUNK(JSONDecoder(MDev.RxObject, ADR('temperature'), REAL_TYPE, ADR(Temperature), 1, 0));
    eTO_JUNK(JSONDecoder(MDev.RxObject, ADR('humidity'), REAL_TYPE, ADR(Humidity), 1, 0));

// [End of file]

Was this article helpful?