Copyright Micropelt

Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 6 Next »

Uplink Decoder:

// Decode uplink function.
//
// Input is an object with the following fields:
// - bytes = Byte array containing the uplink payload, e.g. [255, 230, 255, 0]
// - fPort = Uplink fPort.
// - variables = Object containing the configured device variables.
//
// Output must be an object with the following fields:
// - output = Object representing the decoded payload.
function decodeUplink(input) {
  return {
    data: Decode(input.fPort, input.bytes, input.variables)
  };
}

function get_user_mode(input) {
  var user_mode = input[9] & 0x7;
  switch (user_mode) {
    case 0:
      return "Valve_Position";
    case 1:
      return "RESERVED";
    case 2:
      return "SP_Ambient_Temperature";
    case 3:
      return "Detecting_Opening_Point";
    case 4:
      return "Slow_Harvesting";
    case 5:
      return "Temperature_Drop";
    case 6:
      return "Freeze_Protect";
    case 7:
      return "Forced_Heating";
    default:
      return "Unknown Operating Mode";
  }
}

function get_user_value(input) {
  var user_mode = get_user_mode(input);
  switch (user_mode) {
    case "Valve_Position":
    case "Freeze_Protect":
    case "Forced_Heating":
      return input[10];
    case "SP_Ambient_Temperature":
      return input[10] * 0.5;
    case "Detecting_Opening_Point":
    case "Slow_Harvesting":
      return input[10] * 0.25;
    default:
      return "Invalid User Mode";
  }
}

function decode_port_1(input) {
  var output = {
    Current_Valve_Position: input[0],
    Flow_Sensor_Raw: input[1] * 0.5,
    Flow_Temperature: input[2] * 0.5,
    Ambient_Sensor_Raw: input[3] * 0.25,
    Ambient_Temperature: input[4] * 0.25,
    Temperature_Drop_Detection: input[5] >> 7 & 0x01,
    Energy_Storage: input[5] >> 6 & 0x01,
    Harvesting_Active: input[5] >> 5 & 0x01,
    Ambient_Sensor_Failure: input[5] >> 4 & 0x01,
    Flow_Sensor_Failure: input[5] >> 3 & 0x01,
    Radio_Communication_Error: input[5] >> 2 & 0x01,
    Received_Signal_Strength: input[5] >> 1 & 0x01,
    Motor_Error: input[5] >> 0 & 0x01,
    Storage_Voltage: Number((input[6] * 0.02).toFixed(2)),
    Average_Current_Consumed: input[7] * 10,
    Average_Current_Generated: input[8] * 10,
    Operating_Condition: input[9] >> 7 & 0x01,
    Storage_Fully_Charged: input[9] >> 6 & 0x01,
    Zero_Error: input[9] >> 5 & 0x01,
    Calibration_OK: input[9] >> 4 & 0x01,
  }
  output.User_Mode = get_user_mode(input);
  output.User_Value = get_user_value(input);

  if (input.length == 12) {
    utmp = input[11] * 0.25;
    output.Used_Temperature = utmp;
  }
  return output;
}


function Decode(fPort, bytes) {
  var output = {};
  switch (fPort) {
    case 1:
      output = decode_port_1(bytes);
      break;
    default:
      return {
        errors: ['unknown FPort'],
      };
  }
  return output;
}

Downlink Encoder:

