Minulla
on käytössä kesämökillä aurinkopaneeleita. Yhtä paneelia
valvoo Arduinolla toteutettu laite, mikä katkaisee akun latauksen,
kun jännite nousee 14,5 voltiin. Periaatteessa aivan oikein, mutta
valvoja sijaisee lähellä paneelia, jolloin se ei ota huomioon
syöttöjohdossa syntyvää jännitehäviötä. Toisin sanoen, mitä
paremmin aurinko paistaa, niin sitä suurempi on latausvirta, ja sitä
aikaisemmin valvoja katkaisee latauksen akulta. Näin ei pitäisi
olla.
Varsinkin
silloin, jos käytössä on seurantalaite, eli paneeli seuraa
aurinkon kulkua, ja valvonta on lähellä paneelia, on tärkeätä,
että tuo mainittu jännitehäviö otetaan huomioon. On mahdollista
vetää toiset johdot pelkästään jännitteen mittausta varten
suoraan akun navoista, mutta siihen en halunnut ryhtyä. Toinen tapa
on mitata virta juuri ennen katkaisua, tallettaa sen arvo ja
välittömästi sen jälkeen mitata jännitepudotus. Välittömästi
siksi, että akun napajännite alkaa pudota nopeasti latauksen
loputtua. Ohmin laki U = I * R (jännite on virran ja vastuksen tulo)
tarjoaa tarpeellisen evään ongelman ratkaisuun. Mainituilla
mittauksilla voidaan johtojen vastus laskea R = U / I (jännitepudotus
jaetaan talletetulla virralla, jolloin saadaan esille johtojen
vastus). Jatkossa mitataan latausvirtaa ja lisätään virran ja
lasketun vastuksen tulo tuohon 14,5 voltin katkaisujännitteeseen
Näin yksinkertaista se on!
Tuo oli
luonnollisesti ajatus ja käsitys ennen kuin aloin toteuttaa po.
valvontalaitetta. Miten mitata? Arduinon analogiatulot ovat 0 .. 5VDC
ja 10 bitin muunnos (lukulue 0 .. 1023). Toisin sanoen uusi lukema
saadaan 5mV:in välein (5000mV / 1024 = 4,88mV). Latausjännite ei
kuitenkaan suoraan käy analogiamittaukseen, koska se on paljon
suurempi kuin 5V. Kytkennässä olevilla arvoilla (5,6kohm ja
17,7kohm) koko mitta-alueeksi tulee 20.8V. Toisin sanoen uusi
jännitteen lukema saadaankin tällä kytkennällä noin 20mV välein.
Sekin on hyvä ja aivan riittävä tarkkuus tähän tarkoitukseen.
Virran
mittaukseen ajattelin ensin shunttivastusta. Toisin sanoen
pieniohmista tehovastusta, joka kestää latausvirran vioittumatta.
Tässä tapauksessa 0,125 ohmia (sellaisia oli laatikossa). Se ei voi
olla kovin suuri, sillä latausvirran pitää päätyä akkuun eikä
lämmittää tätä mittausvastusta. 5 ampeerin virralla (noin 100W
paneeli) tuon mittausvastuksen yli vaikuttaa siten 5A * 0,125ohm =
0,625V. Jos shuttivastus sijoittetaan plus-johtoon, vaikuttaa siihen
myös latauslinjan jännite. Tällöin shunttivastuksen molemmista
päistä voidaan tehdä analogiamittaus ja muuntaa näin saatu
jännite-ero latausvirraksi. Koska yli 5voltin jännitettä ei voida
kytkeä suoraan analogiamittaukseen, tarvitaan ym.
jännitteenjakovastukset. Tuolla järjestelyllä pääsisi melkein 3%
resoluutioon, mutta hylkäsin tämänkin, sillä kuvittelin pääseväni
parempaan tarkkuuteen sijoittamalla shuntin miinusjohtoon, jolloin
tuo 0,625V voidaan kytkeä suoraan analogiatuloon. Tällöin
resoluutio olisi hiukan prosenttia parempi. Näin kuvittelin.
Ratkaisu ei toiminut, vaikka sama toimii kuvassa taustalla näkyvässä
vanhassa teholähteessä, minkä alkuperäiset viisarimittarit
lopettivat toimintansa. Se oli sopiva sovelluskohde Arduinolle.
Ilmeisesti (?) ongelma johtui siitä, että miinus ja maapotentiaali
eivät olleet enää sama piste.
|
Hall-generaattoreita "edestä ja takaa". Paksut johdot ovat päävirtaa varten ja ohuet signaalia varten. |
|
Tasavirtaa
voidaan mitata myös Hall-generaattorilla. Se on
puolijohdekomponentti, mikä pystyy muuttamaan magneettikentän
voimakkuuden analogiajännitteeksi. Pienin löytämäni
Hall-generaattori on tyyppiä ACS758LCB-050B ja se on maksimissaa
50A:in virralle. Siis tarpeettoman suuri tähän käyttöön, mutta
sillä lähdin eteenpäin. Sen herkkyys on 40mV/A. Jos ajatellaan
tätä 5A:in mittausaluetta, on komponentista saatava jännite 200mV
(0,2V). Tämä jännite voidaan kytkeä suoraan analogiatuloon, joten
muunnosportaita syntyy noin 40kpl ja tarkkuus jää hiukan alle 2%.
Tähän tarkoitukseen tämäkin tarkkuus on aivan riittävä.
Hall-generaattorin lähtö (siis 0-virralla) ei ole kuitenkaan nolla,
sillä maan ja muu ympäröivä magneettikenttä aiheuttaa
pohjalukeman. Tämä hallitaan siten, että nollavirralla talletetaan
Hall-generaattorin lukema, ja tämä luku vähennetään
varsinaisesta mittausarvosta.
Tuo oli
teoriaosuus. Käytäntö ei ollut lainkaan yksinkertaista ja
suoraviivaista. Ison virheen tein ensinnäkin siinä, että
ensimmäiseen versioon juotin johdot Hall-generaattorin
virtapuolelle. Se on aika paksua kuparia ja kupari johtaa hyvin
lämpöä, joten jotain vioittui puolijohdepiirissä. Lukemat olivat
stabiileita ja järkeviä, niin kauan kun Arduinon syöttö tapahtui
USB-liittimen kautta tietokoneesta, mutta heti syötön siirryttyä
akulle, lukemat heittelivät miten sattui. Syöttöjännitteessä oli
pientä häiriötä, mutta se ei edellyttänyt moisia heittoja. Vasta
viimeiseksi epäilin Hall-generaattoria, joten teettikin paljon
töitä, kun olin eliminoinut muut kuvittelemani virhelähteet pois.
Uudella generaattorilla, mihin kytkin virtajohdot ruuviliitoksin,
mitään tuollaista epämääräisyyttä ei ilmennyt. Johtuen yllä
mainitusta, on tämän ohjelman versionumero 2.4. Aina kun pääsin
testeissäni johonkin välitavoitteeseen, otin uuden versionumeron,
jolloin helposti pääsi palaamaan takaisin edelliseen toimivaan
toteutukseen ja kopioimaan niistä toimivia osuuksia. (Ei noita
versioita kuitenkaan 24 tullut, vaan isomman ponnistuksen jälkeen
siirryin uudelle kymmenluvulle.)
|
Oikealla oleva vastussarja simuloi johtovastusta |
Se oli
vain yksi virheläde. Kovin stabiilia mittausta tästä ei kuitenkaan
ole mahdollista saada. Siksi ohjelmassa käytetään jossakin
mittauksessa keskiarvolaskentaa (mitataan useita peräkkäisiä
mittauksia ja lasketaan niiden keskiarvo) sekä suodatusta
(talletetaan edellinen mitausarvo, otetaan siitä 90% ja uudesta
mittauksesta 10%). Yksi melkoinen hankaluus on jännitteen mittaus
juuri latauksen katkon jälkeen. Ohjelmassa olevat arvot ovat
pitkälti tulosta kokeilusta ja järkevään lopputulokseen
pyrkimisestä. Voin sanoa, että toiminta ei ole kovinkaan tarkkaa,
mutta oikean suuntaista. Ensimmäisellä kerralla katkaisu tapahtuu
14,5 voltin arvolla ja seuraavilla kerroilla siihen lisätään saatu
johtojen häviöjännite, mikä on varmuuden vuoksi rajoitettu alle
yhden voltin, jottei akun latausjännite pääse kasvamaan liian
suureksi. Sekä liian suuri latausvirta että liian suuri
latausjännite pilaavat akun ennen pitkää.
|
Latausvalvojan kytkentä. Molemmat LEDit ovat samassa kuoressa. |
OHJELMASTA
HUOM!
Ilmeisesti työkaluohjelman (Arduino) uusimmassa versiossa on
tapahtunut sellainen muutos, että aliohjelmat eivät voi enää
sijaita pääohjelman perässä (mihin oli tottunut). Jos
sijaitsevat, ilmoittaa kääntäjä virheestä (aliohjelmaa ei ole
määritelty). Tässä ohjelmassa aliohjelmat (funktiot) on
sijoitettu asetuksen (setup()) ja pääohjelman väliin. Tämä
merkitsee myös sitä, että aiemmin täällä julkistamani
ohjelmaesimerkit eivät toimi sellaisenaan, vaan funktiot on
siirrettävä pääohjelman eteen.
Asetuksen
lopussa talletetaan ensimmäisen kerran Hall-generaattorin
pohjalukema. Koska tämä osio suoritetaan ainoastaan kerran, olen
käyttänyt yksinkertaista viivettä (delay(ms)).
Analogiamittaukset
(jännite ja virta) sekä kaksivärisen LEDin (punainen ja vihreä)
sekä latausreleen ohjaus toteutetaan funktioina. Analogiapuolen
aliohjelmat palauttavat liukuluvun. Siksi niiden tyyppi on float
(eikä void).
Pääohjelmassa
on ensimmäisenä kierroslaskuri, millä simuloidaan viiveaikoja.
Ajan tarkkuus ei kuitenkaan ole kovin tähdellistä. Latauskatkon
jälkeinen tauko on noin 5 minuuttia. Muuttujat ovat tyyppiä
etumerkitön kokonaisluku (unsigned int), joten niistä ei koskaan
tule negatiivisiä lukuja. Jos lukualue ( 0 .. 65536) ylittyy, alkaa
uusi laskenta nollasta. Aikalukema on siis aina positiivinen luku.
Seuraavana
latauksen pysäytys- ja käynnistyskytkimien käsittely (kytkin on
kolmiasentoinen). Pysäytyskytkin pysyy asennossaan, joten lataus
voidaan kytkeä tarvittaessa pois päältä. Käynnistyskytkin on
palautuva ja lataus alkaa vivun palautuessa keskiasentoon.
Pysäytyskytkimen (kuvassa irti) toiminnan merkkinä punainen LED
palaa.
Seuraavana
on varsinainen latauksen valvontasekvenssi. Ensimmäisessä
askeleessa aurinkopaneeli on kytketty akkuun ja lataus käynnistyy.
Ensimmäisellä kerralla lataus katkaistaan 14,5 voltin arvolla ja
seuraavilla kerroilla tuohon lisätään jännite, mikä saadaan
kertomalla sen hetkinen latausvirta lasketulla syöttöjohtojen
vastusarvolla. Tässä askeleessa vihreä LED palaa.
Toisessa
askeleessa mitataan lyhyen viiveen (delay(3)) jälkeen välitön
jännitepudotus latauksen loputtua. Tässä kerätään viisi
peräkkäistä mittausta ja lasketaan niiden keskiarvo. Tämä on
jännite, mikä latauksen aikana hävisi johtoihin.
Kolmannessa
askeleessa lasketaan johtojen vastus (plus- ja miinus-johto
yhteensä).
Neljännessä
askeleessa odotetaan noin viisi minuuttia ennen uutta latausta.
Merkkinä askeleesta on punainen vilkku. Punainen LED vaihtaa
tilaansa, jos kierrosten lukumäärä (const int Con_Kierrokset =
800;) on saavutettu, tai on saavutettu puolet siitä. Vilkutusaika on
näin ollen 0,5s päällä ja 0,5s pois.
Viidennessä
askeleessa odotetaan, että akun jännite on laskenut (14,5V –
1,0V) sille tasolle, että uusi lataus voidaan aloittaa. Merkkinä
tästä askeleesta on vihreä vilkku. Jos samoihin akkuihin on
kytketty useampia paneeleita, voi olla, että ne ylläpitävät akun
jännitettä tuon rajan yläpuolella. Lopuksi askeleessa päivitettään
Hall-generaattorin pohja-arvo ja hypätään askeleeseen yksi uutta
latauskierrosta varten.
/***************************************
*
PaneeliValvonta_v24
*
Antti Isännäinen
*
09.11.2016
*
Valvonta käsi (haarukka) kääntimeen
**************************************/
/*
Tähtäimenä paneelivalvoja, mikä ottaa huomioon siirtojohtojen
*
jännitehäviön. Käynnistyksen jälkeen lataus loppuu jännite-
*
arvolla 14,5V. Seuraavilla kerroilla mitataan jännitepudotus
*
latauksen loppuessa ja lasketaan siirtojohtojen lenkkivastus.
*
Seuraavilla kerroilla rajajännitteeeseen lisätään (14,5 + häviö)
*
siirtojohtoihin häviävä, (virta * vastus = jännite) jännite.
*
*
Versiossa 2.1 Jouduin jälleen lähtöruutuun, sillä
jännitepudotuksen
*
mittaus latauksen katkon jälkeen ei edellisessä versiossa (1.2)
*
ollu riittävän luotettava.
*
*
Versiossa 2.2 (v22) on pohjana melko luotettava latauskatkon
*
jälkeinen jännitemittaus ja suodatus. Lyhyen viiveen (10 ms)
*
muodostetaan viiden (5) mittauksen keskiarvo.
*
Tässä versiossa on myös virran mittaus riittävän luotettava.
*
*
Versiossa 2.3 (v23) jatkuu varsinaisen paneelivalvojan
*
kehitys. Pohjana versio 1.2 (v12).
*
*
Versiossa 2.4 (v24) on jännittteiden, virtojen, johtovastuksen
*
ja näistä lasketun lisäjännitteen (katkaisupiste) arvot
toteutuneet
*
käytännössä riittävällä tarkuudella.
*/
//
MÄÄRITTELYT:
//
Ajastukset
const
int Con_Kierrokset = 800;
unsigned
int Unt_Sekunnit = 0; // Viivelaskuri
unsigned
int Unt_Kierrokset = 0; // Ohjelmakierroslaskuri
int Int_Kierrokset = 0;
//
Jännite
const
int Con_AnaTuloV = 0; // Tulopinni A0
int Int_AnaRaakaV = 0; // Muunnosarvo
float
Flo_Jannite = 0; // Jännitteen mittaus
float
Flo_EdellJannite = 0;
float
Flo_JannSuod = 0;
float
Flo_JannMuisti = 0;
float
Flo_JohtoSumma = 0;
//
Virta
const
int Con_AnaTuloI = 2; // Tulopinni A2
int Int_AnaRaakaI = 0; // Muunnosarvo
int Int_PohjaArvo = 0; // Hall-gen. pohjalukema luetaan
asetuksissa
float
Flo_Virta = 0;
float
Flo_VirtaSuod = 0;
float
Flo_EdellVirta = 0;
float
Flo_VirtaMuisti = 0;
//
Digitaalimäärittelyt
//
Tulot
const
int Con_Seis = 4; // Kytkin pysyvästi ylös
boolean
Bol_Seis = false;
int Int_Seis_Viive = 5; // Kytkinvärähtelyn suodatus
int Seq_Seis = 1; // Kytkimen lukusekvenssi
const
int Con_Start = 5; // Kytkin alas palautuva
boolean
Bol_Start = false;
int Int_Start_Viive = 5;
int Seq_Start = 1;
//
Lähdöt
const
int Con_LEDpun = 8;
boolean
Bol_LED_Pun = false;
const
int Con_LEDvih = 9;
boolean
Bol_LED_Vih = false;
const
int Con_Rele = 10;
boolean
Bol_Rele = false;
//
Valvontasekvenssi
const
float Con_Irti_Raja = 14.5; // Akun napojen rajajännite
float
Flo_JohtoJannite = 0.0;
float
Flo_SilmukkaVastus = 0.0;
int Seq_Valvonta = 0; // Valvontasekvenssi
void
setup() {
Serial.begin(9600);
pinMode(Con_Seis, INPUT);
pinMode(Con_Start, INPUT);
pinMode(Con_LEDpun, OUTPUT);
pinMode(Con_LEDvih, OUTPUT);
pinMode(Con_Rele, OUTPUT);
digitalWrite(Con_Rele, true);
delay(300); // Irroitetaan lataus
Int_PohjaArvo =
analogRead(Con_AnaTuloI); // Alustetaan Hall-pohja-arvo
digitalWrite(Con_Rele, false);
delay(100); // kytketään lataus
}//
Asetusten loppu
//
ALIOHJELMAT
//
ANALOGIA ALIOHJELMAT
//
Jännitteen käsittely
float
Fun_MittaaV(){
Int_AnaRaakaV =
analogRead(Con_AnaTuloV);
return
Int_AnaRaakaV / 50.6;
}//
Jännitteen mittaus loppu
//
Virran käsittely
float
Fun_MittaaI(){
Int_AnaRaakaI = analogRead(Con_AnaTuloI);
Int_AnaRaakaI = Int_AnaRaakaI -
Int_PohjaArvo;
Int_AnaRaakaI =
constrain(Int_AnaRaakaI, 0, 100);
return
Int_AnaRaakaI / 8.2;
}//
Virran mittaus loppu
//
OHJAUSTEN ALIOHJELMAT
void
Fun_LEDvih(boolean Tila){
digitalWrite(Con_LEDvih, Tila);
}//
Vihreän LEDin ohjauksen loppu
void
Fun_LEDpun(boolean Tila){
digitalWrite(Con_LEDpun, Tila);
}//
Punaisen LEDin ohjauksen loppu
void
Fun_Rele(boolean Tila){
digitalWrite(Con_Rele, Tila);
}//
Irroitusreleen ohjauksen loppu
//PÄÄOHJELMA
void
loop() {
//
Ohjelmakierros / viivelaskuri
Unt_Kierrokset++;
if(Unt_Kierrokset >
Con_Kierrokset){ // Luvulla sovitetaan kierr -> sek
Unt_Sekunnit++;
Unt_Kierrokset = 0;
}
// Kierroslakuri loppu
//
Kytkintietojen käsittely
//
Seis-kytkin
Bol_Seis
= digitalRead(Con_Seis); // Luetaan kytkimen tila
switch
(Seq_Seis) {
case
1:
if(Bol_Seis == true){
Int_Seis_Viive --;
if(Int_Seis_Viive == 0){
Int_Seis_Viive = 5;
Seq_Valvonta = 0;
Fun_LEDvih(false);
Seq_Seis = 2;
}
}
break;
case
2:
Fun_LEDpun(true);
Fun_Rele(true);
if(Bol_Seis == false){
Fun_LEDpun(false);
Seq_Seis = 1;
}
break;
}
// Seis kytkinsekvenssi loppu
//
Start-kykin
Bol_Start
= digitalRead(Con_Start); // Luetaan kytkimen tila
switch
(Seq_Start) {
case
1:
if(Bol_Start == true){
Int_Start_Viive --;
if(Int_Start_Viive == 0){
Int_Start_Viive = 5;
Seq_Start = 2;
}
}
break;
case
2:
if(Bol_Start == false){
Int_PohjaArvo =
analogRead(Con_AnaTuloI);
Seq_Valvonta = 1;
Seq_Start = 1;
}
break;
}
// Start kytkinsekvenssi loppu
//
Akun latauksen valvontasekvenssi
switch
(Seq_Valvonta) {
case
1: // Latausaskel
Fun_LEDvih(true);
Fun_Rele(false); // rele päästää,
lataus alkaa
Flo_Jannite
= Fun_MittaaV();
Flo_JannSuod = Flo_EdellJannite * 0.9
+ Flo_Jannite * 0.1;
Flo_EdellJannite = Flo_JannSuod;
Flo_Virta
= Fun_MittaaI();
Flo_VirtaSuod = Flo_EdellVirta * 0.9 +
Flo_Virta *0.1;
Flo_EdellVirta = Flo_VirtaSuod;
if(Flo_JannSuod > Con_Irti_Raja +
Flo_SilmukkaVastus * Flo_VirtaSuod){
Flo_JannMuisti = Flo_JannSuod;
Flo_VirtaMuisti = Flo_EdellVirta;
Fun_Rele(true); // rele vetää,
lataus katkaistaan
Unt_Sekunnit = 0; // Nollataan
"sekunnit"
Fun_LEDvih(false);
Seq_Valvonta = 2;
}
break;
case
2: // Jännitepudotuksen mittaus
delay(3);
// 3 ms stabilointiviive
for
(int iV = 0; iV < 5; iV++){ // Mitataan 5 peräkkäistä arvoa
Flo_Jannite = Fun_MittaaV();
Flo_JohtoSumma = Flo_JohtoSumma +
Flo_Jannite;
}
// for-silmukan loppu
Flo_Jannite = Flo_JohtoSumma / 5; //
Muodostetaan viiden keskiarvo
Flo_JohtoSumma = 0; // Nollataan
muisti seuraavaa kierrosta varten
Flo_JohtoJannite = Flo_JannMuisti -
Flo_Jannite;
Flo_JohtoJannite =
constrain(Flo_JohtoJannite, 0.0, 1.0);
Seq_Valvonta = 3;
break;
case
3: // Johtovastuksen laskenta
Flo_SilmukkaVastus = Flo_JohtoJannite
/ Flo_VirtaMuisti;
Seq_Valvonta = 4;
break;
case
4: // Purkausviive
if(Unt_Kierrokset == Con_Kierrokset ||
Unt_Kierrokset == Con_Kierrokset / 2){
Bol_LED_Pun
= !Bol_LED_Pun;}
Fun_LEDpun(Bol_LED_Pun);
if(Unt_Sekunnit > 300){
Fun_LEDpun(false);
Seq_Valvonta = 5;
}
break;
case
5: // Latauksen palautus
if(Unt_Kierrokset == Con_Kierrokset ||
Unt_Kierrokset == Con_Kierrokset / 2){
Bol_LED_Vih
= !Bol_LED_Vih;}
Fun_LEDvih(Bol_LED_Vih);
Flo_Jannite
= Fun_MittaaV();
Flo_JannSuod = Flo_EdellJannite * 0.9
+ Flo_Jannite * 0.1;
Flo_EdellJannite = Flo_JannSuod;
if(Flo_JannSuod < Con_Irti_Raja -
1.0){
Int_PohjaArvo =
analogRead(Con_AnaTuloI); // Alustetaan Hall-pohja-arvo
Fun_LEDvih(false);
Seq_Valvonta = 1;}
break;
}
// Valvontasekvenssi loppu
delay(1);
}
// Pääohjelman loppu