diff --git a/watlow-f4sd/ModbusWatlowRamp/Program.cs b/watlow-f4sd/ModbusWatlowRamp/Program.cs index 599f769..3e1dd0c 100644 --- a/watlow-f4sd/ModbusWatlowRamp/Program.cs +++ b/watlow-f4sd/ModbusWatlowRamp/Program.cs @@ -1,206 +1,206 @@ -using Modbus.Device; // NModbus namespace -using System.IO.Ports; -using static System.Console; +using Modbus.Device; // NModbus namespace for Modbus communication +using System.IO.Ports; // Namespace for serial port communication +using static System.Console; // Static reference to Console for easier access internal class Program { - // Registers - private const ushort READINPUT1 = 100; - private const ushort INPUT1ERROR = 101; - private const ushort ALARM1STATUS = 102; - private const ushort ALARM2STATUS = 106; - private const ushort ANALOGINPUT1DECIMAL = 606; - private const ushort POWEROUTPUT1A = 103; - private const ushort SETPOINT1 = 300; - private const ushort CURRENTDAY = 1920; - private const ushort CURRENTMONTH = 1919; - private const ushort CURRENTYEAR = 1921; - private const ushort CURRENTHOUR = 1916; - private const ushort CURRENTMINUTE = 1917; - private const ushort CURRENTSECOND = 1918; - private const ushort OUTPUT1AFUNCTION = 700; - private const ushort OUTPUT1ACYCLETIMETYPE = 509; - private const ushort OUTPUT1ACYCLETIME = 506; - private const ushort DIGITALINPUT1FUNCTION = 1060; - private const ushort DIGITALINPUT1CONDITION = 1061; - private const ushort DIGITALINPUT1STATUS = 201; - private const ushort POWEROUTACTION = 1206; - private const ushort POWEROUTTIME = 1213; - private const ushort ANALOGINPUTUNITS = 608; - private const ushort CONTROLOUTPUTTYPE = 701; - private const ushort ANALOGINPUT1SENSORTYPE = 601; - private const ushort ANALOGINPUT1SENSOR = 600; - private const ushort ANALOGINPUT1SETPOINTHIGHLIMIT = 603; - private const ushort ANALOGINPUT1SETPOINTLOWLIMIT = 602; - private const ushort TEMPSCALEDISPLAY = 1923; - private const ushort TEMPSCALE = 901; - private const ushort ANALOGOUTPUT1ATYPE = 701; - private const ushort PROPORTIONALBAND = 500; + // Modbus Register Addresses for various parameters + private static class ModbusRegisters + { + public const ushort ReadInput1 = 100; // Register for reading input 1 + public const ushort Input1Error = 101; // Register for input 1 error status + public const ushort Alarm1Status = 102; // Register for alarm 1 status + public const ushort Alarm2Status = 106; // Register for alarm 2 status + public const ushort AnalogInput1Decimal = 606; // Register for analog input 1 decimal setting + public const ushort PowerOutput1A = 103; // Register for power output 1A + public const ushort SetPoint1 = 300; // Register for set point 1 + public const ushort CurrentDay = 1920; // Register for current day + public const ushort CurrentMonth = 1919; // Register for current month + public const ushort CurrentYear = 1921; // Register for current year + public const ushort CurrentHour = 1916; // Register for current hour + public const ushort CurrentMinute = 1917; // Register for current minute + public const ushort CurrentSecond = 1918; // Register for current second + public const ushort Output1AFunction = 700; // Register for output 1A function + public const ushort Output1ACycleTimeType = 509; // Register for output 1A cycle time type + public const ushort Output1ACycleTime = 506; // Register for output 1A cycle time + public const ushort DigitalInput1Function = 1060; // Register for digital input 1 function + public const ushort DigitalInput1Condition = 1061; // Register for digital input 1 condition + public const ushort DigitalInput1Status = 201; // Register for digital input 1 status + public const ushort PowerOutAction = 1206; // Register for power output action + public const ushort PowerOutTime = 1213; // Register for power output time + public const ushort AnalogInputUnits = 608; // Register for analog input units + public const ushort ControlOutputType = 701; // Register for control output type + public const ushort AnalogInput1SensorType = 601; // Register for analog input 1 sensor type + public const ushort AnalogInput1Sensor = 600; // Register for analog input 1 sensor + public const ushort AnalogInput1SetPointHighLimit = 603; // Register for high limit of analog input 1 set point + public const ushort AnalogInput1SetPointLowLimit = 602; // Register for low limit of analog input 1 set point + public const ushort TempScaleDisplay = 1923; // Register for temperature scale display + public const ushort TempScale = 901; // Register for temperature scale + public const ushort AnalogOutput1AType = 701; // Register for analog output 1A type + public const ushort ProportionalBand = 500; // Register for proportional band + } - // Configuration - private const byte slaveID = 1; + // Configuration for Modbus Slave ID + private const byte SlaveID = 1; private static void Main(string[] args) { - ushort[] registers; - - // Initialize the F4 - SerialPort serialPort = (SerialPort)InitializeModbusSerialPort(); - ModbusSerialMaster master = (ModbusSerialMaster)InitializeModbusMaster(serialPort); - - // Then, run it try { - // Monitor continuously - while (true) - { - // Check for escape key press - if (KeyAvailable) - { - ConsoleKeyInfo key = ReadKey(true); - if (key.Key == ConsoleKey.Escape) - { - WriteLine(" pressed. Exiting..."); - break; - } - } + using SerialPort serialPort = InitializeSerialPort(); + using ModbusSerialMaster master = InitializeModbusMaster(serialPort); - // Display current process values - try - { - registers = master.ReadInputRegisters(slaveID, INPUT1ERROR, 1); - if (registers[0] != 0) - { - WriteLine("Failure in the temperature input channel; program ends here...\n"); - return; - }; - registers = master.ReadInputRegisters(slaveID, READINPUT1, 1); - Write("temperature is {0:F1}; ", registers[0] / 10.0); - registers = master.ReadInputRegisters(slaveID, POWEROUTPUT1A, 1); - Write("power output is {0:F2}%\n", registers[0] / 100.0); - // Pause before next read - Thread.Sleep(750); - } - catch (Exception ex) - { - WriteLine($"Error: {ex.Message}"); - break; - } - } + MonitorProcessValues(master); } catch (Exception ex) { WriteLine($"Initialization error: {ex.Message}"); } - finally + } + + private static void MonitorProcessValues(ModbusSerialMaster master) + { + while (true) { - // Shut off the output - master?.WriteSingleRegister(slaveID, SETPOINT1, 0); - - // Clean up - master?.Dispose(); - serialPort?.Close(); - } - - static object InitializeModbusSerialPort() - { - SerialPort serialPort; - - serialPort = new SerialPort + if (KeyAvailable && ReadKey(true).Key == ConsoleKey.Escape) { - PortName = "COM1", - BaudRate = 19200, - DataBits = 8, - Parity = Parity.None, - StopBits = StopBits.One, - ReadTimeout = 1000 - }; - serialPort.Open(); - return serialPort; - } + WriteLine(" pressed. Exiting..."); + break; + } - static object InitializeModbusMaster(SerialPort serialPort) - { - ushort[] registers; - ushort setPoint = 1000; - ModbusSerialMaster master; - DateTime dateTime; + try + { + if (IsInputError(master)) + { + WriteLine("Failure in the temperature input channel; program ends here...\n"); + return; // Exit if there's an error + } - master = ModbusSerialMaster.CreateRtu(serialPort); - - // Set sensor type - WriteLine("Set sensor type to 100 Ω DIN platinum RTD"); - master.WriteSingleRegister(slaveID, ANALOGINPUT1SENSOR, 1); - master.WriteSingleRegister(slaveID, ANALOGINPUT1SENSORTYPE, 11); - - // Set to one decimal place (this cannot be the first initialization instruction) - WriteLine("Set to one decimal place on display"); - master.WriteSingleRegister(slaveID, ANALOGINPUT1DECIMAL, 1); - - // Display current setpoint high and low limits - registers = master.ReadInputRegisters(slaveID, ANALOGINPUT1SETPOINTLOWLIMIT, 1); - WriteLine("Setpoint low limit is {0}", registers[0]); - registers = master.ReadInputRegisters(slaveID, ANALOGINPUT1SETPOINTHIGHLIMIT, 1); - WriteLine("Setpoint high limit is {0}", registers[0]); - - // Set to proportional mode - WriteLine("Make proportional band 5°F"); - master.WriteSingleRegister(slaveID, PROPORTIONALBAND, 5); - - // Set output function to heating - WriteLine("Output function is heating"); - master.WriteSingleRegister(slaveID, OUTPUT1AFUNCTION, 1); - - // Set analog output1 to 4-20 ma - WriteLine("Set the analog output to 4-20 MA"); - master.WriteSingleRegister(slaveID, ANALOGOUTPUT1ATYPE, 0); - - // Set analog input parameter to temperature - master.WriteSingleRegister(slaveID, ANALOGINPUTUNITS, 0); - - // Set temperature SCALE to ON - master.WriteSingleRegister(slaveID, TEMPSCALEDISPLAY, 1); - - // Set temperature scale type to Fahrenheit - master.WriteSingleRegister(slaveID, TEMPSCALE, 0); - - // Set power failure response - master.WriteSingleRegister(slaveID, POWEROUTACTION, 2); - - // Set output1 cycle time type to variable burst - master.WriteSingleRegister(slaveID, OUTPUT1ACYCLETIMETYPE, 0); - - // Read current output1 cycle time - registers = master.ReadInputRegisters(slaveID, OUTPUT1ACYCLETIME, 1); - WriteLine("Output cycle time is {0}", registers[0]); - - // Set output1 cycle time to new value - master.WriteSingleRegister(slaveID, OUTPUT1ACYCLETIME, 500); - - // Check new value - registers = master.ReadInputRegisters(slaveID, OUTPUT1ACYCLETIME, 1); - WriteLine("Output cycle time is now {0}", registers[0]); - - // Write setpoint to F4 - WriteLine("Setting the process variable setpoint"); - master.WriteSingleRegister(slaveID, SETPOINT1, setPoint); - - // Verify setpoint value - WriteLine("Verifying the setpoint value"); - registers = master.ReadInputRegisters(slaveID, SETPOINT1, 1); - WriteLine("Setpoint is {0:F1}", registers[0] / 10.0); - - // Set F4 clock to current date and time - dateTime = DateTime.Now; - master.WriteSingleRegister(slaveID, CURRENTDAY, (ushort)dateTime.Day); - master.WriteSingleRegister(slaveID, CURRENTMONTH, (ushort)dateTime.Month); - master.WriteSingleRegister(slaveID, CURRENTYEAR, (ushort)dateTime.Year); - master.WriteSingleRegister(slaveID, CURRENTHOUR, (ushort)dateTime.Hour); - master.WriteSingleRegister(slaveID, CURRENTMINUTE, (ushort)dateTime.Minute); - master.WriteSingleRegister(slaveID, CURRENTSECOND, (ushort)dateTime.Second); - WriteLine("Set F4 clock to {0} {1}", dateTime.ToLongTimeString(), dateTime.ToLongDateString()); - - return master; + DisplayCurrentValues(master); + Thread.Sleep(750); // Wait before the next read + } + catch (Exception ex) + { + WriteLine($"Error: {ex.Message}"); + break; // Exit on error + } } } + + private static bool IsInputError(ModbusSerialMaster master) + { + ushort[] registers = master.ReadInputRegisters(SlaveID, ModbusRegisters.Input1Error, 1); + return registers[0] != 0; // Non-zero indicates an error + } + + private static void DisplayCurrentValues(ModbusSerialMaster master) + { + ushort[] temperatureRegisters = master.ReadInputRegisters(SlaveID, ModbusRegisters.ReadInput1, 1); + Write("Temperature is {0:F1}; ", temperatureRegisters[0] / 10.0); // Convert to proper format + + ushort[] powerOutputRegisters = master.ReadInputRegisters(SlaveID, ModbusRegisters.PowerOutput1A, 1); + WriteLine("Power output is {0:F2}%", powerOutputRegisters[0] / 100.0); // Convert to percentage + } + + private static SerialPort InitializeSerialPort() + { + SerialPort serialPort = new SerialPort + { + PortName = "COM1", // Set the COM port + BaudRate = 19200, // Set the baud rate + DataBits = 8, // Set data bits + Parity = Parity.None, // Set parity + StopBits = StopBits.One, // Set stop bits + ReadTimeout = 1000 // Set read timeout + }; + serialPort.Open(); // Open the serial port + return serialPort; // Return the configured serial port + } + + private static ModbusSerialMaster InitializeModbusMaster(SerialPort serialPort) + { + ModbusSerialMaster master = ModbusSerialMaster.CreateRtu(serialPort); + ConfigureModbusSettings(master); // Configure Modbus settings + return master; // Return the Modbus master + } + + private static void ConfigureModbusSettings(ModbusSerialMaster master) + { + WriteLine("Configuring Modbus settings..."); + + // Set sensor type and configuration + master.WriteSingleRegister(SlaveID, ModbusRegisters.AnalogInput1Sensor, 1); + master.WriteSingleRegister(SlaveID, ModbusRegisters.AnalogInput1SensorType, 11); + master.WriteSingleRegister(SlaveID, ModbusRegisters.AnalogInput1Decimal, 1); + + // Set limits and modes for the Modbus device + SetSetPointLimits(master); + SetOutputFunction(master); + SetAnalogOutput(master); + SetTemperatureScale(master); + SetPowerFailureResponse(master); + SetOutputCycleTime(master); + SetCurrentDateTime(master); + } + + private static void SetSetPointLimits(ModbusSerialMaster master) + { + ushort[] lowLimitRegisters = master.ReadInputRegisters(SlaveID, ModbusRegisters.AnalogInput1SetPointLowLimit, 1); + WriteLine("Setpoint low limit is {0}", lowLimitRegisters[0]); + + ushort[] highLimitRegisters = master.ReadInputRegisters(SlaveID, ModbusRegisters.AnalogInput1SetPointHighLimit, 1); + WriteLine("Setpoint high limit is {0}", highLimitRegisters[0]); + + master.WriteSingleRegister(SlaveID, ModbusRegisters.ProportionalBand, 5); + master.WriteSingleRegister(SlaveID, ModbusRegisters.SetPoint1, 1000); + } + + private static void SetOutputFunction(ModbusSerialMaster master) + { + master.WriteSingleRegister(SlaveID, ModbusRegisters.Output1AFunction, 1); + } + + private static void SetAnalogOutput(ModbusSerialMaster master) + { + master.WriteSingleRegister(SlaveID, ModbusRegisters.AnalogOutput1AType, 0); + } + + private static void SetTemperatureScale(ModbusSerialMaster master) + { + master.WriteSingleRegister(SlaveID, ModbusRegisters.AnalogInputUnits, 0); + master.WriteSingleRegister(SlaveID, ModbusRegisters.TempScaleDisplay, 1); + master.WriteSingleRegister(SlaveID, ModbusRegisters.TempScale, 0); + } + + private static void SetPowerFailureResponse(ModbusSerialMaster master) + { + master.WriteSingleRegister(SlaveID, ModbusRegisters.PowerOutAction, 2); + } + + private static void SetOutputCycleTime(ModbusSerialMaster master) + { + master.WriteSingleRegister(SlaveID, ModbusRegisters.Output1ACycleTimeType, 0); + + ushort[] cycleTimeRegisters = master.ReadInputRegisters(SlaveID, ModbusRegisters.Output1ACycleTime, 1); + WriteLine("Output cycle time is {0}", cycleTimeRegisters[0]); // Display current cycle time + + master.WriteSingleRegister(SlaveID, ModbusRegisters.Output1ACycleTime, 500); // Set new cycle time + + cycleTimeRegisters = master.ReadInputRegisters(SlaveID, ModbusRegisters.Output1ACycleTime, 1); + WriteLine("Output cycle time is now {0}", cycleTimeRegisters[0]); // Confirm new cycle time + } + + private static void SetCurrentDateTime(ModbusSerialMaster master) + { + DateTime currentDateTime = DateTime.Now; + + master.WriteSingleRegister(SlaveID, ModbusRegisters.CurrentDay, (ushort)currentDateTime.Day); + master.WriteSingleRegister(SlaveID, ModbusRegisters.CurrentMonth, (ushort)currentDateTime.Month); + master.WriteSingleRegister(SlaveID, ModbusRegisters.CurrentYear, (ushort)currentDateTime.Year); + master.WriteSingleRegister(SlaveID, ModbusRegisters.CurrentHour, (ushort)currentDateTime.Hour); + master.WriteSingleRegister(SlaveID, ModbusRegisters.CurrentMinute, (ushort)currentDateTime.Minute); + master.WriteSingleRegister(SlaveID, ModbusRegisters.CurrentSecond, (ushort)currentDateTime.Second); + + WriteLine("Set F4 clock to {0} {1}", currentDateTime.ToLongTimeString(), currentDateTime.ToLongDateString()); // Display confirmation + } } \ No newline at end of file