// Encode downlink function.
//
// Input is an object with the following fields:
// - output = Object representing the payload that must be encoded.
// - variables = Object containing the configured device variables.
//
// Output must be an object with the following fields:
// - bytes = Byte array containing the downlink payload.
function encodeDownlink(input) {
  let mode = input.data.userMode; // "Ambient_Temperature" or "Valve_Position"
  let safetyMode = input.data.safetyMode; // "Ambient_Temperature" or "Valve_Position"
  let setValue = input.data.setValue; // 0-40 for Ambient_Temperature, 0-100 for Valve_Position
  let roomTemperature = input.data.roomTemperature; // 0-40
  let safetyValue = input.data.safetyValue; // 0-40 for Ambient_Temperature, 0-100 for Valve_Position
  let radioInterval = input.data.radioInterval; // 5, 10, 60, 120, 480
  let doReferenceRunNow = input.data.doReferenceRunNow; // 0 or 1

  let bytes = [0, 0, 0, 0, 0, 0];

  // Byte 1: Set value
  if (mode === "Ambient_Temperature") {
      if (setValue < 0 || setValue > 40) {
        throw new Error("Set value out of range for ambient mode");
      }
      else {
        bytes[0] = setValue *2;
      }         
  } else if (mode === "Valve_Position") {
      if (setValue < 0 || setValue > 100) {
        throw new Error("Set value out of range for valve mode");
      } 
      else {
        bytes[0] = setValue;
      }
  } else {
      throw new Error("Invalid user mode");
  }

  // Byte 2: Room temperature (0-40)
  if (roomTemperature < 0 || roomTemperature > 40) throw new Error("Room temperature out of range");
  bytes[1] = roomTemperature * 4;

  // Byte 3: Safety value
  if (safetyMode === "Ambient_Temperature") {
      if (safetyValue < 0 || safetyValue > 40) {
        throw new Error("Safety value out of range for ambient mode");
      } 
      else {
        bytes[2] = safetyValue * 2;
      }
  } else if (safetyMode === "Valve_Position") {
      if (safetyValue < 0 || safetyValue > 100) {
        throw new Error("Safety value out of range for valve mode");
      } 
      else {
        bytes[2] = safetyValue;
      }
  } else {
      throw new Error("Invalid safety mode");
  }

  // Byte 4: Radio interval, user mode, safety mode
  let radioBits;
  switch (radioInterval) {
      case 5:
          radioBits = 1 << 4; // Radio interval 5 minutes
          break;
      case 10:
          radioBits = 0 << 4; // Radio interval 10 minutes
          break;
      case 60:
          radioBits = 2 << 4; // Radio interval 60 minutes
          break;
      case 120:
          radioBits = 3 << 4; // Radio interval 120 minutes
          break;
      case 480:
          radioBits = 4 << 4; // Radio interval 480 minutes
          break;
      default:
          throw new Error("Invalid radio interval");
  }

  let userModeBits;
  if (mode === "Ambient_Temperature") {
      userModeBits = 2 << 2; // User mode "Ambient_Temperature" in bits 3 and 4
  } else {
      userModeBits = 0 << 2; // User mode "Valve_Position" in bits 3 and 4
  }

  let safetyModeBits;
  if (safetyMode === "Ambient_Temperature") {
      safetyModeBits = 0 << 0; // Safety mode "Ambient_Temperature" in bits 1 and 2
  } else {
      safetyModeBits = 2 << 0; // Safety mode "Valve_Position" in bits 1 and 2
  }

  bytes[3] = radioBits | userModeBits | safetyModeBits;

  // Byte 5: Reserved (set to 0)
  bytes[4] = 0;

  // Byte 6: doReferenceRunNow bit (bit 8)
  if (doReferenceRunNow < 0 || doReferenceRunNow > 1) throw new Error("Invalid doReferenceRunNow value");
  bytes[5] = doReferenceRunNow << 7;

  return {
      "bytes": bytes
  };
}

Downlink Encoder Example:

// Example 1
{
      "userMode": "Valve_Position",
      "setValue": 42,
      "roomTemperature": 0,
      "safetyMode": "Ambient_Temperature",
      "safetyValue": 19,
      "radioInterval": 5, 
      "doReferenceRunNow": 1
}

// Example 2
{
      "userMode": "Ambient_Temperature",
      "setValue": 21.5,
      "roomTemperature": 24,
      "safetyMode": "Valve_Position",
      "safetyValue": 0,
      "radioInterval": 60, 
      "doReferenceRunNow": 0
}

// Example 3
{
      "userMode": "Valve_Position",
      "setValue": 36,
      "roomTemperature": 0,
      "safetyMode": "Valve_Position",
      "safetyValue": 21,
      "radioInterval": 5, 
      "doReferenceRunNow": 1 
}

// Example 4
{
      "userMode": "Ambient_Temperature",
      "setValue": 20,
      "roomTemperature": 0,
      "safetyMode": "Ambient_Temperature",
      "safetyValue": 18,
      "radioInterval": 10, 
      "doReferenceRunNow": 0 
}
  • No labels