Artikel Programmeren met een ESP32

SolarEdgeDisplay

Inleiding

Als .NET ontwikkelaar bij Vitas ontwikkel ik mooie software bij klanten. In deze coronatijden is het ook leuk om thuis te knutselen met echte elektronica. Deze combinatie van moderne front-end tot low-level backend vind ik nog steeds super interessant. In deze blog lees je iets meer over een project waarbij zonnepaneel data gebruikt wordt om data op een bijzonder klein schermpje te tonen.

Doel

Ontwikkelen van eigen hardware/firmware app voor uitlezen van zonnepaneel systeem van SolarEdge. Leeronderdelen zijn onder andere;

  • Programmeren in C/C++
  • Gebruik van PlatformIO vanuit Visual Studio
  • Aansturing van een OLED display
  • Gebruik van WIFI
  • Gebruik van een API met HTTPS

Het is handig om wat elektronica kennis te hebben al is dat voor dit systeem niet echt noodzakelijk.

Benodigde onderdelen

Ik ga dit in twee delen opsplitsen; hardware en software.

Voor de hardware gebruik ik een ESP32 microcontroller. Deze microcontroller is een Arduino on steroids. Ik heb gekozen voor de Wemos D1 R32. Deze heeft hetzelfde formaat als een Arduino UNO en heeft ook WiFi en bluetooth aan boord.

Voor het tonen van data maak ik gebruik van een OLED display. Dit schermpje is amper 1 inch groot en heeft 128 x 96 pixels waarvan de bovenste 16 geel zijn. Dit is handig voor bijvoorbeeld een titel of andere compacte informatie. Het display wordt middels een I2C bus verbonden met de microcontroller.

I2C bus?

Met een I2C bus kan er op een eenvoudige manier gecommuniceerd worden tussen meerdere apparaten. Er zijn maar 2 verbindingen nodig (data SDA en klok SCL signaal) en er kan tot maar liefst 127 andere apparaten aangesloten worden. Ik hou het even bij één OLED display.

Aan de bovenkant zijn van links naar rechts de verbindingen te zien;

  • GND = Ground of 0 volt
  • VCC = Voedingsspanning
  • SCL = I2C klok signaal
  • SDA = I2C data signaal

Hardware kosten:

  • WeMos ESP32 € 9,99
  • Mini OLED display € 5,95
  • Breadboard € 2,95
  • Bedrading € 1,59

Voor de software maak ik gebruik van verschillende gratis tools.

Het voordeel van Visual Studio Code is de betere integratie met andere tools zoals GIT en PlatformIO. De editor van VSCode werkt net iets soepeler met zaken als code-completion.

Stappen

Om mijn SolarEdgeDisplay app te ontwikkelen zijn een aantal stappen nodig. Deze kunnen grotendeels zelfstandig ontwikkeld worden.

  • Grafiek tekenen
  • Verbinding maken met WiFi
  • SolarEdge API data ophalen; vandaag overzicht en vermogens data
  • Data verwerken naar grafiek

Uiteraard kan dit veel verder ontwikkeld worden maar voor de proof-of-concept beperk ik de app tot bovenstaande punten. De source code kun je terug vinden op mijn GitHub pagina;

https://github.com/jan-marten/SolarEdgeDisplay

Display-code

display->DrawChart(title, values)

Ik heb gekozen om met de aansturing van het OLED display te beginnen. Op die manier heb je gelijk iets leuks om naar te kijken. De daadwerkelijke invulling van de grafiek komt later. Ter illustratie heb ik in eerste instantie gebruik gemaakt van random-data.

Voor het tonen van een grafiek wil ik 2 dingen zien;

  • Titel met daarin de opbrengst van vandaag en het huidige vermogen
  • Een grafiek opgeleverde energie

Rekensommetje voor positionering

De hoogte van het scherm is 64 pixels. De bovenste rij is 16 pixels hoog en onderaan wil ik 8 pixels reserveren voor tekst onder de grafiek. Dat houdt in dat we 64-16-8=40 pixels over hebben. Dat komt mooi uit want mijn systeem heeft een piekvermogen van 3,75 kW. Voor elke 0,1 kW is dus 1 pixel beschikbaar (schaal 1px:100w).

Voor de breedte hebben we 128 pixels waarvan er 8 gebruikt worden voor de labels aan de linkerzijde. Eigenlijk zijn we ook alleen maar geïnteresseerd in nuttige datapunten tussen 6:00 en 21:00. De SolarEdge API heeft een resolutie van 15 minuten en dat komt dus mooi uit. Van 6:00 tot 21:00 is 60 kwartier en dat kunnen we mooi verdelen over 120 pixels (2 pixels per kwartier). We hoeven dus geen aliasing te doen tussen 2 punten.

display->DrawChartAutoScaled

Omdat het in Nederland ook weleens bewolkt is leveren de zonnepanelen niet altijd het maximale vermogen. Je krijgt op dit soort dagen een vrij platte grafiek met pieken tot bijvoorbeeld 600 W ipv 3,5 kW. Dat ziet er niet zo mooi uit op het display. Om de grafiek wat op te blazen gebruik ik automatische schaling. Voor mijn eigen systeem heb ik dit opgedeeld in 3 verschillende schalen;

  • 0 - 4 kW (schaal 1px:100w)
  • 0 - 2 kW (schaal 1px:50w)
  • 0 - 1 kW (schaal 1px:25w)

Bij het bepalen welke schaal we nodig hebben is een eenvoudige Array.Max functie nodig waarna we de juiste schaal kunnen toepassen.

Netwerk-code

