Reducing Size

I am making a timer that is programmed with the sunrise/sunset times for my city and I am making Arduino use a relay to control some mains lighting. I am using a chronodot to keep time. I have it working, the only problem is that it is too large. My programming skills are basic and I’m sure there is a better way to do this. Below is my code. The current size is 41000 bytes and I need to put it on a Duemilanove so down to 32000 bytes (I think).

#include <Wire.h>
#include "Chronodot.h"
Chronodot RTC;

int LightOff[367] = {0, 930, 930, 930, 929, 929, 929, 928, 928, 927, 927, 926, 925, 925, 924, 923, 922, 921, 20, 919, 918, 917, 916, 915, 913, 912, 911, 909, 908, 906, 905, 903, 902, 900, 859, 857, 855, 854, 852, 850, 848, 846, 845, 843, 841, 839, 837, 835, 833, 831, 829, 827, 825, 823, 820, 818, 816, 814, 812, 810, 809, 808, 805, 803, 801, 759, 756, 754, 752, 750, 747, 745, 743, 741, 738, 736, 734, 731, 729, 727, 724, 722, 720, 717, 715, 713, 710, 708, 706, 703, 701, 659, 656, 654, 652, 650, 647, 645, 643, 640, 638, 636, 634, 631, 629, 627, 625, 623, 620, 618, 616, 614, 612, 610, 608, 607, 606, 601, 559, 557, 555, 553, 552, 550, 548, 546, 544, 542, 540, 539, 537, 535, 534, 532, 530, 529, 527, 526, 524, 523, 521, 520, 519, 517, 516, 515, 514, 513, 512, 510, 509, 509, 508, 507, 506, 505, 505, 504, 503, 503, 502, 502, 501, 501, 501, 500, 500, 500, 500, 500, 500, 500, 500, 500, 501, 501, 501, 502, 502, 503, 503, 504, 504, 505, 506, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 519, 520, 521, 522, 524, 525, 526, 528, 529, 530, 532, 533, 535, 536, 538, 539, 541, 542, 544, 546, 547, 549, 550, 552, 553, 555, 557, 558, 600, 602, 603, 605, 606, 608, 610, 611, 613, 615, 616, 618, 619, 621, 623, 624, 626, 628, 629, 631, 633, 634, 636, 638, 639, 641, 642, 644, 646, 647, 649, 651, 652, 654, 656, 657, 659, 700, 702, 704, 705, 707, 709, 710, 712, 714, 715, 717, 719, 720, 722, 724, 725, 727, 729, 730, 732, 734, 736, 737, 739, 741, 742, 744, 746, 748, 749, 751, 753, 755, 757, 758, 800, 802, 804, 805, 807, 809, 811, 813, 815, 816, 818, 820, 822, 824, 825, 827, 829, 831, 833, 834, 836, 838, 840, 841, 843, 845, 847, 848, 850, 852, 853, 855, 857, 858, 900, 901, 903, 904, 906, 907, 909, 910, 912, 913, 914, 915, 916, 918, 919, 920, 921, 922, 923, 924, 924, 925, 926, 927, 927, 928, 928, 929, 929, 929, 930, 930, 930, 930, 930, 930};
int LightOn[367] ={0, 1650, 1651, 1652, 1653, 1654, 1656, 1657, 1658, 1700, 1701, 1703, 1704, 1706, 1707, 1709, 1710, 1712, 1714, 1715, 1717, 1719, 1721, 1722, 1724, 1726, 1728, 1729, 1731, 1733, 1735, 1737, 1739, 1740, 1742, 1744, 1746, 1748, 1750, 1752, 1754, 1755, 1757, 1759, 1801, 1803, 1805, 1807, 1809, 1810, 1812, 1814, 1816, 1818, 1820, 1822, 1823, 1825, 1827, 1829, 1830, 1831, 1832, 1834, 1836, 1838, 1840, 1841, 1843, 1845, 1847, 1849, 1850, 1852, 1854, 1856, 1857, 1859, 1901, 1903, 1904, 1906, 1908, 1910, 1911, 1913, 1915, 1916, 1918, 1920, 1922, 1923, 1925, 1927, 1929, 1930, 1932, 1934, 1935, 1937, 1939, 1941, 1942, 1944, 1946, 1947, 1949, 1951, 1953, 1954, 1956, 1958, 1959, 2001, 2003, 2005, 2006, 2008, 2010, 2011, 2013, 2015, 2016, 2018, 2020, 2021, 2023, 2025, 2026, 2028, 2030, 2031, 2033, 2035, 2036, 2038, 2039, 2041, 2042, 2044, 2045, 2047, 2048, 2050, 2051, 2052, 2054, 2055, 2056, 2057, 2059, 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107, 2108, 2109, 2110, 2111, 2111, 2112, 2113, 2113, 2114, 2114, 2115, 2115, 2115, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2115, 2115, 2115, 2114, 2114, 2113, 2113, 2112, 2111, 2111, 2110, 2109, 2108, 2107, 2106, 2105, 2104, 2103, 2102, 2101, 2100, 2058, 2057, 2056, 2054, 2053, 2051, 2050, 2048, 2047, 2045, 2044, 2042, 2040, 2038, 2037, 2035, 2033, 2031, 2029, 2028, 2026, 2024, 2022, 2020, 2018, 2016, 2014, 2012, 2010, 2007, 2005, 2003, 2001, 1959, 1957, 1955, 1952, 1950, 1948, 1946, 1943, 1941, 1939, 1937, 1934, 1932, 1930, 1927, 1925, 1923, 1921, 1918, 1916, 1914, 1911, 1909, 1906, 1904, 1902, 1859, 1857, 1855, 1852, 1850, 1847, 1845, 1843, 1841, 1838, 1836, 1834, 1831, 1829, 1827, 1824, 1822, 1820, 1818, 1815, 1813, 1811, 1808, 1806, 1804, 1802, 1800, 1758, 1755, 1753, 1751, 1749, 1747, 1745, 1742, 1740, 1738, 1736, 1734, 1732, 1730, 1728, 1727, 1725, 1723, 1721, 1719, 1717, 1716, 1714, 1712, 1711, 1709, 1707, 1706, 1704, 1703, 1701, 1700, 1658, 1657, 1656, 1654, 1653, 1652, 1651, 1650, 1649, 1648, 1647, 1646, 1645, 1644, 1644, 1643, 1642, 1642, 1641, 1641, 1640, 1640, 1640, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1640, 1640, 1640, 1641, 1641, 1642, 1642, 1643, 1643, 1644, 1645, 1646, 1647, 1648, 1649};

