Posts in category “Wissenstopf”

Proxmox VE Geschichte | Cluster-Node tot

Ok, das war knapp.

Hier kommt eine Geschichte, von deren gutem Ende ich einigermaßen überrascht bin. Auch mal wieder überrascht darüber, wieviel man in kurzer Zeit zu lernen imstande ist, wenn man sich blind auf seine jahrelange Erfahrung in der IT verlässt und dabei ordentlich ins Klo greift. Offenbar steigt mit zunehmender Erfahrung auch die Gefahr, leichtsinnig zu werden.

Aber:

"Wer andauernd begreift, was er tut, bleibt unter seinem Niveau." - Martin Walser

Wie berichtet und hier zu überprüfen, betreibe ich seit einigen Monaten ein Cluster aus Proxmox VE-Nodes, auf denen zahlreiche Dienste in Docker-Containern laufen. Ganz nach der Auffassung, dass meine Daten so nah wie möglich bei mir sein sollten, gehe ich den Weg des Self-Hostings wo immer es möglich und sinnvoll ist.

So entstand über die Zeit im Homelab ein Hypervisor-Cluster aus 4 Nodes und ebenso vielen lokalen Docker-Instanzen, die, zusammen mit zwei Oracle-Cloud-Instanzen, zu einem Environment zusammengefasst sind. Node "pve" schalte ich nur bei Bedarf per WOL hinzu. (Intel Xeon, 12-Kerne, stromdurstig)

Lokale Proxmox-Nodes Cluster-Resources

Soweit so gut.

Da in diesem Cluster mittlerweile neben Credentials (PW-Safe), sämtliche persönliche Daten (Private-Cloud) sowie die Infrastruktur für das papierlose Büro (DMS "Paperless") liegen, nutze ich zur Datensicherung einen virtualisierten Proxmox Backup Server (PBS) mit zusätzlicher Synchronisierung auf einen zweiten PBS, der sich ein einem anderen Brandabschnitt (Kriechkeller) befindet. Mein Konzept umfasst monatliche, wöchentliche, tägliche und stündliche Snapshots mit unterschiedlicher Retention (Vorhaltedauer).

Diese Backup-Daten brauchen Platz. Und daher weht der Wind dieser ganzen Geschichte.

Der bisherige, primäre PBS (Node "pve"), ist eine alte Fujitsu Celsius Workstation mit 12-Kern Xeon und HDDs. Dieser ist zwar extrem zuverlässig, aber eben auch extrem stromdurstig... Mein Plan ist nun, einen der aktuell für ca. 35,00 EUR zu bekommenden Fujitsu Futro S920 Thin-Clients auf wirtschaftlich maximalen Speicherplatz (All-Flash) auszubauen, der so aussehen sollte.

DriveInterfaceSizePurpose
DogfishmSATA256GBBoot-Drive
SandiskSATA2TBData-Drive
WD BlueNVMe2TBBackup-Pool
WD BlueNVMe2TBBackup-Pool
Ext. HDDUSB1TBBackup-Pool

Schnittstellen-Mischmasch
Um wirklich jede Schnittstelle zu verwenden, kommt noch eine externe USB-HDD (aktuell für stündliche Snapshots) zum Einsatz. Diese lässt sich im Notfall (Brand, Flucht) auch schnell abziehen und wegtragen.

Da der Thin-Client keine nativen NVMe-Ports bereitstellt, glaubte ich an einen Glücksfall, als ich bei Amazon folgende Adapterkarte in der Preiskategorie "EnAbblunnenEi" fand:

Vermeintliche Dual-NVMe-Adapterkarte

Derart geblendet von der chinesischen Ingenieurskunst, blies ich alle Warnzeichen in den Wind und ignorierte Gewissenhaft, dass einer der auf den ersten Blick baugleichen M.2-Ports um 180° gedreht auf der PCIe-Karte montiert war. Auch war mir zwar aufgefallen aber letztlich egal, dass 2xNVMe PCIe 3.0 oder gar 4.0 niemals nie ohne aktive Komponenten (Multiplexer) über einen PCIe x4-Steckplatz zu transportieren sein wird. Die Adaper-Karte hat jedoch keinen einzigen Chip an "Board".
Fazit: Das Abendessen musste auf den Tisch, den ich zum Schrauben am Rechner benutzte, die Frau machte Druck und so stopfte ich viel zu schnell alles zusammen:

Grober Schnitzer
Mechanisch passte das ja irgendwie, nur nicht so richtig...

