The clock I made sometimes doesn't increase the timer properly

I tried making a simple clock I made without a delay but it sometimes goes faster than it should and sometimes goes slower. That could happen because the other code I am using takes some time to run and I am not checking every single millisecond if a second has passed. What do I do to fix this? I don't want to use delays, as I am trying to integrate it into a different project and using delays would slow down the switch between different states of the program. What do you suggest I can do to not use delays, but still have the clock accurately run every second.

This is the code for the loop (The commented out code is what I used to use with delays):

    display.clearDisplay();
    display.setCursor(0, 0);
    if(timermin < 10){
      display.print("0");
    }
    display.print(timermin);
    display.print(F(":"));
    if(timersec < 10){
      display.print("0");
    }
    display.print(timersec);
    display.display();
    timeafter = millis();
    Serial.println(timeafter);
    if(timeafter % timepertick == 0){
      timersec++;
      Serial.println("Times are changing!");
    }
    if(timersec >= 60){
      timersec = 0;
      timermin++;
    }
    /*
    if(timepertick < (timerend - timerstart)){
      timepertick = timerend - timerstart;
    }
    delay(timepertick - (timerend - timerstart));
    */

In that case, please post a complete sketch that illustrates the problem

What do you mean by that? If you are talking about the full code, this is it but I don't think most of it would be necessary to find the cause of the issue:

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ezButton.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for SSD1306 display connected using software SPI (default case):
#define OLED_MOSI   9
#define OLED_CLK   10
#define OLED_DC    11
#define OLED_CS    12
#define OLED_RESET 13
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT,
  OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);

ezButton button(2);

int timersec = 0;
int timermin = 0;
int timepertick = 100;

unsigned int mode = 0;
unsigned int disabledmode = 1;
unsigned int temp = 0;
unsigned int timeafter = 0;

const unsigned int width = 128;
const unsigned int height = 64;

float model[] = {1, 0, 0, 0,
                 0, 1, 0, 0,
                 0, 0, 1, 0,
                 0, 0, 0, 1};
float view[]= {1, 0, 0, 0,
               0, 1, 0, 0,
               0, 0, 1, 2,
               0, 0, 0, 1};
float proj[] = {1.2f, 0.0f, 0, 0,
                0.0f, 2.4f, 0, 0,
                0.0f, 0.0f, 0, 1,
                0.0f, 0.0f, 1, 0};
const PROGMEM float vertices[] = {
   -0.5f, -0.5f,  0.5f,
   -0.5f,  0.5f,  0.5f,
    0.5f,  0.5f,  0.5f,
    0.5f,  0.5f,  0.5f,
    0.5f, -0.5f,  0.5f,
   -0.5f, -0.5f,  0.5f,

    0.5f, -0.5f, -0.5f,
    0.5f,  0.5f, -0.5f,
   -0.5f,  0.5f, -0.5f,
   -0.5f,  0.5f, -0.5f,
   -0.5f, -0.5f, -0.5f,
    0.5f, -0.5f, -0.5f,

    0.5f, -0.5f,  0.5f,
    0.5f,  0.5f,  0.5f,
    0.5f,  0.5f, -0.5f,
    0.5f,  0.5f, -0.5f,
    0.5f, -0.5f, -0.5f,
    0.5f, -0.5f,  0.5f,

   -0.5f, -0.5f, -0.5f,
   -0.5f,  0.5f, -0.5f,
   -0.5f,  0.5f,  0.5f,
   -0.5f,  0.5f,  0.5f,
   -0.5f, -0.5f,  0.5f,
   -0.5f, -0.5f, -0.5f,

   -0.5f,  0.5f,  0.5f,
   -0.5f,  0.5f, -0.5f,
    0.5f,  0.5f, -0.5f,
    0.5f,  0.5f, -0.5f,
    0.5f,  0.5f,  0.5f,
   -0.5f,  0.5f,  0.5f,

    0.5f, -0.5f,  0.5f,
    0.5f, -0.5f, -0.5f,
   -0.5f, -0.5f, -0.5f,
   -0.5f, -0.5f, -0.5f,
   -0.5f, -0.5f,  0.5f,
    0.5f, -0.5f,  0.5f
};

unsigned int angle = 0;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  Serial.begin(9600);

  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }


  // Show initial display buffer contents on the screen --
  // the library initializes this with an Adafruit splash screen.
  display.display();
  delay(700);

  // Clear the buffer
  display.clearDisplay();
  display.setCursor(0, 0);
  display.setTextColor(WHITE);
  display.setTextSize(3);
}