int Relay = 7; // Relay on Pin 7
int DayCode = 0;
int Year = 0;
int Month = 0;
int Day = 0;
int Hour = 0;
int Minute = 0;
int Second = 0;
int Multiplier = 0;
int Time = 0;

// ------------------------------------------

void setup() {
  
  pinMode(Relay, OUTPUT);
  
  Serial.begin(9600);
  Wire.begin();
  RTC.begin();
  
}

// -------------------------------------------

void loop() {
  
  DateTime now = RTC.now();    // Retrieve all information from ChronoDot
  Year = now.year();
  Month = now.month();
  Day = now.day();
  Hour = now.hour();
  Minute = now.minute();
  Second = now.second();
  
  if (Month == 1)  {      //Create the multiplier which is added to the day to obtain the date code
    Multiplier = 0;  }
  if (Month == 2)  {
    Multiplier = 31;  }
  if (Month == 3)  {
    Multiplier = 60;  }
  if (Month == 4)  {
    Multiplier = 91;  }
  if (Month == 5)  {
    Multiplier = 121;  }
  if (Month == 6)  {
    Multiplier = 152;  }
  if (Month == 7)  {
    Multiplier = 182;  }
  if (Month == 8)  {
    Multiplier = 213;  }
  if (Month == 9)  {
    Multiplier = 244;  }
  if (Month == 10)  {
    Multiplier = 274;  }
  if (Month == 11)  {
    Multiplier = 305;  }
  if (Month == 12)  {
    Multiplier = 335;  }
  
  DayCode = Multiplier + Day;  // Determine what day it is
  Time = Hour*100 + Minute;
  
  // Perform Calculations to turn relay on/off
  if (DayCode == 1)  {
    if (Time == LightOff[DayCode])  {
      digitalWrite(Relay, LOW);  }
    if (Time == LightOn[DayCode])  {
      digitalWrite(Relay, HIGH);  }
  }
  if (DayCode == 2)  {
    if (Time == LightOff[DayCode])  {
      digitalWrite(Relay, LOW);  }
    if (Time == LightOn[DayCode])  {
      digitalWrite(Relay, HIGH);  }
  }
}
....
....
....
etc for the rest of the days

