Page cover image

TRP-11 Electro

Device Profile for Tesenso IoT Cloud

In the projects it has:

Decoder/Payload Converter for Tesenso IoT Cloud

https://github.com/Tesenso-GmbH/Device-Decoder/blob/main/Tetraedre_TRP-11_Elektro.js
// V1.0, 09.08.2021, DS
// V1.1, 11.03.2022, DS


if (msg.data) {
    var decoded = decodeFromHex(parseHexString(msg.data));
    if(decoded.register0801 != null && decoded.register0802 != null){
        decoded.electricityMeterEnergy = decoded.register0801 + decoded.register0802;
    }
    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(bytes) {
    // Decode an uplink message from a buffer
    // (array) of bytes to an object of fields.

    var telemetry = {};

    // Get data length, main_header and initiate the counter
    var sensorDataLength = bytes.length;
    var counter = 1;
    var main_header = bytes[0];

    // Loop through the data and decode the chuncks. Add them to the telemetry
    // immediatly
    while (counter < sensorDataLength) {
        var identifier = bytes[counter];
        var chunkLength = 0;
        var obj = {};
        var subbytes = [];

        if (identifier >= 01 && identifier <= 95) {
            //identifierChunch = "ChunkTypeA";
            chunkLength =
                3; // Type A Chunk. Fixed size. 3 bytes
            subbytes = bytes.slice(counter, counter +
                chunkLength);
            obj = parseChunkTypeA(subbytes);
        } else if (identifier >= 96 && identifier <= 127) {
            //identifierChunch = "ChunkTypeD";
            chunkLength =
                2; // Type D Chunk. Fixed size. 2 bytes
            subbytes = bytes.slice(counter, counter +
                chunkLength);
            obj = parseChunkTypeD(subbytes);
        } else if (identifier >= 128 && identifier <= 191) {
            //identifierChunch = "ChunkTypeB";
            chunkLength =
                5; // Type B Chunk. Fixed size. 5 bytes
            subbytes = bytes.slice(counter, counter +
                chunkLength);
            obj = parseChunkTypeB(subbytes);
        } else if (identifier >= 192 && identifier <= 254) {
            //identifierChunch = "ChunkTypeC";
            chunkLength = bytes[counter + 1] +
                2; // Type C Chunk. Variable size. Size  + 2 (+ leading 2 bytes)
            if (counter + chunkLength > sensorDataLength) {
                chunkLength = sensorDataLength - counter;
            }
            // check if there already is a timesatmp present
            var timestamp = msg.ts;
            // if ('ts' in telemetry) {
            //     timestamp = telemetry[0].ts;
            // }
            subbytes = bytes.slice(counter, counter +
                chunkLength);
            // check if the payload already contained a timestamp
            obj = parseChunkTypeC(subbytes, timestamp);
        } else {
            continue;
        }
        // Push the parsed data to the telemetry array and increment counter
        // Check if it is an array, push the data if yes, otherwise concatenate
        if (Array.isArray(obj)) {
            telemetry.push(obj);
        } else {
            telemetry = Object.assign(telemetry, obj);
        }

        counter += chunkLength;
    }

    // check if the timestamp has been filled by the payload, otherwise use
    // the timestamp of the uplink to loriot
    if (!('ts' in telemetry)) {
        telemetry.ts = msg.ts;
    }

    return telemetry;
}

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 parseHexString(hex) {
    for (var bytes = [], c = 0; c < hex.length; c += 2)
        bytes.push(parseInt(hex.substr(c, 2), 16));
    return bytes;
}


function decodeToString(payload) {
    return String.fromCharCode.apply(String, payload);
}

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

function twos_complement(input_value, num_bits) {
    var mask = Math.pow(2, num_bits - 1);
    return -(input_value & mask) + (input_value & ~mask);
}

function get_int(bytes) {
    var length = bytes.length;
    var int = 0;
    var i;

    for (i = 0; i < length; i++) {
        int += bytes[length - i - 1] << (i * 8);
    }

    return int;
}

function get_float(bytes) {
    var length = bytes.length;
    var float = 0;
    var int = get_int(bytes);

    if (length == 4) {
        var sign = (int & 0x80000000) ? -1 : 1;
        var exponent = ((int >> 23) & 0xFF) - 127;
        var significand = (int & ~(-1 << 23));

        if (exponent == 128) {
            float = sign * ((significand) ? Number.NaN :
                Number.POSITIVE_INFINITY);
        } else if (exponent == -127) {
            if (significand == 0) {
                float = sign * 0.0;
            } else {
                exponent = -126;
                significand /= (1 << 22);
                float = sign * significand * Math.pow(2,
                    exponent);
            }
        } else {
            significand = (significand | (1 << 23)) / (1 <<
                23);
            float = sign * significand * Math.pow(2,
                exponent);
        }
    } else if (length == 2) {
        var left = (int >> 14) & 0x03;
        var value0 = int & 0x3FFF;
        float = value0;
        if (left == 0) {
            float = float * 0.001;
        } else if (left == 1) {
            float = float * 0.02;
            float = float + 16.38;
        } else if (left == 3) {
            float = float * 5;
            float = float + 16725;
        } else if (left == 2) {
            float = float + 344;
        }
    } else {
        throw "Undefined floating point size";
    }
    return float;
}

function parseChunkTypeA(bytes) {
    var telemetry = {};
    var dataHeader = bytes[0];
    var dataValue = get_int(bytes.slice(1));
    if (dataHeader ==
        1
    ) { // 1 = 0x01 Temperature. signed integer 16bits. 1 LSB = 0.01°C.   0: 0°C; 1: +0.01°C; 0xFFFF : -0.01°C
        telemetry.temperature = twos_complement(dataValue,
            16) / 100;
    } else if (dataHeader ==
        2
    ) { // 2 = 0x02 Relative Humidity. unsigned integer 16bits. 1 LSB = 0.01°RH
        telemetry.humidity = twos_complement(dataValue,
            16) / 100;
    } else if (dataHeader ==
        3
    ) { // 3 = 0x03 Oxygen concentration. 1 LSB = 0.001%
        telemetry.oxygen = dataValue / 1000;
    } else if (dataHeader ==
        4) { // 4 = 0x04 CO2 concentration. 1 LSB = 0.001%
        telemetry.co2 = dataValue / 1000;
    } else if (dataHeader ==
        5
    ) { // 5 = 0x05 Second temperature. signed integer 16bits. 1 LSB = 0.01°C.   0: 0°C; 1: +0.01°C; 0xFFFF : -0.01°C
        telemetry.temperature2 = twos_complement(dataValue,
            16) / 100;
    } else if (dataHeader ==
        6
    ) { // 6 = 0x06 Pressure. unsigned integer 16bits. 1 LSB = 0.5mbar
        telemetry.pressure = dataValue / 2;
    } else if (dataHeader ==
        7) { // 7 = 0x07 analog channel #0. 1 LSB = 1 uA
        telemetry.analogCurrent0 = dataValue;
    } else if (dataHeader ==
        8) { // 8 = 0x08 analog channel #1. 1 LSB = 1 uA
        telemetry.analogCurrent1 = dataValue;
    } else if (dataHeader ==
        9) { // 9 = 0x09 analog channel #2. 1 LSB = 1 uA
        telemetry.analogCurrent2 = dataValue;
    } else if (dataHeader ==
        10) { // 10 = 0x0A analog channel #3. 1 LSB = 1 uA
        telemetry.analogCurrent3 = dataValue;
    } else if (dataHeader ==
        11) { // 11 = 0x0B digital inputs state
        telemetry.digitalInputsState = dataValue;
    } else if (dataHeader ==
        12) { // 12 = 0x0C relative pulse counter 0
        telemetry.relativePulseCounter0 = dataValue;
    } else if (dataHeader ==
        13) { // 13 = 0x0D relative pulse counter 1
        telemetry.relativePulseCounter1 = dataValue;
    } else if (dataHeader ==
        14) { // 14 = 0x0E relative pulse counter 2
        telemetry.relativePulseCounter2 = dataValue;
    } else if (dataHeader ==
        16) { // 16 = 0x10 analog channel #0. 1 LSB = 1 mV
        telemetry.analogVoltage0 = dataValue;
    } else if (dataHeader ==
        17) { // 17 = 0x11 analog channel #1. 1 LSB = 1 mV
        telemetry.analogVoltage1 = dataValue;
    } else if (dataHeader ==
        18) { // 18 = 0x12 analog channel #2. 1 LSB = 1 mV
        telemetry.analogVoltage2 = dataValue;
    } else if (dataHeader ==
        19) { // 19 = 0x12 analog channel #3. 1 LSB = 1 mV
        telemetry.analogVoltage3 = dataValue;
    }
    return telemetry;
}

function parseChunkTypeB(bytes) {
    var telemetry = {};
    var dataHeader = bytes[0];
    var dataValue = bytes.slice(1);
    // console.log(dataHeader)
    if (dataHeader ==
        128
    ) { // 128 = 0x80 timestamp of the measurement. unsigned integer 32 bits. UNIX timestamp
        // console.log(dataValue)
        // console.log(get_int(dataValue))
        //telemetry.ts = twos_complement(get_int(dataValue), 32) *1000; // calculate it in miliseconds
    } else if (dataHeader ==
        129
    ) { // 129 = 0x81 Main energy index. IEEE754 floating point. Unit : kWh for electricity meters (register 1.8.0)
        telemetry.electricityMeterEnergy = get_float(
            dataValue);
    } else if (dataHeader ==
        131
    ) { // 129 = 0x81 Main energy index. IEEE754 floating point. Unit : kWh for electricity meters (register 1.8.0)
        telemetry.register0801 = get_float(
            dataValue);
    } else if (dataHeader ==
        132
    ) { // 129 = 0x81 Main energy index. IEEE754 floating point. Unit : kWh for electricity meters (register 1.8.0)
        telemetry.register0802 = get_float(
            dataValue);
    } else if (dataHeader ==
        133
    ) { // 133 = 0x85 Main energy index. IEEE754 floating point. Unit : m3 for water meter
        telemetry.waterMeter = get_float(dataValue);
    } else if (dataHeader ==
        134
    ) { // 134 = 0x86 Main energy index. IEEE754 floating point. Unit : m3 for uncorrected gas meter
        telemetry.gasMeterVolume = get_float(dataValue);
    } else if (dataHeader ==
        135
    ) { // 135 = 0x87 Flow temperature. IEEE754 floating point. Unit : °C
        telemetry.flowTemperature = get_float(dataValue);
    } else if (dataHeader ==
        138
    ) { // 138 = 0x8a Power register. IEEE754 floating point. Unit W.
        telemetry.power = get_float(dataValue);
    } else if (dataHeader ==
        139
    ) { // 139 = 0x8b Volume index. IEEE754 floating point. Unit: m3 for heat meter
        telemetry.heatMeterVolume = get_float(dataValue);
    } else if (dataHeader ==
        140
    ) { // 140 = 0x8c Return flow temperature. IEEE754 floating point. Unit: °C
        telemetry.returnFlowTemperature = get_float(
            dataValue);
    } else if (dataHeader ==
        141
    ) { // 141 = 0x8d VolumeFlow = flowRate. IEEE754 floating point. Unit: m3/h
        telemetry.flowRate = get_float(dataValue);
    } else if (dataHeader ==
        154
    ) { // 154 = 0x9A IEEE754 floating point. Unit : kWh for heat meters (register 1.8.0)
        telemetry.heatMeterEnergy = get_float(dataValue);
    }
    return telemetry;
}

function parseChunkTypeC(bytes, timestamp) {
    var telemetry = [];
    var size = bytes.length;
    var dataHeader = bytes[0];
    var snr = get_int(bytes.slice(1, 5));
    var status = bytes.slice(5, 6);
    var interval =
        900000; // accumulation interval in milliseconds
    var lastReadIndex = get_float(bytes.slice(6, 10));
    var idx = 10;
    var counter = 1;

    // Get the interval from the status
    if ((status & 0x1c) == 0) {
        interval = 3600000;
    } else if ((status & 0x1c) == 0x4) {
        interval = 900000;
    } else if ((status & 0x1c) == 0x8) {
        interval = 38400000;
    }
    // Loop through the deltas and change the timestamp accordingly
    if (dataHeader ==
        201
    ) { // 140 = 0x8c Return flow temperature. IEEE754 floating point. Unit: °C
        telemetry.push({
            //'ts': timestamp,
            'waterMeterSNR': snr,
            'waterMeterCounter': lastReadIndex,
            'batteryError': Boolean(status &
                0x2),
            'otherError': Boolean(status & 1)
        })
        while (idx <= size) {
            var slice = bytes.slice(idx, idx + 2);
            if (!(get_int(slice) == 0xffff)) {
                telemetry.push({
                    //'ts': timestamp - counter * interval,
                    'waterMeterCounter': lastReadIndex +
                        get_float(slice)
                })
            }
            idx += 2;
            counter++;
        }
    } else if (dataHeader ==
        202
    ) { // 141 = 0x8d Volume flow. IEEE754 floating point. Unit: m3/h
        telemetry.push({
            //'ts': timestamp,
            'gasMeterSNR': snr,
            'gasMeterCounter': lastReadIndex,
            'batteryError': Boolean(status &
                0x2),
            'otherError': Boolean(status & 1)
        })
        while (idx <= size) {
            var slice = bytes.slice(idx, idx + 2);
            if (!(get_int(slice) == 0xffff)) {
                telemetry.push({
                    //'ts': timestamp - counter * interval,
                    'gasMeterCounter': lastReadIndex +
                        get_float(slice)
                })
            }
            idx += 2;
            counter++;
        }
    }
    return telemetry;
}

function parseChunkTypeD(bytes) {
    var telemetry = {};
    var dataHeader = bytes[0];
    var dataValue = get_int(bytes.slice(1));

    if (dataHeader == 96) { // 96 = 0x60 Battery voltage
        var volt = 0;
        if (dataValue >= 81) {
            volt = 4.2 + (dataValue - 80) * 0.1
        } else {
            volt = 1.8 + dataValue * 0.03
        }
        telemetry.batteryVoltage = volt;
    } else if (dataHeader ==
        97) { // 97 = 0x61 M-BUS meter status byte
        telemetry.mBusMeterStatus = dataValue;
    }
    return telemetry;
}

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';
    }
}