void loop() {
  button.loop();
  if(button.isPressed()){
    temp = mode;
    mode = disabledmode;
    disabledmode = temp;
  }
  if(mode == 0){
    display.clearDisplay();
    display.setCursor(0, 0);
    if(timermin < 10){
      display.print("0");
    }
    display.print(timermin);
    display.print(F(":"));
    if(timersec < 10){
      display.print("0");
    }
    display.print(timersec);
    display.display();
    timeafter = millis();
    Serial.println(timeafter);
    if(timeafter % timepertick == 0){
      timersec++;
      Serial.println("Times are changing!");
    }
    if(timersec >= 60){
      timersec = 0;
      timermin++;
    }
    /*
    if(timepertick < (timerend - timerstart)){
      timepertick = timerend - timerstart;
    }
    delay(timepertick - (timerend - timerstart));
    */
  }else if(mode == 1){
    display.clearDisplay();
    drawtrianglearray(vertices, sizeof(vertices)/sizeof(float));
    display.display();
    angle += 8;
    mat4_rotate(angle, 1, model);
  }
}

void mat4_mul(float l[16], float r[16], float *out){
   out[0]  = r[0] * l[0]  + r[4] * l[1]  + r[8]  * l[2]  + r[12] * l[3];
   out[1]  = r[1] * l[0]  + r[5] * l[1]  + r[9]  * l[2]  + r[13] * l[3];
   out[2]  = r[2] * l[0]  + r[6] * l[1]  + r[10] * l[2]  + r[14] * l[3];
   out[3]  = r[3] * l[0]  + r[7] * l[1]  + r[11] * l[2]  + r[15] * l[3];
   out[4]  = r[0] * l[4]  + r[4] * l[5]  + r[8]  * l[6]  + r[12] * l[7];
   out[5]  = r[1] * l[4]  + r[5] * l[5]  + r[9]  * l[6]  + r[13] * l[7];
   out[6]  = r[2] * l[4]  + r[6] * l[5]  + r[10] * l[6]  + r[14] * l[7];
   out[7]  = r[3] * l[4]  + r[7] * l[5]  + r[11] * l[6]  + r[15] * l[7];
   out[8]  = r[0] * l[8]  + r[4] * l[9]  + r[8]  * l[10] + r[12] * l[11];
   out[9]  = r[1] * l[8]  + r[5] * l[9]  + r[9]  * l[10] + r[13] * l[11];
   out[10] = r[2] * l[8]  + r[6] * l[9]  + r[10] * l[10] + r[14] * l[11];
   out[11] = r[3] * l[8]  + r[7] * l[9]  + r[11] * l[10] + r[15] * l[11];
   out[12] = r[0] * l[12] + r[4] * l[13] + r[8]  * l[14] + r[12] * l[15];
   out[13] = r[1] * l[12] + r[5] * l[13] + r[9]  * l[14] + r[13] * l[15];
   out[14] = r[2] * l[12] + r[6] * l[13] + r[10] * l[14] + r[14] * l[15];
   out[15] = r[3] * l[12] + r[7] * l[13] + r[11] * l[14] + r[15] * l[15];
}

void mat4_vec4_mul(float mat[16], float vec[4], float *out){
   out[0] = vec[0] * mat[0]  + vec[1] * mat[1]  + vec[2] * mat[2]  + vec[3] * mat[3];
   out[1] = vec[0] * mat[4]  + vec[1] * mat[5]  + vec[2] * mat[6]  + vec[3] * mat[7];
   out[2] = vec[0] * mat[8]  + vec[1] * mat[9]  + vec[2] * mat[10] + vec[3] * mat[11];
   out[3] = vec[0] * mat[12] + vec[1] * mat[13] + vec[2] * mat[14] + vec[3] * mat[15];
}

