From f1bd6ee2ff7bc2eb8b44fb194972a9d50684a39b Mon Sep 17 00:00:00 2001 From: Roberto Sanz Cirirano Date: Sun, 28 Mar 2021 20:40:25 +0200 Subject: [PATCH] Integration with PID Climate --- opentherm.yaml | 49 ++++++++++++++++++++++++++++++++++++++++-- opentherm_climate.h | 45 ++++++++++++++++++++++++++++++++++++-- opentherm_component.h | 50 +++++++++++++++++++++++++++++++++---------- opentherm_output.h | 21 ++++++++++++++++++ 4 files changed, 150 insertions(+), 15 deletions(-) create mode 100644 opentherm_output.h diff --git a/opentherm.yaml b/opentherm.yaml index fbadeb2..7e8bc64 100644 --- a/opentherm.yaml +++ b/opentherm.yaml @@ -17,6 +17,7 @@ esphome: - opentherm_climate.h - opentherm_switch.h - opentherm_binary.h + - opentherm_output.h wifi: ssid: !secret wifi_name @@ -39,6 +40,21 @@ custom_component: components: - id: opentherm +output: + - platform: custom + type: float + lambda: |- + OpenthermComponent *openthermComp = (OpenthermComponent*) opentherm; + + auto opentherm_pid_output = new OpenthermFloatOutput(); + openthermComp->set_pid_output(opentherm_pid_output); + App.register_component(opentherm_pid_output); + return {opentherm_pid_output}; + outputs: + id: pid_output + #min_power: 30.00% + #max_power: 60.00% + sensor: - platform: custom lambda: |- @@ -48,7 +64,8 @@ sensor: openthermComp->external_temperature_sensor, openthermComp->return_temperature_sensor, openthermComp->pressure_sensor, - openthermComp->modulation_sensor + openthermComp->modulation_sensor, + openthermComp->heatting_target_temperature_sensor }; sensors: - name: "Boiler Temperature" @@ -66,6 +83,21 @@ sensor: - name: "Boiler Modulation" unit_of_measurement: "%" accuracy_decimals: 0 + - name: "Heating Target Temperature" + unit_of_measurement: ºC + accuracy_decimals: 2 + - platform: homeassistant + id: temperature_sensor + entity_id: sensor.temperature_sensor + - platform: pid + name: "PID Climate Result" + type: RESULT + - platform: pid + name: "PID Climate HEAT" + type: HEAT + - platform: pid + name: "PID Climate ERROR" + type: ERROR binary_sensor: - platform: custom @@ -85,6 +117,10 @@ switch: switches: name: "Termostato ambiente" + - platform: template + name: "PID Climate Autotune" + turn_on_action: + - climate.pid.autotune: pid_climate climate: - platform: custom @@ -98,4 +134,13 @@ climate: climates: - name: "Hot water" - name: "Heating water" - + - platform: pid + id: pid_climate + name: "PID Climate Controller" + sensor: temperature_sensor + default_target_temperature: 21°C + heat_output: pid_output + control_parameters: + kp: 0.38197 + ki: 0.01012 + kd: 3.60387 diff --git a/opentherm_climate.h b/opentherm_climate.h index 76e1f3c..596d546 100644 --- a/opentherm_climate.h +++ b/opentherm_climate.h @@ -5,16 +5,37 @@ class OpenthermClimate : public Climate { private: const char *TAG = "opentherm_climate"; + bool supports_auto_mode_ = false; + bool supports_two_point_target_temperature_ = false; public: + void set_supports_auto_mode(bool value) + { + supports_auto_mode_ = value; + } + bool get_supports_auto_mode() + { + return supports_auto_mode_; + } + + void set_supports_two_point_target_temperature(bool value) + { + supports_two_point_target_temperature_ = value; + } + bool get_supports_two_point_target_temperature() + { + return supports_two_point_target_temperature_; + } + + climate::ClimateTraits traits() override { auto traits = climate::ClimateTraits(); traits.set_supports_current_temperature(true); - traits.set_supports_auto_mode(false); + traits.set_supports_auto_mode(supports_auto_mode_); traits.set_supports_cool_mode(false); traits.set_supports_heat_mode(true); - traits.set_supports_two_point_target_temperature(false); + traits.set_supports_two_point_target_temperature(supports_two_point_target_temperature_); traits.set_supports_away(false); traits.set_supports_action(true); @@ -48,6 +69,26 @@ class OpenthermClimate : public Climate { this->target_temperature = temp; this->publish_state(); } + if (call.get_target_temperature_low().has_value()) { + // User requested target temperature change + float temp = *call.get_target_temperature_low(); + // Send target temp to climate + // ... + ESP_LOGD(TAG, "get_target_temperature_low"); + + this->target_temperature_low = temp; + this->publish_state(); + } + if (call.get_target_temperature_high().has_value()) { + // User requested target temperature change + float temp = *call.get_target_temperature_high(); + // Send target temp to climate + // ... + ESP_LOGD(TAG, "get_target_temperature_high"); + + this->target_temperature_high = temp; + this->publish_state(); + } } }; diff --git a/opentherm_component.h b/opentherm_component.h index 39ad003..048f030 100644 --- a/opentherm_component.h +++ b/opentherm_component.h @@ -4,6 +4,7 @@ #include "opentherm_switch.h" #include "opentherm_climate.h" #include "opentherm_binary.h" +#include "opentherm_output.h" // Pins to OpenTherm Adapter @@ -19,6 +20,7 @@ ICACHE_RAM_ATTR void handleInterrupt() { class OpenthermComponent: public PollingComponent { private: const char *TAG = "opentherm_component"; + OpenthermFloatOutput *pid_output_; public: Switch *thermostatSwitch = new OpenthermSwitch(); Sensor *external_temperature_sensor = new Sensor(); @@ -26,15 +28,19 @@ class OpenthermComponent: public PollingComponent { Sensor *boiler_temperature = new Sensor(); Sensor *pressure_sensor = new Sensor(); Sensor *modulation_sensor = new Sensor(); + Sensor *heatting_target_temperature_sensor = new Sensor(); Climate *hotWaterClimate = new OpenthermClimate(); - Climate *heatingWaterClimate = new OpenthermClimate(); + OpenthermClimate *heatingWaterClimate = new OpenthermClimate(); BinarySensor *flame = new OpenthermBinarySensor(); // Set 3 sec. to give time to read all sensors (and not appear in HA as not available) - OpenthermComponent(): PollingComponent(3000) { + OpenthermComponent(): PollingComponent(15000) { } + void set_pid_output(OpenthermFloatOutput *pid_output) { pid_output_ = pid_output; } + + void setup() override { // This will be called once to set up the component // think of it as the setup() call in Arduino @@ -45,6 +51,10 @@ class OpenthermComponent: public PollingComponent { thermostatSwitch->add_on_state_callback([=](bool state) -> void { ESP_LOGD ("opentherm_component", "termostatSwitch_on_state_callback %d", state); }); + + // Adjust HeatingWaterClimate depending on PID + heatingWaterClimate->set_supports_auto_mode(this->pid_output_ != nullptr); + heatingWaterClimate->set_supports_two_point_target_temperature(this->pid_output_ != nullptr); } float getExternalTemperature() { unsigned long response = ot.sendRequest(ot.buildRequest(OpenThermRequestType::READ, OpenThermMessageID::Toutside, 0)); @@ -92,36 +102,54 @@ class OpenthermComponent: public PollingComponent { bool isFlameOn = ot.isFlameOn(response); bool isCentralHeatingActive = ot.isCentralHeatingActive(response); bool isHotWaterActive = ot.isHotWaterActive(response); + float return_temperature = getReturnTemperature(); + float hotWater_temperature = getHotWaterTemperature(); + // Set temperature depending on room thermostat - if (thermostatSwitch->state) { - ot.setBoilerTemperature(heatingWaterClimate->target_temperature); - ESP_LOGD("opentherm_component", "setBoilerTemperature at %f °C (from heating water climate)", heatingWaterClimate->target_temperature); + float heatting_target_temperature; + if (this->pid_output_ != nullptr) { + float pid_output = pid_output_->get_state(); + if (pid_output == 0.0f) { + heatting_target_temperature = 10.0f; + } + else { + heatting_target_temperature = pid_output * (heatingWaterClimate->target_temperature_high - heatingWaterClimate->target_temperature_low) + + heatingWaterClimate->target_temperature_low; + } + ESP_LOGD("opentherm_component", "setBoilerTemperature at %f °C (from PID Output)", heatting_target_temperature); + } + else if (thermostatSwitch->state) { + heatting_target_temperature = heatingWaterClimate->target_temperature; + ESP_LOGD("opentherm_component", "setBoilerTemperature at %f °C (from heating water climate)", heatting_target_temperature); } else { // If the room thermostat is off, set it to 10, so that the pump continues to operate - ot.setBoilerTemperature(10.0); - ESP_LOGD("opentherm_component", "setBoilerTemperature at %f °C (default low value)", 10.0); + heatting_target_temperature = 10.0; + ESP_LOGD("opentherm_component", "setBoilerTemperature at %f °C (default low value)", heatting_target_temperature); } + ot.setBoilerTemperature(heatting_target_temperature); + // Set hot water temperature setHotWaterTemperature(hotWaterClimate->target_temperature); - // Read sensor values + // Read sensor values + /* float boilerTemperature = ot.getBoilerTemperature(); float ext_temperature = getExternalTemperature(); - float return_temperature = getReturnTemperature(); - float hotWater_temperature = getHotWaterTemperature(); float pressure = getPressure(); float modulation = getModulation(); // Publish sensor values - flame->publish_state(isFlameOn); + //flame->publish_state(isFlameOn); external_temperature_sensor->publish_state(ext_temperature); return_temperature_sensor->publish_state(return_temperature); boiler_temperature->publish_state(boilerTemperature); pressure_sensor->publish_state(pressure); modulation_sensor->publish_state(modulation); + */ + heatting_target_temperature_sensor->publish_state(heatting_target_temperature); // Publish status of thermostat that controls hot water hotWaterClimate->current_temperature = hotWater_temperature; diff --git a/opentherm_output.h b/opentherm_output.h new file mode 100644 index 0000000..1f12aa2 --- /dev/null +++ b/opentherm_output.h @@ -0,0 +1,21 @@ +#pragma once + +#include "esphome.h" +using namespace esphome; + + +class OpenthermFloatOutput : public Component, public FloatOutput { + public: + float get_state() const { return state_; } + void setup() override { + // This will be called by App.setup() + //pinMode(5, OUTPUT); + } + + protected: + float state_{0.0f}; + void write_state(float state) override { this->state_ = state; } + +}; + +