// ***DESCRIPTION, HOW TO USE AND CREDITS***
// V1.2 - added DST mode on / off indicator on rightmost decimal point of display.
// Full instructions can be found at https://adrian-smith31.co.uk/blog
// This program will create an access point to configure wifi. SSID "inetclock", password is "password"
// Connect to this AP with an Android or IOS phone and sign in to the network. Config box will appear.
// The clock will do a display test and display dashes to indicate it is connecting to the WiFi network.
// Pressing the function button will toggle daylight saving time on and off.
// Uses open source WiFi manager for easy wireless configuration by Tzapu https://github.com/tzapu
// Rest of libraries part of ESP8266 Arduino repository and installed by library manager in Arduino IDE.
// Written for NodeMCU version 1.0 on ESP-12E V3 module. Should work with others, pin assignments may need changing.
// Settings: CPU clock 80Mhz, flash size 4Mb, V2 lower memory, Vtables=flash, erase flash: only sketch, legacy
// ***TROUBLESHOOTING & PROBLEMS***
// Please use a terminal program such as terraterm to monitor serial output. Any error messages
// will be shown such as HTTP status codes plus any debug messages from the NodeMCU RTOS and / or debugger.
// Serial settings: 115200 baud, 8 bits, no parity, one stop bit. First line will show garbage, that's normal.
// If there is a problem connecting to the internet or WiFi config isn't set, display will show --HELP--
// use ESP8266 community V2.6.0 and change stack size to #define _stackSize (6748/4)
// in stackthunk.cpp in the ESP8266 core files otherwise it just crashes with later
// versions of the libraries. Stackthunk.cpp is in local appdata/arduinoxx where xx version no
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager
#include <ESP8266WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <WiFiClientSecure.h> // for connecting to SSL sites
#include <EEPROM.h>
#include <ArduinoJson.h>
#include <stackthunk.h>
#include <ICM7218.h> // library also works with the ICM7228 with limited functions. Enough for this though.
#include <RTClib.h>
#include <Time.h>
// Configure the 10 OUTPUT pins used to interface with ICM7218: D0-D7, mode, write
ICM7218 myLED(D1, D2, D5, D6, D4, ICM7218::NO_PIN, ICM7218::NO_PIN, D3, D7, D8);
// Pins ID5 and ID6 are not used. Hardwired to CODEB decode mode.
// Accepts numbers 0-9, characters H,E,L,P,-,space and full stop. Full stop lights decimal point.
// Pin numbers not in sequence as some have pullups or specific functions at boot. Onboard LED will
// light when display is in normal operation.
// CODEB character mode: supports digits 0-9, characters 'H', 'E', 'L', 'P',
// space (' ') and hyphen ('-') plus decimal points
//ESP8266WebServer server(80); // configure built in web server to port 80
WiFiClientSecure client;
int buttonState; // stores state of the DST adjust button
long utcOffsetInSeconds = 0; // change this if this gets sold outside of the UK. 3600 seconds per hour.
int timeset = 0;
int dst = 1; // inital high state so DST is off. Button goes low to turn it on.
int tempdisplay = 0; // flag to indicate in temperature display mode or not
int dstset = 0;
int tempc;
byte mode = 0;
const int buttonPin = D0;
const int shortPresstime = 500; // half a second
const int longPresstime = 2000; // 2 seconds
int lastBtnstate = 0;
unsigned long btnPressedtime = 0;
unsigned long btnReleasedtime = 0;
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
// Define NTP Client to get time
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", utcOffsetInSeconds); // a more local NTP server may work better
RTC_Millis rtc; // setup software RTC using CPU clock as timebase. This gets autocorrected every hour.
void setup()
{
pinMode(buttonPin, INPUT); // Pin D0 only has a pulldown resistor, not pullup. Use external pullup instead.
myLED.setMode(ICM7218::CODEB);
myLED.print("8.8.8.8.8.8.8.8."); // do a display test at power up
delay(1000);
myLED.print("--------"); // indicate a dash to show it's trying to connect to the internet
WiFi.hostname("Inetclock1049"); // set up hostname consisting of Inetclock and last 4 digits of MAC address
WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP
Serial.begin(115200);
WiFiManager wm;
wm.setAPCallback(configModeCallback); // this runs if it can't connect to the internet. Function for this below.
timeClient.begin(); // start NTP client
//reset settings - wipe network config (for testing purposes only)
//wm.resetSettings();
// Automatically connect using saved credentials,
// if connection fails, it starts an access point with the specified name ( "YTcounterAP"),
// if empty will auto generate SSID, if password is blank it will be anonymous AP (wm.autoConnect())
// then goes into a blocking loop awaiting configuration and will return success result
bool res;
res = wm.autoConnect("inetclock","password"); // password protected ap
Serial.print("*NET: IP address: ");
Serial.println(WiFi.localIP());
char IP[12];
String LocalIP = String() + "1P" + "." + WiFi.localIP()[2] + "." + WiFi.localIP()[3]; // display last two octets of IP address
strcpy(IP, LocalIP.c_str());
myLED.print(IP);
delay(3000);
client.setInsecure(); // required for version 2.5 and above
timesync(); // sync software RTC with NTP clock at boot.
}
void loop()
{
//timedisplay();
buttoncheck();
if (tempdisplay == 1)
{
displaytemp();
}
else
{
timedisplay();
}
DateTime now = rtc.now();
if (now.minute() == 0 && now.second() == 0 && timeset == 0)
{
timesync(); // sync the time on the hour. Avoids repeated NTP calls & possibly getting IP banned by NTP service.
timeset = 1; // this prevents timesync running over and over till zero second has passed.
Serial.println(F("NTP server sync run. If time still incorrect, power cycle unit or NTP server is down"));
}
if (now.minute() == 10 && now.second() == 0 && timeset == 1)
{
timeset = 0; // reset timeset flag after 10 mins so time will update again on the next hour
}
}
//===============================================================
// This function runs if it can't connect to wifi network
//===============================================================
void configModeCallback (WiFiManager *myWiFiManager)
{
Serial.println("Entered config mode");
Serial.println(WiFi.softAPIP());
Serial.println(myWiFiManager->getConfigPortalSSID());
myLED.print("--HELP--");
}
//===============================================================
// This function monitors the button & selects data to display
//===============================================================
void buttoncheck()
{
buttonState = digitalRead(buttonPin);
if (lastBtnstate == 1 && buttonState == 0) // button pressed
btnPressedtime = millis();
else if (lastBtnstate == 0 && buttonState == 1) // button released
{
btnReleasedtime = millis();
long btnPressduration = btnReleasedtime - btnPressedtime;
if (btnPressduration < shortPresstime)
dst = !dst; // toggle DST mode
//tempdisplay = !tempdisplay;
if (btnPressduration > longPresstime)
dst = !dst; // toggle DST mode
}
lastBtnstate = buttonState;
}
//===============================================================
// This function syncronises the software RTC with NTP time
//===============================================================
void timesync()
{
timeClient.update();
DateTime now = rtc.now();
int hr,mins,sec,days;
sec = timeClient.getSeconds();
mins = timeClient.getMinutes();
hr = timeClient.getHours();
days = timeClient.getDay();
rtc.adjust(DateTime(2021,1,1,hr,mins,sec)); // set the time (date doesn't really matter)
}
//===============================================================
// This function displays the temperature from an analog sensor
//===============================================================
void displaytemp()
{
tempc = analogRead(A0) * 3300/1024;
Serial.printf("Temperature = %02u.%1uC\r\n", tempc / 10, tempc % 10);
char tempcdata[8];
char temp[8];
itoa(tempc /10, temp, 10); // get first digit, convert integer to char and put in temp array
strcpy(tempcdata, temp); // copy temp array to tempcdata array
strcat(tempcdata, "."); // insert decimal point
itoa(tempc %10, temp, 10); // get second digit and convert to char then put in temp array
strcat(tempcdata, temp); // append to temp array
myLED.print(tempcdata); // write to the ICM7228
delay(100);
}
//===============================================================
// This function displays the time with leading zero correction
//===============================================================
void timedisplay() // if / else statements ahoy! A mess. But it works...
{
DateTime now = rtc.now();
char timedata[10];
char temp[8];
if (dst == 1 && dstset == 0)
{
Serial.println("DST ON");
dstset = 1;
}
else if (dst == 0 && dstset == 1 )
{
Serial.println("DST OFF");
dstset = 0;
}
if (now.hour() <10)
{
if (dstset == 1 && (now.hour() == 9)) // do not add 0 if hour +1 is 10
{
itoa(now.hour()+1, temp, 10); // convert integer output from RTC, add 1 then store in char temp array
strcpy(timedata, temp); // copy to timedata array
strcat(timedata, "-"); // append to timedata array
}
else if (dstset == 1) // only add leading zero if hour +1 is 9 or less
{
itoa(0, temp, 10); // convert integer 0 to char and put in temp array
strcpy(timedata, temp); // copy temp array to timedata array
itoa(now.hour()+1, temp, 10); // convert integer output from RTC, add 1 then store in char temp array
strcat(timedata, temp); // append to timedata array
strcat(timedata, "-"); // append to timedata array
}
else
{
itoa(0, temp, 10);
strcpy(timedata, temp);
itoa(now.hour(), temp, 10); // convert int output from RTC, DO NOT add 1 then store in char temp array
strcat(timedata, temp);
strcat(timedata, "-");
}
}
else
{
if (dstset == 1)
{
// this replaces 24 with 00 at midnight if DST is on as it adds one hour to whatever now.hour() is.
if (now.hour() == 23)
{
itoa(0, temp, 10);
strcpy(timedata, temp);
strcat(timedata, "0");
strcat(timedata, "-");
}
else
{
itoa(now.hour()+1, temp, 10);
strcpy(timedata, temp);
strcat(timedata, "-");
}
}
else
{
itoa(now.hour(), temp, 10);
strcpy(timedata, temp);
strcat(timedata, "-");
}
}
if (now.minute() <10)
{
strcat(timedata, "0");
itoa(now.minute(), temp, 10);
strcat(timedata, temp);
strcat(timedata, "-");
}
else
{
itoa(now.minute(), temp, 10);
strcat(timedata, temp);
strcat(timedata, "-");
}
if (now.second() <10)
{
strcat(timedata, "0");
itoa(now.second(), temp, 10);
if (dstset == 1)
{
strcat(timedata, temp);
strcat(timedata, ".");
}
else
{
strcat(timedata, temp);
}
}
else
{
itoa(now.second(), temp, 10);
if (dstset == 1)
{
strcat(timedata, temp);
strcat(timedata, ".");
}
else
{
strcat(timedata, temp);
}
}
myLED.print(timedata);
delay(10); // 10ms delay to allow ICM7228 to update. RTC keeps going whilst function is delayed
}
Comments