Adjusting clock time with buttons

Hello fine people,

I'm using an Arduino Uno R3
128x64 oled display
DS3231 RTC
2 buttons
2 x 330 ohm resistors

Can anyone tell me why the clock time isn't changing automatically on my display?

When I press the button connected to pinMode(9, INPUT); it updates the time on the display but otherwise it sits at
IMG_2184
until the button is pressed again.

Also, I'm trying to delete the alarm function completely. I've taken out everything referencing an alarm but when I take out the remaining few alarm related lines I get slammed with errors, so far mostly about exit status 1 'printDay' was not declared in this scope in line 125 or 127:7: error: 'printMonth' was not declared in this scope in line 127 among several others.

For expamle, deleting anything between lines 337 and 354.

(Sorry, googling now how to display line numbers on this post)

#include <SPI.h> //Library for Adafruit communication to OLED display
#include <Wire.h> //I2C communication library
#include "ds3231.h" //Real Time Clock library
#include <Adafruit_GFX.h> //Graphics library
#include <Adafruit_SSD1306.h> //OLED display library
#include <EEPROM.h> //This library allows reading and writing to the EEPROM


//Uncomment following line to enable display dimming between 10 PM and 5 AM
#define dimming

int framecount2 = 0; //Counter for number of display update periods
//uint8_t secset = 0; //Index for second RTC setting
uint8_t minset = 1; //Index for minute RTC setting
uint8_t hourset = 2; //Index for hour RTC setting
uint8_t wdayset = 3; //Index for weekday RTC setting
uint8_t mdayset = 4; //Index for date RTC setting
uint8_t monset = 5; //Index for month RTC setting
uint8_t yearset = 6; //Index for year RTC setting

//Alarm time variables
uint8_t wake_HOUR = 0;
uint8_t wake_MINUTE = 0;
uint8_t wake_SECOND = 0;
uint8_t wake_SET = 0; //Default alarm to ON in case of power failure or reset

#define OLED_RESET 4 //Define reset for OLED display
Adafruit_SSD1306 display(OLED_RESET); //Reset OLED display

//Check for proper display size - required
#if (SSD1306_LCDHEIGHT != 64)

#endif

unsigned long prev, interval = 100; //Variables for display/clock update rate
byte flash = 0; //Flag for display flashing - toggle once per update interval
byte mode = 0; //Mode for time and date setting
int tempset; //Temporary variable for setting time/date
int beepcount = 0; //Variable for number of 100ms intervals since alarm started sounding
const int alarmEE = 0; //EEPROM alarm status storage location


void setup()

{

  Serial.begin(9600); //Initialize serial port, if needed (not used)
  Wire.begin(); //Initialize I2C communication library


  pinMode(8, INPUT); //Set pin for time/date mode button to input
  digitalWrite(8, HIGH); //Turn on pullup resistors

  pinMode(9, INPUT); //Set pin for time/date set button to input
  digitalWrite(9, HIGH); //Turn on pullup resistors


  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3D (for the 128x64 OLED display)
  display.setTextSize(1); //Set default font size to the smalles
  display.setTextColor(WHITE); //Set font to display color on black background
  display.dim(0); //Set display to full brightness
  display.invertDisplay(0); //Set display to normal video
  // init done
}

