+ Used as on/off bands in Independent mode: heatTargetH, heatTargetL, coolTargetH, coolTargetL. +
+ Uses heatTargetH/L and coolTargetH/L from Advanced Settings. Predictive On: cycle 1 measures thermostat on-time to beerSet+heatTargetH; later cycles heat for the learned seconds; if the beer peak is low, a same-cycle top-up runs after the peak falls (slow filter, 3 s). Over/under next-cycle correction uses full slope-based seconds; changes under 10 s are ignored (deadband). Heater always cuts if beer reaches the upper target. Cooler is unchanged. +
++ Uses heatTargetH/L and coolTargetH/L from Advanced Settings. Predictive On: cycle 1 measures thermostat on-time to beerSet+heatTargetH; later cycles heat for the learned seconds; if the beer peak is low, a same-cycle top-up runs after the peak falls (slow filter, 3 s). Over/under next-cycle correction uses full slope-based seconds; changes under 10 s are ignored (deadband). Heater always cuts if beer reaches the upper target. Cooler is unchanged. +
+This documentation is only valid for the AsyncMqttClient version in this repo/directory
** - ------ - -#### 1. [Getting started](1.-Getting-started.md) + +----- + +#### 1. [Getting started](1.-Getting-started.md) #### 2. [API reference](2.-API-reference.md) #### 3. [Memory management](3.-Memory-management.md) #### 4. [Limitations and known issues](4.-Limitations-and-known-issues.md) diff --git a/lib/async-mqtt-client-master/keywords.txt b/lib/async-mqtt-client-master/keywords.txt index 98c4bf318..29d74d496 100644 --- a/lib/async-mqtt-client-master/keywords.txt +++ b/lib/async-mqtt-client-master/keywords.txt @@ -1,47 +1,47 @@ -####################################### -# Datatypes (KEYWORD1) -####################################### - -AsyncMqttClient KEYWORD1 -AsyncMqttClientDisconnectReason KEYWORD1 -AsyncMqttClientMessageProperties KEYWORD1 - -####################################### -# Methods and Functions (KEYWORD2) -####################################### - -setKeepAlive KEYWORD2 -setClientId KEYWORD2 -setCleanSession KEYWORD2 -setMaxTopicLength KEYWORD2 -setCredentials KEYWORD2 -setWill KEYWORD2 -setServer KEYWORD2 -setSecure KEYWORD2 -addServerFingerprint KEYWORD2 - -onConnect KEYWORD2 -onDisconnect KEYWORD2 -onSubscribe KEYWORD2 -onUnsubscribe KEYWORD2 -onMessage KEYWORD2 -onPublish KEYWORD2 - -connected KEYWORD2 -connect KEYWORD2 -disconnect KEYWORD2 -subscribe KEYWORD2 -unsubscribe KEYWORD2 -publish KEYWORD2 - -####################################### -# Constants (LITERAL1) -####################################### - -TCP_DISCONNECTED LITERAL1 - -MQTT_UNACCEPTABLE_PROTOCOL_VERSION LITERAL1 -MQTT_IDENTIFIER_REJECTED LITERAL1 -MQTT_SERVER_UNAVAILABLE LITERAL1 -MQTT_MALFORMED_CREDENTIALS LITERAL1 -MQTT_NOT_AUTHORIZED LITERAL1 +####################################### +# Datatypes (KEYWORD1) +####################################### + +AsyncMqttClient KEYWORD1 +AsyncMqttClientDisconnectReason KEYWORD1 +AsyncMqttClientMessageProperties KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +setKeepAlive KEYWORD2 +setClientId KEYWORD2 +setCleanSession KEYWORD2 +setMaxTopicLength KEYWORD2 +setCredentials KEYWORD2 +setWill KEYWORD2 +setServer KEYWORD2 +setSecure KEYWORD2 +addServerFingerprint KEYWORD2 + +onConnect KEYWORD2 +onDisconnect KEYWORD2 +onSubscribe KEYWORD2 +onUnsubscribe KEYWORD2 +onMessage KEYWORD2 +onPublish KEYWORD2 + +connected KEYWORD2 +connect KEYWORD2 +disconnect KEYWORD2 +subscribe KEYWORD2 +unsubscribe KEYWORD2 +publish KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +TCP_DISCONNECTED LITERAL1 + +MQTT_UNACCEPTABLE_PROTOCOL_VERSION LITERAL1 +MQTT_IDENTIFIER_REJECTED LITERAL1 +MQTT_SERVER_UNAVAILABLE LITERAL1 +MQTT_MALFORMED_CREDENTIALS LITERAL1 +MQTT_NOT_AUTHORIZED LITERAL1 diff --git a/lib/async-mqtt-client-master/src/AsyncMqttClient/Callbacks.hpp b/lib/async-mqtt-client-master/src/AsyncMqttClient/Callbacks.hpp index 2a4a09f0c..7c3d63dd0 100644 --- a/lib/async-mqtt-client-master/src/AsyncMqttClient/Callbacks.hpp +++ b/lib/async-mqtt-client-master/src/AsyncMqttClient/Callbacks.hpp @@ -1,28 +1,28 @@ -#pragma once - -#include");
+ html += body;
+ html += F("");
+ tempControl.appendIndependentHeatDebugForm(html);
+ html += F("plain text · /fs · home
"); + html += F(""); + request->send(200, "text/html", html); + }); + #if DebugSharedDisplay webServer->on("/dbdisplay",[](AsyncWebServerRequest *request){ String info; @@ -2147,6 +2226,11 @@ void setup(void){ uint32_t _periodicReportTime; #define PeriodicalReportTime 5 void loop(void){ + // Required by ESPAsyncWebServer: free slots/buffers for disconnected WS + // clients. Without this, dead clients (browser tab closed, WiFi blip) keep + // their queued textAll() buffers forever and show up as a slow heap leak. + ws.cleanupClients(); + //{brewpi #if BREWPI_SIMULATE simulateLoop(); @@ -2225,6 +2309,8 @@ void loop(void){ WiFiSetup.stayConnected(); } + duckDNSUpdater.update(); + if(_systemState ==SystemStateRestartPending){ _time=millis(); _systemState =SystemStateWaitRestart; diff --git a/src/BrewPiProxy.cpp b/src/BrewPiProxy.cpp index fc75bad06..03ec1eb98 100755 --- a/src/BrewPiProxy.cpp +++ b/src/BrewPiProxy.cpp @@ -96,6 +96,10 @@ float BrewPiProxy::getFridgeSet(void){ return t; } +bool BrewPiProxy::isIndependentPidOn(void){ + return tempControl.isIndependentPidOn(); +} + char BrewPiProxy::getUnit(void){ return tempControl.cc.tempFormat; } diff --git a/src/BrewPiProxy.h b/src/BrewPiProxy.h index 8ce77fad8..60a76cf5e 100755 --- a/src/BrewPiProxy.h +++ b/src/BrewPiProxy.h @@ -24,6 +24,7 @@ class BrewPiProxy{ float getBeerSet(void); float getFridgeTemp(void); float getFridgeSet(void); + bool isIndependentPidOn(void); char getUnit(void); float getMinSetTemp(void); float getMaxSetTemp(void); diff --git a/src/Config.h b/src/Config.h index 2755b0f56..99163bb86 100755 --- a/src/Config.h +++ b/src/Config.h @@ -317,7 +317,7 @@ #define FS_EEPROM true #endif -#define BPL_VERSION "4.5" +#define BPL_VERSION "5.0" #ifndef MORE_PINS_CONFIGURATION diff --git a/src/DisplayLcd.cpp b/src/DisplayLcd.cpp index c23ca4ec5..563ccb9d2 100755 --- a/src/DisplayLcd.cpp +++ b/src/DisplayLcd.cpp @@ -62,6 +62,10 @@ static const char STR_ing_for[] PROGMEM = "ing for"; static const char STR_Wait_to_[] PROGMEM = "Wait to "; static const char STR__time_left[] PROGMEM = " time left"; static const char STR_empty_string[] PROGMEM = ""; +// Local-only "both active" display state for independent mode. `state` itself must +// stay within 0-9 for CSV log backward compatibility (see LogFormatter), so this +// sentinel is only ever used inside DisplayLcd and never stored/logged. +#define LCD_STATE_HEATING_AND_COOLING 0xFE void LcdDisplay::init(void){ #ifdef BREWPI_IIC_LCD @@ -159,6 +163,10 @@ void LcdDisplay::printTemperature(temperature temp){ //print the stationary text on the lcd. void LcdDisplay::printStationaryText(void){ printAt_P(0, 0, PSTR("Mode")); + // Row labels stay "Beer"/"Fridge" in every mode, including independent (the + // rows still show cs.beerSetting/cs.fridgeSetting there, see + // TempControl::updateIndependentState()) - only the mode name and the state + // line change for independent mode. printAt_P(0, 1, STR_Beer_); printAt_P(0, 2, (flags & LCD_FLAG_DISPLAY_ROOM) ? PSTR("Room ") : STR_Fridge_); printDegreeUnit(18, 1); @@ -200,6 +208,9 @@ void LcdDisplay::printMode(void){ lcd.print_P(STR_Beer_); lcd.print_P(PSTR("Profile")); break; + case MODE_INDEPENDENT: + lcd.print_P(PSTR("Independent")); + break; case MODE_OFF: lcd.print_P(PSTR("Off")); break; @@ -217,6 +228,14 @@ void LcdDisplay::printMode(void){ void LcdDisplay::printState(void){ uint16_t time = UINT16_MAX; // init to max uint8_t state = tempControl.getDisplayState(); + // Independent mode can have the heater and cooler active at the same time; + // `state` alone can only report one of them (see TempControl::updateIndependentState() + // and getDisplayState()), so use the real per-actuator flags to detect that case here + // and show a short "H+C for" line, without touching the shared state enum. + if(!tempControl.isDoorOpen() && tempControl.getMode() == MODE_INDEPENDENT + && tempControl.isIndependentHeaterOn() && tempControl.isIndependentCoolerOn()){ + state = LCD_STATE_HEATING_AND_COOLING; + } if(state != stateOnDisplay){ //only print static text when state has changed stateOnDisplay = state; // Reprint state and clear rest of the line @@ -246,6 +265,11 @@ void LcdDisplay::printState(void){ part1 = STR_Heat; part2 = STR_ing_for; break; + case LCD_STATE_HEATING_AND_COOLING: + // Keep short: "Heat+Cool"+"ing for" overflows a 20-char line once the timer is printed. + part1 = PSTR("H+C for"); + part2 = STR_empty_string; + break; case COOLING_MIN_TIME: part1 = STR_Cool; part2 = STR__time_left; @@ -272,7 +296,7 @@ void LcdDisplay::printState(void){ if(state==IDLE){ time = min(tempControl.timeSinceCooling(), tempControl.timeSinceHeating()); } - else if(state==COOLING || state==HEATING){ + else if(state==COOLING || state==HEATING || state==LCD_STATE_HEATING_AND_COOLING){ time = sinceIdleTime; } else if(state==COOLING_MIN_TIME){ diff --git a/src/DuckDNSUpdater.cpp b/src/DuckDNSUpdater.cpp new file mode 100644 index 000000000..eb0c405c3 --- /dev/null +++ b/src/DuckDNSUpdater.cpp @@ -0,0 +1,61 @@ +#ifdef ESP8266 +#include