/*************************
******* THE SEA-DINGER****
A clock that displays the time in
12- or 24- hour format and strikes
a bell according to ship's time; i.e.
bells; starts at one bell (0430, 0830, 1230, etc)
increasing by one bell every half hour to 8 bells.
Also shows DoW (Day of Week), temperature, month
day, and year.
By SailorMike
***************************/
#include <Wire.h>
#include <DS3231.h>
#include <Big_Digits.h> // Custom, Contains glyphs and codes to make big digits
#include <Arrays.h> //Contains various arrays
#include <ShipsBell.h> // Custom; math and routine for bells
#include <LiquidCrystal.h>
#define CLOCK_ADDRESS 0x68 //DS 3231 RTC
const int timeSetSwitch = 6; // sets time set mode
bool timeFlag;
const int timeUpPin = 5; // Move select time up in set mode
const int timeDownPin = 4; //Move select time down in set mode
const int bellPin = 13;
int mode = 0;
int temp = 0; // Used for temperature
int f_temp = 0;
/*******************************
Modes are used to either display time or to
set the individual time elements
********************************/
bool starTickFlag;
bool setModeFlag;
//LiquidCrystal (RS, EN, D4, D5, D6, D7);
Big_Digits lcd = Big_Digits(12, 11, 10, 9, 8, 7);// initialize display
DS3231 Clock;// libraries courtesy of Eric Ayers
RTClib RTC;
Arrays arr ;
Bell bell;
void setup() {
/* 24-hour mode of DS 3231 RTC is default
12-hour mode can be selected, see RTC docs
*/
Wire.begin();
Serial.begin(115200);
pinMode(timeSetSwitch, INPUT_PULLUP);
pinMode(timeUpPin, INPUT_PULLUP);
pinMode(timeDownPin, INPUT_PULLUP);
pinMode(bellPin, OUTPUT);
digitalWrite(bellPin, LOW);
lcd.begin(20, 4);//start display
for (int i = 0; i < 5; i++) {
// Stores custom glyphs on LCD memory
lcd.createChar(i, lcd.glyphs[i]);
}
lcd.writeComma(13);
/* FOR STRIKE TEST ONLY;
to optimize bell strike
when clock is done
uncomment below 3 lines.
**********
Clock.setHour(23);
Clock.setMinute(59);
Clock.setSecond(55);
**************/
}
void loop() {
long unsigned curTime = millis();
static long unsigned startStarTickTime;
const long unsigned starStartTickPeriod = 250;
static long unsigned startSetTime;
const long unsigned setTimePeriod = 500;
DateTime now = RTC.now(); //Get time
/*if setTimePeriod time elapsed, read and dispay time
Code could be optimized here by using one-second
pulse from DS3231 RTC as a hard interrupt.
in set mides, a star is displayed next to data to be
changed
*/
if (curTime > (startStarTickTime + starStartTickPeriod)) {
startStarTickTime = curTime; //reset pause timer
lcd.writeLocStar(mode, starTickFlag);
starTickFlag = !starTickFlag; // set place for star
if (mode == 0) {
/* advance time if normal mode 0
Mode 0 is normal display of time
*/
advanceTime(mode, timeFlag, now.second());
}
else { // set seconds to 0 while setting clock if mode != 0
Clock.setSecond(0);
lcd.PrintNumberString(0, 18, 0);
}
setMode(mode); // look for time set switch and advance mode
}
if (curTime > (startSetTime + setTimePeriod)) {
modeAction(mode);
startSetTime = curTime; // reset sample time
getTemp();
}
}
String retDoW(int curDoW) { // Day of Week
curDoW --; // index Correction
String strDoW = arr.dayOfWeek[curDoW]; // from array lib
return strDoW;
}
String nowMonth(int newMonth) {
newMonth = newMonth - 1; //index correction
String strMonth = arr.monthList[newMonth]; //From arr lib
return strMonth;
}
void advanceTime (int mode, bool& timeFlag, int seconds) {
/**********
Could use DS3231 pulse here instead
******************/
static int oldSeconds;
if (mode == 0) {
if (oldSeconds != seconds) {
oldSeconds = seconds;
if (mode == 0) {
timeFlag = !timeFlag;
lcd.writeColon(timeFlag);
writeTime();
} else {
lcd.writeColon(true);
}
//tone(13, 800, 50) ;
bellStrike();
}
}
}
void bellStrike() { // Test if bell should be struck
DateTime now = RTC.now();
int nowHour = now.hour();
int nowMinute = now.minute();
int nowSecond = now.second();
bell.Ring(nowHour, nowMinute, nowSecond);
}
void setMode(int & mode) {// Advance mode; various
// modes set different time elements
bool pinRead = digitalRead(timeSetSwitch);
if (!pinRead && setModeFlag) {
mode ++;
if (mode > 6) {// cycle thru 6 set modes
mode = 0;
}
setModeFlag = false;
}
if (pinRead) {
setModeFlag = true;
}
}
/************
ModeAction looks at mode and
takes action to set various time
elements
************/
void modeAction( int mode) {
DateTime now = RTC.now();
int newMinute = now.minute();
int newHour = now.hour();
int newDoW = Clock.getDoW();
int newMonth = now.month();
int newDay = now.day();
int newYear = (now.year() - 2000);
switch (mode) {
case 0: // Display time. Don't set anything
break;
case 1: //set minutes
newMinute = setTime(newMinute);
if (newMinute > 59) {
newMinute = 0;
}
if (newMinute < 0) {
newMinute = 59;
}
Clock.setMinute(newMinute);
lcd.showBigNumber(newMinute, 10);
break;
case 2: //set hours
newHour = setTime(newHour);
if (newHour > 23) { // Modify this if you want 12-hr mode
newHour = 0;
}
if (newHour < 0) {
newHour = 23;
}
Clock.setHour(newHour);
lcd.showBigNumber(newHour, 0);
break;
case 3:
newDoW = setTime(newDoW);
Clock.setDoW(newDoW);
writeTime();
// Set DoW
break;
case 4: //set month
newMonth = setTime(newMonth);
if (newMonth > 12) {
newMonth = 1;
}
if (newMonth < 1) {
newMonth = 12;
}
Clock.setMonth(newMonth);
writeTime();
break;
case 5: // set date
newDay = setTime(newDay);
Clock.setDate(newDay);
writeTime();
break;
case 6: // set year
newYear = setTime(newYear);
Clock.setYear(newYear);
writeTime();
break;
}
}
int setTime(int time) {
/* Increase or decrease selected time element
according to button pressed*/
bool timeUpPressed = digitalRead(timeUpPin);
bool timeDownPressed = digitalRead(timeDownPin);
int newTime = time;
if (!timeUpPressed) {
newTime ++;
}
if (!timeDownPressed) {
newTime --;
}
return newTime;
}
int getTemp() {
/* The DS 3231 RTC has a built-in register that
stores the current temperature in degrees and tenths Celsius.
The stated accuracy is + or - 3 deg. A correction
can be inserted here if desired. */
if (mode == 0) {
unsigned long curTime = millis();
unsigned long tempPeriod = 2000;
static unsigned long startTime;
float floatTemp_C = Clock.getTemperature();//Celsius
//floatTemp += 0.0; // Correction for RTC temp error if any.
/*
The C++ "round()" function is reputed unreliable. The following
routine for rounding was recommended instead.
*/
if (floatTemp_C - floor(temp) > 0.5) {
temp = floor(floatTemp_C) + 1;
}
else {
temp = floor(floatTemp_C);
}
float floatTemp_F = (floatTemp_C * 1.8) + 32;
if (floatTemp_F - floor(floatTemp_F) > 0.5) {
f_temp = floor(floatTemp_F) + 1;
}
else {
f_temp = floor(floatTemp_C);
}
static bool tempFlag;
if (curTime > startTime + tempPeriod) {
startTime = curTime;
tempFlag = !tempFlag;
if (tempFlag) { //Cent
lcd.PrintNumberString(temp, 16, 2);
lcd.write(char(223));
lcd.print("C");
} else {
lcd.PrintNumberString(f_temp, 16, 2);
lcd.write(char(223));
lcd.print("F");
}
}
}
}
void writeTime() {// Actually writes various
// elements to LCD display
DateTime now = RTC.now();
lcd. PrintNumberString(now.second(), 18, 0);
int nowMonth = now.month() - 1;
String strNowMonth = arr.monthList[nowMonth];
lcd.writeString(strNowMonth, 10, 3);
lcd.PrintNumberString(now.day(), 11, 3);
lcd.PrintNumberString(now.year(), 15, 3);
lcd.writeString(retDoW(Clock.getDoW()), 13, 2);
lcd.showBigNumber(now.hour(), 0);
lcd.showBigNumber(now.minute(), 10);
lcd.setCursor(18, 2);
}