added Run Status and Fault Status

Added Run Status, Fault Status. Additional comments as well.
This commit is contained in:
RobertJDavis
2025-10-30 10:04:56 -07:00
parent f44b0e8769
commit a6a5c69f86
6 changed files with 92 additions and 61 deletions

View File

@@ -14,7 +14,7 @@
default_envs = PHX3_VFD_ABB_ACH580_RTU ; Select here the name of the configuration you want to download default_envs = PHX3_VFD_ABB_ACH580_RTU ; Select here the name of the configuration you want to download
[env] [env]
upload_port = COM50 upload_port = COM9
[common_env_options] [common_env_options]
framework = arduino framework = arduino

View File

@@ -19,12 +19,12 @@ The code is written for an ESP8266/ESP32-style microcontroller with WiFi capabil
--- ---
## States and Strategies ## States and Strategies
The hardwire IO signals to/from VFD/PLC are Start Cmd, Stop Cmd, Speed Command, Speed Feedback, VFD Run Status, VFD Fault. The hardwire IO signals to/from VFD/PLC are Start Cmd, Stop Cmd, Speed Command, Speed Feedback, Run Status, Fault Status.
User needs to set the speed command (HR 150) in RPM from the PLC User needs to set the speed command (HR 150) in RPM from the PLC
User needs to set the Start command (HR 151) from the PLC User needs to set the Start command (HR 151) from the PLC
It appears these registers were arbitrarily chosen for the purpose of this Arduino simulation. It appears these hard IO registers were arbitrarily chosen for the purpose of this Arduino simulation.
The registers selected are based on FS Config file from CDR project. The registers selected are based on FS Config file from CDR project. Run Status and Fault Status registers were added for simulation.
Currently there is no connection on Speed Feedback, Run Status, or Fault from Arduino to PICS Currently there is no connection on Speed Feedback, Run Status, or Fault from Arduino to PICS (work in progress).
### Standby State ### Standby State
* **Equipment**: Equipment parameters go back to 0 * **Equipment**: Equipment parameters go back to 0
@@ -37,4 +37,6 @@ Currently there is no connection on Speed Feedback, Run Status, or Fault from Ar
* **Totalizers Strategy**: Inverter kWh cnt, Hours Run * **Totalizers Strategy**: Inverter kWh cnt, Hours Run
### Fail State ### Fail State
* Not used * Enters Fail State if Fault Status is set to 0.
While in Fail State, the Start/Stop command is reset to 0.
The only way to exit Fail State is if Fault Status = 1 --> Standby State.

View File

