/* Tide Clock by Mariya Lupandina 25.04.21
Tide prediction code based of:
Tide_calculator.ino by Luke Miller, copyright (c) 2019
This code calculates the current tide height for the pre-programmed site.
It requires a real time clock (DS1307 or DS3231 chips) to generate a time for the calculation.
The site is set by the name of the included library, for other sites use this link: // Other sites available at http://github.com/millerlp/Tide_calculator.
Written under version 1.6.4 of the Arduino IDE.
The harmonic constituents used here were originally derived from
the Center for Operational Oceanic Products and Services (CO-OPS),
National Ocean Service (NOS), National Oceanic and Atmospheric
Administration, U.S.A.
The display code is based of:
Arduino UNO and 128x128 OLED Display for analog clock by upir, 2023
*/
//Initial setup
//Header files for talking to real time clock
#include <Wire.h> // Required for RTClib
#include <SPI.h> // Required for RTClib to compile properly
#include <RTClib.h> // From https://github.com/millerlp/RTClib
//Header file for tide prediction at given site
#include "TidelibTheBatteryNewYorkHarborNewYork.h"
//Header file for talking with OLED display
#include <Arduino.h>
#include <U8g2lib.h> // u8g2 library for drawing on OLED display - needs to be installed in Arduino IDE first
// ---------------------- Display Setup ----------------------
U8G2_SH1107_128X128_1_HW_I2C u8g2(U8G2_R0); // For 128x128 SH1107
//#define CS 10
//#define DC 9
//#define RST 8
//U8G2_SH1107_128X128_1_4W_HW_SPI u8g2(U8G2_R0, 10, 9, 8); //[page buffer, size = 128 bytes]
const int center_x = 64;
const int center_y = 64;
const int min_radius = 4;
const int max_radius = 31;
int current_radius = min_radius;
bool growing = true;
// ---------------------- RTC + TideCalc ----------------------
RTC_DS1307 RTC;
TideCalc myTideCalc;
int currDay;
DateTime lastPredictionTime;
const float predictionIntervalHours = 24.0; // Can change to 6.0, 12.0, etc.
float tideEventHours[4]; // Decimal hour values for 2 highs + 2 lows
// ---------------------- Timers ----------------------
unsigned long previousTideMillis = 0;
unsigned long previousTickMillis = 0;
const unsigned long tideInterval = 1000;
const unsigned long tickInterval = 5000;
/// ---------------------- Setup ----------------------
void setup() {
Wire.begin();
RTC.begin();
u8g2.begin();
u8g2.setContrast(255);
Serial.begin(57600);
DateTime now = RTC.now();
currDay = now.day();
lastPredictionTime = now;
Serial.println("Starting TideClock");
Serial.print("Station: ");
Serial.print(myTideCalc.returnStationID());
Serial.print(" (ID ");
Serial.print(myTideCalc.returnStationIDnumber());
Serial.println(")");
DateTime startOfDay(now.year(), now.month(), now.day(), 0, 0, 0);
printTodaysTideExtremes(startOfDay);
}
// ---------------------- Loop ----------------------
void loop() {
unsigned long currentMillis = millis();
DateTime now = RTC.now();
// Animate pulse
if (currentMillis - previousTideMillis >= tideInterval) {
previousTideMillis = currentMillis;
if (growing) {
current_radius++;
if (current_radius >= max_radius) growing = false;
} else {
current_radius--;
if (current_radius <= min_radius) growing = true;
}
}
// Update tide predictions if interval elapsed
TimeSpan elapsed = now - lastPredictionTime;
if (elapsed.totalseconds() >= predictionIntervalHours * 3600) {
Serial.println();
Serial.println("Generating new tide prediction...");
printTime(now);
DateTime startOfDay(now.year(), now.month(), now.day(), 0, 0, 0);
printTodaysTideExtremes(startOfDay);
lastPredictionTime = now;
}
// Draw display
u8g2.firstPage();
do {
draw_background();
draw_tides(current_radius, 1);
draw_ticks();
} while (u8g2.nextPage());
delay(100); // Small pause to keep loop stable
}
// ---------------------- Drawing Functions ----------------------
void draw_background() {
u8g2.setDrawColor(1);
u8g2.drawCircle(center_x, center_y, 63, U8G2_DRAW_ALL);
u8g2.drawPixel(center_x, center_y);
}
void draw_ticks() {
for (int i = 0; i < 4; i++) {
if (tideEventHours[i] < 0 || tideEventHours[i] >= 24) continue;
float angle_deg = (tideEventHours[i] / 24.0) * 360.0;
float angle_rad = angle_deg * PI / 180.0;
float x1 = center_x + sin(angle_rad) * 50;
float y1 = center_y - cos(angle_rad) * 50;
float x2 = center_x + sin(angle_rad) * 63;
float y2 = center_y - cos(angle_rad) * 63;
u8g2.drawLine(x1, y1, x2, y2);
}
}
void draw_tides(int radius, uint8_t color) {
u8g2.setDrawColor(color);
u8g2.drawDisc(center_x, center_y, radius, U8G2_DRAW_ALL);
}
// ---------------------- Tide Prediction ----------------------
void printTodaysTideExtremes(DateTime dayStart) {
const int interval = 5; // 5-minute steps
DateTime prevTime = dayStart;
DateTime currTime = dayStart + TimeSpan(0, 0, interval, 0);
float prevTide = myTideCalc.currentTide(prevTime);
float currTide = myTideCalc.currentTide(currTime);
int highsFound = 0;
int lowsFound = 0;
int eventIndex = 0;
Serial.println("Today's predicted tides:");
while ((currTime.day() == dayStart.day()) && (highsFound < 2 || lowsFound < 2)) {
DateTime nextTime = currTime + TimeSpan(0, 0, interval, 0);
float nextTide = myTideCalc.currentTide(nextTime);
// High tide
if (prevTide < currTide && currTide > nextTide && highsFound < 2) {
Serial.print("High Tide: ");
printTime(currTime);
Serial.print(" Height: ");
Serial.print(currTide, 2);
Serial.println(" ft");
float hour = currTime.hour() + currTime.minute() / 60.0;
if (eventIndex < 4) tideEventHours[eventIndex++] = hour;
highsFound++;
}
// Low tide
if (prevTide > currTide && currTide < nextTide && lowsFound < 2) {
Serial.print("Low Tide: ");
printTime(currTime);
Serial.print(" Height: ");
Serial.print(currTide, 2);
Serial.println(" ft");
float hour = currTime.hour() + currTime.minute() / 60.0;
if (eventIndex < 4) tideEventHours[eventIndex++] = hour;
lowsFound++;
}
prevTime = currTime;
prevTide = currTide;
currTime = nextTime;
currTide = nextTide;
}
// Fill remaining slots with invalid marker
for (int i = eventIndex; i < 4; i++) {
tideEventHours[i] = -1;
}
if (highsFound < 2 || lowsFound < 2) {
Serial.println("Warning: Less than 2 highs or lows found today.");
}
Serial.println();
}
void printTime(DateTime now) {
Serial.print(now.year(), DEC); Serial.print("/");
Serial.print(now.month(), DEC); Serial.print("/");
Serial.print(now.day(), DEC); Serial.print(" ");
if (now.hour() < 10) Serial.print("0");
Serial.print(now.hour()); Serial.print(":");
if (now.minute() < 10) Serial.print("0");
Serial.print(now.minute()); Serial.print(":");
if (now.second() < 10) Serial.print("0");
Serial.println(now.second());
}
Loading
grove-oled-sh1107
grove-oled-sh1107