NordicOffGrid´ Logo
Off-grid solcellssystem.
I Nordiskt klimat!
 by Bosse
By AutonomTech.se Frittliv webbplats

 Webbsidan är senast uppdaterad:  

PWM-solladd­regulator

Anpassad för Nordiskt kli­mat & sol­energi

Skapad: 2022-06-02
PWM-solladd­regulator: ESP32, FreeRTOS, framework-arduinoespressif32
PWM-solladd­regulator byggd på: ESP32, FreeRTOS, framework-arduinoespressif32 & C/C++

Det Nordiska klimatet med korta dagar av dagsljus runt midvinter och perioder med låg solinstrålning ställer lite speciella krav på en solladdregulator för att ge ett välfungerande standalone off-grid solcellssystem. Speciellt ihop med blybatterier, men även med LiFePO4.
Beskrivet här som Nordisk solladd­regulator.
Ska bli en hybrid av PWM-regulator och batterimonitor integrerade ihop.
Batterimonitor-delen har en egen webbsida: BatteriMonitor
Kommer uppdatera här efterhand som jag utvecklar regulatorn.

Projektsammanfattning:

PWM-regulatorn kommer byggas kring en ESP32 som kodas i C/C++ ovanpå FreeRTOS, med ström- / effekt-sensorerna INA260 / INA226 samt med Olimex Shield-LCD 16x2.
Batteritemperaturen kommer kännas av via en DS18B20 tempgivare.
Kommer koda ESP32-projektet med framework-arduinoespressif32 då det ger stöd för många bibliotek till sensorer, LCD-display, etc samt i sig är byggt på FreeRTOS så blir ingen extra overhead med det, samt koda med IDE PlatformIO i VSCode.
Övrig elektronikhårdvara utvecklar och konstruerar jag själv.
Kommer bygga på PWM+ solladdteknik baserad på min framtagna strömbuffrande RC-krets.

Utvärdering av FreeRTOS´s möjligheter och funktion på ESP32:

Har läst på om FreeRTOS samt kodat lite experimentellt i en WeMOS ESP32 för att testa, lära och få insikt i möjligheterna med FreeRTOS. Känns superbra för vad jag vill kunna uppnå!
2022-05-30
Lite testande av flera Task i båda kärnorna, med olika prioritet, med styrd exekveringsfrekvens samt med Mutex-låsning vid användning av gemensamma systemresurser:
Kör nu tre Task parallellt multitaskande på två olika kärnor där alla tre skriver till Serial.print(). De får då begära tillgång till Serial.print() och via Mutex låsa den för sin skrivning och sedan släppa den fri igen så en annan Task som kanske väntar på sin tur kan låsa den och skriva sitt. Fungera otroligt bra!
Den ena Task har jag satt till att exekvera 1ggr/1000ms via vTaskDelayUntil(), vilket styrs med en upplösning på ±1ms! Funkar också otroligt bra. Kan bli några ms fördröjning då den väntar på sin tur att kunna få skriva till Serial.print(), men när inte det påverkar så blir det i regel 1000±1ms här. Är extremt användbart!

Tänker ha en Task som uppdaterar LCD-displayen 1ggr/1000ms med låg prioritet, en Task som läser av knapparna på displaymodulen 1ggr/200ms samt en Task för solladdregulatorns reglerloop som troligen får exekvera 1ggr/200ms med lite högre prioritet så den får lite högre precision i det. Samt en Task som hämtar tiden från Internet 1ggr/dygn och synkar den interna RTC från en NTP Client-Server på Internet. Och en Task som sköter webbservern och webbsidan med driftsdata för PWM-regulatorn för lokal visning via wifi.
Och säkert någon mer Task.
Kräver bara så lite kod att få till så avancerad funktion i koden med hjälp av FreeRTOS funktionalitet, så känns riktigt häftigt.
Kodar då i VSCode med PlatformIO, vilket är väldigt trevligt och smidigt!
Och framework-arduinoespressif32 är byggt på FreeRTOS, så blir ingen extra overhead att använda FreeRTOS i min kod.

Exempel på utskrift via Serial.print() från den Task som exekverar 1ggr/1000ms:
Core0TaskTimed_Begin_ZZZZZZZZZZZZZZZZZZZZZZZZZ
Task0 looptime xTaskDelayUntil(1000) (ms): 1000
Task running on core 0
Today date / time: 2022-05-30 14:04:47
Task0 looptime within Serial.print() (ms): 1000
xWasDelayed: 1
Core0TaskTimed_End_ZZZZZZZZZZZZZZZZZZZZZZZZZZZ