Dann kam die Gewissheit: Magic Smoke - Der Geruch von heißer Elektronik nach dem Einschalten des Rechners...

  1. Erkenntnis:
    • Lies die Beschreibung eines Produktes genau durch und lass Dich nicht vom günstigen Preis blenden!
    • Im Fall des o.g. Adapters ist es nämlich so, dass nur der untere Port für NVMe-SSDs und der obere Port ausschließlich für SATA-SSDs zu verwenden ist!
    • Dann läuft die NVMe-SSD über PCIe und die SATA-SSD über den mit dem Mainboard per Kabel zu verbindenden SATA-Port. Die Karte dient der SATA-SSD lediglich zur Stromversorgung.
    • Faulheit und Ignoranz soll und muss auf diesem Weg unbedingt bestraft werden.

Erfolg des Ganzen war, dass der Futro S920 Thin-Client hinüber war und sich nicht mehr einschalten ließ. Meine erste Befürchtung war nun, dass zumindest eine der beiden 2TB NVMe-SSDs gegrillt waren, im schlimmsten Fall vielleicht sogar alles, was am PCIe- und/oder am SATA-Interface hing. Eine optische Überprüfung ergab jedoch keine Rückschlüsse auf defekte Komponenten durch Hitzeentwicklung. Bis jetzt weiß ich nicht, was den Schmorgeruch verursacht hat.

OK, soweit so schlecht.

Wohlweislich hatte ich einen weiteren Futro S920-Client zur Hand. Diesen wollte ich eigentlich zu einer OPNsense-Firewall umbauen, aber nun muss er als Ersatz herhalten. Und wie soll es anders sein? Murphy, was machst Du hier??? Der Ersatz-Client ist nach dem Auspacken direkt ebenfalls kaputt - Dead-on-Arrival...

Es könnte nicht besser laufen, denke ich, erstelle eine Rücksende-Anfrage bei Ebay und bestelle direkt einen weiteren Futro S920 für 35,00 EUR. Unglaublicherweise ist dieser am nächsten Tag per UPS geliefert und es kann weiter gehen.

Dieser funktioniert sogar und nach dem Umbau des RAM und der mSATA-SSD (erstmal ohne die anderen Datenträger) meldet die Kiste, kein bootfähiges Device im Rechner zu finden. Es kostete mich zwei Tage des BIOS-Updatens und anderen Versuchen den Fehler einzugrenzen, bis ich einsehen musste, dass die Boot-Partition beim Einschalten mit dem Magic Smoke beschädigt worden sein musste.

Dies war dahingehend kritisch, da das Boot-Device bei Proxmox VE sämtliche Infos zum Cluster-Node und je nach Einrichtung des Local-Storage (in diesem Fall ZFS) auch noch die Configs der VMs und zugehörige Disk-Images enthält.

Folgende Optionen standen nun zur Auswahl:

  1. Entfernen des defekten Nodes im Cluster nach dieser Anleitung.
  2. Aufsetzen des Nodes auf neuer Hardware unter Verlust sämtlicher Konfigs.
  3. Rejoinen des Nodes in den Cluster und zeitaufwendiges Wiederherstellen der Netzwerke, Storages und VMs aus Backups.

Da der defekte Node zugleich auch den virtualisierten Proxmox Backup Server enthielt, schied das Neuaufsetzen als "schnelle Lösung" aus. Wäre auch zu einfach 😉
Es standen zwar noch VM-Backups auf dem zweiten PBS (Node "pve") zur Verfügung, der letzte Sync lag allerdings mehr als zwei Wochen zurück und den zweiten, nun defekten PBS hatte ich bisher nicht im Backup (Wie sichert man einen ganzen PBS und seinen Sync-Kollegen korrekterweise gegen Ausfall? - das muss ich mir noch überlegen).

Nun aber zurück zum Boot-Problem:

Nach einigem Recherchieren fand ich heraus, dass es im Debug-Modus des Proxmox-Installers (Booten von USB-Stick) das Kommando "proxmox-boot-tool" gibt. Das hört sich vielversprechend an und tatsächlich dient es dem Wiederherstellen einer verloren gegangenen Boot-Partition auf UEFI und Legacy-Systemen, bei denen der Boot-Datenträger mit ZFS angelegt wurde. Grub hat scheinbar u.U. Probleme zuverlässig von einer ZFS-Partiition zu starten (siehe hier).

