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.
Samt med INA226_WE biblioteket kan INA226 kommunicera via en 2:a Wire2 I2C-seriekanal för att slippa krock med Olimex LCD-shieldet.
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
INA226_WE kan kommunicera via ESP32´s 2:a I2C-port introducerat i ver.1.2.3 2021-05-15, vilket är bra när man som här har ett Olimex LCD-shield som är lite klumpig i sin samverkan med andra enheter på samma I2C:
I Wire-biblioteket finns redan två fördefinierade Wire-instanser:
TwoWire Wire = TwoWire(0);
TwoWire Wire1 = TwoWire(1);
Samt via INA226_WE:
INA226_WE ina226 = INA226_WE(&Wire1, I2C_ADDRESS);
- ESP32 Using Two I2C Bus Interfaces
- Simultaneously use the two I2C bus of the ESP32
- Espressif: ESP32 I2C Driver
- ESP32 and multiple I2C buses
- ESP32 I2C Tutorial: Set Pins, Multi Devices, I2C Scanner (Arduino IDE)
- ESP32 Pinout Reference: Which GPIO pins should you use?
- ESP32- Interfacing Multiple device using I2C BUS

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)

2022-12-16
Batterimonitorn utvecklas just nu:
För närvarande utvecklar jag batterimonitor funktionen i PWM-regulatorn.
När den är i drift med verifierad funktion går jag vidare till PWM-regulator delen här.

2022-12-17
Task Serial.print:
Blev tipsad av en som jobbat mycket med embedded systems utveckling om att koda en separat Task som tar hand om all Serial.print då det är en rätt långsam rutin som skriver synkront på serieporten. För att undvika de väntetider / waits där i koden då det annars görs direkt i den. Låter väldigt vettigt och något jag nappat på att testa!
Stämmer mer med hur jag tänkt bygga upp allt med enbart FreeRTOS Tasks specialiserade på olika entydiga uppgifter. Där en separat Task för den långsamma Serial.print är väl motiverat för att snabba på övrig kodexekvering, samt för att hålla all Serial.print i Core 0!
Hans kodtankar var kring en ringbuffert som alla andra Tasks kan skriva till och String.print-Task skriver från. Utifrån min nybörjarkunskap i FreeRTOS så liknande det att använda en FreeRTOS Queue till den Task som enbart hanterar Serial.print, som då även ges en låg Task-prioritet så processorkraft prioriteras till högre Tasks när det behövs. Så testar med Queue.
Innebär även att den Serial.print Task då bara exekverar så länge det finns meddelande att hämta ut från kön och skicka till Serial.print, så finns inga meddelande att hämta från kön så slösar inte Serial.print Task någon processorkraft på att exekvera!
Lite snabbt sökta resurser till detta:
Kan tydligen för textmeddelande göras antingen med en kö av char pointers eller kö av en struct av char/uint8_t-array.
FreeRTOS: Queue Management API
ESP32 Arduino: Using structs as items in FreeRTOS queues, int+int+float in a struct example
How to use Arduino FreeRTOS structure Queue to Receive Data from Multiple Tasks, int+int struct
FreeRTOS: Send a struct through Queue
Using Queue of string in FreeRTOS, send a string data in the queue using a struct
Espressif FreeRTOS Supplemental Features - Ring Buffers, med kodexempel
Send an struct array across a FREERTOS queue ESP32
Receive char array in Queue from UART ISR
FreeRTOS Arduino: How to use Queue Set
Send a struct through Queue
Arduino FreeRTOS Queue Management – How to use Queues
FreeRTOS Tutorial #5 ->Using Queue
Arduino FreeRTOS Message Queue Management med bra detaljer
FreeRTOS: Stream Buffers vs Message Buffers vs Queues

2022-12-25
Fler som ser bristande kunskap om solcellsdrift i Nordiska förhållanden:
Så jag är inte ensam eller unik med den åsikten.
"Halvceller tar över solcellsmarknaden – nu mäts effektvinsten:
Hur det ser ut under nordiska förhållanden är något som forskarna vid IFE [Institutt for energiteknikk] försöker ta reda på vid den nya testanläggningen utanför Oslo.
– Det har gjorts många tester och studier av solceller, men inte så många i Norden. Vi vill öka kunskapen om solceller i vårt klimat och med våra solförhållanden, säger Marie Syre Wiig, forskare vid avdelningen för solkraftssystem på IFE.
" NyTeknik
2023-04-03
Ska nog bara ha egen kod i Core 1 då man dels annars drabbas av komplexiteten av "ESP32 inter-processor communication" vad det gäller access till globala variabler med konflikt om man inte skapar exklusiv access via Semaphore-Mutex, dels kör Espressif all systemfunktion med WiFi på Core 0 som man riskerar att störa.

2023-09-25
Håller på att ta mitt nya UPLUS 90Ah Lead-Carbon (Bly-Kol) USDC12-90 (köpt från Solenergibutiken) i drift med att ladda det 100% SoC fullt inkl. balansera cellerna med längre float-laddning via mitt 5A Labaggregat, och passar då på som utvärdering att emulera de Nordiska laddcykler för blybatteri jag tänker använda.
Att avbryta AGM-blykols 14,2V absorptions-laddning vid <0,015C / <1,5% Tail-current blev jättebra. Float-laddning vid 13,8V justerar jag till att avbrytas vid >10hr && <0,005C / <0,5% Tail-current som kombinerat villkor, vilket ser riktigt bra ut (blev ca 10-12h här nu första laddningen ihop med Battery Reconditioner). Den fortsatta Standby-laddningen vid 13,4V stabiliserades efter många timmar vid ca 0,001C / 0,1% Tail-current, vilket jag tolkar som att den fortsätter att balansera battericellerna långsamt om det behövs men i övrigt ej överladdar blykol-batterierna och därmed undviker uttorkning, så känns som en bra standby-nivå även för AGM-blykol.

2023-12-31
Har uppdaterat kod-blockschemat för batterimonitor-delen med den senaste kodutvecklingen:

Code flow-schart
ESP32 kod blockschema - dokumentation 2023-12-31


SOC = State Of Charge, laddningsstatus
DOD = Depth Of Discharge, urladdningsdjup

Länkar:

Solcellsström vid svagt ljus är ett viktig tillskott!
Webpage: server time: 88.1 ms, (incl. log: 84.4 ms) ||