Trouble controlling Displayed Clock Speed for game timer

Good day,

I am having an issue with my code.
I am trying to control the speed that a timer is displayed.
Individually the time is working as expected.
Faster time is 2x as fast
Normal is 1x
Slower is half of normal.

It looks like my issue is when I transition from one speed to the next.
The following code will show what I mean.

Any help would be appreciated.

#define secondsElapsed (currentMillis / 1000)
#define minutesRemaining (minutes - (currentMillis / 60000))
#define secondsLeftInMinute (59-(secondsElapsed %60))


#include "LiquidCrystal.h"
// This defines the LCD wiring to the DIGITALpins
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

//TIME INITS
byte time[4];
unsigned long startMillis;
unsigned long timeCalcVar;
uint8_t tempTime;
float timeMultiplier   = 1;
float speedupTimer     = 2;
float slowdownTimer    = 0.5;
uint8_t GAMEMINUTES      = 3;
uint8_t ACTIVATESECONDS  = 15;
uint8_t refresh   = 0;
int longTone = 50;
uint8_t minutes;
unsigned long currentMillis;
unsigned long ledInterval;
unsigned long previousLedMillis;
unsigned long buzzerInterval;
unsigned long previousBuzzerMillis;

void setup() {
  Serial.begin(9600);
  lcd.begin(16,2);
  pinMode(6, OUTPUT);
  pinMode(9, OUTPUT);
}


void loop() { 
  destroy();
}

void destroy() {
  minutes = GAMEMINUTES - 1;
  startMillis = millis();
  int longTone = 50;
  previousLedMillis = 0;
  previousBuzzerMillis = 0;
  ledInterval = 500;
  buzzerInterval = 500;
  int ledState = LOW;             // ledState used to set the LED
  int timeState = LOW;

  lcd.clear();

  while (1) {

    currentMillis = ((millis() - startMillis) * timeMultiplier );
    
    //LEDs blinking
    if(currentMillis - previousLedMillis >= ledInterval) {
      previousLedMillis = currentMillis;   
      if (ledState == LOW)
        ledState = HIGH;
      else
        ledState = LOW;
      digitalWrite(6, ledState);
  }

    
    if ((currentMillis - previousBuzzerMillis) > buzzerInterval) {
      if (minutesRemaining >= 2) {
        buzzerInterval = 10000;
        tone(9, 3951, longTone);
        timeMultiplier = 1;
      }
      else {
        if (minutesRemaining >= 1) {
          buzzerInterval = 5000;
          tone(9, 3951, longTone);
          timeMultiplier = slowdownTimer;
        }
        else {    
          if ((minutesRemaining < 1) && (secondsLeftInMinute > ACTIVATESECONDS)) {
            buzzerInterval = 2000;
            longTone = 300;
            tone(9, 3951, longTone);
            timeMultiplier = 1;
          }
          else {  
            if (secondsLeftInMinute <= ACTIVATESECONDS) {
              buzzerInterval = 500;
              longTone = 200;
              tone(9, 3951, longTone);
            }
          }
        }  
      }
    }
    //Sound End
    
  lcd.setCursor(0, 0);
    //print time
    printTime(minutes, currentMillis);     //CUTWIRE TIME MODIFIER
  }
}

void printTime(unsigned long minutes, unsigned long atTime) {
  timeCalcVar = (minutes - (atTime / 60000));
  //Hours
  if (timeCalcVar / 60 < 1 && refresh == 0) {
    lcd.clear();
    refresh = 1;
    delay(100);
  }
  if (timeCalcVar / 60 >= 1) {
    if (timeCalcVar / 60 < 10) {
    lcd.print(F("0"));
    lcd.print(timeCalcVar / 60);
  }
  else {
    lcd.print(timeCalcVar / 60);
  }
  lcd.print(F(":"));
  }
  //minutes
  if ((timeCalcVar %60) < 10) {
    lcd.print(F("0"));
    lcd.print(timeCalcVar %60);
  }
  else {
    lcd.print(timeCalcVar %60);
  }
  lcd.print(F(":"));
  //seconds
  timeCalcVar = (atTime / 1000);
  if ((59 - (timeCalcVar %60)) < 10) {
    lcd.print(F("0"));
    lcd.print(59 - (timeCalcVar %60));
  }
  else {
    lcd.print(59 - (timeCalcVar %60));
  }
  lcd.print(F(":"));
  //this does not match with real time and is meant for effect,
  //it says 999 otherwise millis%1000 could display 0
  lcd.print(999 - (millis() %1000));
}

