NodeMCU: JSON-Daten von Fronius-Wechselrichter einlesen

In Hausautomation, Technik und Elektronik | Erstellt: 05.12.2018, 09:49 | Editiert: 10.12.2018, 14:11 (0,91-Zoll OLED integriert) | 21 mal angesehen | Seite drucken

Daten im JSON-Format aus einem Fronius-Wechselrichter mit Arduino/ESP8266 auslesen

Projekt der Woche: Live-Anzeige der PV- und Verbrauchs-Daten am Zählerschrank mit Minimal-Aufwand. Der Mikrocontroller verbindet sich zum WLAN und fragt zyklisch die Daten aus dem Wechselrichter ab. Diese Parameter können dann direkt weiter verarbeitet werden, z.B. Relais steuern, oder einfach angezeigt werden – je nach Geschmack.

Diese weitere Verarbeitung und die Anzeige sind nicht Teil dieser Seite, im Code ist jedoch die Ansteuerung eines I2C OLEDs integriert.

Hardware

ESP8266-Boards gibt es auch mit integriertem OLED-Display, z.B. hier.

API und Daten

Die Fronius-API stellt die Daten im JSON-Format bereit. Die Doku der API (V1) ist hier zu finden, für den Arduino hat sich die ArduinoJson-Library von Benoît Blanchon bewährt.

ArduinoJson in der Version 5.x in der IDE hinzufügen, nicht die 6.x – diese ist noch im Beta-Stadium.

Zum Test sind hier zwei JSON-Dateien online verfügbar:

  • www.onderka.com/wp-content/fronius-meter-example.json
  • www.onderka.com/wp-content/fronius-flow-example.json

Software und Code

Dieser Code ist schnell zusammengetippt. C ist nicht gerade meine Stärke, um es vorsichtig auszudrücken. Um so seltsamer ist es, dass mir bisher beim Testen keine segfault begegnet ist… SSID und PSK sowie Hostname des Wechselrichters anpassen.

Noch einmal deutlicher: Mit diesem Code sollte man keine Herz-Lungen-Maschine betreiben!

#include <ArduinoJson.h>        // Arduino JSON (5.x stable, nicht 6.x)
#include <ESP8266WiFi.h>        // ESP8266 WiFi
#include <WiFiClient.h>         // ESP8266 Wifi-Client
#include <ESP8266HTTPClient.h>  // ESP8266 HTTP-Client
#include <Wire.h>               // Wire / I2C
#include <SSD1306Ascii.h>       // 1306 OLED Display
#include <SSD1306AsciiWire.h>   // 1306 OLED Display über I2C

// WiFi SSID und PSK
const char *ssid = "meine_ssid";
const char *password = "mein_psk";
const char *host_name = "esp_pva_01";

// Wechselrichter Hostname oder IP-Adresse
const String inverterHostname = "symo.local,lan";

// Fronius Solar API URLs "Meter Data" und "Flow Data"
String urlMeter = "http://" + inverterHostname + "/solar_api/v1/GetMeterRealtimeData.cgi?Scope=Device&DeviceId=0";
String urlFlow  = "http://" + inverterHostname + "/solar_api/v1/GetPowerFlowRealtimeData.fcgi";

// Display-Setup
#define I2C_ADDRESS 0x3C
// Reset-Pin des Displays, falls benötigt
#define RST_PIN -1
//#define RST_PIN 16

// Display initialisieren
SSD1306AsciiWire oled;

// setup()
void setup() {
  // 1s warten
  delay(1000);

  // I2C an
  // Wire.begin(int sda, int scl);
  Wire.begin();
  Wire.setClock(400000L);

  // Display an
  #if RST_PIN >= 0
    oled.begin(&Adafruit128x32, I2C_ADDRESS, RST_PIN);
  #else
    oled.begin(&Adafruit128x32, I2C_ADDRESS);
  #endif
  
  // Serielle Schnittstelle an, 115200/8/N/1
  Serial.begin(115200);

  oled.setFont(System5x7);
  oled.clear();
  oled.println("Seriell 115200");

  // Heltec Wifi Kit 8: GPIO16, Einschlafen des Displays verhindern
  //digitalWrite(16, LOW);
  
  // WiFi aus
  WiFi.mode(WIFI_OFF);
  // 1s warten
  delay(1000);
  // WiFi Client-Modus, AP-Modus aus
  WiFi.mode(WIFI_STA);
  // Hostname setzen, falls gewünscht. Default ist "ESP_[MACADR]"
  //WiFi.hostname(host_name);
  // WiFi verbinden
  WiFi.begin(ssid, password);
  oled.println("Verbinde WiFi ...");

  Serial.println();
  Serial.println("ESP8266 WiFi-Client für Fronius Solar Api V1");
  Serial.println("https://www.onderka.com/hausautomation-technik-und-elektronik/nodemcu-json-daten-von-fronius-wechselrichter-einlesen/");
  Serial.println();
  Serial.print("Verbinde ");

  // Auf Verbindung warten
  while (WiFi.status() != WL_CONNECTED) {
    // Alle 0.5s einen Punkt
    delay(500);
    Serial.print(".");
  }
  Serial.println(" Verbunden.");

  oled.clear();
  oled.println("WiFi verbunden");
  oled.println();
  oled.println(WiFi.localIP());
  oled.println(WiFi.macAddress());

  // Verbindungs-Informationen
  Serial.println();
  Serial.print("SSID:              ");
  Serial.println(ssid);
  Serial.print("Kanal:             ");
  Serial.println(WiFi.channel());
  Serial.print("Signal (RX):       ");
  Serial.print(WiFi.RSSI());
  Serial.println(" dBm");
  Serial.print("IP-Adresse:        ");
  Serial.println(WiFi.localIP());
  Serial.print("MAC-Adresse:       ");
  Serial.println(WiFi.macAddress());
  Serial.println();
  // Ende setup()

  delay(2000);
}