Nu het display aan de praat is geraakt moet ik data gaan ophalen via de SolarEdge API. Hier heb ik eerst een netwerkverbinding voor nodig. Gelukkig kan ik hier bestaande voorbeeld voor gebruiken. Na het verbinden wil ik ook gelijk de klok correct instellen.

Hierna heb ik de netwerk class uitgebreid met de volgende functies;

  • Init - deze maakt verbinding met het WIFI-accespoint
  • SetClock - synchroniseer interne klok middels NTP
  • GetData(url) - generieke API functie, wordt gebruikt door;
    • GetDataOverview voor getalletjes bovenin
    • GetDataPower voor grafiekdata

network->Init() en SetClock()

Het verbinden met een WIFI-netwerk is redelijk eenvoudig. Ik maak gebruik van bestaande voorbeeld code om een verbinding te maken. Na het maken van een verbindingen wil ik de tijd instellen met een NTP server. De tijd willen we later gebruiken om SolarEdge data op te halen.

  • configTime(TIMEZONE_OFFSET, 0, "pool.ntp.org", "time.nist.gov");
    • TIMEZONE_OFFSET is bij ons 1 uur of 3600 seconden
    • 0 is wintertijd, 1 is zomertijd
    • pool.ntp.org en time.nist.gov zijn NTP servers waar ik de klok mee gelijk wil zetten

network->GetData(url)

De ESP32 mag dan wel bekend staan als IOT controller met WIFI maar eigenlijk moet je zelf alles nog programmeren. Zo ook veilig communiceren via HTTPS. Op een gewone computer met een moderne webbrowser zal HTTPS al snel de standaard zijn. De benodigde certificaten worden door het operating system als vanzelfsprekend meegeleverd. In dit geval moeten we dit zelf regelen.

Om te kunnen communiceren met de SolarEdge API is het root-certificaat nodig. Met een webbrowser kan dit eenvoudig opgehaald en geëxporteerd worden naar BASE64-text. Hierna kan ik het in code opnemen.

Via details kun je dan vervolgens het certificaat als Base64 encoded X.509 opslaan waarna je het met notepad weer kunt uitlezen. Deze informatie kunnen we dan meegeven met de HTTPS client.

Het ophalen van data doe ik daarna in 2 stappen.

  • GetDataOverview / Power (public)
  • GetData(url) (private)

De main-functie gebruikt de publieke GetDataOverview en GetDataPower functies die op hun beurt de interne GetData functie gebruiken. De GetData functie levert de ruwe JSON data op en doet er verder niets mee.

Even op de rem trappen; throttling

Het gebruik van de SolarEdge API kent ook zijn beperkingen. Je kunt maar 300 keer per dag data ophalen. Ik wil dus data een poosje bufferen voordat ik opnieuw data ophaal. Omdat er toch maar per kwartier gemeten kan worden is dit een mooi interval;

  • if (previousRequestTime + 15minutes > currentTime)
    • return bufferedData
  • else
    • nieuwe data ophalen
    • in buffer opslaan
    • previousRequestTime bijwerken naar currentTime
    • return bufferedData

Op deze manier hoeft de main-loop hier geen rekening mee te houden en kan dus elk gewenst moment data ophalen en tonen op het display.

network->GetDataOverview/Power

De data die we ophalen van SolarEdge is in JSON formaat. Dit formaat ziet er ongeveer als volgt uit;

{
    "overview": {
        "lastUpdateTime": "2021-03-29 15:43:40",
        "lifeTimeData": {
            "energy": 2328172.0,
            "revenue": 481.62405
        },
        "lastYearData": {
            "energy": 582105.0
        },
        "lastMonthData": {
            "energy": 253544.0
        },
        "lastDayData": {
            "energy": 18769.0
        },
        "currentPower": {
            "power": 1343.0
        },
        "measuredBy": "INVERTER"
    }
}

Met de ArduinoJson library kunnen we dit inlezen en omzetten naar bruikbare array's en data objecten. Hieronder staat code om uit bovenstaande JSON het huidige vermogen uit te lezen;

  • DynamicJsonDocument doc(1024);
  • deserializeJson(doc, GetData(url));
  • CurrentPower = doc["overview"]["currentPower"]["power"];

Zoals je hier zit wordt het JSON document (tekst) omgezet in een array waarbij we direct een specifiek data object kunnen uitlezen. In dit geval zal CurrentPower de waarde 1343.0 hebben. Deze informatie slaan we op in buffer en wordt voor 15 minuten bewaard voordat er weer nieuwe data opgehaald wordt.

End-of-file

Zo komen we alweer bij het laatste deel. We blikken nog even terug en gaan een aantal conclusies trekken van wat er geleerd is. Ten slotte nog een paar ideeën voor toekomstige wijzigingen en verbeteringen.

Terugblik

Ik vond het ontwikkelen op een nieuw platform een leuke oefening. Normaliter ontwikkel ik in C# en hoef je met veel zaken geen rekening te houden zoals HTTPS (certificaten) en het gebruik van pointers in C/C++. Gelukkig is er een grote schare fans voor Arduino's en ESP32's te vinden op internet en maakt dit het maken van software een stuk eenvoudiger (denk aan de U8G2 en ArduinoJson bibliotheken).

Uiteraard zijn er ook valkuilen waar je voor moet oppassen. Voor deze proof-of-concept heb ik veel happy-flow code geschreven. Normaliter hou ik rekening met wat er mis kan gaan in processen. Helaas is mijn hobbytijd beperkt en gaat mijn voorkeur uit naar een werkend geheel.

Neemt niet weg dat ik in de toekomst nog weleens tijd wil besteden aan;

  • Fout afhandeling
  • User interfacing
  • Fraaiere GUI
  • Uitgebreider menu
  • Multi threading
  • Koppeling met P1Monitor
  • Webserver implementatie
  • Etcetera...