@@ -1,8 +1,8 @@
/** /**
* @file State_Fail.cpp * @file State_Fail.cpp
* @brief Implementation of the FailState class. * @brief Implementation of the FailState class.
* @author Emmanuel Hernandez Cruz * @author Robert J Davis
* @date 2025-09-05 * @date 2025-10-30
* *
* This file contains the implementation for the FailState, which defines * This file contains the implementation for the FailState, which defines
* the behavior of the equipment when it has entered a fault condition. * the behavior of the equipment when it has entered a fault condition.
@@ -11,6 +11,7 @@
#include "States/State_Fail.h" #include "States/State_Fail.h"
#include "ModbusPoints/Modbus_Point.h" #include "ModbusPoints/Modbus_Point.h"
#include "Equipment/Equipment.h" #include "Equipment/Equipment.h"
#include "Strategies/Strategy_Ramp.h"
#include "Strategies/Strategy_SingleValue.h" #include "Strategies/Strategy_SingleValue.h"
#include "Strategies/Strategy_PID.h" #include "Strategies/Strategy_PID.h"
@@ -26,21 +27,27 @@
/** /**
* @brief Constructs a new FailState object. * @brief Constructs a new FailState object.
* *
* This constructor receives a list of alarm descriptions and creates strategies * This constructor sets the associated analog signals to the same values as Standby.
* to set the corresponding Modbus points to a value of 1, indicating an
* active alarm. It also initializes a PID strategy for the valve position.
*/ */
template<> template<>
FailState<ModbusRTU>::FailState(const std::vector<std::string>& activeAlarms) { FailState<ModbusRTU>::FailState(const std::vector<std::string>& activeAlarms) {
// Simulate a failure: set common alarm and a specific fan alarm. addStrategy("Output Frequency", new SingleValueStrategy(0.1f, 0.2f, 1000 ));
addStrategy("Output Voltage", new SingleValueStrategy(0.1f, 0.1f, 1000 ));
addStrategy("DC Voltage", new SingleValueStrategy(0.1f, 0.1f, 1000 ));
addStrategy("Output Power", new SingleValueStrategy(0.1f, 0.1f, 1000 ));
addStrategy("Motor Speed Used", new RampStrategy(0.0f, 200.0f, 1000 ));
addStrategy("Motor Speed estimated", new RampStrategy(0.0f, 200.0f, 1000 ));
addStrategy("Motor Current", new RampStrategy(0.0f, 20.0f, 1000 ));
addStrategy("Motor Torque", new RampStrategy(0.0f, 20.0f, 1000 ));
addStrategy("Inverter Temperature", new RampStrategy(0.0f, 1.0f, 1000 ));
} }
/** /**
* @brief Executes the fail state's logic for one update cycle. * @brief Executes the fail state's logic for one update cycle.
* *
* This method checks the "Clear Alm" Modbus point for a command to transition * While in FailState, the Unit cannot be started and the Start/Stop command is reset to 0.
* back to Standby, which would typically happen after a fault is cleared by a * When the fault is cleared --> Standby State.
* user. If no transition is requested, it continues to apply the failure strategies.
* *
* @param equipment Pointer to the Equipment instance. * @param equipment Pointer to the Equipment instance.
* @return A pointer to a new State if a transition should occur, otherwise nullptr. * @return A pointer to a new State if a transition should occur, otherwise nullptr.
@@ -49,24 +56,30 @@ template<>
State<ModbusRTU>* FailState<ModbusRTU>::update(Equipment<ModbusRTU>* equipment) { State<ModbusRTU>* FailState<ModbusRTU>::update(Equipment<ModbusRTU>* equipment) {
// STATE control, add conditions if change to a different state is needed // STATE control, add conditions if change to a different state is needed
Serial.println("Fail update function"); Serial.println("Fail update function");
int faultNotPresent = getPointValue(equipment, "Fault Status");
if(faultNotPresent == 1){
return new StandbyState<ModbusRTU>();
}
setPointValue(equipment, "Start/Stop", 0);
_applyStrategies(equipment); _applyStrategies(equipment);
return nullptr; return nullptr;
} }
/** /**
* @brief Logic to execute once when entering the fail state. Sets the main alarm bit. * @brief Logic to execute once when entering the fail state. Sets the Run Status to 0.
* @param equipment Pointer to the Equipment instance. * @param equipment Pointer to the Equipment instance.
*/ */
template<> template<>
void FailState<ModbusRTU>::enterState(Equipment<ModbusRTU>* equipment) { void FailState<ModbusRTU>::enterState(Equipment<ModbusRTU>* equipment) {
// Logic to run when the equipment enters this state // Logic to run when the equipment enters this state
Serial.println("Enter Fail State..."); Serial.println("Enter Fail State...");
setPointValue(equipment, "Run Status", 0);
} }
/** /**
* @brief Logic to execute once when exiting the fail state. Clears the main alarm bit. * @brief Logic to execute once when exiting the fail state.
* @param equipment Pointer to the Equipment instance. * @param equipment Pointer to the Equipment instance.
*/ */
template<> template<>

View File

