Copyright Micropelt

Milesight MLR003 Payload Decoder/Encoder

Milesight Codec Uplink Decoder

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(bytes) { var output = {}; { output.DEV_EUI = LoRaObject.devEUI; output.RSSI = LoRaObject.rxInfo[0].rssi; output.SNR = LoRaObject.rxInfo[0].loRaSNR; output.Data = LoRaObject.data; output.ADR = LoRaObject.txInfo.adr; output.coderate = LoRaObject.txInfo.codeRate; output.FCnt = LoRaObject.fCnt; output.Port = LoRaObject.fPort; output.Frequency = LoRaObject.txInfo.frequency; output.Modulation = LoRaObject.txInfo.dataRate.modulation; output.Bandwidth = LoRaObject.txInfo.dataRate.bandwidth; output.SpreadingFactor = LoRaObject.txInfo.dataRate.spreadFactor; output.Current_Valve_Position = bytes[0]; output.Flow_Sensor_Raw = bytes[1] * 0.5; output.Flow_Temperature = bytes[2] * 0.5; output.Ambient_Sensor_Raw = bytes[3] * 0.25; output.Ambient_Temperature = bytes[4] * 0.25; output.Temperature_Drop_Detected = bytes[5] >> 7 & 0x01; output.Energy_Storage = bytes[5] >> 6 & 0x01; output.Harvesting_Active = bytes[5] >> 5 & 0x01; output.Ambient_Sensor_Failure = bytes[5] >> 4 & 0x01; output.Flow_Sensor_Failure = bytes[5] >> 3 & 0x01; output.Radio_Communication_Error = bytes[5] >> 2 & 0x01; output.Received_Signal_Strength = bytes[5] >> 1 & 0x01; output.Motor_Error = bytes[5] >> 0 & 0x01; output.Storage_Voltage = Number((bytes[6] * 0.02).toFixed(2)); output.Average_Current_Consumed = bytes[7] * 10; output.Average_Current_Generated = bytes[8] * 10; output.Operating_Condition= bytes[9] >> 7 & 0x01, output.Storage_Fully_Charged= bytes[9] >> 6 & 0x01, output.Zero_Error= bytes[9] >> 5 & 0x01, output.Calibration_OK= bytes[9] >> 4 & 0x01, output.User_Mode = get_user_mode(bytes); output.User_Value = get_user_value(bytes); if (bytes.length == 12) { utmp = bytes[11] * 0.25; output.Used_Temperature = utmp; } } return output; } function decode_port_2(bytes) { var output = {}; { var REV_Major = (bytes[0] & 0xF).toString(); var REV_Minor = ((bytes[0] >> 4) & 0xF).toString(16); output.REV = REV_Major + "." + REV_Minor; } { var HW_Major = (bytes[1] & 0xF).toString(); var HW_Minor = ((bytes[1] >> 4) & 0xF).toString(); output.HW = HW_Major + "." + HW_Minor; } { var FW_Year = bytes[2].toString(); var FW_Month = bytes[3].toString(); var FW_Day = bytes[4].toString(); var FW_Minor = bytes[5].toString(); output.FW = "20" + FW_Year + "." + FW_Month + "." + FW_Day + "." + FW_Minor; } return output; } function decode_port_3(bytes) { var output = {}; switch (bytes[0]) { case 0: output.motor_range = 2.56048; break; case 7: output.motor_range = 1.456; break; case 8: output.motor_range = 1.664; break; case 9: output.motor_range = 1.872; break; case 10: output.motor_range = 2.080; break; case 11: output.motor_range = 2.288; break; case 12: output.motor_range = 2.496; break; default: output.motor_range = 0; break; } return output; } function Decode(fPort, bytes) { var output = {}; switch (fPort) { case 1: output = decode_port_1(bytes); break; case 2: output = decode_port_2(bytes); break; case 3: output = decode_port_3(bytes); break; default: return { errors: ['unknown FPort'], }; } return output; }

Milesight Codec Downlink Encoder (CHANGE ALL DOWNLINK SETTINGS)