I know I need to parse down my hundreds of if statements but am unsure on what statement to use.

Thanks for helping a first time Arduino user,
Spencer

You have set up a (huge) array with the times for turning things on and off.

Then you have a long series of if-statements to check what day it is, then retrieve that day's values from the array.

One simple thing you can do is remove the if-statements, since you're needlessly repeating that piece of code:

  // Perform Calculations to turn relay on/off
  if (DayCode == 1)  {
    if (Time == LightOff[DayCode])  {
      digitalWrite(Relay, LOW);  }
    if (Time == LightOn[DayCode])  {
      digitalWrite(Relay, HIGH);  }
  }
  if (DayCode == 2)  {
    if (Time == LightOff[DayCode])  {
      digitalWrite(Relay, LOW);  }
    if (Time == LightOn[DayCode])  {
      digitalWrite(Relay, HIGH);  }
  }

You can replace all of those with just the two if statements on their own:

    if (Time == LightOff[DayCode])  {
      digitalWrite(Relay, LOW);  }
    if (Time == LightOn[DayCode])  {
      digitalWrite(Relay, HIGH);  }

You could also replace the "multiplier" (which is really an "offset") with an array:

int multiplier [12] = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
…
DayCode = Day + multiplier[ Month - 1 ]; // Array indexes start at 0, so multiplier is 0…11

And then, there's the issue of what are those times representing? If they are sunrise/sunset times you'll find that there are discussions on the old forums about calculating the times. Search for arduino chicken-coop :slight_smile:

I adapted some code I found some years ago to run on the Arduino. With some minor modifications it should suit you:

#define HOME_LATITUDE -37.12345   
#define HOME_LONGITUDE 145.6789   
#define UTC_OFFSET 10               // 10 hours east of UTC

/*****************************************************
 *This is an implementation of the Sunrise/Sunset Algorithm
 *found at 
 * http://williams.best.vwh.net/sunrise_sunset_algorithm.htm
 *from the Almanac for Computers, 1990
 *Published by Nautical Almanac Office
 *Washington, DC 20392
 *Implemented by Chris Snyder
 *****************************************************/

#define OFFICIAL_ZENITH 90.83333
#define CIVIL_ZENITH 96
#define NAUTICAL_ZENITH 102
#define ASTRONOMICAL_ZENITH 108

/*****************************************************
 *Function name:calcSunset
 *Params: $date(The day to calculate sunset or sunrise for)
 *	$lat(The latitude for the location to calculate sunrise or sunset for)
 *	$lon(The longitude for the location to calculate sunrise or sunset for west is negative)
 *	$sunset(if set to 1 calculates sunset if set to 0 sunrise)
 *	$GMToffset(difference in hours from GMT)
 * 
 * 	$zenith:                Sun's zenith for sunrise/sunset
 * 	  offical      = 90 degrees 50'  (90.8333)
 * 	  civil        = 96 degrees
 * 	  nautical     = 102 degrees
 * 	  astronomical = 108 degrees
 * 	
 * 	NOTE: longitude is positive for East and negative for West
 * 	      latitude is positive for North and negative for south
 * 	
 *****************************************************/

#define pi 3.14159265358979323

float AdjustTo360 (float i)
{
  if (i > 360.0)
    i -= 360.0;
  else if (i < 0.0)
    i += 360.0;  
  return i;
} // end of AdjustTo360

float AdjustTo24 (float i)
{
  if (i > 24.0)
    i -= 24.0;
  else if (i < 0.0)
    i += 24.0;  
  return i;
} // end of AdjustTo24

float deg2rad (float degrees)
{
  return degrees * pi / 180;
}  // end of deg2rad


float rad2deg (float radians)
{
  return radians / ( pi / 180 );
}  // end of rad2deg

