Liebe LT-ler/-innen
Im Thread AutoTerm ferngesteuert habe ich beschrieben wie man die AutoTerm mit dem Remoteanschluss fernsteuert. Das funktioniert sehr gut, auch mit dem virtuellen Thermostat. Das einzig negative ist eigentlich, dass sie immer unter Volllast fährt. Das kann sie gut ab, aber sie ist dadurch auch ein bisschen lauter. Dafür heizt sie natürlich den Innenraum zügiger auf. Also ist folgende Lösung eigentlich nur ein "Luxus-Problem".
Mit der nachfolgenden Lösung kann man die AutoTerm mit dem Panel "Comfort Control" - sollten auch andere gehen wie das PU-27 - parallel schalten, so dass man die eingebauten Funktionen verwenden kann, also Thermostat usw. Aktuell werde ich das nur verwenden um Daten der Heizung zu bekommen, also Status und Temperaturen. Für die Steuerung muss ich mir noch ein paar Sachen anschauen. Grundsätzlich sollte das aber bereits gehen. Das folgende Modul wird quasi als "man in the middle" zwischen Control Panel und Heizung angeschlossen.
Wir brauchen dazu - wie soll es auch anders sein ein ESP32 Modul - und ein paar andere Dinge:
1 x JST Verbinder
1 x ESP32 Modul
1 x Leiterplatte
Dann natürlich - weil es mit dem Feuerzeug oder Streichhölzer schlecht machbar ist - nen Lötkolben/-station, Lötzinn und natürlich bisschen Geschick und Zeit.
Verdrahtet wird das folgendermassen:
Wenn man das fertig verdrahtet hat - auch wenn es nicht schön aussieht, Platine ätzen kann ich nicht - sollte das Ergebnis in etwa so aussehen. Ich hab noch eine DC-Buchse angelötet dass ich das alternativ mit 12V versorgen kann, z.B. zum messen oder analysieren. Das ist für den Betrieb nicht notwendig. An dem DC-DC Konverter ist oben auf dem blauen Bauteil eine Messingschraube. Diese so lange drehen bis ihr am Ausgang 5V habt. Dazu natürlich ein Multimeter am Ausgang zur Prüfung anschliessen. Da müsst ihr relativ viel drehen, dachte Anfangs das Ding ist defekt.
Wenn man kein HA hat oder will, kann man natürlich auch via Web-Browser darauf zugreifen und so die Daten anschauen oder anpassen:
Bildschirmfoto 2025-03-17 um 19.01.53.png
In HA würde das dann folgendermassen aussehen - aktuell hab ich nur ein einfaches Dashboard erstellt - wenn ich mehr über die Daten herausgefunden habe werde ich vermutlich ein schöneres erstellen.
Bildschirmfoto 2025-03-17 um 20.27.53.png
Die Daten kommen automatisch ins HA wenn der ESP32 erfolgreich geflasht wurde und ESPHome danach das Modul erkannt hat. Hier noch der Source-Code zum Flashen/programmieren:
esphome:
name: dieselheater
friendly_name: Diesel-Heater
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
ota:
- platform: esphome
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Dieselheater Hotspot"
web_server:
port: 80
captive_portal:
uart:
- id: uart_1_controller
baud_rate: 2400 #autoterm4d:2400
tx_pin: 17
rx_pin: 16
debug:
direction: RX
dummy_receiver: true
after:
delimiter: "\r\n"
sequence:
- uart.write: #Write rx to tx.
id: uart_2_heater
data: !lambda return bytes ;
- lambda: |-
ESP_LOGD("custom", "Display:");
UARTDebug::log_hex(direction, bytes, ':');
- id: uart_2_heater
baud_rate: 2400
tx_pin: 1
rx_pin: 3
debug:
direction: RX
dummy_receiver: true
after:
delimiter: "\r\n"
sequence:
- uart.write: #Write rx to tx.
id: uart_1_controller
data: !lambda return bytes ;
- lambda: |-
ESP_LOGD("custom", "Heater:");
UARTDebug::log_hex(direction, bytes, ':');
globals:
- id: read_state
type: int
restore_value: no
initial_value: '0'
- id: message_length
type: int
restore_value: no
- id: message_data
type: std::vector<uint8_t>
restore_value: no
- id: heaterStatus
type: int
initial_value: '0'
- id: connected
type: bool
initial_value: 'false'
- id: tempSet
type: int
initial_value: '15'
restore_value: no
- id: powerSet
type: int
initial_value: '2'
restore_value: no
script:
- id: send_uart_command
then:
- lambda: |-
std::vector<uint8_t> byteArray = {0xAA, 0x03, 0x06, 0x00, 0x01, 0xFF, 0xFF
, (uint8_t) static_cast<uint8_t>(id(power_mode_slider).state)
, (uint8_t) static_cast<uint8_t>(id(tempSet))
, (uint8_t) static_cast<uint8_t>(id(ventilation_slider).state)
, (uint8_t) static_cast<uint8_t>(id(powerSet))};
uint16_t crc = 0xFFFF;
for (uint8_t pos = 0; pos < byteArray.size(); pos++) {
crc ^= byteArray[pos];
for (uint8_t i = 0; i < 8; i++) {
bool odd = crc & 0x0001;
crc >>= 1;
if (odd) {
crc ^= 0xA001;
}
}
}
byteArray.push_back((crc >> 8) & 0xff);
byteArray.push_back(crc & 0xff);
id(uart_2_heater).write_array(&byteArray[0], byteArray.size());
for (uint8_t i = 0; i < byteArray.size(); i++) {
ESP_LOGD("send_uart_command", "byteArray[%d] = %02X", i, byteArray[i]);
}
- id: update_Settings_uart_command
then:
- lambda: |-
ESP_LOGD("Updating", "");
std::vector<uint8_t> byteArray = {0xAA, 0x03, 0x06, 0x00, 0x02, 0xFF, 0xFF
, (uint8_t) static_cast<uint8_t>(id(power_mode_slider).state)
, (uint8_t) static_cast<uint8_t>(id(tempSet))
, (uint8_t) static_cast<uint8_t>(id(ventilation_slider).state)
, (uint8_t) static_cast<uint8_t>(id(powerSet))};
uint16_t crc = 0xFFFF;
for (uint8_t pos = 0; pos < byteArray.size(); pos++) {
crc ^= byteArray[pos];
for (uint8_t i = 0; i < 8; i++) {
bool odd = crc & 0x0001;
crc >>= 1;
if (odd) {
crc ^= 0xA001;
}
}
}
byteArray.push_back((crc >> 8) & 0xff);
byteArray.push_back(crc & 0xff);
id(uart_2_heater).write_array(&byteArray[0], byteArray.size());
for (uint8_t i = 0; i < byteArray.size(); i++) {
ESP_LOGD("send_uart_command", "byteArray[%d] = %02X", i, byteArray[i]);
}
switch:
- platform: uart
uart_id: uart_2_heater
name: "TurnOnHeaterP3V1heater"
data: [0xAA, 0x03, 0x06, 0x00, 0x01, 0xFF, 0xFF, 0x01, 0x0F, 0x00, 0x05, 0xB6, 0x1F]
- platform: uart
uart_id: uart_2_heater
name: "Turn On Heater in T panel Mode"
data: [0xAA, 0x03, 0x06, 0x00, 0x01, 0xFF, 0xFF, 0x02, 0x0F, 0x02, 0x05, 0x92, 0x1E]
- platform: template
name: "Controllable Heater Start"
lambda: |-
if (id(heaterStatus) != 0) {
return true;
} else {
return false;
}
turn_on_action:
- script.execute: send_uart_command
turn_off_action:
uart.write:
id: uart_2_heater
data: [0xAA, 0x03, 0x00, 0x00, 0x03, 0x5D, 0x7C]
- platform: uart
uart_id: uart_2_heater
name: "Shutdown"
data: [0xAA, 0x03, 0x00, 0x00, 0x03, 0x5D, 0x7C]
- platform: template
name: "Ventillator Power"
turn_on_action:
- uart.write:
id: uart_2_heater
data: [0xAA, 0x03, 0x04, 0x00, 0x23, 0xFF, 0xFF, 0x08, 0xFF]
turn_off_action:
- uart.write:
id: uart_2_heater
data: [0xAA, 0x03, 0x00, 0x00, 0x03]
sensor:
- platform: template
name: "Heater Status"
id: heater_status
update_interval: 1s
- platform: template
name: "Heater Battery Voltage"
id: heater_battery_voltage
update_interval: 1s
unit_of_measurement: "V"
icon: "mdi:battery"
- platform: template
name: "Heater Ventilation Power"
id: heater_ventilation_power
update_interval: 1s
unit_of_measurement: "W"
- platform: template
name: "Heater Temperature"
id: heater_temperature
update_interval: 1s
unit_of_measurement: "°C"
icon: "mdi:thermometer"
- platform: template
name: "Heater Temperature By Panel"
id: heater_temperature_panel
unit_of_measurement: "°C"
icon: "mdi:thermometer"
update_interval: 1s
- platform: template
name: "Heater Power"
id: heater_power_status
update_interval: 1s
unit_of_measurement: '%'
- platform: template
name: "Set Temperature"
id: set_temperature
update_interval: 1s
unit_of_measurement: "°C"
icon: "mdi:thermometer"
- platform: template
name: "Air Temperature"
id: air_temperature
update_interval: 1s
unit_of_measurement: "°C"
icon: "mdi:thermometer"
number:
- platform: template
name: "temperature Slider"
id: "temperature_slider"
step: 1
min_value: 0
max_value: 30
mode: slider
initial_value: '15'
optimistic: true
set_action:
lambda: |-
id(tempSet)=x;
id(update_Settings_uart_command).execute();
- platform: template
name: "power Slider"
id: "power_slider"
step: 1
min_value: 1
max_value: 10
initial_value: '3'
mode: slider
optimistic: true
set_action:
lambda: |-
id(powerSet)=x-1;
id(update_Settings_uart_command).execute();
int modeValue = id(power_slider).state; // Änderung hier zu power_slider
std::map<int, std::string> modes = {{1, "By T Heater"}, {2, "By T Panel"}, {3, "By T Air"}, {4, "Power Mode"}};
if (modes.find(modeValue) != modes.end()) {
id(power_mode_text).publish_state(modes[modeValue]);
} else {
id(power_mode_text).publish_state("Unknown");
}
- platform: template
name: "power mode Slider"
id: "power_mode_slider"
step: 1
min_value: 1
max_value: 4
mode: slider
optimistic: true
set_action:
then:
- script.execute: update_Settings_uart_command
- platform: template
name: "ventilation on/off Slider"
id: "ventilation_slider"
step: 1
min_value: 1
max_value: 2
mode: slider
optimistic: true
set_action:
then:
- script.execute: update_Settings_uart_command
text_sensor:
- platform: template
name: "Power Mode Text"
id: power_mode_text
update_interval: 10s
- platform: template
name: "Heater Ventillator Mode Text"
id: heater_ventillator_mode_text
update_interval: 10s
interval:
- interval: 20ms
then:
- lambda: |-
static uint8_t command_id = 0;
while (id(uart_2_heater).available())
{
uint8_t byte;
id(uart_2_heater).read_byte(&byte);
if (id(read_state) == 0 && byte == 0xAA)
{
// Start of message
id(read_state) = 1;
id(message_data).clear();
}
else if (id(read_state) == 1)
{
id(read_state) = 2;
}
else if (id(read_state) == 2)
{
// Message length
id(message_length) = byte;
id(read_state) = 3;
}
else if (id(read_state) == 3)
{
// 00
id(read_state) = 4;
}
else if (id(read_state) == 4)
{
// this is command id
id(read_state) = 5;
command_id=byte;
}
else if(id(read_state) == 5)
{
id(message_data).push_back(byte);
if (id(message_data).size() >= id(message_length))
{
if (command_id==15)
{
id(connected)=true;
int temperature_calc_heater = id(message_data)[3];
//ESP_LOGD("send_uart_command","Temp Message", id(message_data)[3]);
if (temperature_calc_heater > 127) {
// ESP_LOGD("send_uart_command","Temp temperature_calc_heater", temperature_calc_heater);
temperature_calc_heater = (temperature_calc_heater -256);
// ESP_LOGD("send_uart_command","Temp temperature_calc_heater_calculated", temperature_calc_heater);
}
id(heater_temperature).publish_state(temperature_calc_heater);
//id(heater_temperature).publish_state(id(message_data)[3]);
id(heater_status).publish_state(id(message_data)[9]);
uint8_t statusByte = id(message_data)[9];
int value = static_cast<int>(statusByte);
id(heaterStatus) = value;
id(heater_battery_voltage).publish_state(id(message_data)[6] / 10.0);
id(heater_ventilation_power).publish_state((id(message_data)[16]));
int temperature_calc_air = id(message_data)[4];
//ESP_LOGD("send_uart_command","Temp Message", id(message_data)[4]);
if (temperature_calc_air > 127) {
// ESP_LOGD("send_uart_command","Temp temperature_calc_air", temperature_calc_air);
temperature_calc_air = (temperature_calc_air -256);
// ESP_LOGD("send_uart_command","Temp temperature_calc_air_calculated", temperature_calc_air);
}
id(air_temperature).publish_state(temperature_calc_air);
}
else if (command_id==17)
{
int temperature_calc_panel = id(message_data)[0];
//ESP_LOGD("send_uart_command","Temp Message", id(message_data)[0]);
if (temperature_calc_panel > 127) {
// ESP_LOGD("send_uart_command","Temp temperature_calc_panel", temperature_calc_panel);
temperature_calc_panel = (temperature_calc_panel -256);
// ESP_LOGD("send_uart_command","Temp temperature_calc_panel_calculated", temperature_calc_panel);
}
id(heater_temperature_panel).publish_state(temperature_calc_panel);
//id(temperature_heater).publish_state(id(message_data)[0]);
}
else if (command_id==2 && id(heaterStatus)!=0)
{
id(set_temperature).publish_state(id(message_data)[3]);
id(heater_power_status).publish_state(id(message_data)[5]+1);
std::map<uint32_t, std::string> modes = {{1, "By T Heater"}, {2, "By T Panel"}, {3, "By T Air"}, {4, "By Power"}};
uint32_t mode = round(id(message_data)[2]);
if (modes.find(mode) != modes.end()) {
id(power_mode_text).publish_state(modes[mode]);
} else {
id(power_mode_text).publish_state("Unknown");
}
modes = {{1, "On"}, {2, "Off"}};
mode = round(id(message_data)[4]);
if (modes.find(mode) != modes.end()) {
id(heater_ventillator_mode_text).publish_state(modes[mode]);
} else {
id(heater_ventillator_mode_text).publish_state("Unknown");
}
//id(power_mode_slider).publish_state(id(message_data)[2]);
//id(ventilation_slider).publish_state(id(message_data)[4]);
if(id(temperature_slider).state!=id(message_data)[3])
id(temperature_slider).publish_state(id(message_data)[3]);
if(id(power_slider).state!=id(message_data)[5])
id(power_slider).publish_state(id(message_data)[5]+1);
}
id(read_state) = 0;
}
}
}
Display More
Der Code ist eigentlich für die 2D (da müsste man die Baud-Rate von 9600 verwenden - bei der 5D 2400) - aber bis jetzt funktioniert alles auch bei der 5D.
Bei mir habe ich allerdings andere Stecker verbaut (Verlängerungskabel 5m) deshalb hier noch ein entsprechendes Bild, sollte jemand auch so einen Stecker haben, so werden natürlich andere Anschlüsse benötigt.
Einige haben ja die AutoTerm als Zusatzheizung verbaut, vielleicht ist das ja etwas für euch.
Wie immer, viel Spass beim nachbauen...