Skip to content

Bauanleitung Optolink Adapter auf Basis ESP32 S3, C6 oder C3

AutoLotion edited this page Feb 14, 2026 · 1 revision

Einleitung

Im Rahmen des Energiemanagements in unserem Haus war ich auf der Such nach einer Möglichkeit meine Wärmepumpe vom Typ Viessmann Vitocal 200-G BWP 108 abhängig von der Solar Produktion usw. steuern zu können. In der Community von https://github.com/openv/openv/wiki habe ich einige nützliche Tipps gefunden. Allen Beitragenden sei hiermit gedankt.

In den verschiedenen Bauanleitungen für Optolink Adapter fand ich nicht so genau das, was ich mir vorgestellt hatte. Ich wollte einen möglichst kleinen Adapter mit Wifi Anbindung bauen, der in die kleine Türe passte, welche die Wärmepumpe vor dem Bedienteil aufklappt und direkte ESPHome Unterstützung in Home Assistant mitbringt. Zudem sollte der verwendete Prozessor von der neusten Generation mit aktueller Konnektivität sein, damit die Arbeit, die ich da reinstecke, auch für noch weitere ESPHome Projekt nutzbar sein sollte. Meine Wahl fiel auf die ESP32 Prozessor Plattform und darin den ES32 S3 oder den ESP32 C6, welche beide auf einem kleinen Board (21 x 17.8mm) sehr kompakt daherkommen. Der ESP32 C6 bringt obendrein Wifi 6 und Zigbee mit, was für die Konnektivität viele Möglichkeiten offenlässt. Ein vollständiger Optolink Adapter mit Sender und Empfänger kann damit gerade mal mit den Dimensionen 4.0x2.5x0.5cm (LxBxH) aufgebaut werden. Als Erstes wagte ich mich an den Aufbau eines Optolink Adapters mit dem ESP32 S3 Board von Seeed Studio. Siehe https://wiki.seeedstudio.com/xiao_esp32s3_getting_started/. Allerdings bin ich dabei auf ein paar unerwartete Schwierigkeiten bestossen, die ich mit dem GitHub Issue #699 «Probleme beim Senden resp. Empfangen mit Optolink Adapter auf Basis ESP32 S3» https://github.com/openv/openv/issues/699 protokolliert und zur Diskussion in der Community gestellt habe. Nach Langen Versuchen ist es mir dann doch noch gelungen die Optolink Kommunikation mit der Wärmepumpe aufzubauen. Die wesentlichen Erkenntnisse daraus sind: • Der Standard UART0 kann nicht für die Optolink Kommunikation verwendet werden. Wahrscheinlich wegen der internen Beschaltung auf dem Board in Zusammenhang mit dem USB-C Anschluss. • Die UART Konfiguration für die serielle Kommunikation muss so gesetzt werden, dass der für die Optolink Kommunikation benutzte UART1 nicht der erste in der Liste der UART Konfigurationen ist. UART0 muss der Erste in der Liste sein und explizit aufgeführt werden, auch wenn gar nicht verwendet. • Je nach Viessmann Gerät oder auch nur je nach dem eingesetzten ESP32 Board, muss die Baudrate angepasst werden. Bei meinen ESP32 S3 und C6 Boards auf 5040 Baud. • Eine Verstärkung von Sender und Empfänger mit Transistoren ist bei den ESP32 S3 und ESP32 C6 Boards nicht nötig. Selbst nicht bei Verwendung des Low Power UARTs auf dem ESP32 C6. • Der Abstand von Sender und Empfänger zum Gegenpart an der Wärmepumpe ist gar nicht so heikel. Mit 1.5cm Abstand läuft die Kommunikation immer noch.

ESP32 S3 und ESP32 C6 haben das gleiche Problem mit der Baudrate und den zwei UARTs, die konfiguriert werden müssen. Ich vermute, dass es beim ESP32 C3 dann auch so sein wird.

Diese Bauanleitung vom Optolink Adapter basiert auf den oben erwähnten Erfahrungen, die ich hiermit gerne an die Community weitergeben möchte.

Vergleich Prozessoren resp. Boards ESP32 S3, C6, C3 von Seeed Studio

https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/

image

Die Prozessorleistung ist für die Optolink Kommunikation gar nicht so auschlaggebend. Wenn auf dem Board resp. auf dem Adapter nicht noch weitere Sensoren genutzt werden sollen, kann aus meiner heutigen Sicht auch das ESP32 C3 Board eingesetzt werden.

Laut Espressif Datenblatt bringt der ESP32 S3 Prozessor 3 Hardware UARTs mit, der ESP32 C6 2 HW UARTs und einen Low Power UART, der ESP32 C3 hat 2 HW UARTs. Alle drei Boards führen nur den UART0 direkt an die Löt-Pins. ESP32 C6 2 zusätzlich noch den Low Power UART (LP_UART). Über die GPIO Konfiguration kann jedoch auch der UART1 auf die Löt-Pins des Boards gemappt werden. Siehe z.B. Peripheral Pin Assignment vom ESP32 S3 in https://documentation.espressif.com/esp32-s3_datasheet_en.pdf Seite 27.

image

Die blau markierten GPIOs der Priorität P2 sind frei konfigurierbar. Für UART1 beim ESP32 S3 können dann z.B. GPIO1 und GPIO6 benutzt werden.

Prozessor-Unterstützung in Home Assistant ESPHome Builder

In Home Assistant unterstützt der ESPHome Builder die Prozessoren ESP32 S3, C6 und C3 direkt.

image