Device Labels

in manual
dataKey
dataType
dataFormat

Temperature

temperature

telemetry

integer

Relative Humidity

humidity

telemetry

integer

Oxygen concentration

oxygen

telemetry

CO2 concentration

co2

telemetry

Second temperature

temperature2

telemetry

integer

Pressure

pressure

telemetry

integer

analog channel #0

analogCurrent0

telemetry

analog channel #1

analogCurrent1

telemetry

analog channel #2

analogCurrent2

telemetry

analog channel #3

analogCurrent3

telemetry

digital inputs state

digitalInputsState

telemetry

relative pulse counter 0

relativePulseCounter0

telemetry

relative pulse counter 1

relativePulseCounter1

telemetry

relative pulse counter 2

relativePulseCounter2

telemetry

analog channel #0

analogVoltage0

telemetry

analog channel #1

analogVoltage1

telemetry

analog channel #2

analogVoltage2

telemetry

analog channel #3

analogVoltage3

telemetry

Main energy index

electricityMeterEnergy

telemetry

float

Main energy index

register0801

telemetry

float

Main energy index

register0802

telemetry

float

Main energy index

waterMeter

telemetry

float

Main energy index

gasMeterVolume

telemetry

float

Flow temperature

flowTemperature

telemetry

float

Power register