Har nu följt den loggade looptiden (TaskDelayUntil-tid) för den Task som exekverar 1ggr/1000ms via vTaskDelayUntil() en hel del och är imponerande med vilken precision det sker.
De allra flesta hamnar på en looptid på exakt 1000ms!
När inte den rätt glesa väntan på att tillgången till Serial.print släpps fri via Mutex från annan Task fördröjer så hamnar uppskattningsvis minst 95% av gångerna på 1000ms och övriga på 999ms!
Ser även att den något högre prioritet (5) jag gav denna Task verkligen prioriterar den före en annan Task med lägre prioritet (3) som arbetar snarlikt men med vTaskDelay() istället.
Skapar väldigt bra och kodmässigt enkla möjligheter för mig att realisera den avancerade funktion jag planerar att uppnå i min ESP32 PWM-solladdregulator.

2022-06-02
Kodade in en 4:e Task som pinnades till kärna 1 med vTaskDelayUntil() 200ms, så nu är det 4 Tasks som körs multitaskande parallellt, två på vardera kärna. På kärna 1 körs även main Task, Arduinos loop(), där det mesta görs med mycket Serial.print() som belastar. Koden består av 619 kodrader.
I denna 4:e Task skriver jag bara var 20:e exekvering till Serial.print() för att få bra statistik på hur exakt vTaskDelayUntil() arbetar utan att störas för mycket på att vänta in ledig Serial.print(), så även kodat en statistikdel där.
Fick följande statistik för med vilka tidsmellanrum denna Task exekveras under ca 13000ggr, för två olika prioritet för den (prioritet 1, 3 & 5 hos övriga Task):
Resultat: Min 198ms, Max 221ms, 199ms 225(1%), 200ms 13695(98%), 201ms 9(0%), Priority 7 of the task.
Resultat: Min 0ms, Max 729ms, 199ms 394(3%), 200ms 12437(96%), 201ms 68(0%), Priority 1 of the task samma som Arduino loop().
Man ser tydligt skillnaden en högre prioritet gör, där prioritet 7 ger som resultat att denna 4:e Task´s tidsmellanrum för exekvering blir 200ms i 98% av fallen, 199ms i 1% samt övriga tider mellan 198-221ms i 1% av fallen, där sannolikt omkring 221ms dominerar som en fördröjning i väntan på att få tillgång till Serial.print(). Är intressant och imponerande tycker jag!
FreeRTOS tillför verkligen en väldigt värdefull funktionalitet för kodandet av mina ESP32-projekt, som samtidigt är lätt greppbar att utnyttja i koden!

Blockschema över kodens funktion i ESP32:

2022-06-02
Ett första utkast till blockschema över kodens uppbyggnad i ESP32, baserad på FreeRTOS:
Se även info om Inter-Task Communication, Deferred interrupt samt vTaskDelayUntil().

Code flow-schart
ESP32 kod blockschema - första utkast 2022-06-02

Utveckling kod / funktion i ESP32 under FreeRTOS:

Nytt: 2022-07-05
Första två FreeRTOS-Task, ESP32:
Har nu kodat en funktionell grund för de två första självständiga FreeRTOS Tasks funktionsblocken, LCDbuttonsHandler samt DisplayViewHandler. Samt raderar Arduino loop()-task efter att min första FreeRTOS-task är skapad: vTaskDelete(nullptr);, så bara har egna Tasks.
Fick även skapa en egen startup1(), för funktionen påverkades när man raderade den Arduino-task som kört Arduino startup(). Så i Arduino startup() initierar jag bara min första egna FreeRTOS Task.

ESP32, FreeRTOS, framework-arduinoespressif32
ESP32, FreeRTOS, framework-arduinoespressif32
ESP32, FreeRTOS, framework-arduinoespressif32
ESP32, FreeRTOS, framework-arduinoespressif32

Till en början nu så loopar båda runt med låst fixt intervall via vTaskDelayUntil(), med 200ms för LCD­buttons­Handler samt 1000ms för Display­View­Handler, båda på core 0. Display­View­Handler kommer senare göra en loop varje gång den får signal via en sema­phore.
Med så lite kodat ännu som belastar processorkärnorna blir exekverings­intervallen exakt på ms det inställda! Imponerande!
Så när jag skrev ut uppmätt exkeverings­intervall för Display­View­Handler på LCD­displayen så visades bara 1000 hela tiden, så gick inte att se att det exekverade. Fick därför även skriva ut klocktiden hh:mm:ss från RTC (Realidsklockan), vilken jag under startupp synkar mot en NTP tidsserver på Internet.
Skriver även ut på LCDdisplayen antal reset / reboot (Re) ESP32 gjort sedan första start, vilket jag lagrar permanent i flash-minnet via preferences. Påverkas inte av firmware-uppdatering heller. Är för att ha koll på om WDT (watchdog) rebootar systemet någon gång ibland vid 24/7-drift, så jag så ifall kan felsöka det. Är en temporär displayvy.