void loop()
{
  //char tempF[6]; //Local variable to store converted temperature reading from Real Time Clock module
  //float temperature; //Intermediate temperature variable to convert Celsius to Farenheit
  unsigned long now = millis(); //Local variable set to current value of Arduino internal millisecond run-time timer
  struct ts t; //Structure for retrieving and storing time and date data from real time clock

  //Draw and update display every refresh period (100ms)
  if ((now - prev > interval)) { //Determine whether to start a time and screen update
    framecount2 = framecount2 + 1; //Update counter of refresh periods
    if (framecount2 > 300) {
      framecount2 = 0; //Wrap the refresh period counter to 0 after 300 updates,
      mode = 0; //Reset mode to normal every cycle unless setting buttons pressed to reset cycle counter
    }
    if (flash == 0) {
      flash = 1; //Toggle flash flag for cursor blinking later
    } else {
      flash = 0;
    }
    DS3231_get(&t); //Get time and date and save in t structure


#if defined(dimming)
    if (t.hour >= 18 || t.hour < 5) {
      display.dim(1); //Dim the display between 7 PM and 5 AM
    }
    else {
      display.dim(0); //Otherwise set display to full brightness
    }
#endif


    display.clearDisplay(); //Clear display buffer from last refresh

    //NOTE: Alarm indicators are overwritten in display buffer if full-screen animation is displayed, so no check for that
    if (mode <= 7) { //Alarm indicators and actions in normal and time set display mode only
      if (wake_SET) { //Display alarm on indicator if alarm turned on
        display.setCursor(80, 8); //Position text cursor for alarm on indicator
        display.print("*"); //Print character between time and temperature if alarm on
      }
    }

    if (wake_SET && DS3231_triggered_a1()) { //Display/sound alarm if enabled and triggered

      if (beepcount <= 600) { //Sound alarm for 60 seconds
        if (!flash) { //Flash display and sound interrupted beeper


        }
      }

    }

    if (mode <= 7) {


      //DO NOT CHANGE CURSOR POSITIONING OF TIME AND DATE TEXT FIELDS OR TIME/DATE SET CURSOR WON'T MATCH!!!
      display.setCursor(0, 0); //Position cursor for day-of-week display
      printDay(t.wday); //Lookup day of week string from retrieved RTC data and write to display buffer

      printMonth(t.mon); //Lookup month string from retrieved RTC data and write to display buffer

      if (t.mday < 10) {
        display.print("0"); //Add leading zero to date display if date is single-digit
      }
      display.print(t.mday); //Write date to display buffer
      display.print(", "); //Write spaces and comma between date and year

      display.print(t.year); //Write year to display buffer

      display.setCursor(0, 8); //Position text cursor for time display

      //RTC is operated in 24-hour mode and conversion to 12-hour mode done here, in software
      if (t.hour == 0) {
        display.print("12"); //Convert zero hour for 12-hour display
      }
      else if (t.hour < 13 && t.hour >= 10) {
        display.print(t.hour); //Just display hour if double digit hour
      }
      else if (t.hour < 10) {
        display.print(" ");  //If single digit hour, add leading space
        display.print(t.hour);
      }
      else if (t.hour >= 13 && t.hour >= 22) {
        display.print(t.hour - 12); //If double digit and PM, convert 24 to 12 hour
      }
      else {
        display.print(" ");  //If single digit and PM, convert to 12 hour and add leading space
        display.print(t.hour - 12);
      }

      display.print(":"); //Display hour-minute separator
      if (t.min < 10) {
        display.print("0"); //Add leading zero if single-digit minute
      }
      display.print(t.min); //Display retrieved minutes

      display.print(":"); //Display minute-seconds separator
      if (t.sec < 10) {
        display.print("0"); //Add leading zero for single-digit seconds
      }
      display.print(t.sec); //Display retrieved seconds

      if (t.hour < 12) {
        display.print(" AM"); //Display AM indicator, as needed
      }
      else {
        display.print(" PM"); //Display PM indicator, as needed
      }




      //Position and draw hour tick marks
      for ( int z = 0; z < 360; z = z + 30 ) {

      }


    }

    if (mode > 7) {
      display.setCursor(0, 0); //Position text cursor
      //      display.print("Alarm Set: ");
      //      if(wake_SET){display.print("ON");}else{display.print("OFF");}
      display.setCursor(0, 8); //Position text cursor for time display

      //RTC is operated in 24-hour mode and conversion to 12-hour mode done here, in software
      if (wake_HOUR == 0) {
        display.print("12"); //Convert zero hour for 12-hour display
      }
      else if (wake_HOUR < 13 && wake_HOUR >= 10) {
        display.print(wake_HOUR); //Just display hour if double digit hour
      }
      else if (wake_HOUR < 10) {
        display.print(" ");  //If single digit hour, add leading space
        display.print(wake_HOUR);
      }
      else if (wake_HOUR >= 13 && wake_HOUR >= 22) {
        display.print(wake_HOUR - 12); //If double digit and PM, convert 24 to 12 hour
      }
      else {
        display.print(" ");  //If single digit and PM, convert to 12 hour and add leading space
        display.print(wake_HOUR - 12);
      }

      display.print(":"); //Display hour-minute separator
      if (wake_MINUTE < 10) {
        display.print("0"); //Add leading zero if single-digit minute
      }
      display.print(wake_MINUTE); //Display retrieved minutes

      display.print(":"); //Display minute-seconds separator
      if (wake_SECOND < 10) {
        display.print("0"); //Add leading zero for single-digit seconds
      }
      display.print(wake_SECOND); //Display retrieved seconds

      if (wake_HOUR < 12) {
        display.print(" AM"); //Display AM indicator, as needed
      }
      else {
        display.print(" PM"); //Display PM indicator, as needed
      }
    }


    //Time/Date setting button processing and cursor flashing
    //CURSOR COORDINATES ARE SET TO MATCH TIME/DATE FIELD - DO NOT CHANGE!!
    //Digital and analog time/date display updates with new settings at 5Hz as settings are changed
    switch (mode)
    {
      case 0: break;
      case 1: //Day-of-week setting
        if (flash) {
          display.drawRect(0, 0, 18, 8, WHITE); //Display rectangle cursor every other display update (5Hz blink)
        }
        if (!digitalRead(9) && (!flash)) { //Update setting at 5Hz rate if button held down
          tempset = t.wday; //Get the current weekday and save in temporary variable
          tempset = tempset + 1; //Increment the day at 5Hz rate
          if (tempset > 7) {
            tempset = 1; //Roll over after 7 days
          }
          t.wday = tempset; //After each update, write the day back to the time structure
          set_rtc_field(t, wdayset); //Write the set field only back to the real time clock module after each update
        }
        break;
      case 2: //Month setting
        if (flash) {
          display.drawRect(24, 0, 18, 8, WHITE); //Display rectangle cursor every other display update (5Hz blink)
        }
        if (!digitalRead(9) && (!flash)) { //Update setting at 5Hz rate if button held down
          tempset = t.mon; //Get the current month and save in temporary variable
          tempset = tempset + 1; //Increment the month at 5Hz rate
          if (tempset > 12) {
            tempset = 1; //Roll over after 12 months
          }
          t.mon = tempset; //After each update, write the month back to the time structure
          set_rtc_field(t, monset); //Write the set field only back to the real time clock module after each update
        }
        break;
      case 3: //Date setting
        if (flash) {
          display.drawRect(48, 0, 12, 8, WHITE); //Display rectangle cursor every other display update (5Hz blink)
        }
        if (!digitalRead(9) && (!flash)) { //Update setting at 5Hz rate if button held down
          tempset = t.mday; //Get the current date and save in temporary variable
          tempset = tempset + 1; //Increment the date at 5Hz rate
          //(RTC allows incorrect date setting for months < 31 days, but will use correct date rollover for subsequent months.
          if (tempset > 31) {
            tempset = 1; //Roll over after 31 days
          }
          t.mday = tempset; //After each update, write the date back to the time structure
          set_rtc_field(t, mdayset); //Write the set field only back to the real time clock module after each update
        }
        break;
      case 4: //Year setting
        if (flash) {
          display.drawRect(72, 0, 24, 8, WHITE); //Display rectangle cursor every other display update (5Hz blink)
        }
        if (!digitalRead(9) && (!flash)) { //Update setting at 5Hz rate if button held down
          tempset = t.year; //Get the current year and save in temporary variable
          tempset = tempset + 1; //Increment the year at 5Hz rate
          //RTC allows setting from 1900, but range limited here to 2000 to 2099
          if (tempset > 2099) {
            tempset = 2000; //Roll over after 2099 to 2000
          }
          t.year = tempset; //After each update, write the year back to the time structure
          set_rtc_field(t, yearset); //Write the set field only back to the real time clock module after each update
        }
        break;
      case 5: //Hour setting
        if (flash) {
          display.drawRect(0, 8, 12, 8, WHITE); //Display rectangle cursor every other display update (5Hz blink)
        }
        if (!digitalRead(9) && (!flash)) { //Update setting at 5Hz rate if button held down
          tempset = t.hour; //Get the current hour and save in temporary variable
          tempset = tempset + 1; //Increment the hour at 5Hz rate
          if (tempset > 23) {
            tempset = 0; //Roll over hour after 23rd hour (setting done in 24-hour mode)
          }
          t.hour = tempset; //After each update, write the hour back to the time structure
          set_rtc_field(t, hourset); //Write the set field only back to the real time clock module after each update
        }
        break;
      case 6: //Minute setting
        if (flash) {
          display.drawRect(18, 8, 12, 8, WHITE); //Display rectangle cursor every other display update (5Hz blink)
        }
        if (!digitalRead(9) && (!flash)) { //Update setting at 5Hz rate if button held down
          tempset = t.min; //Get the current minute and save in temporary variable
          tempset = tempset + 1; //Increment the minute at 5Hz rate
          if (tempset > 59) {
            tempset = 0; //Roll over minute to zero after 59th minute
          }
          t.min = tempset; //After each update, write the minute back to the time structure
          set_rtc_field(t, minset); //Write the set field only back to the real time clock module after each update
        }
        break;

      //Set clock + 1 minute, then press and hold to freeze second setting.
      //Release button at 00 seconds to synchronize clock to external time source.
      case 7: //Second synchronization
        if (flash) {
          display.drawRect(36, 8, 12, 8, WHITE); //Display rectangle cursor every other display update (5Hz blink)
        }
        if (!digitalRead(9) && (!flash)) { //Reset second to zero at 5Hz rate if button held down
          t.sec = 0; //After each update, write the zeroed second back to the time structure
          //        set_rtc_field(t, secset); //Write the set field only back to the real time clock module after each update
        }
        break;


        if (!digitalRead(9) && (!flash)) { //Update setting at 5Hz rate if button held down

        }
        break;

        if (!digitalRead(9) && (!flash)) { //Update setting at 5Hz rate if button held down

        }
        break;


        if (!digitalRead(9) && (!flash)) { //Update setting at 5Hz rate if button held down

        }
        break;
    }

    prev = now; //Reset variable for display and time update rate
    display.display(); //Display the constructed frame buffer for this framecount
  }

  //Clock setting mode set - outside time/display update processing for faster button response
  if (!digitalRead(8)) { //Read setting mode button
    delay(25); //100ms debounce time
    if (!digitalRead(8)) { //Activate setting mode change after 100ms button press
      mode = mode + 1; //Increment the time setting mode on each button press
      framecount2 = 0;  //Reset cycle counter if button pressed to delay auto return to normal mode
      if (mode > 10) {
        mode = 0; //Roll the mode setting after 7th mode
      }
      while (!digitalRead(8)) {} //Wait for button release (freezes all display processing and time updates while button held, but RTC continues to keep time)
    }
  }

  if (!digitalRead(9)) { //Reset alarm flag if set button pressed
    delay(25); //25ms debounce time
    if (!digitalRead(9)) {
      DS3231_clear_a1f(); //Reset cycle counter if button pressed to delay auto return to normal mode
      framecount2 = 0;
    }
  }
}



//Function to display month string from numerical month argument
void printMonth(int month)
{
  switch (month)
  {
    case 1: display.print("Jan "); break;
    case 2: display.print("Feb "); break;
    case 3: display.print("Mar "); break;
    case 4: display.print("Apr "); break;
    case 5: display.print("May "); break;
    case 6: display.print("Jun "); break;
    case 7: display.print("Jul "); break;
    case 8: display.print("Aug "); break;
    case 9: display.print("Sep "); break;
    case 10: display.print("Oct "); break;
    case 11: display.print("Nov "); break;
    case 12: display.print("Dec "); break;
    default: display.print("--- "); break; //Display dashes if error - avoids scrambling display
  }
}


//Function to display day-of-week string from numerical day-of-week argument
void printDay(int day)
{
  switch (day)
  {
    case 1: display.print("Mon "); break;
    case 2: display.print("Tue "); break;
    case 3: display.print("Wed "); break;
    case 4: display.print("Thu "); break;
    case 5: display.print("Fri "); break;
    case 6: display.print("Sat "); break;
    case 7: display.print("Sun "); break;
    default: display.print("--- "); break; //Display dashes if error - avoids scrambling display
  }
}

//Subroutine to adjust a single date/time field in the RTC
void set_rtc_field(struct ts t,  uint8_t index)
{
  uint8_t century;

  if (t.year > 2000) {
    century = 0x80;
    t.year_s = t.year - 2000;
  } else {
    century = 0;
    t.year_s = t.year - 1900;
  }

  uint8_t TimeDate[7] = { t.sec, t.min, t.hour, t.wday, t.mday, t.mon, t.year_s };

  Wire.beginTransmission(DS3231_I2C_ADDR);
  Wire.write(index);
  TimeDate[index] = dectobcd(TimeDate[index]);
  if (index == 5) {
    TimeDate[5] += century;
  }
  Wire.write(TimeDate[index]);
  Wire.endTransmission();

  //Adjust the month setting, per data sheet, if the year is changed
  if (index == 6) {
    Wire.beginTransmission(DS3231_I2C_ADDR);
    Wire.write(5);
    TimeDate[5] = dectobcd(TimeDate[5]);
    TimeDate[5] += century;
    Wire.write(TimeDate[5]);
    Wire.endTransmission();
  }
}


