/* Code written by Mark Mahowald, July 28, 2018
*
* Goal of program is to run a load (AC or dehumidifier in my case) off of a
* inverter running off ships batteries.
* When the solar is charging the batteries, let the load run (charge over 13.3 V)
* When the batteries are drained to a level you like turn off the load (via relay),
* and let the batteries recharge. Every 5 days, take two days off to let solar really
* pack in the charge to get batteries 100% full, to extend battery life.
* We can not be assured those two days will be sunny, but at least twice a month the batteries
* should hit 100% charge, and never be lower than 75% charge (or whatever voltage level was chosen).
* That is the plan...
*
* The load can be plugged into the inverter, and the inverter itself switched by the relay
* if the inverter can be turned on by an external on/off switch. This is more efficient as
* you do not have the idle inverter load on the batteries at all times. The other option is to
* make the load switchable my modifying the power cord to connect the "Hot" lead through the relay
* This will ensure power only is available when the relay is closed by the Aurdino
*
* LED status indicator: blinking at 1 second interval== in main loop of operation.
* Blinking 10 seconds on and 10 seconds off in the two day loop letting batteries fully recharge
* Blinking 30 seconds on and 30 seconds off in the "top of the hour" loop, used for timing debugging
*/
/* number of analog samples to take per reading */
#define NUM_SAMPLES 10
/*
* The measured voltage at "5V pin" using the power supply of choice (USB or 9V supply) is the ref_voltage
* This reading is key to get an accurate voltage reading for this project and is power supply dependent
* it measured 5V when using 9V supply, but 4.6 to 4.9 V depending on the type of USB power supply
* used in my tests, so this needs to be measured when powered as it will be in production and set accordingly
*/
float ref_voltage = 4.8800; /* actual voltage at 5V pin on Aurdino when powered up in live system. */
float turn_on_voltage = 13.300; /* measure the turn on and turn off levels and tune for your goals */
float turn_off_voltage = 12.500;
int sum = 0; /* sum of samples taken */
int sample_count = 0; /* current sample number */
float voltage = 0.0; /* calculated voltage */
float realvolts = 0.0; /* Voltage after taking out voltage divider, so true battery voltage */
int seconds = 0;
int hours = 0;
int cycles = 0;
int currentState = HIGH;
int Relay1 = 10; /* use pin 10 for Relay 1 */
int Relay2 = 11; /* use pin 11 for Relay 2 */
/* use these pins to show cycle count */
int sig1 = 2;
int sig2 = 3;
int sig3 = 4;
int sig4 = 5;
int sig5 = 6;
int sig6 = 7;
int sig7 = 8;
int sig8 = 9;
/* Pin 13 has an LED connected on the Arduino board. Use this as a status indicator */
int Led = 13;
void setup()
{
/* initialize the digital pin as an output my control pins as outputs */
pinMode(Led, OUTPUT);
pinMode(Relay1, OUTPUT);
pinMode(Relay2, OUTPUT);
/* pins that will show cycle count in binary on LED's */
pinMode(sig1, OUTPUT);
pinMode(sig2, OUTPUT);
pinMode(sig3, OUTPUT);
pinMode(sig4, OUTPUT);
pinMode(sig5, OUTPUT);
pinMode(sig6, OUTPUT);
pinMode(sig7, OUTPUT);
pinMode(sig8, OUTPUT);
/* Initialize the relays to the off position (HIGH is the off position the way they are wired) */
digitalWrite(Relay1, HIGH);
digitalWrite(Relay2, HIGH);
}
/*
* This routine uses LEDs to give a constant binary display of the number of cycles with output pin 2
* being the "1's" bit. As there are 8 Led's, we can only keep count up to 255 cycles. Since we should not have more than
* one cycle per day as we burn about 150Amp/Hrs from the batteries before we turn off, which will take half a day to recover
* in good sun, and we take two days off each week, this should cover over 50 weeks, so it is plenty.
*/
void cycleCount()
{
digitalWrite(sig1, LOW);
digitalWrite(sig2, LOW);
digitalWrite(sig3, LOW);
digitalWrite(sig4, LOW);
digitalWrite(sig5, LOW);
digitalWrite(sig6, LOW);
digitalWrite(sig7, LOW);
digitalWrite(sig8, LOW);
cycles++;
if (cycles >= 256) cycles = 255;
if (cycles &0x01) digitalWrite(sig1, HIGH);
if (cycles &0x02) digitalWrite(sig2, HIGH);
if (cycles &0x04) digitalWrite(sig3, HIGH);
if (cycles &0x08) digitalWrite(sig4, HIGH);
if (cycles &0x10) digitalWrite(sig5, HIGH);
if (cycles &0x20) digitalWrite(sig6, HIGH);
if (cycles &0x40) digitalWrite(sig7, HIGH);
if (cycles &0x80) digitalWrite(sig8, HIGH);
}
/*
* Gets called after one hour has past. Blinks light 30 seconds on and 30 seconds off
* for as many minutes as the passed parameter "x"
* Nothing else happens during the blinking period, but it gives us a timing measure
* and also shows where we are in the process.
*/
void lightHour(int x)
{
int a = 0;
seconds = 0;
++hours;
while (a < x)
{
/* turn light on, wait 30 seconds */
digitalWrite(Led, HIGH);
delay(30000); /*30 seconds */
seconds = seconds + 30;
/* turn light off, wait 30 seconds */
digitalWrite(Led, LOW);
delay(30000); /*30 seconds */
seconds = seconds + 30;
++a;
}
}
/*
* call this routine if want to take two days off to let solar recover
* Blink the hour LED and board LED every 10 seconds on and off to show we are in this state
*/
void twodaysoff()
{
/*
* first reset counters and shut everything down for two days to allow batteries to fully recharge
* Blink status LED on and off for 10 seconds to show in the two day "recharge" loop
*
*/
hours = 0;
seconds = 0;
digitalWrite(Relay1, HIGH); /* turn the relay off (HIGH is the off level) */
digitalWrite(Relay2, HIGH); /* turn the relay off (HIGH is the off level) */
while (hours < 48)
{
/* turn light on, wait 10 seconds */
digitalWrite(Led, HIGH);
delay(10000); /*10 seconds */
seconds = seconds + 10;
/* check and see if it has been an hour */
if (seconds >= 3600)
{
lightHour(10);
}
/* turn lights off, wait 10 seconds */
digitalWrite(Led, LOW);
delay(10000); /*10 seconds */
seconds = seconds + 10;
/* check and see if it has been an hour */
if (seconds >= 3600)
{
/* turn on timing light for 10 minutes *.
*
*/
lightHour(10);
}
}
/* fell out of loop, so we are at 48 hours. Reset things and leave subroutine */
hours = 0;
seconds = 0;
/* leave lights off when leave routine */
digitalWrite(Led, LOW);
} /* end of twodaysoff */
/*
* call this routine to check main battery voltage levels
*
* Use pin A2 as the analog sample point
*
* This routine takes one second to execute due to the delays in the sampling code loop
* sets the voltage read in the global variable realvolts
*
*/
void readvoltage()
{
int average;
while (sample_count < NUM_SAMPLES)
{
sum += analogRead(A2);
sample_count++;
delay(100);
}
/* First calculate the average reading which is a number between 0 and 1023 */
average = sum/NUM_SAMPLES;
/* Now multiply by the value of the measured voltage (the reference voltage) at the "5 V" pin on the Arduino.
* and divide that by 1024 as the voltage is a scale from 0 at 0 V to 1023 at the reference voltage
* I measured 5.00 volts if powered via the 9V input and 4.88 V when powered by the USB
*/
voltage = ((float)average * ref_voltage) /1024.0;
/* my voltage divider network is 4 "identical" resistors with the analog lead between the last resistor and the ground
* So I am seeing 1/4 of the true voltage so multiply by 4. This allows me to measure up to 20V DC safely
* without putting more than 5V to the analog pin (which would wreck the Ardinuo).
*/
realvolts = (float)voltage * 4.0;
/* reset sum and sample_count for next time through loop */
sample_count = 0;
sum = 0;
} /* End of readvoltage */
void loop() /* Main code loop */
{
/* blink on board LED OFF for one second (while in readvoltage sub routine) */
digitalWrite(Led, LOW);
readvoltage(); /* readvoltage took one second as there were 10 samples, 100ms apart */
seconds++;
/* blink on board LED ON for 1 second To show things are running in main loop*/
digitalWrite(Led, HIGH);
delay(1000);
seconds++;
/* check if it has been one hour */
if (seconds >= 3600)
{
lightHour(5);
}
/* check and see if it has been 5 days, if so, lets rest for two days */
if (hours >= 120)
{
twodaysoff(); /* this routine blinks status LED's slow for two days but never fires the relay */
}
/* if not taking two days off, fire the relay if well charged and sun is shining "turn_on_voltage" */
if (realvolts >= turn_on_voltage)
{
digitalWrite(Relay1, LOW);
digitalWrite(Relay2, LOW);
if (currentState == HIGH)
{
/* so the state has changed, so now a cycle to on */
currentState = LOW;
cycleCount();
}
}
/* turn relays off if voltage under "turn_off_voltage" so we don't run the battery down too far*/
if (realvolts <= turn_off_voltage)
{
digitalWrite(Relay1, HIGH);
digitalWrite(Relay2, HIGH);
/* Set current state to off mode */
currentState = HIGH;
}
}