En skärmdump från PlatformIO serial monitor med (ms)-tidsstämpling, där de två taskens loop-tider skrivs ut samt tid sedan boot (Duration) och klocktiden från RTC. Ska låta den vara igång en hel dag senare för att se hur mycket RTC drar sig i tid:

ESP32, FreeRTOS, framework-arduinoespressif32
PlatformIO (pio) VSCode device serial monitor - with ms-timestamp

Ett sådant här ESP32-processorsystem (SoC) kör en mängd småprogram (tasks) parallellt och oberoende av varandra fördelat på två olika CPU-kärnor med hjälp av realtids­operativ­systemet FreeRTOS. Man kommunicerar mellan de olika tasks med semaforer och dataköer (queue), s.k. Inter-Task Communication som blir en sorts Event-driven funktion. Samt man får vid åtkomst till gemensamma resurser låsa dem via Mutex så bara en task interagerar med en sådan resurs i taget för att inte riskera krockar mellan task kring dem. En annan task som då vill komma åt en låst resurs köar då i väntan på att den släpps fri igen och låser den därefter tillfälligt till sig för sitt processande.
Är väldigt smidigt att kunna dela upp funktionen så i helt fristående kodblock (tasks) som bara gör sitt, som sedan via tydliga gränssnitt kan kommunicera med andra kodblock eller låta sin kodexekvering triggas från andra tasks. Eller exekveras med exakta tidsintervall.
Blir nästan som att ha flera mikroprocessorer som kör vars sitt egna program och sedan kommunicerar med varandra.
Samt ESP32 har två CPU-kärnor där man kan köra kod helt oberoende av varandra också.
Sedan kan externa sensorer signalera via interrupt när de har ny data att läsa av, så gör man det i realtid i koden bara just då så processortid sparas. Blir väldigt effektiv! Som INA226 som arbetar självständigt med datainsamling från strömshunten samt bearbetning av datan och signalerar via interrupt varje gång den har nya aggregerade data ESP32 kan hämta.
2022-07-10
LCDdisplay-handler med menysystem:
Har skapat ett funktionellt ramverk för att hantera olika visningsvyer på LCD-displayen, inkl. ett menysystem med även editerbar data som sparas permanent i ESP32 flash via Preferences.

ESP32, FreeRTOS, framework-arduinoespressif32
ESP32, FreeRTOS, framework-arduinoespressif32, E=edit

Kopierade koden från mitt Clima FanControl projekt men behövde anpassa den en del för att fungera i en FreeRTOS multitasking event-driven kodmiljö, från linjär exekvering.
En sådan sorts event-driven programkod i FreeRTOS gör att koden kan struktureras väldigt bra, samt lätt att även få enskilda task-program­kod­block att exekvera med jämna tidsintervall.
Som jag gör nu att känna av trycknapparna på displaymodulen 1ggr/200ms via en FreeRTOS-task oberoende av övrig kodexekvering, så reaktionen på knapptryckning känns momentan.
Och kan då på knapptryckning posta ett Event via Queue till displayhanterar-task så LCD-displayen uppdateras direkt. Displayen uppdateras annars ca 1ggr/s via interrupt från INA226 strömsensorn när den har nya aggregerade ström- och effekt-data från strömshunten den postar som Event i en Queue till displayhanterar-task för direkt uppdatering på displayen.
Blir bara så smidig effektiv multitaskande kod, där olika task-kodblock exekverar självständigt isolerat från varandra, parallellt mutitaskande med andra task-programkodblock med kontakt enbart via Inter-Task Communication samt globala variabler!
De olika displayvyerna och menysystemet är enkelt skalbart med nya displayvyer och menyer!
E=editmode aktiverad.
Då kan man stega värdet upp/ned med de två mittersta tryckknapparna.
Vänster knapp då = Cancel.
Höger knapp då = spara editerat värde.
Höger knapp aktiverar även in till editmode.
När ej editmode så bläddrar man mellan de olika displayvyerna med de två mittersta knapparna, fram och tillbaka eller runt i en cirkel. Knapptryckning repeteras 1ggr/s konstant nedtryckt.