Also, the tutorial I'm following didn't use resistors but I still had some connected to the buttons from a previous tutorial and this doesn't seem to work without them. It constantly changes the digits on the screen.

I've got this sketch to compile with no errors so now I'm trying to edit it closer to what I'm looking for. In my deleting however, I'm wondering if I've gotten rid of some things I need for regular function. I can upload a version of the sketch I'm using with the deleted lines commented instead of deleted if that would be more helpful to see.

Please forgive my limited understanding of not only what's going on here but also with any feedback you're able to contribute. But as always, thanks so much for your help, reading this far, and for existing. I never would've made it this far without the help of this forum!

Did you read the forum guidelines?

Try changing:

pinMode(9, INPUT);

into

pinMode(9, INPUT_PULLUP);

An INPUT pin is a high impedance and without a pullup it just floats. Fortunately you don't have to wire one in as one is built in. Simply say INPUT_PULLUP and the floaties should go away.

Not if they are pulled down or something. :slight_smile: I'd rather see the wiring...

I'd love to know what the 330 ohm resistors do.

That worked, I change pinMode(9, INPUT_PULLUP); and took the resistors out which got the seconds changing on their own but now the button isn't doing anything

I added the wiring if this helps.

Thanks. Just, next time please add corrections as new posts, don't edit the first post as it makes replies appear confusing.

