jasonacox / TM1637TinyDisplay

Arduino library to display numbers and text on a 4 and 6 digit 7-segment TM1637 display modules.
GNU Lesser General Public License v3.0
69 stars 19 forks source link

Outputting to display significantly slows down entire sketch, not sure why. #11

Closed JunctionRunner closed 3 years ago

JunctionRunner commented 3 years ago

Hi, so I have a thread on the arduino forum where I'm working on a countdown timer where you enter the time with a matrix keypad, and the desired behavior is that if the time is one hour or greater, the display shows it as such

02.25.30

as in two hours, 25 minutes, 30 seconds.

Then, when it drops to one hour and below, I want to have milliseconds update on the display, so it all shifts over like so

59.30.12

59 minutes, 30 seconds, 12ms

However, when I try to update the display at speed in my sketch it slows the whole thing down, basically making one second take three seconds. If I displayed the time in only milliseconds, without turning it into a two digit value, 700ms would take an entire three seconds to change to 600ms, however, the last two digits update extremely fast without any issue.

Its really peculiar, and the sketch being slowed down is fixed by commenting out the display.showNumberDec line, so it's somehow related to the output.

I figure you might be able to help or have a clue as to why this is, the display is clearly able to update extremely fast, but for some reason in this sketch, it bogs down.

Heres my forum thread, with the later few posts being the one cracking down on identifying the issue. https://forum.arduino.cc/t/need-help-setting-up-a-countdown-timer-which-you-set-using-a-keypad/864081/43

`#include // //// Module connection pins (Digital Pins)

define CLK 10

define DIO 11

TM1637TinyDisplay6 display(CLK, DIO); unsigned long dispout;

byte hours = 0; byte minutes = 12; byte seconds = 10;

//number of milliseconds; unsigned long counter = ((hours 3600ul) + (minutes 60ul) + seconds ) * 1000ul;

unsigned long timeMICROS; unsigned long secondMICROS; unsigned long timeMillis;

unsigned long num; unsigned long leftDecimal; unsigned long rightDecimal;

unsigned long HOURS; unsigned long temp; unsigned long MINUTES; unsigned long SECONDS; unsigned long MILLISECONDS;

void setup() { display.setBrightness(BRIGHT_LOW); display.clear(); display.showString("boot");

Serial.begin(115200); // <---------------------<<<<<<<<<<<<<<<<<

Serial.println("Start the count down");

} //END of setup()

void loop() { unsigned long timeNow = micros();

//***** //every 1ms do this if (timeNow - timeMICROS >= 1000) { //restart the TIMER timeMICROS = timeMICROS + 1000;

counter = counter - 1;

}

//***** // RAW 10X milliseconds //every 10ms // if (timeNow - secondMICROS >= 10000) //10000 = 10ms // { //restart the TIMER //secondMICROS = timeNow;

//restart the TIMER // secondMICROS = secondMICROS + 10000;

// num = counter; // leftDecimal = num / 1000; // rightDecimal = (counter - leftDecimal * 1000) / 10; // Serial.print("Milliseconds = "); // Serial.print(leftDecimal); // Serial.print("."); // Serial.println(rightDecimal); // }

//***** //every 10ms do this //if (millis() - timeMillis >= 10)

//every 100ms do this if (millis() - timeMillis >= 100) { //restart the TIMER every 10ms timeMillis = timeMillis + 10;

//restart the TIMER every 100m
//timeMillis = timeMillis + 100;

//total number of seconds
HOURS = counter / 1000ul;
temp = (HOURS / 3600) * 3600;
MINUTES = HOURS - temp;
temp = (MINUTES / 60) * 60;

SECONDS = MINUTES - temp;
MINUTES = MINUTES / 60;
HOURS = HOURS / 3600;
MILLISECONDS = (counter % 1000) / 10;

Serial.print("Counter = ");
if(HOURS < 10)
{
  Serial.print(0);
}
Serial.print(HOURS);
Serial.print(":");
if(MINUTES < 10)
{
  Serial.print(0);
}
Serial.print(MINUTES);
Serial.print(":");
if(SECONDS < 10)
{
  Serial.print(0);
}
Serial.print(SECONDS);
Serial.print(".");
Serial.println(MILLISECONDS);

}

// dispout = ((HOURS 10000) + (MINUTES 100) + SECONDS); //This one if timer is above one hour dispout = ((MINUTES 10000) + (SECONDS 100) + MILLISECONDS); //This one if timer is below one hour

uint8_t dots = 0b01010000;
display.showNumberDec(dispout, dots);

} `

jasonacox commented 3 years ago

Hi @XOIIO - I think I understand what you are trying to do. You might be over-complicating it. The key thing to remember is that millis() is your accurate clock in milliseconds. You basically want to measure time as a delta from that (elapsed time) and use that to produce the countdown display (subtraction).