@@ -1,7 +1,7 @@
/** /**
* @file State_Running.cpp * @file State_Running.cpp
* @brief Implementation of the RunningState class. * @brief Implementation of the RunningState class.
* @author Emmanuel Hernandez Cruz, Robert J Davis * @author Robert J Davis
* @date 2025-10-22 * @date 2025-10-22
* *
* This file contains the implementation for the RunningState, which defines * This file contains the implementation for the RunningState, which defines
@@ -36,8 +36,8 @@
* @brief Constructs a new RunningState object. * @brief Constructs a new RunningState object.
* *
* This constructor initializes behavior strategies active during the running * This constructor initializes behavior strategies active during the running
* state, such as a PID controller for the 'CW Valve Position' and totalizers * state, such as speed feedback, current, torque, hours run, etc.
* for the run-hours of each EC fan. * These values are based on a 50hp motor, 480V, 65 FLA, 60Hz, 1800 rpm (PHX3 DC1/2)
*/ */
template<> template<>
RunningState<ModbusRTU>::RunningState() { RunningState<ModbusRTU>::RunningState() {
@@ -53,18 +53,16 @@ RunningState<ModbusRTU>::RunningState() {
addStrategy("Output Power", new RampStrategy(36.7f, 2.0f, 1000 )); addStrategy("Output Power", new RampStrategy(36.7f, 2.0f, 1000 ));
addStrategy("Inverter kWh cnt", new TotalizerStrategy(1000)); addStrategy("Inverter kWh cnt", new TotalizerStrategy(1000));
addStrategy("Hours Run", new TotalizerStrategy(1000)); addStrategy("Hours Run", new TotalizerStrategy(1000));
} }
/** /**
* @brief Executes the running state's logic for one update cycle. * @brief Executes the running state's logic for one update cycle.
* *
* This method first checks for state transition commands: * This method first checks for state transition commands:
* 1. It reads the "ON/OFF Command By BMS" point. If it's 0, it transitions to StandbyState. * 1. If Fault is 0 (there is a fault present) --> FailState
* 2. It reads the "Fault Code" point. If it's non-zero, it transitions to FailState, * 2. It reads the "Stop/Start" command point (from PLC). If it's 0, it transitions to StandbyState.
* passing the corresponding alarm description.
* *
* If no transition occurs, it applies the strategies defined for the running state. * If no transition occurs, it updates values according to speed setpoint sent from PLC.
* *
* @param equipment Pointer to the Equipment instance. * @param equipment Pointer to the Equipment instance.
* @return A pointer to a new State if a transition should occur, otherwise nullptr. * @return A pointer to a new State if a transition should occur, otherwise nullptr.
@@ -74,6 +72,17 @@ State<ModbusRTU>* RunningState<ModbusRTU>::update(Equipment<ModbusRTU>* equipmen
// STATE control, add conditions if change to a different state is needed // STATE control, add conditions if change to a different state is needed
Serial.println("Running update function"); Serial.println("Running update function");
// If Fault Status = 0, there is a fault --> FailState
int faultNotPresent = getPointValue(equipment, "Fault Status");
if(faultNotPresent == 0){
return new FailState<ModbusRTU>({"Fault Status"});
}
int VFD_Start_Stop = getPointValue(equipment, "Start/Stop");
if (VFD_Start_Stop == 0){
return new StandbyState<ModbusRTU>();
}
float speed_pct = getPointValue(equipment, "Speed Cmd") / 1800.0f; float speed_pct = getPointValue(equipment, "Speed Cmd") / 1800.0f;
// Based on Affinity Laws. Motor: 65 FLA, 480V, 60Hz, 1800 rpm, 50hp // Based on Affinity Laws. Motor: 65 FLA, 480V, 60Hz, 1800 rpm, 50hp
float voltage_update = speed_pct * 480; float voltage_update = speed_pct * 480;
@@ -83,11 +92,6 @@ State<ModbusRTU>* RunningState<ModbusRTU>::update(Equipment<ModbusRTU>* equipmen
float freq_update = speed_pct * 60; float freq_update = speed_pct * 60;
float power_update = speed_pct * speed_pct * speed_pct * 36.77f; // 50 hp ~ 36.77kW float power_update = speed_pct * speed_pct * speed_pct * 36.77f; // 50 hp ~ 36.77kW
int VFD_Start_Stop = getPointValue(equipment, "Start/Stop");
if (VFD_Start_Stop == 0){
return new StandbyState<ModbusRTU>();
}
float currentSP = getPointValue(equipment, "Speed Cmd"); float currentSP = getPointValue(equipment, "Speed Cmd");
Strategy_Behavior* motorSpeedUsed = getStrategy("Motor Speed Used"); Strategy_Behavior* motorSpeedUsed = getStrategy("Motor Speed Used");
// 2. Check if the strategy exists // 2. Check if the strategy exists
@@ -140,19 +144,19 @@ State<ModbusRTU>* RunningState<ModbusRTU>::update(Equipment<ModbusRTU>* equipmen
/** /**
* @brief Logic to execute once when entering the running state. * @brief Logic to execute once when entering the running state.
* Sets the "Chiller Sts" point to indicate the unit is running. * Sets the Run Status" point to indicate the unit is running.
* @param equipment Pointer to the Equipment instance. * @param equipment Pointer to the Equipment instance.
*/ */
template<> template<>
void RunningState<ModbusRTU>::enterState(Equipment<ModbusRTU>* equipment) { void RunningState<ModbusRTU>::enterState(Equipment<ModbusRTU>* equipment) {
// Logic to run when the equipment enters this state // Logic to run when the equipment enters this state
Serial.println("Enter Running State..."); Serial.println("Enter Running State...");
setPointValue(equipment, "Run Status", 1);
} }
/** /**
* @brief Logic to execute once when exiting the running state. * @brief Logic to execute once when exiting the running state.
* Sets the "Chiller Sts" point to indicate the unit is no longer running. * Sets the "Output Frequency" to 0.
* @param equipment Pointer to the Equipment instance. * @param equipment Pointer to the Equipment instance.
*/ */
template<> template<>

View File

@@ -1,7 +1,7 @@
/** /**
* @file State_Standby.cpp * @file State_Standby.cpp
* @brief Implementation of the StandbyState class. * @brief Implementation of the StandbyState class.
* @author Emmanuel Hernandez Cruz, Robert J Davis * @author Robert J Davis
* @date 2025-10-23 * @date 2025-10-23
* *
* This file contains the implementation for the StandbyState, which defines * This file contains the implementation for the StandbyState, which defines
@@ -26,8 +26,7 @@
* @brief Constructs a new StandbyState object. * @brief Constructs a new StandbyState object.
* *
* In this state, the equipment is idle. This constructor initializes several * In this state, the equipment is idle. This constructor initializes several
* strategies to generate random values for various status points, simulating * strategies to simulate a live but non-operational unit. Most values are ramped down to 0.
* a live but non-operational unit.
*/ */
template<> template<>
StandbyState<ModbusRTU>::StandbyState() { StandbyState<ModbusRTU>::StandbyState() {
@@ -41,15 +40,16 @@ StandbyState<ModbusRTU>::StandbyState() {
addStrategy("Motor Current", new RampStrategy(0.0f, 20.0f, 1000 )); addStrategy("Motor Current", new RampStrategy(0.0f, 20.0f, 1000 ));
addStrategy("Motor Torque", new RampStrategy(0.0f, 20.0f, 1000 )); addStrategy("Motor Torque", new RampStrategy(0.0f, 20.0f, 1000 ));
addStrategy("Inverter Temperature", new RampStrategy(0.0f, 1.0f, 1000 )); addStrategy("Inverter Temperature", new RampStrategy(0.0f, 1.0f, 1000 ));
} }
/** /**
* @brief Executes the standby state's logic for one update cycle. * @brief Executes the standby state's logic for one update cycle.
* *
* This method checks the "Chiller On-Off" Modbus point for a command to * This method first checks for state transition commands:
* transition to the Running state. If no transition is requested, it applies * 1. If Fault is 0 (there is a fault present) --> FailState
* the strategies defined for the standby state. * 2. It reads the "Start/Stop" point (from PLC). If it's 1 --> RunningState
*
* If no transition is requested, it applies the strategies defined for the standby state.
* *
* @param equipment Pointer to the Equipment instance. * @param equipment Pointer to the Equipment instance.
* @return A pointer to a new State if a transition should occur, otherwise nullptr. * @return A pointer to a new State if a transition should occur, otherwise nullptr.
@@ -58,6 +58,13 @@ template<>
State<ModbusRTU>* StandbyState<ModbusRTU>::update(Equipment<ModbusRTU>* equipment) { State<ModbusRTU>* StandbyState<ModbusRTU>::update(Equipment<ModbusRTU>* equipment) {
// STATE control, add conditions if change to a different state is needed // STATE control, add conditions if change to a different state is needed
Serial.println("Standby update function"); Serial.println("Standby update function");
// If Fault Status = 0, there is a fault --> FailState
int faultNotPresent = getPointValue(equipment, "Fault Status");
if(faultNotPresent == 0){
return new FailState<ModbusRTU>({"Fault Status"});
}
int VFD_Start_Stop = getPointValue(equipment, "Start/Stop"); int VFD_Start_Stop = getPointValue(equipment, "Start/Stop");
if (VFD_Start_Stop == 1){ if (VFD_Start_Stop == 1){
return new RunningState<ModbusRTU>(); return new RunningState<ModbusRTU>();
@@ -69,14 +76,14 @@ State<ModbusRTU>* StandbyState<ModbusRTU>::update(Equipment<ModbusRTU>* equipmen
/** /**
* @brief Logic to execute once when entering the standby state. * @brief Logic to execute once when entering the standby state.
* Sets the "Chiller Sts" point to indicate the unit is not running. * Sets the "Run Status" point to indicate the unit is not running.
* @param equipment Pointer to the Equipment instance. * @param equipment Pointer to the Equipment instance.
*/ */
template<> template<>
void StandbyState<ModbusRTU>::enterState(Equipment<ModbusRTU>* equipment) { void StandbyState<ModbusRTU>::enterState(Equipment<ModbusRTU>* equipment) {
// Logic to run when the equipment enters this state // Logic to run when the equipment enters this state
Serial.println("Enter Standby State..."); Serial.println("Enter Standby State...");
setPointValue(equipment, "Run Status", 0);
} }
/** /**

View File

@@ -1,11 +1,13 @@
/** /**
* @file config.h * @file config.h
* @brief Main configuration file for the ABB ACH580 (VFD) emulator. * @brief Main configuration file for the ABB ACH580 (VFD) emulator.
* @author Emmanuel Hernandez Cruz, Robert J Davis * @author Robert J Davis
* @date 2025-10-22 * @date 2025-10-22
* *
* This file contains important configurations for the Modbus RTU communication * This file contains important configurations for the Modbus RTU communication
* and the specific register map for the emulated device. * and the specific register map for the emulated device.
* Received these registers from CDR for their ACH580.
* Added "Run Status" and "Fault Status" to simulated hard IO points and send feedback to PLC during simulation.
*/ */
#ifndef CONFIG_H #ifndef CONFIG_H
@@ -54,9 +56,6 @@
*/ */
modbusMap mb_map[] = modbusMap mb_map[] =
{ {
{HR, 149, 0, "Speed Cmd"}, // expecting rpm (1800 rpm max)
{HR, 151, 0, "Start/Stop"},
{HR, 152, 0, "HOA Command"},
{HR, 100, 0, "Motor Speed Used"}, // RJD: 1800 rpm max {HR, 100, 0, "Motor Speed Used"}, // RJD: 1800 rpm max
{HR, 101, 0, "Motor Speed estimated"}, // RJD: 1800 rpm max {HR, 101, 0, "Motor Speed estimated"}, // RJD: 1800 rpm max
{HR_10x, 105, 0, "Output Frequency"}, // 60 Hz @100% speed {HR_10x, 105, 0, "Output Frequency"}, // 60 Hz @100% speed
@@ -66,24 +65,30 @@ modbusMap mb_map[] =
{HR, 112, 0, "Output Voltage"}, // RJD: 480 VAC {HR, 112, 0, "Output Voltage"}, // RJD: 480 VAC
{HR, 113, 0, "Output Power"}, //max 372580 // RJD: Changed from HR_10x to HR, 50 hp ~ 36.77 kW {HR, 113, 0, "Output Power"}, //max 372580 // RJD: Changed from HR_10x to HR, 50 hp ~ 36.77 kW
{HR_10x, 119, 0, "Inverter kWh cnt"}, {HR_10x, 119, 0, "Inverter kWh cnt"},
{HR, 149, 1800, "Speed Cmd"}, // arbitrary register number - receive signal from PLC (hardwire IO in field); expecting rpm (1800 rpm max)
{HR, 151, 0, "Start/Stop"}, // arbitrary register number - receive signal from PLC (hardwire IO in practice)
{HR, 152, 0, "HOA Command"}, // arbitrary register number - not used in program
{HR, 154, 0, "Run Status"}, // arbitrary register number - 0:off, 1:on (simulated hardwire IO) sending feedback to PLC during simulation.
{HR, 155, 1, "Fault Status"}, // arbitrary register number - 0:faulted, 1:not faulted (simulated hardwire IO). When = 0, will turn off VFD.
{HR, 502, 0, "Hours Run"}, {HR, 502, 0, "Hours Run"},
{HR, 510, 0, "Inverter Temperature"}, // RJD: Changed from HR_10x to HR, % of fault limit {HR, 510, 0, "Inverter Temperature"}, // RJD: Changed from HR_10x to HR, % of fault limit
{HR, 521, 0, "HOA Status Word"}, {HR, 521, 0, "HOA Status Word"}, // not used in program
{HR, 410, 0, "Last Fault"}, {HR, 410, 0, "Last Fault"}, // not used in program
{HR, 411, 0, "2nd to last Fault"}, {HR, 411, 0, "2nd to last Fault"}, // not used in program
{HR, 412, 0, "3rd to last Fault"}, {HR, 412, 0, "3rd to last Fault"}, // not used in program
{HR, 439, 0, "Event Word Param"}, {HR, 439, 0, "Event Word Param"}, // not used in program
{HR, 610, 0, "Status Word 1"}, {HR, 610, 0, "Status Word 1"}, // not used in program
{HR, 615, 0, "Status Word 2"}, {HR, 615, 0, "Status Word 2"}, // not used in program
{HR, 616, 0, "Status Word 3"}, {HR, 616, 0, "Status Word 3"}, // not used in program
{HR, 617, 0, "Status Word 4"}, {HR, 617, 0, "Status Word 4"}, // not used in program
{HR, 618, 0, "Status Word 5"}, {HR, 618, 0, "Status Word 5"}, // not used in program
{HR, 619, 0, "Status Word 6"}, {HR, 619, 0, "Status Word 6"}, // not used in program
{HR, 620, 0, "Status Word 7"}, {HR, 620, 0, "Status Word 7"}, // not used in program
{HR, 621, 0, "Status Word 8"}, {HR, 621, 0, "Status Word 8"}, // not used in program
}; };
//Size of modbus map used in FOR cycles, automatically calculated. //Size of modbus map used in FOR cycles, automatically calculated.
/** /**