330 ohms is acceptable, but very low value for a switch pull up, normally you would see at least 1000 ohms in that application.

What you have shown is a layout diagram, which doesn't give full confidence about the connection information. If you still have switch problems, I would like to see a conventional schematic or else photos.

Have you ohm'ed the switch in its current position, to verify the leads? Or looked carefully at the switch specifications?

Thanks for your patience. I am as new to using forums as I am with electronics so I appreciate the tip and will definitely add corrections as new posts in the future, that makes a sense.

I tried switching it to the 1000 ohms and there is the same result. With both pinMode(8, INPUT); and pinMode(8, INPUT_PULLUP);

Do you know how I can get the seconds to continue changing automatically while maintaining the button's function?

Is this picture an okay size to be helpful? I am still having switch problems. I checked the switch with the multimeter and it seems good. Then I checked it in its current position but I'm not sure what it should be doing.

Holding the switch down the numbers jumps round like this
IMG_2189

I looked at the switch specifications and as far as I can tell they seem okay

I've been trying to make a conventional schematic but I tried to do it for free with a software called Eagle. This software seems like a very powerful tool but maybe a bit much for what I'm trying to do. I think I will just end up buying the fritzing software and hope its a little more beginner friendly because I'm excited to get back to my clock once I am able to show you a conventional schematic. Thanks again for your help!

That is not going to do much good since the switches are wired to 5 V and the resistors to ground in the diagram. :roll_eyes:

It did start the clock back up on the oled but stopped the button function. Do you know how I could wire it differently to keep both, the clock and button function?

No wonder you found it difficult. Eagle will make schematics, but it is really for PCB design. Please don't post Fritzing diagrams here. You can use Fritzing to make actual schematics, but it is almost just as easy to use pen and paper.

Saw it coming, post #4.

One thing, you claim:

  //Clock setting mode set - outside time/display update processing for faster button response

but inside the slower interval controlled block you access the button:

        if (!digitalRead(9) && (!flash)) { //Update setting at 5Hz rate if button held down

Also some style issues... give the pins names. 8 and 9 don't really speak to what they do. Use functions. Your program is one big monolithic block, which makes it hard to read. The "clever" ideas like '!digitalRead(9)' only make this worse. This should look more like
digitalRead(settingsPin) == LOW
Mean what you say, say what you mean. The multitude of if-else in your program is a huge red flag, probably meaning that you have missed many opportunities to simplify the code by using mathematical expressions instead.

Hence, I refuse to read it closely enough to discover the logic problem.

Me too! :thinking:

Don't get me wrong, the potential for good code is definitely there. It just needs some factoring, structure and less obfuscation.

No worries, gang. Thanks anyway :+1:
I'm still having fun trying to figure it out.

There is no need. When posted correctly using code tags, which you did, it is trivial to copy the code to an editor for examination

I'm with @anon57585045 on this matter.

Maybe one day it will make sense to learn up on a real CAD software for schematics and stuff.

For now, a hand drawn schematic will do very well. Just use your best common sense, draw all the connections, snap a celly and post it.

If you don't know anything about schematics, a bit of googling on how to will be enough so you can start to communicate in this way.

I would argue that until you can do a decent hand drawn schematic it makes one sense to try to learn Eagle or whatever.

a7

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.