Stap 5: ZONNEPANEEL toezicht – ARDUINO SKETCH
Het schrijven van een programma voor de Arduino was een nieuwe ervaring voor mij. Verwend door Windows/Linux en zelfs Android hebben in principe geen geheugenlimiet die ik lopen zeer binnenkort in problemen met de zeer beperkte code en variabele ruimte op de Arduino boards Arduino boards. De Arduino Yun biedt slechts 28,627 bytes voor code en 2560 bytes voor globale variabelen.
Wat ik leerde snel was die ik om het geheugen van de code omhoog gebruiken kan naar 99,9%, maar als ik gebruik meer dan 60% van het geheugen voor globale variabelen de schets was niet langer werken betrouwbaar. Ik denk dat de onderliggende code (bootloader,...) sommige vrije ruimte int globale variabelen geheugen vereist. Dus ik moest mijn code optimaliseren en gebruik van wereldwijde variabelen zo veel mogelijk te beperken. Voor de code zelf splitsen ik de Arduino schets in meerdere bestanden. Ik heb geprobeerd om commentaar van de code zo goed als mogelijk, dus het moet zichzelf.
spMonitor.ino = dummy file with description of the other files<br>spMonitor_a_definitions => includes and global variables spMonitor_b_utils => utility functions spMonitor_c_lightsensor => interface to TSL2561 light sensor spMonitor_x_measurement => measurement functions spMonitor_y_setup => Arduino setup function spMonitor_z_loop => Arduino loop function
De namen van de bestanden volgen een specifieke regeling. De compiler voor de schets vereist dat sub functies gebruikt moeten worden gedeclareerd voordat ze worden gebruikt. De bestandsnamen zijn dus SKETCHNAME_index_MODUL. Met de index krijg ik de bestanden in de juiste volgorde worden opgesteld.
SPMONITOR_A_DEFINITIONS
In het eerste deel zijn alle externe bestanden die nodig zijn voor de verschillende functies opgenomen.
Wire.h => i2c communication library<br>Adafruit_Sensor.h, Adafruit_TSL2561_U.h and pgmspace.h for the light sensor EmonLib.h for the CT and voltage sensors FileIO.h to access the SD card of the Linux part of the board YunServer.h and YunClient.h for the web communication avr/wdt.h for the watchdog implementation
Kalibratie waarden:
/** iCal definition for CT 1 (solar) */<br>#define iCal1 5.7 /** iCal definition for CT 2 (mains) */ #define iCal2 11.5 /** Phase shift definition for voltage (solar) */ #define pShift1 1.3 /** Phase shift definition for voltage (mains) */ #define pShift2 6.1 /** vCal definition for voltage measurement */ #define vCal 255
Deze waarden zijn cruciaal voor de metingen. Deze waarden zijn afhankelijk van de CT-sensoren gebruikt, toleranties van de gebruikte weerstanden en condensatoren en andere dingen. Als u deze waarden correct, stapte ik meerdere malen door de gids van de kalibratie van de OpenEnergyMonitor bouwsteen Kalibratieprocedure. Zolang heb je de juiste waarden hier zullen alle uw metingen min of meer CRAP! De kalibratie om mogelijk te maken zonder het schrijven van een speciale script ik een "kalibratie-modus" opgenomen in de Android applicatie. Wanneer de kalibratie-modus is geactiveerd wordt het Android-applicaties gegevens ophaalt uit de Arduino elke 10 seconden en geeft details over de arbeidsfactor, echte en schijnbare macht en gemeten voltage. De kalibratie-waarden kunnen vervolgens worden gewijzigd totdat de waarden zijn zoals verwacht (bijvoorbeeld arbeidsfactor dicht bij 1.o).
De andere globale variabelen zijn goed commentaar.
SPMONITOR_B_UTILS
Deze module bevat 2 functies die worden gebruikt voor het opslaan van de geregistreerde waarden.
String getTimeStamp() geeft als resultaat de huidige datum en tijd van het systeem. Om het te krijgen gebruiken we de "" brugfunctie ".punt" voert u een opdracht aan de kant van Linux te ontvangen van de uitvoer via de "brug".
/** Instance to Linino process */<br>Process time;
/* date is a command line utility to get the date and the time */ /* in different formats depending on the additional parameter */ time.begin ( "date" ); time.addParameter ( "+%y,%m,%d,%H:%M" ); wdt_reset(); time.run(); /* run the command */
De mogelijkheid om te beginnen met opdrachten en scripts op een Linux-shell breidt de mogelijkheden van de Arduino board. In plaats van de problemen toe te voegen van complexe functies in de ruimte beperkt programma van de AVR-controller kunnen ze worden overgedragen aan het Linux-deel.
ongeldig saveData () gebruikt ook "brug" functies schrijven van de verzamelde gegevens in een database, het verzenden van een wolk-webservice en opslaan voor gemakkelijke toegang aan de kant van de Linux.
De metingen zijn verricht elke seconde, maar de waarden worden opgeslagen met alleen elke minuut. De gemeten waarden worden opgeslagen in een buffertank. Het deel van de code
/** Light value collected since last saving */long light = 0; if ( collCount[2] != 0 ) { light = collLight / collCount[2]; } /** Solar power value collected since last saving */ double solar = collPower[0] / collCount[0]; /** Consumed power value collected since last saving */ double cons = collPower[1] / collCount[1];
Berekent het gemiddelde van de metingen gedaan sinds de laatste besparing van waarden.
Het deel van de code
Bridge.put ( "L", String ( light ) );Bridge.put ( "S", String ( solar ) ); Bridge.put ( "C", String ( cons ) );
opslag van de meetwaarden op de processor van de Linux met behulp van een sleutel/waarde-structuur. Deze structuur kan worden opgehaald als een JSON-string door eenvoudige roepen "http://IPADDRESS//data/get". (IP-adres is het IP-adres toegewezen aan de Arduino Yun). De gegevens eruit zien:
{ "value": { "c":"2.56", "L":"0", "sv":"219.43", "cv":"215.85", "cp":"0.97", "sa":"100.44", "ca":"553.57", "cr":"539.18", "C":"596.05", "s":"0.46", "S":"0.00", "sp":"0.20", "l":"0", "sr":"0.00" }, "response":"get" }
Hier ziet u de gegevens die wij met het bovenstaande gedeelte van de code in deze JSON-string.
De L, S en C waarde wordt elke minuut bijgewerkt en gebruikt voor het bijwerken van de UI in de Android app. De c, cv, cr... waarden worden bijgewerkt na elke meting cyclus (~ 1s) en kunnen worden gebruikt tijdens de kalibratie-modus. Ze worden ook weergegeven op de Gebruikersinterface van de Android app. Het volgende deel van de code
/** Instance to Linino process */Process sqLite; timeString = getTimeStamp(); timeString.replace(',', '-'); dataString = "sqlite3 /mnt/sda1/s.db 'insert into s (d,s,c,l) Values (\"" + timeString + "\"," + String ( solar ) + "," + String ( cons ) + "," + String ( light ) + ");'";
sqLite.runShellCommand ( dataString );
de functie "brug" ".runShellCommand" te schrijven van de gemeten waarden in de Sqlite database. Daar kunnen ze worden opgehaald via het lokale netwerk met het script query.php.
En het laatste deel van de code
/** Instance to Linino process */Process emonCMS; dataString = "curl \"http://emoncms.org/api/post?apikey=ENTER_YOUR_OWN_API_KEY_HERE&json={s:"; dataString += String ( solar ); dataString += ",c:"; dataString += String ( cons ); dataString += ",l:"; dataString += String ( light ); dataString += "}\""; emonCMS.runShellCommand ( dataString );
gebruikt ook de brugfunctie van de "" ".runShellCommand" de gegevens te verzenden naar een webservice wolk. Deze wolk web service emoncms.org biedt (momenteel gratis) de mogelijkheid om sensorgegevens opslaan en ze te visualiseren op het web. Controleren van hun service, ze worden aangedreven door OpenEnergyMonitor!
Voegde ik een van de Visualisatieopties in de bovenstaande foto's. Hier ziet u de gegevens van de reële consumptie/productie van mijn huis in Mijn zonne- app van emoncms.org.
SPMONITOR_C_LIGHTSENSOR
Deze module behandelt de lichtsensor. De communicatie met de lichtsensor is gedaan binnen de bibliotheken Adafruit_Sensor en Adafruit_TSL2561_U. Deze bibliotheken zijn beschikbaar als open source uit Adafruit, de fabrikant van de lichtsensor-module.
void configureSensor () initieert de autofunctie bereik van de lichtsensor en stelt deze integratie tijd op de hoogste waarde.
void readLux () initieert een meting op de lichtsensor-module. Het is aangepast aan verschillende lichte intensiteiten nodig zijn om de tijd van de integratie van de sensor. De sensor is in eerste instantie ingesteld op de hoogste tijd van integratie die de beste in situaties met weinig licht is. Maar als het licht helderder wordt, de sensor zal verzadigen en het is noodzakelijk om te schakelen naar een korter van integratie. De sensor biedt 3 verschillende integratie keer. Het deel van de code
/* Display the results (light is measured in lux) */if ( event.light ) { /** Int value read from AD conv for sun measurement */ accLux += event.light; lightOk++; /* Increase counter of successful measurements */ if ( lightInteg == 1 ) { /* we are at medium integration time, try a higher one */ tsl.setIntegrationTime ( TSL2561_INTEGRATIONTIME_402MS ); /* 16-bit data but slowest conversions */ lightInteg = 2; } else if ( lightInteg == 0 ) { /* we are at lowest integration time, try a higher one */ tsl.setIntegrationTime ( TSL2561_INTEGRATIONTIME_101MS ); /* medium resolution and speed */ lightInteg = 1; } }
leest een waarde van de sensor. Als de sensor een waarde levert, wordt de code schakelt over naar een hogere integratie tijd.
Maar als de sensor is al verzadigd, wij verlagen de integratie tijd en probeer het opnieuw lezen van de sensor
} else { /* If event.light = 0 lux the sensor is probably saturated and no reliable data could be generated! */ if ( lightInteg == 2 ) { /* we are at highest integration time, try a lower one */ tsl.setIntegrationTime ( TSL2561_INTEGRATIONTIME_101MS ); /* medium resolution and speed */ wdt_reset(); tsl.getEvent ( &event );
if ( event.light == 0 ) { /* Still saturated? */ lightInteg = 0; tsl.setIntegrationTime ( TSL2561_INTEGRATIONTIME_13MS ); /* fast but low resolution */ wdt_reset(); tsl.getEvent ( &event ); if ( event.light != 0 ) { /* Got a result now? */ accLux += event.light; lightOk++; /* Increase counter of successful measurements */ } } else { lightInteg = 1; accLux += event.light; lightOk++; /* Increase counter of successful measurements */ } } else if ( lightInteg == 1 ) { /* we are at medium integration time, try a lower one */ lightInteg = 0; tsl.setIntegrationTime ( TSL2561_INTEGRATIONTIME_13MS ); /* fast but low resolution */ wdt_reset(); tsl.getEvent ( &event ); if ( event.light != 0 ) { /* Got a result now? */ accLux += event.light; lightOk++; /* Increase counter of successful measurements */ } } }
Deze lus is 5 keer herhaald aan te passen aan de veranderende lichtomstandigheden.
SPMONITOR_X_MEASUREMENT
Deze module bevat de CT routines voor de meting van de sensor. De rechtstreekse meting van de stroom en de spanning wordt gedaan door de emonLib-bibliotheek. EmonLib is een open source bibliotheek verstrekt door OpenEnergyMonitor.
void getCTValues (int index) roept emonLib tot de meting van de stroom, spanning en berekening van vermogen en arbeidsfactor. De parameter index bepaalt de sensor die moet worden gelezen. index = 0 begint een meting van de CT-sensor gekoppeld aan de zonnepaneel draad, 1 begint een meting van de CT-sensor aangesloten op de lijn van de belangrijkste macht.
/* Get the measured current from the solar panel */emon[index].calcVI(20, 2000);
/** Measured power in W */ double power = emon[index].realPower;
De verzamelde waarden worden vervolgens opgeslagen in een buffertank. Op dit moment is het systeem in staat om een meting rond elke seconde. De waarden die zijn opgeslagen in de accu worden gebruikt voor het berekenen van een gemiddelde waarde voor het opslaan van de waarden in de database, die wordt gedaan om de 60 seconden.
collPower[index] = collPower[index] + power;collCount[index] += 1;
'S nachts het zonnepaneel is niet produceren macht, maar het is met behulp van een kleine stand-by huidige. Wij elimineren deze standby-stroom met de code
/** Sensor 0 is measuring the solar panel, if it is less than 20W then mostlikely that is the standby current drawn by the inverters */if ( emon[index].Irms < 0.55 ) { power = 0.0; }
Deze manier, wij zorgen ervoor dat we een niet bestaande elektriciteitsproductie tijdens nacht niet opnemen.
De gemeten waarden worden vervolgens opgeslagen aan de kant van de Linux met de functie "Bridge.put" als een sleutel/waarde-structuur. De sleutels worden gemaakt met behulp van het voorvoegsel s voor waarden van de zonne-sensor en de c voorvoegsel voor waarden van de hoofdlijn-sensor.
Bridge.put ( prefix, String ( emon[index].Irms ) ); // CurrentBridge.put ( prefix + "r", String ( power ) ); // Real power Bridge.put ( prefix + "v", String ( emon[index].Vrms ) ); // Voltage Bridge.put ( prefix + "a", String ( emon[index].apparentPower ) ); // Apparent power Bridge.put ( prefix + "p", String ( emon[index].powerFactor ) ); // Power factor
SPMONITOR_Z_LOOP
Dit is de hoofdlus van het programma's. Het draait de hele tijd. Binnen de lus gebruiken we de functie millis() om metingen en opslaan van waarden in specifieke frequenties.
Op dit moment wordt de meting gestart elke seconde. Dit is mogelijk omdat de lichtsensor is nog niet gebruikt. Zodra de lichtsensor is gekoppeld moet de frequentie van de meting worden verlaagd, als de routine voor het lichte meten meer dan 2 seconden ergste geval kan nemen.
/** Actual time in milliseconds since start of spMonitor */ unsigned long now = millis(); if ( now - lastMeasure >= measureFreq ) { /* initiate measurement every 1 seconds */ lastMeasure = now; wdt_reset(); /* Activity LED on */ digitalWrite ( activityLED, HIGH ); wdt_reset(); /* Get the light measurement if a sensor is attached */ readLux(); /* Get the measured current from the solar panel */ getCTValues(0); /** Get the measured current from mains */ getCTValues(1); /* Activity LED off */ digitalWrite ( activityLED, LOW ); }
Deze code roept de meting functies voor de CT-sensoren en de lichtsensor. De cyclus van de meting wordt gevisualiseerd door een LED gemonteerd op het schild van de verbinding sensor.
De volgende code roept de functie om op te slaan van de waarden in de database elke minuut:
if ( now - lastSave >= 60000 ) { /* Save data every minute */ lastSave = now; wdt_reset(); saveData(); }
En tot slot hebben we een deel van de code rechtstreeks communiceert met het lokale netwerk met een PC of Android apparaat. Toen ik begon met de toepassing, ik heb geprobeerd om alle communicatie afgehandeld binnen deze Arduino-code, maar als gevolg van het geheugenbeperkingen realiseerde ik me dat dit niet mogelijk was. Aan het einde dus er slechts één functie links hier. Zoals de meeste delen van de mededeling zijn gedelegeerd rechtstreeks naar het Linux-deel van de Arduino board is alleen de status rapport functie hier behandeld.
/* There is a new client? */ if ( client.available() ) { wdt_reset(); /** Character holding the command that was sent */ char command = client.read(); if ( command == 'e' ) { /* Get the actual settings */ client.print ( "F " + String ( measureFreq ) + "s" ); client.println ( " V " + String ( vCal ) ); client.print ( "C1 " + String ( iCal1, 1 ) ); client.println ( " C2 " + String ( iCal2, 1 ) ); } /* Close connection and free resources. */ wdt_reset(); client.flush(); client.stop(); }
De status kan worden door elke browser verzoek of aanvraag gewoon telefonisch http://IPADDRESS/arduino/e
Het antwoord is
F 1000s V 255 C1 5.7 C2 11.5
F is de frequentie van de meting, V is de waarde van de kalibratie spanning, C1 en C2 zijn de CT-sensor kalibratie-waardes.
Deze functie zal worden gebruikt door de androïde toepassing te vinden van de Arduino-apparaat op het lokale netwerk.
SPMONITOR_Y_SETUP
Deze routine heet eenmaal na een reset van de Arduino board. Het bevat de initialisatieroutines voor de netwerkcommunicatie en de sensoren 1-instellen van de poort voor de activiteits-LED
/* set pin to output */ pinMode ( activityLED, OUTPUT );
2-het initialiseren van de netwerkcommunicatie en toegang tot het bestandssysteem van de Linux.
/* Initialize bridge connection */ Bridge.begin(); /* Listen for incoming connection only from localhost */ /* (no one from the external network could connect) */ server.listenOnLocalhost(); server.begin(); /* Initialize access to SDcard */ FileSystem.begin();
3 – initialisatie van het licht en de CT sensoren
/* Initialize counters and accumulators */ collPower[0] = collPower[1] = 0.0; collCount[0] = collCount[1] = collCount[2] = 0; /* Configure the YHDC SCT013-000 current sensors */ /* Initialise the current sensor 1 */ emon[0].voltage( 2, vCal, pShift1 ); // AD2, Vcal = 255, phase shift = 1.3 emon[0].current ( 0, iCal1 ); // AD0, Ical = 5.7 /* Initialise the current sensor 2 */ emon[1].voltage( 2, vCal, pShift2 ); // AD2, Vcal = 255, phase shift = 6.1 emon[1].current ( 1, iCal2 ); // AD1, Ical = 11.5 /* Get initial reading to setup the low pass filter */ unsigned int i = 0; while (i<50) { /* LED on */ digitalWrite ( activityLED, HIGH ); emon[0].calcVI ( 20, 2000 ); emon[1].calcVI ( 20, 2000 ); /* LED off */ digitalWrite ( activityLED, LOW ); i++; }
4 – initialisatie van de timer en de watchdog
/* Initiate call of getMeasures and saveData every 5 seconds / 60 seconds */ lastMeasure = lastSave = millis(); /* Activate the watchdog */ wdt_enable(WDTO_8S);
Met betrekking tot de watchdog, kunt u
wdt_reset();
vaak in de broncode weergeven. Met het gebruik van de watchdog zorg ik ervoor dat wanneer het programma vast te overal zitten komt dat laatste na 8 seconden een reset van de Arduino board wordt gestart. Dit zorgt ervoor dat het programma kan worden uitgevoerd zonder toezicht voor een lange tijd.
Dit beëindigt de korte beschrijving van de broncode van de Arduino. Om te zien alle details, Bekijk mijn repository op Github u de volledige broncode vindt.