Thursday, October 24, 2019

ESP32 OLED display of power generated via Fronius inverter

This is a followup to an earlier post where I used an ESP8266 to connect to Wifi and call a JSON web service on my Fronius inverter to show the current power being generated by the solar panels here on the house.

Since then, I've moved on to marvellous little boards that combine an ESP32 with an OLED display.


(Yes, I'm rushing to finish this before the sun goes down).

The Fronius inverter joins the home wifi network and has a simple web service endpoint that returns JSON data.

To compile this you'll need:
Settings in the Arduino IDE are:

  • Board: WEMOS LOLIN32
  • Upload speed: 921600
  • CPU Frequency: 240MHz (WiFi/BT)
  • Flash Frequency: 80MHz
  • Partition Scheme: "Default"
  • Port: "/dev/cu.SLAB_USBtoUART" (Note that I'm on macOS)

Documentation is a bit of a puzzle and some of the examples are wrong for this board. The magic I needed to talk to the display on this board is:

SSD1306  display(0x3c, 5, 4);

To program the ESP32 board from the Arduino IDE, the trick is to click upload and wait until it starts showing "Connecting........_____.". Then hold the "Boot" button, and let go when it starts uploading code.

For my own future reference (and you never know, this might help someone else), here's my code. I was using Arduino 1.8.10 but have now switched to Visual Studio Code with PlatformIO. (Note that Angle Brackets don't work on Blogger so the include files have quotes in place of them). Don't forget to fill in your own WiFi SSID and Password.


#include "SSD1306.h" // alias for `#include "SSD1306Wire.h"`
#include "WiFi.h"
#include "ArduinoJson.h"
#include "HTTPClient.h"

// Fronius Inverter
const char *HOST = "192.168.86.23";
const char *SSID = "XXXXXX";
const char *PASSWORD = "XXXXXX";
const long kMaxPower = 3300; // maximum watt for the bar graph
const int16_t displayWidth = 128;
const int16_t displayHeight = 64;
  
// Initialize the OLED display using Wire library
SSD1306  display(0x3c54);
void connectToWiFi(const char * ssidconst char * pwd);

void setup() {
  // put your setup code here, to run once:
  //Serial.begin(115200);
  display.init();
  // display.flipScreenVertically();
  display.setContrast(255);
  display.setFont(ArialMT_Plain_24);
  display.setTextAlignment(TEXT_ALIGN_CENTER);
  connectToWiFi(SSID, PASSWORD);
}

void connectToWiFi(const char * ssidconst char * pwd)
{
    WiFi.begin(ssid, pwd);
    while (WiFi.status() != WL_CONNECTED) 
    {
        delay(500);
        //Serial.print(".");
    }
    //Serial.print("Wifi connected");
}

void displayThis(String textlong power
{
  display.clear();
  display.drawString(displayWidth / 216, text); // x,y
  const int16_t barHeight = 8;
  
  display.drawRect(0, displayHeight - barHeight, displayWidth, barHeight);
  long barWidth =  power * displayWidth / kMaxPower;
  display.fillRect(0, displayHeight - barHeight, barWidth, barHeight);
  display.display();
}

void loop() {  
  if((WiFi.status() == WL_CONNECTED)) {
        HTTPClient http;

        String url = "http://" + String(HOST) + "/solar_api/v1/GetInverterRealtimeData.cgi?Scope=System";
        http.begin(url);

        // start connection and send HTTP header
        int httpCode = http.GET();

        // httpCode will be negative on error
        if(httpCode > 0) {
            // HTTP header has been send and Server response header has been handled

            // file found at server
            if(httpCode == HTTP_CODE_OK) {
                String payload = http.getString();
                
                // Allocate the JSON document
                // Use arduinojson.org/v6/assistant to compute the capacity.
                //const size_t capacity = JSON_OBJECT_SIZE(3) + JSON_ARRAY_SIZE(2) + 60;
                DynamicJsonDocument doc(900);

                // Parse JSON object
                 DeserializationError error = deserializeJson(doc, payload);
                 if (error) {
                      //Serial.print(F("deserializeJson() failed: "));
                      //Serial.println(error.c_str());
                      return;
                  }

                  // Extract current power generated from the Fronius inverter
                  long generatedPower = doc["Body"]["Data"]["PAC"]["Values"]["1"];
                  String displayedPower = String(generatedPower) + " W";
                  displayThis(displayedPower, generatedPower);
            }
        } else {
          displayThis("HTTP Error"0);
            //Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
        }

        http.end();
    } else {
          displayThis("Wifi Error"0);
          connectToWiFi(SSID, PASSWORD);
    }
  delay(5000);
}

The sun has come out again!


I've added a bar that shows how much of the maximum output of the panels we're at. Not in the code above but it's pretty simple using the excellent display library.


Update: now building under PlatformIO

I prefer to develop using Visual Studio Code and PlatformIO but initially ran in to trouble getting it to work with the ESP32. This tutorial gave me the key, which is to use the "Espressif ESP32 Dev Module" as the board.

One reason why I prefer using VSC to edit is the excellent code completion:


The Arduino IDE has a lot of weirdness compared to what I'm used to.

1 comment:

Sven H. said...

hey! you did a great job!
I am using parts of your code to send date e.g. to node-red and to influx to show everything in grafana.
You only did extract the actual power the inverter generates. But it would be nice if you also could extract the information about the actual value of the electricity meter. I mean the power which is actualy in use respectively the power which is actualy going into the grid.

At the moment I read this value by an LDR sensor directly at my electricity meter. But I want to use the data of the smartmeter to get rid of the additional device at my electricity meter.

Can you help me somehow?