I’m working on a thermostat that should, in theory, be simple for controlling an old air conditioner. I don’t know much about electronics and have only basic programming knowledge, so I’ve relied on the work of others and ChatGPT. However, there’s one small issue I haven’t been able to solve.
Thermostat setup:
- Microcontroller: ESP32
- Components: Temperature and humidity sensor, two-channel relay, small display, and a physical button to turn it on/off.
- Air conditioner wiring:
- 24V wire
- Wire to turn on the compressor
- Wire to turn on the fan
Expected behavior and current status:
Pressing the button toggles between cooling mode and off. (Works)
When the AC is on, the display should be on; when turned off, the display should be blank. (Works)
When turning on the AC, both the compressor and fan should turn on. If the compressor was recently turned off, it should wait a set time before turning back on. (Works)
When the target temperature is reached, the compressor should turn off, but the fan should remain on. (Works)
When turning off the AC, the compressor should turn off immediately, and the fan should turn off shortly after. (Partially works)
Specific issue:
If I turn off the AC using the physical button while the compressor is running (cool_action), everything works correctly.
But if the compressor is already off and only the fan is running (idle_action), the system doesn’t turn off the fan.
I’m sharing the full code below to see if anyone can help me fix this last issue. Thanks in advance!
esphome:
name: esphome-web-aa565c
friendly_name: Aire Recepción
min_version: 2024.11.0
name_add_mac_suffix: false
on_boot:
priority: 600
then:
- delay: 5s # Espera a que el ESP termine de inicializar
- climate.control:
id: termostato_area
mode: "OFF" # Apaga el termostato tras reinicio
- switch.turn_off: relay_compresor
- switch.turn_off: relay_ventilador # Asegura que el ventilador también se apague
esp32:
board: esp32dev
framework:
type: esp-idf
# Enable logging
logger:
# Enable Home Assistant API
api:
# Allow Over-The-Air updates
ota:
- platform: esphome
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
substitutions:
area: "Recepción"
# Configuración del bus I2C
i2c:
sda: GPIO21
scl: GPIO22
scan: true
id: bus_a
# Sensor de temperatura y humedad interno
sensor:
- platform: am2320
address: 0x5C
temperature:
name: "Temperatura ${area}"
id: temperatura_area
filters:
- offset: -0.8
humidity:
name: "Humedad ${area}"
id: humedad_area
update_interval: 180s
# Sensor de temperatura promedio desde Home Assistant
- platform: homeassistant
id: temperatura_promedio_ha
entity_id: sensor.temperatura_promedio_recepcion
force_update: true
# Sensor de temperatura usada
- platform: template
id: temperatura_final
icon: mdi:thermometer
name: "Temperatura Final ${area}"
unit_of_measurement: "°C" # Agregar esta línea
device_class: temperature # Esto también ayuda
state_class: measurement # Permite estadísticas en HA
update_interval: 180s
lambda: |-
if (!isnan(id(temperatura_promedio_ha).state)) {
return id(temperatura_promedio_ha).state;
} else {
return id(temperatura_area).state;
}
font:
- file: "fonts/Roboto_Condensed-Medium.ttf"
id: font_large
size: 50
- file: "fonts/Roboto_Condensed-Medium.ttf"
id: font_medium
size: 22
display:
- platform: ssd1306_i2c
model: "SSD1306 128x64"
address: 0x3C
id: pantalla
lambda: |-
if (id(termostato_area).mode != CLIMATE_MODE_OFF) {
// Mostrar la temperatura actual
it.printf(0, 10, id(font_large), "%.0f°", id(temperatura_final).state);
// Mostrar la temperatura objetivo
it.printf(73, 6, id(font_medium), "%.1f°", id(termostato_area).target_temperature);
// Mostrar el modo de operación
std::string modo;
if (id(termostato_area).mode == CLIMATE_MODE_COOL) {
modo = "FRIO";
} else if (id(termostato_area).mode == CLIMATE_MODE_FAN_ONLY) {
modo = "VENT";
} else {
modo = "OFF";
}
it.print(73, 42, id(font_medium), modo.c_str());
} else {
it.fill(COLOR_OFF); // Muestra pantalla en blanco cuando el aire está apagado
}
# Configuración Termostato
globals:
- id: ultimo_apagado
type: int
restore_value: no
initial_value: '0'
climate:
- platform: thermostat
name: "Aire Acondicionado ${area}"
id: termostato_area
visual:
min_temperature: 18 °C
max_temperature: 30°C
temperature_step: 0.5 °C
sensor: temperatura_final
min_cooling_off_time: 1s # Recomendado espera 4 min antes de encender de nuevo el compresor pero se está evaluando en cool_action
min_cooling_run_time: 180s
min_idle_time: 60s
min_fanning_off_time: 0s
min_fanning_run_time: 0s
cool_deadband: 0.5
cool_overrun: 0.0
cool_action:
- switch.turn_on: relay_ventilador # Enciende el ventilador primero
- lambda: 'id(pantalla).turn_on();' # Enciende la pantalla junto con el ventilador
- while:
condition:
lambda: 'return (millis() - id(ultimo_apagado)) < 240000;' # Espera hasta que pasen 4 minutos
then:
- delay: 1s # Revisa la condición cada segundo
- if:
condition:
lambda: 'return id(temperatura_final).state > id(termostato_area).target_temperature;'
then:
- switch.turn_on: relay_compresor # Enciende el compresor solo si la temperatura es mayor que la deseada
idle_action:
- lambda: 'id(ultimo_apagado) = millis();' # Guarda el tiempo de apagado
- switch.turn_off: relay_compresor
- switch.turn_on: relay_ventilador
- delay: 30s # Reduce consumo energético
- if:
condition:
lambda: 'return id(termostato_area).mode == CLIMATE_MODE_OFF;'
then:
- switch.turn_off: relay_ventilador
fan_only_action:
- switch.turn_on: relay_ventilador
- switch.turn_off: relay_compresor
- lambda: 'id(pantalla).turn_on();' # Enciende la pantalla
# Configuración de los relés
switch:
- platform: gpio
id: relay_ventilador
name: "Ventilador ${area}"
icon: "mdi:fan"
internal: False
pin: GPIO13
restore_mode: RESTORE_DEFAULT_OFF
inverted: False
- platform: gpio
id: relay_compresor
name: "Compresor ${area}"
icon: "mdi:hvac"
internal: False
pin: GPIO14
restore_mode: ALWAYS_OFF
inverted: False
# Botón físico para alternar entre "Cool" y "Off"
binary_sensor:
- platform: gpio
pin:
number: GPIO19
mode: INPUT_PULLUP
inverted: true
name: "Botón Aire Acondicionado ${area}"
on_press:
then:
- if:
condition:
lambda: 'return id(termostato_area).mode == climate::CLIMATE_MODE_OFF;'
then:
- climate.control:
id: termostato_area
mode: COOL
else:
- climate.control:
id: termostato_area
mode: "OFF"