Fix Threshold and improve program and UI features

This commit is contained in:
2026-07-10 20:00:31 -05:00
parent c6213e1752
commit e56543e2fd
6 changed files with 137 additions and 18 deletions

View File

@@ -30,6 +30,8 @@ bool ledInverted = DEFAULT_LED_INVERTED;
bool pumpEnabled = false;
ProgramMode programMode = PROGRAM_OFF;
float programTargetC = PROGRAM_DEFAULT_TARGET_C;
float programToleranceC = PROGRAM_TOLERANCE_C;
uint16_t programMaxRuntimeMinutes = PROGRAM_DEFAULT_MAX_RUNTIME_MINUTES;
String updateUrl = DEFAULT_UPDATE_URL;
String networkHostname;
bool networkDhcp = true;
@@ -112,6 +114,10 @@ void loadSettings() {
programMode = (ProgramMode)constrain(prefs.getUChar("progMode", PROGRAM_OFF), PROGRAM_OFF, PROGRAM_WARM);
programTargetC = prefs.getFloat("progTargetC", PROGRAM_DEFAULT_TARGET_C);
if (isnan(programTargetC) || programTargetC < -40.0f || programTargetC > 85.0f) programTargetC = PROGRAM_DEFAULT_TARGET_C;
programToleranceC = prefs.getFloat("progTolC", PROGRAM_TOLERANCE_C);
if (isnan(programToleranceC) || programToleranceC < 0.0f || programToleranceC > 20.0f) programToleranceC = PROGRAM_TOLERANCE_C;
programToleranceC = roundf(programToleranceC * 100.0f) / 100.0f;
programMaxRuntimeMinutes = (uint16_t)constrain((int)prefs.getUShort("progMaxMin", PROGRAM_DEFAULT_MAX_RUNTIME_MINUTES), 1, 1440);
updateUrl = prefString("updateUrl", DEFAULT_UPDATE_URL);
networkHostname = prefString("netHost", "");
networkDhcp = prefs.getBool("netDhcp", true);

View File

@@ -10,6 +10,8 @@ static bool temperatureStarted = false;
static bool temperaturePending = false;
static float lastTemperatureC = NAN;
static uint32_t temperatureRequestMs = 0;
static uint32_t programPumpRunStartedMs = 0;
static uint32_t programModeSseUntilMs = 0;
static void beginTemperatureSensor() {
if (temperatureStarted) return;
@@ -98,7 +100,8 @@ static String programJsonFields() {
String json = "\"mode\":\"" + programModeName() + "\"";
json += ",\"targetTemperatureC\":" + String(programTargetC, 2);
json += ",\"targetTemperatureF\":" + String((programTargetC * 9.0f / 5.0f) + 32.0f, 2);
json += ",\"toleranceC\":" + String(PROGRAM_TOLERANCE_C, 2);
json += ",\"toleranceC\":" + String(programToleranceC, 2);
json += ",\"maxRuntimeMinutes\":" + String(programMaxRuntimeMinutes);
json += ",\"pumpEnabled\":" + String(pumpEnabled ? "true" : "false");
json += ",\"sensorConnected\":";
json += isnan(temperatureC) ? "false" : "true";
@@ -115,22 +118,49 @@ void updateProgram() {
lastProgramMs = now;
if (programMode == PROGRAM_OFF) {
programPumpRunStartedMs = 0;
setPumpEnabled(false, " by program");
return;
}
float temperatureC = currentTemperatureC();
if (isnan(temperatureC)) {
programPumpRunStartedMs = 0;
setPumpEnabled(false, " by program sensor fault");
return;
}
float roundedTemperatureC = roundf(temperatureC * 100.0f) / 100.0f;
bool shouldRun = false;
if (programMode == PROGRAM_COOL) {
setPumpEnabled(roundedTemperatureC >= programTargetC + PROGRAM_TOLERANCE_C, " by cool program");
shouldRun = pumpEnabled
? roundedTemperatureC > programTargetC
: roundedTemperatureC >= programTargetC + programToleranceC;
} else if (programMode == PROGRAM_WARM) {
setPumpEnabled(roundedTemperatureC <= programTargetC - PROGRAM_TOLERANCE_C, " by warm program");
shouldRun = pumpEnabled
? roundedTemperatureC < programTargetC
: roundedTemperatureC <= programTargetC - programToleranceC;
}
if (!shouldRun) {
programPumpRunStartedMs = 0;
setPumpEnabled(false, " by " + programModeName() + " program");
return;
}
if (!pumpEnabled || programPumpRunStartedMs == 0) programPumpRunStartedMs = now;
uint32_t maxRuntimeMs = (uint32_t)programMaxRuntimeMinutes * 60000UL;
if (now - programPumpRunStartedMs >= maxRuntimeMs) {
programMode = PROGRAM_OFF;
prefs.putUChar("progMode", programMode);
programPumpRunStartedMs = 0;
programModeSseUntilMs = now + 15000UL;
setPumpEnabled(false, " by program max runtime");
appendLog(LOG_WARN, "Program switched off after pump ran for " + String(programMaxRuntimeMinutes) + " minutes without reaching target");
return;
}
setPumpEnabled(true, " by " + programModeName() + " program");
}
void handlePing() {
@@ -170,10 +200,17 @@ void handleProgram() {
programMode = parseProgramMode(jsonStringValue(body, "mode", programModeName()), programMode);
programTargetC = constrain(jsonFloatValue(body, "targetTemperatureC", programTargetC), -40.0f, 85.0f);
programTargetC = roundf(programTargetC * 100.0f) / 100.0f;
programToleranceC = constrain(jsonFloatValue(body, "toleranceC", programToleranceC), 0.0f, 20.0f);
programToleranceC = roundf(programToleranceC * 100.0f) / 100.0f;
programMaxRuntimeMinutes = (uint16_t)constrain(jsonIntValue(body, "maxRuntimeMinutes", programMaxRuntimeMinutes), 1, 1440);
programPumpRunStartedMs = 0;
programModeSseUntilMs = 0;
prefs.putUChar("progMode", programMode);
prefs.putFloat("progTargetC", programTargetC);
prefs.putFloat("progTolC", programToleranceC);
prefs.putUShort("progMaxMin", programMaxRuntimeMinutes);
updateProgram();
appendLog(LOG_INFO, "Program set to " + programModeName() + " target " + String(programTargetC, 2) + " C");
appendLog(LOG_INFO, "Program set to " + programModeName() + " target " + String(programTargetC, 2) + " C threshold " + String(programToleranceC, 2) + " C max runtime " + String(programMaxRuntimeMinutes) + " min");
}
sendJson(200, jsonOk(programJsonFields()));
}
@@ -190,6 +227,10 @@ void handleTemperatureEvents() {
payload += "data: {" + temperatureJsonFields() + "}\n\n";
payload += "event: pump\n";
payload += "data: {" + pumpJsonFields() + "}\n\n";
if ((int32_t)(millis() - programModeSseUntilMs) < 0) {
payload += "event: program\n";
payload += "data: {\"mode\":\"" + programModeName() + "\",\"pumpEnabled\":" + String(pumpEnabled ? "true" : "false") + ",\"reason\":\"maxRuntime\"}\n\n";
}
server.sendHeader("Cache-Control", "no-store");
server.sendHeader("Connection", "close");
server.send(200, "text/event-stream", payload);