Frisch ans Werk:

  1. Booten des Proxmox-Installers vom USB-Stick und Starten des Debug-Mode per Menü und anschließendem (CTRL-D).
  2. Mittels lsblk -o +FSTYPE sucht man nun die 512MB große Partition mit dem VFAT-Dateisystem heraus und merkt sich das Device (in meinem Fall /dev/sda2). Gibt es eine 512MB große Partition ohne Dateisystem, wird diese zuerst mittels proxmox-boot-tool format /dev/sdXY formatiert (Ansonsten kann auf das Formatieren verzichtet werden).
  3. Anschließend wird die Partition mittels proxmox-boot-tool init /dev/sdXY der Konfiguration hinzugefügt.
  4. Sollte eine Warnung bezüglich einer "non-existing UUID" erscheinen, diese nicht existierende Konfiguration mittels proxmox-boot-tool clean entfernen.
  5. Nun kann mittels proxmox-boot-tool status überprüft werden, ob eine Partition mit dem Ergebnis grub und/oder uefi ausgegeben wird.
  6. Nach einem Reboot (Strg-Alt-Entf) sollte klar sein, ob der Proxmox-Node wieder von selbst startet.

In meinem Fall tat er das erwartungsgemäß nicht, so dass ich mit dem Defekt scheinbar ganze Arbeit geleistet und sämtliche Daten und nicht nur die Grub-Konfig aus der Partition gelöscht hatte. Zu merken war dies daran, dass der Rechner nun direkt in die Grub-Rescue-Shell bootete. Immerhin ein Fortschritt.

Dem Problem der fehlenden Dateien kommt man folgendermaßen bei:

  1. Booten des Proxmox-Installers vom USB-Stick und Starten des Debug-Mode per Menü und anschließendem (CTRL-D).
  2. Mounten des ZFS-Pools (meistens "rpool") in einen leeren Pfad (z.B. /mnt) mittels zpool import -f -R /mnt rpool
  3. Bind-mounten der virtuellen Dateisysteme, die zum Ausführen von proxmox-boot-tool benötigt werden:
    • mount -o rbind /proc /mnt/proc
    • mount -o rbind /sys /mnt/sys
    • mount -o rbind /dev /mnt/dev
    • mount -o rbind /run /mnt/run
  4. Root in /mnt wechseln mittels chroot /mnt /bin/bash
  5. Wiederholen der Schritte 3-6 aus dem obigen Abschnitt (beginnend mit proxmox-boot-tool init /dev/sdXY).

Mit ein bisschen Glück startet die ganze Fuhre nun wieder brav in die Proxmox-Instanz 😀

Nach diesem kurzen Intermezzo sehe ich ein, dass Dual-NVMe-Adapter für PCIe x4-Slots nicht an jeder Ecke und schon gar nicht günstig zu haben sind, so dass es nun vorerst bei 2 anstelle 4TB zusätzlichem Backup-Speicherplatz bleiben muss.

Dazu genügt nun wirklich ein Single-Slot-Adapter für 7,50 EUR.

So funktioniert es

Folding@Home: Mitmachen statt mitwarten

Dem Corona-Virus (und anderen Krankheiten) die Stirn bieten kann man auch dadurch, ungenutzte Rechenkraft der Heim-IT dem internationalen Konsortium des Forschungsprojektes "Folding@Home" für die Forschung nach einem patentfreien Corona-Impfstoff zur Verfügung zu stellen.

