Page cover image

LPN AI

4..20mA to LoRa interface

LPN AI SW Specification / V0.11

Product Datasheet

Device Profile for Tesenso IoT Cloud

Decoder/Payload Converter for Tesenso IoT Cloud

https://github.com/Tesenso-GmbH/Device-Decoder/blob/main/Comtac_LPN_AI.js
//V1.0, 20.08.2021,DS

if (msg.data) {
    var decoded = decodeFromHex(msg.data);
    decoded.ts = msg.ts;
    decoded.rssi = msg.rssi;
    decoded.snr = msg.snr;
    decoded.toa = msg.toa;
    decoded.frequency = msg.frequency;
    decoded.dr = msg.dr;
    decoded.bat = decodeBattery(msg.bat);
    decoded.hex = msg.data;

    return {
        msg: decoded,
        metadata: metadata,
        msgType: msgType
    };
} else {
    return {
        msg: msg,
        metadata: metadata,
        msgType: msgType
    };
}

function decodeFromHex(data) {
    // Decode an uplink message from a buffer
    // (array) of bytes to an object of fields.

    var telemetry = {};
    var attributes = {};
    
    var bytes = parseHexString(data);
    var hexString = "0x";
    
    if (bytes[0] == "22")
    {
        //var rssi = -bytes[3];
        //var snr = bytes[4];
        
        var dataString11 = data.slice(14,16);
        var dataString12 = data.slice(12,14);
        var dataFixed1 = hexString.concat(dataString11,dataString12);
        var tempInt1 = parseInt(dataFixed1);
        var analogInput1 = tempInt1/1000.0;
        
        var dataString21 = data.slice(20,22);
        var dataString22 = data.slice(18,20);
        var dataFixed2 = hexString.concat(dataString21,dataString22);
        var tempInt2 = parseInt(dataFixed2);
        var analogInput2 = tempInt2/1000.0;
        telemetry.analogInput1 = analogInput1;
        telemetry.analogInput2 = analogInput2;
    }
    
    return telemetry;
}

function parseFloat(str) {
    var float = 0,
        sign, order, mantiss, exp,
        int = 0,
        multi = 1;
    if (/^0x/.exec(str)) {
        int = parseInt(str, 16);
    } else {
        for (var i = str.length - 1; i >= 0; i -= 1) {
            if (str.charCodeAt(i) > 255) {
                console.log('Wrong string parametr');
                return false;
            }
            int += str.charCodeAt(i) * multi;
            multi *= 256;
        }
    }
    sign = (int >>> 31) ? -1 : 1;
    exp = (int >>> 23 & 0xff) - 127;
    mantissa = ((int & 0x7fffff) + 0x800000).toString(2);
    for (i = 0; i < mantissa.length; i += 1) {
        float += parseInt(mantissa[i]) ? Math.pow(2, exp) :
            0;
        exp--;
    }
    return float * sign;
}

function findRegister(bytes, registersActive) {
    var registers = new Array(4);
    var start = 3;

    for (var i = 0; i < registersActive.length; i++) {
        var current = start * (i + 1);
        if (registersActive[i] == 1) {
            registers[i] = bytes[current] + "." + bytes[
                current + 1] + "." + bytes[current + 2];
        }
    }
    return registers;

}

function decodeBattery(byte) {
    if (byte == 0) {
        return 'External power source';
    } else if (byte > 0 && byte < 255) {
        return byte / 254 * 100;
    } else {
        return 'Unknown battery state';
    }
}

function calculateBattery(nibbles) {
    var result = nibbles[2] * 10;
    return result;
}

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

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

function parseHexStringToNibbles(hex) {
    for (var nibbles = [], c = 0; c < hex.length; c += 1)
        nibbles.push(parseInt(hex.substr(c, 1), 16));
    return nibbles;
}

