From ce71ec9267ad2fe20f4861904868ab2c7f2241a8 Mon Sep 17 00:00:00 2001 From: RobertJDavis Date: Tue, 21 Oct 2025 16:09:00 -0700 Subject: [PATCH] added York YVAA RTU --- platformio.ini | 11 +- src/BMS/CHILLER/CH_York_YVAA_RTU/README.md | 43 +++++ .../CHILLER/CH_York_YVAA_RTU/StateUtils.cpp | 85 +++++++++ src/BMS/CHILLER/CH_York_YVAA_RTU/StateUtils.h | 43 +++++ .../CHILLER/CH_York_YVAA_RTU/State_Fail.cpp | 107 +++++++++++ .../CH_York_YVAA_RTU/State_Running.cpp | 171 ++++++++++++++++++ .../CH_York_YVAA_RTU/State_Standby.cpp | 136 ++++++++++++++ src/BMS/CHILLER/CH_York_YVAA_RTU/config.h | 119 ++++++++++++ src/BMS/CHILLER/CH_York_YVAA_RTU/main.cpp | 78 ++++++++ 9 files changed, 790 insertions(+), 3 deletions(-) create mode 100644 src/BMS/CHILLER/CH_York_YVAA_RTU/README.md create mode 100644 src/BMS/CHILLER/CH_York_YVAA_RTU/StateUtils.cpp create mode 100644 src/BMS/CHILLER/CH_York_YVAA_RTU/StateUtils.h create mode 100644 src/BMS/CHILLER/CH_York_YVAA_RTU/State_Fail.cpp create mode 100644 src/BMS/CHILLER/CH_York_YVAA_RTU/State_Running.cpp create mode 100644 src/BMS/CHILLER/CH_York_YVAA_RTU/State_Standby.cpp create mode 100644 src/BMS/CHILLER/CH_York_YVAA_RTU/config.h create mode 100644 src/BMS/CHILLER/CH_York_YVAA_RTU/main.cpp diff --git a/platformio.ini b/platformio.ini index 651346d..144a2c8 100644 --- a/platformio.ini +++ b/platformio.ini @@ -11,10 +11,10 @@ [platformio] -default_envs = PDU_Maverick_Power_TCP ; Select here the name of the configuration you want to download +default_envs = CH_York_YVAA_RTU ; Select here the name of the configuration you want to download [env] -upload_port = COM15 +upload_port = COM9 [common_env_options] framework = arduino @@ -132,7 +132,6 @@ extends = common_env_options build_flags = -D USE_MODBUS_IP build_src_filter = -<*> + - [env:Susol_Smart_MCCB_TCP] platform = espressif32 board = dfrobot_firebeetle2_esp32e @@ -195,3 +194,9 @@ board = dfrobot_firebeetle2_esp32e extends = common_env_options build_flags = -D USE_MODBUS_IP build_src_filter = -<*> + + +[env:CH_York_YVAA_RTU] +platform = espressif32 +board = dfrobot_firebeetle2_esp32e +extends = common_env_options +build_src_filter = -<*> + \ No newline at end of file diff --git a/src/BMS/CHILLER/CH_York_YVAA_RTU/README.md b/src/BMS/CHILLER/CH_York_YVAA_RTU/README.md new file mode 100644 index 0000000..0d95a1b --- /dev/null +++ b/src/BMS/CHILLER/CH_York_YVAA_RTU/README.md @@ -0,0 +1,43 @@ +# CHILLER YORK YVAA 0428IOK46BAVTXX TCP + +## Brief Introduction + +*** NOTE! *** +This code has not been verified with Chiller and Chiller Manager PLC program. +It is a best-guess based on a preliminary review of Chiller PLC program, but +has yet to be fully vetted and local tested with PLC programs. + +Chiller receives Temp SP and Enable from PLC (Modscan) +Alarms are also simulated via Modscan, though those signals will be internal to Chiller +Many hard IO points are simulated using Modscan. +Assumes all Modbus points are for monitoring only and go to Ignition - not sent to PLC + +## List of Equipment +This cofiguration has been used for these models: +* **YVAA**: 10-14-25 + +## Hardware Prerequisites + +The code is written for an ESP8266/ESP32-style microcontroller with WiFi capabilities. +* **Microcontroller**: [Firebeetle 2 ESP32.](https://www.dfrobot.com/product-2231.html) + +--- + +## States and Strategies +Updates Alarms States. If any active alarms --> FailState +Updates Free Cooling Mode: Free Cooling Mode is activated using a coil, for simulation purposes only. +Modbus points are simulated, mostly with a SingleValue strategy for image verification in Ignition. +While in RunningState, the Supply Temp dynamically ramps to the Supply Temp SP sent from PLC (Modscan) +The CHW In and CHW Out temperature values also dynamically ramp to match the Return and Supply Temps. + +### Standby State +* **Chiller Status**: set to 0 +* **Operational Code**: set to 77 +* **Chiller Start Command**: set to 0 + +### Running State +* **Chiller status**: set to 1 +* **Supply Temperature**: **Ramp Strategy** ramps to Temp Setpoint from PLC (Modscan) + +### Fail State +* **Chiller Status**: set to 0 \ No newline at end of file diff --git a/src/BMS/CHILLER/CH_York_YVAA_RTU/StateUtils.cpp b/src/BMS/CHILLER/CH_York_YVAA_RTU/StateUtils.cpp new file mode 100644 index 0000000..696d09a --- /dev/null +++ b/src/BMS/CHILLER/CH_York_YVAA_RTU/StateUtils.cpp @@ -0,0 +1,85 @@ +/** + * @file StateUtils.cpp + * @brief Implementation of the StateUtils class. + * @author Robert J. Davis + * @date 2025-10-14 + * + * This file contains implementation of utility functions that are used in multiple States. + */ +#include "Strategies/Strategy_Ramp.h" +#include "Strategies/Strategy_Random.h" +#include "Strategies/Strategy_Saw.h" +#include "Strategies/Strategy_SingleValue.h" +#include "Strategies/Strategy_Square.h" +#include "Strategies/Strategy_PID.h" +#include "ModbusPoints/Modbus_Point.h" +#include "ModbusPoints/Modbus_FloatDecorator.h" +#include "Equipment/Equipment.h" +#include "States/State_Standby.h" +#include "States/State_Running.h" +#include "States/State_Fail.h" +#include "States/State.h" +#include "StateUtils.h" +#include +#include +#if defined(USE_MODBUS_IP) + #include +#else + #include +#endif + +/** + * @brief Updates Alarms states + * + * This function will update the Alarm status DI bits according to the Alarm Commands from Coils (Modscan) + * The appropriate Fault Code will also be set to 56 (Condenser Fan VSD Warning) + * + * This is a function used in the update() of the Standby, Running, and Fail States. + * +*/ + +void updateAlarms(Equipment* equipment){ + Modbus_Point* Sys1FanAlarmCommand = equipment->getModbus_Point("Sys 1 Fan Fault ON"); + Modbus_Point* Sys2FanAlarmCommand = equipment->getModbus_Point("Sys 2 Fan Fault ON"); + Modbus_Point* Sys1FanAlarm = equipment->getModbus_Point("Sys 1 Fan Fault Alarm"); + Modbus_Point* Sys2FanAlarm = equipment->getModbus_Point("Sys 2 Fan Fault Alarm"); + if (Sys1FanAlarmCommand) { + Sys1FanAlarm->setValue(Sys1FanAlarmCommand->getValue()); + if (Sys1FanAlarmCommand->getValue() == 1){ + equipment->setModbus_Point("Sys 1 Fault Code", 56); + } + else equipment->setModbus_Point("Sys 1 Fault Code", 0); + } + if (Sys2FanAlarmCommand) { + Sys2FanAlarm->setValue(Sys2FanAlarmCommand->getValue()); + if (Sys2FanAlarmCommand->getValue() == 1){ + equipment->setModbus_Point("Sys 2 Fault Code", 56); + } + else equipment->setModbus_Point("Sys 2 Fault Code", 0); + } +} + +/** + * @brief Updates Free Cooling Mode + * + * This function will update the Free Cooling Mode and Valve based on Free Cooling Command + * received from Modscan. This is for simulation purposes only - in practice, the Chiller + * will transition to Free Cooling Mode based on its own internal logic. + * + * For ease of testing, this is a function used in the update() of the Standby, Running, and Fail States. + * +*/ + +void updateFreeCooling(Equipment* equipment){ + Modbus_Point* FreeCoolingCommand = equipment->getModbus_Point("Free Cooling Mode ON"); + Modbus_Point* FreeCoolingMode = equipment->getModbus_Point("Free Cooling Mode"); + Modbus_Point* FreeCoolingValve = equipment->getModbus_Point("Free Cooling Valve"); + if (FreeCoolingCommand->getValue() == 1) { + FreeCoolingMode->setValue(1); + FreeCoolingValve->setValue(1); + } + else { + FreeCoolingMode->setValue(0); + FreeCoolingValve->setValue(0); + } +} \ No newline at end of file diff --git a/src/BMS/CHILLER/CH_York_YVAA_RTU/StateUtils.h b/src/BMS/CHILLER/CH_York_YVAA_RTU/StateUtils.h new file mode 100644 index 0000000..92438ec --- /dev/null +++ b/src/BMS/CHILLER/CH_York_YVAA_RTU/StateUtils.h @@ -0,0 +1,43 @@ +/** + * @file config.h + * @brief StateUtils class + * @author Robert J Davis + * @date 2025-10-06 + * + * Defines the StateUtils class, which contains utility functions used in multiple States. + */ + +#pragma once + +#include "ModbusPoints/Modbus_Point.h" +#include "ModbusPoints/Modbus_FloatDecorator.h" +#include "Equipment/Equipment.h" +#include "States/State_Standby.h" +#include "States/State_Running.h" +#include "States/State_Fail.h" +#include "States/State.h" +#include +#include + +#if defined(USE_MODBUS_IP) + #include +#else + #include +#endif + +template +class State; + +/** + * @brief Updates Alarms states + * @param equipment Pointer to the Equipment instance. + * @return void + */ +void updateAlarms(Equipment* equipment); + +/** + * @brief Updates Free Cooling Mode + * @param equipment Pointer to the Equipment instance. + * @return void + */ +void updateFreeCooling(Equipment* equipment); \ No newline at end of file diff --git a/src/BMS/CHILLER/CH_York_YVAA_RTU/State_Fail.cpp b/src/BMS/CHILLER/CH_York_YVAA_RTU/State_Fail.cpp new file mode 100644 index 0000000..0991bb1 --- /dev/null +++ b/src/BMS/CHILLER/CH_York_YVAA_RTU/State_Fail.cpp @@ -0,0 +1,107 @@ +/** + * @file State_Fail.cpp + * @brief Implementation of the FailState class. + * @author Robert J. Davis + * @date 2025-10-14 + * + * This file contains the implementation for the FailState, which defines + * the behavior of the equipment when it has entered a fault condition. + */ +#include "ModbusPoints/Modbus_Point.h" +#include "Equipment/Equipment.h" +#include "Strategies/Strategy_Ramp.h" +#include "Strategies/Strategy_SingleValue.h" +#include "Strategies/Strategy_PID.h" +#include "States/State_Standby.h" +#include "States/State_Running.h" +#include "States/State_Fail.h" +#include "StateUtils.h" +#include +#include + +#if defined(USE_MODBUS_IP) + #include +#else + #include +#endif + +/** + * @brief Constructs a new FailState object. + * + * This constructor receives a list of alarm descriptions and creates strategies + * to set the Compressor and Fan kW to 0. + * + * @param activeAlarms A vector of strings, where each string is the + * description of a Modbus point to be set as an active alarm. + */ +template<> +FailState::FailState(const std::vector& activeAlarms) { + addStrategy("Sys 1 Compressor Pct FLA", new SingleValueStrategy(0.0f, 0.0f, 1000)); + addStrategy("Sys 2 Compressor Pct FLA", new SingleValueStrategy(0.0f, 0.0f, 1000)); + addStrategy("Sys 1 Fan KW", new SingleValueStrategy(0.0f, 0.0f, 1000)); + addStrategy("Sys 2 Fan KW", new SingleValueStrategy(0.0f, 0.0f, 1000)); + addStrategy("Sys 1 Compressor KW", new SingleValueStrategy(0.0f, 0.0f, 1000)); + addStrategy("Sys 2 Compressor KW", new SingleValueStrategy(0.0f, 0.0f, 1000)); +} + +/** + * @brief Executes the fail state's logic for one update cycle. + * + * This method first updates all alarms states and Free Cooling Mode (for ease of testing). + * If all alarms have been cleared --> StandbyState. + * If alarms are still active, ensures the Chiller Start Command remains at 0. + * + * @param equipment Pointer to the Equipment instance. + * @return A pointer to a new State if a transition should occur, otherwise nullptr. + */ +template<> +State* FailState::update(Equipment* equipment) { + // Update alarms states, Free Cooling mode, Freeze Protection Mode + updateAlarms(equipment); + updateFreeCooling(equipment); + + const std::vector alarmDescriptions = { + "Sys 1 Alarm", "Sys 2 Alarm", "Sys 1 Fan Fault Alarm", "Sys 2 Fan Fault Alarm", + }; + + // If no alarms active --> send to StandbyState() + bool alarms_active = false; + for (const auto& desc : alarmDescriptions) { + Modbus_Point* point = equipment->getModbus_Point(desc); + if (point->getValue() == 1) { + alarms_active = true; + } + } + if (!alarms_active) return new StandbyState(); + + setPointValue(equipment, "Chiller Start Command", 0); + + _applyStrategies(equipment); + return nullptr; +} + +/** + * @brief Logic to execute once when entering the fail state. + * Sets the Chiller Status to off, and updates the Operational Code for Systems to 77 (Not Running) + * The Chiller Start Command is also set to 0. + * @param equipment Pointer to the Equipment instance. + */ +template<> +void FailState::enterState(Equipment* equipment) { + // Logic to run when the equipment enters this state + Serial.println("Enter Fail State..."); + setPointValue(equipment, "Chiller Start Command", 0); + setPointValue(equipment, "Chiller Status", 0); + setPointValue(equipment, "Sys 1 Operational Code", 77); + setPointValue(equipment, "Sys 2 Operational Code", 77); +} + +/** + * @brief Logic to execute once when exiting the fail state. + * @param equipment Pointer to the Equipment instance. + */ +template<> +void FailState::exitState(Equipment* equipment) { + // Cleanup logic to run when the equipment leaves this state + Serial.println("Exit Fail State..."); +} \ No newline at end of file diff --git a/src/BMS/CHILLER/CH_York_YVAA_RTU/State_Running.cpp b/src/BMS/CHILLER/CH_York_YVAA_RTU/State_Running.cpp new file mode 100644 index 0000000..b891f34 --- /dev/null +++ b/src/BMS/CHILLER/CH_York_YVAA_RTU/State_Running.cpp @@ -0,0 +1,171 @@ +/** + * @file State_Running.cpp + * @brief Implementation of the RunningState class. + * @author Robert J Davis + * @date 2025-10-12 + * + * This file contains the implementation for the RunningState, which defines + * the behavior of the equipment when it is actively running. + */ + +#include "ModbusPoints/Modbus_Point.h" +#include "ModbusPoints/Modbus_FloatDecorator.h" +#include "Equipment/Equipment.h" +#include "Strategies/Strategy_Ramp.h" +#include "Strategies/Strategy_Random.h" +#include "Strategies/Strategy_Saw.h" +#include "Strategies/Strategy_SingleValue.h" +#include "Strategies/Strategy_Square.h" +#include "Strategies/Strategy_PID.h" +#include "Strategies/Strategy_Totalizer.h" +#include "States/State_Standby.h" +#include "States/State_Running.h" +#include "States/State_Fail.h" +#include "States/State.h" +#include "StateUtils.h" +#include +#include + +#if defined(USE_MODBUS_IP) + #include +#else + #include +#endif + +/** + * @brief Constructs a new RunningState object. + * + * This constructor initializes behavior strategies active during the running + * state including various analog values, number of starts for each System and totalizers + * for the run-hours of each System. + */ +template<> +RunningState::RunningState() { + addStrategy("Supply Temp", new RampStrategy(67.0f, 1.0f, 1000)); + addStrategy("Return Temp", new SawStrategy(79.0f, 83.0f, 1.0f, 1000)); + addStrategy("Ambient Temp", new SingleValueStrategy(100.0f, 1.0f, 1000)); + + addStrategy("System CHW Out", new RampStrategy(67.0f, 1.0f, 1000)); + addStrategy("System CHW In", new RampStrategy(81.0f, 1.0f, 1000)); + addStrategy("Sys 1 Condenser Temp", new SingleValueStrategy(125.0f, 5.0f, 1000)); + addStrategy("Sys 2 Condenser Temp", new SingleValueStrategy(125.0f, 5.0f, 1000)); + addStrategy("Sys 1 Oil Pressure", new SingleValueStrategy(450.0f, 5.0f, 1000)); + addStrategy("Sys 2 Oil Pressure", new SingleValueStrategy(450.0f, 5.0f, 1000)); + addStrategy("Sys 1 Suction Pressure", new SingleValueStrategy(70.0f, 2.0f, 1000)); + addStrategy("Sys 2 Suction Pressure", new SingleValueStrategy(70.0f, 2.0f, 1000)); + addStrategy("Sys 1 Discharge Pressure", new SingleValueStrategy(375.0f, 4.0f, 1000)); + addStrategy("Sys 2 Discharge Pressure", new SingleValueStrategy(375.0f, 4.0f, 1000)); + addStrategy("Sys 1 Compressor Pct FLA", new SingleValueStrategy(93.0f, 2.0f, 1000)); + addStrategy("Sys 2 Compressor Pct FLA", new SingleValueStrategy(93.0f, 2.0f, 1000)); + addStrategy("Sys 1 Run Hours", new TotalizerStrategy(1000)); + addStrategy("Sys 2 Run Hours", new TotalizerStrategy(1000)); + addStrategy("Local Leaving Temp Setpoint", new SingleValueStrategy(70.0f, 0.0f, 1000)); + addStrategy("Sys 1 Fan KW", new SingleValueStrategy(35.0f, 2.0f, 1000)); + addStrategy("Sys 2 Fan KW", new SingleValueStrategy(23.0f, 2.0f, 1000)); + addStrategy("Sys 1 Compressor KW", new SingleValueStrategy(304.0f, 5.0f, 1000)); + addStrategy("Sys 2 Compressor KW", new SingleValueStrategy(198.0f, 5.0f, 1000)); + +} + +/** + * @brief Executes the running state's logic for one update cycle. + * + * This method first checks if there are any active alarms --> FailState. + * Also checks for Free Cooling Mode (for ease of testing) + * If no alarms are active, checks for "Chiller Start Command" = 0 (Modscan, but will be from PLC) + * for a command to transition to the Standby state. If no transition is requested, it updates the + * rampStrategy targetValues for the Supply Temp, CHW In, CHW Out analog values and applies the + * strategies defined for the running state. + * + * If no transition occurs, it applies the strategies defined for the running state. + * + * @param equipment Pointer to the Equipment instance. + * @return A pointer to a new State if a transition should occur, otherwise nullptr. + */ +template<> +State* RunningState::update(Equipment* equipment) { + // Update alarms states, Free Cooling mode, Freeze Protection Mode + updateAlarms(equipment); + updateFreeCooling(equipment); + + std::vector activeAlarmsDescriptions = {}; + const std::vector alarmDescriptions = { + "Sys 1 Alarm", "Sys 2 Alarm", "Sys 1 Fan Fault Alarm", "Sys 2 Fan Fault Alarm", + }; + + // Loop through alarms, create array of active alarms and send to FailState if any alarms are active + bool alarms_active = false; + for (const auto& desc : alarmDescriptions) { + Modbus_Point* point = equipment->getModbus_Point(desc); + if (point->getValue() == 1) { + activeAlarmsDescriptions.push_back(desc); + alarms_active = true; + } + } + if (alarms_active) return new FailState(activeAlarmsDescriptions); + + // If no alarms active and Start Command = 0--> send to StandbyState() + int Chiller_Enable = getPointValue(equipment, "Chiller Start Command"); // Modscan COIL 1 + if (Chiller_Enable == 0){ + return new StandbyState(); + } + + // Set the Supply Temp ramp target value equal to the Chiller Temp Setpoint + // Ramp CHW In Temp to Return Temp and CHW Out Temp to Supply Temp + float BMS_Temp_Setpoint = getPointValue(equipment, "Chiller Temp Setpoint"); + float supplyTemp = getPointValue(equipment, "Supply Temp"); + float returnTemp = getPointValue(equipment, "Return Temp"); + Strategy_Behavior* Supply_Temp_strat = getStrategy("Supply Temp"); + Strategy_Behavior* CHW_Out_strat = getStrategy("System CHW Out"); + Strategy_Behavior* CHW_In_strat = getStrategy("System CHW In"); + if (Supply_Temp_strat){ + static_cast(Supply_Temp_strat)->setTarget(BMS_Temp_Setpoint); + static_cast(CHW_Out_strat)->setTarget(supplyTemp); + static_cast(CHW_In_strat)->setTarget(returnTemp); + } + + // Apply any strategies defined for the standby state + _applyStrategies(equipment); + return nullptr; +} + +/** + * @brief Logic to execute once when entering the running state. + * + * Sets the "Chiller Status" point to indicate the unit is running. + * Update System Operational Code to 78 (Running). + * Increment a counter for number of starts for each System. + * We are assuming when the Chiller is commanded to run that both Systems will activate. + * Not enough information in Vendor SOO to add details for running systems independently, + * switching to Free-Cooling Mode, etc. + * + * @param equipment Pointer to the Equipment instance. + */ +template<> +void RunningState::enterState(Equipment* equipment) { + // Logic to run when the equipment enters this state + Serial.println("Enter Running State..."); + + setPointValue(equipment, "Chiller Status", 1); + setPointValue(equipment, "Sys 1 Operational Code", 78); + setPointValue(equipment, "Sys 2 Operational Code", 78); + + // Add one to the System 1 and 2 Starts counter + int Sys1_num_starts = getPointValue(equipment, "Sys 1 Starts"); + int Sys2_num_starts = getPointValue(equipment, "Sys 2 Starts"); + Sys1_num_starts++; + Sys2_num_starts++; + setPointValue(equipment, "Sys 1 Starts", Sys1_num_starts); + setPointValue(equipment, "Sys 2 Starts", Sys2_num_starts); + +} + +/** + * @brief Logic to execute once when exiting the running state. + * @param equipment Pointer to the Equipment instance. + */ +template<> +void RunningState::exitState(Equipment* equipment) { + // Cleanup logic to run when the equipment leaves this state + Serial.println("Exit Running State..."); +} \ No newline at end of file diff --git a/src/BMS/CHILLER/CH_York_YVAA_RTU/State_Standby.cpp b/src/BMS/CHILLER/CH_York_YVAA_RTU/State_Standby.cpp new file mode 100644 index 0000000..9448627 --- /dev/null +++ b/src/BMS/CHILLER/CH_York_YVAA_RTU/State_Standby.cpp @@ -0,0 +1,136 @@ +/** + * @file State_Standby.cpp + * @brief Implementation of the StandbyState class. + * @author Robert J Davis + * @date 2025-10-13 + * + * This file contains the implementation for the StandbyState, which defines + * the behavior of the equipment when it is in an idle or standby mode. + */ +#include "ModbusPoints/Modbus_Point.h" +#include "ModbusPoints/Modbus_FloatDecorator.h" +#include "Equipment/Equipment.h" +#include "Strategies/Strategy_Ramp.h" +#include "Strategies/Strategy_Random.h" +#include "Strategies/Strategy_Saw.h" +#include "Strategies/Strategy_SingleValue.h" +#include "Strategies/Strategy_Square.h" +#include "Strategies/Strategy_PID.h" +#include "States/State_Standby.h" +#include "States/State_Running.h" +#include "States/State_Fail.h" +#include "States/State.h" +#include "StateUtils.h" +#include +#include +#if defined(USE_MODBUS_IP) + #include +#else + #include +#endif + +/** + * @brief Constructs a new StandbyState object. + * + * In this state, the equipment is idle. This constructor initializes strategies + * to bring the system to a safe, idle condition. It sets a stable value for + * the analog readings and takes Fans and Compressors to 0 kW. + */ +template<> +StandbyState::StandbyState() { + addStrategy("Supply Temp", new SingleValueStrategy(80.0f, 1.0f, 1000)); + addStrategy("Return Temp", new SingleValueStrategy(80.0f, 1.0f, 1000)); + addStrategy("Ambient Temp", new SingleValueStrategy(100.0f, 1.0f, 1000)); + + addStrategy("System CHW Out", new SingleValueStrategy(80.0f, 1.0f, 1000)); + addStrategy("System CHW In", new SingleValueStrategy(80.0f, 1.0f, 1000)); + addStrategy("Sys 1 Condenser Temp", new SingleValueStrategy(124.0f, 1.0f, 1000)); + addStrategy("Sys 2 Condenser Temp", new SingleValueStrategy(124.0f, 1.0f, 1000)); + addStrategy("Sys 1 Oil Pressure", new SingleValueStrategy(420.0f, 1.0f, 1000)); + addStrategy("Sys 2 Oil Pressure", new SingleValueStrategy(420.0f, 1.0f, 1000)); + addStrategy("Sys 1 Suction Pressure", new SingleValueStrategy(70.0f, 1.0f, 1000)); + addStrategy("Sys 2 Suction Pressure", new SingleValueStrategy(70.0f, 1.0f, 1000)); + addStrategy("Sys 1 Discharge Pressure", new SingleValueStrategy(70.0f, 1.0f, 1000)); + addStrategy("Sys 2 Discharge Pressure", new SingleValueStrategy(70.0f, 1.0f, 1000)); + addStrategy("Sys 1 Compressor Pct FLA", new SingleValueStrategy(0.0f, 0.0f, 1000)); + addStrategy("Sys 2 Compressor Pct FLA", new SingleValueStrategy(0.0f, 0.0f, 1000)); + addStrategy("Local Leaving Temp Setpoint", new SingleValueStrategy(70.0f, 0.0f, 1000)); + addStrategy("Sys 1 Fan KW", new SingleValueStrategy(0.0f, 0.0f, 1000)); + addStrategy("Sys 2 Fan KW", new SingleValueStrategy(0.0f, 0.0f, 1000)); + addStrategy("Sys 1 Compressor KW", new SingleValueStrategy(0.0f, 0.0f, 1000)); + addStrategy("Sys 2 Compressor KW", new SingleValueStrategy(0.0f, 0.0f, 1000)); + +} + +/** + * @brief Executes the standby state's logic for one update cycle. + * + * This method first checks if there are any active alarms --> FailState. + * Also checks for Free Cooling Mode (for ease of testing) + * If no alarms are active, checks for "Chiller Start Command" (Modscan, but will be from PLC) + * for a command to transition to the Running state. If no transition is requested, it applies + * the strategies defined for the standby state. + * + * @param equipment Pointer to the Equipment instance. + * @return A pointer to a new State if a transition should occur, otherwise nullptr. + */ +template<> +State* StandbyState::update(Equipment* equipment) { + // Update alarms states, Free Cooling mode, Freeze Protection Mode + updateAlarms(equipment); + updateFreeCooling(equipment); + + std::vector activeAlarmsDescriptions = {}; + const std::vector alarmDescriptions = { + "Sys 1 Alarm", "Sys 2 Alarm", "Sys 1 Fan Fault Alarm", "Sys 2 Fan Fault Alarm", + }; + + // Loop through alarms, create array of active alarms and send to FailState if any alarms are active + bool alarms_active = false; + for (const auto& desc : alarmDescriptions) { + Modbus_Point* point = equipment->getModbus_Point(desc); + if (point->getValue() == 1) { + activeAlarmsDescriptions.push_back(desc); + alarms_active = true; + } + } + if (alarms_active) return new FailState(activeAlarmsDescriptions); + + // If no alarms active and Start Command = 1--> send to RunningState() + int Chiller_Enable = getPointValue(equipment, "Chiller Start Command"); // Modscan COIL 1 + if (Chiller_Enable == 1){ + return new RunningState(); + } + + // Apply any strategies defined for the standby state + _applyStrategies(equipment); + return nullptr; +} + +/** + * @brief Logic to execute once when entering the standby state. + * Sets the "Chiller Status" point to indicate the unit is not running. + * Updates Operational Codes for Sys 1 and Sys 2 + * Ensure Chiller Start Command is reset to 0. + * @param equipment Pointer to the Equipment instance. + */ +template<> +void StandbyState::enterState(Equipment* equipment) { + // Logic to run when the equipment enters this state + Serial.println("Enter Standby State..."); + setPointValue(equipment, "Chiller Start Command", 0); + setPointValue(equipment, "Chiller Status", 0); + setPointValue(equipment, "Sys 1 Operational Code", 77); + setPointValue(equipment, "Sys 2 Operational Code", 77); + +} + +/** + * @brief Logic to execute once when exiting the standby state. + * @param equipment Pointer to the Equipment instance. + */ +template<> +void StandbyState::exitState(Equipment* equipment) { + // Cleanup logic to run when the equipment leaves this state + Serial.println("Exit Standby State..."); +} \ No newline at end of file diff --git a/src/BMS/CHILLER/CH_York_YVAA_RTU/config.h b/src/BMS/CHILLER/CH_York_YVAA_RTU/config.h new file mode 100644 index 0000000..fbd900f --- /dev/null +++ b/src/BMS/CHILLER/CH_York_YVAA_RTU/config.h @@ -0,0 +1,119 @@ +/** + * @file config.h + * @brief Main configuration file for the York Chiller (RTU) emulator. + * @author Robert J Davis + * @date 2025-10-12 + * + * This file contains important configurations for the Modbus RTU communication + * and the specific register map for the emulated device. + */ + +#ifndef CONFIG_H +#define CONFIG_H +#include +#include "core.h" +#include "Equipment/Equipment.h" + + +#if defined(USE_MODBUS_IP) +/** + * @defgroup ModbusTCPConfig Modbus IP Configuration + * @brief Parameters for Modbus TCP communication. + * @{ + */ + #include + const char *ssid = "wifi_name"; /**< @brief The SSID of the WiFi network. */ + const char *password = "wifi_password"; /**< @brief The password for the WiFi network. */ + IPAddress local_IP(192, 168, 1, 234); /**< @brief The static IP address for the device. */ + IPAddress gateway(192, 168, 1, 1); /**< @brief The gateway IP address. */ + IPAddress subnet(255, 255, 255, 0); /**< @brief The subnet mask. */ + + ModbusIP mb; +#else + /** + * @defgroup ModbusRTUConfig Modbus RTU Configuration + * @brief Parameters for serial Modbus RTU communication. + * @{ + */ + #include + const int BAUDRATE = 19200; /**< @brief The serial communication speed in bits per second. */ + const int RX_PIN = 17; /**< @brief The GPIO pin used for receiving data (RX). */ + const int TX_PIN = 16; /**< @brief The GPIO pin used for transmitting data (TX). */ + const int RST_PIN = 4; /**< @brief The GPIO pin connected to the RS485 driver's DE/RE pins for direction control. */ + const int MODBUS_ID = 1; /**< @brief The unique slave ID for this device on the Modbus bus. */ + /** @} */ + + /** @brief Global instance of the Modbus RTU server. */ + ModbusRTU mb; +#endif + +/** + * @brief The Modbus map for the Equipment device. + * This array defines all the Modbus points available on the emulated device. + * The `description` field is crucial as it's used to look up points within the application logic. + */ +modbusMap mb_map[] = +{ + {COIL, 0, 0, "Chiller Start Command"}, // Use in Modscan - Hard IO in SCP, Used for Arduino simulation only + {COIL, 1, 0, "Sys 1 Alarm"}, // Use in Modscan - Hard IO in SCP, Used for Arduino simulation only + {COIL, 2, 0, "Sys 2 Alarm"}, // Use in Modscan - Hard IO in SCP, Used for Arduino simulation only + {COIL, 3, 0, "Sys 1 Fan Fault ON"}, // Use in Modscan - Used for Arduino simulation only + {COIL, 4, 0, "Sys 2 Fan Fault ON"}, // Use in Modscan - Used for Arduino simulation only + {COIL, 5, 0, "Free Cooling Mode ON"}, // Use in Modscan - Used for Arduino simulation only + + {DI, 0, 0, "Sys 1 Fan Fault Alarm"}, + {DI, 1, 0, "Sys 2 Fan Fault Alarm"}, + + {HR, 0, 0, "Chiller Status"}, // Use in Modscan - Hard IO in SCP, Used for Arduino simulation only + {HR, 1, 0, "Chiller Temp Setpoint"}, // Use in Modscan - Hard IO in SCP, Used for Arduino simulation only + {HR, 2, 0, "Supply Temp"}, // Use in Modscan - Hard IO in SCP (PICS?), Used for Arduino simulation only + {HR, 3, 0, "Return Temp"}, // Use in Modscan - Hard IO in SCP (PICS?), Used for Arduino simulation only + + {HR, 4, 70, "System CHW Out"}, + {HR, 5, 70, "System CHW In"}, + {HR, 7, 0, "Sys 1 Condenser Temp"}, + {HR, 9, 0, "Ambient Temp"}, + + {HR, 11, 0, "Sys 1 Oil Pressure"}, + {HR, 12, 0, "Sys 1 Suction Pressure"}, + {HR, 13, 0, "Sys 1 Discharge Pressure"}, + {HR, 14, 0, "Sys 1 Compressor Pct FLA"}, + {HR, 15, 0, "Sys 1 Run Hours"}, + {HR, 16, 0, "Sys 1 Starts"}, + + {HR, 20, 0, "Sys 2 Oil Pressure"}, + {HR, 21, 0, "Sys 2 Suction Pressure"}, + {HR, 22, 0, "Sys 2 Discharge Pressure"}, + {HR, 23, 0, "Sys 2 Compressor Pct FLA"}, + {HR, 24, 0, "Sys 2 Run Hours"}, + {HR, 25, 0, "Sys 2 Starts"}, + + {HR, 29, 77, "Sys 1 Operational Code"}, + {HR, 30, 0, "Sys 1 Fault Code"}, + {HR, 31, 77, "Sys 2 Operational Code"}, + {HR, 32, 0, "Sys 2 Fault Code"}, + + {HR, 39, 0, "Local Leaving Temp Setpoint"}, + + {HR, 40, 0, "Sys 1 Fan KW"}, + {HR, 41, 0, "Sys 1 Compressor KW"}, + {HR, 42, 0, "Sys 2 Fan KW"}, + {HR, 43, 0, "Sys 2 Compressor KW"}, + {HR, 49, 0, "Sys 2 Condenser Temp"}, + {HR, 50, 0, "Free Cooling Mode"}, + {HR, 51, 0, "Free Cooling Valve"}, + +}; +//Size of modbus map used in FOR cycles, automatically calculated. +/** + * @brief The total number of entries in the `mb_map` array. + * This is calculated at compile time and used for iterating over the map. + */ +const int map_size = sizeof(mb_map) / sizeof(mb_map[0]); + +/** + * @brief The main loop update interval in milliseconds. + */ +int interval = 250; + +#endif // CONFIG_H diff --git a/src/BMS/CHILLER/CH_York_YVAA_RTU/main.cpp b/src/BMS/CHILLER/CH_York_YVAA_RTU/main.cpp new file mode 100644 index 0000000..5dbc9f1 --- /dev/null +++ b/src/BMS/CHILLER/CH_York_YVAA_RTU/main.cpp @@ -0,0 +1,78 @@ +/** + * @file main.cpp + * @brief Main execution program for the Daikin Chiller (RTU) Emulator. + * @author Emmanuel Hernandez Cruz + * @date 2025-09-02 + * + * @details This file contains the main execution program for an Arduino-based + * emulator of a Daikin Chiller unit. The program communicates via the + * Modbus RTU protocol over a serial connection. + * + * The setup() function initializes the following: + * - Serial communication for debugging. + * - A Modbus RTU server with parameters from config.h. + * - Modbus points (Coils, Holding Registers, etc.) based on a predefined map in config.h. + * + * The loop() function continuously: + * - Services the Modbus RTU server to handle incoming requests. + * - Periodically calls the main update loop for the emulated equipment, which + * manages state transitions and behavior strategies. + * + * @see config.h for Modbus RTU and register map configuration. + * @see Equipment.h for the main equipment logic. + * @see State.h for different equipment states. + * @see Strategies/Strategy_Behavior.h for value generation strategies. + * @see Modbus_Point.h for the base class for all Modbus points. + */ +//================================================================================================================================= +//Libraries and declaration of variables. +#include +#include "config.h" +#include "ModbusPoints/Modbus_PointFactory.h" + +//================================================================================================================================= +/** + * @brief Initializes the application. + * @details This function runs once at startup. It configures the serial communication + * for debugging and the Modbus RTU server. It then creates and initializes all + * the Modbus points based on the `mb_map` array in `config.h`. + */ +const int rtsPin = 4; +void setup() { + Serial.begin(115200); + Serial.println("Setup function started"); + + Serial2.begin(BAUDRATE, SERIAL_8N1, RX_PIN, TX_PIN); + mb.begin(&Serial2, RST_PIN); // Start the server + mb.slave(MODBUS_ID); // Set the slave ID + + for(int i = 0; i < map_size; i++){ + Modbus_Point* point = createModbus_Point(&mb, mb_map[i].category, mb_map[i].address, mb_map[i].value, mb_map[i].description); + if (point) { + point->addToModbusServer(); + EquipmentInstance.addModbus_Point(mb_map[i].description, point); + } + } + Serial.println("Setup function ended"); +} +//================================================================================================================================= +/** + * @brief The main application loop. + * @details This function runs repeatedly after setup() has completed. It performs two main actions: + * 1. It continuously services the Modbus server by calling `mb.task()` to handle + * incoming requests from a Modbus master. + * 2. At a fixed interval (defined in `config.h`), it calls `EquipmentInstance.update()` + * to run the emulator's internal state machine and behavior logic. + */ +void loop() { + mb.task(); + unsigned long currentMillis = millis(); + if (currentMillis - previousMillis >= interval) { + previousMillis = currentMillis; + unsigned long startTime = millis(); + EquipmentInstance.update(); + unsigned long endTime = millis(); + unsigned long elapsedTime = endTime - startTime; + Serial.printf("Control Execution time: %d ms\n", elapsedTime); + } +}