//Includes #include #include #include #include #include //Constants const unsigned int timeZone = 10; const unsigned int NTPTimeout = 5000; const unsigned int earthtoolsTimeout = 5000; unsigned int lcdPin = 3; unsigned int ledPin = 5; const unsigned int lcdNightBrightness = 10; const unsigned int lcdDayBrightness = 255; const unsigned int ledNightBrightness = 0; const unsigned int ledDayBrightness = 255; const String latitude = "-33.909508"; const String longitude = "151.22162"; const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0xE4, 0x0B }; unsigned int localPort = 8888; IPAddress timeServer(203, 0, 178, 191); byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets IPAddress myIP(10, 0, 36, 200); // fallback local IP (no DHCP) const char serverName[] = "www.earthtools.org"; const int timeOffset = 0; //minutes const int sunriseDelay = 2; //hours const int sunsetDelay = 5; //hours //Variables int sunDate = 0; int syncDate = 0; int ledBrightness = ledNightBrightness; int lcdBrightness = lcdNightBrightness; time_t dawn; time_t sunrise; time_t sunset; time_t twilight; String dawnText; String sunriseText; String sunsetText; String twilightText; unsigned int lastAttemptTime = 0; int sunStatus = 0; // 1: down. 2: rising. 3: up. 4: setting. int prevSunStatus = 0; int prevLcdBrightness = -1; int prevLedBrightness = -1; long timeNowOffset = 0; int secondsDisplayed = 0; //Characters byte lamp[8] = { B01110, B10001, B10001, B10001, B01110, B01110, B00100, }; byte up[8] = { B00100, B10101, B01110, B11111, B01110, B10101, B00100, }; byte down[8] = { B01100, B00110, B00011, B00011, B00011, B00110, B01100, }; byte rising[8] = { B00000, B00111, B00011, B00101, B01000, B10000, B00000, }; byte setting[8] = { B00000, B10000, B01000, B00101, B00011, B00111, B00000, }; byte logo[8] = { B00000, B00000, B01000, B11010, B11010, B00010, B00000, }; //Initialisation LiquidCrystal lcd(8, 9, 4, 2, 6, 7); EthernetClient earthtools; EthernetUDP Udp; void setup(){ //Initialise Hardware Serial.begin(9600); Serial.println("_______________________________"); Serial.println("Start serial (setup())"); lcd.begin(16, 2); pinMode (ledPin, OUTPUT); pinMode (lcdPin, OUTPUT); analogWrite(ledPin, ledNightBrightness); analogWrite(lcdPin, lcdNightBrightness); lcd.createChar(0, lamp); lcd.createChar(1, down); lcd.createChar(2, rising); lcd.createChar(3, up); lcd.createChar(4, setting); lcd.createChar(5, logo); lcd.setCursor(8,0); lcd.write(byte(5)); lcd.setCursor(0,1); lcd.print("DHCP... "); Serial.println("Attempting to get an IP address using DHCP:"); if (!Ethernet.begin(mac)) { Serial.println("failed to get an IP address using DHCP, trying manually"); lcd.print("Manual"); Ethernet.begin(mac, myIP); } else { Serial.println("DHCP Success"); lcd.print("ok"); delay(1000); } Serial.print("IP Address: "); lcd.setCursor(0,1); lcd.print("IP: "); Serial.println(Ethernet.localIP()); lcd.print(Ethernet.localIP()); delay(1000); Udp.begin(localPort); Serial.print("Starting NTP sync... "); setSyncProvider(getNtpTime); setSyncInterval(43200); Serial.println("End setup()"); lcd.clear(); } void loop(){ if(timeStatus() != timeNotSet ){ //wait until you know what time it is. timeNowOffset = now() + (timeOffset * 60); if(sunDate == day()) { calculateBrightness(); updateLED(); updateLCD(); } else { getSuntimes(); } } else { lcd.clear(); lcd.print("no clock sync..."); getNtpTime(); //get the time if you don't know what time it is. delay(500); } } void getSuntimes() { connectToEarthtools(); while (!earthtools.available()) { Serial.println("Waiting for Earthtools..."); delay(500); } Serial.println("Looking for Sun Times..."); if(earthtools.find("")) { sunriseText = ""; while (!sunriseText.endsWith("<")) { char inChar = earthtools.read(); sunriseText += inChar; } sunriseText.replace("<", ""); sunrise = formatTime(sunriseText, sunriseDelay); Serial.println("Sunrise: " + sunriseText); } if(earthtools.find("")) { dawnText = ""; while (!dawnText.endsWith("<")) { char inChar = earthtools.read(); dawnText += inChar; } dawnText.replace("<", ""); dawn = formatTime(dawnText, sunriseDelay); Serial.println("Dawn: " + dawnText); } if(earthtools.find("")) { sunsetText = ""; while (!sunsetText.endsWith("<")) { char inChar = earthtools.read(); sunsetText += inChar; } sunsetText.replace("<", ""); sunset = formatTime(sunsetText, sunsetDelay); Serial.println("Sunset: " + sunsetText); } if(earthtools.find("")) { twilightText = ""; while (!twilightText.endsWith("<")) { char inChar = earthtools.read(); twilightText += inChar; } twilightText.replace("<", ""); twilight = formatTime(twilightText, sunsetDelay); Serial.println("Twilight: " + twilightText); } Serial.println("Sun times OK at " + String(hour()) + ":" + String(minute()) + ":" + String(second())); earthtools.flush(); earthtools.stop(); sunDate = day(); } time_t formatTime(String timeIn, int offset){ time_t timeout; TimeElements tempTime; String event_hour = timeIn.substring( 0 , timeIn.indexOf( ":" ) ); String event_minute = timeIn.substring( timeIn.indexOf(":")+1, timeIn.indexOf( ":", timeIn.indexOf(":")+1 ) ); String event_second = timeIn.substring( timeIn.indexOf(":", timeIn.indexOf(":")+1)+1); tempTime.Second = event_second.toInt(); tempTime.Minute = event_minute.toInt(); tempTime.Hour = event_hour.toInt() + offset; tempTime.Day = day(); tempTime.Month = month(); tempTime.Year = year(); tempTime.Year = tempTime.Year - 1970; timeout = makeTime(tempTime); return(timeout); } void connectToEarthtools() { Serial.print("Connecting to Earthtools... "); if (earthtools.connect(serverName, 80)) { earthtools.println("GET /sun-1.0/" + latitude + "/" + longitude + "/" + String(day()) + "/" + String(month()) + "/" + timeZone + "/0 HTTP/1.1"); earthtools.println("HOST: " + String(serverName)); earthtools.println("Connection: close"); earthtools.println(); Serial.println("Connected to Earthtools"); } } void calculateBrightness() { if (timeNowOffset >= dawn && timeNowOffset < sunrise) { // Sun is Rising ledBrightness = map(timeNowOffset, dawn, sunrise, ledNightBrightness, ledDayBrightness); lcdBrightness = map(timeNowOffset, dawn, sunrise, lcdNightBrightness, lcdDayBrightness); sunStatus = 2; } else if (timeNowOffset >= sunrise && timeNowOffset < sunset) { // Sun is Up ledBrightness = ledDayBrightness; lcdBrightness = lcdDayBrightness; sunStatus = 3; } else if (timeNowOffset >= sunset && timeNowOffset < twilight) { // Sun is Setting ledBrightness = map(timeNowOffset, sunset, twilight, ledDayBrightness, ledNightBrightness); lcdBrightness = map(timeNowOffset, sunset, twilight, lcdDayBrightness, lcdNightBrightness); sunStatus = 4; } else { // Sun is Down ledBrightness = ledNightBrightness; lcdBrightness = lcdNightBrightness; sunStatus = 1; } } void updateLED(){ analogWrite(ledPin, ledBrightness); } void updateLCD(){ analogWrite(lcdPin, lcdBrightness); if (prevSunStatus != sunStatus) { Serial.println("Sun is " + String(sunStatus) + " (1: down. 2: rising. 3: up. 4: setting)"); lcd.setCursor(12,0); lcd.write(byte(0)); //lamp icon lcd.setCursor(10,0); lcd.write(byte(sunStatus)); //Sun state icon prevSunStatus = sunStatus; } if (prevLedBrightness != ledBrightness) { lcd.setCursor(13,0); if (ledBrightness < 10) { lcd.print("0"); } if (ledBrightness < 100) { lcd.print("0"); } lcd.print(ledBrightness); prevLedBrightness = ledBrightness; } if (secondsDisplayed != second()) { lcd.setCursor(0, 0); lcd.print(hour()); printDigits(minute()); printDigits(second()); if(hour()<10) { lcd.print(" "); } secondsDisplayed = second(); switch (sunStatus) { //1: down. 2: rising. 3: up. 4: setting. case 1: //down lcd.setCursor(0,1); lcd.write(byte(4)); //setting icon lcd.setCursor(2,1); lcd.print(String(hour(twilight)) + ":" + String(minute(twilight)) + " "); lcd.setCursor(9,1); lcd.write(byte(2)); //rising icon lcd.setCursor(11,1); lcd.print(String(hour(dawn)) + ":" + String(minute(dawn)) + " "); break; case 2: //rising lcd.setCursor(0,1); lcd.write(byte(1)); //down icon lcd.setCursor(2,1); lcd.print(String(hour(dawn)) + ":" + String(minute(dawn)) + " "); lcd.setCursor(9,1); lcd.write(byte(3)); //up icon lcd.setCursor(11,1); lcd.print(String(hour(sunrise)) + ":" + String(minute(sunrise)) + " "); break; case 3: //up lcd.setCursor(0,1); lcd.write(byte(2)); //rising icon lcd.setCursor(2,1); lcd.print(String(hour(sunrise)) + ":" + String(minute(sunrise)) + " "); lcd.setCursor(9,1); lcd.write(byte(4)); //setting icon lcd.setCursor(11,1); lcd.print(String(hour(sunset)) + ":" + String(minute(sunset)) + " "); break; case 4: //setting lcd.setCursor(0,1); lcd.write(byte(3)); //day icon lcd.setCursor(2,1); lcd.print(String(hour(sunset)) + ":" + String(minute(sunset)) + " "); lcd.setCursor(9,1); lcd.write(byte(1)); //night icon lcd.setCursor(11,1); lcd.print(String(hour(twilight)) + ":" + String(minute(twilight)) + " "); break; } } } void printDigits(int digits){ lcd.print(":"); if(digits < 10) lcd.print('0'); lcd.print(digits); } time_t getNtpTime() { while (Udp.parsePacket() > 0) ; // discard any previously received packets Serial.print("Transmitting NTP Request... "); sendNTPpacket(timeServer); uint32_t beginWait = millis(); while (millis() - beginWait < 1500) { int size = Udp.parsePacket(); if (size >= NTP_PACKET_SIZE) { Serial.println("NTP synced"); Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer unsigned long secsSince1900; // convert four bytes starting at location 40 to a long integer secsSince1900 = (unsigned long)packetBuffer[40] << 24; secsSince1900 |= (unsigned long)packetBuffer[41] << 16; secsSince1900 |= (unsigned long)packetBuffer[42] << 8; secsSince1900 |= (unsigned long)packetBuffer[43]; return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR; } } Serial.println("NTP sync failed."); return 0; // return 0 if unable to get the time } // send an NTP request to the time server at the given address void sendNTPpacket(IPAddress &address) { // set all bytes in the buffer to 0 memset(packetBuffer, 0, NTP_PACKET_SIZE); // Initialize values needed to form NTP request // (see URL above for details on the packets) packetBuffer[0] = 0b11100011; // LI, Version, Mode packetBuffer[1] = 0; // Stratum, or type of clock packetBuffer[2] = 6; // Polling Interval packetBuffer[3] = 0xEC; // Peer Clock Precision // 8 bytes of zero for Root Delay & Root Dispersion packetBuffer[12] = 49; packetBuffer[13] = 0x4E; packetBuffer[14] = 49; packetBuffer[15] = 52; // all NTP fields have been given values, now // you can send a packet requesting a timestamp: Udp.beginPacket(address, 123); //NTP requests are to port 123 Udp.write(packetBuffer, NTP_PACKET_SIZE); Udp.endPacket(); }