Hi here is something I had written a while back that could give you a sense how to call a function (performTick()) on a user specific clock. Default clock speed is 1 tick per second, but you can just select the pace by sending a code through the serial monitor (set at 115200 bauds)

// Author: J-M-L for Arduino Forum (https://forum.arduino.cc/index.php?action=profile;u=438300)
// originally posted at  https://forum.arduino.cc/index.php?topic=649133.msg4377744#msg4377744
// Software License Agreement = standard BSD License

unsigned long tickPeriod = 1000; // 1s
unsigned long chrono;

void printUsage()
{
  Serial.print(F("------- P:"));
  Serial.print(tickPeriod);
  Serial.println(F(" ------"));
  Serial.println(F("Enter Tick options"));
  Serial.println(F("0\t1/4x speed"));
  Serial.println(F("1\t1/2x speed"));
  Serial.println(F("2\t1x speed"));
  Serial.println(F("3\t2x speed"));
  Serial.println(F("4\t4x speed"));
  Serial.println(F("------------------"));
}

void performTick()
{
  Serial.print(F("Tick "));
  Serial.println(chrono / 100); // integral division
}

void checkUserInput()
{
  int b = Serial.read(); // returns -1 if nothing to read. don't bother with available() which is slower since commands are only 1 character long
  switch (b) {
    case '0': {
        tickPeriod = 4000;
        printUsage();
        break;
      }
    case '1': {
        tickPeriod = 2000;
        printUsage();
        break;
      }
    case '2': {
        tickPeriod = 1000;
        printUsage();
        break;
      }
    case '3': {
        tickPeriod = 500;
        printUsage();
        break;
      }
    case '4': {
        tickPeriod = 250;
        printUsage();
        break;
      }
  }
}

void checkTick()
{
  if (millis() - chrono >= tickPeriod) {
    performTick();
    chrono += tickPeriod;
  }
}

void setup()
{
  Serial.begin(115200);
  printUsage();
  chrono = millis();
}

void loop()
{
  checkUserInput();
  checkTick();
  // you can do other stuff here if it's not too long
}

Very interesting code, Thanks.

I will look into how I might be able to use the concept as a millis() replacement in my code.

Tinkercad circuit

Maybe visually this might help.

HI,
Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?
Not a Tinkercad or Fritzy picture.

Post a schematic that shows labels on pins and part numbers.

Thanks.. Tom... :slight_smile:

The picture you have from Tinkercad shows the circuit perfectly? No?
I am using a standard 16X2 LCD

// This defines the LCD wiring to the DIGITALpins

const int rs = 12, 
             en = 11, 
             d4 = 5, 
             d5 = 4, 
             d6 = 3, 
             d7 = 2;

LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

and I am using standard 5mm LED with 220 Ohm resistor tied to pin 6 of the UNO,
and the buzzer is one that you would find in an old PC (I am not using a resistor though I should probably be using a 1K Ohm resistor) is pied to pin 9 of the UNO.

Essentially the circuit is irrelevant to the problem as I am just working with timings.
References to lcd.print() could simply be replaced by Serial.print() with the same effect.

The link to Tinkercad was made to make simulating the problem easier,
as you can run the program online through the emulator without having to built the circuit yourself.

Alain74Martel:
The picture you have from Tinkercad shows the circuit perfectly? No?

No?
These pictures do not help with any trouble shooting.
Have you built this project in the real world?
If not, then get your hands dirty and BUILD IT.
Run it in the real world, and see if the timing problem REALLY exists.
Simulators are not perfect, especially free ones.
Tom.... :slight_smile:

Yes Tom,
I have built the circuit in the real world and tested the system.

I was able to reproduce the exact issue by dumbing down both the code and the circuit.

Once again, this is a software issue and not a hardware problem.

I will not be providing any pictures, as I said before the issue is in the code not the circuit.
It can’t get any simpler than connecting an LCD directly to the Arduino.

It looks like my issue is when I transition from one speed to the next.

what do you see and what would you want to see?
explain the behavior you are trying to generate

Hi,

The link to Tinkercad was made to make simulating the problem easier,
as you can run the program online through the emulator without having to built the circuit yourself.

No problem, but at this stage we were lead to believe you had only run it on simulator.

I stand corrected... Tom.. :slight_smile:

Hi,
Running your simulation without any info about when it is supposed to change speed and what it does when the fault occurs, leave us needing more information.

  • When is the alarm supposed to sound.
  • What speed are we seeing when the simulation starts?
  • When does the speed change?

Thanks.. Tom... :slight_smile: