// Example program for the MD_DS3231 library // // Test the library and control all aspects of the RTC chip functions // using from the serial monitor. #include <MD_DS3231.h> #include <Wire.h> #define PRINTS(s) Serial.print(F(s)) #define PRINT(s, v) { Serial.print(F(s)); Serial.print(v); } void setup() { Serial.begin(9600); usage(); RTC.setAlarm1Callback(cbAlarm1); RTC.setAlarm2Callback(cbAlarm2); } void usage(void) { PRINTS("\n[MD_DS3231_Tester]"); PRINTS("\n?\thelp - this message"); PRINTS("\ntw yyyymmdd hhmmss dw\twrite the current date, time and day of week (1-7)"); PRINTS("\ntr\tread the current time"); PRINTS("\nrr\tread the IC registers as raw RAM"); PRINTS("\ns\tstatus of the RTC"); PRINTS("\nd\tcalculate day of week from current date"); PRINTS("\n\na1i v\talm 1 interrupt enable (v 0=disabled, 1=enabled)"); PRINTS("\na1f\talm 1 flag reset"); PRINTS("\na1t v\talm 1 set type (v 0=all s, 1=s, 2=s+m, 3=s+m+h, 4=s+m+h+dt, 5=s+m+h+dy)"); PRINTS("\na1z\talm 1 zero all values"); PRINTS("\na1w t v\talm 1 write type t=(as above) time v=yyyymmdd hhmmss dw"); PRINTS("\n\na2i v\talm2 interrupt enable (v 0=disabled, 1=enabled)"); PRINTS("\na2f\talm 2 flag reset"); PRINTS("\na2t v\talm2 set type (v 0=all m, 1=m, 2=m+h, 3=m+h+dt, 4=m+h+dy)"); PRINTS("\na2z\talm 2 zero all values"); PRINTS("\na2w t v\talm 2 write type t=(as above) time v=yyyymmdd hhmmss dw"); PRINTS("\n\nc n v\tcontrol write value v to status n, where n is"); PRINTS("\n\t0 - Clock Halt (v 0=run, 1=halt)"); PRINTS("\n\t1 - BB SQW Enable(v 0=halt, 1=run)"); PRINTS("\n\t2 - SQ Wave Freq (v 1=1Hz, 2=1khz, 3=4khz, 4=8kHz)"); PRINTS("\n\t3 - TCXO conversion (v unused)"); PRINTS("\n\t4 - 12 hour mode (v 0=24h, 1=12h)"); PRINTS("\n\t5 - Alm interrupt operation (v 0=disabled, 1=enabled)"); PRINTS("\n\t6 - Reset Halted Flag (v unused)"); PRINTS("\n\t7 - Enabled 32kHz output (v 0=disabled, 1=enabled)"); PRINTS("\n\t8 - Set aging register (v hex value)"); } const char *dow2String(uint8_t code) { static const char *str[] = {" ---", " Sun", " Mon", " Tue", " Wed", " Thu", " Fri", " Sat"}; return(str[code]); } const char *ctl2String(codeRequest_t code) { switch (code) { case DS3231_CLOCK_HALT: return("CLOCK HALT"); case DS3231_SQW_ENABLE: return("SQW ENABLE"); case DS3231_SQW_TYPE: return("SQW TYPE"); case DS3231_12H: return("12H MODE"); case DS3231_TCONV: return("TEMP CONV"); case DS3231_INT_ENABLE: return("INTERRUPT ENABLE"); case DS3231_A1_INT_ENABLE:return("ALM1 ENABLE"); case DS3231_A2_INT_ENABLE:return("ALM2 ENABLE"); case DS3231_HALTED_FLAG:return("HALTED FLAG"); case DS3231_32KHZ_ENABLE:return("32KHZ ENABLE"); case DS3231_BUSY_FLAG: return("BUSY FLAG"); case DS3231_A1_FLAG: return("ALM1 FLAG"); case DS3231_A2_FLAG: return("ALM2 FLAG"); case DS3231_AGING_OFFSET:return("AGING OFFSET"); default: return("??"); }; } const char *sts2String(codeStatus_t code, boolean bHexValue = false) { if (!bHexValue) { switch (code) { case DS3231_ERROR: return("ERROR"); case DS3231_ON: return("ON"); case DS3231_OFF: return("OFF"); case DS3231_SQW_1HZ: return("1Hz"); case DS3231_SQW_1KHZ: return("1KHz"); case DS3231_SQW_4KHZ: return("4KHz"); case DS3231_SQW_8KHZ: return("8KHz"); default: return("??"); } } else { static char sz[5]; sprintf(sz, "0x%02x", code); return(sz); } } const char *alm2String(almType_t alm) { switch (alm) { case DS3231_ALM_ERROR:return("ALM_ERROR"); case DS3231_ALM_SEC: return("ALM_SEC"); case DS3231_ALM_S: return("ALM_S"); case DS3231_ALM_MIN: return("ALM_MIN"); case DS3231_ALM_M: return("ALM_M"); case DS3231_ALM_MS: return("ALM_MS"); case DS3231_ALM_HM: return("ALM_HM"); case DS3231_ALM_HMS: return("ALM_HMS"); case DS3231_ALM_DTHM: return("ALM_DTHM"); case DS3231_ALM_DTHMS:return("ALM_DTHMS"); case DS3231_ALM_DDHM: return("ALM_DDHM"); case DS3231_ALM_DDHMS:return("ALM_DDHMS"); default: return("??"); }; } uint8_t htoi(char c) { c = toupper(c); if (c >= '0' && c <= '9') return(c - '0'); else if (c >= 'A' && c <= 'F') return(c - 'A' + 10); else return(0); } uint8_t i2dig(uint8_t mode) // input 2 digits in the specified base { uint8_t v = 0; char c[3] = "00"; c[0] = readNext(); c[1] = readNext(); switch (mode) { case DEC: v = atoi(c); break; case HEX: v = (htoi(c[0]) << 4) + htoi(c[1]); ; break; } return(v); } char *p2dig(uint8_t v, uint8_t mode) // 2 digits leading zero { static char s[3]; uint8_t i = 0; uint8_t n = 0; switch(mode) { case HEX: n = 16; break; case DEC: n = 10; break; } if (v < n) s[i++] = '0'; itoa(v, &s[i], n); return(s); } void showStatus() { PRINTS("\n>- Settings -<"); PRINT("\n/EOSC Halt Enabled:\t", sts2String(RTC.status(DS3231_CLOCK_HALT))); PRINT("\nBBSQW Sq Wave Enable:\t", sts2String(RTC.status(DS3231_SQW_ENABLE))); PRINT("\nRS1_2 Sq Wave Freq:\t", sts2String(RTC.status(DS3231_SQW_TYPE))); PRINT("\nCONV Temp Convert:\t", sts2String(RTC.status(DS3231_TCONV))); PRINT("\nINTCN Int enable:\t", sts2String(RTC.status(DS3231_INT_ENABLE))); PRINT("\n 12h mode:\t\t", sts2String(RTC.status(DS3231_12H))); PRINT("\nEN32k 32kHz enabled:\t", sts2String(RTC.status(DS3231_32KHZ_ENABLE))); PRINT("\nAGING Aging Offset:\t", sts2String(RTC.status(DS3231_AGING_OFFSET), true)); PRINTS("\n\n>- Status -<"); PRINT("\nBSY Busy Flag:\t\t", sts2String(RTC.status(DS3231_BUSY_FLAG))); PRINT("\nOSF Halted Flag:\t", sts2String(RTC.status(DS3231_HALTED_FLAG))); #if ENABLE_TEMP_COMP PRINT("\nTEMP Temp register:\t", RTC.readTempRegister()); #endif PRINTS("\n\n>- Alarm 1 at "); showAlarm1(); PRINT("\nA1IE Intrpt:\t", sts2String(RTC.status(DS3231_A1_INT_ENABLE))); PRINT("\nA1F Flag:\t", sts2String(RTC.status(DS3231_A1_FLAG))); PRINT("\nA1M1_4 Type:\t", alm2String(RTC.getAlarm1Type())); PRINTS("\n\n>- Alarm 2 at "); showAlarm2(); PRINT("\nA2IE Intrpt:\t", sts2String(RTC.status(DS3231_A2_INT_ENABLE))); PRINT("\nA2F Flag:\t", sts2String(RTC.status(DS3231_A2_FLAG))); PRINT("\nA2M2_4 Type:\t", alm2String(RTC.getAlarm2Type())); } void printTime() { PRINT("", RTC.yyyy); PRINT("-", p2dig(RTC.mm, DEC)); PRINT("-", p2dig(RTC.dd, DEC)); PRINT(" ", p2dig(RTC.h, DEC)); PRINT(":", p2dig(RTC.m, DEC)); PRINT(":", p2dig(RTC.s, DEC)); if (RTC.status(DS3231_12H) == DS3231_ON) PRINT("", RTC.pm ? " pm" : " am"); PRINT("", dow2String(RTC.dow)); } void showTime() { RTC.readTime(); PRINTS("\nTime - "); printTime(); } void showAlarm1() { RTC.readAlarm1(); printTime(); } void showAlarm2() { RTC.readAlarm2(); printTime(); } void showRAM() { #define MAX_READ_BUF (DS3231_RAM_MAX / 8) // do 8 lines uint8_t k, regCount = 0; uint8_t buf[MAX_READ_BUF]; PRINTS("\n\n--------"); for (uint8_t i=0; i<DS3231_RAM_MAX; i+=MAX_READ_BUF) { RTC.readRAM(i, buf, MAX_READ_BUF); PRINT("\n", p2dig(i, HEX)); PRINTS(": "); for (uint8_t j=0; j<MAX_READ_BUF; j++) { if (regCount < DS3231_RAM_MAX) { PRINT("", p2dig(buf[j], HEX)); PRINTS(" "); k = j; // save this index for the next loop regCount++; } else PRINTS(" "); } PRINTS(" "); for (uint8_t j=0; j<=k; j++) { if (isalnum(buf[j]) || ispunct(buf[j])) { PRINT("", (char) buf[j]); } else { PRINTS("."); } PRINTS(" "); } } PRINTS("\n--------"); } void inputTime(void) { RTC.yyyy = i2dig(DEC)*100 + i2dig(DEC); RTC.mm = i2dig(DEC); RTC.dd = i2dig(DEC); RTC.h = i2dig(DEC); RTC.m = i2dig(DEC); RTC.s = i2dig(DEC); RTC.dow = i2dig(DEC); } void showDoW(void) { RTC.readTime(); PRINT("\nCalculated DoW is", dow2String(RTC.calcDoW(RTC.yyyy, RTC.mm, RTC.dd))); } void cbAlarm1() // callback function for Alarm 2 { showTime(); PRINTS("\t-> Alarm 1"); } void cbAlarm2() // callback function for Alarm 2 { showTime(); PRINTS("\t-> Alarm 2"); } void doAlarm1() { char c = readNext(); codeRequest_t item; codeStatus_t value; almType_t alm; switch (toupper(c)) { case 'I': // interrupt enable item = DS3231_A1_INT_ENABLE; c = readNext(); switch (toupper(c)) { case '0': value = DS3231_OFF; break; case '1': value = DS3231_ON; break; default: goto error; } PRINT("\nAlarm 1 ", ctl2String(item)); PRINT(" value ", sts2String(value)); PRINT(" result ", RTC.control(item, value)); break; case 'F': // alarm flag item = DS3231_A1_FLAG; value = DS3231_OFF; PRINT("\nAlarm 1 ", ctl2String(item)); PRINT(" value ", sts2String(value)); PRINT(" result ", RTC.control(item, value)); break; case 'T': // alarm type c = readNext(); switch (toupper(c)) { case '0': alm = DS3231_ALM_SEC; break; case '1': alm = DS3231_ALM_S; break; case '2': alm = DS3231_ALM_MS; break; case '3': alm = DS3231_ALM_HMS; break; case '4': alm = DS3231_ALM_DTHMS; break; case '5': alm = DS3231_ALM_DDHMS; break; default: goto error; } PRINT("\nAlarm 1 set type ", alm2String(alm)); PRINT(" result ", RTC.setAlarm1Type(alm)); break; case 'Z': // zero all values RTC.yyyy = RTC.mm = RTC.dd = 0; RTC.h = RTC.m = RTC.s = 0; RTC.dow = RTC.pm = 0; RTC.writeAlarm1(DS3231_ALM_DTHMS); showAlarm1(); break; case 'W': // write alarm setting c = readNext(); switch (toupper(c)) { case '0': alm = DS3231_ALM_SEC; break; case '1': alm = DS3231_ALM_S; break; case '2': alm = DS3231_ALM_MS; break; case '3': alm = DS3231_ALM_HMS; break; case '4': alm = DS3231_ALM_DTHMS; break; case '5': alm = DS3231_ALM_DDHMS; break; default: goto error; } inputTime(); RTC.writeAlarm1(alm); showAlarm1(); break; default: error: PRINTS("\nBad control element or parameter"); return; } return; } void doAlarm2() { char c = readNext(); codeRequest_t item; codeStatus_t value; almType_t alm; switch (toupper(c)) { case 'I': // interrupt enable item = DS3231_A2_INT_ENABLE; c = readNext(); switch (toupper(c)) { case '0': value = DS3231_OFF; break; case '1': value = DS3231_ON; break; default: goto error; } PRINT("\nAlarm 2 ", ctl2String(item)); PRINT(" value ", sts2String(value)); PRINT(" result ", RTC.control(item, value)); break; case 'F': // alarm flag item = DS3231_A2_FLAG; value = DS3231_OFF; PRINT("\nAlarm 2 ", ctl2String(item)); PRINT(" value ", sts2String(value)); PRINT(" result ", RTC.control(item, value)); break; case 'T': // alarm type c = readNext(); switch (toupper(c)) { case '0': alm = DS3231_ALM_MIN; break; case '1': alm = DS3231_ALM_M; break; case '2': alm = DS3231_ALM_HM; break; case '3': alm = DS3231_ALM_DTHM; break; case '4': alm = DS3231_ALM_DDHM; break; default: goto error; } PRINT("\nAlarm 2 set type ", alm2String(alm)); PRINT(" result ", RTC.setAlarm2Type(alm)); break; case 'Z': // zero all values RTC.yyyy = RTC.mm = RTC.dd = 0; RTC.h = RTC.m = RTC.s = 0; RTC.dow = RTC.pm = 0; RTC.writeAlarm2(DS3231_ALM_DTHM); showAlarm2(); break; case 'W': // write alarm setting c = readNext(); switch (toupper(c)) { case '0': alm = DS3231_ALM_MIN; break; case '1': alm = DS3231_ALM_M; break; case '2': alm = DS3231_ALM_HM; break; case '3': alm = DS3231_ALM_DTHM; break; case '4': alm = DS3231_ALM_DDHM; break; default: goto error; } inputTime(); RTC.writeAlarm2(alm); showAlarm2(); break; default: error: PRINTS("\nBad control element or parameter"); return; } return; } void writeControl() { char c = readNext(); codeRequest_t item; codeStatus_t value; switch (toupper(c)) { case '0': // halt item = DS3231_CLOCK_HALT; c = readNext(); switch (toupper(c)) { case '0': value = DS3231_OFF; break; case '1': value = DS3231_ON; break; default: goto error; } break; case '1': // enable item = DS3231_SQW_ENABLE; c = readNext(); switch (toupper(c)) { case '0': value = DS3231_OFF; break; case '1': value = DS3231_ON; break; default: goto error; } break; case '2': // type on item = DS3231_SQW_TYPE; c = readNext(); switch (toupper(c)) { case '1': value = DS3231_SQW_1HZ; break; case '2': value = DS3231_SQW_1KHZ; break; case '3': value = DS3231_SQW_4KHZ; break; case '4': value = DS3231_SQW_8KHZ; break; default: goto error; } break; case '3': // tcxo conversion item = DS3231_TCONV; value = DS3231_ON; break; case '4': // 12 h mode item = DS3231_12H; c = readNext(); switch (toupper(c)) { case '0': value = DS3231_OFF; break; case '1': value = DS3231_ON; break; default: goto error; } break; case '5': // alm interrupt operation item = DS3231_INT_ENABLE; c = readNext(); switch (toupper(c)) { case '0': value = DS3231_OFF; break; case '1': value = DS3231_ON; break; default: goto error; } break; case '6': // reset halted flag item = DS3231_HALTED_FLAG; value = DS3231_OFF; break; case '7': // enable 32kHz output item = DS3231_32KHZ_ENABLE; c = readNext(); switch (toupper(c)) { case '0': value = DS3231_OFF; break; case '1': value = DS3231_ON; break; default: goto error; } break; case '8': // enable 32kHz output item = DS3231_AGING_OFFSET; value = (codeStatus_t) i2dig(HEX); break; default: error: PRINTS("\nBad control element or parameter"); return; } // do it PRINT("\nControlling ", ctl2String(item)); PRINT(" to ", sts2String(value, (item == DS3231_AGING_OFFSET))); PRINT(", result ", RTC.control(item, value)); return; } char readNext() // Read the next character from the serial input stream, skip whitespace. // Busy loop that also checks for an alarm occurence. { char c; do { while (!Serial.available()) { RTC.checkAlarm1(); RTC.checkAlarm2(); } c = Serial.read(); } while (isspace(c)); return(c); } void loop() { char c; PRINTS("\n"); // we need to get the next character to know what command we want to process c = readNext(); switch (toupper(c)) { case '?': usage(); break; case 'S': showStatus(); break; case 'C': writeControl(); break; case 'D': showDoW(); break; case 'A': c = readNext(); switch(toupper(c)) { case '1': doAlarm1(); break; case '2': doAlarm2(); break; default: goto no_good; } break; // Read functions case 'R': // Display updates c = readNext(); switch (toupper(c)) { case 'R': showRAM(); break; default: goto no_good; } break; // Write functions case 'T': // Display updates c = readNext(); switch (toupper(c)) { case 'R': showTime(); break; case 'W': inputTime(); RTC.writeTime(); showTime(); break; default: goto no_good; } break; default: // don't know what to do with this! no_good: // label for default escape when we can't process a character { PRINT("\nBad parameter '", c); PRINTS("'"); while (Serial.available()) // flush the buffer c = readNext(); } break; } }