NodeMCU: JSON-Daten von Fronius-Wechselrichter einlesen

Geschrieben am 05.12.2018, zuletzt geändert am 19.10.2019
In Home » Technik & Elektronik

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:

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

Andere Seiten unter 'Technik & Elektronik'