Here is my take on what I think you are trying to do:

#include <TM1637TinyDisplay6.h>

/* Digital Pins to TM1637 */
#define CLK 10
#define DIO 11

/* Useful Constants */
#define SECS_PER_MIN  (60UL)
#define SECS_PER_HOUR (3600UL)
#define SECS_PER_DAY  (SECS_PER_HOUR * 24L)

/* Useful Macros for time (s) */
#define numberOfSeconds(_time_) (_time_ % SECS_PER_MIN)
#define numberOfMinutes(_time_) ((_time_ / SECS_PER_MIN) % SECS_PER_MIN)
#define numberOfHours(_time_) (( _time_% SECS_PER_DAY) / SECS_PER_HOUR)
#define elapsedDays(_time_) ( _time_ / SECS_PER_DAY)
#define hmsToMillis(_h_, _m_, _s_) ((_h_ * SECS_PER_HOUR) + (_m_ * SECS_PER_MIN) + _s_ ) * 1000ul;

/* Set up Display */
TM1637TinyDisplay6 display(CLK, DIO);

/* Global Variables in milliseconds */
unsigned long startTime;
unsigned long lastLoopTime;
unsigned long startCount;
unsigned long dispout;

void setup()
{
  display.setBrightness(BRIGHT_HIGH);
  display.clear();
  display.showString("boot");
  delay(500);

  // Record Epoch - Same as Timer Reset
  startTime = millis();

  // Time to countdown from in h, m, s
  startCount = hmsToMillis(1, 0, 10);
}

void loop()
{
  unsigned long timeNow = millis();
  unsigned long timeElapsed = timeNow - startTime;  // true amount of time since start
  unsigned long counter = startCount - timeElapsed; // current state of countdown

  // Update Display - every 10ms
  if (timeNow - lastLoopTime >= 10) {
    lastLoopTime = timeNow; // remember last time we displayed

    // Compute the values
    unsigned long HOURS = numberOfHours(counter / 1000);
    unsigned long MINUTES = numberOfMinutes(counter / 1000);
    unsigned long SECONDS = numberOfSeconds(counter / 1000);
    unsigned long MILLISECONDS = (counter % 1000lu) / 10;

    // Convert time values to integer to display
    if (HOURS <= 0) {
      //This one if timer is below one hour
      dispout = ((MINUTES * 10000) + (SECONDS * 100) + MILLISECONDS);
    } else {
      //This one if timer is above one hour
      dispout = ((HOURS * 10000) + (MINUTES * 100) + SECONDS);
    }
    uint8_t dots = 0b01010000;
    display.showNumberDec(dispout, dots);
  }
}
JunctionRunner commented 3 years ago

Hi, that runs great, the timer code was contributed by someone else as this is my first go at counting down in such a manner, so I wasn't sure where the issue was, I wasn't sure if there was some sort of possible delay or time needed by the display to process whatever information it gets, but this, and my testing before shows that it's not the case.

This code looks to be a much more elegant way of counting down as well, I'm guessing that I'll be able to replace the numerical entries in "startCount = hmsToMillis(1, 0, 10);" with variables to tie in the entry using the keypad, I'll test that out and report back.

Not quite sure if I'll get to it tonight, starting to feel a bit tired, but I'm feeling very optimistic!

jasonacox commented 3 years ago

That's great! I think many people expect instructions to take zero time. That is not the case. Each function call or math operation will take cycles. When you call showNumberDec() it has a sequence of instructions and signal timing delays it must execute to send the data to the TM1637 module. The amount of time to make those calls will vary based on the number you are sending as well. You can only reliable measure time by using the built in clock via the millis() function.

You are correct. With my sketch above, you just need to adjust the parameters or you can use something like this if it helps:

  // Set countdown timer in h, m, s
  int Hour = 1;
  int Min = 0;
  int Sec = 10;
  startCount = hmsToMillis(Hour, Min, Sec);
JunctionRunner commented 3 years ago

Oh, who am I kidding, there's no way I'd be able to sleep.

Merged your timer code with what I have for setting the variables from the keypad and it works beautifully. Now I just need to set it up to display the numbers as you enter them, maybe a menu and monitor some inputs and it's golden. Thanks a million!

