6 Commits

Author SHA1 Message Date
Yurii
f245f37dfd chore: bump version 2024-05-26 17:41:12 +03:00
Yurii
a825412f37 feat: added fault state GPIO setting 2024-05-25 02:51:10 +03:00
Yurii
935f8bd0a8 fix: outdoor sensor GPIO validation fixed 2024-05-25 00:17:55 +03:00
Yurii
f78d2f38b8 fix: equitherm with BLE indoor sensor in emergency mode fixed 2024-04-25 13:38:03 +03:00
Yurii
ef083991e3 feat: added board info on portal 2024-04-23 10:30:42 +03:00
Yurii
3c0f846335 fix: added set target indoor temp to CH2 for native heating control #58 2024-04-23 08:13:03 +03:00
11 changed files with 177 additions and 10 deletions

View File

@@ -136,6 +136,16 @@ public:
return isValidResponse(response);
}
bool setRoomSetpointCh2(float temperature) {
unsigned long response = this->sendRequest(buildRequest(
OpenThermMessageType::WRITE_DATA,
OpenThermMessageID::TrSetCH2,
temperatureToData(temperature)
));
return isValidResponse(response);
}
bool setRoomTemp(float temperature) {
unsigned long response = this->sendRequest(buildRequest(
OpenThermMessageType::WRITE_DATA,

View File

@@ -27,6 +27,7 @@ lib_deps =
laxilef/TinyLogger@^1.1.0
build_flags =
-D PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
;-D PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
-D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK305
-mtext-section-literals
-D MQTT_CLIENT_STD_FUNCTION_CALLBACK=1
@@ -51,7 +52,7 @@ monitor_speed = 115200
monitor_filters = direct
board_build.flash_mode = dio
board_build.filesystem = littlefs
version = 1.4.0-rc.24
version = 1.4.0
; Defaults
[esp8266_defaults]
@@ -68,7 +69,7 @@ board_build.ldscript = eagle.flash.4m1m.ld
[esp32_defaults]
platform = espressif32@^6.6
platform_packages =
platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32/archive/refs/tags/2.0.15.zip
platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32/archive/refs/tags/2.0.15.zip
board_build.partitions = esp32_partitions.csv
lib_deps =
${env.lib_deps}

View File

@@ -30,6 +30,8 @@ protected:
unsigned long dhwSetTempTime = 0;
unsigned long heatingSetTempTime = 0;
byte configuredRxLedGpio = GPIO_IS_NOT_CONFIGURED;
byte configuredFaultStateGpio = GPIO_IS_NOT_CONFIGURED;
bool faultState = false;
const char* getTaskName() {
@@ -116,6 +118,7 @@ protected:
return;
}
// RX LED GPIO setup
if (settings.opentherm.rxLedGpio != this->configuredRxLedGpio) {
if (this->configuredRxLedGpio != GPIO_IS_NOT_CONFIGURED) {
digitalWrite(this->configuredRxLedGpio, LOW);
@@ -131,6 +134,27 @@ protected:
}
}
// Fault state setup
if (settings.opentherm.faultStateGpio != this->configuredFaultStateGpio) {
if (this->configuredFaultStateGpio != GPIO_IS_NOT_CONFIGURED) {
digitalWrite(this->configuredFaultStateGpio, LOW);
}
if (GPIO_IS_VALID(settings.opentherm.faultStateGpio)) {
this->configuredFaultStateGpio = settings.opentherm.faultStateGpio;
this->faultState = false ^ settings.opentherm.invertFaultState ? HIGH : LOW;
pinMode(this->configuredFaultStateGpio, OUTPUT);
digitalWrite(
this->configuredFaultStateGpio,
this->faultState
);
} else if (this->configuredFaultStateGpio != GPIO_IS_NOT_CONFIGURED) {
this->configuredFaultStateGpio = GPIO_IS_NOT_CONFIGURED;
}
}
bool heatingEnabled = (vars.states.emergency || settings.heating.enable) && this->pump && this->isReady();
bool heatingCh2Enabled = settings.opentherm.heatingCh2Enabled;
if (settings.opentherm.heatingCh1ToCh2) {
@@ -144,7 +168,7 @@ protected:
heatingEnabled,
settings.opentherm.dhwPresent && settings.dhw.enable,
false,
false,
settings.opentherm.nativeHeatingControl,
heatingCh2Enabled,
settings.opentherm.summerWinterMode,
settings.opentherm.dhwBlocking
@@ -174,6 +198,16 @@ protected:
vars.states.fault = false;
vars.states.diagnostic = false;
// Force fault state = on
if (this->configuredFaultStateGpio != GPIO_IS_NOT_CONFIGURED) {
bool fState = true ^ settings.opentherm.invertFaultState ? HIGH : LOW;
if (fState != this->faultState) {
this->faultState = fState;
digitalWrite(this->configuredFaultStateGpio, this->faultState);
}
}
return;
}
@@ -199,6 +233,16 @@ protected:
vars.states.fault = CustomOpenTherm::isFault(response);
vars.states.diagnostic = CustomOpenTherm::isDiagnostic(response);
// Fault state
if (this->configuredFaultStateGpio != GPIO_IS_NOT_CONFIGURED) {
bool fState = vars.states.fault ^ settings.opentherm.invertFaultState ? HIGH : LOW;
if (fState != this->faultState) {
this->faultState = fState;
digitalWrite(this->configuredFaultStateGpio, this->faultState);
}
}
// These parameters will be updated every minute
if (millis() - this->prevUpdateNonEssentialVars > 60000) {
if (!heatingEnabled && settings.opentherm.modulationSyncWithHeating) {
@@ -286,6 +330,9 @@ protected:
// Get fault code (if necessary)
if (vars.states.fault) {
updateFaultCode();
} else if (vars.sensors.faultCode != 0) {
vars.sensors.faultCode = 0;
}
updatePressure();
@@ -402,6 +449,13 @@ protected:
} else {
Log.swarningln(FPSTR(L_OT_HEATING), F("Failed set target indoor temp"));
}
// Set target temp to CH2
if (settings.opentherm.heatingCh1ToCh2) {
if (!this->instance->setRoomSetpointCh2(convertedTemp)) {
Log.swarningln(FPSTR(L_OT_HEATING), F("Failed set target indoor temp to CH2"));
}
}
}
// force enable pump

View File

@@ -512,6 +512,34 @@ protected:
doc["system"]["maxFreeBlockHeap"] = getMaxFreeBlockHeap();
doc["system"]["minMaxFreeBlockHeap"] = getMaxFreeBlockHeap(true);
doc["system"]["resetReason"] = getResetReason();
#ifdef ARDUINO_ARCH_ESP8266
doc["system"]["chipModel"] = esp_is_8285() ? "ESP8285" : "ESP8266";
doc["system"]["chipRevision"] = 0;
doc["system"]["chipCores"] = 1;
doc["system"]["cpuFreq"] = ESP.getCpuFreqMHz();
doc["system"]["coreVersion"] = ESP.getCoreVersion();
doc["system"]["flashSize"] = ESP.getFlashChipSize();
doc["system"]["flashRealSize"] = ESP.getFlashChipRealSize();
#elif ARDUINO_ARCH_ESP32
doc["system"]["chipModel"] = ESP.getChipModel();
doc["system"]["chipRevision"] = ESP.getChipRevision();
doc["system"]["chipCores"] = ESP.getChipCores();
doc["system"]["cpuFreq"] = ESP.getCpuFreqMHz();
doc["system"]["coreVersion"] = ESP.getSdkVersion();
doc["system"]["flashSize"] = ESP.getFlashChipSize();
doc["system"]["flashRealSize"] = doc["system"]["flashSize"];
#else
doc["system"]["chipModel"] = 0;
doc["system"]["chipRevision"] = 0;
doc["system"]["chipCores"] = 0;
doc["system"]["cpuFreq"] = 0;
doc["system"]["coreVersion"] = 0;
doc["system"]["flashSize"] = 0;
doc["system"]["flashRealSize"] = 0;
#endif
doc.shrinkToFit();
this->bufferedWebServer->send(200, "application/json", doc);

View File

@@ -192,8 +192,15 @@ protected:
}
if (vars.states.emergency) {
etRegulator.Kt = 0;
etRegulator.indoorTemp = 0;
if (settings.sensors.indoor.type == SensorType::MANUAL) {
etRegulator.Kt = 0;
etRegulator.indoorTemp = 0;
} else {
etRegulator.Kt = settings.equitherm.t_factor;
etRegulator.indoorTemp = indoorTemp;
}
etRegulator.outdoorTemp = outdoorTemp;
} else if (settings.pid.enable) {

View File

@@ -65,7 +65,7 @@ protected:
bool indoorTempUpdated = false;
bool outdoorTempUpdated = false;
if (settings.sensors.outdoor.type == SensorType::DS18B20 && GPIO_IS_VALID(settings.sensors.indoor.gpio)) {
if (settings.sensors.outdoor.type == SensorType::DS18B20 && GPIO_IS_VALID(settings.sensors.outdoor.gpio)) {
outdoorTemperatureSensor();
outdoorTempUpdated = true;
}

View File

@@ -51,6 +51,8 @@ struct Settings {
byte inGpio = DEFAULT_OT_IN_GPIO;
byte outGpio = DEFAULT_OT_OUT_GPIO;
byte rxLedGpio = DEFAULT_OT_RX_LED_GPIO;
byte faultStateGpio = DEFAULT_OT_FAULT_STATE_GPIO;
byte invertFaultState = false;
unsigned int memberIdCode = 0;
bool dhwPresent = true;
bool summerWinterMode = false;

View File

@@ -1,5 +1,5 @@
#define PROJECT_NAME "OpenTherm Gateway"
#define PROJECT_VERSION "1.4.0-rc.24"
#define PROJECT_VERSION "1.4.0"
#define PROJECT_REPO "https://github.com/Laxilef/OTGateway"
#define MQTT_RECONNECT_INTERVAL 15000
@@ -103,6 +103,10 @@
#define DEFAULT_OT_RX_LED_GPIO GPIO_IS_NOT_CONFIGURED
#endif
#ifndef DEFAULT_OT_FAULT_STATE_GPIO
#define DEFAULT_OT_FAULT_STATE_GPIO GPIO_IS_NOT_CONFIGURED
#endif
#ifndef DEFAULT_SENSOR_OUTDOOR_GPIO
#define DEFAULT_SENSOR_OUTDOOR_GPIO GPIO_IS_NOT_CONFIGURED
#endif

View File

@@ -342,6 +342,8 @@ void settingsToJson(const Settings& src, JsonVariant dst, bool safe = false) {
dst["opentherm"]["inGpio"] = src.opentherm.inGpio;
dst["opentherm"]["outGpio"] = src.opentherm.outGpio;
dst["opentherm"]["rxLedGpio"] = src.opentherm.rxLedGpio;
dst["opentherm"]["faultStateGpio"] = src.opentherm.faultStateGpio;
dst["opentherm"]["invertFaultState"] = src.opentherm.invertFaultState;
dst["opentherm"]["memberIdCode"] = src.opentherm.memberIdCode;
dst["opentherm"]["dhwPresent"] = src.opentherm.dhwPresent;
dst["opentherm"]["summerWinterMode"] = src.opentherm.summerWinterMode;
@@ -642,6 +644,32 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
}
}
if (!src["opentherm"]["faultStateGpio"].isNull()) {
if (src["opentherm"]["faultStateGpio"].is<JsonString>() && src["opentherm"]["faultStateGpio"].as<JsonString>().size() == 0) {
if (dst.opentherm.faultStateGpio != GPIO_IS_NOT_CONFIGURED) {
dst.opentherm.faultStateGpio = GPIO_IS_NOT_CONFIGURED;
changed = true;
}
} else {
unsigned char value = src["opentherm"]["faultStateGpio"].as<unsigned char>();
if (GPIO_IS_VALID(value) && value != dst.opentherm.faultStateGpio) {
dst.opentherm.faultStateGpio = value;
changed = true;
}
}
}
if (src["opentherm"]["invertFaultState"].is<bool>()) {
bool value = src["opentherm"]["invertFaultState"].as<bool>();
if (value != dst.opentherm.invertFaultState) {
dst.opentherm.invertFaultState = value;
changed = true;
}
}
if (!src["opentherm"]["memberIdCode"].isNull()) {
unsigned int value = src["opentherm"]["memberIdCode"].as<unsigned int>();
@@ -652,8 +680,12 @@ bool jsonToSettings(const JsonVariantConst src, Settings& dst, bool safe = false
}
if (src["opentherm"]["dhwPresent"].is<bool>()) {
dst.opentherm.dhwPresent = src["opentherm"]["dhwPresent"].as<bool>();
changed = true;
bool value = src["opentherm"]["dhwPresent"].as<bool>();
if (value != dst.opentherm.dhwPresent) {
dst.opentherm.dhwPresent = value;
changed = true;
}
}
if (src["opentherm"]["summerWinterMode"].is<bool>()) {

View File

@@ -87,7 +87,7 @@
<tbody>
<tr>
<th scope="row">Version:</th>
<td><b id="version"></b></td>
<td><b id="version"></b>, core/sdk: <b id="core-version"></b></td>
</tr>
<tr>
<th scope="row">Build date:</th>
@@ -101,6 +101,10 @@
<th scope="row">Free memory:</th>
<td><b id="free-heap"></b> of <b id="total-heap"></b> bytes (min: <b id="min-free-heap"></b> bytes)<br />max free block: <b id="max-free-block-heap"></b> bytes (min: <b id="min-max-free-block-heap"></b> bytes)</td>
</tr>
<tr>
<th scope="row">Board:</th>
<td>Chip <b id="chip-model"></b> (rev. <span id="chip-revision"></span>)<br />Cores: <b id="chip-cores"></b>, frequency: <b id="cpu-freq"></b> mHz<br />Flash size: <b id="flash-size"></b> MB (real: <b id="flash-real-size"></b> MB)</td>
</tr>
<tr>
<th scope="row">Last reset reason:</th>
<td><b id="reset-reason"></b></td>
@@ -164,6 +168,15 @@
setValue('#max-free-block-heap', result.system.maxFreeBlockHeap);
setValue('#min-max-free-block-heap', result.system.minMaxFreeBlockHeap);
setValue('#reset-reason', result.system.resetReason);
setValue('#chip-model', result.system.chipModel);
setValue('#chip-revision', result.system.chipRevision);
setValue('#chip-cores', result.system.chipCores);
setValue('#cpu-freq', result.system.cpuFreq);
setValue('#core-version', result.system.coreVersion);
setValue('#flash-size', result.system.flashSize / 1024 / 1024);
setValue('#flash-real-size', result.system.flashRealSize / 1024 / 1024);
setBusy('#system-busy', '#system-table', false);
} catch (error) {

View File

@@ -435,6 +435,20 @@
<input type="checkbox" id="opentherm-get-min-max-temp" name="opentherm[getMinMaxTemp]" value="true">
Get min/max temp from boiler
</label>
<hr />
<fieldset>
<label for="opentherm-fault-state-gpio">
Fault state GPIO
<input type="number" inputmode="numeric" id="opentherm-fault-state-gpio" name="opentherm[faultStateGpio]" min="0" max="254" step="1">
<small>Can be useful to switch on another boiler <u>via relay</u>. Blank - not use.</small>
</label>
<label for="opentherm-invert-fault-state">
<input type="checkbox" id="opentherm-invert-fault-state" name="opentherm[invertFaultState]" value="true">
Invert fault state
</label>
</fieldset>
<hr />
<label for="opentherm-native-heating-control">
@@ -680,6 +694,8 @@
setInputValue('#opentherm-in-gpio', data.opentherm.inGpio < 255 ? data.opentherm.inGpio : '');
setInputValue('#opentherm-out-gpio', data.opentherm.outGpio < 255 ? data.opentherm.outGpio : '');
setInputValue('#opentherm-rx-led-gpio', data.opentherm.rxLedGpio < 255 ? data.opentherm.rxLedGpio : '');
setInputValue('#opentherm-fault-state-gpio', data.opentherm.faultStateGpio < 255 ? data.opentherm.faultStateGpio : '');
setCheckboxValue('#opentherm-invert-fault-state', data.opentherm.invertFaultState);
setInputValue('#opentherm-member-id-code', data.opentherm.memberIdCode);
setCheckboxValue('#opentherm-dhw-present', data.opentherm.dhwPresent);
setCheckboxValue('#opentherm-sw-mode', data.opentherm.summerWinterMode);