Zum Mitmachen rufe ich all diejenigen auf, die aktuelle sowie "abgehangene" Hardware einem neuen oder zusätzlichen Verwendungszweck zuführen möchten. Besonders ältere Hardware zeigt zwar nicht die optimale Effizienz und es darf nicht verschwiegen werden, dass, gerade bei Nutzung von GPU`s (Grafikkarten), der Stromverbrauch schnell teuer werden kann.

Aus diesem Grund berichte ich nun von meiner Herangehensweise, vorhandene Hardware für Folding@Home mit zu verwenden. In Hinsicht auf die immens steigenden Energiekosten verwende ich dieses Proof-of-Concept, um das Projekt möglichst ausschliesslich mit überschüssiger Energie aus unserer PV-Anlage zu betreiben.

HostCPUKerne/ThreadsRAMGPUUIPOSStatus
falter01i5-2520M@2.50GHz2/48GBn/a20V3A60WDebianOffline
falter02i5-2520M@2.50GHz2/48GBn/a20V3A60WDebianOffline
falter03i5-2520M@2.50GHz2/48GBn/a20V3A60WDebianOffline
falter04i5-2520M@2.50GHz2/48GBn/a20V3A60WDebianOffline
celsiusXeon6/1223GBn/a230V1,08A250WProxmoxOffline
justineXeon E5-2680v4@2.40GHz14/28128GBn/a230V0,86A200WProxmoxOnline

Update:

Wie ihr seht, sind die meisten physikalischen Rechner mittlerweile offline. Hintergrund: Das scriptgesteuerte Ein- und Ausschalten per WOL- und Shutdown-Befehl hat sich in der Praxis als wenig tauglich bewiesen. 3 Stk. der Core i5-Hosts habe ich z.B. entfernt an PV-Inselanlagen betrieben und konnte diese nur per VPN-Tunnel netzwerktechnisch erreichen. WOL funktioniert leider über Netzwerkgrenzen nicht, so dass ich diese nur durchgehend betreiben konnte. Diese widerspricht dem Ansatz des Überschuss-Verbrauchens.

Zwischenzeitlich bin ich vom Einsatz vieler einzelner, leistungsschwacher Rechner, zur Nutzung eines einzelnen, leistungsstarken Servers mit dem schönen Namen "Justine" (meiner Namensvetterin aus Frankenstein) übergegangen, der ebenfalls auf nicht mehr taufrischen Komponenten aufsetzt und aus vorhandener bzw. gebrauchter Hardware besteht. Der Bezug zu Frankenstein ergibt sich aus dem interessanten Motherboard, dass einen Uralt-Chipsatz mit einem NVME-Slot für moderne SSDs kombiniert.

Fortsetzung hier.

SVG Magic

Heute ging es zur Schwiegermutter Sektchen trinken und gebeizten Lachs mit Kartoffelpuffer essen.
Ostertradition at it's best.

Davor habe ich allerdings noch ein kleines Lernerlebnis bezüglich SVG-Paths und dem mir bisher unbekannten MorphSVGPlugin gehabt, das ich hier kurz am Beispiel "Moritz Industries" zeigen möchte:

See the Pen Moritz Industries by Konrad (@konz) on CodePen.

Mit dem kostenfreien GIMP lassen sich ganz hervorragend SVG-Pfade erstellen und der SVG-Path-Editor ist bei der korrekten Positionierung des "Moritz"-Schriftzuges mit dessen Translate-Funktion äusserst hilfreich.

Das Ergbnis gefällt mir durch den 80er-Jahre-Retro-Glow-Effekt.

Netter Nebeneffekt: Meine Tochter (5. Klasse) hat an diesem Beispiel glaube ich erkannt, dass X- und Y-Koordinaten doch zu irgendetwas gut sein können...

Stromkosten live auf LED-Matrixdisplay anzeigen

In Anbetracht rasant steigender Strompreise sollte es idealerweise einen gewissen erzieherischen Effekt in der Familie haben, die tatsächlichen Bezugskosten des laufenden Tages tagtäglich am Esstisch unter die Nase gerieben zu bekommen. Die Erkenntnis soll sein, die Stromkosten durch das eigene Verhalten beeinflussen zu können, anstelle diese als unveränderbar hinzunehmen.

Die Live-Anzeige der Stromkosten ist die ideale Ergänzung zu meinem früheren Projekt "Grünes Licht für den Eigenverbrauch". Artikel muss noch geschrieben werden

Die Umsetzung der Verbrauchsanzeige geschah innerhalb von 3 Tagen mit folgenden Mitteln:

Hardware:

ArtikelPreisBezugsquelleFunktion
Wemos D1 Mini6,99 €AZ-DeliveryMikrocontroller
LED Matrix9,49 €AZ-DeliveryAnzeigemodul
USB-Ladegerät0,00 €SchubladeSpannungsversorgung

Software:

ArtikelPreisBezugsquelleFunktion
Arduino IDE0,00 €DownloadEntwicklungsumgebung
ioBroker0,00 €HomepageGebäudeautomatisierung

Jetzt geht der Spass im ioBroker los 😀

Das folgende Script berechnet im Sekundentakt, diesen Takt gibt das SMA-EnergyMeter vor, die unterhalb des Codes abgebildeten Datenpunkte. Die für die Anzeige interessanten Datenpunkte werden gleichzeitig in das entsprechende MQTT-Topic geschrieben.

// Sektion Bezug

// Bezugszählerstand jeweils um Mitternacht
// in Datenpunkt pregardcounter24h festhalten
schedule('{"time":{"exactTime":true,"start":"00:00"},"period":{"days":1}}', async function () {
  setState("0_userdata.0.Datenpunkte.Berechnungen.sma-em.pregardcounter24h"/*Datenpunkte.Berechnungen.sma-em.pregardcounter24h*/, getState("sma-em.0.1900235801.pregardcounter").val, true);
});

// Bezugskosten des aktuellen Tages jeweils um 23:59 Uhr
// im Datenpunkt BezugskostenGestern festhalten. DP wird
// bei Änderung in history.0 für 2 Jahre gespeichert.
schedule('{"time":{"exactTime":true,"start":"23:59"},"period":{"days":1}}', async function () {
  setState("0_userdata.0.Datenpunkte.Berechnungen.sma-em.BezugskostenGestern"/*Datenpunkte.Berechnungen.sma-em.BezugskostenGestern*/, getState("0_userdata.0.Datenpunkte.Berechnungen.sma-em.BezugskostenSeit24Uhr").val, true);
});

// Bezugskosten pro Tag seit Mitternacht in Euro in
// Datenpunkt BezugskostenSeit24Uhr schreiben / Aktueller
// Preis pro kWh 32,12 Cent (Maingau Januar 2022)
//
// binde: Wert automatisch in MQTT-Topic "MatrixDisplay1.text"
// kopieren (Anzeige in ESP8266-Matrix-Display im Wohnzimmer)
on({id: 'sma-em.0.1900235801.pregardcounter', change: "ne"}, async function (obj) {
  var value = obj.state.val;
  var oldValue = obj.oldState.val;
  setState("0_userdata.0.Datenpunkte.Berechnungen.sma-em.BezugskostenSeit24Uhr"/*Datenpunkte.Berechnungen.sma-em.BezugskostenSeit24Uhr*/, Math.round((parseFloat((parseFloat(getState("sma-em.0.1900235801.pregardcounter").val) - getState("0_userdata.0.Datenpunkte.Berechnungen.sma-em.pregardcounter24h").val)) * 0.3212)*100)/100, true);
});
on({id: '0_userdata.0.Datenpunkte.Berechnungen.sma-em.BezugskostenSeit24Uhr', change: "ne"}, function (obj) {
  setState('mqtt.0.MatrixDisplay1.text', obj.state.val);
});


// Sektion Erzeugung

// Einspeisezählerstand jeweils um Mitternacht
// in Datenpunkt psurpluscounter24h festhalten
schedule('{"time":{"exactTime":true,"start":"00:00"},"period":{"days":1}}', async function () {
  setState("0_userdata.0.Datenpunkte.Berechnungen.sma-em.psurpluscounter24h"/*Datenpunkte.Berechnungen.sma-em.psurpluscounter24h*/, getState("sma-em.0.1900235801.psurpluscounter").val, true);
});

// Einspeiseverguetung des aktuellen Tages jeweils um 23:59
// Uhr im Datenpunkt EinspeiseverguetungGestern festhalten.
// DP wird bei Änderung in history.0 für 2 Jahre gespeichert.
schedule('{"time":{"exactTime":true,"start":"23:59"},"period":{"days":1}}', async function () {
  setState("0_userdata.0.Datenpunkte.Berechnungen.sma-em.EinspeiseverguetungGestern"/*Datenpunkte.Berechnungen.sma-em.EinspeiseverguetungGestern*/, getState("0_userdata.0.Datenpunkte.Berechnungen.sma-em.EinspeiseverguetungSeit24Uhr").val, true);
});

// Einspeiseverguetung pro Tag seit Mitternacht in Euro
// in Datenpunkt EinspeiseverguetungSeit24Uhr schreiben /
// Einspeisevergütung pro kWh 12,00 Cent (Inbetriebnahme 2016)
//
// binde: Wert automatisch in MQTT-Topic "MatrixDisplay1.text2"
// kopieren (Anzeige in ESP8266-Matrix-Display im Wohnzimmer)
on({id: 'sma-em.0.1900235801.psurpluscounter', change: "any"}, async function (obj) {
  var value = obj.state.val;
  var oldValue = obj.oldState.val;
  setState("0_userdata.0.Datenpunkte.Berechnungen.sma-em.EinspeiseverguetungSeit24Uhr"/*Datenpunkte.Berechnungen.sma-em.EinspeiseverguetungSeit24Uhr*/, Math.round((parseFloat((parseFloat(getState("sma-em.0.1900235801.psurpluscounter").val) - getState("0_userdata.0.Datenpunkte.Berechnungen.sma-em.psurpluscounter24h").val)) * 0.12)*100)/100, true);
});
on({id: '0_userdata.0.Datenpunkte.Berechnungen.sma-em.EinspeiseverguetungSeit24Uhr', change: "ne"}, function (obj) {
  setState('mqtt.0.MatrixDisplay1.text2', obj.state.val);
});

Ergebnis des obigen Salates sind die folgenden Datenpunkte:

Berechnete Datenpunkte im ioBroker

Sowie die zugehörigen MQTT-Topics:

Aus Datenpunkten befüllte MQTT-Topics

Nun geht die Sause in der Arduino-IDE weiter 😃

Die folgende Programmierung verleitet den ESP8266-Mikrocontroller auf der angeschlossenen LED-Matrix zur Darstellung der ganz unten im Video zu sehenden Teufelei:

#include <ESP8266WebServer.h>
#include <ESP8266WiFi.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Max72xxPanel.h>
#include <time.h>
#include <PubSubClient.h>

WiFiClient espClient; 
PubSubClient client(espClient); 

bool starte = false;
char tmp[50];
String Text = ""; // Variable Text wird angelegt.
char ssid[] = "Hier die SSID des heimischen WLANs eintragen";  //  SSID (name)
char pass[] = "Hier den Key des heimischen WLANs eintragen";       // password
const char* mqtt_server = "Hier die IP-Adresse des heimischen ioBrokers eintragen"; //Broker IP Address
const char* mqttUser = "Optional: MQTT-Benutzer"; // Broker Name
const char* mqttPassword = "Optional: MQTT-Passwort"; // Broker Passwort

void connect_to_MQTT() {
 client.setServer(mqtt_server, 1883);//MQTT Server, - Port
 client.setCallback(callback); // aktiviert das zuhören 
 // Solltet Ihr kein Benutzer/Passwort haben so müssen folgende Zeilen entfernt werden: 
 // (client.connect("MatrixDisplay1" , mqttUser, mqttPassword))
 // und mit folgender getauscht werden: 
 // if (client.connect("MatrixDisplay1");
  if (client.connect("MatrixDisplay1")) {
  Serial.println("Verbinde zum MQTT Server");
  } else {
  Serial.println("zu MQTT Server nicht verbunden ");
  if (!client.connect("MatrixDisplay1")) {
      Serial.print("Fehlgeschlagen, state=");
      Serial.print(client.state());
      Serial.println(" Versuch in 5 Sekunden nochmal");
      delay(5000);
    }
  }
  client.subscribe("MatrixDisplay1.text");
  client.subscribe("MatrixDisplay1.text2");
  client.subscribe("MatrixDisplay1.kWhLadenAktuell");
  // Es könne weitere Topics hinzugefügt werden! 
  // Alternativ steht folgende Alternative zur Verfügung: 
  //client.subscribe("topic/#"); --> es werden dann alle topics abonniert ( Beispiel: MatrixDisplay1/# ) Es werden alle Nachrichten aus Wohnzimmer empfangen! 
  
  //client.subscribe("MatrixDisplay1/woche");
  //client.subscribe("MatrixDisplay1/verbraucht");
  //client.subscribe("MatrixDisplay1/internet");
  //client.subscribe("MatrixDisplay1/zimmertemperatur");
}
    
int pinCS = D8; // CS PIN
int numberOfHorizontalDisplays = 4; //Display Anzahl
int numberOfVerticalDisplays   = 1; // Diplay höhen Anzahl
char time_value[20];
// LED Matrix Pin -> ESP8266 Pin
// Vcc            -> 3v / 5v
// Gnd            -> Gnd 
// DIN            -> D7 
// CS             -> D8  
// CLK            -> D5
Max72xxPanel matrix = Max72xxPanel(pinCS, numberOfHorizontalDisplays, numberOfVerticalDisplays);
int wait = 95; // In millis -> Scrollgeschwindigkeit! 
int spacer = 1;
int width  = 5 + spacer; // Die Panels haben 5 Pixel + Leerzeichen

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED) {
  delay(500);
  Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  
  connect_to_MQTT(); 
  
  configTime(1 * 3600, 0, "192.168.10.1", "pool.ntp.org"); // Zeitserver aus dem LAN und ein alternativer Zeitserver im Internet mit Festlegung der Zeitzone
  setenv("TZ", "CET-1CEST,M3.5.0/01,M10.5.0/02",1);
  matrix.setIntensity(5); // Lichtstärke einstellen! Bis 8 kann man sicherlich über den Wemos D1 mini ansteuern, alles darüber sollte das Netzteil aufgeteilt werden und direkt mit Strom versorgt werden!! 
  matrix.setRotation(0, 1);    
  matrix.setRotation(1, 1);    
  matrix.setRotation(2, 1);    
  matrix.setRotation(3, 1);    
}

void loop() {

  matrix.fillScreen(LOW);
  time_t now = time(nullptr);
  String time = String(ctime(&now));
  time.trim();
  Serial.println(time);
  time.substring(11,19).toCharArray(time_value, 10); 
  matrix.drawChar(2,0, time_value[0], HIGH,LOW,1); // H
  matrix.drawChar(8,0, time_value[1], HIGH,LOW,1); // HH
  matrix.drawChar(14,0,time_value[2], HIGH,LOW,1); // HH:
  matrix.drawChar(20,0,time_value[3], HIGH,LOW,1); // HH:M
  matrix.drawChar(26,0,time_value[4], HIGH,LOW,1); // HH:MM
  matrix.write(); // Bitmap an das Display senden.
  
  delay(2000);
  if (!client.connected()) {
  Serial.println("Keine Verbindung zum MQTT Server");
  connect_to_MQTT();
  }
  client.loop();
}
void display_message(String message){
   for ( int i = 0 ; i < width * message.length() + matrix.width() - spacer; i++ ) {
  //matrix.fillScreen(LOW);
  int letter = i / width;
  int x = (matrix.width() - 1) - i % width;
  int y = (matrix.height() - 8) / 2; // Zentriert die Ausgabe.
  while ( x + width - spacer >= 0 && letter >= 0 ) {
    if ( letter < message.length() ) {
    matrix.drawChar(x, y, message[letter], HIGH, LOW, 1); // HIGH LOW 
    }
    letter--;
    x -= width;
  }
  matrix.write(); // Bitmap an das Display senden.
  delay(wait/2);
  }
}
void callback(char* topic, byte* payload, unsigned int length) {
 // Zähler
 int i = 0;
 // Hilfsvariablen für die Convertierung der Nachricht in ein String
 char message_buff[100];
 
 Serial.println("Message arrived: topic: " + String(topic));
 Serial.println("Length: " + String(length,DEC));
 
 // Kopieren der Nachricht und erstellen eines Bytes mit abschließender \0
 for(i=0; i<length; i++) {
 message_buff[i] = payload[i];
 }
 message_buff[i] = '\0';
 
 // Konvertierung der Nachricht in ein String
 String msgString = String(message_buff);
 Serial.println("Payload: " + msgString);
 if (strcmp(topic,"MatrixDisplay1.text")==0){
  Text = "Stromkosten heute: " + msgString + " EUR";
  display_message(Text);
  }

  if (strcmp(topic,"MatrixDisplay1.text")==0){
  Text = "-" + msgString + " EUR";
  display_message(Text);
  }

 if (strcmp(topic,"MatrixDisplay1.text2")==0){
  Text = "Stromerzeugung heute: " + msgString + " EUR";
  display_message(Text);
  }

  if (strcmp(topic,"MatrixDisplay1.text2")==0){
  Text = "+" + msgString + " EUR";
  display_message(Text);
  }
  
  if (strcmp(topic,"MatrixDisplay1.kWhLadenAktuell")==0){
  Text = "E-Ladevorgang: " + msgString + " kWh";
  display_message(Text);
  }

  /*
  Für die Zukunft:
  Zum freimachen, das "/*" und am ende "* /" entfernen!
  
  ################################################ 
  ## Topic filtern und etwas bestimmtes machen! ##
  ################################################
  
  if (strcmp(topic,"display1.woche")==0){
  Text = "Woche Gesamt: " + msgString + "Std.";
  display_message(Text);
  display_message(Text);
  }
  if (strcmp(topic,"display1.zimmertemperatur")==0){
  Text = msgString + " Grad";
  display_message(Text);
  display_message(Text);
  long rssi = WiFi.RSSI();
  itoa(rssi,tmp,10);
  client.publish("display1/rssi",tmp);
  //display_message(Text);
  }
   
*/
 

Nun geht der Spass mit der Hardware und dem Anschliessen weiter :😍

Der Wemos D1 Mini, oder jeder andere ESP8266-Controller, wird nun anhand der im Arduino-Code festgelegten Pin-Belegung mit der LED-Matrix verbunden und per USB-Netzteil mit Spannung versorgt.

Ich bitte nun wirklich jede und jeden inständig, den im Bild zu sehenden Aufbau unter !!!KEINEN UMSTÄNDEN!!! nachzumachen. Es dient lediglich der Illustration wie man es !!!auf gar keinen Fall!!! machen darf! Danke!

Niemals nie nachmachen!

Zusammen mit dem alten Voltmeter aus dem Physikunterricht ergibt das eine zumindest interessante Kombination.

Nicht schön aber selten

Sozusagen als Abfallprodukt wird die Uhrzeit sowie die, bei aktivem Ladevorgang, an der Wallbox ins E-Auto geladene Energiemenge in kWh angezeigt.

Das Ergbnis des gesamten Reigens ist nun dies Video 😀

PS: Wenn die Anzeige im Winter auch schmerzt, so freue ich mich auf den Frühling und Sommer, wenn die Bezugskosten nahe 0 € bleiben dürften und die Familie live sieht, warum die PV-Anlage unbedingt sein musste.

Windows 10: Kein Ton / No Sound

Wer wie ich, seit einem bestimmten Windows-Update unter Windows 10 (bei mir Enterprise) plötzlich keine Sound-Ausgabe mehr hat, findet hier möglicherweise die Lösung.

Ich war jedenfalls mehr als 2 Monate am Dienst-Notebook ohne Ton und Mikro unterwegs, was derzeit bei der stark gestiegenen Anzahl von Videokonferenzen und Webinaren natürlich ganz besonders hilfreich ist...

Symptomatisch war, dass zwar der Geräte-Manager keinerlei Auffälligkeiten zeigte, das Lautsprechersymbol in der Taskleiste jedoch ein rotes Kreuz mit dem Hinweis "Kein Audioausgabegerät installiert" und "Lautsprechereinrichtung (UNKNOWN)" zierte. Die dabei angebotene Windows-Problembehandlung glänzte erneut durch Wirkungslosigkeit.

Das folgende strukturierte Vorgehen zur Fehlerbehebung hat bei mir rein gar nichts gebracht, könnte in Deinem Fall aber vielleicht trotzdem helfen:

  • Treiber löschen und von der Herstellerseite frisch heruntergeladene Soundtreiber installieren
  • Sicherheitshalber nachschauen, ob der Onboard-Sound durch irgendeinen Zufall im BIOS deaktiviert wurde
  • Wenn vorhanden, die für Dein Gerät aktuelle BIOS-Version installieren (in der Verzweiflung versucht man alles)
  • Den Status der Windows-Dienste "Windows-Audio" und "Windows-Audio-Endpunkterstellung" auf "Wird ausgeführt" prüfen
  • Schauen, ob Dein Hersteller ein Tool bereitstellt, dass Treiber für Dein Gerät automatisch passend herunterlädt und installiert (z.B. Fujitsu DeskUpdate)
  • Alle zuletzt installierten Windows-Updates deinstallieren, auf einen früheren Wiederherstellungspunkt oder ein selbstverständlich vorhandenes Backup zurücksetzen
  • Ebenfalls zur Sicherheit, dass nicht irgendwas an der Hardware faul ist, ein beliebiges Live-Linux von USB-Stick starten und sicherstellen, dass Audio funktioniert

Nachdem ich kurz davor war beim Chef ein neues Notebook zu erbetteln bzw. sogar bereit gewesen wäre Windows 11 zu installieren, in der Hoffnung der Fehler würde dadurch verschwinden, unternahm ich noch einen letzten Anlauf. Diese Hartnäckigkeit sollte belohnt werden!

Die Lösung in meinem Fall:

  1. Öffnen einer Kommandozeile mit Admin-Rechten (Rechtsklick "Als Administrator ausführen")
  2. Eingabe von net localgroup Administratoren /add networkservice (bei deutschem Windows, ansonsten den Gruppennamen der Sprache anpassen) -> Enter
  3. Eingabe von net localgroup Administratoren /add localservice ("") -> Enter
  4. exit
  5. Reboot des Rechners

Ich kann nicht erklären, was da zu irgendeinem Zeitpunkt beim Update schief läuft, gehört aber in die Kategorie "zum Kübeln".

Ich hoffe, dass Du nun auch wieder Audio unter Windows hast :) Soll ja manchmal ganz nützlich sein...