So, one of my adax heaters isn’t playing nice with the local, official integration, and I stumble upon this, and figure I’d give it a go… Except I can’t get it to compile…
Tried to copy as close as possible without success.
The esphome:
substitutions:
DEFAULT_CURRENT_IDLE: "0.033"
DEFAULT_CURRENT_HEATING: "0.033"
DEFAULT_VOLTAGE: "230"
NTC_SELF_HEATING_COMPENSATION: "-0.2"
MIN_TEMPERATURE: "5.0"
MAX_TEMPERATURE: "25.0"
DISPLAY_TIMEOUT: "10000"
DISPLAY_MODE_OFF: "Off"
DISPLAY_MODE_INTERACT: "Interact"
DISPLAY_MODE_ON: "On"
DISPLAY_STATE_NONE: "0"
DISPLAY_STATE_CURRENT_TEMP: "1"
DISPLAY_STATE_TARGET_TEMP: "2"
DISPLAY_STATE_TURN_ON: "3"
DISPLAY_STATE_TURN_OFF: "4"
esphome:
name: "adax_vavstuga"
friendly_name: "Adax vävstuga"
name_add_mac_suffix: True
esp32:
board: esp32dev
flash_size: 8MB
framework:
type: arduino
logger:
level: INFO
api:
id: api_id
ota:
- platform: esphome
wifi:
id: wifi_id
ssid: !secret wifi_ssid
password: !secret wifi_password
captive_portal:
web_server:
port: 80
version: 3
time:
- platform: homeassistant
id: hassTime
external_components:
source: github://asergunov/7segment_gpio
globals:
- id: interactTimeout
type: int
initial_value: "0"
- id: displayState
type: int
initial_value: ${DISPLAY_STATE_NONE}
- id: displayFractional
type: bool
initial_value: "false"
number:
- platform: template
id: currentIdle
name: "Current Idle"
icon: mdi:flash-off-outline
mode: BOX
unit_of_measurement: "A"
disabled_by_default: True
entity_category: CONFIG
min_value: 0
max_value: 1
step: 0.001
restore_value: True
initial_value: ${DEFAULT_CURRENT_IDLE}
optimistic: True
set_action:
then:
- component.update: currentPower
- platform: template
id: currentHeating
name: "Current Heating"
icon: mdi:flash-outline
mode: BOX
unit_of_measurement: "A"
disabled_by_default: True
entity_category: CONFIG
min_value: 0
max_value: 12
step: 0.001
restore_value: True
initial_value: ${DEFAULT_CURRENT_HEATING}
optimistic: True
set_action:
then:
- component.update: currentPower
- platform: template
id: voltage
name: "Voltage"
icon: mdi:sine-wave
mode: BOX
unit_of_measurement: "V"
disabled_by_default: True
entity_category: CONFIG
min_value: 100
max_value: 300
step: 0.1
restore_value: False
initial_value: ${DEFAULT_VOLTAGE}
optimistic: True
set_action:
then:
- component.update: currentPower
- platform: template
id: temperatureCompensation
name: "Temperature Compensation"
icon: mdi:thermometer-minus
mode: BOX
unit_of_measurement: "°C"
disabled_by_default: True
entity_category: CONFIG
min_value: -10
max_value: 10
step: 0.01
restore_value: True
initial_value: 0
optimistic: True
select:
- platform: template
id: displayMode
name: "Display Mode"
icon: mdi:counter
disabled_by_default: True
entity_category: CONFIG
optimistic: True
restore_value: True
options:
- ${DISPLAY_MODE_OFF}
- ${DISPLAY_MODE_INTERACT}
- ${DISPLAY_MODE_ON}
initial_option: ${DISPLAY_MODE_INTERACT}
- platform: template
id: ledMode
name: "Led Mode"
icon: mdi:led-on
disabled_by_default: True
entity_category: CONFIG
optimistic: True
restore_value: True
options:
- ${DISPLAY_MODE_OFF}
- ${DISPLAY_MODE_INTERACT}
- ${DISPLAY_MODE_ON}
initial_option: ${DISPLAY_MODE_ON}
switch:
- platform: factory_reset
id: factoryReset
internal: True
- platform: gpio
id: connectionLed
pin:
number: GPIO18
mode: output
inverted: True
- platform: gpio
id: heatingLed
pin:
number: GPIO19
mode: output
inverted: True
- platform: gpio
id: heatingControl
pin:
number: GPIO27
mode: output
on_turn_on:
then:
- component.update: currentPower
on_turn_off:
then:
- component.update: currentPower
- platform: template
id: childLock
name: "Child Lock"
icon: mdi:lock
disabled_by_default: True
entity_category: CONFIG
optimistic: True
restore_mode: RESTORE_DEFAULT_OFF
button:
- platform: restart
name: "Restart"
disabled_by_default: True
entity_category: DIAGNOSTIC
- platform: factory_reset
name: "Factory Reset"
disabled_by_default: True
entity_category: DIAGNOSTIC
binary_sensor:
- platform: gpio
pin:
number: GPIO22
mode:
input: True
inverted: True
id: minusButton
on_multi_click:
- timing:
- ON for at most 1s
then:
- lambda: id(modTargetTemp)->execute(false, false);
- platform: gpio
pin:
number: GPIO35
mode:
input: True
inverted: True
id: plusButton
on_multi_click:
- timing:
- ON for at most 1s
then:
- lambda: id(modTargetTemp)->execute(true, false);
- platform: gpio
pin:
number: GPIO34
mode:
input: True
inverted: True
id: okButton
on_multi_click:
- timing:
- ON for at most 1s
then:
- lambda: id(setDisplayState)->execute(${DISPLAY_STATE_CURRENT_TEMP}, false);
- timing:
- ON for 3s to 10s
then:
- lambda: |-
if (!id(childLock).state) {
auto call = id(climateControl).make_call();
if (id(climateControl).mode == CLIMATE_MODE_OFF) {
call.set_mode("HEAT");
} else {
call.set_mode("OFF");
}
call.perform();
if (id(climateControl).mode == CLIMATE_MODE_OFF) {
id(setDisplayState)->execute(${DISPLAY_STATE_TURN_OFF}, false);
} else {
id(setDisplayState)->execute(${DISPLAY_STATE_TURN_ON}, false);
}
}
- timing:
- ON for at least 10s
then:
- lambda: if (!id(childLock).state) { id(factoryReset).turn_on(); }
- platform: template
name: "Heating"
icon: mdi:radiator
disabled_by_default: True
condition:
switch.is_on:
id: heatingControl
sensor:
- platform: adc
id: tempSensorVoltage
pin: A0
filters:
- sliding_window_moving_average:
window_size: 24
send_every: 12
update_interval: 5s
- platform: resistance
id: tempSensorResistance
sensor: tempSensorVoltage
configuration: DOWNSTREAM
resistor: 91kOhm
reference_voltage: 2.925
- platform: ntc
id: currentTemp
name: "Current Temperature"
icon: mdi:thermometer
unit_of_measurement: "°C"
device_class: temperature
disabled_by_default: True
accuracy_decimals: 1
sensor: tempSensorResistance
calibration:
reference_temperature: 25°C
reference_resistance: 10kOhm
b_constant: 3950
filters:
- lambda: return x + ${NTC_SELF_HEATING_COMPENSATION} + id(temperatureCompensation).state;
- platform: template
name: "Current Power"
id: currentPower
unit_of_measurement: "W"
accuracy_decimals: 1
device_class: power
disabled_by_default: True
lambda: |-
if (id(heatingControl).state) {
return id(currentHeating).state * id(voltage).state;
} else {
return id(currentIdle).state * id(voltage).state;
}
- platform: total_daily_energy
name: "Energy Consumption"
power_id: currentPower
unit_of_measurement: "kWh"
device_class: energy
disabled_by_default: True
method: left
accuracy_decimals: 3
filters:
- multiply: 0.001
- platform: wifi_signal
name: "Signal dB"
icon: mdi:wifi-strength-3
disabled_by_default: True
entity_category: DIAGNOSTIC
- platform: template
id: targetTemperature
name: "Target temperature"
icon: mdi:thermometer-lines
unit_of_measurement: "°C"
device_class: temperature
disabled_by_default: True
accuracy_decimals: 1
lambda: return id(climateControl).target_temperature;
text_sensor:
- platform: wifi_info
ip_address:
name: "IP Address"
icon: mdi:ip
disabled_by_default: True
entity_category: DIAGNOSTIC
ssid:
name: "Network SSID"
icon: mdi:wifi
disabled_by_default: True
entity_category: DIAGNOSTIC
mac_address:
name: "MAC Address"
icon: mdi:numeric
disabled_by_default: True
entity_category: DIAGNOSTIC
climate:
- platform: thermostat
id: climateControl
name: "Thermostat"
visual:
min_temperature: ${MIN_TEMPERATURE}
max_temperature: ${MAX_TEMPERATURE}
temperature_step: 0.5
sensor: currentTemp
min_heating_off_time: 10s
min_heating_run_time: 10s
min_idle_time: 10s
heat_deadband: 0.4
heat_overrun: 0.0
heat_action:
- switch.turn_on: heatingControl
idle_action:
- switch.turn_off: heatingControl
target_temperature_change_action:
then:
- lambda: |-
id(targetTemperature).publish_state(id(climateControl).target_temperature);
on_control:
- lambda: |-
if (id(climateControl).mode == CLIMATE_MODE_HEAT) {
if (x.get_mode() == CLIMATE_MODE_OFF) {
id(setDisplayState)->execute(${DISPLAY_STATE_TURN_OFF}, false);
} else if (id(climateControl).target_temperature != x.get_target_temperature()) {
id(setDisplayState)->execute(${DISPLAY_STATE_TARGET_TEMP}, false);
}
} else {
if (x.get_mode() == CLIMATE_MODE_HEAT) {
id(setDisplayState)->execute(${DISPLAY_STATE_TURN_ON}, false);
} else if (id(climateControl).target_temperature != x.get_target_temperature()) {
id(setDisplayState)->execute(${DISPLAY_STATE_TARGET_TEMP}, false);
}
}
display:
- platform: 7segment_gpio
id: lcd
digit_pins: [14, 13]
iterate_digits: True
segment_pins: [4, 32, 33, 26, 25, 21, 23]
compensate_brightness: True
intensity: 0
update_interval: 200ms
interval:
- interval: 60s
then:
- lambda: id(currentPower).publish_state(id(currentPower).state);
- interval: 200ms
then:
- lambda: |-
unsigned long _Millis = millis();
id(setLedStates)->execute(_Millis);
if (_Millis - id(interactTimeout) > ${DISPLAY_TIMEOUT}) {
id(displayState) = ${DISPLAY_STATE_NONE};
}
id(updateDisplay)->execute(_Millis);
script:
- id: setHeatingLedState
parameters:
state: bool
then:
- lambda: |-
if (state) {
if (!id(heatingLed).state) {
id(heatingLed).turn_on();
}
} else {
if (id(heatingLed).state) {
id(heatingLed).turn_off();
}
}
- id: setConnectionLedState
parameters:
state: bool
then:
- lambda: |-
if (state) {
if (!id(connectionLed).state) {
id(connectionLed).turn_on();
}
} else {
if (id(connectionLed).state) {
id(connectionLed).turn_off();
}
}
- id: setLedStates
parameters:
millis: int
then:
- lambda: |-
auto _LedMode = id(ledMode).active_index();
if ((_LedMode == 2) || ((_LedMode == 1) && ((millis - id(interactTimeout)) < ${DISPLAY_TIMEOUT}))) {
bool _LedOn = (millis / 1000) % 2 == 0;
id(setHeatingLedState)->execute(id(heatingControl).state || ((id(climateControl).mode == CLIMATE_MODE_HEAT) && _LedOn));
id(setConnectionLedState)->execute(id(wifi_id).is_connected() && (id(api_id).is_connected() || _LedOn));
} else {
id(setHeatingLedState)->execute(false);
id(setConnectionLedState)->execute(false);
}
- id: updateDisplay
parameters:
millis: int
then:
- lambda: |-
auto _displayMode = id(displayMode).active_index();
int _displayState = id(displayState);
if (_displayMode == 0) {
_displayState = ${DISPLAY_STATE_NONE};
} else {
if (millis - id(interactTimeout) > ${DISPLAY_TIMEOUT}) {
if (_displayMode == 1) {
_displayState = ${DISPLAY_STATE_NONE};
} else {
_displayState = ${DISPLAY_STATE_CURRENT_TEMP};
}
}
}
switch(_displayState) {
case ${DISPLAY_STATE_CURRENT_TEMP}:
case ${DISPLAY_STATE_TARGET_TEMP}:
float val;
if (_displayState == ${DISPLAY_STATE_CURRENT_TEMP}) {
val = id(climateControl).current_temperature;
} else {
val = id(climateControl).target_temperature;
}
val = round(val * 10.0f) / 10.0f;
if (val < 0) {
lcd->print("-0");
} else {
if ((((millis - id(interactTimeout)) / 1000) % 2) == id(displayFractional)) {
lcd->printf(0, "%02d", static_cast<int>(val));
} else {
lcd->printf(0, "_%d", static_cast<int>(round(val * 10.0f)) % 10);
}
}
break;
case ${DISPLAY_STATE_TURN_ON}:
lcd->print("ON");
break;
case ${DISPLAY_STATE_TURN_OFF}:
lcd->print("OF");
break;
default:
lcd->print(" ");
}
- id: setDisplayState
parameters:
state: int
fractional: bool
then:
- lambda: |-
unsigned long _Millis = millis();
id(displayFractional) = fractional;
id(interactTimeout) = _Millis;
id(displayState) = state;
id(updateDisplay)->execute(_Millis);
- id: modTargetTemp
parameters:
up: bool
large: bool
then:
- lambda: |-
bool _displayFractional = false;
if (id(displayState) == ${DISPLAY_STATE_TARGET_TEMP}) {
if (!id(childLock).state) {
double _current = id(climateControl).target_temperature;
double _value = _current + (up ? (large ? 1.0f : 0.5f) : (large ? -1.0f : -0.5f));
_value = round(_value * 10.0f) / 10.0f;
_value = std::min(${MAX_TEMPERATURE}, std::max(${MIN_TEMPERATURE}, _value));
_displayFractional = floor(_value) == floor(_current);
auto call = id(climateControl).make_call();
call.set_target_temperature(_value);
call.perform();
}
}
id(setDisplayState)->execute(${DISPLAY_STATE_TARGET_TEMP}, _displayFractional);
The result, normal stuff followed by:
src/esphome/components/7segment_gpio/7segment_gpio.cpp:189:51: warning: ignoring attribute 'section (".iram1.3")' because it conflicts with previous 'section (".iram1.1")' [-Wattributes]
189 | void IRAM_ATTR HOT LcdDigitsData::timer_interrupt() {
| ^
In file included from src/esphome/components/7segment_gpio/7segment_gpio.cpp:1:
src/esphome/components/7segment_gpio/7segment_gpio.h:70:22: note: previous declaration here
70 | void IRAM_ATTR HOT timer_interrupt();
| ^~~~~~~~~~~~~~~
src/esphome/components/7segment_gpio/7segment_gpio.cpp: In member function 'void esphome::lcd_digits::LcdDigitsComponent::set_mode(Mode)':
src/esphome/components/7segment_gpio/7segment_gpio.cpp:355:5: error: 'timerAlarmEnable' was not declared in this scope; did you mean 'timerAlarm'?
355 | timerAlarmEnable(timer);
| ^~~~~~~~~~~~~~~~
| timerAlarm
src/esphome/components/7segment_gpio/7segment_gpio.cpp:358:5: error: 'timerAlarmDisable' was not declared in this scope
358 | timerAlarmDisable(timer);
| ^~~~~~~~~~~~~~~~~
src/esphome/components/7segment_gpio/7segment_gpio.cpp: In member function 'virtual void esphome::lcd_digits::LcdDigitsComponent::setup()':
src/esphome/components/7segment_gpio/7segment_gpio.cpp:436:21: error: too many arguments to function 'hw_timer_t* timerBegin(uint32_t)'
436 | timer = timerBegin(0, 80, true);
| ~~~~~~~~~~^~~~~~~~~~~~~
In file included from /data/cache/platformio/packages/framework-arduinoespressif32/cores/esp32/esp32-hal.h:98,
from /data/cache/platformio/packages/framework-arduinoespressif32/cores/esp32/Arduino.h:44,
from src/esphome/core/macros.h:7,
from src/esphome.h:2,
from src/esphome/components/7segment_gpio/7segment_gpio.h:15:
/data/cache/platformio/packages/framework-arduinoespressif32/cores/esp32/esp32-hal-timer.h:35:13: note: declared here
35 | hw_timer_t *timerBegin(uint32_t frequency);
| ^~~~~~~~~~
src/esphome/components/7segment_gpio/7segment_gpio.cpp:438:25: error: too many arguments to function 'void timerAttachInterrupt(hw_timer_t*, void (*)())'
438 | timerAttachInterrupt(timer, &s_timer_intr, true);
| ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~
/data/cache/platformio/packages/framework-arduinoespressif32/cores/esp32/esp32-hal-timer.h:50:6: note: declared here
50 | void timerAttachInterrupt(hw_timer_t *timer, void (*userFunc)(void));
| ^~~~~~~~~~~~~~~~~~~~
src/esphome/components/7segment_gpio/7segment_gpio.cpp:442:5: error: 'timerAlarmWrite' was not declared in this scope; did you mean 'timerWrite'?
442 | timerAlarmWrite(timer, 50, true);
| ^~~~~~~~~~~~~~~
| timerWrite
src/esphome/components/7segment_gpio/7segment_gpio.cpp:443:5: error: 'timerAlarmEnable' was not declared in this scope; did you mean 'timerAlarm'?
443 | timerAlarmEnable(timer);
| ^~~~~~~~~~~~~~~~
| timerAlarm
Compiling .pioenvs/adax_vavstuga/src/esphome/components/api/list_entities.cpp.o
Compiling .pioenvs/adax_vavstuga/src/esphome/components/api/proto.cpp.o
*** [.pioenvs/adax_vavstuga/src/esphome/components/7segment_gpio/7segment_gpio.cpp.o] Error 1
========================= [FAILED] Took 18.37 seconds =========================
I’ve been able to troubleshoot most esphome stuff I’ve run into, but external components are somewhat beyond me… All I can find is some stuff about timerAlarmEnable being removed from arduino esp32 3.0 api, which whilst probably interesting on some level, seems irrelevant since when I tried to set arduino to a pre-3.0 version I got an error saying it only supports 3.0 and higher or something similar.
Or is that the problem?
Edit: nevermind, seems that was indeed the problem… found a fork of 7segment_gpio that solved it.
4 more to go, then figure out how to do the same on my 2 esp8266 adax heaters.
Many thanks to McTB!