2022-07-11
ESP32 RealTidsKlockans (RTC) tidsstabilitet:
Har mätt hur mycket den interna RTC (realtidsklockan) drar sig i detta exemplaret av ESP32, och under 12h är det runt 1 sekund (antar då att datorns RTC avviker försumbart på 24h!):
Första tidsangivelsen är tidsstämpling i PlatformIO´s Serial Monitor inkl. (ms), Duration är tid sedan ESP32 bootade upp och synkade tiden mot en tidsserver på Internet samt sista tiden är från ESP32´s interna RTC:
08:24:35.885 > Duration: 00:02:41, RTC Clock: 2022-07-10 08:24:35
15:22:43.993 > Duration: 07:00:49, RTC Clock: 2022-07-10 15:22:43
20:23:02.876 > Duration: 12:01:07, RTC Clock: 2022-07-10 20:23:01
Fortfarande bara 1s skillnad efter 24h mot datorns Internetsynkade RTC, så max 1,5s/24h:
24h: 08:21:55.582 > Duration:24:00:00, Clock:2022-07-11 08:21:54
Verkar vara i klass med en armbandsklocka eller separat RTC, som verkar ligga mellan ±5ppm och ±20ppm i stabilitet (dock då över hela sitt temperaturområde).
Känns helt OK då jag tänkt synka RTC med tidsserver på Internet 1ggr/24h!
Och skulle synkningen missa en vecka pga dålig mobil uppkoppling så är det OK ändå för tidsstämplingen av loggdata.

2022-07-23
INA226 spänning-ström-effekt sensor bibliotek:
Har valt bland 3-4 olika bibliotek som kan installeras direkt inifrån PlatformIO / VSCode för Arduino ESP32, utifrån att tittat igenom dem kod- och funktions-mässigt. Är två viktiga funktioner jag beöver ha i biblioteket förutom basfunktionen:
1. Att kunna använda valfri strömshunt och ange data för den.
2. Att kunna få en Interrupt-signal när ny data finns att hämta.
Jag tänker låta INA226 göra ett antal mätningar själv och medelvärdesbilda dessa, så jag får en alert signal när nya aggregerade data finns att hämta ca 1ggr/s. Sökte även bibliotek med några exempelkodfiler samt lite information om det.
Valet föll på INA226_WE för de första testerna:
GitHub: INA226_WE
INA226_WE.cpp
INA226_WE.h
INA226_WE Wiring HiSide, bild inkoppling
Continuous_With_Resistor_Value.ino, exempel med shunt data
INA226_WE: INA226 Current and Power Sensor, bibliotekets funktion beskriven
TI: Datasheet INA226
TI: INA226

2022-08-03
Wemos D1 R32 Uno board i ESP32 Arduino PlatformIO.ini:
I ESP32 Arduino 2.0.4 based on ESP-IDF 4.4.2 finns nu Wemos D1 R32 Uno board tillagt med mer info hos GitHub: WEMOS D1 R32. Är det jag har i projektet, så känns bra!
Använder därmed nu - board = wemos_d1_uno32 - i platformio.ini (esp32dev tidigare).

2022-09-05
Fail-safe funktion för PWM-regulatorn:
För att driva high-side MOSFET:en som pulsar strömmen till blybatteribanken använder jag en MOSFET-driver typ IR2184, vilken kräver ständigt pulsande för att skapa spänningen till MOSFET-styret. Så om ESP32 hänger sig kan man få till att MOSFET:en stängs av automatisk i brist på pulsande och laddströmmen upphör. Ska även titta på om andra rimliga fail-safe funktioner kan implementeras, allt för att förhindra skadlig överladdning av blybatteribanken vid eventuellt haveri hos PWM-solladdregulatorn. Safety integrity level (SIL) & Determining Safety Integrity Levels (SIL) for Your Process Application kan även vara input.

2022-10-23
Om egenutvecklad solcellselektronik:
Är även andra som utvecklar sin egen elektronik kring solcells-batteri-system så är inte extremt, som i den i Okt 2022 lanserade svenska Clean Motion´s EVIG transportfordon:
"Bolaget har valt att konstruera tre viktiga elek­tronik­en­heter på egen hand. Det handlar om bat­teri­över­vak­nings­sy­stemet (BMS), sol­cells­regu­latorn av typen MPPT och huvuddatorn (vehicle control unit) som bland annat sköter fordonets uppkoppling.
– Vi har lärt oss den hårda vägen att om man inte har kontroll över till exempel bat­teri­över­vak­nings­sy­stemet så blir det besvärligt om vi behöver göra justeringar, till exempel byta cellkemi, säger Göran Folkesson.
" (NyTeknik)


SOC = State Of Charge, laddningsstatus
DOD = Depth Of Discharge, urladdningsdjup
Solcellsström vid svagt ljus är ett viktig tillskott!
Webpage: server time: 66.6 ms, (incl. log: 47.9 ms) ||