Ich wollte meine Dotmatrix Module mal zum Leben erwecken. Also wieder mal eine Anzeige für Zeit und Temperatur gebastelt.
Dot Matrix 64x56 = 4096 LEDs 25x22cm ACHTUNG STROMAUFNAHME!!
SPI Bus 1 Dotmatrix rot 64x32 für die Uhrzeit und Wochentag - Helligkeit Tag/Nacht im Home Assistant einstellbar
SPI Bus 2 Dotmatrix gruen 64x24 für die Temperatur - Helligkeit Tag/Nacht im Home Assistant einstellbar
Umschaltung Helligkeit über Sonnenaufgang/Sonnenuntergang
CS und Clock mit Levelschifter (MAX7219 erfordert 5V),
MOSI ohne Levelschifter mit 10kOhm pulldown, CLK mit 10 kΩ‑Pull‑Down, CS (LOAD) mit einem 10 kΩ‑Pull‑Up
täglicher Reset = Vorbeugung Modulausfall
# Chip is ESP32-D0WDQ6 (revision v1.1) Berrybase NMCU-ESP32
# Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
# Crystal is 40MHz MAC: b0:b2:1c:ff:a1:c4
# Dot Matrix 64x56 = 4096 LEDs 25x22cm ACHTUNG STROMAUFNAHME!!
# SPI Bus 1 Dotmatrix rot 64x32 für die Uhrzeit und Wochentag - Helligkeit Tag/Nacht im Home Assistant einstellbar
# SPI Bus 2 Dotmatrix gruen 64x24 für die Temperatur - Helligkeit Tag/Nacht im Home Assistant einstellbar
# Umschaltung Helligkeit über Sonnenaufgang/Sonnenuntergang
# CS und Clock mit Levelschifter (MAX7219 erfordert 5V),
# MOSI ohne Levelschifter mit 10kOhm pulldown, CLK mit 10 kΩ‑Pull‑Down, CS (LOAD) mit einem 10 kΩ‑Pull‑Up
# täglicher Reset = Vorbeugung Modulausfall
# Juni 2025
esphome:
name: 20matrix-esp32
friendly_name: 20matrix-esp32
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
level: ERROR
# Enable Home Assistant API
api:
encryption:
key: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
ota:
- platform: esphome
password: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "20Matrix-Esp32 Fallback Hotspot"
password: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
captive_portal:
spi: # Use VSPI # https://https://wolles-elektronikkiste.de/esp32-mit-arduino-code-programmieren
- id: spi_bus_rot
clk_pin: GPIO18 #18 (VSPI_CLK) gruen
mosi_pin: GPIO23 #23 (VSPI_MOSI) orange
# miso_pin: GPIO19 #19 (VSPI_MISO) nicht verwendet/notwendig
- id: spi_bus_gruen
clk_pin: GPIO14 #14 (HSPI_CLK) gruen/weiss
mosi_pin: GPIO13 #13 (HSPI_MOSI) orange
# miso_pin: GPIO19 #12 (HSPI_MISO) nicht verwendet/notwendig
display:
- platform: max7219digit
spi_id: spi_bus_gruen
id: Dotmatrix_gruen
cs_pin: GPIO15 # (HSPI_CS = Chip Select) gelb/weiss
num_chips: 24
num_chip_lines: 3
chip_lines_style: snake
# intensity: 2 nicht aktiv, wird im HA mit Sonnenstand eingestellt
update_interval: 5s
lambda: |-
// Helligkeit setzen
id(Dotmatrix_gruen).intensity(id(current_brightness_temp));
it.clear();
if (!isnan(id(mw_temp_aussen).state)) { // warten auf Wert von HA
float value = id(mw_temp_aussen).state;
int whole = abs((int)value); // z.B. 12
int tens = whole / 10; // 1
int ones = whole % 10; // 2
int fraction = abs(int((value - (int)value) * 10)); // 7
bool is_negative = value < 0;
int y = -8; // Höhe Temperaturanzeige global
// Vorzeichen darstellen
if (is_negative) {
it.filled_rectangle(0, 12, 8, 3); // kleiner Block
}
// Zehnerstelle Temp (nur anzeigen wenn >= 10)
if (tens > 0) {
it.print(11, y, id(big_clock_font), to_string(tens).c_str());
}
// Einerstelle Temp
it.print(26, y, id(big_clock_font), to_string(ones).c_str());
// Nachkommastelle Temp
it.print(44, y, id(big_clock_font), to_string(fraction).c_str());
// Komma als Grafik
it.line(40, 21, 42, 21); //oberste Zeile
it.line(39, 22, 41, 22);
it.line(38, 23, 40, 23);
} else {
// Nur wenn Wert ungültig ist – z. B. beim Start
it.draw_pixel_at(10, 10, COLOR_ON); // dezentes Zeichen
// Oder: it.print(0, 0, id(small_font), "...");
}
- platform: max7219digit
spi_id: spi_bus_rot
id: Dotmatrix_rot
cs_pin: GPIO05 # (VSPI_CS = Chip Select) gelb
num_chips: 32
num_chip_lines: 4
chip_lines_style: snake
# intensity: 2 nicht aktiv, wird im HA und Sonnenstand eingestellt
update_interval: 1s
lambda: |-
// Helligkeit setzen
id(Dotmatrix_rot).intensity(id(current_brightness));
it.clear();
auto now = id(esptime).now();
if (!now.is_valid()) {
it.print(0, -3, id(pixelmix8), "....");
return;
}
int y = 1; // vertikale Feinjustierung (0= oben 10px frei, unten 2px 2= oben 12px frei, unten 0px)
char buf[2] = {0};
// H1 Stunden 10er Stelle
buf[0] = '0' + (now.hour / 10);
it.print(0, y, id(big_clock_font), buf);
// H2 Stunden 1er Stelle
buf[0] = '0' + (now.hour % 10);
it.print(14, y, id(big_clock_font), buf);
// Doppelpunkt (blinkend)
if (now.second % 2 == 0) {
it.filled_circle(31, 17, 2); // oberer Punkt
it.filled_circle(31, 27, 2); // unterer Punkt
}
// M1 Minuten 10er Stelle
buf[0] = '0' + (now.minute / 10);
it.print(36, y, id(big_clock_font), buf);
// M2 Minuten 1er Stelle
buf[0] = '0' + (now.minute % 10);
it.print(50, y, id(big_clock_font), buf);
// ======================= Wochentag auf Deutsch
const char* tage_deutsch[] = {"SONNTAG", "MONTAG", "DIENSTAG", "MITTWOCH", "DONNERSTAG", "FREITAG", "SAMSTAG"};
char tag[12] = "";
uint8_t dow = now.day_of_week;
if (dow >= 1 && dow <= 7) {
snprintf(tag, sizeof(tag), "%s", tage_deutsch[dow - 1]);
}
// Wochentag mittig oben anzeigen
int text_width = strlen(tag) * 6; // ca. 6 Pixel pro Zeichen
int x_pos = (64 - text_width) / 2;
it.print(x_pos, 0, id(pixelmix8), tag); // 0,0 ist links oben, bei Y=0 bleibt die oberste Reihe frei, bei Y=-1 ganz oben
number:
- platform: homeassistant
name: "Helligkeit Tag Uhrzeit"
id: brightness_day
entity_id: input_number.matrix_uhr_tag # muss in HA existieren ist als Helfer Zahlenwert Eingabe angelegt
- platform: homeassistant
name: "Helligkeit Nacht Uhrzeit"
id: brightness_night
entity_id: input_number.matrix_uhr_nacht # muss in HA existieren ist als Helfer Zahlenwert Eingabe angelegt
- platform: homeassistant
name: "Helligkeit Nacht Temperatur"
id: temperature_night
entity_id: input_number.matrix_temperatur_nacht # muss in HA existieren ist als Helfer Zahlenwert Eingabe angelegt
- platform: homeassistant
name: "Helligkeit Tag Temperatur"
id: temperature_day
entity_id: input_number.matrix_temperatur_tag # muss in HA existieren ist als Helfer Zahlenwert Eingabe angelegt
globals:
- id: current_brightness
type: int
restore_value: true
initial_value: '0'
- id: current_brightness_temp
type: int
restore_value: true
initial_value: '0'
interval:
- interval: 1min
then:
- lambda: |-
// Hole aktuelle Zeit als "hh:mm"
auto now = id(esptime).now();
if (!now.is_valid()) return;
// Sonnenaufgang/-untergang als hh:mm Strings
std::string sunrise = std::string(id(sonnenaufgang).state.c_str());
std::string sunset = std::string(id(sonnenuntergang).state.c_str());
// Vergleiche aktuelle Zeit
std::string current_time =
(now.hour < 10 ? "0" : "") + std::to_string(now.hour) + ":" +
(now.minute < 10 ? "0" : "") + std::to_string(now.minute);
if (current_time >= sunrise && current_time < sunset) {
// Tag: setze Helligkeit auf brightness_day
id(current_brightness) = (int)id(brightness_day).state;
id(current_brightness_temp) = (int)id(temperature_day).state;
} else {
// Nacht: setze Helligkeit auf brightness_night
id(current_brightness) = (int)id(brightness_night).state;
id(current_brightness_temp) = (int)id(temperature_night).state;
}
switch:
- platform: restart
id: restart_switch
internal: true
- platform: template
name: "Letzter Neustart durch Zeitplan"
id: dummy_restart_log
restore_mode: ALWAYS_OFF
optimistic: true
entity_category: diagnostic
internal: false # Wenn true, erscheint er gar nicht in Home Assistant
time:
- platform: homeassistant
id: esptime
on_time:
- seconds: 0
minutes: 2
hours: 4 # "*" wäre jede Stunde
then:
- switch.turn_on: dummy_restart_log
- delay: 200ms
- switch.turn_off: dummy_restart_log
- logger.log: "Stündlicher Neustart"
- switch.toggle: restart_switch
text_sensor:
- platform: homeassistant
id: sonnenaufgang
entity_id: sensor.sonnenaufgang
internal: true
- platform: homeassistant
id: sonnenuntergang
entity_id: sensor.sonnenuntergang
internal: true
sensor:
- platform: wifi_signal # Reports the WiFi signal strength/RSSI in dB
name: "WiFi Signal dB"
id: wifi_signal_db
update_interval: 60s
entity_category: "diagnostic"
- platform: copy # Reports the WiFi signal strength in %
source_id: wifi_signal_db
name: "wifi_signal_Prozent"
id: wifi_signal_Prozent
filters:
- lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
unit_of_measurement: "Signal %"
entity_category: "diagnostic"
device_class: ""
- platform: homeassistant
id: mw_temp_aussen
entity_id: sensor.wetterstation_temperatur_aussen
font:
- file: "OpenSans-CondBold.ttf" # ideal für grosse Ziffern
id: big_clock_font
size: 28 # !! ergibt eine Ziffer mit 20 Pixel Höhe ??!!
glyphs: "0123456789-,:"
- file: "pixelmix.ttf" # ideal für Wochentag
id: pixelmix8
size: 8
# - file: "unscii-8.ttf" Schrift zu breit
# - file: "5x7.bdf" Schrift zu klein