├── GPS-RTC-Clock.ino └── README.md /GPS-RTC-Clock.ino: -------------------------------------------------------------------------------- 1 | 2 | #include //for RTC comms over I2C 3 | #include //for LED output to Max7219 module 4 | 5 | //VERSION 7: NANO PIN ASSIGNMENTS, adds simple count up timer, LED dimmer, shows GPS PPS status. 6 | // Commit Date: 23-June-2020 7 | // RTC always has UTC time and date 8 | 9 | //Nano pins: D10-D13, SPI for Max; A3/D17(SQW), A4(SDA) and A5(SCL) for I2C to DS3231 RTC. 10 | // D0,D1(serial) D2(PPS) for GPS. D3 for IR receiver interrupt. 11 | 12 | //STATE MACHINE SETUP// 13 | enum { DEBUG, BOOTUP, REG_OPS, TOGGLE_DISPLAY, COUNTER, CHECK_PPS, GPS_INIT, GPS_PPS_SYNC, GPS_NMEA_SYNC} StateMachine; 14 | 15 | //PIN DEFS & ADDRESSES// 16 | const byte RTC_SQW_Pin = 17; //same as A3 pin (using analog pin as digital for RTC SQW) 17 | const byte GPS_PPS_Pin = 2; // for receiving the GPS PPS signal 18 | const byte ir_pin = 3; //Interrupt pin for IR receiver data pin. 19 | const byte ChipSelectPin = 10; // For Max7219. We set the SPI Chip Select/Slave Select Pin here. 10 for uno/nano. 53 for mega 20 | 21 | const int RTC_I2C_ADDRESS = 0x68; // Must be data type [int] to keep wire.h happy. Sets RTC DS3231 i2C address. 22 | 23 | 24 | //TIMERS AND EDGE DETECTORS// 25 | unsigned long GPS_INIT_t0, t0, t1, t2; //for timers 26 | 27 | byte RTC_SQW_Current = LOW; 28 | byte RTC_SQW_Prev = LOW; 29 | byte GPS_PPS_Current = HIGH; 30 | byte GPS_PPS_Prev = HIGH; 31 | 32 | //ISR HANDLERS// 33 | volatile unsigned int pulseChangeTime; //long or int? 34 | volatile byte pulseFlag = 0; 35 | 36 | 37 | //UTC offset handlers// //ie, Time Zones and Daylight Savings (summer) Time // 38 | bool UTC_offset_enable = true; // False for UTC time. True for local time. 39 | 40 | const char offsetStandardHr = -5; 41 | const char offsetStandardMin = 0; 42 | const char offsetDSTHr = -4; 43 | const char offsetDSTMin = 0; 44 | 45 | const byte startDST[4] = {2,0,3,2}; // {nth,day of week,month,hh} use [0..6] for [Sunday...Saturday] 46 | const byte startStandard[4] = {1,0,11,2}; // {nth,day of week,month,hh} [0..6] for [Sunday...Saturday] 47 | // set n=5 for 5th or last. Assume DST starts/stops at 2am local time 48 | 49 | const byte days[] = {0,31,28,31,30,31,30,31,31,30,31,30,31}; // mapping days of the 12 months 50 | 51 | const int currentCentury = 2000; 52 | 53 | /* globals that store our offset values*/ 54 | int offYYYY; 55 | byte offMO; 56 | byte offDD; 57 | byte offHH; 58 | byte offMM; 59 | byte offSS; 60 | 61 | //RTC date+time holders// 62 | byte ssRTC, mmRTC, hhRTC, dowRTC, ddRTC, moRTC, ctyRTC, yyRTC; 63 | byte countSS, countMM, countHH; 64 | 65 | //GPS UTC date+time handlers// 66 | byte hhGPS, mmGPS, ssGPS, ddGPS, moGPS; 67 | int yyyyGPS = 0; 68 | 69 | bool newGPS_dateAvail = false; 70 | bool newGPS_timeAvail = false; 71 | bool NMEA_processFlag = false; 72 | bool GGA_msg = false; 73 | bool RMC_msg = false; 74 | bool ZDA_msg = false; 75 | bool msgStart = false; 76 | 77 | bool GPS_sec_primed = false; 78 | bool PPS_done = false; 79 | 80 | byte byteIndex = 0; 81 | 82 | 83 | // SONY REMOTE CONTROL HANDLERS // 84 | unsigned int IR_start_bit = 2000; //Start bit threshold (Microseconds) 2408 85 | unsigned int IR_1 = 1000; //Binary 1 threshold (Microseconds) 1184-1240 86 | unsigned int IR_0 = 400; //Binary 0 threshold (Microseconds) 556-640 87 | unsigned int IR_timeout = 2700; 88 | 89 | bool counter_enable = true; 90 | 91 | /* ----------------------- MAX STUFF ALL HERE ---------------------------vvvvvv 92 | ICSP BLOCK PINOUT 93 | 5 3 1 94 | 6 4 2 95 | 96 | ===MAPPING SPI HARDWARE AND ARDUINO'S TO MAX7219 MODULES=== 97 | 98 | 7219 MODULE: DIN LOAD(CS) CLK (*n/a*) 99 | SPI: MOSI SS SCK MISO 100 | UNO/NANO: 11/ICSP-4 10 13/ICSP-3 12/ICSP-1 101 | MEGA: 51/ICSP-4 53 52/ICSP-3 50/ICSP-1 102 | * 103 | [CS(SS) can actually be any unused PIN on the uController, but these 104 | are the typical conventions.] 105 | */ 106 | 107 | 108 | // Max7219 digit bitmaps for digit registers 0x01 to 0x08 109 | 110 | const byte dp = 0b10000000; // Decimal Point. Do bitwise OR to combine with any digit to add decimal point 111 | 112 | const byte blank = 0b00000000; 113 | const byte hyphen = 0b00000001; 114 | const byte excl = 0b10100000; //exclamation point! 115 | 116 | // 0b0abcdefg 117 | const byte A = 0b01110111; 118 | const byte B = 0b00011111; 119 | const byte C = 0b01001110; 120 | const byte D = 0b00111101; 121 | const byte E = 0b01001111; 122 | const byte F = 0b01000111; 123 | const byte G = 0b01111011; 124 | const byte H = 0b00110111; 125 | const byte I = 0b00110000; 126 | const byte J = 0b00111100; 127 | const byte L = 0b00001110; 128 | const byte N = 0b00010101; 129 | const byte O = 0b00011101; 130 | const byte P = 0b01100111; 131 | const byte Q = 0b01110011; 132 | const byte R = 0b00000101; 133 | const byte S = 0b01011011; 134 | const byte T = 0b00001111; 135 | const byte U = 0b00011100; 136 | const byte M, K, V, W, X 137 | = 0b00000000; 138 | const byte Y = 0b00111011; 139 | const byte Z = 0b01101101; 140 | 141 | 142 | byte char_library[28] = {blank,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,hyphen}; 143 | 144 | //array of digits 0-9 with corresponding segment bit maps 145 | byte digit[10] = {0b01111110, //0 146 | 0b00110000, //1 147 | 0b01101101, //2 148 | 0b01111001, //3 149 | 0b00110011, //4 150 | 0b01011011, //5 151 | 0b01011111, //6 152 | 0b01110000, //7 153 | 0b01111111, //8 154 | 0b01110011}; //9 155 | 156 | // 0b0abcdefg 157 | // -- a 158 | // f | | b 159 | // g -- 160 | // e | | c 161 | // d -- 162 | //0b0abcdefg 163 | 164 | // Max7219 Address Registers 165 | const byte reg_nonop = 0x00; // generally not used 166 | const byte reg_d1 = 0x01; // "Digit0" in the datasheet 167 | const byte reg_d2 = 0x02; // "Digit1" in the datasheet 168 | const byte reg_d3 = 0x03; // "Digit2" in the datasheet 169 | const byte reg_d4 = 0x04; // "Digit3" in the datasheet 170 | const byte reg_d5 = 0x05; // "Digit4" in the datasheet 171 | const byte reg_d6 = 0x06; // "Digit5" in the datasheet 172 | const byte reg_d7 = 0x07; // "Digit6" in the datasheet 173 | const byte reg_d8 = 0x08; // "Digit7" in the datasheet 174 | const byte reg_decode = 0x09; // 0x00 no decode, to 0xFF decode all; use a bit map to toggle, ie 0b00000010 175 | const byte reg_intensity = 0x0A; // min 0x00, max 0x0F... 16 duty-cycle options. 0x07 middle. 176 | const byte reg_scanlimit = 0x0B; // from 0x00 to 0x07 (sets number of digits being scanned) 177 | const byte reg_shutdown = 0x0C; // 0x00 shutdown mode, 0x01 normal ops 178 | const byte reg_displaytest = 0X0F; // 0x00 normal ops, 0x01 display test mode 179 | 180 | void SPIwrite (byte reg_address, byte regdata) { 181 | //Writes 2 bytes to SPI. This is optimized for Max7219 Comms. 182 | 183 | digitalWrite(ChipSelectPin,LOW); // take the CS/SS pin low to select the chip 184 | SPI.transfer(reg_address); 185 | SPI.transfer(regdata); 186 | digitalWrite(ChipSelectPin,HIGH); // take the CS/SS pin high to deselect the chip 187 | 188 | } //end of SPIwrite 189 | 190 | void setAllDigitsTo (byte set_digit) { 191 | //this sets all the digits to set_digit 192 | 193 | for (byte i=1;i<=8;i++) { 194 | SPIwrite(i,set_digit); 195 | } //end for 196 | 197 | } //end of setAllDigitsTo() 198 | 199 | 200 | void initializeMax7219() { 201 | 202 | //reset the Max by activating shutdown mode 203 | SPIwrite(reg_shutdown,0x00); // 0x00 shutdown, 0x01 normal ops 204 | 205 | //initialize each digit with known values 206 | setAllDigitsTo(hyphen); 207 | 208 | //set intensity 209 | SPIwrite(reg_intensity, 0x07); // min 0x00, max 0x0F... 16 duty-cycle options. 0x07 middle. 210 | 211 | //set scan limit 212 | SPIwrite(reg_scanlimit, 0x07); // from 0x00 to 0x07 (sets number of digits being scanned) 213 | 214 | //set decode 215 | SPIwrite(reg_decode, 0b00000000); // built-in decode, from 0x00 [all off] to 0xFF [all on] (bit map toggles digits 1-8). 216 | 217 | //flash a display test 218 | SPIwrite(reg_displaytest, 0x01); // 0x00 normal ops, 0x01 display test mode 219 | delay(100); 220 | 221 | //end the test 222 | SPIwrite(reg_displaytest, 0x00); // 0x00 normal ops, 0x01 display test mode 223 | delay(100); 224 | 225 | //exit shutdown mode. resume normal ops 226 | SPIwrite(reg_shutdown,0x01); // 0x00 shutdown, 0x01 normal ops 227 | delay (100); 228 | 229 | } //end of initializeMax7219 230 | 231 | void maxDisplay(byte a, byte b, byte c, byte d, byte e, byte f, byte g, byte h) { 232 | SPIwrite(8,a); 233 | SPIwrite(7,b); 234 | SPIwrite(6,c); 235 | SPIwrite(5,d); 236 | SPIwrite(4,e); 237 | SPIwrite(3,f); 238 | SPIwrite(2,g); 239 | SPIwrite(1,h); 240 | } 241 | 242 | 243 | //**** ISR ROUTINE ****// 244 | 245 | void ISR_pulse_detected() { 246 | pulseChangeTime = micros(); 247 | pulseFlag = 1; 248 | } //end of ISR_pulse_detected() 249 | 250 | 251 | //**** SONY REMOTE ROUTINES ****// 252 | 253 | void processSonyIR(int code) { // Sony IR code processors 254 | 255 | static byte brightness = 0x07; 256 | 257 | if (code == 0xCD15) { // displays the date 258 | displayRTCDate(); 259 | StateMachine = TOGGLE_DISPLAY; 260 | t1 = millis(); //sets t1 for date display in TOGGLE_DISPLAY state machine 261 | } //end if 262 | 263 | if (code == 0xCD61) { // toggles UTC vs local time 264 | UTC_offset_enable = !UTC_offset_enable; 265 | } //end if 266 | 267 | if (code == 0xCD25) { // Starts counter mode 268 | countSS = 0; //we reset all the counter registers to 0 269 | countMM = 0; 270 | countHH = 0; 271 | displayRTC_timeOnMax(countHH,countMM,countSS); 272 | StateMachine = COUNTER; 273 | } //end if 274 | 275 | if (code == 0xCD52) { // turns counter timer on and off 276 | counter_enable = !counter_enable; 277 | } //end if 278 | 279 | if (code == 0xCD20) { // Goes into REG_OPS (used to exit counter mode) 280 | StateMachine = REG_OPS; 281 | } //end if 282 | 283 | if (code == 0xCD3D) { // does a 4 second check displaying whether PPS signal is active 284 | StateMachine = CHECK_PPS; 285 | maxDisplay(P,P,S,blank,O,F,F,blank); // set this as default display. will be overwritten if PPS is active 286 | t1 = millis(); //sets t1 for date display in CHECK_PPS state machine 287 | } //end if 288 | 289 | if (code == 0xCD76) { // toggles through brightness levels 290 | delay(160); // Sony IR transmits 3 signals w/ each button press. This delay avoids repeat commands here. 291 | switch (brightness) { 292 | case 0x07: 293 | brightness = 0x0F; 294 | break; 295 | case 0x0F: 296 | brightness = 0x01; 297 | break; 298 | case 0x01: 299 | brightness = 0x07; 300 | break; 301 | } // end of switch 302 | 303 | SPIwrite(reg_intensity, brightness); // min 0x00, max 0x0F... 16 duty-cycle options. 0x07 middle. 304 | 305 | } //end if 306 | 307 | 308 | if (code == 0xCD10) { // display status of local or UTC time 309 | if (UTC_offset_enable) { 310 | maxDisplay(L,O,C,A,L,blank,blank,blank); 311 | } //end if 312 | else { 313 | maxDisplay(blank,U,T,C,blank,blank,blank,blank); 314 | } //end else 315 | StateMachine = TOGGLE_DISPLAY; 316 | t1 = millis(); //sets t1 for date display in TOGGLE_DISPLAY state machine 317 | } //end if 318 | } //end processSonyIR() 319 | 320 | void SonyIR_analyzer() { // see http://www.righto.com/2010/03/understanding-sony-ir-remote-codes-lirc.html 321 | static bool startFlag = false; 322 | static byte bitCount = 99; 323 | static bool valid = false; 324 | static byte state = 0; // {0 startup, 1 initial, 2 process} 325 | 326 | static unsigned int lastTime = 0; 327 | static unsigned int delta = 0; 328 | static unsigned int IRvalue = 0; //could experiment with this being 16. might work fine. 329 | 330 | delta = pulseChangeTime - lastTime; 331 | 332 | pulseFlag = 0; // reset the global volatile that got us here 333 | 334 | if (delta > IR_start_bit && delta < IR_timeout) { //we test for a startbit first 335 | state = 1; 336 | } //end if 337 | 338 | switch (state) { 339 | case 1: 340 | startFlag = true; 341 | bitCount = 0; 342 | valid = false; // even is false, odd is true for bitCount 343 | IRvalue = 0; 344 | state = 2; 345 | break; //end case 1 346 | 347 | case 2: 348 | if (startFlag && valid) { //we have started, and valid bit -- ie, not a spacer. 349 | bitCount++; 350 | IRvalue = IRvalue | ( (delta > IR_1) << (bitCount - 1)); 351 | valid = !valid; 352 | if (bitCount == 20) { //bitfield full. Do final process on IRvalue. 353 | processSonyIR(IRvalue); //action we take with IRvalue. 354 | startFlag = false; 355 | IRvalue = 0; 356 | state = 0; 357 | bitCount = 99; 358 | } //end if 359 | } //end if 360 | else if (startFlag && !valid) { 361 | valid = !valid; 362 | } //end elseif 363 | break; //end case 2 364 | }// end switch 365 | 366 | lastTime = pulseChangeTime; 367 | 368 | } //end SonyIR_analyzer() 369 | 370 | 371 | //**** UNIVERSAL FUNCTIONS ****// 372 | 373 | byte bcd2dec(byte n) { 374 | // Converts binary coded decimal to normal decimal numbers 375 | return ((n/16 * 10) + (n % 16)); 376 | } //end of bcd2dec() 377 | 378 | byte dec2bcd(byte n) { //n must be in range [0..99] incl 379 | // Converts normal decimal to binary coded decimal. Speed optimized. 380 | // return ((n / 10 * 16) + (n % 10)); <----- slower method. 381 | // see https://forum.arduino.cc/index.php?topic=185235.msg1372439#msg1372439 382 | 383 | uint16_t a = n; 384 | byte b = (a*103) >> 10; 385 | return n + b*6; 386 | 387 | } //end of dec2bcd() 388 | 389 | 390 | void clearSerialInputBuffer() { 391 | while (Serial.available() > 0) { 392 | Serial.read(); 393 | }//end while 394 | } //end of clearSerialInputBuffer() 395 | 396 | 397 | //**** DISPLAY HANDLERS **** // 398 | 399 | void displayRTC_timeOnMax(byte rtc_h, byte rtc_m, byte rtc_s) { //receiving [0-99] decimals from caller 400 | 401 | maxDisplay(digit[(rtc_h/10)%10],digit[rtc_h%10],hyphen, 402 | digit[(rtc_m/10)%10],digit[rtc_m%10],hyphen, 403 | digit[(rtc_s/10)%10],digit[rtc_s%10]); 404 | 405 | } //end of displayRTC_timeOnMax() 406 | 407 | 408 | //**** GPS HANDLERS **** // 409 | 410 | bool PPS_detect() { // top of the second for ublox 6 GPS (default setting) is rising edge of PPS time pulse 411 | GPS_PPS_Prev = GPS_PPS_Current; 412 | GPS_PPS_Current = digitalRead(GPS_PPS_Pin); 413 | return (GPS_PPS_Prev == LOW && GPS_PPS_Current == HIGH); //returns true if PPS has gone high! 414 | } // end of PPS_detect() 415 | 416 | void processNMEA() { 417 | byte inByte; 418 | if (Serial.available() > 0) { //do all of this only if byte ready to read 419 | inByte = Serial.read(); 420 | byteIndex++; // we only increment index if we read a byte 421 | 422 | switch (byteIndex) { 423 | case 1 ... 3: 424 | break; 425 | case 4: 426 | GGA_msg = (inByte == 'G'); 427 | RMC_msg = (inByte == 'R'); 428 | ZDA_msg = (inByte == 'Z'); 429 | break; 430 | case 5: 431 | GGA_msg = (GGA_msg && inByte == 'G'); 432 | RMC_msg = (RMC_msg && inByte == 'M'); 433 | ZDA_msg = (ZDA_msg && inByte == 'D'); 434 | break; 435 | case 6: 436 | GGA_msg = (GGA_msg && inByte == 'A'); 437 | RMC_msg = (RMC_msg && inByte == 'C'); 438 | ZDA_msg = (ZDA_msg && inByte == 'A'); 439 | NMEA_processFlag = (GGA_msg || RMC_msg || ZDA_msg); // if we have any of these, we keep processing 440 | break; 441 | case 7: 442 | break; 443 | case 8: // hour tens 444 | hhGPS = (inByte - '0')*10; 445 | break; 446 | case 9: //hour units 447 | hhGPS += (inByte - '0'); 448 | break; 449 | case 10: // min tens 450 | mmGPS = (inByte - '0')*10; 451 | break; 452 | case 11: //min units 453 | mmGPS += (inByte - '0'); 454 | break; 455 | case 12: // sec tens 456 | ssGPS = (inByte - '0')*10; 457 | break; 458 | case 13: //sec units 459 | ssGPS += (inByte - '0'); 460 | //**AT THIS POINT WE HAVE GPS TIME**// CAN ACTUALLY UPDATE RTC! 461 | newGPS_timeAvail = true; 462 | NMEA_processFlag = ZDA_msg; //if we have ZDA, this is true and we keep processing. 463 | break; 464 | case 14 ... 17: //** WILL NEED TO TUNE THIS TO ACTUAL OUTPUT OF NEO 6M 465 | break; 466 | case 18: //day tens 467 | ddGPS = (inByte - '0')*10; 468 | break; 469 | case 19: //day units 470 | ddGPS += (inByte - '0'); 471 | break; 472 | case 20: 473 | break; 474 | case 21: //mo tens 475 | moGPS = (inByte - '0')*10; 476 | break; 477 | case 22: //mo units 478 | moGPS += (inByte - '0'); 479 | break; 480 | case 23: 481 | break; 482 | case 24: 483 | yyyyGPS = (inByte - '0')*1000; 484 | break; 485 | case 25: 486 | yyyyGPS += (inByte - '0')*100; 487 | break; 488 | case 26: 489 | yyyyGPS += (inByte - '0')*10; 490 | break; 491 | case 27: 492 | yyyyGPS += (inByte - '0'); 493 | newGPS_dateAvail = true; //all date fields now read 494 | NMEA_processFlag = false; //and no need to process further 495 | break; 496 | default: 497 | NMEA_processFlag = false; 498 | 499 | } //end switch byteIndex 500 | } //end if Serial available 501 | } //end of processNMEA 502 | 503 | /* // 123456789012345678901234567||| [27] GPS unit gives 2 decimals on time 504 | // $GPGGA,hhmmss.tt,... 505 | // /* $GPGGA, $GPRMC: $GPRMC,hhmmss.tt,... 506 | // $GPZDA: $GPZDA,hhmmss.tt,dd,mm,yyyy,... 507 | */ 508 | 509 | //**** UTC TIMEZONE OFFSET AND DST HANDLERS ****// 510 | 511 | void offsetAdj(int y, byte mo, byte d, byte h, byte m, char offsetHr, char offsetMin) { 512 | offMM = (m + offsetMin + 120) % 60; 513 | if (m + offsetMin > 59) { 514 | offsetHr ++; 515 | } 516 | if (m + offsetMin < 0) { 517 | offsetHr --; 518 | } 519 | offHH = (h + offsetHr + 24) % 24; 520 | offDD = d; 521 | offYYYY = y; 522 | offMO = mo; 523 | if (offsetHr + h < 0) { 524 | //Do a decrement 525 | if (d > 1) { 526 | offDD--; 527 | } 528 | else { //rollover 529 | offDD = days[((mo+11-1)%12+1)] + (mo==2 && isLeap(y)); 530 | offMO--; 531 | if (mo == 1) { 532 | offMO = 12; 533 | offYYYY = y - 1; 534 | }//end if 535 | } 536 | } //end if for decrement day 537 | if (offsetHr + h >= 24) { 538 | // Do an increment 539 | if (d == (days[mo] + (mo == 2 && isLeap(y)))) { //ie, if d is last day of month 540 | offDD = 1; 541 | offMO++; 542 | if (mo == 12) { 543 | offMO = 1; 544 | offYYYY = y + 1; 545 | } 546 | }//end if 547 | else { 548 | offDD = d+1; 549 | } 550 | } //end if increment day 551 | } //end offsetAdj 552 | 553 | bool isLeap (byte y) { 554 | return ((y%4==0) || (!(y%100==0) && (y%400==0))); 555 | } //end isLeap() 556 | 557 | byte dowDate(int y, byte n, byte dow_target, byte m) { // returns date number in month of nth day of week in month 558 | char temp = 1; // char because could be negative. Set at 1 for 1st of month. 559 | byte maxDays = days[m]; // number of days in given month 560 | if (m==2) { 561 | maxDays = days[m] + isLeap(y); 562 | } 563 | byte startDOW = dow(y,m,1); //gets dow of 1st of given month 564 | temp += dow_target - dow(y,m,1); 565 | if (temp < 0) { 566 | temp += 7; 567 | } //end if 568 | temp += 7*(n-1); 569 | if (temp > maxDays) { 570 | temp -= 7; 571 | } 572 | return temp; 573 | } //end dowDate() 574 | 575 | byte dow(int y, byte m, byte d) { //pass non-leading zero values 576 | static byte t[] = {0,3,2,5,0,3,5,1,4,6,2,4}; 577 | y -= m < 3; 578 | return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7; 579 | // returns [0..6] for [Sunday...Saturday] 580 | } //end of dow() 581 | 582 | void getLocalTime(int y, byte mo, byte d, byte h, byte m) { 583 | // **this function sets offMO, offDD, offYYYY, offHH, offMM to local time** 584 | // we first get a baseline offset 585 | // NOTE: int y is being passed as RTC's 2-digit date. 586 | 587 | if (yyyyGPS == 0) { // Converting RTC 2 digit date to 4 digit: 588 | y += currentCentury; // takes our 2-digit year and converts to 4 digit 589 | } 590 | else { // or if GPS signal available, we just use that 591 | y = yyyyGPS; // uses GPS 4-digit year if available 592 | } 593 | 594 | offsetAdj(y,mo,d,h,m,offsetStandardHr,offsetStandardMin); 595 | 596 | // next we do our DST checks, and recalc offset if DST is in effect 597 | 598 | if (offMO > startDST[2] && offMO < startStandard[2]) { 599 | offsetAdj(y,mo,d,h,m,offsetDSTHr,offsetDSTMin); 600 | } //end if 601 | else if (offMO == startDST[2]) { 602 | byte dowDateDST = dowDate(y,startDST[0],startDST[1],startDST[2]); 603 | if (offDD > dowDateDST) { 604 | offsetAdj(y,mo,d,h,m,offsetDSTHr,offsetDSTMin); 605 | } //end if 606 | else if (offDD == dowDateDST && offHH >= startDST[3]) { 607 | offsetAdj(y,mo,d,h,m,offsetDSTHr,offsetDSTMin); 608 | } //end else if 609 | } //end else if 610 | else if (offMO == startStandard[2]) { 611 | byte dowDateStd = dowDate(y,startStandard[0],startStandard[1],startStandard[2]); 612 | if (offDD < dowDateStd) { 613 | offsetAdj(y,mo,d,h,m,offsetDSTHr,offsetDSTMin); 614 | }// end if 615 | else if (offDD == dowDateStd && offHH < (startStandard[3]-1)) { 616 | offsetAdj(y,mo,d,h,m,offsetDSTHr,offsetDSTMin); 617 | }// end else if 618 | } //end else if 619 | } //end getLocalTime() 620 | 621 | 622 | //**** RTC HANDLERS ****// 623 | 624 | void getRTC() { //reads all current time/date data from DS3231 chip via I2C 625 | // ssRTC, mmRTC, hhRTC, dowRTC, ddRTC, moRTC, ctyRTC, yyRTC; 626 | 627 | byte temp_buffer; 628 | 629 | Wire.beginTransmission(RTC_I2C_ADDRESS); 630 | Wire.write(0x00); //set register points to address 00h on DS3231 631 | Wire.endTransmission(); 632 | Wire.requestFrom(RTC_I2C_ADDRESS, 7); // need 7 reads to clear this. 633 | ssRTC = bcd2dec(Wire.read()); // read reg 0 [range 00-59] 634 | mmRTC = bcd2dec(Wire.read()); // read reg 1 [range 00-59] 635 | hhRTC = bcd2dec(Wire.read() & 0b00111111); // read reg 2 and mask out BITS 7-8 636 | dowRTC = bcd2dec(Wire.read()); // read reg 3 [range 1-7] 637 | ddRTC = bcd2dec(Wire.read()); // read reg 4 [range 01-31] 638 | temp_buffer = bcd2dec(Wire.read()); // read reg 5 639 | moRTC = bcd2dec(temp_buffer & 0b00011111); 640 | ctyRTC = temp_buffer >> 7; 641 | yyRTC = bcd2dec(Wire.read()); //read reg 6 [range 00-99] 642 | 643 | } //end of getRTC() 644 | 645 | bool RTC_detect() { //Detects DS3231 SQW falling edge 646 | RTC_SQW_Prev = RTC_SQW_Current; 647 | RTC_SQW_Current = digitalRead(RTC_SQW_Pin); 648 | return (RTC_SQW_Prev == HIGH && RTC_SQW_Current == LOW); 649 | } //end of detect_RTC 650 | 651 | void displayRTCDate() { // adjusts to local date if flag set 652 | getRTC(); // updates ssRTC, mmRTC, hhRTC, dowRTC, ddRTC, moRTC, ctyRTC, yyRTC; 653 | if (UTC_offset_enable == false) { // if flag false, we do UTC time and date 654 | displayRTC_timeOnMax(ddRTC,moRTC,yyRTC); // displays UTC date 655 | } //end if 656 | else { // else requests local offset time 657 | getLocalTime(yyRTC,moRTC,ddRTC,hhRTC,mmRTC); // updates offDD,offMO,offYYYY 658 | displayRTC_timeOnMax(offDD,offMO,offYYYY % 100); 659 | } 660 | } //end displayRTCDate 661 | 662 | 663 | void countUp() { 664 | 665 | RTC_SQW_Prev = RTC_SQW_Current; 666 | RTC_SQW_Current = digitalRead(RTC_SQW_Pin); 667 | if (counter_enable && RTC_SQW_Prev == HIGH && RTC_SQW_Current == LOW) { //tests for falling edge 668 | 669 | countSS++; 670 | 671 | if (countSS == 60) { 672 | countSS = 0; 673 | countMM++; 674 | } //end if 675 | if (countMM == 60) { 676 | countMM = 0; 677 | countHH++; 678 | } //end if 679 | if (countHH == 100) { 680 | countHH = 0; 681 | } 682 | 683 | displayRTC_timeOnMax(countHH,countMM,countSS); 684 | 685 | } //end if 686 | } //end of countUp 687 | 688 | void displayRTC() { //updates display if new RTC time. Detects DS3231 SQW falling edge then trigger display of time update 689 | 690 | RTC_SQW_Prev = RTC_SQW_Current; 691 | RTC_SQW_Current = digitalRead(RTC_SQW_Pin); 692 | 693 | if (RTC_SQW_Prev == HIGH && RTC_SQW_Current == LOW) { //test for falling edge 694 | getRTC(); // updates ssRTC, mmRTC, hhRTC, dowRTC, ddRTC, moRTC, ctyRTC, yyRTC; 695 | if (UTC_offset_enable == false) { // flag requests UTC time 696 | displayRTC_timeOnMax(hhRTC,mmRTC,ssRTC); 697 | } //end if 698 | else { // flag requests local offset time 699 | getLocalTime(yyRTC,moRTC,ddRTC,hhRTC,mmRTC); 700 | displayRTC_timeOnMax(offHH,offMM,ssRTC); 701 | } //end else 702 | } //end if 703 | } // end of displayRTC() 704 | 705 | void displayRTC_now() { //immediate retrieve and display of RTC time registers 706 | getRTC(); // updates ssRTC, mmRTC, hhRTC, dowRTC, ddRTC, moRTC, ctyRTC, yyRTC; 707 | displayRTC_timeOnMax(hhRTC,mmRTC,ssRTC); 708 | } // end of displayRTC() 709 | 710 | void sendRTC(byte reg_addr, byte byte_data) { 711 | Wire.beginTransmission(RTC_I2C_ADDRESS); 712 | Wire.write(reg_addr); //set register pointer to address on DS3231 713 | Wire.write(byte_data); 714 | Wire.endTransmission(); 715 | } //end of sendRTC() 716 | 717 | byte getSingleRTC(byte reg_addr) { //returns a single raw byte from the provided address register 718 | Wire.beginTransmission(RTC_I2C_ADDRESS); 719 | Wire.write(reg_addr); //set to reg address on DS3231 720 | Wire.endTransmission(); 721 | Wire.requestFrom(RTC_I2C_ADDRESS, 1); 722 | return (Wire.read()); 723 | } //end of getRTC_BCD() 724 | 725 | void setRTC_Time(byte hh, byte mm, byte ss) { //must be [0-99] 726 | // example use: setRTC_Time(23,49,50); //hh,mm,ss 727 | 728 | Wire.beginTransmission(RTC_I2C_ADDRESS); 729 | Wire.write(0x00); //set register pointer to address on DS3231 730 | Wire.write(dec2bcd(ss)); //set seconds 731 | Wire.write(dec2bcd(mm)); //set minutes 732 | Wire.write(dec2bcd(hh)); //set hours. Bit 6 low keeps at 24hr mode. So can leave as is. 733 | Wire.endTransmission(); 734 | } //end of setRTC_Time() 735 | 736 | void setRTC_Date(int yyyy, byte mo, byte dd) { 737 | Wire.beginTransmission(RTC_I2C_ADDRESS); 738 | Wire.write(0x04); //set register pointer to address on DS3231 739 | Wire.write(dec2bcd(dd)); //set date. sending the last 2 digits only. 740 | Wire.write(dec2bcd(mo)); //set month. ignore century as don't have any use for that. 741 | Wire.write(dec2bcd(yyyy % 100)); //set year. sending the 2 LS digits only. 742 | Wire.endTransmission(); 743 | } //end of setRTC_Time() 744 | 745 | //**** STATE MACHINE **** // 746 | 747 | void RunStateMachine() { 748 | byte temp_buffer; 749 | 750 | switch (StateMachine) { 751 | 752 | case DEBUG: //remember, it's looping! 753 | 754 | if (RTC_detect()) { 755 | t1=micros(); 756 | } 757 | if (PPS_detect()) { 758 | t2=micros(); 759 | } 760 | break; //end DEBUG case 761 | 762 | 763 | // ------------------------------ 764 | 765 | case BOOTUP: 766 | sendRTC(0x0E,0x00); // enables the 1Hz pulse on RTC DS3231's SQW pin 767 | delay(50); 768 | temp_buffer = getSingleRTC(0x02); //get hour byte from addr 0x02. Bit 6: LOW (0) = 24 hr mode. HIGH (1) = 12 hr. 769 | if ((temp_buffer & 0b01000000) != 0) { //if Bit 6 is HIGH, i.e., if 24 hour time is *not* set, then... 770 | getRTC(); // grab time 771 | if (mmRTC > 58 && ssRTC > 58) { // checking to make sure not near an hours rollover. 772 | break; //keep breaking until we roll over the seconds 773 | } //end if 774 | else { 775 | sendRTC(0x02,temp_buffer ^ 0b01000000); //set BIT 6 low to enable 24 hr time 776 | } //end else 777 | } //end if 778 | 779 | //StateMachine = DEBUG; // >>>> State Change! <<<>>> State Change! <<<>>> State Change! <<<>>> State Change! <<< 4000) { //timeout this state after 4 secs if no PPS detected 857 | StateMachine = GPS_NMEA_SYNC; // >>>> State Change! <<<>>> State Change! <<< 0) { 905 | if (Serial.read() == '$') { //start of NMEA message 906 | NMEA_processFlag = true; //we only want to start processing at beginning of message 907 | GGA_msg = false; 908 | RMC_msg = false; 909 | ZDA_msg = false; 910 | byteIndex = 1; 911 | } //end if 912 | } //end if 913 | } //end else 914 | 915 | if (millis() - GPS_INIT_t0 > 14000) { //timeout this state after 10 secs if no PPS detected 916 | StateMachine = REG_OPS; // >>>> State Change! <<<>>> State Change! <<< 0) { 946 | if (Serial.read() == '$') { //start of NMEA message 947 | NMEA_processFlag = true; 948 | GGA_msg = false; 949 | RMC_msg = false; 950 | ZDA_msg = false; 951 | byteIndex = 1; 952 | } //end if 953 | } //end if 954 | } //end else 955 | 956 | if (millis() - GPS_INIT_t0 > 14000) { //timeout this state after 10 secs if no PPS detected 957 | StateMachine = REG_OPS; // >>>> State Change! <<<