Close to final! Here is what is working:
Able to calculate 10 years of tide predictions
RTC(realtime clock) generates accurate date/time
Daily tide prediction updates occur at midnight
Cursor indicates current time( green line )
Tide graph data tests for out of bounds(greater than 7ft and less than 0 ft) situations
Annotated tide peaks( needs more edge case testing )
Annotate King Tide peaks
What’s not working:
nothing noticed so far but expect min-max peak detection to show something when peaks happen at columns 0 or 31.
TODO:
Adjust tide calc graph to remove daylight savings adjustment. Clock cursor should be on 24hr lines
Enable/disable serial output with a DEBUG flag
Add 2 buttons to move the cursor forwards or backwards in time(ie 24hr clock time adjustment).
wood cover: laser cut a piece of wood so LEDs show through holes
wood cover: laser engrave time marks and other decorations
Make a box for holding electronics and add micro-USB connector for powering
source code:
#include <Adafruit_NeoPixel.h>
//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
#define PIN 6
// Tide calculation library setup.
#include "TidelibLaJollaScrippsInstitutionWharfCalifornia.h"
// Other sites available at http://github.com/millerlp/Tide_calculator
// using a 32x8 segment matrix display for the tide chart graph
const int8_t iChartCol=32;
int8_t iChartData[32];
float fChartData[32];
int8_t iChartDataDec[32];
int8_t iMinMax[4];
RTC_DS3231 rtc;
#define BRIGHTNESS 40 // Set BRIGHTNESS to about 1/5 (max = 255)
#define MATRIX_WIDTH 8
#define MATRIX_LENGTH 32
#define LED_COUNT 32*8
// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
void setup() {
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
Serial.flush();
while (1) delay(10);
}
if (rtc.lostPower()) {
Serial.println("RTC lost power, let's set the time!");
// When time needs to be set on a new device, or after a power loss, the
// following line sets the RTC to the date & time this sketch was compiled
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// This line sets the RTC with an explicit date & time, for example to set
// January 21, 2014 at 3am you would call:
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}
// When time needs to be re-set on a previously configured device, the
// following line sets the RTC to the date & time this sketch was compiled
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// This line sets the RTC with an explicit date & time, for example to set
// January 21, 2014 at 3am you would call:
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip.show(); // Turn OFF all pixels ASAP
strip.setBrightness(BRIGHTNESS);
// initialize fChartData[]
for( int8_t i=0; i<iChartCol; i++ ){
iChartData[i]=0;
fChartData[i]=0;
iChartDataDec[i]=0;
}
// For debugging output to serial monitor
Serial.begin(57600); // Set baud rate to 57600 in serial monitor for slow 8MHz micros
getChartData();//iChartData, iChartCol);
getMinMaxData();
Serial.println(F("Todays tide in 45 minute increments:"));
for( int8_t x=1; x <= iChartCol; x++ ) {
Serial.print(F(" Tide height: "));
//Serial.print(iChartData[x-1],3);
Serial.print(fChartData[x-1],3);
//Serial.print(".");Serial.print(iChartDataDec[x-1]);
Serial.print(F(" ft."));
Serial.println();
}
Serial.println();
}
void loop() {
// check for new day and run getChartData() on newday( hour=0 )
static int8_t iLastHour = 24;
if( rtc.now().hour() == 0 && iLastHour != 0){ // new day
getChartData();
getMinMaxData();
iLastHour=0;
strip.clear();
}
else
iLastHour = rtc.now().hour();
const int8_t iSpeed = 10; //lower is faster
// Fill along the length of the strip in various colors...
//colorWipe(strip.Color(255, 0, 0) , iSpeed); // Red
timeLine(strip.Color( 0, 50, 0) , iSpeed/5); // light Green
tideWipe(strip.Color( 0, 0, 75) , iSpeed); // Blue
//minMaxPeaks(strip.Color( 0, 0, 255) , iSpeed/5); // Blue
}
// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
for(uint16_t i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
strip.setPixelColor(i, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
delay(wait); // Pause for a moment
}
}
// Turn on a matrix columns pixel one after another with a color.
// Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void tideWipe(uint32_t color, int wait) {
int8_t iCol;
uint32_t iColor = color;
for(uint16_t i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
// check for which column
iCol = i/MATRIX_WIDTH;
//Serial.print("column:");Serial.println(iCol);
// set tide peaks colors
if( iCol==iMinMax[0] || iCol==iMinMax[1] || iCol==iMinMax[2] || iCol==iMinMax[3] )
if( fChartData[iCol]>=7 || fChartData[iCol]<=-1.0 )
color=strip.Color(75,0,0); // red marks King Tide
else
color=strip.Color(48,213,200); // turquoise
//color=strip.Color(48,213,200); // turquoise
else
color=iColor;
if( (iCol % 2) == 0 ) // true == even
strip.setPixelColor(7+(iCol*MATRIX_WIDTH)-iChartData[iCol], color); // Set pixel's color (in RAM)
else // odd
strip.setPixelColor((iCol*MATRIX_WIDTH)+iChartData[iCol], color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
delay(wait); // Pause for a moment
}
}
void timeLine(uint32_t color, int wait) {
int8_t iCol, iTimeCol;
int8_t iHour, iMinutes;
static int8_t iCurCol=0;
DateTime curTime;
curTime = rtc.now();
iHour = curTime.hour();
iMinutes = curTime.minute();
iTimeCol=abs((iHour*60+iMinutes)/45);
for(uint16_t i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
// check for which column
iCol = i/MATRIX_WIDTH;
//Serial.print("column:");Serial.println(iCol);
if( iCol == iTimeCol ) { // true == even
if( iCol != iCurCol ) // clear previous line
for(int8_t j=0; j<MATRIX_WIDTH; j++)
strip.setPixelColor(iCurCol*MATRIX_WIDTH+j, strip.Color(0, 0, 0)); // Clear pixel's
for(int8_t j=0; j<MATRIX_WIDTH; j++){
// test if pixel color is already set, skip if isSet
if( strip.getPixelColor(iCol*MATRIX_WIDTH+j) == 0 )
strip.setPixelColor(iCol*MATRIX_WIDTH+j, color); // Set pixel's color (in RAM)
}
iCurCol=iCol;
}
strip.show(); // Update strip to match
delay(wait); // Pause for a moment
}
}
void minMaxPeaks(uint32_t color, int wait) {
int8_t iCol;
uint32_t iColor, iBrightColor;
iBrightColor=strip.Color(255,127,127);
for(uint16_t i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
// check for which column
iCol = i/MATRIX_WIDTH;
//Serial.print("column:");Serial.println(iCol);
if( iCol==iMinMax[0] || iCol==iMinMax[1] || iCol==iMinMax[2] || iCol==iMinMax[3] ){
color = iBrightColor;
if( (iCol % 2) == 0 ) // true == even
strip.setPixelColor(7+(iCol*MATRIX_WIDTH)-iChartData[iCol], color); // Set pixel's color (in RAM)
else // odd
strip.setPixelColor((iCol*MATRIX_WIDTH)+iChartData[iCol], color); // Set pixel's color (in RAM)
}
else
color = iColor;
strip.show(); // Update strip to match
delay(wait); // Pause for a moment
}
}
//*************************************
void getChartData(void) {
TideCalc myTideCalc; // Create TideCalc object called myTideCalc
float result;
DateTime today, startTime;
//today = DateTime(__DATE__,__TIME__);
//today = DateTime(2022,3,19,13,29,0);
today = rtc.now();
Serial.print(today.year(), DEC);
Serial.print('/');
Serial.print(today.month(), DEC);
Serial.print('/');
Serial.println(today.day(), DEC);
startTime = DateTime(today.year(),today.month(),today.day(),00,00,0); // TODO: get today() hr=0 min=0
// 24 hours in a day but 32 segments...
//(24hr * 60min * 60sec)/32 // seconds/day / 32
for( int8_t x=1; x <= iChartCol; x++ ) {
result = myTideCalc.currentTide(startTime.unixtime() + ((24 * 60L * 60)/iChartCol) * x);
fChartData[x-1] = result;
iChartData[x-1] = (int8_t)round(result);
// test if low tide is less than 0 and move to zero. Disp only does 0-7'
if( result < 0.50 )
iChartData[x-1] = 0;
int whole = (int)result;
//Serial.print(result,3);Serial.print("::");Serial.println((int8_t)abs((result-whole)*100));
iChartDataDec[x-1] = (int8_t)abs((result-whole)*100);
}
}
void getMinMaxData(void) {
int8_t iFMin, iFMax, iSMin, iSMax;
iMinMax[0]=iFMin = findMin(0);
iMinMax[1]=iFMax = findMax(0);
if( iFMax < iFMin ) {
//iFMax = findMax(iFMin);
iMinMax[1]= iFMax = findMax(iFMin);
}
// find 2nd Min and Max
iMinMax[2]=iSMin = findMin(iFMax);
iMinMax[3]=iSMax = findMax(iSMin);
if( iSMax < iSMin ) { // 2nd max returned 0
//iSMax = findMax(iFMin);
iMinMax[3]=iSMax = findMax(0);
}
Serial.print("First Min=");Serial.println(iFMin);
Serial.print("First Max=");Serial.println(iFMax);
Serial.print("Second Min=");Serial.println(iSMin);
Serial.print("Second Max=");Serial.println(iSMax);
}
int8_t findMin(int iStart)
{
for( int i=iStart; i<32; i++ )
if( (i<31) && (fChartData[i]-fChartData[i+1] < 0))
return i;
return 0;
}
int8_t findMax(int iStart)
{
for( int i=iStart; i<32; i++ )
if( (i<31) && (fChartData[i+1]-fChartData[i] < 0))
return i;
return 0;
}
//*************************************