// How to use push button
// Slow double click accesses the settings
// Single click moves down the menu
// Holding down the button will select the option.
//oled library
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
//RTC Address
#define DS1307_ADDRESS 0x68 //Needed to prevent conflict with i2c display
//oled hardware defs
#define Screen_Width 128
#define Screen_Height 64
#define Screen_Reset - 1
Adafruit_SSD1306 display(Screen_Width, Screen_Height, & Wire, Screen_Reset);
//Pi definition for calculations
float pi = 3.14; //if the resolution of pi is too high the 45 and 15 minute alignment is wonky
//Time array declared global
int timearray[8];
//declare clock vars for convience
int clockcentreX = (display.width() / 4);
int clockcentreY = (display.height() / 2);
int clockradius = (display.height() - 1) / 2;
//dayarray array for weekday names
// patched, wiretransmission messed up and array messed up too, patched
String dayNames[] = {
"",
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
};
//Hardware button
#include "OneButton.h"
#define PIN_INPUT 3
OneButton button(PIN_INPUT, true);
// records milis var at start of long press for later comparison
unsigned long pressStartTime;
int displaystate = 0; //0 for clock, 1 for settings, etc
int selection = 1; //memory for user selection
int customselection = 0;
int Alarmhours = 0;
int Alarmmints = 0;
char alarmtime[6];
bool alarmMode = false;
bool SpeakerOnAndLoud = false;
#define BUZZER 13
float beta = 4000; //temp function
//time setting func
char dispSetTime[6];
int newHours = 0;
int newMints = 0;
int settimetoken = 0;
uint8_t modhours;
uint8_t modmints;
void setup() {
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.display();
display.clearDisplay();
//button
attachInterrupt(digitalPinToInterrupt(PIN_INPUT), checkTicks, CHANGE);
button.attachClick(singleClick);
button.attachDoubleClick(doubleClick);
//Longpress
button.setPressTicks(1000); //time until long press is activated
button.attachLongPressStart(pressStart);
}
void loop() {
button.tick(); //get button state
display.clearDisplay();
updateTime(); //Updates int timearray[3]; Updates time
//display state 0 is normal clock face
if (displaystate == 0) {
clock();
customfunction();
if (alarmMode) {
AlarmCheck();
}
if (SpeakerOnAndLoud) {
GoToSpeaker();
}
}
//display state show settings
if (displaystate == 1) {
settings();
}
//determines the user's settings and updates vars
if (displaystate == 2) {
//we take the displaystate var
// and the selection var
customselection = selection;
//save & exit
displaystate = 0; //reset display state var
if (selection == 1) {
displaystate = 3;
}
if (selection == 4) {
displaystate = 4;
}
}
//set Alarm display state
if (displaystate == 3) {
setAlarm();
}
//display set time screen
if (displaystate == 4) {
setTime();
}
display.display();
}
//settings menu
void settings() {
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(1, 1);
display.println("Settings:");
display.setCursor(10, 11);
display.println("Set Alarm");
display.setCursor(10, 21);
display.println("Display Date");
display.setCursor(10, 31);
display.println("Display Temp - *C");
display.setCursor(10, 41);
display.println("Set Time");
display.setCursor(10, 51);
display.println("Exit");
if (selection == 6) {
selection = 1;
}
display.fillCircle(4, 4 + (10 * selection), 2, SSD1306_WHITE);
}
//custom function
void customfunction() {
//Serial.println(customselection);
//date display function
if (customselection == 2) {
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor((display.width() / 2) + 3, 40);
display.println("Date");
display.setCursor((display.width() / 2) + 8, 51);
char datetm[9];
sprintf(datetm, "%d/%02d/%02d", timearray[5], timearray[6], timearray[7]);
display.println(datetm);
}
//temp display function
if (customselection == 3) {
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor((display.width() / 2) + 3, 40);
display.println("Temp");
display.setCursor((display.width() / 2) + 8, 51);
int ntcRead = analogRead(A0);
float temp = 1 / (log(1 / (1023. / ntcRead - 1)) / beta + 1.0 / 298.15) - 273.15;
display.print(temp);
display.println(" *C");
}
}
//set time
void setTime() {
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(1, 1);
display.println("Set New time");
sprintf(dispSetTime, "%02d:%02d", newHours, newMints);
display.setTextSize(3);
display.setTextColor(WHITE);
display.setCursor(19, 12);
display.println(dispSetTime);
if (selection == 1) {
display.drawLine(19, 35, 52, 35, SSD1306_WHITE);
}
if (selection == 2) {
display.drawLine(73, 35, 105, 35, SSD1306_WHITE);
}
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(1, 40);
display.println("Single click to add Long click to move onDouble to quit");
}
//set alarm
void setAlarm() {
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(1, 1);
display.println("Enter Time for Alarm:");
sprintf(alarmtime, "%02d:%02d", Alarmhours, Alarmmints);
display.setTextSize(3);
display.setTextColor(WHITE);
display.setCursor(19, 12);
display.println(alarmtime);
if (selection == 1) {
display.drawLine(19, 35, 52, 35, SSD1306_WHITE);
}
if (selection == 2) {
display.drawLine(73, 35, 105, 35, SSD1306_WHITE);
}
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(1, 40);
display.println("Single click to add Long click to move onDouble to quit");
}
//alarm check
void AlarmCheck() {
if (Alarmhours == timearray[0] && Alarmmints == timearray[1] && timearray[2] == 0) {
SpeakerOnAndLoud = 1;
}
}
//alarm calling
void GoToSpeaker() {
if (millis() % 10 == 0) {
tone(BUZZER, 500);
} else {
noTone(BUZZER);
}
}
//mainscreen (clock)
void clock() {
//Draw Line between digital and analouge clock
display.drawLine((display.width() / 2) + 1, 0, 65, display.height(), SSD1306_WHITE);
DrawClock(); //Calls function to define Analog Clock face
DrawClockTime(); //Function that calls functions to draw moving hands
DrawDigital(); //Calls function to define digital clock & values
}
void DrawClock() {
//Serial.println(clockradius); //debuggging
//Draw Clock Face circle
display.drawCircle(clockcentreX, clockcentreY, clockradius, SSD1306_WHITE);
//Hour marks
for (int i = 0; i < 12; i++) {
int y = (31 * cos(pi - (2 * pi) / 12 * i)) + clockcentreY;
int x = (31 * sin(pi - (2 * pi) / 12 * i)) + clockcentreX;
display.drawLine(clockcentreX, clockcentreY, x, y, SSD1306_WHITE);
}
//Black Circle to cover up hour lines
display.fillCircle((display.width() / 4), display.height() / 2, (display.height() / 3) + 6, SSD1306_BLACK);
//Draw Circle in middle so its easy to see centre
display.fillCircle(clockcentreX, clockcentreY, 3, SSD1306_WHITE);
//Draw Numbers
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(clockcentreX - 6, 6);
display.println("12");
}
void DrawClockTime() {
DrawSecondHand(timearray[2]);
DrawMinuteHand(timearray[1]);
DrawHourHand(timearray[0], timearray[1]);
}
void DrawSecondHand(int second) {
int y = (26 * cos(pi - (2 * pi) / 60 * second)) + clockcentreY;
int x = (26 * sin(pi - (2 * pi) / 60 * second)) + clockcentreX;
display.drawLine(clockcentreX, clockcentreY, x, y, SSD1306_WHITE);
}
void DrawMinuteHand(int minute) {
int y = (24 * cos(pi - (2 * pi) / 60 * minute)) + clockcentreY;
int x = (24 * sin(pi - (2 * pi) / 60 * minute)) + clockcentreX;
display.drawLine(clockcentreX, clockcentreY, x, y, SSD1306_WHITE);
}
void DrawHourHand(float hour, float minute) {
minute = minute / 60;
hour = (hour + minute) * 60; //hour variable has minutes included so the hour hand shows progress to next val
int scale = 12 * 60; //scale is present so the hour hand can have finer steps
int y = (17 * cos(pi - (2 * pi) / scale * hour)) + clockcentreY;
int x = (17 * sin(pi - (2 * pi) / scale * hour)) + clockcentreX;
display.drawLine(clockcentreX, clockcentreY, x, y, SSD1306_WHITE);
//Fills in circle in centre for style
display.fillCircle(clockcentreX, clockcentreY, 2, SSD1306_BLACK);
}
void DrawDigital() {
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor((display.width() / 2) + 3, 1);
display.println("Time");
display.setCursor((display.width() / 2) + 8, 10);
//use Sprtf to concate time values for display
char hmsTime[20];
sprintf(hmsTime, "%d:%02d:%02d", timearray[0], timearray[1], timearray[2]);
display.println(hmsTime);
display.setCursor((display.width() / 2) + 3, 20);
display.println("Today is");
display.setCursor((display.width() / 2) + 8, 29);
display.println(dayNames[timearray[4]]);
}
uint8_t bcdToDec(uint8_t input) {
return ((input / 16 * 10) + (input % 16));
}
uint8_t ConverToTwelveHr(int hours) {
if (hours > 12) {
hours = hours - 12;
}
return hours;
}
void updateTime() {
//initate rtc
Wire.beginTransmission(DS1307_ADDRESS);
Wire.write(0x00);
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDRESS, 0x07);
//get times
uint8_t seconds = bcdToDec(Wire.read());
uint8_t minutes = bcdToDec(Wire.read());
uint8_t hours = bcdToDec(Wire.read() & 0xff);
uint8_t dayofweek = bcdToDec(Wire.read());
uint8_t mday = bcdToDec(Wire.read());
uint8_t month = bcdToDec(Wire.read());
uint8_t year = bcdToDec(Wire.read());
if (settimetoken == 1) {
modhours = (hours - newHours);
modmints = (minutes - newMints);
settimetoken = 0;
}
timearray[0] = hours - modhours;
timearray[1] = minutes - modmints;
timearray[2] = seconds;
timearray[4] = dayofweek;
timearray[5] = mday;
timearray[6] = month;
timearray[7] = year;
}
//button functions
void checkTicks() {
button.tick(); //tick check state
// check ticks or button.tick comes from the one button libary.
// This function is called at the beginning of the main loop (button.tick();).
// This function will determine the user's use of the button (one click or double click)
//Serial.println("InterruptTest"); //when button is used it calls the interrupt function checkTicks()
}
void singleClick() {
if (SpeakerOnAndLoud == 1) { //Takes a while for it to actually toggle
SpeakerOnAndLoud = 0;
}
if (displaystate == 3) {
if (selection == 1) {
Alarmhours++;
if (Alarmhours >= 24) {
Alarmhours = 0;
}
}
if (selection == 2) {
Alarmmints = Alarmmints + 5;
if (Alarmmints >= 60) {
Alarmmints = 0;
}
}
} else if (displaystate == 4) {
if (selection == 1) {
newHours++;
if (newHours >= 24) {
newHours = 0;
}
}
if (selection == 2) {
newMints = newMints + 1;
if (newMints >= 60) {
newMints = 0;
}
}
} else {
selection++;
}
}
void doubleClick() {
displaystate = 1;
selection = 1;
if (selection == 3) { //can disable alarm by entering and exiting alarm setting
alarmMode = 0;
}
}
void pressStart() { //long click
if (displaystate == 1 && selection == 4) {
selection = 0;
displaystate = 4;
}
if (displaystate == 1 && selection == 1) {
displaystate = 3;
selection = 0;
}
if (displaystate == 3) {
selection++;
if (selection == 3) {
statusAlarm();
}
}
if (displaystate == 4) {
selection++;
if (selection == 3 && displaystate == 4) {
statusSetTime();
settimetoken = 1;
}
}
if (selection != 4 && selection != 1 && displaystate != 3 && displaystate != 4) {
displaystate = 2;
}
}
// The above 3 functions are called upon by button.tick()
// The attached interrupt button (PIN_INPUT) calls the function checkTicks
//attachInterrupt(digitalPinToInterrupt(PIN_INPUT), checkTicks, CHANGE);
//button.attachClick(singleClick);
//button.attachDoubleClick(doubleClick);
//button.setPressTicks(1000); //time until long press is activated
//button.attachLongPressStart(pressStart);
// The button talks to the code via attachInterrupt, "OneButton.h" interprets how the button was used
// The functions called by the interrupt are also driven by the current state of the display
// "displaystate" determines which screen is being shown
// "selection" determins the user's choice
// For time setting/alarm "selection" is used to seperate hours and minutes.
// The settings menu is completely driven by states in terms of current display and user input
void statusAlarm() {
selection = 1;
displaystate = 1;
alarmMode = 1;
display.clearDisplay();
display.setTextSize(3);
display.setTextColor(WHITE);
display.setCursor(1, 12);
display.println("Alarm Set!");
//Serial.println(displaystate);
display.display();
}
void statusSetTime() {
selection = 1;
displaystate = 1;
display.clearDisplay();
display.setTextSize(3);
display.setTextColor(WHITE);
display.setCursor(1, 12);
display.println("Time Set!");
//Serial.println("TIMESET");
display.display();
}