1487 lines
39 KiB
C++
1487 lines
39 KiB
C++
#include <Arduino.h>
|
|
#include <macros.h>
|
|
#include <Component.h>
|
|
#include <enums.h>
|
|
#include <ArduinoLog.h>
|
|
|
|
#include "./PHApp.h"
|
|
#include "./config.h"
|
|
#include "./config-modbus.h"
|
|
#include "esp32_compat.h"
|
|
#include "Settings.h"
|
|
#include <components/Logger.h>
|
|
|
|
#include <components/OmronE5Types.h>
|
|
#include <components/OmronE5.h>
|
|
|
|
#ifdef ENABLE_MODBUS_TCP
|
|
#include <modbus/ModbusTCP.h>
|
|
#include <modbus/ModbusTypes.h>
|
|
#endif
|
|
|
|
#include <LittleFS.h>
|
|
#include <components/RestServer.h>
|
|
|
|
#if defined(ENABLE_AMPERAGE_BUDGET_MANAGER)
|
|
void PHApp_onWarmupComplete(Component *owner)
|
|
{
|
|
PHApp *app = static_cast<PHApp *>(owner);
|
|
if (app)
|
|
{
|
|
bool wasInitializing = false;
|
|
#ifdef ENABLE_PROFILE_TEMPERATURE
|
|
for (int i = 0; i < PROFILE_TEMPERATURE_COUNT; i++)
|
|
{
|
|
if (app->tempProfiles[i] != nullptr && app->tempProfiles[i]->getCurrentStatus() == PlotStatus::INITIALIZING)
|
|
{
|
|
wasInitializing = true;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (wasInitializing)
|
|
{
|
|
// TODO: Implement any logic that should occur when the warmup is complete.
|
|
LS_INFO("PHApp received warmup complete notification from AmperageBudgetManager.");
|
|
#ifdef ENABLE_FEEDBACK_BUZZER
|
|
if (app->feedbackBuzzer_0)
|
|
{
|
|
app->feedbackBuzzer_0->setMode(FeedbackBuzzer::E_BuzzerMode::MODE_FAST_BLINK, 3000);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#define MB_R_APP_STATE_REG 9
|
|
#define MB_R_SYSTEM_CMD_PRINT_RESET 1
|
|
#define MB_R_SYSTEM_CMD_PRINT_REGS 2
|
|
#define MB_R_SYSTEM_CMD_PRINT_MEMORY 5
|
|
#define MB_R_SYSTEM_CMD_PRINT_VFD 6
|
|
|
|
#ifdef ENABLE_PROFILER
|
|
uint32_t PHApp::initialFreeHeap = 0;
|
|
uint64_t PHApp::initialCpuTicks = 0;
|
|
#endif
|
|
|
|
#ifndef LOG_LEVEL
|
|
#define LOG_LEVEL LOG_LEVEL_VERBOSE
|
|
#endif
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Network Servers
|
|
//
|
|
#if defined(ENABLE_WEBSERVER)
|
|
WiFiServer server(80);
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODBUS_TCP
|
|
ModbusServerTCPasync mb;
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Omron E5
|
|
//
|
|
#undef ENABLE_TRUTH_COLLECTOR
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Factory : Instances
|
|
//
|
|
#define ADD_RELAY(relayNum, relayPin, relayKey, relayAddr) \
|
|
relay_##relayNum = new Relay(this, relayPin, relayKey, relayAddr); \
|
|
components.push_back(relay_##relayNum);
|
|
|
|
#ifdef ENABLE_SOLENOID_0
|
|
#define ADD_SOLENOID(solenoidNum, solenoidPin, solenoidKey, solenoidAddr) \
|
|
solenoid_##solenoidNum = new Solenoid(this, solenoidPin, solenoidKey, solenoidAddr); \
|
|
components.push_back(solenoid_##solenoidNum);
|
|
#endif
|
|
|
|
#define ADD_POT(potNum, potPin, potKey, potAddr, ...) \
|
|
pot_##potNum = new POT(this, potPin, potKey, potAddr, ##__VA_ARGS__); \
|
|
components.push_back(pot_##potNum);
|
|
|
|
#define ADD_POS3ANALOG(posNum, switchPin1, switchPin2, switchKey, switchAddr) \
|
|
pos3Analog_##posNum = new Pos3Analog(this, switchPin1, switchPin2, switchKey, switchAddr); \
|
|
components.push_back(pos3Analog_##posNum);
|
|
|
|
#ifdef ENABLE_PID
|
|
#define ADD_PID(pidNum, nameStr, doPin, csPin, clkPin, outPin, key) \
|
|
pidController_##pidNum = new PIDController(key, nameStr, doPin, csPin, clkPin, outPin); \
|
|
components.push_back(pidController_##pidNum);
|
|
#endif
|
|
|
|
Logger *g_logger = nullptr;
|
|
|
|
void PHApp::printRegisters()
|
|
{
|
|
L_VERBOSE(F("--- Entering PHApp::printRegisters ---"));
|
|
|
|
#if ENABLED(HAS_MODBUS_REGISTER_DESCRIPTIONS)
|
|
Log.setShowLevel(false);
|
|
Serial.print("| Name | ID | Address | RW | Function Code | Number Addresses |Register Description| \n");
|
|
Serial.print("|------|----------|----|----|----|----|-------|\n");
|
|
short size = components.size();
|
|
L_VERBOSE(F("PHApp::printRegisters - Processing %d components..."), size);
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
Component *component = components[i];
|
|
if (!component)
|
|
{
|
|
L_ERROR(F("PHApp::printRegisters - Found NULL component at index %d"), i);
|
|
continue;
|
|
}
|
|
L_VERBOSE(F("PHApp::printRegisters - Component %d: ID=%d, Name=%s"), i, component->id, component->name.c_str());
|
|
// if (!(component->nFlags & 1 << OBJECT_NET_CAPS::E_NCAPS_MODBUS)) // <-- Modbus flag check might be different now
|
|
// {
|
|
// continue;
|
|
// }
|
|
// Log.verbose("| %s | %d | %d | %s | %d | %d | %s |\n", // <-- Calls to removed ModbusGateway methods
|
|
// component->name.c_str(),
|
|
// component->id,
|
|
// component->getAddress(),
|
|
// component->getRegisterMode(),
|
|
// component->getFunctionCode(),
|
|
// component->getNumberAddresses(),
|
|
// component->getRegisterDescription().c_str());
|
|
Log.verbose("| %s | %d | - | - | - | - | - |\n", // <-- Simplified output
|
|
component->name.c_str(),
|
|
component->id);
|
|
}
|
|
Log.setShowLevel(true);
|
|
#endif
|
|
L_VERBOSE(F("--- Exiting PHApp::printRegisters ---"));
|
|
}
|
|
short PHApp::reset(short val0, short val1)
|
|
{
|
|
_state = APP_STATE::RESET;
|
|
_error = E_OK;
|
|
|
|
#if defined(ESP32) || defined(ESP8266) // Use ESP.restart() for ESP32 and ESP8266
|
|
ESP.restart();
|
|
#else
|
|
return E_NOT_IMPLEMENTED;
|
|
#endif
|
|
return E_OK;
|
|
}
|
|
short PHApp::list(short val0, short val1)
|
|
{
|
|
uchar s = components.size();
|
|
for (uchar i = 0; i < s; i++)
|
|
{
|
|
Component *component = components[i];
|
|
if (component)
|
|
{
|
|
L_VERBOSE("PHApp::list - %d | %s (ID: %d)", i, component->name.c_str(), component->id);
|
|
}
|
|
else
|
|
{
|
|
Log.warningln("PHApp::list - NULL component at index %d", i);
|
|
}
|
|
}
|
|
return E_OK;
|
|
}
|
|
short PHApp::setup()
|
|
{
|
|
_state = APP_STATE::RESET;
|
|
_error = E_OK;
|
|
#ifdef ENABLE_PROFILER
|
|
if (initialFreeHeap == 0 && initialCpuTicks == 0)
|
|
{
|
|
initialFreeHeap = ESP.getFreeHeap();
|
|
initialCpuTicks = esp_cpu_get_ccount();
|
|
}
|
|
#endif
|
|
|
|
#ifndef DISABLE_SERIAL_LOGGING
|
|
// Serial Setup
|
|
Serial.begin(SERIAL_BAUD_RATE);
|
|
while (!Serial && !Serial.available())
|
|
{
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_LOGGER
|
|
logger_0 = new Logger(this, COMPONENT_KEY_LOGGER, MB_ADDR_LOGGER_0);
|
|
g_logger = logger_0;
|
|
components.push_back(logger_0);
|
|
#endif
|
|
|
|
#ifndef DISABLE_SERIAL_LOGGING
|
|
// Log Setup
|
|
#if defined(ENABLE_LOGGER)
|
|
#if defined(ENABLE_LOGGING_TARGET_PRINT)
|
|
if (logger_0)
|
|
{
|
|
Serial.begin(SERIAL_BAUD_RATE);
|
|
logger_0->addPrintTarget(&Serial);
|
|
}
|
|
#endif
|
|
Log.begin(LOG_LEVEL, logger_0);
|
|
#else
|
|
Log.begin(LOG_LEVEL, &Serial);
|
|
#endif
|
|
Log.setShowLevel(true);
|
|
#else
|
|
// Log Setup (without Serial)
|
|
Log.begin(LOG_LEVEL_WARNING, nullptr);
|
|
Log.setShowLevel(false);
|
|
#endif
|
|
|
|
L_INFO("PHApp::setup() started. Logger configured.");
|
|
|
|
// Application stuff
|
|
#ifdef ENABLE_RUNTIME_STATE
|
|
runtimeState = new RuntimeState(this, COMPONENT_KEY_RUNTIME_STATE);
|
|
components.push_back(runtimeState);
|
|
#endif
|
|
|
|
// Components
|
|
#ifdef ENABLE_SERIAL_BRIDGE
|
|
bridge = new Bridge(this);
|
|
components.push_back(bridge);
|
|
#endif
|
|
#ifndef DISABLE_SERIAL_LOGGING
|
|
com_serial = new SerialMessage(Serial, bridge);
|
|
components.push_back(com_serial);
|
|
#endif
|
|
// Network
|
|
short networkSetupResult = setupNetwork();
|
|
if (networkSetupResult != E_OK)
|
|
{
|
|
L_ERROR("Network setup failed with error code: %d", networkSetupResult);
|
|
}
|
|
|
|
#ifdef ENABLE_SETTINGS
|
|
appSettings = new Settings(this);
|
|
components.push_back(appSettings);
|
|
|
|
#endif
|
|
|
|
#ifdef ENABLE_LOGGER
|
|
#if defined(ENABLE_LOGGING_TARGET_WEBSOCKET) && defined(ENABLE_WEBSERVER) && defined(ENABLE_WEBSOCKET)
|
|
if (logger_0 && webServer)
|
|
{
|
|
logger_0->addWebSocketTarget(webServer);
|
|
L_INFO("WebSocket logging target added.");
|
|
}
|
|
#endif
|
|
#if defined(ENABLE_LOGGING_TARGET_FILE) && defined(ENABLE_LITTLEFS)
|
|
if (logger_0)
|
|
{
|
|
logger_0->addFileTarget();
|
|
L_INFO("File logging target added.");
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
// Components
|
|
#ifdef ENABLE_RELAYS
|
|
#ifdef AUX_RELAY_0
|
|
ADD_RELAY(0, AUX_RELAY_0, COMPONENT_KEY_RELAY_0, MB_ADDR_AUX_5);
|
|
#endif
|
|
#ifdef AUX_RELAY_1
|
|
ADD_RELAY(1, AUX_RELAY_1, COMPONENT_KEY_RELAY_1, MB_ADDR_AUX_6);
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef ENABLE_POT0
|
|
ADD_POT(0, MB_ANALOG_0, COMPONENT_KEY_ANALOG_0, MB_ADDR_AUX_2, POTDampingAlgorithm::DAMPING_EMA, true);
|
|
#endif
|
|
|
|
#ifdef ENABLE_POT1
|
|
ADD_POT(1, MB_ANALOG_1, COMPONENT_KEY_ANALOG_1, MB_ADDR_AUX_3, POTDampingAlgorithm::DAMPING_EMA, true);
|
|
#endif
|
|
|
|
#if (defined(AUX_ANALOG_3POS_SWITCH_0) && (defined(AUX_ANALOG_3POS_SWITCH_1)))
|
|
ADD_POS3ANALOG(0, AUX_ANALOG_3POS_SWITCH_0, AUX_ANALOG_3POS_SWITCH_1, COMPONENT_KEY_MB_ANALOG_3POS_SWITCH_0, MB_ADDR_AUX_3);
|
|
#endif
|
|
|
|
#if (defined(MB_ANALOG_3POS_SWITCH_2) && (defined(MB_ANALOG_3POS_SWITCH_3)))
|
|
// ADD_POS3ANALOG(1, MB_ANALOG_3POS_SWITCH_2, MB_ANALOG_3POS_SWITCH_3, COMPONENT_KEY_MB_ANALOG_3POS_SWITCH_1, MB_R_SWITCH_1); // <-- Temporarily disable
|
|
#endif
|
|
|
|
#ifdef MB_GPIO_MB_MAP_7
|
|
// --- Define configuration for the MB_GPIO group ---
|
|
std::vector<GPIO_PinConfig> gpioConfigs;
|
|
gpioConfigs.reserve(2); // Reserve space for 2 elements
|
|
gpioConfigs.push_back(
|
|
GPIO_PinConfig(
|
|
E_GPIO_7, // pinNumber: The physical pin to manage
|
|
E_GPIO_TYPE_ANALOG_INPUT, // pinType: Treat as analog input
|
|
300, // startAddress: Modbus register address
|
|
E_FN_CODE::FN_READ_HOLD_REGISTER, // type: Map to a Holding Register
|
|
MB_ACCESS_READ_ONLY, // access: Allow Modbus read only
|
|
1000, // opIntervalMs: Update interval in milliseconds
|
|
"GPIO_6", // name: Custom name for this pin
|
|
"GPIO_Group" // group: Group name for this pin
|
|
));
|
|
|
|
gpioConfigs.push_back(
|
|
GPIO_PinConfig(
|
|
E_GPIO_15, // pinNumber: The physical pin to manage
|
|
E_GPIO_TYPE_ANALOG_INPUT, // pinType: Treat as analog input
|
|
301, // startAddress: Modbus register address
|
|
E_FN_CODE::FN_READ_HOLD_REGISTER, // type: Map to a Holding Register
|
|
MB_ACCESS_READ_ONLY, // access: Allow Modbus read only
|
|
1000, // opIntervalMs: Update interval in milliseconds
|
|
"GPIO_15", // name: Custom name for this pin
|
|
"GPIO_Group" // group: Group name for this pin
|
|
));
|
|
const short gpioGroupId = COMPONENT_KEY_GPIO_MAP; // Using defined key
|
|
gpio_0 = new MB_GPIO(this, gpioGroupId, gpioConfigs);
|
|
components.push_back(gpio_0);
|
|
#endif
|
|
|
|
#ifdef PIN_ANALOG_LEVEL_SWITCH_0
|
|
analogLevelSwitch_0 = new AnalogLevelSwitch(
|
|
this, // owner
|
|
PIN_ANALOG_LEVEL_SWITCH_0, // analogPin
|
|
ALS_0_NUM_LEVELS, // numLevels
|
|
ALS_0_ADC_STEP, // levelStep
|
|
ALS_0_ADC_OFFSET, // adcValueOffset
|
|
ID_ANALOG_LEVEL_SWITCH_0, // id
|
|
ALS_0_MB_ADDR // modbusAddress
|
|
);
|
|
if (analogLevelSwitch_0)
|
|
{
|
|
components.push_back(analogLevelSwitch_0);
|
|
L_INFO(F("AnalogLevelSwitch_0 initialized. Pin:%d, ID:%d, Levels:%d, Step:%d, Offset:%d, Smooth:%d(Fixed), Debounce:%d(Fixed), MB:%d"),
|
|
PIN_ANALOG_LEVEL_SWITCH_0, ID_ANALOG_LEVEL_SWITCH_0, ALS_0_NUM_LEVELS,
|
|
ALS_0_ADC_STEP, ALS_0_ADC_OFFSET,
|
|
ALS_SMOOTHING_SIZE,
|
|
ALS_DEBOUNCE_COUNT,
|
|
ALS_0_MB_ADDR);
|
|
}
|
|
else
|
|
{
|
|
L_ERROR(F("AnalogLevelSwitch_0 initialization failed."));
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_STATUS
|
|
statusLight_0 = new StatusLight(this,
|
|
STATUS_WARNING_PIN,
|
|
COMPONENT_KEY_FEEDBACK_0,
|
|
MB_MONITORING_STATUS_FEEDBACK_0); // Keep original address for now, add to config-modbus.h later if needed
|
|
components.push_back(statusLight_0);
|
|
statusLight_1 = new StatusLight(this,
|
|
STATUS_ERROR_PIN,
|
|
COMPONENT_KEY_FEEDBACK_1,
|
|
MB_MONITORING_STATUS_FEEDBACK_1); // Keep original address for now
|
|
components.push_back(statusLight_1);
|
|
#else
|
|
statusLight_0 = NULL;
|
|
statusLight_1 = NULL;
|
|
#endif
|
|
L_INFO("PHApp::setup - Base App::setup() called.");
|
|
|
|
#ifdef PIN_LED_FEEDBACK_0
|
|
ledFeedback_0 = new LEDFeedback(
|
|
this, // owner
|
|
PIN_LED_FEEDBACK_0, // pin
|
|
LED_PIXEL_COUNT_0, // pixelCount
|
|
ID_LED_FEEDBACK_0, // id
|
|
LED_FEEDBACK_0_MB_ADDR // modbusAddress
|
|
);
|
|
if (ledFeedback_0)
|
|
{
|
|
components.push_back(ledFeedback_0);
|
|
L_INFO(F("LEDFeedback_0 initialized. Pin:%d, Count:%d, ID:%d, MB:%d"),
|
|
PIN_LED_FEEDBACK_0, LED_PIXEL_COUNT_0,
|
|
ID_LED_FEEDBACK_0, LED_FEEDBACK_0_MB_ADDR);
|
|
}
|
|
else
|
|
{
|
|
L_ERROR(F("LEDFeedback_0 initialization failed."));
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_JOYSTICK
|
|
joystick_0 = new Joystick(
|
|
this, // owner
|
|
PIN_JOYSTICK_UP, // UP pin
|
|
PIN_JOYSTICK_DOWN, // DOWN pin
|
|
PIN_JOYSTICK_LEFT, // LEFT pin
|
|
PIN_JOYSTICK_RIGHT, // RIGHT pin
|
|
MB_ADDR_AUX_7 // modbusAddress
|
|
);
|
|
if (joystick_0)
|
|
{
|
|
components.push_back(joystick_0);
|
|
}
|
|
else
|
|
{
|
|
L_ERROR(F("Joystick_0 initialization failed."));
|
|
}
|
|
#endif
|
|
|
|
#ifdef PIN_AUX_1
|
|
|
|
pushButton_1 = new PushButton(this, PIN_AUX_1, COMPONENT_KEY_PUSH_BUTTON_0, MB_IREG_ANALOG_0);
|
|
components.push_back(pushButton_1);
|
|
|
|
#endif
|
|
|
|
#ifdef ENABLE_FEEDBACK_3C
|
|
feedback3C_0 = new Feedback3C(this,
|
|
GPIO_PIN_CH4,
|
|
GPIO_PIN_CH5,
|
|
GPIO_PIN_CH6,
|
|
COMPONENT_KEY_FEEDBACK_0,
|
|
MB_ADDR_FEEDBACK_0);
|
|
components.push_back(feedback3C_0);
|
|
#endif
|
|
|
|
#ifdef ENABLE_FEEDBACK_BUZZER
|
|
feedbackBuzzer_0 = new FeedbackBuzzer(this,
|
|
GPIO_PIN_CH3,
|
|
COMPONENT_KEY_FEEDBACK_1,
|
|
MB_ADDR_AUX_9);
|
|
components.push_back(feedbackBuzzer_0);
|
|
#endif
|
|
|
|
// Systems : Hydraulic Cylinder - Loadcell sensor - RS485
|
|
#ifdef ENABLE_SOLENOID_0
|
|
uint32_t solenoid0Addr = appSettings->get("SOLENOID_0_MB_ADDR", (uint32_t)MB_ADDR_SOLENOID_0);
|
|
ADD_SOLENOID(0, PIN_SOLENOID_0, COMPONENT_KEY_SOLENOID_0, solenoid0Addr);
|
|
#endif
|
|
|
|
#ifdef ENABLE_SOLENOID_1
|
|
uint32_t solenoid1Addr = appSettings->get("SOLENOID_1_MB_ADDR", (uint32_t)MB_ADDR_SOLENOID_1);
|
|
ADD_SOLENOID(1, PIN_SOLENOID_1, COMPONENT_KEY_SOLENOID_1, MB_ADDR_SOLENOID_1);
|
|
#endif
|
|
|
|
#ifdef ENABLE_LOADCELL_0
|
|
uint32_t slaveId0 = appSettings->get("LOADCELL_SLAVE_ID_0", (uint32_t)LOADCELL_SLAVE_ID_0);
|
|
loadCell_0 = new Loadcell(this, slaveId0, LOADCELL_READ_INTERVAL, COMPONENT_KEY_LOADCELL_0);
|
|
components.push_back(static_cast<RTU_Base *>(loadCell_0));
|
|
#endif
|
|
|
|
#ifdef ENABLE_LOADCELL_1
|
|
uint32_t slaveId1 = appSettings->get("LOADCELL_SLAVE_ID_1", (uint32_t)LOADCELL_SLAVE_ID_1);
|
|
loadCell_1 = new Loadcell(this, slaveId1, LOADCELL_READ_INTERVAL, COMPONENT_KEY_LOADCELL_1);
|
|
components.push_back(static_cast<RTU_Base *>(loadCell_1));
|
|
#endif
|
|
|
|
#ifdef ENABLE_PRESS_CYLINDER
|
|
pressCylinder_0 = new PressCylinder(this, COMPONENT_KEY_PRESS_CYLINDER_0, MB_ADDR_PRESS_CYLINDER_0, joystick_0, pushButton_1);
|
|
#ifdef ENABLE_LOADCELL_0
|
|
if (loadCell_0)
|
|
pressCylinder_0->addLoadcell(loadCell_0);
|
|
#endif
|
|
#ifdef ENABLE_LOADCELL_1
|
|
if (loadCell_1)
|
|
pressCylinder_0->addLoadcell(loadCell_1);
|
|
#endif
|
|
#ifdef ENABLE_SOLENOID_0
|
|
if (solenoid_0)
|
|
pressCylinder_0->addSolenoid(solenoid_0);
|
|
#endif
|
|
#ifdef ENABLE_SOLENOID_1
|
|
if (solenoid_1)
|
|
pressCylinder_0->addSolenoid(solenoid_1);
|
|
#endif
|
|
components.push_back(pressCylinder_0);
|
|
#endif
|
|
|
|
#ifdef ENABLE_OPERATOR_SWITCH
|
|
operatorSwitch_0 = new OperatorSwitch(this,
|
|
PIN_OPERATOR_SWITCH_STOP,
|
|
PIN_OPERATOR_SWITCH_CYCLE,
|
|
COMPONENT_KEY_OPERATOR_SWITCH,
|
|
MB_ADDR_AUX_8);
|
|
components.push_back(operatorSwitch_0);
|
|
#endif
|
|
|
|
#ifdef ENABLE_MODBUS_MIRROR
|
|
modbusMirror_0 = new ModbusMirror(this, COMPONENT_KEY_MODBUS_MIRROR);
|
|
components.push_back(modbusMirror_0);
|
|
#endif
|
|
|
|
#ifdef ENABLE_IFTTT
|
|
iftttWebhook = new IFTTTWebhook(this, COMPONENT_KEY_IFTTT);
|
|
components.push_back(iftttWebhook);
|
|
#endif
|
|
|
|
#ifdef ENABLE_NETWORK_VALUE_TEST
|
|
L_INFO("PHApp::setup - Initializing NetworkValueTest...");
|
|
networkValueTest = new NetworkValueTest(this, COMPONENT_KEY_TEST_NV, MB_ADDR_AUX_TEST_NV);
|
|
networkValueTest->owner = this;
|
|
components.push_back(networkValueTest);
|
|
#endif
|
|
|
|
#ifdef ENABLE_NETWORK_VALUE_TEST_PB
|
|
L_INFO("PHApp::setup - Initializing NetworkValueTestPB...");
|
|
networkValueTestPB = new NetworkValueTestPB(this, COMPONENT_KEY_TEST_NV_PB, MB_ADDR_AUX_TEST_NV + 10);
|
|
networkValueTestPB->owner = this;
|
|
components.push_back(networkValueTestPB);
|
|
#endif
|
|
|
|
// Motors
|
|
#ifdef ENABLE_SAKO_VFD
|
|
vfd_0 = new SAKO_VFD(this, MB_SAKO_VFD_SLAVE_ID, MB_SAKO_VFD_READ_INTERVAL);
|
|
components.push_back(vfd_0);
|
|
#endif
|
|
|
|
#ifdef ENABLE_DELTA_VFD
|
|
vfd_2 = new DELTA_VFD(this, MB_DELTA_VFD_SLAVE_ID, MB_DELTA_VFD_READ_INTERVAL);
|
|
components.push_back(vfd_2);
|
|
#endif
|
|
|
|
// Temperature
|
|
#ifdef ENABLE_PROFILE_TEMPERATURE
|
|
for (ushort i = 0; i < PROFILE_TEMPERATURE_COUNT; ++i)
|
|
{
|
|
// Assign unique ID: COMPONENT_KEY_PROFILE_START (910) + slot index
|
|
ushort profileComponentId = COMPONENT_KEY_PROFILE_START + i;
|
|
ushort baseAddr = MB_HREG_TEMP_PROFILE_BASE + (i * TEMP_PROFILE_REGISTER_COUNT);
|
|
tempProfiles[i] = new TemperatureProfile(this, i, profileComponentId, baseAddr);
|
|
tempProfiles[i]->setEventsDelegate(this);
|
|
components.push_back(tempProfiles[i]);
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef ENABLE_PROFILE_PRESSURE
|
|
for (ushort i = 0; i < PROFILE_PRESSURE_COUNT; ++i)
|
|
{
|
|
ushort profileComponentId = COMPONENT_KEY_PRESSURE_PROFILE_START + i;
|
|
ushort baseAddr = MB_HREG_PRESSURE_PROFILE_BASE + (i * PRESSURE_PROFILE_REGISTER_COUNT);
|
|
pressureProfiles[i] = new PressureProfile(this, i, profileComponentId, baseAddr);
|
|
pressureProfiles[i]->setEventsDelegate(this);
|
|
components.push_back(pressureProfiles[i]);
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_PROFILE_SIGNAL_PLOT
|
|
for (int i = 0; i < PROFILE_SIGNAL_PLOT_COUNT; i++)
|
|
{
|
|
ushort profileComponentId = COMPONENT_KEY_SIGNAL_PLOT_START + i;
|
|
ushort modbusAddress = MB_HREG_SIGNAL_PLOT_BASE + (i * SIGNAL_PLOT_REGISTER_COUNT);
|
|
signalPlots[i] = new SignalPlot(this, i, profileComponentId, modbusAddress);
|
|
signalPlots[i]->setEventsDelegate(this);
|
|
components.push_back(signalPlots[i]);
|
|
#ifdef ENABLE_MODBUS_TCP
|
|
if (modbusManager != nullptr)
|
|
{
|
|
signalPlots[i]->setModbusTCP(modbusManager);
|
|
}
|
|
else
|
|
{
|
|
L_ERROR("PHApp::setup - SignalPlot %d: modbusManager is nullptr", i);
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_PID
|
|
const int8_t PID2_THERMO_DO = 19; // Example MISO pin
|
|
const int8_t PID2_THERMO_CS = 5; // Example Chip Select pin
|
|
const int8_t PID2_THERMO_CLK = 18; // Example SCK pin
|
|
const int8_t PID2_OUTPUT_PIN = 23; // Example PWM/Output pin
|
|
ADD_PID(0, "PID Temp Controller 2", PID2_THERMO_DO, PID2_THERMO_CS, PID2_THERMO_CLK, PID2_OUTPUT_PIN, COMPONENT_KEY_PID_2);
|
|
#endif
|
|
// RS485
|
|
#ifdef ENABLE_RS485
|
|
rs485 = new RS485(this);
|
|
components.push_back(rs485);
|
|
#endif
|
|
|
|
#ifdef ENABLE_AMPERAGE_BUDGET_MANAGER
|
|
pidManagerAmperage = new AmperageBudgetManager(this, MB_ADDR_AMPERAGE_BUDGET_BASE);
|
|
#if defined(ENABLE_OMRON_E5) && defined(ENABLE_PROFILE_TEMPERATURE)
|
|
pidManagerAmperage->setCanUseCallback(&PHApp_canUsePID);
|
|
pidManagerAmperage->setOnWarmupCompleteCallback(&PHApp_onWarmupComplete);
|
|
#endif
|
|
components.push_back(pidManagerAmperage);
|
|
#endif
|
|
|
|
// Systems : Extruder
|
|
#ifdef ENABLE_EXTRUDER
|
|
extruder_0 = new Extruder(this, vfd_0, nullptr, nullptr, nullptr);
|
|
components.push_back(extruder_0);
|
|
#endif
|
|
|
|
// Systems : Injector
|
|
#ifdef ENABLE_PLUNGER
|
|
plunger_0 = new Plunger(this, vfd_2, joystick_0, pot_0, pot_1);
|
|
components.push_back(plunger_0);
|
|
plunger_0->setEventsDelegate(this);
|
|
#endif
|
|
|
|
#ifdef ENABLE_INFLUXDB
|
|
influxDb_0 = new InfluxDB(this, COMPONENT_KEY_INFLUXDB);
|
|
components.push_back(influxDb_0);
|
|
#endif
|
|
|
|
registerComponents(bridge);
|
|
|
|
#ifdef ENABLE_BRIDGE
|
|
serial_register(bridge);
|
|
#endif
|
|
|
|
App::setup();
|
|
|
|
#ifdef ENABLE_SETTINGS
|
|
loadAppSettings();
|
|
#endif
|
|
|
|
init(0, 0);
|
|
onRun();
|
|
return E_OK;
|
|
}
|
|
|
|
short PHApp::onRun()
|
|
{
|
|
App::onRun();
|
|
|
|
#ifdef ENABLE_MODBUS_TCP
|
|
for (Component *comp : components)
|
|
{
|
|
if (comp && comp->hasNetCapability(OBJECT_NET_CAPS::E_NCAPS_MODBUS))
|
|
{
|
|
comp->mb_tcp_register(modbusManager);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
load(0, 0);
|
|
|
|
#ifdef ENABLE_RELAYS
|
|
#ifdef AUX_RELAY_0
|
|
relay_0->setValue(1);
|
|
#endif
|
|
#endif
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Post initialization
|
|
//
|
|
#ifdef ENABLE_WEBSERVER
|
|
registerRoutes(webServer);
|
|
#endif
|
|
|
|
#if ENABLED(ENABLE_AMPERAGE_BUDGET_MANAGER, ENABLE_OMRON_E5)
|
|
RTU_Base *const *devices = rs485->deviceManager.getDevices();
|
|
int numDevicesInManager = rs485->deviceManager.getMaxDevices();
|
|
for (int i = 0; i < numDevicesInManager; ++i)
|
|
{
|
|
if (devices[i] != nullptr)
|
|
{
|
|
Component *comp = devices[i];
|
|
if (comp == nullptr || comp->type != COMPONENT_TYPE::COMPONENT_TYPE_PID)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!pidManagerAmperage->addManagedDevice(static_cast<OmronE5 *>(comp)))
|
|
{
|
|
L_ERROR("Failed to add OmronE5 device to AmperageBudgetManager");
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if ENABLED(ENABLE_TEMPERATURE_PROFILES, ENABLE_OMRON_E5, ENABLE_MODBUS_TCP)
|
|
tempProfiles[0]->disable();
|
|
if (tempProfiles[0] && rs485)
|
|
{
|
|
RTU_Base *const *devices = rs485->deviceManager.getDevices();
|
|
int numDevicesInManager = rs485->deviceManager.getMaxDevices();
|
|
int targetRegisterIndex = 0; // Dedicated index for _targetRegisters
|
|
for (int i = 0; i < numDevicesInManager; i++)
|
|
{
|
|
Component *comp = devices[i];
|
|
if (comp == nullptr || comp->type != COMPONENT_TYPE::COMPONENT_TYPE_PID)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_NETWORK_VALUE_TEST
|
|
// networkValueTest->init();
|
|
#endif
|
|
|
|
#ifdef ENABLE_RUNTIME_STATE
|
|
if (runtimeState)
|
|
{
|
|
for (auto *component : components)
|
|
{
|
|
if (component && component->hasPersistence(E_PF_ENABLED))
|
|
{
|
|
runtimeState->addManagedComponent(component);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
restoreState();
|
|
// setAllOmronComWrite(true, false);
|
|
return E_OK;
|
|
}
|
|
short PHApp::serial_register(Bridge *bridge)
|
|
{
|
|
/*
|
|
bridge->registerMemberFunction(COMPONENT_KEY_BASE::COMPONENT_KEY_APP, this, C_STR("list"), (ComponentFnPtr)&PHApp::list);
|
|
bridge->registerMemberFunction(COMPONENT_KEY_BASE::COMPONENT_KEY_APP, this, C_STR("reset"), (ComponentFnPtr)&PHApp::reset);
|
|
bridge->registerMemberFunction(COMPONENT_KEY_BASE::COMPONENT_KEY_APP, this, C_STR("printRegisters"), (ComponentFnPtr)&PHApp::printRegisters);
|
|
bridge->registerMemberFunction(COMPONENT_KEY_BASE::COMPONENT_KEY_APP, this, C_STR("load"), (ComponentFnPtr)&PHApp::load);
|
|
*/
|
|
return E_OK;
|
|
}
|
|
short PHApp::onWarning(short code)
|
|
{
|
|
return E_OK;
|
|
}
|
|
short PHApp::onError(short id, short code)
|
|
{
|
|
if (code == getLastError())
|
|
{
|
|
return code;
|
|
}
|
|
Log.error(F("* App:onError - component=%d code=%d" CR), id, code);
|
|
setLastError(code);
|
|
#ifdef ENABLE_STATUS
|
|
if (statusLight_0)
|
|
{
|
|
switch (id)
|
|
{
|
|
case COMPONENT_KEY_PLUNGER:
|
|
{
|
|
|
|
#if defined(ENABLE_PLUNGER)
|
|
case (short)PlungerState::IDLE:
|
|
{
|
|
statusLight_1->set(0, 0);
|
|
break;
|
|
}
|
|
case (short)PlungerState::JAMMED:
|
|
{
|
|
statusLight_1->set(1, 1);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
statusLight_1->set(1, 0);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
return code;
|
|
}
|
|
short PHApp::onStop(short val)
|
|
{
|
|
return E_OK;
|
|
}
|
|
short PHApp::clearError()
|
|
{
|
|
setLastError(E_OK);
|
|
return E_OK;
|
|
}
|
|
|
|
short PHApp::loop()
|
|
{
|
|
App::loop();
|
|
|
|
if (millis() - _last_cycle_loop_ms >= LOOP_CYCLE_INTERVAL)
|
|
{
|
|
_last_cycle_loop_ms = millis();
|
|
loopCycle();
|
|
#ifdef ENABLE_PROFILE_TEMPERATURE
|
|
loopProfiles();
|
|
#endif
|
|
}
|
|
|
|
#ifdef ENABLE_RUNTIME_STATE
|
|
if (runtimeState && (millis() - _last_runtime_save_ms >= RUNTIME_STATE_SAVE_INTERVAL))
|
|
{
|
|
_last_runtime_save_ms = millis();
|
|
updateRuntimeState();
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_INFLUXDB
|
|
testInfluxDB();
|
|
#endif
|
|
|
|
#ifdef ENABLE_WEBSERVER
|
|
loopWeb();
|
|
#endif
|
|
#ifdef ENABLE_MODBUS_TCP
|
|
loopModbus();
|
|
#endif
|
|
return E_OK;
|
|
}
|
|
|
|
short PHApp::loopWeb()
|
|
{
|
|
#if defined(ENABLE_WIFI) && defined(ENABLE_WEBSERVER)
|
|
if (webServer != nullptr)
|
|
{
|
|
webServer->loop();
|
|
}
|
|
#endif
|
|
return E_OK;
|
|
}
|
|
short PHApp::getAppState(short val)
|
|
{
|
|
return _state;
|
|
}
|
|
PHApp::PHApp() : App()
|
|
{
|
|
name = "PHApp";
|
|
webServer = nullptr;
|
|
pidController_0 = nullptr;
|
|
bridge = nullptr;
|
|
com_serial = nullptr;
|
|
pot_0 = nullptr;
|
|
pot_1 = nullptr;
|
|
pot_2 = nullptr;
|
|
statusLight_0 = nullptr;
|
|
statusLight_1 = nullptr;
|
|
relay_0 = nullptr;
|
|
relay_1 = nullptr;
|
|
relay_2 = nullptr;
|
|
relay_3 = nullptr;
|
|
relay_4 = nullptr;
|
|
relay_5 = nullptr;
|
|
relay_6 = nullptr;
|
|
relay_7 = nullptr;
|
|
pos3Analog_0 = nullptr;
|
|
pos3Analog_1 = nullptr;
|
|
modbusManager = nullptr;
|
|
logPrinter = nullptr;
|
|
rs485 = nullptr;
|
|
joystick_0 = nullptr;
|
|
networkValueTest = nullptr;
|
|
networkValueTestPB = nullptr;
|
|
feedback3C_0 = nullptr;
|
|
feedbackBuzzer_0 = nullptr;
|
|
operatorSwitch_0 = nullptr;
|
|
solenoid_0 = nullptr;
|
|
pressCylinder_0 = nullptr;
|
|
appSettings = nullptr;
|
|
#ifdef ENABLE_PROFILE_TEMPERATURE
|
|
// Initialize the array elements to nullptr
|
|
for (int i = 0; i < PROFILE_TEMPERATURE_COUNT; ++i)
|
|
{
|
|
tempProfiles[i] = nullptr;
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_PROFILE_PRESSURE
|
|
for (int i = 0; i < PROFILE_PRESSURE_COUNT; ++i)
|
|
{
|
|
pressureProfiles[i] = nullptr;
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_PROFILE_SIGNAL_PLOT
|
|
for (int i = 0; i < PROFILE_SIGNAL_PLOT_COUNT; ++i)
|
|
{
|
|
signalPlots[i] = nullptr;
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_INFLUXDB
|
|
influxDb_0 = nullptr;
|
|
#endif
|
|
|
|
// WiFi settings are now initialized by WiFiNetworkSettings constructor
|
|
}
|
|
void PHApp::cleanupComponents()
|
|
{
|
|
L_INFO("PHApp::cleanupComponents - Cleaning up %d components...", components.size());
|
|
for (Component *comp : components)
|
|
{
|
|
delete comp;
|
|
}
|
|
components.clear(); // Clear the vector AFTER deleting objects
|
|
|
|
// Nullify pointers that were manually managed or outside the vector
|
|
bridge = nullptr;
|
|
com_serial = nullptr;
|
|
pot_0 = nullptr;
|
|
pot_1 = nullptr;
|
|
pot_2 = nullptr;
|
|
statusLight_0 = nullptr;
|
|
statusLight_1 = nullptr;
|
|
relay_0 = nullptr;
|
|
relay_1 = nullptr;
|
|
relay_2 = nullptr;
|
|
relay_3 = nullptr;
|
|
relay_4 = nullptr;
|
|
relay_5 = nullptr;
|
|
relay_6 = nullptr;
|
|
relay_7 = nullptr;
|
|
pos3Analog_0 = nullptr;
|
|
pos3Analog_1 = nullptr;
|
|
pidController_0 = nullptr;
|
|
modbusManager = nullptr;
|
|
joystick_0 = nullptr;
|
|
networkValueTest = nullptr;
|
|
networkValueTestPB = nullptr;
|
|
feedback3C_0 = nullptr;
|
|
feedbackBuzzer_0 = nullptr;
|
|
operatorSwitch_0 = nullptr;
|
|
solenoid_0 = nullptr;
|
|
pressCylinder_0 = nullptr;
|
|
appSettings = nullptr;
|
|
#ifdef ENABLE_PROFILE_TEMPERATURE
|
|
// Clean up temperature profiles (they were also added to components vector, so already deleted there)
|
|
// Ensure the pointers in the array are nulled
|
|
for (int i = 0; i < PROFILE_TEMPERATURE_COUNT; ++i)
|
|
{
|
|
tempProfiles[i] = nullptr;
|
|
}
|
|
#endif
|
|
#ifdef ENABLE_PROFILE_PRESSURE
|
|
for (int i = 0; i < PROFILE_PRESSURE_COUNT; ++i)
|
|
{
|
|
pressureProfiles[i] = nullptr;
|
|
}
|
|
#endif
|
|
#ifdef ENABLE_WEBSERVER
|
|
if (webServer)
|
|
{
|
|
delete webServer;
|
|
webServer = nullptr;
|
|
}
|
|
#endif
|
|
#ifdef ENABLE_RS485
|
|
rs485 = nullptr; // RS485 interface was in the vector, already deleted
|
|
#endif
|
|
#ifdef ENABLE_MODBUS_MIRROR
|
|
delete modbusMirror_0;
|
|
#endif
|
|
#ifdef ENABLE_INFLUXDB
|
|
delete influxDb_0;
|
|
#endif
|
|
#ifdef ENABLE_IFTTT
|
|
delete iftttWebhook;
|
|
#endif
|
|
L_INFO("PHApp::cleanupComponents - Cleanup complete.");
|
|
}
|
|
PHApp::~PHApp()
|
|
{
|
|
cleanupComponents();
|
|
}
|
|
short PHApp::setAppState(short newState)
|
|
{
|
|
if (_state != newState)
|
|
{
|
|
_state = (APP_STATE)newState;
|
|
}
|
|
return E_OK;
|
|
}
|
|
|
|
#ifdef ENABLE_SETTINGS
|
|
short PHApp::loadAppSettings()
|
|
{
|
|
if (!appSettings->load("/settings.json"))
|
|
{
|
|
L_WARN("Failed to load settings, using defaults.");
|
|
}
|
|
|
|
// Initialize App Settings
|
|
setAppWarmup(appSettings->get("ALWAYS_WARMUP", true), false);
|
|
setAppPidLag(appSettings->get("PID_LAG_COMPENSATION", true), false);
|
|
|
|
#if defined(ENABLE_OMRON_E5) && defined(ENABLE_RS485)
|
|
if (!rs485)
|
|
{
|
|
L_ERROR("RS485 not initialized, cannot apply settings to Omron devices.");
|
|
return E_NOT_INITIALIZED;
|
|
}
|
|
|
|
RTU_Base *const *devices = rs485->deviceManager.getDevices();
|
|
int numDevicesInManager = rs485->deviceManager.getMaxDevices();
|
|
|
|
for (int i = 0; i < numDevicesInManager; ++i)
|
|
{
|
|
RTU_Base *device = devices[i];
|
|
if (device == nullptr || device->type != COMPONENT_TYPE::COMPONENT_TYPE_PID)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bool foundInSettings = false;
|
|
bool shouldBeEnabled = false;
|
|
|
|
for (uint8_t p = 0; p < appSettings->partitionCount; ++p)
|
|
{
|
|
PartitionConfig &partition = appSettings->partitions[p];
|
|
for (uint8_t c = 0; c < partition.controllerCount; ++c)
|
|
{
|
|
ControllerConfig &controller = partition.controllers[c];
|
|
if (device->slaveId == controller.slaveId)
|
|
{
|
|
foundInSettings = true;
|
|
shouldBeEnabled = controller.enabled;
|
|
break;
|
|
}
|
|
}
|
|
if (foundInSettings)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (foundInSettings)
|
|
{
|
|
if (shouldBeEnabled)
|
|
{
|
|
device->enable();
|
|
}
|
|
else
|
|
{
|
|
device->disable();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
device->disable();
|
|
}
|
|
}
|
|
#endif
|
|
return E_OK;
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_PID
|
|
short PHApp::getPid2Register(short offset, short unused)
|
|
{
|
|
if (!pidController_0)
|
|
{
|
|
L_ERROR("Serial Command Error: PID Controller 2 not initialized.");
|
|
return E_INVALID_PARAMETER; // Use defined error code
|
|
}
|
|
if (offset < 0 || offset >= PID_2_REGISTER_COUNT)
|
|
{
|
|
L_ERROR("Serial Command Error: Invalid PID2 offset %d.", offset);
|
|
return E_INVALID_PARAMETER;
|
|
}
|
|
|
|
short address = MB_HREG_PID_2_BASE_ADDRESS + offset;
|
|
short value = pidController_0->mb_tcp_read(address);
|
|
Log.noticeln("PID2 Register Offset %d (Addr %d) Value: %d", offset, address, value);
|
|
// Optionally send value back over serial if needed by the protocol
|
|
return E_OK;
|
|
}
|
|
|
|
short PHApp::setPid2Register(short offset, short value)
|
|
{
|
|
if (!pidController_0)
|
|
{
|
|
L_ERROR("Serial Command Error: PID Controller 2 not initialized.");
|
|
return E_INVALID_PARAMETER; // Use defined error code
|
|
}
|
|
if (offset < 0 || offset >= PID_2_REGISTER_COUNT)
|
|
{
|
|
L_ERROR("Serial Command Error: Invalid PID2 offset %d.", offset);
|
|
return E_INVALID_PARAMETER;
|
|
}
|
|
|
|
short address = MB_HREG_PID_2_BASE_ADDRESS + offset;
|
|
short result = pidController_0->mb_tcp_write(address, value);
|
|
|
|
if (result == E_OK)
|
|
{
|
|
Log.noticeln("PID2 Register Offset %d (Addr %d) set to: %d", offset, address, value);
|
|
}
|
|
else
|
|
{
|
|
L_ERROR("PID2 Register Offset %d (Addr %d) failed to set to %d. Error: %d", offset, address, value, result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#endif
|
|
|
|
short PHApp::onMessage(int id, E_CALLS verb, E_MessageFlags flags, void *user, Component *src)
|
|
{
|
|
#if defined(ENABLE_INFLUXDB) && defined(ENABLE_OMRON_E5)
|
|
// Intercept messages from Omron PID controllers to log their data.
|
|
if (verb == E_CALLS::EC_USER && src && src->type == COMPONENT_TYPE::COMPONENT_TYPE_PID)
|
|
{
|
|
// Log.infoln("PHApp::onMessage - OmronE5");
|
|
// We know it's an OmronE5, so we can safely cast it.
|
|
OmronE5 *omron = static_cast<OmronE5 *>(src);
|
|
uint16_t sp, pv;
|
|
|
|
// Ensure both SP and PV are valid before logging
|
|
if (omron->getSP(sp) && omron->getPV(pv))
|
|
{
|
|
Point p("pid_controller"); // Create a local Point
|
|
p.addTag("slaveId", String(omron->slaveId));
|
|
p.addField("sp", sp);
|
|
p.addField("pv", pv);
|
|
p.addField("isHeating", omron->isHeating());
|
|
influxDb_0->write(p);
|
|
}
|
|
else
|
|
{
|
|
Log.errorln("PHApp::onMessage - OmronE5 - SP or PV not valid");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if ENABLED(ENABLE_RS485, ENABLE_WEBSERVER, ENABLE_WEBSOCKET)
|
|
if (verb == E_CALLS::EC_USER && user != nullptr && webServer != nullptr)
|
|
{
|
|
MB_UpdateData *update = static_cast<MB_UpdateData *>(user);
|
|
return webServer->onMessage(id, E_CALLS::EC_USER, E_MessageFlags::E_MF_NONE, user, src);
|
|
}
|
|
if (verb == E_CALLS::EC_PROTOBUF_UPDATE && user != nullptr)
|
|
{
|
|
PB_UpdateData *update = static_cast<PB_UpdateData *>(user);
|
|
return webServer->onMessage(id, E_CALLS::EC_PROTOBUF_UPDATE, E_MessageFlags::E_MF_NONE, user, src);
|
|
}
|
|
#endif
|
|
if (verb == E_CALLS::EC_DISPATCH && user != nullptr)
|
|
{
|
|
JsonDocument *doc = static_cast<JsonDocument *>(user);
|
|
this->broadcast(BROADCAST_ERROR_MESSAGE, *doc);
|
|
return E_OK;
|
|
}
|
|
return App::onMessage(id, verb, flags, user, src);
|
|
}
|
|
|
|
/**
|
|
* @brief Retrieves a component by its ID.
|
|
*
|
|
* @param id The ID of the component to retrieve.
|
|
* @return A pointer to the component with the specified ID, or nullptr if not found.
|
|
* @note Top-Level PHApp cant be part of components vector, so we need to handle it separately.
|
|
*/
|
|
Component *PHApp::byId(ushort id)
|
|
{
|
|
Component *comp = App::byId(id);
|
|
if (comp)
|
|
{
|
|
return comp;
|
|
}
|
|
else if (id == COMPONENT_KEY_APP)
|
|
{
|
|
return this;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
short PHApp::init(short arg1, short arg2)
|
|
{
|
|
|
|
#if ENABLED(ENABLE_AMPERAGE_BUDGET_MANAGER, ENABLE_OMRON_E5)
|
|
RTU_Base *const *devices = rs485->deviceManager.getDevices();
|
|
int numDevicesInManager = rs485->deviceManager.getMaxDevices();
|
|
for (int i = 0; i < numDevicesInManager; ++i)
|
|
{
|
|
if (devices[i] != nullptr)
|
|
{
|
|
Component *comp = devices[i];
|
|
if (comp == nullptr || comp->type != COMPONENT_TYPE::COMPONENT_TYPE_PID)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
OmronE5 *omronE5 = static_cast<OmronE5 *>(comp);
|
|
omronE5->stop();
|
|
omronE5->setSP(0);
|
|
}
|
|
}
|
|
#endif
|
|
return E_OK;
|
|
}
|
|
|
|
#ifdef ENABLE_PROFILE_SIGNAL_PLOT
|
|
void PHApp::startSignalPlot(short slotId)
|
|
{
|
|
if (slotId >= 0 && slotId < PROFILE_SIGNAL_PLOT_COUNT && signalPlots[slotId] != nullptr)
|
|
{
|
|
L_INFO("PHApp: Starting SignalPlot in slot %d (triggered by TemperatureProfile).", slotId);
|
|
signalPlots[slotId]->start();
|
|
}
|
|
else
|
|
{
|
|
Log.warningln("PHApp: Could not start SignalPlot. Invalid slotId %d or plot not initialized.", slotId);
|
|
}
|
|
}
|
|
|
|
void PHApp::stopSignalPlot(short slotId)
|
|
{
|
|
if (slotId >= 0 && slotId < PROFILE_SIGNAL_PLOT_COUNT && signalPlots[slotId] != nullptr)
|
|
{
|
|
L_INFO("PHApp: Stopping SignalPlot in slot %d (triggered by TemperatureProfile).", slotId);
|
|
signalPlots[slotId]->stop();
|
|
}
|
|
else
|
|
{
|
|
Log.warningln("PHApp: Could not stop SignalPlot. Invalid slotId %d or plot not initialized.", slotId);
|
|
}
|
|
}
|
|
|
|
void PHApp::enableSignalPlot(short slotId, bool enable)
|
|
{
|
|
if (slotId >= 0 && slotId < PROFILE_SIGNAL_PLOT_COUNT && signalPlots[slotId] != nullptr)
|
|
{
|
|
if (enable)
|
|
{
|
|
L_INFO("PHApp: Enabling SignalPlot in slot %d (triggered by TemperatureProfile).", slotId);
|
|
signalPlots[slotId]->enable(enable);
|
|
}
|
|
else
|
|
{
|
|
L_INFO("PHApp: Disabling SignalPlot in slot %d (triggered by TemperatureProfile).", slotId);
|
|
signalPlots[slotId]->disable();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log.warningln("PHApp: Could not enable/disable SignalPlot. Invalid slotId %d or plot not initialized.", slotId);
|
|
}
|
|
}
|
|
|
|
void PHApp::pauseSignalPlot(short slotId)
|
|
{
|
|
if (slotId >= 0 && slotId < PROFILE_SIGNAL_PLOT_COUNT && signalPlots[slotId] != nullptr)
|
|
{
|
|
L_INFO("PHApp: Pausing SignalPlot in slot %d (triggered by TemperatureProfile).", slotId);
|
|
signalPlots[slotId]->pause();
|
|
}
|
|
else
|
|
{
|
|
Log.warningln("PHApp: Could not pause SignalPlot. Invalid slotId %d or plot not initialized.", slotId);
|
|
}
|
|
}
|
|
|
|
void PHApp::resumeSignalPlot(short slotId)
|
|
{
|
|
if (slotId >= 0 && slotId < PROFILE_SIGNAL_PLOT_COUNT && signalPlots[slotId] != nullptr)
|
|
{
|
|
L_INFO("PHApp: Resuming SignalPlot in slot %d (triggered by TemperatureProfile).", slotId);
|
|
signalPlots[slotId]->resume();
|
|
}
|
|
else
|
|
{
|
|
Log.warningln("PHApp: Could not resume SignalPlot. Invalid slotId %d or plot not initialized.", slotId);
|
|
}
|
|
}
|
|
|
|
#endif // ENABLE_PROFILE_SIGNAL_PLOT
|
|
|
|
#ifdef ENABLE_INFLUXDB
|
|
void PHApp::testInfluxDB()
|
|
{
|
|
if (influxDb_0 && (millis() - _last_influx_test_ms >= 5000))
|
|
{
|
|
_last_influx_test_ms = millis();
|
|
|
|
Point p("test_data"); // Create a local Point
|
|
p.addTag("device", "ESP32-S3");
|
|
p.addTag("source", "1");
|
|
|
|
p.addField("random_value", random(100));
|
|
p.addField("random_value2", random(100));
|
|
p.addField("on-off-1", random(2));
|
|
p.addField("on-off-2", random(2));
|
|
|
|
if (influxDb_0->write(p) == E_OK)
|
|
{
|
|
Log.infoln("PHApp: Sent test data to InfluxDB.");
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
bool PHApp::isSlave() const
|
|
{
|
|
return _is_slave;
|
|
}
|
|
|
|
short PHApp::setSlave(bool value, bool sync)
|
|
{
|
|
_is_slave = value;
|
|
|
|
bool enabled = !value; // When slave=true, disable components; when slave=false, enable them
|
|
|
|
#if defined(ENABLE_RS485) && defined(ENABLE_OMRON_E5)
|
|
if (rs485)
|
|
{
|
|
RTU_Base *const *devices = rs485->deviceManager.getDevices();
|
|
int numDevices = rs485->deviceManager.getMaxDevices();
|
|
for (int i = 0; i < numDevices; ++i)
|
|
{
|
|
if (devices[i] != nullptr && devices[i]->type == COMPONENT_TYPE::COMPONENT_TYPE_PID)
|
|
{
|
|
OmronE5 *omron = static_cast<OmronE5 *>(devices[i]);
|
|
if (omron)
|
|
{
|
|
omron->enable(enabled);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_AMPERAGE_BUDGET_MANAGER
|
|
if (pidManagerAmperage)
|
|
{
|
|
pidManagerAmperage->enable(enabled);
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_LOADCELL_0
|
|
if (loadCell_0)
|
|
{
|
|
loadCell_0->enable(enabled);
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_LOADCELL_1
|
|
if (loadCell_1)
|
|
{
|
|
loadCell_1->enable(enabled);
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_PROFILE_TEMPERATURE
|
|
for (int i = 0; i < PROFILE_TEMPERATURE_COUNT; ++i)
|
|
{
|
|
if (tempProfiles[i] != nullptr)
|
|
{
|
|
tempProfiles[i]->enable(enabled);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (sync)
|
|
{
|
|
updateRuntimeState();
|
|
}
|
|
return E_OK;
|
|
}
|
|
|
|
bool PHApp::getAllOmronStop() const
|
|
{
|
|
return _all_omron_stop;
|
|
}
|
|
|
|
short PHApp::setAllOmronStop(bool value, bool sync)
|
|
{
|
|
_all_omron_stop = value;
|
|
|
|
#if defined(ENABLE_RS485) && defined(ENABLE_OMRON_E5)
|
|
if (!rs485)
|
|
{
|
|
L_INFO("PHApp: No RS485 found");
|
|
return E_OK;
|
|
}
|
|
|
|
RTU_Base *const *devices = rs485->deviceManager.getDevices();
|
|
int numDevices = rs485->deviceManager.getMaxDevices();
|
|
for (int i = 0; i < numDevices; ++i)
|
|
{
|
|
if (devices[i] != nullptr && devices[i]->type == COMPONENT_TYPE::COMPONENT_TYPE_PID)
|
|
{
|
|
OmronE5 *omron = static_cast<OmronE5 *>(devices[i]);
|
|
if (omron && omron->enabled())
|
|
{
|
|
if (value)
|
|
{
|
|
omron->stop();
|
|
}
|
|
else
|
|
{
|
|
omron->run();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
updateRuntimeState();
|
|
|
|
#endif
|
|
|
|
return E_OK;
|
|
}
|
|
|
|
bool PHApp::getAllOmronComWrite() const
|
|
{
|
|
return _all_omron_com_write;
|
|
}
|
|
|
|
short PHApp::setAllOmronComWrite(bool value, bool sync)
|
|
{
|
|
_all_omron_com_write = value;
|
|
|
|
#if defined(ENABLE_RS485) && defined(ENABLE_OMRON_E5)
|
|
if (!rs485)
|
|
{
|
|
L_INFO("PHApp: No RS485 found");
|
|
return E_OK;
|
|
}
|
|
|
|
RTU_Base *const *devices = rs485->deviceManager.getDevices();
|
|
int numDevices = rs485->deviceManager.getMaxDevices();
|
|
for (int i = 0; i < numDevices; ++i)
|
|
{
|
|
if (devices[i] != nullptr && devices[i]->type == COMPONENT_TYPE::COMPONENT_TYPE_PID)
|
|
{
|
|
OmronE5 *omron = static_cast<OmronE5 *>(devices[i]);
|
|
if (omron && omron->enabled())
|
|
{
|
|
omron->setCommsWriting(value);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
if (sync)
|
|
{
|
|
updateRuntimeState();
|
|
}
|
|
return E_OK;
|
|
}
|
|
|
|
short PHApp::setAppWarmup(bool value, bool sync)
|
|
{
|
|
_app_warmup = value;
|
|
if (appSettings)
|
|
{
|
|
appSettings->set("heating", "ALWAYS_WARMUP", value);
|
|
appSettings->save();
|
|
}
|
|
if (sync)
|
|
{
|
|
updateRuntimeState();
|
|
}
|
|
return E_OK;
|
|
}
|
|
|
|
short PHApp::setAppPidLag(bool value, bool sync)
|
|
{
|
|
_app_pid_lag = value;
|
|
if (appSettings)
|
|
{
|
|
appSettings->set("heating", "PID_LAG_COMPENSATION", value);
|
|
appSettings->save();
|
|
}
|
|
if (sync)
|
|
{
|
|
updateRuntimeState();
|
|
}
|
|
return E_OK;
|
|
}
|
|
|
|
void app_main()
|
|
{
|
|
// Arduino will still call setup()/loop()
|
|
} |