Strompreisdisplay

Lesezeit: 8 Minuten

Es zeigt den aktuellen Börsenstrompreis (inkl. MwSt, exkl. Grund- und Netzgebühren) und eine Prognose an. Dazu ermittelt es die günstigste drei-Stunden-Periode innerhalb der nächsten 8 Stunden und zeigt, wie lange es in etwa noch dauert, bis diese beginnt. Außerdem zeigen Pfeile an, ob die nächste Stunde teurer oder günstiger wird.

Außerdem kann es bei Dunkelheit das Display abschalten.

Als Datenquelle wird die kostenlose API von aWATTar genutzt.

Anwendungsbeispiel

Man möchte über Nacht den Geschirrspüler einschalten und sieht am Display, dass die günstigsten 3 h in 6 h beginnen. Somit kann man die Spülmaschine programmieren, damit sie in 6 h ihr Programm beginnt.

Schaltplan

Komponenten

Mikroprozessor ESP82665 €
Display SSH11067 €
Fotowiderstand (optional)2 €
Widerstand 10 kΩ (optional)
Gehäuse (optional)zum Selberdrucken

Installation und Konfiguration

Schritt 1: Installation der Arduino IDE

  1. Lade die Arduino IDE herunter und installiere sie:

Schritt 2: Einrichtung der ESP8266-Board-Unterstützung

  1. Starte die Arduino IDE.
  2. Gehe zu Datei > Voreinstellungen (File > Preferences).
  3. Füge unter „Zusätzliche Boardverwalter-URLs“ (Additional Boards Manager URLs) die folgende URL hinzu:
http://arduino.esp8266.com/stable/package_esp8266com_index.json
  1. Klicke auf OK.
  2. Gehe zu Werkzeuge > Board > Boardverwalter (Tools > Board > Board Manager).
  3. Suche nach esp8266 und installiere die ESP8266-Plattform, indem du auf Installieren klickst.

Schritt 3: Auswahl des ESP8266 Boards

  1. Gehe zu Werkzeuge > Board und wähle dein spezifisches ESP8266-Board aus (zum Beispiel LOLIN(WEMOS) D1 R2 & mini).
  2. Wähle unter Werkzeuge > Port den COM-Port deines ESP8266 aus. Falls du dir unsicher bist, welcher Port es ist, kannst du den ESP8266 einmal aus- und einstecken und überprüfen, welcher Port neu hinzugekommen ist.

Schritt 4: Installation der benötigten Bibliotheken

  1. Öffne die Arduino IDE und gehe zu Werkzeuge > Bibliotheken verwalten (Tools > Manage Libraries).
  2. Installiere folgende Bibliotheken, indem du sie in der Suchleiste suchst und installierst:
    • ESP8266WiFi (dies ist Teil der ESP8266-Plattform)
    • WiFiClientSecure (ebenfalls Teil der ESP8266-Plattform)
    • ArduinoJson (Version 6 oder höher)
    • Adafruit GFX Library
    • Adafruit SH110X Library (suche nach „SH1106“, da diese Bibliothek für dein Display benötigt wird)

Schritt 5: Anschließen der Hardware

  1. Verkabelung des Displays:
    • Verbinde den VCC-Pin des SH1106-Displays mit dem 3.3V-Pin des ESP8266.
    • Verbinde den GND-Pin des SH1106-Displays mit dem GND-Pin des ESP8266.
    • Verbinde den SCL-Pin des SH1106-Displays mit dem D1-Pin (GPIO 5) des ESP8266.
    • Verbinde den SDA-Pin des SH1106-Displays mit dem D2-Pin (GPIO 4) des ESP8266.
  2. Verkabelung des Fotowiderstands:
    • Verbinde einen Fotowiderstand zwischen A0 und GND.
    • Schließe einen 10K Widerstand zwischen A0 und 3.3V an.

Falls du keinen Fotowiderstand verwendest und das Display immer an sein soll, musst du im Code den Wert darkkThreshold auf 0 oder 1024 setzen, ich bin mir gerade nicht sicher.

Schritt 6: Konfiguration des Codes

  1. Öffne die Arduino IDE und kopiere den angegebenen Code in das neue Projektfenster.
  2. Ändere im Code die WiFi-Anmeldedaten auf deine:
const char* ssid = "yourWiFiName";
const char* password = "yourWiFiPassword";
  1. Falls du den Code für Deutschland verwenden möchtest, ändere die API-URLs im Code wie angegeben:
client.connect("api.awattar.de", 443);

Schritt 7: Treiber installieren

Für die USB-Verbindung mit dem Board ist dieser Treiber zu installieren.

Schritt 8: Hochladen des Codes auf den ESP8266

  1. Schließe deinen ESP8266 über ein USB-Kabel an deinen Computer an.
  2. Gehe zu Werkzeuge > Port und stelle sicher, dass der richtige COM-Port ausgewählt ist.
  3. Klicke auf den Upload-Button (Pfeil nach rechts) in der Arduino IDE, um den Code auf den ESP8266 zu übertragen.
  4. Die IDE wird den Code kompilieren und auf das Board hochladen. Beobachte das Konsolenfenster am unteren Rand der IDE, um sicherzustellen, dass der Upload erfolgreich war.

