I made some progress und puplished most of the temperature sensors of the wr3223 to ha.
So I programmed a custom UARTDevice (see [Custom UART Device — ESPHome])(Custom UART Device — ESPHome)
Here is the code for my custom device (sorry for the mess, its my first time developing in c++ )
#include "esphome.h"
class MessageControl {
public:
/** Start der Nachricht */
static constexpr const uint8_t STX = 0x02;
/** Ende der Nachricht */
static constexpr const uint8_t ETX = 0x03;
/** Ende der Übertragung */
static constexpr const uint8_t EOT = 0x04;
/** Anfrage / Anforderung */
static constexpr const uint8_t ENQ = 0x05;
/** Positive Rückmeldung */
static constexpr const uint8_t ACK = 0x06;
/** Negative Rückmeldung */
static constexpr const uint8_t NAK = 0x15;
};
class WR3223Commands {
public:
/** Commando Bit für die Temperatursensoren */
static constexpr const char Temperatur = 'T';
/** Verdampfertemperatur istwert auslesen */
static constexpr const char T_Verdampfer = '1';
/** Kondensatortemperatur auslesen */
static constexpr const char T_Kondensator = '2';
/** Aussentemperatur auslesen */
static constexpr const char T_Aussen = '3';
/** Ablufttemperatur (Raumtemperatur) */
static constexpr const char T_Raum = '4';
/** Temperatur nach Wärmetauscher (Fortluft) lesen */
static constexpr const char T_Fortluft = '5';
/** Zulufttemperatur auslesen */
static constexpr const char T_Zuluft = '6';
/** Temperatur nach Vorheizregister lesen */
static constexpr const char T_NachVorheizregister = '8';
};
class WR3223Connector : public PollingComponent, public UARTDevice {
public:
WR3223Connector(int update_intervall, UARTComponent *parent) : PollingComponent(update_intervall), UARTDevice(parent) { }
uint8_t CmdQuery[8] = { MessageControl::EOT, 0x30, 0x30, 0x31, 0x31, 0x00, 0x00, MessageControl::ENQ };
Sensor *verdampfertemperatur_sensor = new Sensor();
Sensor *kondensatortemperatur_sensor = new Sensor();
Sensor *aussentemperatur_sensor = new Sensor();
Sensor *ablufttemperatur_sensor = new Sensor();
Sensor *fortlufttemperatur_sensor = new Sensor();
Sensor *zulufttemperatur_sensor = new Sensor();
Sensor *nachvorheizregistertemperatur_sensor = new Sensor();
void setup() override { }
int readline(int readch, char *buffer, int len)
{
static int pos = 0;
int rpos;
if (readch > 0) {
switch (readch) {
case '\n': // Ignore new-lines
break;
case MessageControl::ETX: // Ende der Nachricht
buffer[pos++] = readch;
buffer[pos++] = read(); // Checksumme noch mit dranhängen
rpos = pos;
pos = 0; // Reset position index ready for next time
return rpos;
default:
if (pos < len-1) {
buffer[pos++] = readch;
buffer[pos] = 0;
}
}
}
// No end of line has been found, so return -1.
return -1;
}
void publish_temp(char * data, Sensor *sensor) {
try
{
ESP_LOGD("PUBLISHER", "Temperatur: %s", data);
std::string fs(data);
float f = std::stof(fs);
sensor->publish_state(f);
}
catch(const std::exception& e)
{
ESP_LOGE("PUBLISHER", "Fehler beim veröffentlichen der Temperatur: %s", e.what());
}
}
const int data_start = 3;
int get_data_length(char *data, int maxLength)
{
int counter = 0;
if(maxLength < data_start)
return 0;
//ESP_LOGD("get_data_length", "Data MaxLength: %i", maxLength);
for (int i = data_start; i < maxLength + data_start; i++)
{
//ESP_LOGD("get_data_length", "Data Position: %i, Data Char: %c",i, data[i]);
if(uint8_t(data[i]) == MessageControl::ETX)
{
return counter;
}
else
{
counter++;
}
}
ESP_LOGD("get_data_length", "Kein Abschluss-Flag gefunden, so können wir die Datenlänge nicht bestimmen. (counter: %i)", counter);
return 0;
}
void publish_temp(char *data, int len)
{
int dataLength = get_data_length(data, len - 3);
if(dataLength < 1)
return;
char tempData[dataLength+1];
std::copy(data + data_start, data + data_start + dataLength, tempData);
tempData[dataLength+1] = 0;
switch(data[2])
{
case WR3223Commands::T_Verdampfer:
publish_temp(tempData, verdampfertemperatur_sensor);
break;
case WR3223Commands::T_Kondensator:
publish_temp(tempData, kondensatortemperatur_sensor);
break;
case WR3223Commands::T_Aussen:
publish_temp(tempData, aussentemperatur_sensor);
break;
case WR3223Commands::T_Raum:
publish_temp(tempData, ablufttemperatur_sensor);
break;
case WR3223Commands::T_Fortluft:
publish_temp(tempData, fortlufttemperatur_sensor);
break;
case WR3223Commands::T_Zuluft:
publish_temp(tempData, zulufttemperatur_sensor);
break;
case WR3223Commands::T_NachVorheizregister:
publish_temp(tempData, nachvorheizregistertemperatur_sensor);
break;
default:
ESP_LOGD("PUBLISHER", "Unbehandeltes Kommando: %c", data[2]);
break;
}
}
void publish_data(char *data, int len)
{
if(uint8_t(data[0]) != MessageControl::STX)
{
ESP_LOGD("PUBLISHER", "Falscher Beginn der Nachricht: %x",uint8_t(data[0]));
return;
}
if(data[1] == WR3223Commands::Temperatur)
{
publish_temp(data, len);
}
}
/// @brief Setzt die Anfrage für den Controller
/// @param pRequest
/// @return
void SetCommand(const char pCommandCharOne, const char pCommandCharTwo)
{
ESP_LOGD("Writer", "SetCommand: %c%c", pCommandCharOne, pCommandCharTwo);
CmdQuery[5] = uint8_t(pCommandCharOne);
CmdQuery[6] = uint8_t(pCommandCharTwo);
}
void loop() override {
const int max_line_length = 80;
static char buffer[max_line_length];
while (available()) {
int len = readline(read(), buffer, max_line_length);
if(len > 0)
{
ESP_LOGD("Reader", "Buffer length: %i", len);
publish_data(buffer, len);
}
}
}
const int cmd_delay = 500;
void update() override {
SetCommand(WR3223Commands::Temperatur, WR3223Commands::T_Verdampfer);
write_array(CmdQuery, 8);
delay(cmd_delay);
SetCommand(WR3223Commands::Temperatur, WR3223Commands::T_Kondensator);
write_array(CmdQuery, 8);
delay(cmd_delay);
SetCommand(WR3223Commands::Temperatur, WR3223Commands::T_Aussen);
write_array(CmdQuery, 8);
delay(cmd_delay);
SetCommand(WR3223Commands::Temperatur, WR3223Commands::T_Raum);
write_array(CmdQuery, 8);
delay(cmd_delay);
SetCommand(WR3223Commands::Temperatur, WR3223Commands::T_Fortluft);
write_array(CmdQuery, 8);
delay(cmd_delay);
SetCommand(WR3223Commands::Temperatur, WR3223Commands::T_Zuluft);
write_array(CmdQuery, 8);
delay(cmd_delay);
SetCommand(WR3223Commands::Temperatur, WR3223Commands::T_NachVorheizregister);
write_array(CmdQuery, 8);
}
};
And here the yaml file for the esp32:
esphome:
name: "heizung-controller"
includes:
- wr3223_component.h
platformio_options:
build_flags:
-fexceptions
build_unflags:
-fno-exceptions
esp32:
board: az-delivery-devkit-v4
framework:
type: arduino
# Enable logging
logger:
baud_rate: 0
# Enable Home Assistant API
api:
password: !secret api_password
ota:
password: !secret ota_password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "Heizung-Control Fallback Hotspot"
password: !secret wifi_fallback_ap_password
manual_ip:
static_ip: 192.168.178.132
gateway: 192.168.178.1
subnet: 255.255.255.0
captive_portal:
uart:
- id: uart_1
tx_pin: GPIO19
rx_pin: GPIO18
baud_rate: 9600
data_bits: 7
parity: EVEN
stop_bits: 1
debug:
sensor:
- platform: custom
lambda: |-
auto wr3223 = new WR3223Connector(5000, id(uart_1));
App.register_component(wr3223);
return {wr3223->verdampfertemperatur_sensor,
wr3223->kondensatortemperatur_sensor,
wr3223->aussentemperatur_sensor,
wr3223->ablufttemperatur_sensor,
wr3223->fortlufttemperatur_sensor,
wr3223->zulufttemperatur_sensor,
wr3223->nachvorheizregistertemperatur_sensor};
sensors:
- name: "Verdampfertemperatur"
unit_of_measurement: °C
accuracy_decimals: 1
- name: "Kondensatortemperatur"
unit_of_measurement: °C
accuracy_decimals: 1
- name: "Aussentemperatur"
unit_of_measurement: °C
accuracy_decimals: 1
- name: "Ablufttemperatur (Raumtemperatur)"
unit_of_measurement: °C
accuracy_decimals: 1
- name: "Temperatur nach Wärmetauscher (Fortluft)"
unit_of_measurement: °C
accuracy_decimals: 1
- name: "Zulufttemperatur"
unit_of_measurement: °C
accuracy_decimals: 1
- name: "Temperatur nach Vorheizregister"
unit_of_measurement: °C
accuracy_decimals: 1
The Problem with the wr3223 is, you have to ask the system for every sensor. I dont know if this ist the best way to solve it, but it works pretty well:
've read that to set states in the wr3223 using this method, the normal control unit must be pulled out. That’s why I haven’t implemented any switches yet. But I think this is a good start.
I would be happy about any suggestions for improvement or hints, especially about the c++ code.
Best regards