/* 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;

    }

   

   

   

}