Schritt 9: Überwachung der Ausgabe (optional)

  1. Sobald der Code hochgeladen ist, kannst du den Seriellen Monitor öffnen (Tools > Serial Monitor), um Debug-Informationen zu sehen. Stelle sicher, dass die Baudrate auf 115200 eingestellt ist.
  2. Der Monitor zeigt dir, ob der ESP8266 erfolgreich mit dem WLAN verbunden wurde und ob die API-Daten abgerufen werden.

Schritt 10: Fehlerbehebung

  • Wenn das Display nichts anzeigt, überprüfe die I2C-Verbindung (SDA/SCL) und die Stromversorgung (VCC/GND).
  • Wenn der ESP8266 keine Verbindung zum WLAN herstellt, überprüfe die WLAN-SSID und das Passwort.
  • Solltest du Probleme mit der API-Verbindung haben, prüfe die Internetverbindung und ob die richtige API-URL verwendet wird.
  • Geht das Display aus, könnte die Dunkelheitsschwelle darkkThreshold zu niedrig sein. Setze sie im Zweifel auf 1023.

Nein, darkkThreshold ist kein Tippfehler sondern eine Hommage an die allerbesteste aller Metal-Bands, die ich noch nie gehört habe, Darkk.

Code

#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#include <cfloat>
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeSans12pt7b.h>
#include <Fonts/FreeSans18pt7b.h>

// SH1106 display setup
#define OLED_RESET    -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, &Wire, OLED_RESET);

// WiFi credentials
const char* ssid = "yourWiFiName";         // WiFi login goes here
const char* password = "yourWiFiPassword"; // WiFi login goes here

// Photoresistor threshold setup
#define darkkThreshold 1000  // 0...1023 Threshold value for darkkness intensity, higher number means display-off when darker
// 3.3V -- 10K -- A0 -- Photoresistor -- GND

// This code is getting Prices for Austria!!! For Germany just change the values commented. Just search the code for "Germany".



// Awattar API endpoint
const char* apiEndpoint = "/v1/marketdata";

// Arrow bitmaps
const uint8_t PROGMEM triangle_up[] = {
  0b00000000, 0b00000000,
  0b00000000, 0b10000000,
  0b00000001, 0b11000000,
  0b00000011, 0b11100000,
  0b00000111, 0b11110000,
  0b00001111, 0b11111000,
  0b00011111, 0b11111100,
  0b00111111, 0b11111110,
  0b01111111, 0b11111111,
  0b00000000, 0b00000000,
  0b00000000, 0b00000000,
  0b00000000, 0b00000000,
  0b00000000, 0b00000000,
  0b00000000, 0b00000000,
  0b00000000, 0b00000000,
  0b00000000, 0b00000000
};

const uint8_t PROGMEM triangle_down[] = {
  0b00000000, 0b00000000,
  0b00000000, 0b00000000,
  0b00000000, 0b00000000,
  0b00000000, 0b00000000,
  0b00000000, 0b00000000,
  0b00000000, 0b00000000,
  0b00000000, 0b00000000,
  0b01111111, 0b11111111,
  0b00111111, 0b11111110,
  0b00011111, 0b11111100,
  0b00001111, 0b11111000,
  0b00000111, 0b11110000,
  0b00000011, 0b11100000,
  0b00000001, 0b11000000,
  0b00000000, 0b10000000,
  0b00000000, 0b00000000
};


void setup() {
  Serial.begin(115200);
  display.begin(0x3C); // Initialize the display with I2C address 0x3C
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SH110X_WHITE);
  
  // Connect to WiFi
  display.setCursor(0,0);
  display.println("Connecting to WiFi:");
  display.println();
  display.println(ssid);
  display.println();
  display.println("...");
  display.display();
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected to WiFi");
  display.clearDisplay();
  display.setCursor(0,0);
  display.println("WiFi connected!");
  display.println();
  display.println(WiFi.SSID());
  display.println(WiFi.localIP());
  display.display();
  delay(5000);

  //Display Info
  display.clearDisplay();
  display.setCursor(0,0);
  display.println("Display zeigt Preis");
  display.println("und guenstigste 3h");
  display.println("innerhalb von 8h an.");
  display.println("Pfeil zeigt Trend.");
  display.println();
  display.println("Preise AT inkl MwSt."); // change to DE for Germany
  display.println();
  int darkkLevel = analogRead(A0);
  display.print("v5.2 240904 ");
  display.print(darkkLevel);
  display.print("/");
  display.println(darkkThreshold);
  display.display();
  delay(10000); // Display Info Text that long

  
  // Fetch and display the electricity price
  fetchAndDisplayPrice();
}

