#include <SPI.h>
#include <Wire.h>
#include "RTClib.h"
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
RTC_DS1307 RTC;
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
//Indlæs soltider i programhukommelse
const unsigned int soltid[366][2] PROGMEM = {
{1600, 849}, //1, 01-01-2017
{1602, 849},
{1603, 848},
{1604, 848},
{1606, 848},
{1607, 847},
{1608, 847},
{1610, 846},
{1612, 845},
{1613, 844},
{1615, 844},
{1617, 843},
{1618, 842},
{1620, 841},
{1622, 840},
{1624, 838},
{1626, 837},
{1627, 836},
{1629, 835},
{1631, 833},
{1633, 832},
{1635, 831},
{1637, 829},
{1639, 828},
{1641, 826},
{1643, 824},
{1645, 823},
{1648, 821},
{1650, 819},
{1652, 818},
{1654, 816},
{1656, 814}, //32, 01-02-2017
{1658, 812},
{1700, 810},
{1702, 808},
{1704, 806},
{1707, 804},
{1709, 802},
{1711, 800},
{1713, 758},
{1715, 756},
{1717, 754},
{1720, 752},
{1722, 750},
{1724, 747},
{1726, 745},
{1728, 743},
{1730, 741},
{1732, 738},
{1735, 736},
{1737, 734},
{1739, 731},
{1741, 729},
{1743, 727},
{1745, 724},
{1747, 722},
{1749, 719},
{1752, 717},
{1754, 715},
{1756, 712}, //60, 01-03-2017
{1758, 710},
{1800, 707},
{1802, 705},
{1804, 702},
{1806, 700},
{1808, 657},
{1810, 655},
{1812, 652},
{1814, 650},
{1816, 647},
{1818, 644},
{1820, 642},
{1822, 639},
{1824, 637},
{1826, 634},
{1828, 632},
{1831, 629},
{1833, 626},
{1835, 624},
{1837, 621},
{1839, 619},
{1841, 616},
{1843, 613},
{1945, 711},
{1947, 708},
{1949, 706},
{1951, 703},
{1953, 700},
{1955, 658},
{1957, 655},
{1959, 653}, //91, 01-04-2017
{2001, 650},
{2002, 648},
{2004, 645},
{2006, 642},
{2008, 640},
{2010, 637},
{2012, 635},
{2014, 632},
{2016, 630},
{2018, 627},
{2020, 625},
{2022, 622},
{2024, 620},
{2026, 617},
{2028, 615},
{2030, 612},
{2032, 610},
{2034, 607},
{2036, 605},
{2038, 602},
{2040, 600},
{2042, 558},
{2044, 555},
{2046, 553},
{2048, 551},
{2050, 548},
{2052, 546},
{2054, 544},
{2056, 542},
{2058, 539}, //121, 01-05-2017
{2100, 537},
{2102, 535},
{2104, 533},
{2106, 531},
{2108, 529},
{2110, 527},
{2112, 525},
{2114, 523},
{2116, 521},
{2118, 519},
{2119, 517},
{2121, 515},
{2123, 513},
{2125, 511},
{2127, 509},
{2129, 508},
{2130, 506},
{2132, 504},
{2134, 503},
{2136, 501},
{2137, 459},
{2139, 458},
{2140, 456},
{2142, 455},
{2144, 454},
{2145, 452},
{2147, 451},
{2148, 450},
{2149, 449},
{2151, 448},
{2152, 447}, //152, 01-06-2017
{2153, 446},
{2155, 445},
{2156, 444},
{2157, 443},
{2158, 442},
{2159, 441},
{2200, 441},
{2201, 440},
{2202, 440},
{2203, 439},
{2204, 439},
{2204, 438},
{2205, 438},
{2206, 438},
{2206, 438},
{2207, 438},
{2207, 438},
{2207, 438},
{2208, 438},
{2208, 438},
{2208, 438},
{2208, 439},
{2208, 439},
{2208, 439},
{2208, 440},
{2208, 440},
{2207, 441},
{2207, 442},
{2207, 442},
{2206, 443}, //182, 01-07-2017
{2206, 444},
{2205, 445},
{2205, 446},
{2204, 447},
{2203, 448},
{2202, 449},
{2202, 450},
{2201, 451},
{2200, 453}, //191
{2159, 454},
{2158, 455},
{2156, 457},
{2155, 458},
{2154, 459},
{2153, 501},
{2151, 502},
{2150, 504},
{2148, 505},
{2147, 507},
{2145, 509},
{2144, 510},
{2142, 512},
{2141, 513},
{2139, 515},
{2137, 517},
{2135, 519},
{2134, 520},
{2132, 522},
{2130, 524},
{2128, 526},
{2126, 528}, //213, 01-08-2017
{2124, 529},
{2122, 531},
{2120, 533},
{2118, 535},
{2116, 537},
{2114, 539},
{2111, 541},
{2109, 543},
{2107, 544},
{2105, 546},
{2103, 548},
{2100, 550},
{2058, 552},
{2056, 554},
{2054, 556},
{2051, 558},
{2049, 600},
{2046, 602},
{2044, 603},
{2042, 605},
{2039, 607},
{2037, 609},
{2034, 611},
{2032, 613},
{2029, 615},
{2027, 617},
{2024, 619},
{2022, 621},
{2019, 623},
{2017, 625},
{2014, 626}, //244, 01-09-2017
{2012, 628},
{2009, 630},
{2007, 632},
{2004, 634},
{2001, 636},
{1959, 638},
{1956, 640},
{1954, 642},
{1951, 644},
{1949, 646},
{1946, 647},
{1943, 649},
{1941, 651},
{1938, 653},
{1935, 655},
{1933, 657},
{1930, 659},
{1928, 701},
{1925, 703},
{1922, 705},
{1920, 707},
{1917, 708},
{1915, 710},
{1912, 712},
{1909, 714},
{1907, 716},
{1904, 718},
{1902, 720},
{1859, 722},
{1856, 724}, //273, 01-10-2017
{1854, 726},
{1851, 728},
{1849, 730},
{1846, 732},
{1844, 734},
{1841, 736},
{1838, 738},
{1836, 740},
{1833, 742},
{1831, 744},
{1828, 746},
{1826, 748},
{1823, 750},
{1821, 752},
{1819, 754},
{1816, 756},
{1814, 758},
{1811, 800},
{1809, 802},
{1807, 804},
{1804, 806},
{1802, 808},
{1800, 810},
{1757, 812},
{1755, 814},
{1753, 816},
{1751, 818},
{1648, 720},
{1646, 722},
{1644, 725},
{1642, 727}, //305, 01-11-2017
{1640, 729},
{1638, 731},
{1636, 733},
{1634, 735},
{1632, 737},
{1630, 739},
{1628, 741},
{1626, 743},
{1624, 745},
{1622, 747},
{1620, 749},
{1619, 752},
{1617, 754},
{1615, 756},
{1614, 758},
{1612, 800},
{1610, 802},
{1609, 803},
{1607, 805},
{1606, 807},
{1605, 809},
{1603, 811},
{1602, 813},
{1601, 815},
{1600, 817},
{1559, 818},
{1558, 820},
{1557, 822},
{1556, 823},
{1555, 825}, //335, 01-12-2017
{1554, 827},
{1553, 828},
{1553, 830},
{1552, 831},
{1551, 833},
{1551, 834},
{1550, 835},
{1550, 837},
{1550, 838},
{1550, 839},
{1549, 840},
{1549, 841},
{1549, 842},
{1549, 843},
{1549, 844},
{1550, 845},
{1550, 846},
{1550, 846},
{1551, 847},
{1551, 847},
{1552, 848},
{1552, 848},
{1553, 849},
{1554, 849},
{1554, 849},
{1555, 849},
{1556, 849},
{1557, 849},
{1558, 849},
{1559, 849}, //365, 31-12-2017
{1559, 849} //366 i tilfælde af skudår
};
/* PORTOVERSIGT
A4 -> display SDA + rtc SDA
A5 -> display SCK + rtc SCL
D4 -> relay IN1 (foran hus)
D5 -> relay IN2 (bag hus)
D6 -> knap 1 (stil 1 time tilbage)
D7 -> knap 2 (stil 1 time frem)
D8 -> knap 3 (tænd lys manuelt)
D13 -> intern LED
*/
const int Relay1 = 4; //Sæt til port D4
const int Relay2 = 5; //Sæt til port D5
const int button1 = 6; //Sæt til port D6
const int button2 = 7; //Sæt til port D7
const int button3 = 8; //Sæt til port D8
const int statusBlinkPin = 13; //Sæt til port D13, intern LED
const unsigned long interval = 1000; // the time we need to wait
const unsigned long intervalTimer = 900000; //Tid lys skal være tændt ved tryk på knap 3. 900000 msek = 15 min, 15000 msek = 15 sek.
unsigned long currentMillis;
unsigned long previousMillis = 0; // millis() returns an unsigned long
unsigned long previousTimerMillis = 0; // millis() returns an unsigned long
//bool setBackTime = false;
bool manualOn = false; //Er lys tændt manuelt
bool button3State = false;
bool statusBlinkState = false; //For toggle blink on/off
bool statusRelay1 = false; //false = slukket, true = tændt
bool statusRelay2 = false; //false = slukket, true = tændt
DateTime now;
//Vi kan spare 36 bytes SRAM ved at bruge PROGMEM
const char charOn[] PROGMEM = {"ON"};
const char charOff[] PROGMEM = {"OFF"};
const char charForan[] PROGMEM = {" <Foran"};
const char charBag[] PROGMEM = {" Bag>"};
const char charGemTid[] PROGMEM = {" Gem tid? "};
const char charJa[] PROGMEM = {" JA: Tryk igen "};
const char charNej[] PROGMEM = {" NEJ: Vent "};
const char charTidNul[] PROGMEM = {":00:00"};
void setup() {
//Serial.begin(9600);
Wire.begin();
RTC.begin();
//RTC.adjust(DateTime(__DATE__, __TIME__));
//Sæt tid: DateTime(year,month,day,hour,min,sec)
pinMode(Relay1, OUTPUT);
pinMode(Relay2, OUTPUT);
pinMode(button1, INPUT);
pinMode(button2, INPUT);
pinMode(button3, INPUT);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3C
display.ssd1306_command(SSD1306_SETCONTRAST); //Vælg lysstyrkeregister
display.ssd1306_command(0); //Sæt lysstyrkeregister til det lavest mulige
display.display(); //Skriv skærmbuffer (logo) til skærm
delay(5000);
//Clear the buffer/screen
display.clearDisplay();
display.display();
delay(500);
pinMode(statusBlinkPin, OUTPUT);
}
void loop() {
currentMillis = millis(); //grab current time
if ((unsigned long)(currentMillis - previousMillis) >= interval) {
if (((unsigned long)(currentMillis - previousTimerMillis) >= intervalTimer) && (manualOn == true)) {
previousTimerMillis = millis(); //save the "current" time
manualOn = false;
}
now = RTC.now(); //Hent tid fra RTC
setRelays((now.hour() * 100) + now.minute(), calculateDayOfYear(now.day(), now.month(), now.year()) - 1); //styr relæerne med: (tid som int, day of year)
display.clearDisplay(); //Clear display
printRelayStatus();
printStaticText();
printTimeDate();
printSunUpDown();
statusBlink();
display.display(); //Print to screen
previousMillis = millis(); //save the "current" time
}
if (digitalRead(button1) == HIGH) {
delay(20); //Debounce
if (digitalRead(button1) == HIGH) {
while (digitalRead(button1) == HIGH) delay(10); //Debounce, wait for no button press
knap1();
}
}
if (digitalRead(button2) == HIGH) {
delay(20); //Debounce
if (digitalRead(button2) == HIGH) {
while (digitalRead(button2) == HIGH) delay(10); //Debounce, wait for no button press
knap2();
}
}
if (digitalRead(button3) == HIGH) {
delay(20);
if (button3State == false) {
if (digitalRead(button3) == HIGH) {
if (manualOn == true) {
manualOn = false;
} else {
manualOn = true;
}
button3State = true;
}
}
previousTimerMillis = millis();
} else button3State = false;
}
void knap1() {
char printChar;
//int k;
int len;
display.clearDisplay();
display.setCursor(6, 0);
display.setTextSize(2);
display.setTextColor(BLACK, WHITE);
//display.println(" Gem tid? ");
len = strlen_P(charGemTid);
for (int k = 0; k < len; k++) {
printChar = pgm_read_byte_near(charGemTid + k);
display.print(printChar);
}
display.println();
display.setTextColor(WHITE);
display.setTextSize(1);
display.println();
display.print(" ");
display.setTextSize(2);
display.print(" ");
if (now.hour() < 10) display.print("0");
display.print(now.hour(), DEC);
//display.println(":00:00");
len = strlen_P(charTidNul);
for (int k = 0; k < len; k++) {
printChar = pgm_read_byte_near(charTidNul + k);
display.print(printChar);
}
display.println();
display.setTextSize(1);
display.println();
//display.println(" JA: Tryk igen ");
len = strlen_P(charJa);
for (int k = 0; k < len; k++) {
printChar = pgm_read_byte_near(charJa + k);
display.print(printChar);
}
display.println();
//display.println(" NEJ: Vent ");
len = strlen_P(charNej);
for (int k = 0; k < len; k++) {
printChar = pgm_read_byte_near(charNej + k);
display.print(printChar);
}
display.println();
display.display(); //Print to screen
for (int i = 0; i < 5000; i++) {
if (digitalRead(button1) == HIGH) {
RTC.adjust(DateTime(now.year(), now.month(), now.day(), now.hour(), 0, 0));
display.clearDisplay();
display.setCursor(6, 0);
display.setTextSize(2);
display.setTextColor(BLACK, WHITE);
//display.println(" Gem tid? ");
len = strlen_P(charGemTid);
for (int k = 0; k < len; k++) {
printChar = pgm_read_byte_near(charGemTid + k);
display.print(printChar);
}
display.println();
display.setTextColor(WHITE);
display.setTextSize(2);
display.println();
display.setTextSize(4);
display.setCursor(36, 24);
display.print("OK!");
display.display(); //Print to screen
delay(2500);
//setBackTime = false;
break;
}
delay(1);
}
}
void knap2() {
char printChar;
//int k;
int len;
display.clearDisplay();
display.setCursor(6, 0);
display.setTextSize(2);
display.setTextColor(BLACK, WHITE);
//display.println(" Gem tid? ");
len = strlen_P(charGemTid);
for (int k = 0; k < len; k++) {
printChar = pgm_read_byte_near(charGemTid + k);
display.print(printChar);
}
display.println();
display.setTextColor(WHITE);
display.setTextSize(1);
display.println();
display.print(" ");
display.setTextSize(2);
display.print(" ");
if (now.hour() < 10) display.print("0");
display.print(now.hour()+1, DEC);
//display.println(":00:00");
len = strlen_P(charTidNul);
for (int k = 0; k < len; k++) {
printChar = pgm_read_byte_near(charTidNul + k);
display.print(printChar);
}
display.println();
display.setTextSize(1);
display.println();
//display.println(" JA: Tryk igen ");
len = strlen_P(charJa);
for (int k = 0; k < len; k++) {
printChar = pgm_read_byte_near(charJa + k);
display.print(printChar);
}
display.println();
//display.println(" NEJ: Vent ");
len = strlen_P(charNej);
for (int k = 0; k < len; k++) {
printChar = pgm_read_byte_near(charNej + k);
display.print(printChar);
}
display.println();
display.display(); //Print to screen
for (int i = 0; i < 5000; i++) {
if (digitalRead(button2) == HIGH) {
RTC.adjust(DateTime(now.year(), now.month(), now.day(), now.hour()+1, 0, 0));
display.clearDisplay();
display.setCursor(6, 0);
display.setTextSize(2);
display.setTextColor(BLACK, WHITE);
//display.println(" Gem tid? ");
len = strlen_P(charGemTid);
for (int k = 0; k < len; k++) {
printChar = pgm_read_byte_near(charGemTid + k);
display.print(printChar);
}
display.println();
display.setTextColor(WHITE);
display.setTextSize(2);
display.println();
display.setTextSize(4);
display.setCursor(36, 24);
display.print("OK!");
display.display(); //Print to screen
delay(2500);
//setBackTime = false;
break;
}
delay(1);
}
}
void statusBlink() {
if (statusBlinkState == true) {
digitalWrite(statusBlinkPin, LOW);
statusBlinkState = false;
display.fillRect(60, 24, 4, 12, BLACK); //Fjern kolon i tid hvert interval
} else {
digitalWrite(statusBlinkPin, HIGH);
statusBlinkState = true;
}
}
void setRelays(int timeNow, int dayOfYear) {
if (manualOn == true) {
taendRelay(Relay1);
taendRelay(Relay2);
statusRelay1 = true;
statusRelay2 = true;
} else {
int solOp = pgm_read_word(&(soltid[dayOfYear][1]));
int solNed = pgm_read_word(&(soltid[dayOfYear][0]));
//Tjek om vi er ((over/lig solopgang) OG (under solnedgang)) ELLER ((over 00:15) OG (under 05:15))
if (((timeNow >= solOp) && (timeNow < solNed)) || ((timeNow >= 15) && (timeNow < 515))) {
//Sluk lys
slukRelay(Relay1);
slukRelay(Relay2);
statusRelay1 = false;
statusRelay2 = false;
} else {
//Tænd lys
taendRelay(Relay1);
slukRelay(Relay2);
statusRelay1 = true;
statusRelay2 = false;
}
}
}
void taendRelay(int R) {
digitalWrite(R, LOW);
}
void slukRelay(int R) {
digitalWrite(R, HIGH);
}
void printRelayStatus() {
display.setCursor(0, 0);
display.setTextSize(2);
if (manualOn == true) {
const long divider = 60000; //60000 ved 15 min, sæt til 1000 hvis 15 sek.
int timerLeft = ((intervalTimer - (currentMillis - previousTimerMillis)) / divider) + 1;
if (timerLeft > (intervalTimer / divider)) {
timerLeft = intervalTimer / divider;
}
display.setTextColor(BLACK, WHITE);
display.print(timerLeft);
//display.setTextColor(WHITE);
if (timerLeft > 9) {
//display.print(" ");
display.setCursor(100, 0);
} else display.setCursor(112, 0); //display.print(" ");
//display.setTextColor(BLACK, WHITE);
display.print(timerLeft);
//display.print((int)((intervalTimer - (currentMillis - previousTimerMillis)) / divider));
} else {
char printChar;
//int k;
int len;
//display.clearDisplay();
if (statusRelay1 == true) {
display.setTextColor(BLACK, WHITE);
len = strlen_P(charOn);
for (int k = 0; k < len; k++) {
printChar = pgm_read_byte_near(charOn + k);
display.print(printChar);
}
//display.print("ON");
display.setTextColor(WHITE);
display.print(" "); //Kan ikke spare noget ved PROGMEM
}
else {
display.setTextColor(WHITE);
len = strlen_P(charOff);
for (int k = 0; k < len; k++) {
printChar = pgm_read_byte_near(charOff + k);
display.print(printChar);
}
//display.print("OFF");
}
display.setCursor(92, 0);
display.setTextColor(WHITE);
if (statusRelay2 == true) {
display.setTextColor(WHITE);
display.print(" "); //Kan ikke spare noget ved PROGMEM
display.setTextColor(BLACK, WHITE);
//display.print("ON");
len = strlen_P(charOn);
for (int k = 0; k < len; k++) {
printChar = pgm_read_byte_near(charOn + k);
display.print(printChar);
}
}
else {
len = strlen_P(charOff);
for (int k = 0; k < len; k++) {
printChar = pgm_read_byte_near(charOff + k);
display.print(printChar);
}
//display.print("OFF");
}
}
}
void printStaticText() {
char printChar;
//int k;
int len;
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
len = strlen_P(charForan);
for (int k = 0; k < len; k++) {
printChar = pgm_read_byte_near(charForan + k);
display.print(printChar);
}
display.println();
//display.println(" <Foran");
len = strlen_P(charBag);
for (int k = 0; k < len; k++) {
printChar = pgm_read_byte_near(charBag + k);
display.print(printChar);
}
display.println();
//display.println(" Bag>");
}
void printTimeDate() {
display.setCursor(4, 16);
display.setTextSize(4);
display.setTextColor(WHITE);
if (now.hour() < 10) display.print(0, DEC);
display.print(now.hour(), DEC);
display.print(":");
if (now.minute() < 10) display.print(0, DEC);
display.println(now.minute(), DEC);
//display.setTextColor(WHITE);
display.setCursor(36, 48);
display.setTextSize(1);
if (now.day() < 10) display.print(0, DEC);
display.print(now.day(), DEC);
display.print("/");
if (now.month() < 10) display.print(0, DEC);
display.print(now.month(), DEC);
display.print("/");
display.println(now.year(), DEC);
}
void printSunUpDown() {
//Deklarer og sæt day of year til 0
int dagnr = 0;
//Udregn day of year og træk 1 fra, da array index starter på 0
dagnr = calculateDayOfYear(now.day(), now.month(), now.year()) - 1;
//Hent solop og -ned fra array
int sunUp = pgm_read_word(&(soltid[dagnr][1]));
int sunDown = pgm_read_word(&(soltid[dagnr][0]));
//display.setCursor(0, 64);
display.setTextSize(1);
display.print(" Op: ");
display.print(convertIntToTime(sunUp));
display.print(" Ned: ");
display.print(convertIntToTime(sunDown));
//if (digitalRead(button3) == HIGH) display.print("!");
}
String convertIntToTime(int intTime) {
String tempStreng = String(intTime).substring(0, String(intTime).length() - 2) + ":";
return tempStreng + String(intTime).substring(String(intTime).length() - 2);
}
int calculateDayOfYear(int day, int month, int year) {
// Given a day, month, and year (4 digit), returns
// the day of year. Errors return 999.
int daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// Verify we got a 4-digit year
if (year < 1000) {
return 999;
}
// Check if it is a leap year, this is confusing business
if (year % 4 == 0) {
if (year % 100 != 0) {
daysInMonth[1] = 29;
}
else {
if (year % 400 == 0) {
daysInMonth[1] = 29;
}
}
}
// Make sure we are on a valid day of the month
if (day < 1) {
return 999;
} else if (day > daysInMonth[month - 1]) {
return 999;
}
int doy = 0;
for (int i = 0; i < month - 1; i++) {
doy += daysInMonth[i];
}
doy += day;
return doy;
}