Components and supplies
Pushbutton 6 mm
Quad level translator
Arduino Nano Every with headers
Breadboard - 830 contacts
Module RTC 1302
MONOCHROME 0.96" 128X64 OLED GRAPHIC DISPLAY
Apps and platforms
Arduino IDE 1.8
Project description
Code
Tide clock
cpp
Adapt line #12 of this code to your own display
1/********** Tide Clock **********/ 2 3/* If you do not want the moon phase display, 4 comment or suppress the next line */ 5#define moonDisplayActive 6 7#include <U8g2lib.h> 8#include <RTClib.h> 9#include <Fsm.h> 10#include <EEPROM.h> 11 12U8G2_SSD1306_128X64_NONAME_F_HW_I2C 13 u8g2(U8G2_R0); 14 15int displayWidth, displayHeight; 16int HMiddle, VMiddle; 17 18const int plusButtonPin=2; 19const int minusButtonPin=3; 20const int modeButtonPin=4; 21const int selectButtonPin=5; 22const int RTC_CLK=8; 23const int RTC_DAT=7; 24const int RTC_RST=6; 25 26DS1302 rtc(RTC_RST, RTC_CLK, RTC_DAT); 27 28unsigned long tideSeconds=0; 29const unsigned long tidePeriod=44714; 30/* Number of seconds of the moon 31 half period i. e. 12 h 25 mn 14 s */ 32 33DateTime current; 34DateTime highTideRef; 35int year, month, day, hour, minute; 36 37enum 38{ 39 EEPROMId0, 40 EEPROMId1, 41 EEPROMId2, 42 EEPROMYear, 43 EEPROMMonth, 44 EEPROMDay, 45 EEPROMHour, 46 EEPROMMinute 47}; 48 49enum 50{ 51 selYear, selMonth, selDay, 52 selHour, selMinute 53}; 54int selected; 55 56bool plusPressed=false, 57 minusPressed=false, 58 buttonPressed=false; 59volatile bool modePressed=false, 60 selectPressed=false; 61 62/* 63 Objects of the class stopWatch are used to 64 perform time dependant actions without 65 blocking the program by delay functions. 66*/ 67class stopWatch 68{ 69 unsigned long previousTime, currentTime; 70 public : 71 unsigned long elapsed; 72 void init(); 73 void now(); 74 stopWatch(); 75}; 76 77void stopWatch::init() 78{ 79 currentTime=millis(); 80 previousTime=currentTime; 81} 82 83void stopWatch::now() 84{ 85 currentTime=millis(); 86 if (currentTime<previousTime) 87 elapsed=0xFFFFFFFFul-previousTime+ 88 currentTime; 89 else 90 elapsed=currentTime-previousTime; 91} 92 93stopWatch::stopWatch() 94{ 95 currentTime=millis(); 96 previousTime=currentTime; 97} 98 99/***** Functions to manage pushbuttons *******/ 100 101/* 102 The Mode and Select pushbuttons are treated 103 by interrupts because they are used by 104 single presses. 105 The + and - pushbuttons are treated by 106 program because they can be held down to 107 repeat their actions. 108*/ 109void interruptRoutineReadMode(void) 110{ 111 noInterrupts(); 112 delayMicroseconds(10000); 113 if(digitalRead(modeButtonPin)==LOW) 114 modePressed=true; 115 interrupts(); 116} 117 118void interruptRoutineReadSelect(void) 119{ 120 noInterrupts(); 121 delayMicroseconds(10000); 122 if(digitalRead(selectButtonPin)==LOW) 123 selectPressed=true; 124 interrupts(); 125} 126 127void buttonsManagement(void) 128{ 129 const int debounce=20; 130 const int repeatPeriod=300; 131 const int beginRepeatTime=1000; 132 static stopWatch plusButtonStopWatch, 133 minusButtonStopWatch; 134 static stopWatch plusRepeatStopWatch, 135 minusRepeatStopWatch; 136 int plusButton, minusButton; 137 static int nbPlusButton=0, 138 nbMinusButton=0; 139 140 plusButton=digitalRead(plusButtonPin); 141 minusButton=digitalRead(minusButtonPin); 142 143 if(plusButton==HIGH) 144 { 145 plusButtonStopWatch.init(); 146 nbPlusButton=0; 147 } 148 else 149 { 150 plusButtonStopWatch.now(); 151 switch(nbPlusButton) 152 { 153 case 0 : 154 if(plusButtonStopWatch.elapsed> 155 debounce) 156 { 157 plusPressed=true; 158 buttonPressed=true; 159 nbPlusButton=1; 160 } 161 break; 162 case 1 : 163 if(plusButtonStopWatch.elapsed> 164 beginRepeatTime) 165 { 166 plusPressed=true; 167 nbPlusButton=2; 168 plusRepeatStopWatch.init(); 169 } 170 break; 171 default : 172 plusRepeatStopWatch.now(); 173 if(plusRepeatStopWatch.elapsed> 174 repeatPeriod) 175 { 176 plusPressed=true; 177 plusRepeatStopWatch.init(); 178 } 179 break; 180 } 181 } 182 183 if(minusButton==HIGH) 184 { 185 minusButtonStopWatch.init(); 186 nbMinusButton=0; 187 } 188 else 189 { 190 minusButtonStopWatch.now(); 191 switch(nbMinusButton) 192 { 193 case 0 : 194 if(minusButtonStopWatch.elapsed> 195 debounce) 196 { 197 minusPressed=true; 198 buttonPressed=true; 199 nbMinusButton=1; 200 } 201 break; 202 case 1 : 203 if(minusButtonStopWatch.elapsed> 204 beginRepeatTime) 205 { 206 minusPressed=true; 207 nbMinusButton=2; 208 minusRepeatStopWatch.init(); 209 } 210 break; 211 default : 212 minusRepeatStopWatch.now(); 213 if(minusRepeatStopWatch.elapsed> 214 repeatPeriod) 215 { 216 minusPressed=true; 217 minusRepeatStopWatch.init(); 218 } 219 break; 220 } 221 } 222} 223 224/********* Display functions *********/ 225 226/* 227 Display of round tide clock 228*/ 229void backgroundDisplay(void) 230{ 231 float angle, arrowAngle; 232 float radius; 233 int leftRight; 234 235// Central dot: 236 u8g2.drawDisc(HMiddle, VMiddle, 1); 237 238// Graduations: 239 radius=min(displayWidth, displayHeight)/2-1; 240 for(int i=0; i<12; i++) 241 { 242 angle=PI/6*i; 243 u8g2.drawLine(HMiddle+radius*sin(angle), 244 VMiddle-radius*cos(angle), 245 HMiddle+(radius-4)*sin(angle), 246 VMiddle-(radius-4)*cos(angle)); 247 } 248 249/* Arrow to show the rotation direction. It is 250 drawn on the opposite side of the hand: */ 251 radius=min(displayWidth, displayHeight)*0.24; 252 if(tideSeconds>tidePeriod/2) 253 leftRight=1; 254 else 255 leftRight=-1; 256 for(int i=-10; i<=10; i++) 257 { 258 u8g2.drawPixel(HMiddle+ 259 radius*sin(leftRight*PI/2+i*PI/80), 260 VMiddle- 261 radius*cos(leftRight*PI/2+i*PI/80)); 262 } 263 arrowAngle=leftRight*PI/2+PI/8; 264 u8g2.drawLine(HMiddle+radius*sin(arrowAngle), 265 VMiddle-radius*cos(arrowAngle), 266 HMiddle+radius*sin(arrowAngle)+ 267 leftRight*4, 268 VMiddle-radius*cos(arrowAngle)- 269 leftRight*2); 270 u8g2.drawLine(HMiddle+radius*sin(arrowAngle), 271 VMiddle-radius*cos(arrowAngle), 272 HMiddle+radius*sin(arrowAngle)- 273 leftRight*2, 274 VMiddle-radius*cos(arrowAngle)- 275 leftRight*3); 276 277// Texts "high" and "low": 278 u8g2.setFont(u8g2_font_5x7_mf); 279 u8g2.setCursor(HMiddle- 280 u8g2.getStrWidth("high")/2, 281 15); 282 u8g2.print("high"); 283 u8g2.setCursor(HMiddle- 284 u8g2.getStrWidth("low")/2, 285 displayHeight-9); 286 u8g2.print("low"); 287} 288 289void handDisplay(void) 290{ 291 float handAngle; 292 int handLength=min(HMiddle, VMiddle)-8; 293 294 handAngle=2*PI*tideSeconds/tidePeriod; 295 u8g2.drawCircle(HMiddle+sin(handAngle)*6, 296 VMiddle-cos(handAngle)*6, 4); 297 u8g2.drawCircle(HMiddle+sin(handAngle)*13, 298 VMiddle-cos(handAngle)*13, 2); 299 u8g2.drawLine(HMiddle+sin(handAngle)*16, 300 VMiddle-cos(handAngle)*16, 301 HMiddle+sin(handAngle)*handLength, 302 VMiddle-cos(handAngle)*handLength); 303} 304 305void tideClockDisplay(void) 306{ 307 current=rtc.now(); 308 tideSeconds=(current.unixtime()- 309 highTideRef.unixtime())% 310 tidePeriod; 311 u8g2.clearBuffer(); 312 backgroundDisplay(); 313 handDisplay(); 314 u8g2.sendBuffer(); 315 Serial.println(tideSeconds); 316} 317 318/* 319 Display of daily tide. A sine curve shows 320 the evolution of the tide along the current 321 day, and a vertical line shows the tide 322 at the current time. 323*/ 324void dayTideDisplay(void) 325{ 326 current=rtc.now(); 327 DateTime currentDay(current.year(), 328 current.month(), 329 current.day(), 330 0, 0, 0); 331 332/* Rounded display width and offset are used 333 to display the daily clock on a reduced 334 and centered screen in order to have 335 regular hour graduations. 336 */ 337 int rdDisplayWidth=24*(displayWidth/24); 338 int offs=(displayWidth-rdDisplayWidth)/2; 339 340 u8g2.clearBuffer(); 341// Sinus curve: 342 for(int i=0; i<rdDisplayWidth; i++) 343 { 344 tideSeconds=((currentDay+((long)i*86400/ 345 rdDisplayWidth)).unixtime()- 346 highTideRef.unixtime())% 347 tidePeriod; 348 u8g2.drawPixel(i+offs, 349 (1-cos(2*PI*tideSeconds/tidePeriod))* 350 (displayHeight-15)/2); 351 } 352 353// Vertical line: 354 int x=((long)rdDisplayWidth* 355 ((current.hour()*60+ 356 current.minute())))/1440+offs; 357 for(int i=0; i<displayHeight-15; i++) 358 u8g2.drawPixel(x, i); 359 360// Time indications: 361 u8g2.setFont(u8g2_font_5x7_mf); 362 u8g2.setCursor(rdDisplayWidth/6- 363 u8g2.getStrWidth("4h")/2+ 364 offs, 365 displayHeight); 366 u8g2.print("4h"); 367 u8g2.setCursor(rdDisplayWidth/2- 368 u8g2.getStrWidth("12h")/2+ 369 offs, 370 displayHeight); 371 u8g2.print("12h"); 372 u8g2.setCursor(rdDisplayWidth*5/6- 373 u8g2.getStrWidth("20h")/2+ 374 offs, 375 displayHeight); 376 u8g2.print("20h"); 377 for(int i=0; i<24; i++) 378 u8g2. drawLine(i*rdDisplayWidth/24+offs, 379 displayHeight-9, 380 i*rdDisplayWidth/24+offs, 381 displayHeight-11); 382 for(int i=4; i<24; i+=8) 383 u8g2.drawPixel(i*rdDisplayWidth/24+offs, 384 displayHeight-12); 385 386 u8g2.sendBuffer(); 387} 388 389void myDrawDisc(int xCenter, int yCenter, 390 int R) 391{ 392 for(int x=0; x<displayWidth; x++) 393 for(int y=0; y<displayHeight; y++) 394 if(((long)x-(long)xCenter)* 395 ((long)x-(long)xCenter)+ 396 ((long)y-(long)yCenter)* 397 ((long)y-(long)yCenter)<= 398 (long)R*(long)R) 399 u8g2.drawPixel(x, y); 400} 401 402/* Moon display */ 403void moonDisplay() 404{ 405/* first new moon since 01/01/2000 */ 406 DateTime orgNewMoon(2000, 1, 6, 18, 14, 0); 407/* Moon period : 29 days, 12 hours, 408 44 minutes, 2,9 seconds 409 ie 2551443 seconds*/ 410 const unsigned long moonPeriod=2551443; 411 unsigned long moonSeconds, moonPixelsDelay; 412 int moonRadius=min(displayWidth, 413 displayHeight)/2-2; 414 int shadowRadius; 415 int shadowDistance; //from moon center 416 417 current=rtc.now(); 418 moonSeconds=(current.unixtime()- 419 orgNewMoon.unixtime())% 420 moonPeriod; 421 moonPixelsDelay=4*moonRadius*moonSeconds/ 422 moonPeriod; 423 u8g2.clearBuffer(); 424 425 if(moonPixelsDelay<=moonRadius) 426 // new moon -> first quarter 427 { 428 u8g2.setDrawColor(1); 429 shadowDistance=moonRadius-moonPixelsDelay; 430 shadowRadius=moonRadius/ 431 sin(PI-2*atan2(moonRadius, 432 shadowDistance)); 433 u8g2.drawDisc(HMiddle, VMiddle, 434 moonRadius); 435 u8g2.setDrawColor(0); 436 if(shadowDistance>0) 437 myDrawDisc(HMiddle+shadowDistance- 438 shadowRadius, VMiddle, 439 shadowRadius); 440 else 441 u8g2.drawBox(0, 0, displayWidth/2, 442 displayHeight); 443 } 444 445 else if(moonPixelsDelay<=2*moonRadius) 446 // first quarter -> full moon 447 { 448 u8g2.setDrawColor(1); 449 shadowDistance=moonPixelsDelay-moonRadius; 450 shadowRadius=moonRadius/ 451 sin(PI-2*atan2(moonRadius, 452 shadowDistance)); 453 if(shadowDistance>0) 454 { 455 myDrawDisc(HMiddle-shadowDistance+ 456 shadowRadius, VMiddle, 457 shadowRadius); 458 u8g2.setDrawColor(0); 459 for(int x=0; x<displayWidth; x++) 460 for(int y=0; y<displayHeight; y++) 461 if((x-HMiddle)*(x-HMiddle)+ 462 (y-VMiddle)*(y-VMiddle)> 463 moonRadius*moonRadius) 464 u8g2.drawPixel(x,y); 465 } 466 else 467 u8g2.drawDisc(HMiddle, VMiddle, 468 moonRadius, 469 U8G2_DRAW_UPPER_RIGHT | 470 U8G2_DRAW_LOWER_RIGHT); 471 } 472 473 else if(moonPixelsDelay<=3*moonRadius) 474 // full moon -> last quarter 475 { 476 u8g2.setDrawColor(1); 477 shadowDistance=3*moonRadius- 478 moonPixelsDelay; 479 shadowRadius=moonRadius/ 480 sin(PI-2*atan2(moonRadius, 481 shadowDistance)); 482 if(shadowDistance>0) 483 { 484 myDrawDisc(HMiddle+shadowDistance- 485 shadowRadius, VMiddle, 486 shadowRadius); 487 u8g2.setDrawColor(0); 488 for(int x=0; x<displayWidth; x++) 489 for(int y=0; y<displayHeight; y++) 490 if((x-HMiddle)*(x-HMiddle)+ 491 (y-VMiddle)*(y-VMiddle)> 492 moonRadius*moonRadius) 493 u8g2.drawPixel(x,y); 494 } 495 else 496 u8g2.drawDisc(HMiddle, VMiddle, 497 moonRadius, 498 U8G2_DRAW_UPPER_LEFT | 499 U8G2_DRAW_LOWER_LEFT); 500 } 501 502 else 503 // last quarter -> new moon 504 { 505 u8g2.setDrawColor(1); 506 shadowDistance=moonPixelsDelay- 507 3*moonRadius; 508 shadowRadius=moonRadius/ 509 sin(PI-2*atan2(moonRadius, 510 shadowDistance)); 511 u8g2.drawDisc(HMiddle, VMiddle, 512 moonRadius); 513 u8g2.setDrawColor(0); 514 if(shadowDistance>0) 515 myDrawDisc(HMiddle-shadowDistance+ 516 shadowRadius, VMiddle, 517 shadowRadius); 518 else 519 u8g2.drawBox(HMiddle, 0, 520 displayWidth/2, displayHeight); 521 } 522 u8g2.setDrawColor(1); 523 u8g2.drawCircle(HMiddle, VMiddle, 524 moonRadius); 525 u8g2.sendBuffer(); 526} 527 528/* 529 This function is used to display the date 530 and the time for the current time and for 531 the reference high tide time. One of the 532 5 displayed values is highlighted, the one 533 that is selected. 534*/ 535void dateTimeAdjustDisplay(void) 536{ 537 u8g2.setFont(u8g2_font_9x15_mf); 538 u8g2.setCursor( 539 (displayWidth-u8g2.getStrWidth 540 ("2021/07/26"))/2, 37); 541 u8g2.setDrawColor(1); 542 if(selected==selYear) u8g2.setDrawColor(0); 543 u8g2.print(year); 544 u8g2.setDrawColor(1); 545 u8g2.print("/"); 546 if(selected==selMonth) u8g2.setDrawColor(0); 547 if(month<10) u8g2.print("0"); 548 u8g2.print(month); 549 u8g2.setDrawColor(1); 550 u8g2.print("/"); 551 if(selected==selDay) u8g2.setDrawColor(0); 552 if(day<10) u8g2.print("0"); 553 u8g2.print(day); 554 u8g2.setDrawColor(1); 555 u8g2.setCursor( 556 (displayWidth-u8g2.getStrWidth 557 ("08:39"))/2, 60); 558 if(selected==selHour) u8g2.setDrawColor(0); 559 if(hour<10) u8g2.print("0"); 560 u8g2.print(hour); 561 u8g2.setDrawColor(1); 562 u8g2.print(":"); 563 if(selected==selMinute) u8g2.setDrawColor(0); 564 if(minute<10) u8g2.print("0"); 565 u8g2.print(minute); 566 u8g2.setDrawColor(1); 567} 568 569void currentTimeDisplay(void) 570{ 571 u8g2.clearBuffer(); 572 u8g2.setFont(u8g2_font_7x13_mf); 573 u8g2.setCursor( 574 (displayWidth-u8g2.getStrWidth 575 ("Current time"))/2, 13); 576 u8g2.print("Current time"); 577 dateTimeAdjustDisplay(); 578 u8g2.sendBuffer(); 579} 580 581void highTideRefDisplay(void) 582{ 583 u8g2.clearBuffer(); 584 u8g2.setFont(u8g2_font_7x13_mf); 585 u8g2.setCursor( 586 (displayWidth-u8g2.getStrWidth 587 ("Ref. high tide"))/2, 13); 588 u8g2.print("Ref. high tide"); 589 dateTimeAdjustDisplay(); 590 u8g2.sendBuffer(); 591} 592 593/* 594 This function is called to adjust the 5 595 values of the current time or of the 596 reference high tide time with the +/- 597 pushbuttons. Several tests are made to 598 prevent from displaying an impossible date 599 such as february the 30th. 600*/ 601void dateTimeAdjust(void) 602{ 603 int numberOfDays; 604 605 buttonsManagement(); 606 switch(selected) 607 { 608 case selYear : 609 if(plusPressed && year<2100) 610 year++; 611 if(minusPressed && year>2000) 612 year--; 613 break; 614 615 case selMonth : 616 if(plusPressed) 617 { 618 month++; 619 if(month>12) month=1; 620 } 621 if(minusPressed) 622 { 623 month--; 624 if(month<1) month=12; 625 } 626 if(plusPressed || minusPressed) 627 { 628 if ((month==4 || month==6 || 629 month==9 ||month==11) && 630 day==31) 631 day=30; 632 if (month==2 && day>28) 633 day=28; 634 } 635 break; 636 637 case selDay : 638 numberOfDays=31; 639 if(plusPressed) day++; 640 if(minusPressed) day--; 641 if(plusPressed || minusPressed) 642 { 643 if (month==4 || month==6 || 644 month==9 || month == 11) 645 numberOfDays=30; 646 if (month==2) 647 if (year%4!=0 || 648 (year%100==0 && year%400!=0)) 649 numberOfDays=28; 650 else 651 numberOfDays=29; 652 if(day<1) day=numberOfDays; 653 if(day>numberOfDays) day=1; 654 } 655 break; 656 657 case selHour : 658 if(plusPressed) ++hour%=24; 659 if(minusPressed) 660 if(hour>0) hour--; 661 else hour=23; 662 break; 663 664 case selMinute : 665 if(plusPressed) ++minute%=60; 666 if(minusPressed) 667 if(minute>0) minute--; 668 else minute=59; 669 break; 670 } 671 plusPressed=false; 672 minusPressed=false; 673} 674 675/* 676 Functions called by the Finite State Machine 677 to adjust current time and reference high 678 tide time. 679*/ 680void currentTimeSetEnter(void) 681{ 682 selected=selYear; 683 current=rtc.now(); 684 year=current.year(); 685 month=current.month(); 686 day=current.day(); 687 hour=current.hour(); 688 minute=current.minute(); 689 currentTimeDisplay(); 690 buttonPressed=false; 691} 692 693void currentTimeSet(void) 694{ 695 dateTimeAdjust(); 696 currentTimeDisplay(); 697} 698 699void currentTimeSetExit(void) 700{ 701/* The RTC is set to the displayed time only 702 if the + or - pushbutton has been pressed. 703 Otherwise, the RTC is still up to date. 704*/ 705 if(buttonPressed) 706 rtc.adjust(DateTime(year, month, day, 707 hour, minute, 0)); 708} 709 710void highTideRefSetEnter(void) 711{ 712 selected=selYear; 713 year=highTideRef.year(); 714 month=highTideRef.month(); 715 day=highTideRef.day(); 716 hour=highTideRef.hour(); 717 minute=highTideRef.minute(); 718 highTideRefDisplay(); 719} 720 721void highTideRefSet(void) 722{ 723 dateTimeAdjust(); 724 highTideRefDisplay(); 725} 726 727void highTideRefSetExit(void) 728{ 729 highTideRef.setyear(year); 730 highTideRef.setmonth(month); 731 highTideRef.setday(day); 732 highTideRef.sethour(hour); 733 highTideRef.setminute(minute); 734 highTideRef.setsecond(0); 735 736 EEPROM.update(EEPROMYear, year%100); 737 EEPROM.update(EEPROMMonth, month); 738 EEPROM.update(EEPROMDay, day); 739 EEPROM.update(EEPROMHour, hour); 740 EEPROM.update(EEPROMMinute, minute); 741} 742 743void readEEPROM(void) 744{ 745 if(EEPROM.read(EEPROMId0)==byte('t') && 746 EEPROM.read(EEPROMId1)==byte('i') && 747 EEPROM.read(EEPROMId2)==byte('d')) 748 { 749 highTideRef.setyear(EEPROM.read 750 (EEPROMYear)); 751 highTideRef.setmonth(EEPROM.read 752 (EEPROMMonth)); 753 highTideRef.setday(EEPROM.read 754 (EEPROMDay)); 755 highTideRef.sethour(EEPROM.read 756 (EEPROMHour)); 757 highTideRef.setminute(EEPROM.read 758 (EEPROMMinute)); 759 } 760 else 761 { 762 EEPROM.update(EEPROMId0, 't'); 763 EEPROM.update(EEPROMId1, 'i'); 764 EEPROM.update(EEPROMId2, 'd'); 765 highTideRef.setyear(2021); 766 EEPROM.update(EEPROMYear, 767 highTideRef.year()%100); 768 highTideRef.setmonth(7); 769 EEPROM.update(EEPROMMonth, 770 highTideRef.month()); 771 highTideRef.setday(26); 772 EEPROM.update(EEPROMDay, 773 highTideRef.day()); 774 highTideRef.sethour(8); 775 EEPROM.update(EEPROMHour, 776 highTideRef.hour()); 777 highTideRef.setminute(39); 778 EEPROM.update(EEPROMYear, 779 highTideRef.minute()); 780 } 781} 782 783/* 784 A Finite State Machine is used to navigate 785 between the 5 display modes thanks to the 786 Mode pushbutton. 787*/ 788State tideClockDisplayState(&tideClockDisplay, 789 NULL, NULL); 790State dayTideDisplayState(&dayTideDisplay, 791 NULL, NULL); 792State moonDisplayState(&moonDisplay, 793 NULL, NULL); 794State currentTimeSetState(¤tTimeSetEnter, 795 ¤tTimeSet, 796 ¤tTimeSetExit); 797State higtTideRefSetState(&highTideRefSetEnter, 798 &highTideRefSet, 799 &highTideRefSetExit); 800Fsm modeFsm(&tideClockDisplayState); 801 802void setTransitions(void) 803{ 804 modeFsm.add_transition( 805 &tideClockDisplayState, 806 &dayTideDisplayState, 807 1, NULL); 808#ifdef moonDisplayActive 809 modeFsm.add_transition( 810 &dayTideDisplayState, 811 &moonDisplayState, 812 1, NULL); 813 modeFsm.add_transition( 814 &moonDisplayState, 815 ¤tTimeSetState, 816 1, NULL); 817#else 818 modeFsm.add_transition( 819 &dayTideDisplayState, 820 ¤tTimeSetState, 821 1, NULL); 822#endif 823 modeFsm.add_transition( 824 ¤tTimeSetState, 825 &higtTideRefSetState, 826 1, NULL); 827 modeFsm.add_transition( 828 &higtTideRefSetState, 829 &tideClockDisplayState, 830 1, NULL); 831 832/* These two transitions are used to refresh 833 the two clock displays every 10 seconds. 834*/ 835 modeFsm.add_timed_transition( 836 &tideClockDisplayState, 837 &tideClockDisplayState, 838 10000, 839 NULL); 840 modeFsm.add_timed_transition( 841 &dayTideDisplayState, 842 &dayTideDisplayState, 843 10000, 844 NULL); 845 modeFsm.add_timed_transition( 846 &moonDisplayState, 847 &moonDisplayState, 848 10000, 849 NULL); 850} 851 852void setup() 853{ 854 u8g2.begin(); 855 displayWidth=u8g2.getDisplayWidth(); 856 displayHeight=u8g2.getDisplayHeight(); 857 HMiddle=displayWidth/2; 858 VMiddle=displayHeight/2; 859 Serial.begin(9600); 860 rtc.begin(); 861 if (!rtc.isrunning()) 862 { 863 Serial.println("RTC is NOT running!"); 864 // following line sets the RTC to the 865 // date & time this sketch was compiled 866 rtc.adjust(DateTime(__DATE__, __TIME__)); 867 } 868 pinMode(plusButtonPin, INPUT_PULLUP); 869 pinMode(minusButtonPin, INPUT_PULLUP); 870 pinMode(modeButtonPin, INPUT_PULLUP); 871 pinMode(selectButtonPin, INPUT_PULLUP); 872 attachInterrupt(digitalPinToInterrupt 873 (modeButtonPin), 874 interruptRoutineReadMode,FALLING); 875 attachInterrupt(digitalPinToInterrupt 876 (selectButtonPin), 877 interruptRoutineReadSelect, FALLING); 878/* 879 If Mode and Select pushbuttons are pressed 880 simultaneously during power on, the 881 reference high tide time is reset to 882 its default value. 883*/ 884 if(digitalRead(modeButtonPin)==LOW && 885 digitalRead(selectButtonPin)==LOW) 886 { 887 EEPROM.update(EEPROMId0, 0xFF); 888 EEPROM.update(EEPROMId1, 0xFF); 889 EEPROM.update(EEPROMId2, 0xFF); 890 } 891 readEEPROM(); 892 setTransitions(); 893} 894 895void loop() 896{ 897 int event=0; 898 899 noInterrupts(); 900 if(modePressed) 901 { 902 event=1; 903 modePressed=false; 904 } 905 if(selectPressed) 906 { 907 ++selected%=5; 908 selectPressed=false; 909 } 910 interrupts(); 911 modeFsm.run_machine(); 912 modeFsm.trigger(event); 913}
Downloadable files
Tide clock schematics
Tide clock with RTC 1.jpg
Comments
Only logged in users can leave comments