void loop() {
  // Check Ambient Light Level
  int darkkLevel = analogRead(A0);
  Serial.println(darkkLevel);

  if (darkkLevel > darkkThreshold) {
    display.clearDisplay(); // Clear the display if the threshold is exceeded
    display.display();      // Actually clear Display
    Serial.print(darkkLevel);
    Serial.print(" / ");
    Serial.println(darkkThreshold);
  } else {
    // Fetch and display the electricity price
    fetchAndDisplayPrice();
  }
  // Wait
  delay(600000); // Wait for 10 minutes
  // delay(10000); // Wait for 10 seconds
}

void fetchAndDisplayPrice() {
  WiFiClientSecure client;
  client.setInsecure(); // Disable SSL certificate validation

  if (!client.connect("api.awattar.at", 443)) {  // change to api.awattar.de for Germany
    Serial.println("Connection to API failed");
    display.clearDisplay();
    display.setCursor(0,0);
    display.println("API connection failed");
    display.display();
    return;
  }

  // Send HTTP GET request
  client.print(String("GET ") + apiEndpoint + " HTTP/1.1\r\n" +
               "Host: api.awattar.at\r\n" + // change to api.awattar.de for Germany
               "Connection: close\r\n\r\n");

  // Read the response
  String payload = "";
  bool jsonStarted = false;
  while (client.connected() || client.available()) {
    String line = client.readStringUntil('\n');
    if (jsonStarted) {
      payload += line + "\n";
    }
    if (line == "\r") {
      jsonStarted = true; // Start reading the JSON part
    }
  }
  
  client.stop();

  Serial.println("Received payload:");
  Serial.println(payload); // Print the received payload for debugging

  // Remove chunk size if present
  int chunkHeaderIndex = payload.indexOf("\r\n");
  if (chunkHeaderIndex != -1) {
    payload = payload.substring(chunkHeaderIndex + 2);
  }

  // Parse JSON response
  DynamicJsonDocument doc(8192); // Increase buffer size if needed
  DeserializationError error = deserializeJson(doc, payload);
  if (error) {
    Serial.print(F("deserializeJson() failed: "));
    Serial.println(error.f_str());
    display.clearDisplay();
    display.setCursor(0,0);
    display.println("JSON parse failed");
    display.display();
    return;
  }

  // Extract the current electricity price
  float currentPriceEURPerMWh = doc["data"][0]["marketprice"];
  float nextHrPriceEURPerMWh = doc["data"][1]["marketprice"];
  float currentPriceCTPerKWh = (currentPriceEURPerMWh / 10) * 1.2; // Convert to ct/kWh and add VAT, change to 1.19 for Germany
  
  // Find the cheapest 3-hour period within the next 8 hours
  float minAvgPrice = FLT_MAX;
  int minStartIndex = -1;
  int numEntries = doc["data"].size();
  
  for (int i = 0; i < numEntries - 2 && i < 8; ++i) {
    float sum = 0;
    for (int j = 0; j < 3; ++j) {
      sum += doc["data"][i + j]["marketprice"].as<float>();
    }
    float avgPrice = sum / 3;
    if (avgPrice < minAvgPrice) {
      minAvgPrice = avgPrice;
      minStartIndex = i;
    }
  }
  
  // Calculate the start time and average price of the cheapest 3-hour period
  float minAvgPriceCTPerKWh = (minAvgPrice / 10) * 1.2; // Convert to ct/kWh and add VAT, change to 1.19 for Germany
  
  // Display the prices and the start time
  display.clearDisplay();

  // Display current price
  display.setFont(&FreeSans18pt7b);
  display.setCursor(0,30);
  display.print(currentPriceCTPerKWh, 1);
  display.setFont(&FreeSans9pt7b);
  display.print(" ct");

  // Display trend arrows
  if (currentPriceEURPerMWh < nextHrPriceEURPerMWh) {
    display.drawBitmap(106, 1, triangle_up, 16, 16, SH110X_WHITE);
  }
  if (currentPriceEURPerMWh > nextHrPriceEURPerMWh) {
    display.drawBitmap(106, 4, triangle_down, 16, 16, SH110X_WHITE);
  }

  // Display cheapest 3-hour period price and hours from now to begin of cheapest hour
  display.setCursor(0,58);
  if (minStartIndex == 0) {
    display.setFont(&FreeSans9pt7b);
    display.println("Jetzt billig!");
  }
  else {
    display.setFont(&FreeSans12pt7b);
    display.print(minAvgPriceCTPerKWh, 1);
    display.setFont(&FreeSans9pt7b);
    display.print(" ct");
    display.print(" in ");
    display.setFont(&FreeSans12pt7b);
    display.print(minStartIndex, 0);
    display.setFont(&FreeSans9pt7b);
    display.println(" h");
  }
  

  display.display();
}

Wenn du Feedback loswerden möchtest, nutze bitte das Kontaktformular.

Falls dir mein Beitrag weitergeholfen hat, würde ich mich sehr über einen kleinen Kaffee freuen. Oder du nutzt einen meiner Empfehlungslinks und sparst dir damit etwas Geld: Hetzner Cloud (20€ Guthaben), Ufodrive (30€ Rabatt), aWATTar. Außerdem habe ich eine Wunschliste bei Amazon.


Schreibe einen Kommentar