function convertToBinary(x) {
    var bin = 0;
    var rem, i = 1,
        step = 1;
    while (x != 0) {
        rem = x % 2;
        x = parseInt(x / 2);
        bin = bin + rem * i;
        i = i * 10;
    }
    return bin;
}

function decodeToJson(payload) {
    // covert payload to string.
    var str = decodeToString(payload);
    // parse string to JSON
    var data = JSON.parse(str);
    return data;
}

Quick Setup

At first, connect the Device like in the picture shown:

Technical Specification

Document Information:

The LPN AI can handle 2 analog inputs, according to user configuration, and transmit the results to a LoRaWAN server.

Telegrams from the server to the Node (LPN AI) are downlinks and from the node to the server are uplinks. In the LoRaWAN, all uplinks are provided with a CRC by default, but the downlinks are not.

  • 1. Jumper settings:

Before connecting the input signal path to the LPN AI, set the jumper correctly:

For correct measurement configure the AI1 and AI2 MeasureTyp equivalent. Attention: Wrong Jumper settings can damage the device, especially the position 2-3 (0...20mA) with input voltage

>2V can damage the burden!

  • 2. Function Buttons

Reset of the device configuration during power up possible: If both buttons are pressed, the LoRa configuration (CFG.TXT) is reset to the default values.Function of LEDs

  • 3. Function of LEDs

After switching on, all LEDs light up for 0.5 seconds, if the LEDs remain lit and the red LED flashes briefly, the bootloader is active. Blink variants of the blue and green LEDs: 12%-> 0.7s off + 0.1s on; 50%--> 0.4s off + 0.4s on; 88%-> 0.1 s off + 0.7s on

  • 4. LoRa Up and Downlink

LoRa Port 4’AI’:

An Uplink is sent on port 4, when an AI application uplink is triggered or button 1 was pressed or by the live sign interval LivesignConfirmedTx.

The port 4 uplink is also used for each unknown (and Port 4) confirmed port downlink

Uplink payload structure:

The size of the payload is depending on the selected send method chosen in the config.txt file.

Payload size (n+1) is 9..49 (Standard size is 11). The AI1 or AI2 outputs data in the Fehler! Verweisquelle konnte nicht gefunden werden. according to the MeasureTyp.

  • 5. Configuration via USB interface

Insert the USB cable and open CFG.TXT, where all settings for LoRa and AI can be configured (not in USB-CDC Mode).

Configuration changes only take effect after a restart.

LoRa configuration in CFG. TXT:

LoRa (vers. 0x43010200):

PrivateNetwork=0 // 0 = Public (Preamble = 0x34) 1 = Private (Preamble = 0x12)

Activation:

OTA=0

OTA(OverTheAir):

DevEUI=3734333665357D04

AppEUI=70B3D5FFFE29701B

AppKey=2B8DEFCD2301674554761032DCF E98BA

ABP(ActivationByPersonalization): DevAddr=0x00420136

NetwSesKey=1123456789ABCDEFFEDCBA9 876543211

AppSesKey=EEDCBA987654321001234567 89ABCDEE

Broadcast: BC_Addr=0x00000000 // 0 for not used

BC_NetwSesKey=2223456789ABCDEEEEDCB A9876543222

BC_AppSesKey=DDDCBA98765432111123456 789ABCDDD

Datarate (0.7;) DR_0... DR_7. SF12... FSK):

MinDR =0

MaxDR =7

DefDR =0

Rx2DefDR=0 // default receives data rate

Startup: // Start-up behavior first sending in a time slot or random: SlotTime=000 [100ms] // for Var1 + 3 (min. 10 s at OTA; = 0-> OTA 10s ABP s = 2.3)

TimeSlotNr=0000 // Var1: (0 see Var2) 1.. 9999-> OTA: TimeSlotNr * 10 s ABP: TimeSlotNr * 2.3

s RndTime=0010 [m] // Var2: (0 see Var3) 1.. 9999-> randomize 10 s... XXXX * 60s