// loop()
void loop() {
  // HTTP-Clients initialisieren
  HTTPClient http;
  const size_t capacityMeter = JSON_OBJECT_SIZE(3) + JSON_ARRAY_SIZE(2) + 60;
  const size_t capacityFlow  = JSON_OBJECT_SIZE(3) + JSON_ARRAY_SIZE(2) + 60;

  // JSON-Puffer initialisieren
  DynamicJsonBuffer jsonBufferMeter(capacityMeter);
  DynamicJsonBuffer jsonBufferFlow(capacityFlow);

  // URL #1 - Meter Data
  http.begin(urlMeter);
  int httpCodeMeter = http.GET();
  // JSON-Antwort
  String httpResponseMeter = http.getString();
  // HTTP-Client beenden
  http.end();

  Serial.print("URL:               ");
  Serial.println(urlMeter);
  Serial.print("HTTP Status:       ");
  Serial.println(httpCodeMeter);
  JsonObject& jsonMeter = jsonBufferMeter.parseObject(httpResponseMeter);
  if (!jsonMeter.success()) {
    // JSON nicht erfolgreich geparst
    Serial.println("JSON-Parser:       Fehler");
  } else {
    Serial.println("JSON-Parser:       OK");
  }

  // URL #2 - Flow Data
  http.begin(urlFlow);
  int httpCodeFlow = http.GET();
  // JSON-Antwort
  String httpResponseFlow = http.getString();
  // HTTP-Client beenden
  http.end();

  Serial.print("URL:               ");
  Serial.println(urlFlow);
  Serial.print("HTTP Status:       ");
  Serial.println(httpCodeFlow);
    JsonObject& jsonFlow = jsonBufferFlow.parseObject(httpResponseFlow);
  if (!jsonFlow.success()) {
    // JSON nicht erfolgreich geparst
    Serial.println("JSON-Parser:       Fehler");
  } else {
    Serial.println("JSON-Parser:       OK");
  }
  Serial.println();

  // Timestamp Daten Power Meter
  String ts_meter = ( jsonMeter["Head"]["Timestamp"] | "Leer" );
  // Momentanverbrauch Phase1/2/3
  float p_phase1  = ( jsonMeter["Body"]["Data"]["PowerReal_P_Phase_1"] | 0 );
  float p_phase2  = ( jsonMeter["Body"]["Data"]["PowerReal_P_Phase_2"] | 0 );
  float p_phase3  = ( jsonMeter["Body"]["Data"]["PowerReal_P_Phase_3"] | 0 );
  // Spannung Phase1/2/3
  float u_phase1  = ( jsonMeter["Body"]["Data"]["Voltage_AC_Phase_1"] | 0 );
  float u_phase2  = ( jsonMeter["Body"]["Data"]["Voltage_AC_Phase_2"] | 0 );
  float u_phase3  = ( jsonMeter["Body"]["Data"]["Voltage_AC_Phase_3"] | 0 );
  // Strom Phase1/2/3
  float i_phase1  = jsonMeter["Body"]["Data"]["Current_AC_Phase_1"].as<float>();
  float i_phase2  = jsonMeter["Body"]["Data"]["Current_AC_Phase_2"].as<float>();
  float i_phase3  = jsonMeter["Body"]["Data"]["Current_AC_Phase_3"].as<float>();
  // Momentane Leistung Summe
  float p_summe   = ( jsonMeter["Body"]["Data"]["PowerReal_P_Sum"] | 0 );
  // Momentane Blindleistung Summe
  float p_sum_ap  = ( jsonMeter["Body"]["Data"]["PowerApparent_S_Sum"] | 0 );
  // Durchschnitt Netzfrequenz
  float net_freq  = jsonMeter["Body"]["Data"]["Frequency_Phase_Average"].as<float>();

  // Timestamp Daten Power Flow
  String ts_flow  = ( jsonFlow["Head"]["Timestamp"] | "Leer" );
  // Energie Tag
  float p_day     = ( jsonFlow["Body"]["Data"]["Inverters"]["1"]["E_Day"] | 0 );
  // Energie Jahr
  float p_year    = ( jsonFlow["Body"]["Data"]["Inverters"]["1"]["E_Year"] | 0 );
  // Energie Gesamt
  float p_total   = ( jsonFlow["Body"]["Data"]["Inverters"]["1"]["E_Total"] | 0 );
  // Einspeisung / Bezug: Negativ Einspeisung, positiv Bezug
  float in_out    = ( jsonFlow["Body"]["Data"]["Site"]["P_Grid"] | 0 );
  // Verbrauch momentan
  float cons      = ( jsonFlow["Body"]["Data"]["Site"]["P_Load"] | 0 );
  // Produktion momentan
  float prod      = ( jsonFlow["Body"]["Data"]["Site"]["P_PV"] | 0 );
  // Autonomie (% Produktion an Verbrauch)
  float autonomy  = ( jsonFlow["Body"]["Data"]["Site"]["rel_Autonomy"] | 0 );
  // Selbstverbrauch (% Eigenverbrauch an Produktion)
  float selfcons  = ( jsonFlow["Body"]["Data"]["Site"]["rel_SelfConsumption"] | 0 );

  // Ausgabe seriell
  Serial.print("Timestamp Meter:   ");
  Serial.println(ts_meter);
  Serial.print("Timestamp Flow:    ");
  Serial.println(ts_flow);

  Serial.print("Spannung Phase 1:  ");
  Serial.print(u_phase1);
  Serial.println(" V");
  Serial.print("Spannung Phase 2:  ");
  Serial.print(u_phase2);
  Serial.println(" V");
  Serial.print("Spannung Phase 3:  ");
  Serial.print(u_phase3);
  Serial.println(" V");

  Serial.print("Strom Phase 1:     ");
  Serial.print(i_phase1);
  Serial.println(" A");
  Serial.print("Strom Phase 2:     ");
  Serial.print(i_phase2);
  Serial.println(" A");
  Serial.print("Strom Phase 3:     ");
  Serial.print(i_phase3);
  Serial.println(" A");

  Serial.print("Leistung Phase 1:  ");
  Serial.print(p_phase1 * -1);
  Serial.println(" W");
  Serial.print("Leistung Phase 2:  ");
  Serial.print(p_phase2 * -1);
  Serial.println(" W");
  Serial.print("Leistung Phase 3:  ");
  Serial.print(p_phase3 * -1);
  Serial.println(" W");

  Serial.print("Leistung Summe:    ");
  Serial.print(p_summe * -1);
  Serial.println(" W");
  Serial.print("Scheinleistung:    ");
  Serial.print(p_sum_ap);
  Serial.println(" W");

  Serial.print("Netzfrequenz:      ");
  Serial.print(net_freq);
  Serial.println(" Hz");

  Serial.print("Produktion Tag:    ");
  Serial.print(p_day / 1000);
  Serial.println(" kWh");
  Serial.print("Produktion Jahr:   ");
  Serial.print(p_year / 1000);
  Serial.println(" kWh");
  Serial.print("Produktion Gesamt: ");
  Serial.print(p_total / 1000);
  Serial.println(" kWh");

  Serial.println();

  // Negativ Einspeisung, positiv Bezug
  if ( in_out >= 0) {
    // Bezug
    Serial.print("Strom Bezug:       ");
    Serial.print(in_out);
    Serial.println(" W");
    Serial.println("Strom Einspeisung: 0.00 W");
  } else {
    // Einspeisung
    Serial.println("Strom Bezug:       0.00 W");
    Serial.print("Strom Einspeisung: ");
    Serial.print(in_out * -1);
    Serial.println(" W");
  }

  Serial.print("Verbrauch Haus:    ");
  Serial.print(cons * -1);
  Serial.println(" W");
  Serial.print("Leistung PV:       ");
  Serial.print(prod);
  Serial.println(" W");
  Serial.print("Autonomie:         ");
  Serial.print(autonomy);
  Serial.println(" %");
  Serial.print("Eigenverbrauch:    ");
  Serial.print(selfcons);
  Serial.println(" %");
  Serial.println();

  // OLED
  oled.clear();

  oled.print("Verbrauch:  ");
  oled.print(cons * -1);
  oled.println(" W");
  oled.print("Produktion: ");
  oled.print(prod);
  oled.println(" W");
  // Negativ Einspeisung, positiv Bezug
  if ( in_out >= 0) {
    // Bezug
    oled.print("Bezug:      ");
    oled.print(in_out);
    oled.println(" W");
    oled.println("Einspeisg.: 0.00 W");
  } else {
    // Einspeisung
    oled.println("Bezug:      0.00 W");
    oled.print("Einspeisg.: ");
    oled.print(in_out * -1);
    oled.println(" W");
  }

  // 10s warten
  delay(10000);

  // Ende loop()
}

// EOF

Weitere Seiten in 'Hausautomation, Technik und Elektronik'