Issue with TM1637 Display and millis()

Hey guys,

I am trying to make a simple stopwatch that will start and stop when a button is hit using the TM1637 four digit display. I have started very simple with just trying to get a working timer with seconds on the left of the colon and milliseconds on the right of the colon. 00:00. this will only give me a minute I know but that is sufficient for the project.

The problem that I have is the math for making it keep proper time. It is running about three seconds per digit change on the second side of the display.

Any advice would be greatly appreciated!
Jman

#include <TM1637Display.h>

const int CLK = 25; //Set the CLK pin connection to the display
const int DIO = 33; //Set the DIO pin connection to the display

TM1637Display display(CLK, DIO);  //set up the 4-Digit Display.

uint8_t digit1 = 0;
uint8_t digit2 = 0;
uint8_t digit3 = 0;
uint8_t digit4 = 0;

unsigned long interval = 1;           // interval at which to run (milliseconds)
unsigned long previousMillis = 0;      
uint8_t data[] = { 0xff, 0xff, 0xff, 0xff };

void setup() {
  display.setBrightness(0x0a);  //set the diplay to maximum brightness
}

void loop() {
  unsigned long currentMillis = millis();
  data[0] = display.encodeDigit(digit4); //update digits every 1 ms
  data[1] = display.encodeDigit(digit3);
  data[2] = display.encodeDigit(digit2);
  data[3] = display.encodeDigit(digit1);
  display.setSegments(data);
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    digit1++;
    if (digit1 > 9) { //set the segments back to zero when they hit 9
      digit1 = 0;
      digit2++;
      if (digit2 > 9) {
        digit2 = 0;
        digit3++;
        if (digit3 > 9) {
          digit3 = 0;
          digit4++;
        }
      }
    }
  }
}

Millis isn't particularly accurate. You are assumed that everything that needs to happen happens in less than 1ms, which it might not.