`#include

include

// Module connection pins (Digital Pins)

define CLK 10

define DIO 11

const byte ROWS = 4; //setting up keypad const byte COLS = 3;

char hexaKeys[ROWS][COLS] = { {'1', '2', '3'}, {'4', '5', '6'}, {'7', '8', '9'}, {'*', '0', '#'} };

byte rowPins[ROWS] = {2, 3, 4, 5}; byte colPins[COLS] = {6, 7, 8};

Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS); char customKey; TM1637TinyDisplay6 display(CLK, DIO);

// gubbins to set timer variables

int sethours1; int sethours2; int setminutes1; int setminutes2; int setseconds1; int setseconds2; int timeup = 0;

// actual timer parts start here

/ Useful Constants /

define SECS_PER_MIN (60UL)

define SECS_PER_HOUR (3600UL)

define SECS_PER_DAY (SECS_PER_HOUR * 24L)

/ Useful Macros for time (s) /

define numberOfSeconds(time) (time % SECS_PER_MIN)

define numberOfMinutes(time) ((time / SECS_PER_MIN) % SECS_PER_MIN)

define numberOfHours(time) (( time% SECS_PER_DAY) / SECS_PER_HOUR)

define elapsedDays(time) ( time / SECS_PER_DAY)

define hmsToMillis(h, m, s) ((h SECS_PER_HOUR) + (m SECS_PER_MIN) + s ) * 1000ul;

/ Global Variables in milliseconds / unsigned long startTime; unsigned long lastLoopTime; unsigned long startCount; unsigned long dispout;

int hours = 0; int minutes = 0; int seconds = 0;

void setup(){ Serial.begin(115200); char timerdisp[7]; timerdisp[0] = '-'; // 6 character string for display output of timer timerdisp[1] = '-'; timerdisp[2] = '-'; timerdisp[3] = '-'; timerdisp[4] = '-'; timerdisp[5] = '-'; timerdisp[6] = 0; // 7th array element is a null terminator Serial.begin(115200); display.setBrightness(BRIGHT_LOW); display.clear(); display.showString(timerdisp); delay(1000);

settimer(); }

void loop(){ unsigned long timeNow = millis(); unsigned long timeElapsed = timeNow - startTime; // true amount of time since start long int counter = startCount - timeElapsed; // current state of countdown

// Update Display - every 10ms if (timeNow - lastLoopTime >= 10) { lastLoopTime = timeNow; // remember last time we displayed

// Compute the values
unsigned long HOURS = numberOfHours(counter / 1000);
unsigned long MINUTES = numberOfMinutes(counter / 1000);
unsigned long SECONDS = numberOfSeconds(counter / 1000);
unsigned long MILLISECONDS = (counter % 1000lu) / 10;

// Convert time values to integer to display
if (HOURS <= 0) {
  //This one if timer is below one hour
  dispout = ((MINUTES * 10000) + (SECONDS * 100) + MILLISECONDS);
} else {
  //This one if timer is above one hour
  dispout = ((HOURS * 10000) + (MINUTES * 100) + SECONDS);
}
uint8_t dots = 0b01010000;
display.showNumberDec(dispout, dots, true);

} Serial.println(counter); if (counter <= 0){ timerstopped(); } }

void settimer(){ //set first hour section
Serial.println("set hours"); //setting hours

sethours1 = (customKeypad.waitForKey() - 48); //subtract 48 to convert hex to decimal Serial.println(sethours1); hours = (sethours1 * 10); Serial.println(hours);

sethours2 = (customKeypad.waitForKey() - 48); //subtract 48 to convert hex to decimal Serial.println(sethours2); hours = (hours + sethours2); Serial.println(hours);

Serial.println("set Minutes"); //setting minutes

setminutes1 = (customKeypad.waitForKey() - 48); //subtract 48 to convert hex to decimal Serial.println(setminutes1); minutes = (setminutes1 * 10); Serial.println(minutes);

setminutes2 = (customKeypad.waitForKey() - 48); //subtract 48 to convert hex to decimal Serial.println(setminutes2); minutes = (minutes + setminutes2); Serial.println(minutes);

Serial.println("set Seconds"); //setting seconds

setseconds1 = (customKeypad.waitForKey() - 48); //subtract 48 to convert hex to decimal Serial.println(setseconds1); seconds = (setseconds1 * 10); Serial.println(seconds);

setseconds2 = (customKeypad.waitForKey() - 48); //subtract 48 to convert hex to decimal Serial.println(setseconds2); seconds = (seconds + setseconds2); Serial.println(seconds);

Serial.println("end of set timer");

// Record Epoch - Same as Timer Reset startTime = millis(); startCount = hmsToMillis(hours, minutes, seconds); loop(); }

void timerstopped(){ timeup = 1; Serial.println("Timer Stopped"); while (timeup == 1) { display.showString("000000"); delay(500); display.clear(); delay(500); } }`

JunctionRunner commented 3 years ago

Hmm, might pick your brain for one other thing since I've been trying a few things and haven't had any luck, possibly due to lack of sleep.

Basically, it will instruct you to set the time with a string in HH MM SS format, and then switch to all zeroes. After that, as you type in the numbers I'm hoping them to pop up on the screen in the appropriate positions.

I did try using something similar to " dispout = ((HOURS 10000) + (MINUTES 100) + SECONDS);" with the "setx1 or setx2" variables, however it glitched out if the hour number was too high when multiplied.

I figured updating a char array would be the way to do it but can't figure out how to format the char array to basically input the button presses.

I figure calling this section after each button press and updating the char array is cleanest, but yeah, can't quite get it to work, I'm sure it's simple but I haven't really used char arrays either.

`

void showinput(){ char showinputdisp[7]; showinputdisp[0] = '(sethours1)'; showinputdisp[1] = '(sethours2)'; showinputdisp[2] = setminutes1; showinputdisp[3] = setminutes2; showinputdisp[4] = setseconds1; showinputdisp[5] = setseconds2; showinputdisp[6] = 0; // 7th array element is a null terminator display.showString(showinputdisp); } `

JunctionRunner commented 3 years ago

I think I need something along the lines of this, though it's not quite there., it actually freezes the sketch it seems. Oh well, time to sleep.

edit: Actually, having the decimal points lit up for this too would be good, so doing it numerically might be best, when I tried to use decimals with a string it didn't work iirc.

`void settimer(){

char showinputdisp[7]; showinputdisp[0] = '0'; showinputdisp[1] = '0'; showinputdisp[2] = '0'; showinputdisp[3] = '0'; showinputdisp[4] = '0'; showinputdisp[5] = '0'; showinputdisp[6] = 0; // 7th array element is a null terminator

display.showString("Set time - Hours Minutes Seconds"); display.showString(showinputdisp);

//set first hour section
Serial.println("set hours"); //setting hours

sethours1 = (customKeypad.waitForKey() - 48); //subtract 48 to convert hex to decimal Serial.println(sethours1); hours = (sethours1 * 10); Serial.println(hours); strcpy(showinputdisp[0],sethours1); display.showString(showinputdisp);

sethours2 = (customKeypad.waitForKey() - 48); //subtract 48 to convert hex to decimal Serial.println(sethours2); hours = (hours + sethours2); Serial.println(hours); strcpy(showinputdisp[1],sethours2); display.showString(showinputdisp);

Serial.println("set Minutes"); //setting minutes

setminutes1 = (customKeypad.waitForKey() - 48); //subtract 48 to convert hex to decimal Serial.println(setminutes1); minutes = (setminutes1 * 10); Serial.println(minutes); strcpy(showinputdisp[2],setminutes1); display.showString(showinputdisp);

setminutes2 = (customKeypad.waitForKey() - 48); //subtract 48 to convert hex to decimal Serial.println(setminutes2); minutes = (minutes + setminutes2); Serial.println(minutes); strcpy(showinputdisp[3],setminutes2); display.showString(showinputdisp);

Serial.println("set Seconds"); //setting seconds

setseconds1 = (customKeypad.waitForKey() - 48); //subtract 48 to convert hex to decimal Serial.println(setseconds1); seconds = (setseconds1 * 10); Serial.println(seconds); strcpy(showinputdisp[4],setseconds1); display.showString(showinputdisp);

setseconds2 = (customKeypad.waitForKey() - 48); //subtract 48 to convert hex to decimal Serial.println(setseconds2); seconds = (seconds + setseconds2); Serial.println(seconds); strcpy(showinputdisp[5],setseconds2); display.showString(showinputdisp);

Serial.println("end of set timer");

// Record Epoch - Same as Timer Reset startTime = millis(); startCount = hmsToMillis(hours, minutes, seconds); loop();

} `

jasonacox commented 3 years ago

Hi @XOIIO nice job on getting it to work!

You can use showNumberDec() to display Hours, Minutes and Seconds separately using the position and length parameters.

showNumberDec(int num, uint8_t dots = 0, bool leading_zero = false, uint8_t length = MAXDIGITS, uint8_t pos = 0);

See this: https://github.com/jasonacox/TM1637TinyDisplay/blob/master/TM1637TinyDisplay.h for more details. Here is how it would fit in your code:

    uint8_t dots = 0b01010000;

    if (HOURS <= 0) {
      // Display M:S.ms if timer is below one hour
      display.showNumberDec(MINUTES, dots, true, 2, 0);
      display.showNumberDec(SECONDS, dots, true, 2, 2);
      display.showNumberDec(MILLISECONDS, 0, true, 2, 4);

    } else {
      // Display H:M:S if timer is above one hour
      display.showNumberDec(HOURS, dots, true, 2, 0);
      display.showNumberDec(MINUTES, dots, true, 2, 2);
      display.showNumberDec(SECONDS, 0, true, 2, 4);
    }

By the way, here is a pro tip for Github, you can paste syntax highlighted and formatted python code by prefixing your code snip with this:

```python
loop();

of course end with ``` and it will look like

loop();