Clocks Suck
At least, human readable clocks do. There’s so many edge cases to account for and I haven’t even gotten into automatically adjusting my time zone for daylight savings…
Regardless, here’s the code I rapidly tossed together in an impromptu code sprint last night. It includes the following features so far:
- WiFi connectivity.
- RTC Synchronization over WiFi.
- Output to an OLED via SPI.
- Multiple displays/application states.
- Input via a rotary encoder with rotation and button presses.
My next goals are as follows:
- Decide on a battery solution to make the setup portable.
- Build out the multiple application states to make them more robust and useful.
- I should be able to manage this better by setting a minimum and maximum screen state, equal to the number of possible display states then having the rotation of the encoder be obfuscated as “left” or “right” and incrementing the screen state better.
- Solve some more niche time keeping issues, like DST.
//Load the libraries and files we need..
#include <SPI.h>
#include <Wire.h>
#include <Encoder.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
#include <WiFi101.h>
#include <WiFiUdp.h>
#include <DHT.h>
#include <RTCZero.h>
RTCZero rtc;
#include "arduino_secrets.h"
#include "bitmaps.h"
//Declare all of our variables..
//Everything used for the rotary encoder:
Encoder myEnc(1, 0);
#define SW 2
unsigned long lastButtonPress = 0;
//Everything Used for Screen
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_3V 7
#define OLED_CS 8
#define OLED_DC 9
#define OLED_RESET 10
#define OLED_MOSI 11
#define OLED_CLK 12
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT,
OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);
//Everything used for DHT22 Temp/Humidty Sensor:
#define DHTPIN 3 // what pin we're connected to
#define DHTTYPE DHT22 // DHT 22 (AM2302)
DHT dht(DHTPIN, DHTTYPE);
//RTC Test Variables:
/* Change these values to set the current initial time */
const byte seconds = 1;
const byte minutes = 1;
const byte hours = 1;
int rssi = WiFi.RSSI();
int status = WL_IDLE_STATUS;
int year;
int month;
int day;
int screen = 1;
int line1 = 0;
int line2 = 8;
int line3 = 16;
int line4 = 24;
int line5 = 32;
int line6 = 40;
int line7 = 48;
int line8 = 56;
int artificialDelay = 2000;
unsigned long lastEpoch;
unsigned long hcurEpoch;
unsigned long tcurEpoch;
unsigned long bootTime;
float h;
float t;
//int hours;
//int minutes;
//int seconds;
//Needed for encoder:
long oldPosition = 100;
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID; // your network SSID (name)
char pass[] = SECRET_PASS; // your network password (use for WPA, or use as key for WEP)
int keyIndex = 0; // your network key Index number (needed only for WEP)
//Set the local timezone offset from GMT
// Eastern Standard Time (EST) = GMT-5
//Eastern Daylight Time (EDT) = GMT-4
const int TIMEZONE = -4;
void setup() {
pinMode(SW, INPUT_PULLUP);
pinMode(OLED_3V, OUTPUT);
digitalWrite(OLED_3V, HIGH);
Serial.begin(115200);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextSize(1.5);
display.setTextColor(SSD1306_WHITE);
display.setCursor(8, line1);
display.println("Cyborg Augmentation");
display.setCursor(48, line2);
display.println("v0.7");
display.setTextSize(1);
display.setCursor(32, line4);
display.print("Booting...");
display.display();
dht.begin();
delay(artificialDelay);
// attempt to connect to WiFi network:
while (status != WL_CONNECTED) {
display.setCursor(12, line6);
display.print("WiFi Connecting...");
display.display();
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
// you're connected now, so print out the status:
rtc.begin();
unsigned long epoch;
int numberOfTries = 0, maxTries = 20;
do {
display.setCursor(0, line8);
display.print("Synchronizing Epoch..");
epoch = WiFi.getTime();
bootTime = epoch;
numberOfTries++;
display.display();
delay(artificialDelay);
}
while ((epoch == 0) && (numberOfTries < maxTries));
if (numberOfTries == maxTries) {
Serial.print("NTP unreachable!!");
while (1)
;
}
else {
Serial.print("Epoch received: ");
Serial.println(epoch);
rtc.setEpoch(epoch);
Serial.println();
}
}
int monthDays(int y, int m) {
// Fourth, eleventh, ninth, and sixth,
// thirty days to each we fix.
if ((m == 4) || (m == 11) || (m == 9) || (m == 6)) return 30;
// Every other, thirty-one,
// except the second month alone,
if (m != 2) return 31;
// which hath twenty-eight, in fine,
// till leap-year give it twenty-nine.
if ((y % 400) == 0) return 29; // leap year
if ((y % 100) == 0) return 28; // not a leap year
if ((y % 4) == 0) return 29; // leap year
return 28; // not a leap year
}
void print2digits(int number) {
if (number < 10) {
display.print("0"); // print a 0 before if the number is < than 10
}
display.print(number);
}
void printWifi() {
//Print the current WiFi Status:
display.setCursor(0, 0);
display.print("WiFi State:");
display.println(WiFi.status());
rssi = WiFi.RSSI();
if (rssi == -100) {
rssi = 0;
}
display.setTextColor(SSD1306_WHITE);
display.setCursor(76, 0);
display.setTextSize(1);
display.print(rssi);
display.println("dBm");
if (rssi >= -55 && rssi < 0) {
display.drawBitmap(112, 0, wifiSymbolExcellent, 16, 8, SSD1306_WHITE);
}
if (rssi < -55 && rssi >= -67) {
display.drawBitmap(112, 0, wifiSymbolGood, 16, 8, SSD1306_WHITE);
}
if (rssi < -67 && rssi >= -90) {
display.drawBitmap(112, 0, wifiSymbolBad, 16, 8, SSD1306_WHITE);
}
if (rssi < -90 || rssi == 0) {
display.drawBitmap(112, 0, wifiSymbolNone, 16, 8, SSD1306_WHITE);
}
}
void printTime() {
int localHours = rtc.getHours() + TIMEZONE; // add timezone offset
int minutes = rtc.getMinutes();
int seconds = rtc.getSeconds();
if (localHours < 0) { // handle negative values
localHours += 24;
} else if (localHours >= 24) { // handle values over 23
localHours -= 24;
}
display.print("Local Time: ");
if (localHours > 12) {
print2digits(localHours - 12);
} else {
print2digits(localHours);
}
display.print(":");
print2digits(rtc.getMinutes());
display.print(":");
print2digits(rtc.getSeconds());
if (localHours < 12) {
display.println("am");
} else {
display.println("pm");
}
}
void printDate() {
year = rtc.getYear();
month = rtc.getMonth();
if (rtc.getHours() > 24 - TIMEZONE) {
day = (rtc.getDay() - 1);
} else {
day = rtc.getDay();
}
display.print("Local Date: ");
print2digits(month);
display.print("/");
if (rtc.getHours() > 24 - TIMEZONE && rtc.getDay() > 1) {
print2digits(day - 1);
}
if (rtc.getHours() + TIMEZONE < 0 && rtc.getDay() == 1 && rtc.getMonth() > 1) {
print2digits(monthDays(rtc.getYear(), rtc.getMonth() - 1));
}
if (rtc.getHours() + TIMEZONE < 0 && rtc.getDay() == 1 && rtc.getMonth() == 1) {
print2digits(31);
} else {
print2digits(day);
}
display.print("/");
print2digits(year);
display.println(" ");
}
void printHumid() {
hcurEpoch = rtc.getEpoch();
if (hcurEpoch >= lastEpoch + 10) {
display.setCursor(0, line8);
display.println("Reading DHT...");
float h = dht.readHumidity();
lastEpoch = hcurEpoch;
}
display.setCursor(0, line6);
display.print("Humid:");
display.println(h);
// Check if any reads failed and exit early (to try again).
if (isnan(h)) {
display.setCursor(0, line8);
display.println("DHT sensor Failed!");
return;
}
}
void printTemp() {
tcurEpoch = rtc.getEpoch();
if (tcurEpoch >= lastEpoch + 10) {
display.setCursor(0, line8);
display.println("Reading DHT...");
float t = dht.readTemperature();
lastEpoch = tcurEpoch;
}
display.setCursor(72, line6);
display.print("Temp:");
display.println(t);
// Check if any reads failed and exit early (to try again).
if (isnan(t)) {
display.setCursor(0, line8);
display.println("DHT sensor Failed!");
return;
}
}
void printBoot() {
display.print("Runtime:");
display.println(rtc.getEpoch() - bootTime);
}
void manageWiFi() {
display.setCursor(8, 40);
if (WiFi.status() == 3) {
WiFi.end();
display.println("WiFi Disconnected");
} else if (WiFi.status() == 0) {
WiFi.begin(ssid, pass);
display.println("WiFi Connecting");
}
display.display();
}
//Do the cool stuff:
void loop() {
if (screen == 1) {
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, line1);
printWifi();
display.setCursor(0, line3);
printTime();
display.setCursor(0, line4);
printDate();
display.setCursor(0, line6);
printHumid();
printTemp();
display.display();
}
if (screen == 2) {
display.clearDisplay();
display.setCursor(0, line1);
display.print("Current RTC Epoch");
display.setCursor(0, line2);
display.print(rtc.getEpoch());
display.setCursor(0, line8);
printBoot();
display.display();
}
//Rotary Loop:
long newPosition = myEnc.read();
if (newPosition > oldPosition) {
oldPosition = newPosition;
screen = 2;
Serial.print("screen:");
Serial.println(screen);
}
if (newPosition < oldPosition) {
oldPosition = newPosition;
screen = 1;
Serial.print("screen:");
Serial.println(screen);
}
int btnState = digitalRead(SW);
if (btnState == LOW) {
//if 50ms have passed since last LOW pulse, it means that the
//button has been pressed, released and pressed again
if (millis() - lastButtonPress > 50) {
manageWiFi();
}
// Remember last button press event
lastButtonPress = millis();
}
}