void calcSunset(int n, float lat, float lon, bool sunset, float GMToffset, float zenith, 
                byte & hour, byte & minutes)
{

  hour = minutes = 0;

  //Convert the longitude to hour value and calculate and
  //Approximate time.
  float lonhour = (lon/15);
  float t, m;

  if(sunset)
    t=n+((18 - lonhour)/24);
  else
    t=n+((6 - lonhour)/24);

  //Calculate the Sun's mean anomaly
  m = (0.9856*t) - 3.289;

  //Calculate the Sun's true longitude
  float sinm = sin(deg2rad(m));
  float sin2m = sin(2*deg2rad(m));

  float l= AdjustTo360 (m +(1.916 * sinm) + (0.02 * sin2m) + 282.634);

  //Calculate the Sun's right ascension(RA)
  float tanl = 0.91764 * tan(deg2rad(l));
  float ra = AdjustTo360 (rad2deg(atan(tanl)));

  //Putting the RA value into the same quadrant as L
  float lq = (floor(l/90)) * 90;
  float raq = (floor(ra/90)) * 90;
  ra = ra + (lq - raq);

  //Convert RA values to hours
  ra /= 15;

  //calculate the Sun's declination
  float sindec = 0.39782 * sin(deg2rad(l));
  float cosdec = cos(asin(sindec));

  //calculate the Sun's local hour angle
  float cosh = (cos(deg2rad(zenith)) - (sindec * sin(deg2rad(lat)))) 
    / (cosdec * cos(deg2rad(lat)));

  //if cosH > 1 the sun never rises on this date at this location
  //if cosH < -1 the sun never sets on this date at this location

  if (cosh >  1)
    return; 
  else if (cosh < -1)
    return;

  float h;

  //finish calculating H and convert into hours
  if(sunset)
    h = rad2deg(acos(cosh));
  else
    h = 360 - rad2deg(acos(cosh));

  h /= 15;

  //Calculate local mean time of rising/setting
  t = h + ra - (0.06571 * t) - 6.622;

  //Adjust back to UTC
  float ut = AdjustTo24 (t - lonhour);
  //Adjust for current time zone
  ut= AdjustTo24 (ut+GMToffset);

  hour = floor(ut);
  minutes = round(60*(ut - hour));

}  // end of calcSunset

int days [] = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 };

int dayNumber (int year, int month, int day)
  {
  int extra = 0;
  
  if (floor (year % 4) == 0 && month > 2)
    extra = 1;
    
  return days [month - 1] + day + extra - 1;
        
  }  // end of dayNumber


void setup ()
{
  Serial.begin (115200);
  
  byte hour, minutes;
  
  int day = dayNumber (2011, 8, 2);   // year, month, day
  
  Serial.print ("day is ");
  Serial.println (day, DEC);
  
  calcSunset (day, HOME_LATITUDE, HOME_LONGITUDE, false, UTC_OFFSET, OFFICIAL_ZENITH,  hour, minutes);
  
  Serial.print ("Sunrise at: ");
  Serial.print (hour, DEC);
  Serial.print (":");
  Serial.print (minutes, DEC);
  Serial.println ();

  calcSunset (day, HOME_LATITUDE, HOME_LONGITUDE, true, UTC_OFFSET, OFFICIAL_ZENITH,  hour, minutes);

  Serial.print ("Sunset at: ");
  Serial.print (hour, DEC);
  Serial.print (":");
  Serial.print (minutes, DEC);
  Serial.println ();
  
}  // end of setup

void loop ()
{
}

You need to replace a couple of things:

  • The hard-coded date (2011/8/2) would be replaced by the output from the clock chip
  • Change the first three lines to reflect your latitude, longitude and UTC time offset
  • If you change OFFICIAL_ZENITH to CIVIL_ZENITH (where the function is called) you get Dawn and Dusk rather than Sunrise and Sunset.

Having done that the code should give you the sunrise/sunset times (as illustrated by the debugging prints) and you could use that to work out whether to turn your lights on or not.

Example output when I tested:

day is 214
Sunrise at: 7:16
Sunset at: 17:32

This is correct for where I am.

Code size:

Binary sketch size: 6414 bytes (of a 32256 byte maximum)

OK, so now I (and possibly others) are waiting to hear - did these changes fix your memory problem, or do you need still more tweaking? If so, please post your latest code and we can hack at it...

You can replace all of those with just the two if statements on their own:

    if (Time == LightOff[DayCode])  {

digitalWrite(Relay, LOW);  }
    if (Time == LightOn[DayCode])  {
      digitalWrite(Relay, HIGH);  }

This was the one. I definitely overlooked this one. I replaced 2200 lines of code with these 4. It saved 32kb. I feel a little stupid now.

I really appreciate the help and the eagerness to find out the results (I'm a busy man lol at least give me 24 hours to update it). Super duper forum. Thanks tons.

Spencer