void mat4_rotate(int degrees, unsigned int axis, float *out){
   float radians = degrees * (22.0f / 7.0f) / 180.0f;
   if(axis == 0){
      out[0]  =  1.0f;
      out[1]  =  0.0f;
      out[2]  =  0.0f;
      out[3]  =  0.0f;
      out[4]  =  0.0f;
      out[5]  =  cosf(radians);
      out[6]  = -sinf(radians);
      out[7]  =  0.0f;
      out[8]  =  0.0f;
      out[9]  =  sinf(radians);
      out[10] =  cosf(radians);
      out[11] =  0.0f;
      out[12] =  0.0f;
      out[13] =  0.0f;
      out[14] =  0.0f;
      out[15] =  1.0f;
   }else if(axis == 1){
      out[0]  =  cosf(radians);
      out[1]  =  0.0f;
      out[2]  =  sinf(radians);
      out[3]  =  0.0f;
      out[4]  =  0.0f;
      out[5]  =  1.0f;
      out[6]  =  0.0f;
      out[7]  =  0.0f;
      out[8]  = -sinf(radians);
      out[9]  =  0.0f;
      out[10] =  cosf(radians);
      out[11] =  0.0f;
      out[12] =  0.0f;
      out[13] =  0.0f;
      out[14] =  0.0f;
      out[15] =  1.0f;
   }else if(axis == 2){
      out[0]  =  cosf(radians);
      out[1]  = -sinf(radians);
      out[2]  =  0.0f;
      out[3]  =  0.0f;
      out[4]  =  sinf(radians);
      out[5]  =  cosf(radians);
      out[6]  =  0.0f;
      out[7]  =  0.0f;
      out[8]  =  0.0f;
      out[9]  =  0.0f;
      out[10] =  1.0f;
      out[11] =  0.0f;
      out[12] =  0.0f;
      out[13] =  0.0f;
      out[14] =  0.0f;
      out[15] =  1.0f;
   }
}
int vertexshader(float position[3], float model[16], float view[16], float proj[16], int out[2]){
   float int1[4];
   float int2[4];
   float int3[4];
   float int4[4];
   float int5[2];
   float int6[2];
   int3[0] = position[0];
   int3[1] = position[1];
   int3[2] = position[2];
   int3[3] = 1.0f;
   mat4_vec4_mul(model, int3, int1);
   mat4_vec4_mul(view, int1, int2);
   mat4_vec4_mul(proj, int2, int4);
   int5[0] = int4[0] / int4[3];
   int5[1] = int4[1] / int4[3];
   int6[0] = int5[0] * 0.5f + 0.5f;
   int6[1] = int5[1] * 0.5f + 0.5f;
   out[0] = (int)(int6[0] * width); 
   out[1] = (int)((1.0f - int6[1]) * height);
}
void drawtriangle(float pos1[3], float pos2[3], float pos3[3]){
   int v1[2];
   int v2[2];
   int v3[2];
   vertexshader(pos1, model, view, proj, v1);
   vertexshader(pos2, model, view, proj, v2);
   vertexshader(pos3, model, view, proj, v3);
   display.fillTriangle(v1[0], v1[1], v2[0], v2[1], v3[0], v3[1], WHITE);
}
void drawtrianglearray(float *positions, int size){
   float pos1[3];
   float pos2[3];
   float pos3[3];
   int i;
   for(i = 0; i < size / 9; i++){
      pos1[0] = pgm_read_float_near(positions + i * 9);     //positions[i * 9];
      pos1[1] = pgm_read_float_near(positions + i * 9 + 1); //positions[i * 9 + 1];
      pos1[2] = pgm_read_float_near(positions + i * 9 + 2); //positions[i * 9 + 2];
      pos2[0] = pgm_read_float_near(positions + i * 9 + 3); //positions[i * 9 + 3];
      pos2[1] = pgm_read_float_near(positions + i * 9 + 4); //positions[i * 9 + 4];
      pos2[2] = pgm_read_float_near(positions + i * 9 + 5); //positions[i * 9 + 5];
      pos3[0] = pgm_read_float_near(positions + i * 9 + 6); //positions[i * 9 + 6];
      pos3[1] = pgm_read_float_near(positions + i * 9 + 7); //positions[i * 9 + 7];
      pos3[2] = pgm_read_float_near(positions + i * 9 + 8); //positions[i * 9 + 8];
      drawtriangle(pos1, pos2, pos3);
   }
}

I meant that the problem may be in the code that you had not posted. Suppose you used delay() in it, or had a blocking while loop or for loop then how would we know without seeing the code ?

Instead of keeping a separate counter, you could consider storing the start time and adding the current system time.

void loop() {
  static unsigned long startTime {12345678};
  Serial.println(startTime + millis() / 1000);
}

[edit]

Here is a suggestion for pretty printing the time.

void loop() {
  static unsigned long startTime {86390};

  unsigned long t {startTime + millis() / 1000};
  uint8_t s = t % 60;
  uint8_t m = t / 60 % 60;
  uint8_t h = t / 3600 % 24;

  char str[9] {};
  sprintf(str, "%02i:%02i:%02i", h, m, s);
  Serial.println(str);
}

similar to above
consider

unsigned long sec;
unsigned min;
unsigned hr;

unsigned long msec0;

// -----------------------------------------------------------------------------
void
setTime (
    unsigned hr,
    unsigned min,
    unsigned sec )
{
    msec0 = ((hr * 3600L) + (min * 60L) + sec) * 1000;
}

// -----------------------------------------------------------------------------
void
dispTime (
    unsigned long msec )
{
    sec = msec/ 1000;
    min = sec  / 60;
    hr  = min  / 60;

    if (hr >= 13)
        hr = 1;
    min %= 60;
    sec %= 60;

    static unsigned secLst;

    if (secLst != sec)  {
        secLst = sec;
        char s [30];
        sprintf (s, "%2d:%02d:%02ld", hr, min, sec);
        Serial.println (s);
    }
}

// -----------------------------------------------------------------------------
void loop()
{
    dispTime (msec0 + millis ());
}

void
setup (void)
{
    Serial.begin (9600);
    setTime (11, 58, 57);
}

That's a very good suggestion, thanks!

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