GrpDevAddr=1024 // Var3: (0 see Var2 with 0060) 1.. 9999-> TimeSlotNr = DevAddr/GrpDevAddr + 1-> Var1

Communication:

ConfirmedTx=0 // send 0 = unconfirmed 1 = confirmed uplinks

LivesignConfirmedTx=1440 [m] // At the latest after this time + ConfirmedTxTimeout send confirmed Tx

uplink ConfirmedTxTimeout=0000 [s] // 0 = send immediate. x = no later than x seconds send

RxConfirmTimeout=0000[s] // 0 = confirm immediately. x = confirm after x seconds

The first uplink can also be forced with button 1. Uplinks varies randomly in the range of 0...2s. LivesignConfirmedTx ensures, at a defined interval, that the uplink is maintained by triggering a confirmed Tx. By means of ConfirmedTxTimeout, an application telegram can also be sent as confirmed if an application telegram is sent in this time window. The Acknowledgment can be terminated by means of the RxConfirmTimeout with the confirmed downlink, so an application response can also contain the acknowledgment during this time (the Ack is sent immediately at 0).

  • 6. AI configuration in CFG. TXT

AI:

SendInterval=0060 [m]

MinSendInterval=0010 [m] (for Sendmethod Delta and Trigger)

MeasureTyp 0:U 0..10000[mV]; 1:I 0..20000[uA]; 2:PT1000 -500..+1500[°C/10] (see Jumper settings)

| Sendmethod 0:Value 1:Data list 2:Mean data list 3:Mean 4:Mean-Min-Max 5:Delta 6:Trigger

| | Measureinterval [m] (0:SendInterval for Sendmethod 1+2 and loop (about 8..16ms) for Sendmethod>2)

| | | Delta

| | | | Lower Trigger

| | | | | Upper

Trigger

AI_1=0;0;0000;0000;00000;00000

AI_2=0;0;0000;0000;00000;00000

Max. 10 values can be saved in the data list, so SendInterval / Measureinterval should be <= 10, otherwise older values will be overwritten by newer ones.

The mean value in the Mean data list is measured in a loop (8ms for 1 channel or 16ms for both channels). Other mean values are measured in the Measureinterval (0 is loop).

The mean value summarizes up to 65535 measurements (about 18 minutes for 2 channels and 9 minutes for one channel) and after 65535 measurements it calculate like this sum= sum-sum/65535+measure.

The Delta, Lower Trigger and Upper Trigger value should be in the Fehler! Verweisquelle konnte nicht gefunden werden. of the corresponding MeasureTyp.

An AI uplink is generated each SendInterval or in case of Sendmethod-Delta or Sendmethod-Trigger additionally on event and expired MinSendInterval. The SendInterval and MinSendInterval are reloaded after each uplink.

  • 7. SW update via USB bootloader

If necessary, the CM1 can be updated using USB DFU:

  1. DFU tool «DFuSe demo» start (link http://www.st.com/en/development-tools/stsw-stm32080.html ).

  2. Click "Choose..." under upgrade or verify action (bottom right) to load the current DFU file.

  3. Take out the battery (turn off device), plug in USB cable with the button pushed and then put the batteries back in (turn on device).

  4. The device should be now in bootloader mode (device appears under «available DFU devices» and both LEDs blink time-shifted every 500ms).

  5. Click «Upgrade» and ignore any messages. The update takes about 2 minutes and 20 seconds.

  6. Once the update is finished, unplug the USB cable, and restart the device (take out batteries, and put them back in).

Important: After having installed the DFU tool, look up the UM0412.pdf file. Before starting with the first update, the driver path must be searched manually (C:\Program files (x 86) \STMicroelectronics\Software\DfuSe v3.0.5\Bin\Driver).

Server Attributes for converting the 4-20mA Signal into a measurement value

Server Attributes:

  • valueMax

  • valueMin

  • indicatorMax

  • indicatorMin

Last updated