Browse Source

Thermostat upgrade (#1711)

* Add "Enable Thermostat" switch

* Add heater/cooler thermostat mode
rules-rpn
DmitryBlinov 5 years ago
committed by Max Prokhorov
parent
commit
bb33dfd102
5 changed files with 2742 additions and 2661 deletions
  1. BIN
      code/espurna/data/index.thermostat.html.gz
  2. +2645
    -2642
      code/espurna/static/index.thermostat.html.gz.h
  3. +78
    -19
      code/espurna/thermostat.ino
  4. +9
    -0
      code/html/custom.css
  5. +10
    -0
      code/html/index.html

BIN
code/espurna/data/index.thermostat.html.gz View File


+ 2645
- 2642
code/espurna/static/index.thermostat.html.gz.h
File diff suppressed because it is too large
View File


+ 78
- 19
code/espurna/thermostat.ino View File

@ -11,6 +11,11 @@ Copyright (C) 2017 by Dmitry Blinov <dblinov76 at gmail dot com>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <float.h> #include <float.h>
bool _thermostat_enabled = true;
bool _thermostat_mode_cooler = false;
const char* NAME_THERMOSTAT_ENABLED = "thermostatEnabled";
const char* NAME_THERMOSTAT_MODE = "thermostatMode";
const char* NAME_TEMP_RANGE_MIN = "tempRangeMin"; const char* NAME_TEMP_RANGE_MIN = "tempRangeMin";
const char* NAME_TEMP_RANGE_MAX = "tempRangeMax"; const char* NAME_TEMP_RANGE_MAX = "tempRangeMax";
const char* NAME_REMOTE_SENSOR_NAME = "remoteSensorName"; const char* NAME_REMOTE_SENSOR_NAME = "remoteSensorName";
@ -89,6 +94,26 @@ enum thermostat_cycle_type {cooling, heating};
unsigned int _thermostat_cycle = heating; unsigned int _thermostat_cycle = heating;
String thermostat_remote_sensor_topic; String thermostat_remote_sensor_topic;
//------------------------------------------------------------------------------
void thermostatEnabled(bool enabled) {
_thermostat_enabled = enabled;
}
//------------------------------------------------------------------------------
bool thermostatEnabled() {
return _thermostat_enabled;
}
//------------------------------------------------------------------------------
void thermostatModeCooler(bool cooler) {
_thermostat_mode_cooler = cooler;
}
//------------------------------------------------------------------------------
bool thermostatModeCooler() {
return _thermostat_mode_cooler;
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
std::vector<thermostat_callback_f> _thermostat_callbacks; std::vector<thermostat_callback_f> _thermostat_callbacks;
@ -223,6 +248,12 @@ void notifyRangeChanged(bool min) {
// Setup // Setup
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void commonSetup() { void commonSetup() {
_thermostat_enabled = getSetting(NAME_THERMOSTAT_ENABLED).toInt() == 1;
DEBUG_MSG_P(PSTR("[THERMOSTAT] _thermostat_enabled = %d\n"), _thermostat_enabled);
_thermostat_mode_cooler = getSetting(NAME_THERMOSTAT_MODE).toInt() == 1;
DEBUG_MSG_P(PSTR("[THERMOSTAT] _thermostat_mode_cooler = %d\n"), _thermostat_mode_cooler);
_temp_range.min = getSetting(NAME_TEMP_RANGE_MIN, THERMOSTAT_TEMP_RANGE_MIN).toInt(); _temp_range.min = getSetting(NAME_TEMP_RANGE_MIN, THERMOSTAT_TEMP_RANGE_MIN).toInt();
_temp_range.max = getSetting(NAME_TEMP_RANGE_MAX, THERMOSTAT_TEMP_RANGE_MAX).toInt(); _temp_range.max = getSetting(NAME_TEMP_RANGE_MAX, THERMOSTAT_TEMP_RANGE_MAX).toInt();
DEBUG_MSG_P(PSTR("[THERMOSTAT] _temp_range.min = %d\n"), _temp_range.min); DEBUG_MSG_P(PSTR("[THERMOSTAT] _temp_range.min = %d\n"), _temp_range.min);
@ -268,6 +299,8 @@ void _thermostatReload() {
#if WEB_SUPPORT #if WEB_SUPPORT
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void _thermostatWebSocketOnSend(JsonObject& root) { void _thermostatWebSocketOnSend(JsonObject& root) {
root["thermostatEnabled"] = thermostatEnabled();
root["thermostatMode"] = thermostatModeCooler();
root["thermostatVisible"] = 1; root["thermostatVisible"] = 1;
root[NAME_TEMP_RANGE_MIN] = _temp_range.min; root[NAME_TEMP_RANGE_MIN] = _temp_range.min;
root[NAME_TEMP_RANGE_MAX] = _temp_range.max; root[NAME_TEMP_RANGE_MAX] = _temp_range.max;
@ -296,6 +329,8 @@ void _thermostatWebSocketOnSend(JsonObject& root) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
bool _thermostatWebSocketOnReceive(const char * key, JsonVariant& value) { bool _thermostatWebSocketOnReceive(const char * key, JsonVariant& value) {
if (strncmp(key, NAME_THERMOSTAT_ENABLED, strlen(NAME_THERMOSTAT_ENABLED)) == 0) return true;
if (strncmp(key, NAME_THERMOSTAT_MODE, strlen(NAME_THERMOSTAT_MODE)) == 0) return true;
if (strncmp(key, NAME_TEMP_RANGE_MIN, strlen(NAME_TEMP_RANGE_MIN)) == 0) return true; if (strncmp(key, NAME_TEMP_RANGE_MIN, strlen(NAME_TEMP_RANGE_MIN)) == 0) return true;
if (strncmp(key, NAME_TEMP_RANGE_MAX, strlen(NAME_TEMP_RANGE_MAX)) == 0) return true; if (strncmp(key, NAME_TEMP_RANGE_MAX, strlen(NAME_TEMP_RANGE_MAX)) == 0) return true;
if (strncmp(key, NAME_REMOTE_SENSOR_NAME, strlen(NAME_REMOTE_SENSOR_NAME)) == 0) return true; if (strncmp(key, NAME_REMOTE_SENSOR_NAME, strlen(NAME_REMOTE_SENSOR_NAME)) == 0) return true;
@ -353,8 +388,8 @@ void setThermostatState(bool state) {
void debugPrintSwitch(bool state, double temp) { void debugPrintSwitch(bool state, double temp) {
char tmp_str[6]; char tmp_str[6];
dtostrf(temp, 1-sizeof(tmp_str), 1, tmp_str); dtostrf(temp, 1-sizeof(tmp_str), 1, tmp_str);
DEBUG_MSG_P(PSTR("[THERMOSTAT] switch %s, temp: %s, min: %d, max: %d, relay: %s, last switch %d\n"),
state ? "ON" : "OFF", tmp_str, _temp_range.min, _temp_range.max, relayStatus(THERMOSTAT_RELAY) ? "ON" : "OFF", millis() - _thermostat.last_switch);
DEBUG_MSG_P(PSTR("[THERMOSTAT] switch %s, temp: %s, min: %d, max: %d, mode: %s, relay: %s, last switch %d\n"),
state ? "ON" : "OFF", tmp_str, _temp_range.min, _temp_range.max, _thermostat_mode_cooler ? "COOLER" : "HEATER", relayStatus(THERMOSTAT_RELAY) ? "ON" : "OFF", millis() - _thermostat.last_switch);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -372,23 +407,44 @@ inline void switchThermostat(bool state, double temp) {
//----------- Main function that make decision --------------------------------- //----------- Main function that make decision ---------------------------------
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void checkTempAndAdjustRelay(double temp) { void checkTempAndAdjustRelay(double temp) {
// if thermostat switched ON and t > max - switch it OFF and start cooling
if (relayStatus(THERMOSTAT_RELAY) && temp > _temp_range.max) {
_thermostat_cycle = cooling;
switchThermostat(false, temp);
// if thermostat switched ON for max time - switch it OFF for rest
} else if (relayStatus(THERMOSTAT_RELAY) && lastSwitchEarlierThan(_thermostat_max_on_time)) {
switchThermostat(false, temp);
// if t < min and thermostat switched OFF for at least minimum time - switch it ON and start
} else if (!relayStatus(THERMOSTAT_RELAY) && temp < _temp_range.min
&& (_thermostat.last_switch == 0 || lastSwitchEarlierThan(_thermostat_min_off_time))) {
_thermostat_cycle = heating;
switchThermostat(true, temp);
// if heating cycle and thermostat switchaed OFF for more than min time - switch it ON
// continue heating cycle
} else if (!relayStatus(THERMOSTAT_RELAY) && _thermostat_cycle == heating
&& lastSwitchEarlierThan(_thermostat_min_off_time)) {
switchThermostat(true, temp);
if (_thermostat_mode_cooler == false) { // Main operation mode. Thermostat is HEATER.
// if thermostat switched ON and t > max - switch it OFF and start cooling
if (relayStatus(THERMOSTAT_RELAY) && temp > _temp_range.max) {
_thermostat_cycle = cooling;
switchThermostat(false, temp);
// if thermostat switched ON for max time - switch it OFF for rest
} else if (relayStatus(THERMOSTAT_RELAY) && lastSwitchEarlierThan(_thermostat_max_on_time)) {
switchThermostat(false, temp);
// if t < min and thermostat switched OFF for at least minimum time - switch it ON and start
} else if (!relayStatus(THERMOSTAT_RELAY) && temp < _temp_range.min
&& (_thermostat.last_switch == 0 || lastSwitchEarlierThan(_thermostat_min_off_time))) {
_thermostat_cycle = heating;
switchThermostat(true, temp);
// if heating cycle and thermostat switchaed OFF for more than min time - switch it ON
// continue heating cycle
} else if (!relayStatus(THERMOSTAT_RELAY) && _thermostat_cycle == heating
&& lastSwitchEarlierThan(_thermostat_min_off_time)) {
switchThermostat(true, temp);
}
} else { // Thermostat is COOLER. Inverse logic.
// if thermostat switched ON and t < min - switch it OFF and start heating
if (relayStatus(THERMOSTAT_RELAY) && temp < _temp_range.min) {
_thermostat_cycle = heating;
switchThermostat(false, temp);
// if thermostat switched ON for max time - switch it OFF for rest
} else if (relayStatus(THERMOSTAT_RELAY) && lastSwitchEarlierThan(_thermostat_max_on_time)) {
switchThermostat(false, temp);
// if t > max and thermostat switched OFF for at least minimum time - switch it ON and start
} else if (!relayStatus(THERMOSTAT_RELAY) && temp > _temp_range.max
&& (_thermostat.last_switch == 0 || lastSwitchEarlierThan(_thermostat_min_off_time))) {
_thermostat_cycle = cooling;
switchThermostat(true, temp);
// if cooling cycle and thermostat switchaed OFF for more than min time - switch it ON
// continue cooling cycle
} else if (!relayStatus(THERMOSTAT_RELAY) && _thermostat_cycle == cooling
&& lastSwitchEarlierThan(_thermostat_min_off_time)) {
switchThermostat(true, temp);
}
} }
} }
@ -460,6 +516,9 @@ double getLocalHumidity() {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
void thermostatLoop(void) { void thermostatLoop(void) {
if (!thermostatEnabled())
return;
// Update temperature range // Update temperature range
if (mqttConnected()) { if (mqttConnected()) {
if (millis() - _temp_range.ask_time > _temp_range.ask_interval) { if (millis() - _temp_range.ask_time > _temp_range.ask_interval) {


+ 9
- 0
code/html/custom.css View File

@ -284,6 +284,9 @@ label[for].toggle {
input[name="relay"] + .toggle:before { input[name="relay"] + .toggle:before {
content: "OFF"; content: "OFF";
} }
input[name="thermostatMode"] + .toggle:before {
content: "Heater";
}
.toggle:after{ .toggle:after{
content: "YES"; content: "YES";
right: 20px; right: 20px;
@ -291,6 +294,9 @@ input[name="relay"] + .toggle:before {
input[name="relay"] + .toggle:after { input[name="relay"] + .toggle:after {
content: "ON"; content: "ON";
} }
input[name="thermostatMode"] + .toggle:after {
content: "Cooler";
}
.toggle__handler { .toggle__handler {
display: inline-block; display: inline-block;
position: relative; position: relative;
@ -328,6 +334,9 @@ input:checked + .toggle .toggle__handler {
border-top-right-radius: 4px; border-top-right-radius: 4px;
border-bottom-right-radius: 4px; border-bottom-right-radius: 4px;
} }
input[name="thermostatMode"]:checked + .toggle .toggle__handler {
background: #00c0c0;
}
input[disabled] + .toggle .toggle__handler { input[disabled] + .toggle .toggle__handler {
background: #ccc; background: #ccc;
} }


+ 10
- 0
code/html/index.html View File

@ -1061,6 +1061,16 @@
<div class="page"> <div class="page">
<div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4">Enable Thermostat</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="thermostatEnabled" tabindex="30" /></div>
</div>
<div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4">Thermostat Mode</label>
<div class="pure-u-1 pure-u-lg-1-4"><input type="checkbox" name="thermostatMode" tabindex="30" /></div>
</div>
<div class="pure-g"> <div class="pure-g">
<label class="pure-u-1 pure-u-lg-1-4" for="thermostatOperationMode">Operation mode</label> <label class="pure-u-1 pure-u-lg-1-4" for="thermostatOperationMode">Operation mode</label>
<input class="pure-u-1 pure-u-lg-1-4" name="thermostatOperationMode" type="text" readonly /> <input class="pure-u-1 pure-u-lg-1-4" name="thermostatOperationMode" type="text" readonly />


Loading…
Cancel
Save