ESPHome Builder / New Device / New Device Setup …

Schaltplan

In Anlehnung an die Bauanleitungen von https://github.com/JuergenLeber/home-assistant-optolink und https://github.com/openv/openv/wiki/ESPHome-Optolink und den Erfahrungen au meinen Erstversuchen habe ich mir einen Optolink Adapter mit einem seeed studio ESP32 S3 nach folgendem Schaltplan und Pin Belegung aufgebaut.

image

Pin Liste ESP32 S3

image

Stückliste

Die Stückliste für die Beschaltung des Optolink Adapters, wie ESP32 Board, Widerstände, Sender IR-LED und Empfänger Photo-Transistor usw. wurde in Anlehnung an https://github.com/JuergenLeber/home-assistant-optolink aufgebaut.

• Streifenrasterplatine, Hartpapier, z.B. 50x100mm (z.B. Reichelt) • https://www.reichelt.com/ch/de/shop/produkt/streifenrasterplatine_hartpapier_50x100mm-8275

• Xiao ESP32S3, Dual-Core, WiFi / BT5.0, ohne Header (z.B. Reichelt) https://www.reichelt.com/ch/de/shop/produkt/xiao_esp32s3_dual-core_wifi_bt5_0_ohne_header-358354 • Oder Xiao ESP32C6, WiFi 6 / BT5.0, Zigbee, Thread (z.B. Reichelt) • https://www.reichelt.com/ch/de/shop/produkt/xiao_esp32c6_wifi_6_bt5_0_zigbee_thread-379732 • Oder Xiao ESP32C3, WiFi / BT, ohne Header (z.B. Reichelt) • https://www.reichelt.com/ch/de/shop/produkt/xiao_esp32c3_wifi_bt_ohne_header-358356

• Banana Pi - 5dB Antenne (UFL Pigtail) (z.B. Reichelt) • https://www.reichelt.com/ch/de/shop/produkt/banana_pi_-_5db_antenne-152351 • Falls der WLAN Verbindung knapp sein sollte

• Fototransistor, NPN, 730...1120nm, 24°, THT-3mm SFH-309 FA (z.B. Reichelt) https://www.reichelt.de/ch/de/shop/produkt/fototransistor_npn_730_1120nm_24_tht-3mm-60553

• Infrarot-Diode, GaAlAs, 880 nm, 50°, 3 mm, T1L-7104SF4BT KB (z.B. Reichelt) https://www.reichelt.de/ch/de/shop/produkt/infrarot-diode_gaalas_880_nm_50_3_mm_t1-216819 • Oder gleich paarweise von Aliexpress: SFH487-2 GaAlAs Infrarot Emitter SFH309FA Silizium NPN Fototransistor SFH487 SFH309 SFH DIP-2 original (880nm) • https://de.aliexpress.com/item/1005003481028413.html?spm=a2g0o.order_list.order_list_main.67.3cf75c5fz2aIL6&gatewayAdapt=glo2deu#nav-specification

• Widerstand, Kohleschicht, 180 Ohm, 0207, 250 mW, 5% (z.B. Reichelt) https://www.reichelt.de/ch/de/shop/produkt/widerstand_kohleschicht_180_ohm_0207_250_mw_5_-1362

• Widerstand, Kohleschicht, 10 kOhm, 0207, 250 mW, 5% (z.B. Reichelt) https://www.reichelt.de/ch/de/shop/produkt/widerstand_kohleschicht_10_kohm_0207_250_mw_5_-1338

• Stifte / Draht zum durchkontaktieren der genutzten ESP32 Pins auf die Streifenrasterplatine • Schaltdraht 1.5mm2 für die Konstruktion der Viessmann-V-Befestigung • Schrumpfschlauch gross zur elektrischen Insolation der Platine • 2 Stück Schrumpfschlauch klein zur optischen Insolation von Sender und Empfänger

Aufbau Adapter

Die Bauanleitung geht davon aus, dass der ESP32 ohne Stiftleiste verbaut wird, resp. nur die 4 Pins angelötet werden, die auch benötigt werden. GND, 3V3, RX und TX. Die Streifenrasterplatine ist in der Abbildung mit der Kupfer Seite nach oben zu den Bauteilen dargestellt, zur besseren Sichtbarkeit der Kupferbahnen und damit der Verbindungen der Board Pins. Für den effektiven Bau des Adapters muss die Platine natürlich umgedreht werden. Beim weissen Balken sollte die Kupferbahn unterbrochen werden. Ansonsten werden über die Drahtbrücken vom V drei Kupferbahnen miteinander verbunden und führen damit alle 3V3.

image

Beim weissen Balken sollte die Kupferbahn unterbrochen werden. Ansonsten werden über die Drahtbrücken vom V drei Kupferbahnen miteinander verbunden und führen damit alle 3V3.

ESP32 S3 und ESP32 C6 wurden mit diesem Aufbau schon getestet. Mit dem gleichen Layout müsste aber auch der ESP32 C3 eingesetzt werden können. Beim ESP32 C6 könnte alternativ auch der LP_UART (auch ohne Verstärker-Transistoren) verwendet werden. Aber für das Layout des Adapters wird auch hier mit den GPIOs gearbeitet. Weil einfacher zu löten und für den Adapter kann so das genau gleiche Layout verwendet werden wie beim ESP32 S3.

Zusammenbau