power

telemetry

float

Volume index

heatMeterVolume

telemetry

float

Return flow temperature

returnFlowTemperature

telemetry

float

VolumeFlow

flowrate

telemetry

float

floating point

heatMeterEnergy

telemetry

float

Battery

batteryVoltage

telemetry

M-BUS meter status byte

mBusMeterStatus

telemetry

{
    "cmd": "gw",
    "seqno": 127364,
    "EUI": "BE7A000000000019",
    "ts": 1661429994737,
    "fcnt": 20744,
    "port": 3,
    "freq": 868500000,
    "toa": 1646,
    "dr": "SF12 BW125 4/5",
    "ack": false,
    "gws": [{
        "rssi": -63,
        "snr": 4.2,
        "ts": 1661429994737,
        "time": "2022-08-25T12:19:54.703339Z",
        "gweui": "9C65F9FFFF3CDE7F",
        "ant": 0,
        "lat": 47.3759744,
        "lon": 8.5327872
    }, {
        "rssi": -111,
        "snr": -0.2,
        "ts": 1661429994745,
        "time": "2022-08-25T12:19:54.681657Z",
        "gweui": "9C65F9FFFF3CDDC3",
        "ant": 0,
        "lat": 47.3759744,
        "lon": 8.5327872
    }],
    "bat": 255,
    "data": "0180011cd5a3603d61008144b2328f",
    "_id": "630768ea3184c84733e88996"
}
jso
{
    "msg": {
        "batteryVoltage": 3.63,
        "mBusMeterStatus": 0,
        "electricityMeterEnergy": 1425.5799560546875,
        "ts": 1661262701281,
        "rssi": -80,
        "snr": 9.8,
        "toa": 61,
        "frequency": 867700000,
        "dr": "SF7 BW125 4/5",
        "bat": "External power source",
        "hex": "0180011cd5a3603d61008144b2328f"
    },
    "metadata": {
        "deviceType": "default",
        "deviceName": "Test Device",
        "ts": "1661430878088"
    },
    "msgType": "POST_TELEMETRY_REQUEST"
}

Last updated