The reason for using the RTC is that both mains power and internet are not very stable. Lots of outings. With the RTC at least the time is kept and immediately available when power comes back.
In the code time is retrieved from the RTC which is updated each day. Also the timezone and summertime are adjusted.
Start times and filling time are in the sketch hard coded. This I want to change to input via 4x4 keypad with PCF8574.
I checked once again the I2C addresses and the PCF8574 is 0x20 whereas the LCD backpack has 0x57 and 0x64.
The LED stands for a Solid state relay.
//#include <Arduino.h>
// ESP WIFI Udp NTP Client
#include <millisDelay.h> // For pellets filling
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <RTClib.h>
#include <SPI.h>
#include <Timezone.h> // https://github.com/JChristensen/Timezone
#include <TimeLib.h> // Not used, only required for Timezone
#include <hd44780.h> // for LCD
#include <hd44780ioClass/hd44780_I2Cexp.h> // include i/o class header
// Initiate RTC
RTC_DS3231 rtc;
// A UDP instance to let us send and receive packets over UDP
WiFiUDP udp;
// Initiate LCD and define rows/columns
hd44780_I2Cexp lcd; // declare lcd object: auto locate & config display for hd44780 chip
#include <hd44780ioClass/hd44780_I2Cexp.h> // i2c expander i/o class header
#define LCD_COLS 16
#define LCD_ROWS 2
// Initiate millisDelay
millisDelay fillDelay; // the delay object for filling pellets
millisDelay syncDelay; // delay object for rtcSyncing
// Timezone rules need to before set-up in order to store with CE.writeRules(100); in void(setup).
// Central European Time (Frankfurt, Paris)
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; // Central European Summer Time
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60}; // Central European Standard Time
Timezone CE(CEST, CET);
TimeChangeRule *tcr; //pointer to the time change rule, use to get TZ abbrev
#ifndef STASSID
#define STASSID "xxx"
#define STAPSK "yyy"
#endif
const char * ssid = STASSID; // network SSID (name)
const char * pass = STAPSK; // network password
unsigned int localPort = 2390; // local port to listen for UDP packets
IPAddress timeServerIP; // pool.ntp.org NTP server address
const char* ntpServerName = "pool.ntp.org";
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
char daysOfTheWeek[7][5] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
time_t epoch;
// HARDCODED Start times according LOCAL time !!!!! =======
//=============================================================================
int startH1 = 9 ; // Start time Hours (24 clock) as H not HH
int startM1 = 0 ; // Start time Minutes as M not MM
int startH2 = 19 ;
int startM2 = 0 ;
const int fillPeriod = 15 ; //fillPeriod (input) in minutes as
//=============================================================================
// Define DELAY_TIME for millisDelay function
const unsigned long fillDelayTime = fillPeriod*1000*60L; // Running time pellet filling in milliseconds
const unsigned long syncDelayTime = 60*60*1000L; // sync RTC every 60 minutes
int startH1utc = 0; // Start time converted from LOCAL to utc (=RTC time)
int startH2utc = 0;
long diffTz = 0; // Difference in seconds UTC - LOCAL
time_t local = 0;
const int relayPin = D6; // Relay pin op WEMOS D6
// lcdClockDisplay=============================================
// ================================================================
void lcdClockDisplay(unsigned long local) {
char bufDate[17];
char bufTime[17];
sprintf(bufDate, "Date: %.4d/%.2d/%.2d", year(local), month(local), day(local));
lcd.clear(); lcd.setCursor(0, 0); lcd.print(bufDate);
sprintf(bufTime, "Time: %.2d:%.2d:%.2d", hour(local), minute(local), second(local));
lcd.setCursor(0, 1); lcd.print(bufTime);
}
// lcdPrintDigits=============================================
// ================================================================
void lcdPrintDigits(int digits) {
// utility function for lcd clock display: prints preceding colon and leading 0
lcd.print(":");
if (digits < 10)
lcd.print('0');
lcd.print(digits);
}
// FUNCTION fillPellets ==============================================
// ===================================================================
void F_fillPellets (int startHour, int startMinute) {
DateTime now = rtc.now();
if (now.hour() == startHour && now.minute() == startMinute && now.second() == 0) {
fillDelay.start(fillDelayTime);
digitalWrite(relayPin,HIGH);
Serial.print("Filling ON!");
lcd.setCursor(0,1);
lcd.print("Filling Pellets ");
}
if (fillDelay.justFinished()) { // don't combine this test with any other condition
// delay timed out
digitalWrite(relayPin, LOW); // turn led off
Serial.println("Filling finished.");
lcd.clear();
}
}
// FUNCTION startUtc adjust to utc ===============================
//=================================================================
int F_startUtc(int hr) {
int result;
// Calculate difference Local - utc
time_t utc = epoch;
time_t local = CE.toLocal(utc, &tcr);
if ((local - utc) != diffTz) {
diffTz = (local - utc);
}
// Calculate local hour to utc hour
// diffTz follows timezone (GMT + diffTz)
hr = hr - diffTz / 3600L;
if (hr > 23) {
hr = hr - 24;
}
else {
if (hr < 0) {
hr = hr + 24;
}
}
return result = hr;
}
// FUNCTION F_rtcPrint===============================
// =====================================================
void F_rtcPrint(){
DateTime now = rtc.now();
Serial.print(now.year(), DEC);
Serial.print('/');
Serial.print(now.month(), DEC);
Serial.print('/');
Serial.print(now.day(), DEC);
Serial.print(" (");
Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
Serial.print(") ");
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.println();
}
// FUNCTION obtainUTC ====================================
// ==========================================================
time_t obtainUTC() {
//get a random server from the pool
WiFi.hostByName(ntpServerName, timeServerIP);
sendNTPpacket(timeServerIP); // send an NTP packet to a time server
// wait to see if a reply is available
delay(1000);
int cb = udp.parsePacket();
if (!cb) {
//Serial.println("no packet yet");
}
else {
//Serial.print("packet received, length=");
//Serial.println(cb);
udp.read(packetBuffer, NTP_PACKET_SIZE);
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
unsigned long secsSince1900 = highWord << 16 | lowWord;
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
// subtract seventy years:
epoch = secsSince1900 - seventyYears;
}
return epoch;
}
// FUNCTION sendNTPpacket====================================
// ==========================================================
// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress& address) {
//Serial.println("sending NTP packet...");
// 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();
}
//================================================================
void setup() {
Serial.begin(115200);
lcd.begin(LCD_COLS, LCD_ROWS); // Initiate LCD, ROWS and Columns
pinMode(relayPin,OUTPUT);
digitalWrite(relayPin,LOW);
// Connecting to a WiFi network
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, pass);
// LCD message
lcd.clear(); lcd.setCursor(0,0);
lcd.print("Connecting WIFI");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
// LCD message
lcd.setCursor(0,1);lcd.print("............");
}
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
// LCD message
lcd.clear();
lcd.setCursor(0,0);lcd.print("WIFI connected");
lcd.setCursor(0,1);lcd.print(WiFi.localIP());
delay(3000);
Serial.println("Starting UDP");
udp.begin(localPort);
Serial.print("Local port: ");
Serial.println(udp.localPort());
// Check RTC connection
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
// LCD message
lcd.clear();
lcd.setCursor(0,0);lcd.print("Not found RTC");
lcd.setCursor(0,1);lcd.print("Check battery?");
Serial.flush();
while (1) delay(10);
}
if (rtc.lostPower()) {
Serial.println("RTC lost power, let's set the time!");
epoch = obtainUTC();
rtc.adjust(epoch);
F_rtcPrint();
}
Serial.println("Adjust RTC to NTP");
epoch = obtainUTC();
rtc.adjust(epoch);
F_rtcPrint();
// Start syncDelay
syncDelay.start(syncDelayTime);
}
//====================================================================
void loop() {
// Repeating timer delay for rtc syncing
if (syncDelay.justFinished()){
syncDelay.repeat();
epoch = obtainUTC();
rtc.adjust(epoch);
Serial.println("RTC synced");
}
// Display LOCAL time on LCD
lcdClockDisplay(rtc.now().unixtime() + diffTz);
// Display RTC time on Serial port
F_rtcPrint();
// Functions for pellet filling
startH1utc = F_startUtc(startH1);
F_fillPellets(startH1utc,startM1);
startH2utc = F_startUtc(startH2);
F_fillPellets(startH2utc,startM2);
delay(1000);
}