Damit der Adapter einfacher bestückt und verlötet werden kann, würde ich empfehlen, dass der Reihe nach die Bauteile mit der geringsten Bauhöhe eingesetzt werden.

  1. Widerstände
  2. ESP32 Board (die 4 benutzten Pins GND, 3V3, RX und TX mit Stiften in der Platine durchkontaktieren und auf Ebene Streifenrasterplatine und dann ESP32 Board anlöten)
  3. IR-LED und Photo-Transistor (für diese beiden Bauteile habe ich für meine Wärmepumpe einen kleinen Distanzhalter unterlegt, damit der USB-C Stecker, der an das Board gesteckt wird, dann noch an der Front vom Bedienteil der Wärmepumpe vorbeikommt. Allenfalls die IR-LED oder dann den Photo-Transistor in erster Phase über ein kurzes Kabel verlöten, damit die Schaltung vor dem Anschluss an die Heizung getestet und ausgemessen werden kann. Siehe auch Kapitel «Testen Adapter»).
  4. Schaltdraht-Bügel für Nachbildung des Viessmann V (für das Einlöten muss die Bohrung in der Platine ein wenig aufgebohrt werden.
image

Da ich leider nicht im Besitz eines 3D Druckers bin, verzichte ich auf ein Gehäuse und behelfe mich ganz einfach mit einem Schrumpfschlauch. Eine Anleitungen zum eigenen Gehäuse mit integriertem Viessmann-V findet sich z.B. unter https://github.com/openv/openv/wiki/ESPHome-Optolink

image

Im Weiteren werden zur optischen Isolation der Sender IR LED und des Empfänger Photo Transistors je ein kleiner Schrumpfschlauch aufgesteckt, damit keine Lichteinflüsse der Umgebung die Signale stören können.

image

Testen Adapter

Nach dem Zusammenbau hat es sich bewährt den Adapter mindesten dem folgenden, einfachen Test zu unterziehen. Somit kann sichergestellt werden, dass die Hardware vom gebauten Adapter funktioniert, bevor weitere Funktionen in der ESPHome Software umgesetzt werden.

Prüfen von Sender und Empfänger

Zum Prüfen von Sender und Empfänger kann nachfolgendes ESPHome YAML verwendet werden. Über den Sender Switch auf dem Home Assistant Device oder auf der Web Seite des ESP32 Devices (IP Adresse des ESPHome Devices) kann die Sender IR LED ein- oder ausgeschaltet werden.

image
esphome:
name: vitocal-200
friendly_name: Vitocal 200

esp32:
#  board: esp32dev   #-- for example ESP32 WEMOS D1 MINI  https://www.espboards.dev/esp32/d1-mini32/
board: esp32-s3-devkitc-1   #-- ESP32 S3  https://wiki.seeedstudio.com/xiao_esp32s3_getting_started/
framework:
	type: esp-idf

#-- Enable logging
logger:
hardware_uart: UART0
baud_rate: 0      #-- deactivate the logging to UART0 with a baudrate value of 0. Even if you deactivate logging on UART0, the log outputs can be seen on the local computer via the esphome command line utility
level: DEBUG #-- level shoult be set. otherwise Level DEBUG (Default) is active also for ESPHome Output

#-- Enable Home Assistant API
api:
encryption:
	key: ""

ota:
- platform: esphome
	password: ""

wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password

#-- Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
	ssid: "Vitocal-200 Fallback Hotspot"
	password: ""

captive_portal:

web_server:
port: 80

binary_sensor:
- name: "Optolink Receiver (Photo-Transistor)"
	platform: gpio
	pin: GPIO01    #-- ESP32 S3 RX UART1
	#pin: GPIO44    #-- ESP32 S3 RX UART0
	#pin: GPIO16    #-- ESP32 Wemos D1 RX UART1
	on_press: 
	then:
		- logger.log: "---- Receiver on_press"
switch:
- name: "Optolink Sender (IR-LED)"
	platform: gpio
	pin: GPIO06    #-- ESP32 S3 TX UART1
	#pin: GPIO43    #-- ESP32 S3 TX UART0
	#pin: GPIO17    #-- ESP32 Wemos D1 TX UART1

Mit der Smartphone Kamera kann die Sender IR-LED einfach getestet werden. Die Kamera zeigt auch Anteile vom Infrarotlicht. Es wird ein leicht violettes Licht sichtbar. image

Werden Sender und Empfänger optisch kurzgeschlossen wie in folgender Abbildung,

image

wird der gesendete Zustand optisch auf den Empfänger übertragen. Damit kann die ganze Strecke Sender --> Empfänger getestet werden.

Ausmessen der Schaltung

Das gleiche YAML kann für das elektronische Ausmessen der ganzen Beschaltung des Adapters verwendet werden. Siehe auch Kapitel Schalplan.

Testen Betrieb mit Mini Tool

Für das Testen im Betrieb mit der Wärmepumpe oder anderen Viessmann Gräten mit Optolink Schnittstelle wird der Adapter auf die Optolink Schnittstelle montiert. image

image

Wichtig dabei ist, dass der Adapter so montier wird, dass die Position der IR-RED und des Photo-Transistors vom Adapter mit dem Viessmann Gerät übereinstimmt. Wobei Sender und Empfänger vom Viessmann Gerät natürlich gekreuzt gegenüber liegen. Die folgenden Abbildung zeigt die Sender IR-LED der Wärmepumpe, die sichtbar immer grün leuchtet, mit dem Empfänger Photo-Transistor des Adapters.

image

Mit dem nachfolgen aufgeführten Mini Tool (YAML für ESPHome Builder) kann die Kommunikation über den Optolink Adapter mit dem Viessmann Gerät überprüft werden. Mit dem Mini Tool ist es auch möglich die initial im YAML konfigurierte Baudrate zur Laufzeit zu verändern.

image
#=============================================================================
# Title       : Mini Tool for Testing Optolink Adapter
# Purpose     : Provides functions in ESPHome for testing optolink communication with Viessmann heating devices.
#               The web user interface can be reached by opening a browser at IP address where the ESP32 is connected to.
#               For the communication with the Viessmann heating devices the KW/VS1-Protokoll is used. https://github.com/openv/openv/wiki/Protokoll-KW
#               With great help from the community on https://github.com/openv/openv.
# Requirements: ESP8266 and ESP32 Microcontroller and optolink adapter with optical sender and receiver for example: 
#               https://github.com/openv/openv/wiki/ESPHome-Optolink or https://github.com/JuergenLeber/home-assistant-optolink
#               No custom components like optolink or esphome_vitoconnect are needed for this testing tool. So UART configuration is absolutely independent.
# Author      : AutoLotion
#-----------------------------------------------------------------------------
# Version History
#         |   |
# 10.02.26|1.0|- First version for testing Optolink adapter on base off ESP32, ESP32 S3 or ESP32 C6.
#         |   |
#-----------------------------------------------------------------------------
# HA Core                : 2026.1.3
# HA Supervisor          : 2026.02.1
# HA Operating System    : 17.0
# HA Frontend            : 20260107.2
# ESPHome Device Builder : 2026.1.4
#============================================================================= 

#-- ESPHome documentation https://next.esphome.io/components/

esphome:
  name: vitocal-200
  friendly_name: Vitocal 200

esp32:
#  board: esp32dev   #-- for example ESP32 WEMOS D1 MINI  https://www.espboards.dev/esp32/d1-mini32/
  board: esp32-s3-devkitc-1   #-- ESP32 S3  https://wiki.seeedstudio.com/xiao_esp32s3_getting_started/
  framework:
	type: esp-idf
	#type: arduino    #-- must be arduino for the optolink component, not esp-idf

#-- Enable logging
logger:
  hardware_uart: UART0
  baud_rate: 0      #-- deactivate the logging to UART0 with a baudrate value of 0. Even if you deactivate logging on UART0, the log outputs can be seen on the local computer via the esphome command line utility
  level: DEBUG #-- level shoult be set. otherwise Level DEBUG (Default) is active also for ESPHome Output
  
#-- Enable Home Assistant API
api:
  encryption:
	key: ""

ota:
  - platform: esphome
	password: ""

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  #-- Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
	ssid: "Vitocal-200 Fallback Hotspot"
	password: ""

captive_portal:


#-- add the web server, if you want to access the user interface outside Home Assistant
web_server:
  port: 80    #-- To reach the web user interface, open browser on IP address where the esp32 is connected to the network.

globals:
  - id: telegram_list_g
	type: std::vector<std::array<uint16_t, 4>>
	#initial_value: "std::vector<std::array<uint16_t, 3>>{ { {1,2,3} }, { {4,5,6} }, { {7,8,9} } }"
	initial_value: "std::vector<std::array<uint16_t, 4>>{}"   #-- command, address, number_bytes, new_value (for setting new values)


uart:
  #-- for ESP32 S3 UART0 can not be used for optolink adapter. Nevertheless UART0 MUST be configured for example as dummy. It MUST be the first in the list of the configuration of UART Buses
  #-- for other ESP32 Boards this UART configuration can be deleted
  - id: uart_dummy
	rx_pin: GPIO44  #-- ESP32 S3 UART0 RX Pin      https://wiki.seeedstudio.com/xiao_esp32s3_getting_started/
	tx_pin: GPIO43  #-- ESP32 S3 UART0 TX Pin      https://wiki.seeedstudio.com/xiao_esp32s3_getting_started/
	baud_rate: 4800
	data_bits: 8
	parity: EVEN
	stop_bits: 2
	debug:
	  dummy_receiver: true
	  direction: BOTH

  #-- uart configuration for communicaton with Viessmann heating device througth optolink adapter
  #-- for ESP32 S3 the UART for the optolink adapter MUST be UART1, It MUST be configured as second in the list of the configuration of UART Buses
  - id: uart_optolink
	rx_pin: GPIO01  #-- ESP32 S3 general GPIO as RX Pin  https://wiki.seeedstudio.com/xiao_esp32s3_getting_started/
	tx_pin: GPIO06  #-- ESP32 S3 genenal GPIO as TX Pin  https://wiki.seeedstudio.com/xiao_esp32s3_getting_started/
#    rx_pin: GPIO16  #-- RX Pin for ESP32 WEMOS D1 MINI  https://www.espboards.dev/esp32/d1-mini32/
#    tx_pin: GPIO17  #-- TX Pin for ESP32 WEMOS D1 MINI  https://www.espboards.dev/esp32/d1-mini32/


	#-- Die Optolink Schnittstelle wird immer mit 4800 bps, 8 Bits, Even Parity (gerade Parität) und 2 Stopbits (4800,8,E,2) betrieben.
	#-- https://github.com/openv/openv/wiki/Die-Optolink-Schnittstelle
	baud_rate: 5040   #-- for some ESP32 S3 boards 4800 baud will not work, some how 5040 baud do work.
	data_bits: 8
	parity: EVEN
	stop_bits: 2

	debug:
	  dummy_receiver: true  #-- true means read and log also received data. If an external component like optolink or vitoconnect is used, set it to false
	  direction: BOTH
	  sequence:
		  #----------------------------------------------------------------------------------------
		  #-- This coummunication code uses the optolink protocoll KW (VS1) for requesting data by telegrams from a Viessmann heating devices
		  #-- https://github.com/openv/openv/wiki/Protokoll-KW
		  #-- For getting data from the device a telegram sent to the device must have the following format, example: "Device ID": 01F700F802, or "Aussentemperatur": 01F7080002
		  #----------------------------------------------------------------------------------------
		- lambda: |-
			//-- Log all sent and received bytes
			//UARTDebug::log_string(direction, bytes);
			UARTDebug::log_hex(direction, bytes, ':');
			//UARTDebug::log_int(direction, bytes, ',');
			//ESP_LOGD("UART Debugging", "Bytes size: %d", bytes.size());
			if ((direction == UART_DIRECTION_RX) &&  (bytes.size() == 1)) //-- check for received singel bytes
			  {
				//-- wait for periodic 0x05 from heating device
				if (bytes[0] == 0x05) {
				  //-- after 0x05 has been received, send telegram immediately
				  //uint8_t request[] = {0x01, 0xF7, 0x00, 0xF8, 0x02};
				  //id(uart_optolink).write_array(request, sizeof(request));

				  //-- get data from global telegram list
				  if (!id(telegram_list_g).empty()) {
					uint8_t command = id(telegram_list_g).front()[0];
					uint16_t address = id(telegram_list_g).front()[1];
					uint8_t no_bytes = id(telegram_list_g).front()[2];
					//ESP_LOGD("uart", "----- !id(telegram_list_g).empty(), command: %02X Address: %04X, no_bytes: %02X", command, address, no_bytes);

					if (command == 0xF7) {
					  uint8_t telegram[5];
					  telegram[0] = 0x01;         //-- initiate optolink communication sequence
					  telegram[1] = command;      //-- set command (0xF7=request value, 0xF4=set values)
					  telegram[2] = (uint8_t)(address >> 8)  & 0xFF;  //-- get high byte of address
					  telegram[3] = (uint8_t)(address  & 0xFF);       //-- get low byte of address
					  telegram[4] = no_bytes;
					  //-- log uart writing separately to log output, because we are inside the debug sequence
					  ESP_LOGD("uart", "----- >>> %02X:%02X:%02X:%02X:%02X", telegram[0], telegram[1], telegram[2], telegram[3], telegram[4]);
					  id(uart_optolink).write_array(telegram, sizeof(telegram));     //-- write telegram to uart
					}
					id(telegram_list_g).erase(id(telegram_list_g).begin());   //-- erase the processed telegram from the global list
				  } else {
					//id(uart_optolink).write_byte(0x04);   //-- keep sync alive
					//-- log uart writing separately to log output, because we are inside the debug sequence
					//ESP_LOGD("uart", "----- >>> 04");
				  }
				}
				//std::string str(bytes.begin(), bytes.end());   //-- Pack all received bytes into a string
				//id(test_rec).publish_state(str.c_str());     //-- Publish results to an existing text sensor.
			  }          

number:
  - platform: template
	name: "201: Baudrate uart_optolink"
	id: baud_rate_uart_optolink
	mode: BOX
	optimistic: True
	min_value: 4000
	max_value: 6000
	step: 10
	initial_value: 5040 
	on_value:
	  then:
		- lambda: |-
			//uint32_t new_baud_rate = stoi(x);
			uint32_t new_baud_rate = (uint32_t) x;
			ESP_LOGD("change_baud_rate", "Changing baud rate from %i to %i",id(uart_optolink).get_baud_rate(), new_baud_rate);
			id(uart_optolink).flush();
			if (id(uart_optolink).get_baud_rate() != new_baud_rate) {
			  id(uart_optolink).set_baud_rate(new_baud_rate);
			  id(uart_optolink).load_settings();
			}


button:
#----------------------------------------------------------------------------------
#-- several functions with hard-coded addresses
#----------------------------------------------------------------------------------
  - platform: template
	name: "180: Get Device Info at Address 0x00F8"
	icon: "mdi:button-pointer"
	on_press:
	  then:
		#- uart.write: [0x01,0xF7, 0x00, 0xF8, 0x08]
		- lambda: |-
			uint8_t command = 0xF7;
			uint16_t address = 0x00F8;
			uint8_t no_bytes = 8;
			id(telegram_list_g).push_back({{command, address, no_bytes, 0}});    //-- add telegram to global list

  - platform: template
	name: "181: Get Aussentemperatur at Address 0x0800"
	icon: "mdi:button-pointer"
	on_press:
	  then:
		#- uart.write: [0x01, 0xF7, 0x08, 0x00, 0x02]
		- lambda: |-
			uint8_t command = 0xF7;
			uint16_t address = 0x0800;
			uint8_t no_bytes = 2;
			id(telegram_list_g).push_back({{command, address, no_bytes, 0}});    //-- add telegram to global list

  - platform: template
	name: "190: Send 0x04 to sync Optolink Protocol"
	icon: "mdi:button-pointer"
	on_press:
	  then:
		- lambda: |-
			id(uart_optolink).write_byte(0x04);


interval:
  - interval: 30s
	startup_delay: 0s
	then:
	  #-- log actual baud rate of the active uarts
	  - logger.log:
		  format: "----- Baudrate uart_dummy: %i"
		  args: [ 'id(uart_dummy).get_baud_rate()' ]
	  - logger.log:
		  format: "----- Baudrate uart_optolink: %i"
		  args: [ 'id(uart_optolink).get_baud_rate()' ]

  - interval: 30s   #-- interval for periodic request data from heating device
	startup_delay: 10s
	then:
	  #- logger.log: "----- Interval: Get Device Info"
	  - lambda: |-
		  //id(telegram_list_g).push_back({{0xF7, 0x00F8, 8, 0}});    //-- get "Device Info" (8 bytes)
	  - delay: 2s
	  #- logger.log: "----- Interval: Get Aussentemperatur"
	  - lambda: |-
		  //id(telegram_list_g).push_back({{0xF7, 0x0800, 2, 0}});    //-- get "Aussentemperatur"

Ist der Adapter richtig an das Viessmann Gerät montiert und das Mini Tool über den Home Assistant ESPHome Builder aktiviert, ist auch automatisch der Empfang aktiviert und wird geloggt. Das Viessmann Gerät sendet periodisch 0x05 (ca. jede Sekunde, KW Protokoll) über die Optolink Schnittstelle. Das ist über den Log Output des Mini Test Tools ersichtlich. Das würde erst mal heissen, der Empfang funktioniert grundsätzlich. Das Mini Tool basiert auf den Kommunikationsprotokoll KW. Siehe auch https://github.com/openv/openv/wiki/Protokoll-KW.

image

Das Mini Tool stellt Benutzer Funktionen zum Abfragen von der vollständigen Geräte Info (8 Bytes) und der Aussentemperatur zur Verfügung, die in vielen Viessmann Geräten auf der gleichen Adresse liegen. Siehe auch https://github.com/openv/openv/wiki/Adressen.

Log Output

Nach dem Installieren des Devices in Home Assistant ESPHome Builder, oder auch wenn das Log über den Home Assistant ESPHome Builder aktiviert wird, sollte folgende Log Output erscheinen:

[18:11:45.104][I][app:206]: ESPHome version 2026.1.4 compiled on 2026-02-10 18:10:39 +0100
[18:11:45.107][I][app:213]: ESP32 Chip: ESP32-S3 r0.2, 2 core(s)
[18:11:45.107][C][logger:316]: Logger:
[18:11:45.107][C][logger:316]:   Max Level: DEBUG
[18:11:45.107][C][logger:316]:   Initial Level: DEBUG
[18:11:45.113][C][logger:322]:   Log Baud Rate: 0
[18:11:45.113][C][logger:322]:   Hardware UART: UART0
[18:11:45.113][C][logger:332]:   Task Log Buffer Size: 768 bytes
[18:11:45.156][C][uart.idf:238]: UART Bus 0:
[18:11:45.159][C][uart.idf:152]:   TX Pin: GPIO43
[18:11:45.164][C][uart.idf:152]:   RX Pin: GPIO44
[18:11:45.164][C][uart.idf:243]:   RX Buffer Size: 256
[18:11:45.164][C][uart.idf:243]:   RX Full Threshold: 3
[18:11:45.164][C][uart.idf:243]:   RX Timeout: 2
[18:11:45.167][C][uart.idf:249]:   Baud Rate: 4800 baud
[18:11:45.167][C][uart.idf:249]:   Data Bits: 8
[18:11:45.167][C][uart.idf:249]:   Parity: EVEN
[18:11:45.167][C][uart.idf:249]:   Stop bits: 2
[18:11:45.172][D][api.connection:2221]: Home Assistant 2026.1.3 (192.168.30.100): connected
[18:11:45.176][C][uart.idf:238]: UART Bus 1:
[18:11:45.180][C][uart.idf:152]:   TX Pin: GPIO6
[18:11:45.180][C][uart.idf:152]:   RX Pin: GPIO1
[18:11:45.183][C][uart.idf:243]:   RX Buffer Size: 256
[18:11:45.183][C][uart.idf:243]:   RX Full Threshold: 3
[18:11:45.183][C][uart.idf:243]:   RX Timeout: 2
[18:11:45.190][C][uart.idf:249]:   Baud Rate: 5040 baud
[18:11:45.190][C][uart.idf:249]:   Data Bits: 8
[18:11:45.190][C][uart.idf:249]:   Parity: EVEN
[18:11:45.190][C][uart.idf:249]:   Stop bits: 2
[18:11:45.194][C][template.number:016]: Template Number '201: Baudrate uart_optolink'
[18:11:45.196][C][template.number:049]:   Optimistic: YES
[18:11:45.200][C][template.number:456]:   Update Interval: 60.0s
…..
[18:12:04.239][D][uart_debug:113]: <<< 05
[18:12:05.261][D][uart_debug:113]: <<< 05
[18:12:06.280][D][uart_debug:113]: <<< 05
[18:12:07.300][D][uart_debug:113]: <<< 05
[18:12:08.338][D][uart_debug:113]: <<< 05
[18:12:09.358][D][uart_debug:113]: <<< 05
[18:12:10.278][D][main:284]: ----- Baudrate uart_dummy: 4800
[18:12:10.281][D][main:287]: ----- Baudrate uart_optolink: 5040
[18:12:10.383][D][uart_debug:113]: <<< 05
[18:12:11.398][D][uart_debug:113]: <<< 05
[18:12:12.436][D][uart_debug:113]: <<< 05
[18:12:13.456][D][uart_debug:113]: <<< 05
[18:12:14.477][D][uart_debug:113]: <<< 05
[18:12:15.513][D][uart_debug:113]: <<< 05
[18:12:16.534][D][uart_debug:113]: <<< 05
[18:12:27.364][D][button:022]: '180: Get Device Info at Address 0x00F8' Pressed.
[18:12:27.801][D][uart_debug:113]: <<< 05
[18:12:27.814][D][uart:145]: ----- >>> 01:F7:00:F8:08
[18:12:27.979][D][uart_debug:113]: <<< 20:00:45:00:04:00:16:00
[18:12:28.840][D][uart_debug:113]: <<< 05
[18:12:29.855][D][uart_debug:113]: <<< 05
[18:12:30.877][D][uart_debug:113]: <<< 05
[18:12:31.848][D][button:022]: '181: Get Aussentemperatur at Address 0x0800' Pressed.
[18:12:31.911][D][uart_debug:113]: <<< 05
[18:12:31.919][D][uart:145]: ----- >>> 01:F7:08:00:02
[18:12:32.084][D][uart_debug:113]: <<< 4C:00
[18:12:32.927][D][uart_debug:113]: <<< 05
[18:12:37.034][D][uart_debug:113]: <<< 05
[18:12:40.092][D][uart_debug:113]: <<< 05
[18:12:40.278][D][main:284]: ----- Baudrate uart_dummy: 4800
[18:12:40.285][D][main:287]: ----- Baudrate uart_optolink: 5040

Dabei wird initial auch die UART Bus Konfiguration ausbewiesen. UART Bus 1, welcher für die Optolink Kommunikation konfiguriert wurde, sollte der Zweite in der Auflistung sein. Im obigen Log sind auch die manuell ausgelösten Abfragen für Device Info und Aussentemperatur inklusive der zugehörigen Antworten von der Wärmepumpe protokolliert. Funktionieren die zwei grundsätzlichen Abfragen, steht der erweiterten Konfiguration für ein definitives ESPHome resp. Home Assistant Device nichts mehr im Weg.

Aufbau definitives ESPHome resp. Home Assistant Device

Um das für die Optolink Kommunikation verwendete Protokoll (siehe auch https://github.com/openv/openv/wiki/Protokoll-KW) einfach umsetzen zu können, werden auf Github zwei ESPHome Komponenten (ESPHome Components) angeboten. • Optolink» Component • esphome_vitoconnect» Component

Aufbau mit «Optolink» Component

Unter https://github.com/openv/openv/wiki/ESPHome-Optolink wird eine ESPHome Component vorgestellt, die die Konfiguration von allen möglichen Sensoren ermöglicht. Inkl. setzen / schreiben von Werten. Leider verwaltet die Optolink Component seinen UART für die Optolink Kommunikation komplett selber inkl. Baudrate. Einzig die UART Pins, die für die Optolink Kommunikation verwendet werden sollen, können vorgeben werden. Damit kann die Baudrate nicht über den uart_bus Component in ESPHome gesetzt werden, falls das auch bei anderen Viessmann Geräten oder Prozessor Boards, als meinen, nötig sein sollte. Auch kann die Optolink Component auf dem ESP32 C6 nicht eingesetzt werden, wegen dem benötigten Arduino Framework, das vom ESPHome Builder für den ESP32 C6 nicht unterstützt wird.

Aufbau mit «esphome_vitoconnect» Component

Esphome_vitoconnect baut auf dem Framework esp-idf auf und die UART Parameter sind komplett im YAML konfigurierbar. Siehe auch https://github.com/dannerph/esphome_vitoconnect. Esphome_vitoconnect läuft damit auch mit vorgegebenen 5040 Baud. Leider sind in der originalen Plattform vitoconnect keine text und number Components für Optolink möglich. Nur sensor und binary_sensor. Auch ist original kein Setzen / Schreiben von Werten möglich.

Man kann sich jedoch für das Schreiben von Werten mit einer abgeleiteten Version behelfen, die hier vorgestellt wird: https://github.com/dannerph/esphome_vitoconnect/issues/13. Damit können auch number und switch Components für die Plattform vitoconnect konfiguriert werden. Für beide wird schreiben unterstützt.

Eine minimales Beispiel YAML würde dann so aussehen: esphome: name: vitocal-200 friendly_name: Vitocal 200

esp32:
#  board: esp32dev   #-- for example ESP32 WEMOS D1 MINI  https://www.espboards.dev/esp32/d1-mini32/
  board: esp32-s3-devkitc-1   #-- ESP32 S3  https://wiki.seeedstudio.com/xiao_esp32s3_getting_started/
#  board: esp32-c6-devkitc-1     #-- ESP32 C6 https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/
  framework:
	type: esp-idf

#-- Enable logging
logger:
  hardware_uart: UART0
  baud_rate: 0      #-- deactivate the logging to UART0 with a baudrate value of 0. Even if you deactivate logging on UART0, the log outputs can be seen on the local computer via the esphome command line utility
  level: DEBUG #-- level shoult be set. otherwise Level DEBUG (Default) is active also for ESPHome Output
  
#-- Enable Home Assistant API
api:
  encryption:
	key: ""

ota:
  - platform: esphome
	password: ""

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  #-- Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
	ssid: "Vitocal-200 Fallback Hotspot"
	password: ""

captive_portal:


#-- add the web server, if you want to access the user interface outside Home Assistant
web_server:
  port: 80    #-- To reach the web user interface, open browser on IP address where the esp32 is connected to the network.

uart:
  #-- for ESP32 S3, C6 and C3 UART0 can not be used for optolink adapter. Nevertheless UART0 MUST be configured for example as dummy. 
  #   It MUST be the first in the list of the configuration of UART Buses
  #-- for other ESP32 Boards this UART configuration can be deleted
  - id: uart_dummy
	rx_pin: GPIO44  #-- ESP32 S3 UART0 RX Pin      https://wiki.seeedstudio.com/xiao_esp32s3_getting_started/
	tx_pin: GPIO43  #-- ESP32 S3 UART0 TX Pin      https://wiki.seeedstudio.com/xiao_esp32s3_getting_started/
#    rx_pin: GPIO17  #-- ESP32 C6 UART0 RX Pin      https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/
#    tx_pin: GPIO16  #-- ESP32 C6 UART0 TX Pin      https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/
#    rx_pin: GPIO20    #-- ESP32 C3 UART1 RX Pin       https://wiki.seeedstudio.com/XIAO_ESP32C3_Getting_Started/
#    tx_pin: GPIO21    #-- ESP32 C3 UART1 TX Pin       https://wiki.seeedstudio.com/XIAO_ESP32C3_Getting_Started/
	baud_rate: 4800
	data_bits: 8
	parity: EVEN
	stop_bits: 2
	debug:
	  dummy_receiver: true
	  direction: BOTH

  #-- uart configuration for communicaton with Viessmann heating device througth optolink adapter
  #-- for ESP32 S3 the UART for the optolink adapter MUST be UART1, It MUST be configured as second in the list of the configuration of UART Buses
  - id: uart_optolink
#    rx_pin: GPIO16  #-- RX Pin for ESP32 WEMOS D1 MINI  https://www.espboards.dev/esp32/d1-mini32/
#    tx_pin: GPIO17  #-- TX Pin for ESP32 WEMOS D1 MINI  https://www.espboards.dev/esp32/d1-mini32/
	rx_pin: GPIO01  #-- ESP32 S3 general GPIO as RX Pin  https://wiki.seeedstudio.com/xiao_esp32s3_getting_started/
	tx_pin: GPIO06  #-- ESP32 S3 genenal GPIO as TX Pin  https://wiki.seeedstudio.com/xiao_esp32s3_getting_started/
#    rx_pin: GPIO04    #-- ESP32 C6 LP_UART1 RX Pin       https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/
#    tx_pin: GPIO05    #-- ESP32 C6 LP_UART1 TX Pin       https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/
#    rx_pin: GPIO00    #-- ESP32 C6 UART1 RX Pin       https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/
#    tx_pin: GPIO23    #-- ESP32 C6 UART1 TX Pin       https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/
#    rx_pin: GPIO02    #-- ESP32 C3 UART1 RX Pin       https://wiki.seeedstudio.com/XIAO_ESP32C3_Getting_Started/
#    tx_pin: GPIO07    #-- ESP32 C3 UART1 TX Pin       https://wiki.seeedstudio.com/XIAO_ESP32C3_Getting_Started/

	#-- Die Optolink Schnittstelle wird immer mit 4800 bps, 8 Bits, Even Parity (gerade Parität) und 2 Stopbits (4800,8,E,2) betrieben.
	#-- https://github.com/openv/openv/wiki/Die-Optolink-Schnittstelle
	baud_rate: 5040   #-- for some ESP32 S3 boards 4800 baud will not work, some how 5040 baud do work.
	data_bits: 8
	parity: EVEN
	stop_bits: 2
#    rx_buffer_size: 256    #-- (Optional, int): The size of the buffer used for receiving UART messages. Increase if you use an integration that needs to read big payloads from UART. Defaults to 256.
#    rx_full_threshold: 4   #-- (Optional, int): ESP32 only. After receiving this number of bytes, the data becomes available for processing. The default is calculated at compilation time to be approximately ten milliseconds (about 8 bytes at 9600 baud, 114 bytes at 115200 baud).

	debug:

external_components:
  - source: 
	  type: git
	  url: https://github.com/dannerph/esphome_vitoconnect
	  ref: master
	refresh: 30s
  - source: github://s10l/esphome_vitoconnect@number
	components: [ vitoconnect ]
	refresh: always

vitoconnect:
  uart_id: uart_optolink
  protocol: KW
  update_interval: 30s

sensor:
  - name: "Aussentemperatur"
	id: aussen_temperatur
	platform: vitoconnect
	unit_of_measurement: "°C"
	accuracy_decimals: 1
	filters:
	  - multiply: 0.1
	  - clamp:
		  #min_value: 0   #-- if not set, there is no lower bound
		  max_value: 50   #-- if not set, there is no upper bound
		  ignore_out_of_range: true #-- sensor values outside those bounds will be ignored
	state_class: "measurement"
	device_class: "temperature"
	address: 0x0800
	length: 2

binary_sensor:
  - name: "Status Wärmepumpe"
	platform: vitoconnect
	address: 0x088A

  - name: "Status Warmwasser"
	platform: vitoconnect
	address: 0x08B0


number:
  - name: "Raum Temperatur"
	id: raum_temperatur
	platform: vitoconnect
	unit_of_measurement: "°C"
	min_value: 10
	max_value: 30
	step: 1
	div_ratio: 10
	mode: box
	device_class: temperature
	address: 0x471B
	length: 2

Austesten Optolink Datenpunkte / Adressen mit ESPHome Device

Falls für das zu verbindende Viessmann Gerät noch keine vollständige oder genügende Liste von Adressen bekannt ist z.B. unter https://github.com/openv/openv/wiki/Adressen, kann mit diesem ESPHome Tool weitere Adressen / Datenpunkte verifiziert resp. gescannt werden. https://github.com/openv/openv/wiki/Austesten-Optolink-Adressen-mit-ESPHome-Device

Einführung

Python / Node.js (Linux, Windows etc)

Microcontroller

Clone this wiki locally