// Possible parameters: // userMode : "Ambient_Temperature" or "Valve_Position" // setValue : 0 to 40 (0.5° resolution) for "Ambient_Temperature" // setValue : 0 to 100 (1% resolution) for "Valve_Position" // safetyMode : "Ambient_Temperature" or "Valve_Position" // safetyValue : 0 to 40 (0.5° resolution) for "Ambient_Temperature" // safetyValue : 0 to 100 (1% resolution) for "Valve_Position" // roomTemperature : 0 (Internal Ambient Estimate) or Temperature from External Sensor (0.25° resolution) // radioInterval : 5 or 10 or 60 or 120 or 480 (minutes) // doReferenceRunNow : 0 or 1 (0-False; 1-True) /* IMPORTANT: All of the parameters in obj is NOT required for the encoder to work Eg. Only the "roomTemperature" parameter can be added and For the remaining parameters, the defaults will be applied */ var DEFAULT_USER_MODE_BITS = 2 << 2; // "Ambient Temperature" var DEFAULT_SET_VALUE = 38; // 19° Ambient Temperature" var DEFAULT_SAFETY_MODE_BITS = 0 << 0; // "Ambient Temperature" var DEFAULT_SAFETY_MODE_VALUE = 38; // 19° Ambient Temperature" var DEFAULT_RADIO_INTERVAL_BITS = 0 << 4; // 10 minutes var DEFAULT_ROOM_TEMPERATURE_VALUE = 0; // Use Internal Ambient Estimate var DEFAULT_DO_REFERENCE_RUN_NOW_BIT = 0; // 0- No Reference Run function Encode(fPort, obj) { var mode = obj.userMode; // "Ambient_Temperature" or "Valve_Position" var safetyMode = obj.safetyMode; // "Ambient_Temperature" or "Valve_Position" var setValue = obj.setValue; // 0-40 for Ambient_Temperature, 0-100 for Valve_Position var roomTemperature = obj.roomTemperature; // 0-40 var safetyValue = obj.safetyValue; // 0-40 for Ambient_Temperature, 0-100 for Valve_Position var radioInterval = obj.radioInterval; // 5, 10, 60, 120, 480 var doReferenceRunNow = obj.doReferenceRunNow; // 0 or 1 var 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 { bytes[0] = DEFAULT_SET_VALUE; } // Byte 2: Room temperature (0-40) if (roomTemperature < 0 || roomTemperature > 40) { throw new Error("Room temperature out of range"); } else if (typeof roomTemperature === "undefined") { bytes[1] = DEFAULT_ROOM_TEMPERATURE_VALUE; } else { 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 { bytes[2] = DEFAULT_SAFETY_MODE_VALUE; } // Byte 4: Radio interval, user mode, safety mode var 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: radioBits = DEFAULT_RADIO_INTERVAL_BITS; } var userModeBits; if (mode === "Ambient_Temperature") { userModeBits = 2 << 2; // User mode "Ambient_Temperature" in bits 3 and 4 } else if (typeof mode === "undefined") { userModeBits = DEFAULT_USER_MODE_BITS; } else if (mode === "Valve_Position") { userModeBits = 0 << 2; // User mode "Valve_Position" in bits 3 and 4 } var safetyModeBits; if (safetyMode === "Ambient_Temperature") { safetyModeBits = 0 << 0; // Safety mode "Ambient_Temperature" in bits 1 and 2 } else if (typeof safetyMode === "undefined") { safetyModeBits = DEFAULT_SAFETY_MODE_BITS; } else if (safetyMode === "Valve_Position") { 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"); } else if (typeof doReferenceRunNow === "undefined") { bytes[5] = DEFAULT_DO_REFERENCE_RUN_NOW_BIT; } else { bytes[5] = doReferenceRunNow << 7; } return bytes; }

Downlink 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.5, "roomTemperature": 23.75, "safetyMode": "Ambient_Temperature", "safetyValue": 18, "radioInterval": 10, "doReferenceRunNow": 0 }

Milesight Codec Downlink Encoder (CHANGE ONLY SET-POINT AMBIENT TEMPERATURE OR VALVE POSITION: SUITABLE for BACNET) (Other settings are set to default)

function Encode(fPort, obj) { var encoded_payload = []; if("Valve_Position" in obj) { var new_vp = obj.Valve_Position; if (new_vp < 0 || new_vp > 100) { throw new Error("Set value out of range for valve mode"); } else { encoded_payload = [new_vp,0x00,0x26,0x00,0x00,0x00]; } } if ("Set_Point_Ambient_Temperature" in obj) { var new_spt = (obj.Set_Point_Ambient_Temperature)*2; if (new_spt < 0 || new_spt > 80) { throw new Error("Set value out of range for Set point ambient"); } else { encoded_payload = [new_spt,0x00,0x28,0x08,0x00,0x00]; } } return encoded_payload; }

Downlink example

{ "Set_Point_Ambient_Temperature":23 } { "Valve_Position":42 }