LHi110-DNiL Electronics Codec help

Hi , we get some UplinkCodec Error on the code we got from manufacturer and I get no response back on further help. And my lack of skills in javascript makes it almost impossible. There is a comment about the code is not compatible with Otto JS ES5.

Are there someone that can identify the problem in the decoder?

/Torbjorn

function Decode(payload, fPort) {
    // Use the port to determine uplink data type
    // All are MSB, Most Significant Bit/Byte first.
    // Port 1: Protocol data
    var obj = {};
    var bytes = HexToBytes(payload);
  
    function toHexString(byteArray) {
      return Array.prototype.map
        .call(byteArray, function (byte) {
          return ("0" + (byte & 0xff).toString(16)).slice(-2);
        })
        .join("-");
    }
  
    if (fPort === 1) {
      // Data
      if (bytes[0] === 0x01) {
        // Data received, byte 1 contains index
        switch (bytes[1]) {
          // FW Build hash
          case 0x03: {
            // Convert received butes to a string from byte 2 onwards
            obj.fwVersion = String.fromCharCode.apply(String, bytes.slice(2));
            break;
          }
          // CPU Voltage
          case 0x06: {
            // CPU Voltage is send as a 16-bit unsigned integer
            obj.vdd = (bytes[2] << 8) | bytes[3];
            break;
          }
          // CPU Temperature
          case 0x0a: {
            // CPU Temperature is send as a 16-bit unsigned int
            // with 0.01C coding and 50C offset
            obj.CPUTemperature = (((bytes[2] << 8) | bytes[3]) - 5000) / 100;
            break;
          }
          // Status
          case 0x20: {
            // Bitfield, convert to string of base 2
            obj.status = bytes[2].toString(2);
            break;
          }
          // ReportInterval
          case 0x22: {
            // Interval encoded as uint16
            obj.reportInterval = (bytes[2] << 8) | bytes[3];
            break;
          }
          // MsgType
          case 0x23: {
            obj.msgType = bytes[2];
            break;
          }
  
          default: {
          }
        }
      } else if (bytes[0] === 0x02) {
        // NACK received, byte 1 contains nack index
        obj.nackIndex = bytes[1];
      }
    } else if (fPort === 2) {
      function zfill(num, len) {
        return (Array(len).join("0") + num).slice(-len);
      }
  
      function arrToUint16(byteArr, startIdx) {
        return (byteArr[startIdx] << 8) | byteArr[startIdx + 1];
      }
  
      function arrToUint32(byteArr, startIdx) {
        return (
          (byteArr[startIdx] << 24) |
          (byteArr[startIdx + 1] << 16) |
          (byteArr[startIdx + 2] << 8) |
          byteArr[startIdx + 3]
        );
      }
  
      /*
      function arrToUint40(byteArr, startIdx) {
        // This doesn't work with old "otto" JS engine in "Go" language, 32-bit limits hit... Leave it for now
        return (
          (byteArr[startIdx] << 32) |
          (byteArr[startIdx + 1] << 24) |
          (byteArr[startIdx + 2] << 16) |
          (byteArr[startIdx + 3] << 8) |
          byteArr[startIdx + 4]
        );
      }
      */

// CoPilot change before know Otto JS engine
      function arrToUint40(byteArr, startIdx) {
        return (
          (BigInt(byteArr[startIdx]) << BigInt(32)) |
          (BigInt(byteArr[startIdx + 1]) << BigInt(24)) |
          (BigInt(byteArr[startIdx + 2]) << BigInt(16)) |
          (BigInt(byteArr[startIdx + 3]) << BigInt(8)) |
          BigInt(byteArr[startIdx + 4])
        );
      }


      function decodePower(pwrData) {
        if ((1 << 15) & pwrData) {
          // If bit 15 is set, scale by 100W
          return (pwrData & ~(1 << 15)) * 100;
        } else {
          return pwrData;
        }
      }
  
      function decodeVoltages(byteArr, startIdx) {
        var lineVoltArr = arrToUint32(byteArr, startIdx);
  
        var l1Volt_mV = ((lineVoltArr & 0x3ff00000) >> 20) * 250;
        var l2Volt_mV = ((lineVoltArr & 0x000ffc00) >> 10) * 250;
        var l3Volt_mV = ((lineVoltArr & 0x000003ff) >> 0) * 250;
  
        if (l1Volt_mV != 0) {
          l1Volt_mV += 20000;
        }
        if (l2Volt_mV != 0) {
          l2Volt_mV += 20000;
        }
        if (l3Volt_mV != 0) {
          l3Volt_mV += 20000;
        }
        return [l1Volt_mV, l2Volt_mV, l3Volt_mV];
      }
  
      function decodeCurrents(byteArr, startIdx) {
        var lineCurrArr = arrToUint32(byteArr, startIdx);
        return [
          ((lineCurrArr & 0x3ff00000) >> 20) * 100,
          ((lineCurrArr & 0x000ffc00) >> 10) * 100,
          ((lineCurrArr & 0x000003ff) >> 0) * 100,
        ];
      }
  
      obj.msgType = bytes[0];
      obj.timeStamp = arrToUint32(bytes, 1);
      // JS expects timestamp in milliseconds
      var dateObj = new Date(obj.timeStamp * 1000);
      obj.timeString =
        dateObj.getUTCFullYear() +
        "-" +
        zfill(dateObj.getUTCMonth() + 1, 2) +
        "-" +
        zfill(dateObj.getUTCDate(), 2);
      obj.timeString +=
        " " +
        zfill(dateObj.getUTCHours(), 2) +
        ":" +
        zfill(dateObj.getUTCMinutes(), 2) +
        ":" +
        zfill(dateObj.getUTCSeconds(), 2);
  
      switch (obj.msgType) {
        // Msg type 1
        case 0x01: {
          // Meter readings
          obj.activeImportReading = arrToUint40(bytes, 5);
          // Active power data, peak and averages
          obj.actL1ImportPowerPeak = decodePower(arrToUint16(bytes, 10));
          obj.actL1ImportPowerAver = decodePower(arrToUint16(bytes, 12));
          obj.actL2ImportPowerPeak = decodePower(arrToUint16(bytes, 14));
          obj.actL2ImportPowerAver = decodePower(arrToUint16(bytes, 16));
          obj.actL3ImportPowerPeak = decodePower(arrToUint16(bytes, 18));
          obj.actL3ImportPowerAver = decodePower(arrToUint16(bytes, 20));
          // Line voltages
          obj.l1VoltageAver_mV = decodeVoltages(bytes, 22)[0];
          obj.l2VoltageAver_mV = decodeVoltages(bytes, 22)[1];
          obj.l3VoltageAver_mV = decodeVoltages(bytes, 22)[2];
          // Line currents
          obj.l1CurrentPeak_mA = decodeCurrents(bytes, 26)[0];
          obj.l2CurrentPeak_mA = decodeCurrents(bytes, 26)[1];
          obj.l3CurrentPeak_mA = decodeCurrents(bytes, 26)[2];
          break;
        }
  
        // Msg type 2
        case 0x02: {
          // Meter readings
          obj.activeImportReading = arrToUint40(bytes, 5);
          obj.activeExportReading = arrToUint40(bytes, 10);
          // Active power data, peak and averages
          obj.actL1ImportPowerPeak = decodePower(arrToUint16(bytes, 15));
          obj.actL1ImportPowerAver = decodePower(arrToUint16(bytes, 17));
          obj.actL2ImportPowerPeak = decodePower(arrToUint16(bytes, 19));
          obj.actL2ImportPowerAver = decodePower(arrToUint16(bytes, 21));
          obj.actL3ImportPowerPeak = decodePower(arrToUint16(bytes, 23));
          obj.actL3ImportPowerAver = decodePower(arrToUint16(bytes, 25));
  
          obj.actL1ExportPowerAver = decodePower(arrToUint16(bytes, 27));
          obj.actL2ExportPowerAver = decodePower(arrToUint16(bytes, 29));
          obj.actL3ExportPowerAver = decodePower(arrToUint16(bytes, 31));
          // Line voltages
          obj.l1VoltageAver_mV = decodeVoltages(bytes, 33)[0];
          obj.l2VoltageAver_mV = decodeVoltages(bytes, 33)[1];
          obj.l3VoltageAver_mV = decodeVoltages(bytes, 33)[2];
          // Line current
          obj.l1CurrentPeak_mA = decodeCurrents(bytes, 37)[0];
          obj.l2CurrentPeak_mA = decodeCurrents(bytes, 37)[1];
          obj.l3CurrentPeak_mA = decodeCurrents(bytes, 37)[2];
          break;
        }
  
        // Msg type 3
        case 0x03: {
          // Meter readings
          obj.activeImportReading = arrToUint40(bytes, 5);
          obj.reactiveImportReading = arrToUint40(bytes, 10);
          obj.reactiveExportReading = arrToUint40(bytes, 15);
          // Active power data, peak and averages
          obj.actL1ImportPowerAver = decodePower(arrToUint16(bytes, 20));
          obj.actL2ImportPowerAver = decodePower(arrToUint16(bytes, 22));
          obj.actL3ImportPowerAver = decodePower(arrToUint16(bytes, 24));
  
          obj.reactL1ImportPowerAver = decodePower(arrToUint16(bytes, 26));
          obj.reactL2ImportPowerAver = decodePower(arrToUint16(bytes, 28));
          obj.reactL3ImportPowerAver = decodePower(arrToUint16(bytes, 30));
  
          obj.reactL1ExportPowerAver = decodePower(arrToUint16(bytes, 32));
          obj.reactL2ExportPowerAver = decodePower(arrToUint16(bytes, 34));
          obj.reactL3ExportPowerAver = decodePower(arrToUint16(bytes, 36));
          // Line voltages
          obj.l1VoltageAver_mV = decodeVoltages(bytes, 38)[0];
          obj.l2VoltageAver_mV = decodeVoltages(bytes, 38)[1];
          obj.l3VoltageAver_mV = decodeVoltages(bytes, 38)[2];
          // Line currents
          obj.l1CurrentPeak_mA = decodeCurrents(bytes, 42)[0];
          obj.l2CurrentPeak_mA = decodeCurrents(bytes, 42)[1];
          obj.l3CurrentPeak_mA = decodeCurrents(bytes, 42)[2];
          break;
        }
  
        // Msg type 4
        case 0x04: {
          // Meter readings
          obj.activeImportReadingLowTariff = arrToUint40(bytes, 5);
          obj.activeImportReadingNormTariff = arrToUint40(bytes, 10);
          // Active power averages
          obj.actL1ImportPowerAver = decodePower(arrToUint16(bytes, 15));
          obj.actL2ImportPowerAver = decodePower(arrToUint16(bytes, 17));
          obj.actL3ImportPowerAver = decodePower(arrToUint16(bytes, 19));
          // Line voltages
          obj.l1VoltageAver_mV = decodeVoltages(bytes, 21)[0];
          obj.l2VoltageAver_mV = decodeVoltages(bytes, 21)[1];
          obj.l3VoltageAver_mV = decodeVoltages(bytes, 21)[2];
          // Line currents
          obj.l1CurrentPeak_mA = decodeCurrents(bytes, 25)[0];
          obj.l2CurrentPeak_mA = decodeCurrents(bytes, 25)[1];
          obj.l3CurrentPeak_mA = decodeCurrents(bytes, 25)[2];
          // Tariff indicator
          obj.tariff = bytes[29];
          break;
        }
  
        // Msg type 5
        case 0x05: {
          // Meter readings
          obj.activeImportReadingLowTariff = arrToUint40(bytes, 5);
          obj.activeImportReadingNormTariff = arrToUint40(bytes, 10);
  
          obj.activeExportReadingLowTariff = arrToUint40(bytes, 15);
          obj.activeExportReadingNormTariff = arrToUint40(bytes, 20);
          // Active power averages
          obj.actL1ImportPowerAver = decodePower(arrToUint16(bytes, 25));
          obj.actL2ImportPowerAver = decodePower(arrToUint16(bytes, 27));
          obj.actL3ImportPowerAver = decodePower(arrToUint16(bytes, 29));
  
          obj.actL1ExportPowerAver = decodePower(arrToUint16(bytes, 31));
          obj.actL2ExportPowerAver = decodePower(arrToUint16(bytes, 33));
          obj.actL3ExportPowerAver = decodePower(arrToUint16(bytes, 35));
          // Line voltages
          obj.l1VoltageAver_mV = decodeVoltages(bytes, 37)[0];
          obj.l2VoltageAver_mV = decodeVoltages(bytes, 37)[1];
          obj.l3VoltageAver_mV = decodeVoltages(bytes, 37)[2];
          // Line currents
          obj.l1CurrentPeak_mA = decodeCurrents(bytes, 41)[0];
          obj.l2CurrentPeak_mA = decodeCurrents(bytes, 41)[1];
          obj.l3CurrentPeak_mA = decodeCurrents(bytes, 41)[2];
          // Tariff indicator
          obj.tariff = bytes[45];
          break;
        }
      }
    } else if (fPort === 10) {
      obj.equipmentId = toHexString(bytes);
    }
  
    delete obj.timeStamp;
    delete obj.timeString;
    delete obj.msgType;
  
    return { data: obj };
  }
  
  
  1. At least you can show a screenshot of the codec error in Chirpstack?

  2. The codec seems to be for TTNv2.
    Are you using ChirpStack v3 or V4?

If ChirpStack v4 then put this in front of your Codec.
function decodeUplink(input) {
return {
data: Decode(input.bytes, input.fPort)
};
}

We are using Chirpstack V4.7, so the expanded error message are not availble if I understund correctly. Will update to V8.1 to.

Ok, will make changes and test again.