I didn't have a chance to fully analyze this, but try:

  if (currentMillis - previousMillis >= interval) {
    previousMillis +=  interval;

PerryBebbington:
Millis isn't particularly accurate. You are assumed that everything that needs to happen happens in less than 1ms, which it might not.

Thanks for the comment! I know it's not perfect, but not much is happening in this sketch so I thought it would be at least close. I could be completely wrong about that though! :o

The speed that millis runs as in independent of how much code you have. It might or might not be accurate enough for your purposes, I suggest that this is one thing to investigate.

Your code is based on catching millis every millisecond, for that to work your code must take less than 1 millisecond to go round once. I don’t know whether it does or not, but I think you should find out. My feeling, untested, is that it’s tight.

aarg:
I didn't have a chance to fully analyze this, but try:

  if (currentMillis - previousMillis >= interval) {

previousMillis +=  interval;

Thanks for the suggestion! I tried that, but I am still getting approximately a 3 second delay per digit change on the seconds digit. I'm thinking about scrapping the hundredths digit and just going with 10ths to see if that helps. I hate to do that though because it would be really nice to have.

PerryBebbington:
The speed that millis runs as in independent of how much code you have. It might or might not be accurate enough for your purposes, I suggest that this is one thing to investigate.

Your code is based on catching millis every millisecond, for that to work your code must take less than 1 millisecond to go round once. I don't know whether it does or not, but I think you should find out. My feeling, untested, is that it's tight.

I removed the 100ths digit and it's working fine now. I am surprised though because I've seen other people have it without a problem. I might see if I can add it back with some math. I am using an esp32 if that makes a difference.

jman31:
I removed the 100ths digit and it's working fine now. I am surprised though because I've seen other people have it without a problem. I might see if I can add it back with some math. I am using an esp32 if that makes a difference.

I'm glad you got it working. I would imagine that an ESP32 would be fast enough to do everything in 1ms, but I don't have one to test on.

Well, the ESP32 is new on my bench too. I can test it when I get home. I wish you had mentioned that in your first post, there are huge differences in the ESP32 core implementation vs. the AVR. When I saw your pin numbers, I assumed "Mega2560". Having said that, the very last project I built (and it was only last week), was a TM1637 display on an ESP32. I had no problems with delays in the display at all.

So, what are you doing for 3.3V/5V level translation on that? Which ESP32 module are you using?

Edit - I'm not going to try anything until I get a reply...

aarg:
Well, the ESP32 is new on my bench too. I can test it when I get home. I wish you had mentioned that in your first post, there are huge differences in the ESP32 core implementation vs. the AVR. When I saw your pin numbers, I assumed "Mega2560". Having said that, the very last project I built (and it was only last week), was a TM1637 display on an ESP32. I had no problems with delays in the display at all.

So, what are you doing for 3.3V/5V level translation on that? Which ESP32 module are you using?

Edit - I'm not going to try anything until I get a reply...

Hi aarg,

Sorry about leaving that information out. I assumed it was a programming issue. I am using the esp32 dev module v1.1 in this image.

I am using the 3.3v logic from the esp32, but powering the module with a separate 5v power source. My pins are 25 for the CLK and 33 for the DIO.

I have seen many other use the remainder operator (%), but I don't understand the way they are using it.

Thanks for taking the time to respond!
Jman

Here is the code as it stands now.

void stopwatch() {
  unsigned long currentMillis = millis();
  data[0] = display.encodeDigit(digit4); //update digits every 1 ms
  data[1] = display.encodeDigit(digit3);
  data[2] = display.encodeDigit(digit2);
  data[3] = display.encodeDigit(digit1);
  display.setSegments(data);
  if (currentMillis - previousMillis2 >= interval2) {
    previousMillis2 +=  interval2;
    digit1++;
    if (digit1 > 9) {
      digit1 = 0;
      digit2++;
      if (digit2 > 9) {
        digit2 = 0;
        digit3++;
        if (digit3 > 9) {
          digit3 = 0;
          digit4++;
          if (digit4 > 5) {
            digit4 = 0;
          }
        }
      }
    }
  }
}

Can you please post a complete sketch? I have hardware to test it with. I don't want to risk cut and pasting together something that's different than what you currently have.

Also, instead of a verbal description, which only guarantees the "gist" of what you're doing, can you provide a wiring diagram? It's more iron clad.

However, I'll fast forward a little bit on that, it sounds like the answer on level translation was a "no". That is not good for the ESP32. The TM1637 can run perfectly well on 3.3V, I suggest you try that (although you may have already compromised the ESP32 I/O pins).

You can just use two variables

uint16_t msCounter
uint16_t secCounter

every millisecond

msCounter++;

Then to get the hundreds and tens digit (for the millisecond fields)

hundDigit = msCounter/100;
tensDigit = (msCounter%100)/10;

Apply the same on the seconds counter.

@hzrnbgy, just to be clear, is that advice an enhancement, or does it repair the problem stated in the original post?

I have seen many other use the remainder operator (%), but I don't understand the way they are using it

an example for what he is asking

hzrnbgy:
an example for what he is asking

Ok, now I understand. There was a side question about the % operator...

Just one thing...

Why

tensDigit = (msCounter%100)/10;

instead of the usual
tensDigit = msCounter%10;?

That would give you the ones digit.

He's only get the hundreds and tens digit on his TM1637 display

00:00

(sec):(ms)

Okay, there have been times when I didn't get the point...

tensDigit = (msCounter%100)/10;

suppose msCounter == 1234

then

msCounter%100 == 34

and

34/10 == 3

????

He said he's using the two digits to the right of the colon symbol for millisecond so he can only display the hundreds and tens digit of a millisecond.

999 ms would be displayed as 00:99
998 ms would be the same 00:99 since there's no third digit to use for the ones component

I assume that his ms counter would go back to zero when it's reaches 1000 and increment the second counter

aarg:
Can you please post a complete sketch? I have hardware to test it with. I don’t want to risk cut and pasting together something that’s different than what you currently have.

Also, instead of a verbal description, which only guarantees the “gist” of what you’re doing, can you provide a wiring diagram? It’s more iron clad.

However, I’ll fast forward a little bit on that, it sounds like the answer on level translation was a “no”. That is not good for the ESP32. The TM1637 can run perfectly well on 3.3V, I suggest you try that (although you may have already compromised the ESP32 I/O pins).

Thanks again. I switched over to 3.3v and a new esp dev board. I still have the same issue. I am a teacher and don’t have the tools to make a wiring diagram, but what I have set up on my breadboard is just the esp32 and the display. Wiring is GND to GND, VCC to 3.3v, pin 25 to CLK and pin 33 to DIO.

HERE is a photo of something like my setup, but with a slightly different pin out.

This is my code for the display

#include <TM1637Display.h>

const int CLK = 25; //Set the CLK pin connection to the display
const int DIO = 33; //Set the DIO pin connection to the display

TM1637Display display(CLK, DIO);  //set up the 4-Digit Display.

uint8_t digit1 = 0;
uint8_t digit2 = 0;
uint8_t digit3 = 0;
uint8_t digit4 = 0;

unsigned long interval = 1;           // interval at which to blink (milliseconds)
unsigned long previousMillis = 0;        // will store last time LED was updated
uint8_t data[] = { 0xff, 0xff, 0xff, 0xff };

void setup() {
  display.setBrightness(0x0a);  //set the diplay to maximum brightness
}

void loop() {
  stopwatch();
}


void stopwatch() {
  unsigned long currentMillis = millis();
  data[0] = display.encodeDigit(digit4); //update digits every 1 ms
  data[1] = display.encodeDigit(digit3);
  data[2] = display.encodeDigit(digit2);
  data[3] = display.encodeDigit(digit1);
  display.setSegments(data);

  if (currentMillis - previousMillis >= interval) {
    //previousMillis = currentMillis;
    previousMillis +=  interval;
    digit1++;
    if (digit1 > 9) {
      digit1 = 0;
      digit2++;
      if (digit2 > 9) {
        digit2 = 0;
        digit3++;
        if (digit3 > 9) {
          digit3 = 0;
          digit4++;
          if (digit4 > 5) {
            digit4 = 0;
          }
        }
      }
    }
  }
}

jman31:
I still have the same issue. I am a teacher and don't have the tools to make a wiring diagram, but what I have set up on my breadboard is just the esp32 and the display. Wiring is GND to GND, VCC to 3.3v, pin 25 to CLK and pin 33 to DIO.

Thanks, I will set it up here and have a look. You can always make a wiring diagram with a pencil and paper, take a picture with a cell phone. But in this case it's obvious enough...