Millis (); won't work properly

unsigned long PERIOD1 = 25;  // interval at which to blink (microseconds) = 20 Hz
unsigned long PERIOD2 = 12.5;  // interval at which to blink (microseconds) = 40 Hz
unsigned long PERIODact = 5000;  // amount of time to keep blinking (milliseconds)
unsigned long PERIODbtn = 150;  // (microseconds)

Do i need unsigned long?

OK. I went thru it in a bit more detail. Try this:

#include <Wire.h>   
#include <LiquidCrystal_I2C.h>

#define BTN_PRESSED     HIGH        //logic level of pin when button is pressed

// Pin assignement
const uint8_t ledPin = 7;
const uint8_t btnPin = 8;

LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); 
//LiquidCrystal_I2C lcd(0x3f, 16, 2);

enum fcnMode 
{
    OFF=0,
    BLINK1,
    BLINK2,
    NBSTATE
    
};  // OFF = 0 and NBSTATE=7

uint8_t 
    ledState = LOW;  // ledState used to set the led
uint8_t 
    btnLast;
uint8_t 
    funcState = OFF;
uint32_t
    currentTime1, 
    currentTime2, 
    currentTimebtn, 
    currentTimeact1, 
    currentTimeact2;  // will store current time
uint32_t
    previousTime1, 
    previousTime2, 
    previousTimebtn, 
    previousTimeact1, 
    previousTimeact2;  // will store last time led was updated
    
const uint32_t 
    PERIOD1 = 25000ul,      // interval at which to blink (microseconds) = 20 Hz
    PERIOD2 = 12500ul,      // interval at which to blink (microseconds) = 40 Hz
    PERIODact = 5000ul,     // amount of time to keep blinking (milliseconds)
    PERIODbtn = 50ul;       // button read interval (milliseconds)

/******************************************************************\
SETUP
\******************************************************************/
void setup() 
{
    lcd.begin(16, 2); // LCD 
    pinMode(btnPin, INPUT_PULLUP);
    btnLast = digitalRead( btnPin);
    pinMode(ledPin, OUTPUT);
}
/******************************************************************\
Main Function of the code
\******************************************************************/
void loop() 
{
    buttonPressed();
    setMode();
}
/******************************************************************
SUBFUNCTIONS
\******************************************************************/
void buttonPressed()
{
    currentTimebtn = millis();
    if (currentTimebtn - previousTimebtn >= PERIODbtn) 
    { 
        // push button delay to prevent fast toggling between funcstates when pressed
        previousTimebtn = currentTimebtn;
        
        uint8_t btnNow = digitalRead( btnPin );
        if( btnNow != btnLast )
        {
            btnLast = btnNow;
            if( btnNow == BTN_PRESSED )
            {
                funcState += 1;
                if (funcState >= NBSTATE)   //changed from '4' 
                    funcState = 0;
                
                lcd.clear();
                switch( funcState )
                {
                    case    OFF:
                        digitalWrite(ledPin, LOW);
                        lcd.setCursor(4,0);
                        lcd.print("*Idle*"); // LCD main idle screen
                        
                    break;
                    
                    case    BLINK1:
                        lcd.setCursor(2,0);
                        lcd.print("Mode: 20 Hz"); // LCD shows selected mode
                        currentTimeact1 = millis();
                        previousTime1 = micros();
                        
                    break;
                    
                    case    BLINK2:
                        lcd.setCursor(2,0);
                        lcd.print("Mode: 40 Hz"); // LCD shows selected mode
                        currentTimeact2 = millis();
                        previousTime2 = micros(); 
                        
                    break;
                    
                    default:
                    break;
                    
                }//switch

            }//if button is pressed
            
        }//if now does not equal last
        
    }//if time for a read

}//buttonPressed

void setMode() 
{
    // All Off
    switch (funcState) 
    {
        case OFF:
            //do nothing
        break;
        
        case BLINK1:
            blinkled1();
        break;
        
        case BLINK2:
            blinkled2();
        break;
    }
}

void blinkled1() 
{
    currentTime1 = micros();    
    if( (currentTime1 - previousTime1) >= PERIOD1 ) 
    { 
        // defines the blinking frequency
        // save the last time you blinked the led
        previousTime1 = currentTime1;
        // if the led is off turn it on and vice-versa:
        if (ledState == LOW) 
            ledState = HIGH;
        else 
            ledState = LOW;
            
        // set the led with the ledState of the variable: 
        digitalWrite(ledPin, ledState);
        
    }//if

    if( (millis() - previousTimeact1) >= PERIODact ) 
    { 
        // checks if time after the start is equal or longer than 5 seconds to stop blinking
        ReturnToIdleState();
        
    }//if
    
}//blinkled1

void blinkled2() 
{
    currentTime2 = micros();
    if( (currentTime2 - previousTime2) >= PERIOD2 ) 
    { 
        // defines the blinking frequency
        // save the last time you blinked the led
        previousTime2 = currentTime2;
        // if the led is off turn it on and vice-versa:
        if (ledState == LOW) 
            ledState = HIGH;
        else 
            ledState = LOW;
            
        // set the led with the ledState of the variable:
        digitalWrite(ledPin, ledState);
    }//if
    
    if( (millis() - previousTimeact2) >= PERIODact ) 
    { 
        // checks if time after the start is equal or longer than 5 seconds to stop blinking
        ReturnToIdleState();
    }//if
    
}//blinkled2

void ReturnToIdleState( void )
{
    lcd.clear();
    funcState = OFF; //go back to iddle screen
    digitalWrite(ledPin, LOW);
    lcd.setCursor(4,0);
    lcd.print("*Idle*"); // LCD main idle screen
    
}//ReturnToIdleState

This one if press button fast as soon as the program was loaded in arduino memory, or arduino is reseted, it works for 5 secs and stops, and then it does not matter how many times i hit the button again, it is still stuck in * Idle * and nothing happens.
It seem that the 5 secs counter starts as soon as arduino starts running the program and the operation below can not get the result equal or longer than 5 secs like it was suposed to do.

if( (millis() - previousTimeact1) >= PERIODact ) // checks if time after the start is equal or longer than 5 seconds to stop blinking

I guess the timer starts counting 1...2...3...4...5...6...7...8...9...10...infintely, so the program only gets 5 sec once, and then every math operation after, the result value is always higher than 5 sec, and it automatically stays in iddle forever.

I admittedly haven't tried it on an Arduino here; if time permits tomorrow I'll give it a try.

In the meantime, how is your switch wired? I noticed that you look for a HIGH to indicate the button is pressed but also use INPUT_PULLUP. I switched the code to look for a low-to-high transition but that still may not work right if the switch it wired weirdly.

Can you set the pinmode to INPUT (not INPUT_PULLUP)? With the pullup you're forming a resistor divider on pin 8.

    pinMode(btnPin, INPUT);

Done, nothing changed about the main issue with the timer.

OK. I'll try to replicate tomorrow.

Alright mate, i appreciate your effort to help me. Thank you!

Here's a duty cycle sketch to play with:

/*
 Enter Hz in Serial monitor with h followed by number like this:
   h20{ENTER]
 Enter percent like:
   p50[ENTER]
 Or both:
 h20,p50[ENTER] 
 */
 
unsigned long cycleStart,
              onTime,              
              cycleTime,
              hz;
int val;              
const byte powerPin = LED_BUILTIN;
byte percent = 50;
void setup()
{
  Serial.begin(9600);
  Serial.println(" Hz = 5  Duty Cycle = 50%");
  hz = 5;
  cycleTime = 1000000UL / hz;
  onTime = cycleTime / 2;  
  pinMode(powerPin,OUTPUT);
}
void loop()
{
  digitalWrite(powerPin,micros() - cycleStart < onTime);  
  if(micros() - cycleStart > cycleTime)
    cycleStart += cycleTime;
  // if there's any serial available, read it:
  if(Serial.available() > 0)
  {
     val = Serial.read();
    if(val == 'h' || val == 'H')
      hz = Serial.parseInt();
    else if(val == 'p' || val == 'P')
    {
      percent = Serial.parseInt();   
    }
    else
      while(Serial.available() > 0)
        Serial.read();  
    cycleTime = 1000000UL / hz;
    percent = constrain(percent, 0, 100);
    onTime = cycleTime * 0.01 * percent;
    cycleStart = micros();
    Serial.print(" Hz = ");
    Serial.print(hz);
    Serial.print("  Duty Cycle = ");
    Serial.print(percent);
    Serial.println("%");
  }
} 

I will also take a look at this, thanks!

Yes. And you should keep the const.

12.5 is not a valid integer. The comment is not correct. Because of the fraction you will have to use micros.

Because of the overhead between each toggle there will be some jitter. What accuracy are you expecting?

May I ask you why? You won't be able to see a 40 Hz blinking when looking directly at it. You may be able to perceive some flicker with your peripheral vision (which is more sensitive to movement), but that's all. Perchance you are trying to emulate PWM? What is your prototype about?

@ 330R
I'm going to lower the frequency later, at the moment it does not matter, it has nothing to do with the issue i described.

@ Coding_Badly
I changed from millis (); to micros (); and if i got it right what you wrote, i put const unsigned long. The issue persists.

const unsigned long PERIOD2 = 125000;

No. It isn't. Setting currentTimeact1 is not the same as setting previousTimeact1 to the right value ("to millis()") in the right place ("when the button changes funcState").

1 Like

OK, found the problem: I was updating currentTimeact1 instead of previousTimeact1 in the button-press section. Seems to work now:

#include <Wire.h>   
#include <LiquidCrystal_I2C.h>

#define BTN_PRESSED     HIGH        //logic level of pin when button is pressed

// Pin assignement
const uint8_t ledPin = 7; //LED_BUILTIN;    //built-in for debug
const uint8_t btnPin = 8;

//LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); 
LiquidCrystal_I2C lcd(0x3f, 16, 2);

enum fcnMode 
{
    OFF=0,
    BLINK1,
    BLINK2,
    NBSTATE
    
};  // OFF = 0 and NBSTATE=7

uint8_t 
    ledState = LOW;  // ledState used to set the led
uint8_t 
    btnLast;
uint8_t 
    funcState = OFF;
uint32_t
    currentTime1, 
    currentTime2, 
    currentTimebtn, 
    currentTimeact1, 
    currentTimeact2;  // will store current time
uint32_t
    previousTime1, 
    previousTime2, 
    previousTimebtn, 
    previousTimeact1, 
    previousTimeact2;  // will store last time led was updated
    
const uint32_t 
    PERIOD1 = 25000ul,      // interval at which to blink (microseconds) = 20 Hz
    PERIOD2 = 12500ul,      // interval at which to blink (microseconds) = 40 Hz
    PERIODact = 5000ul,     // amount of time to keep blinking (milliseconds)
    PERIODbtn = 50ul;       // button read interval (milliseconds)

/******************************************************************\
SETUP
\******************************************************************/
void setup() 
{
    Serial.begin(9600); //debug
    lcd.begin(16, 2); // LCD 
    pinMode(btnPin, INPUT);
    btnLast = digitalRead( btnPin);
    pinMode(ledPin, OUTPUT);
}
/******************************************************************\
Main Function of the code
\******************************************************************/
void loop() 
{
    buttonPressed();
    setMode();
}
/******************************************************************
SUBFUNCTIONS
\******************************************************************/
void buttonPressed()
{
    currentTimebtn = millis();
    if (currentTimebtn - previousTimebtn >= PERIODbtn) 
    {         
        // push button delay to prevent fast toggling between funcstates when pressed
        previousTimebtn = currentTimebtn;
        
        uint8_t btnNow = digitalRead( btnPin );
        if( btnNow != btnLast )
        {
            btnLast = btnNow;
            if( btnNow == BTN_PRESSED )
            {                
                funcState += 1;
                if (funcState >= NBSTATE)   //changed from '4' 
                    funcState = 0;

                //Serial.print( "Func state: " ); Serial.println( funcState );
                lcd.clear();
                switch( funcState )
                {
                    case    OFF:
                        digitalWrite(ledPin, LOW);
                        lcd.setCursor(4,0);
                        lcd.print("*Idle*"); // LCD main idle screen
                        
                    break;
                    
                    case    BLINK1:                        
                        lcd.setCursor(2,0);
                        lcd.print("Mode: 20 Hz"); // LCD shows selected mode
                        previousTimeact1 = millis();     //activity timer
                        previousTime1 = micros();       //blink timer
                        
                    break;
                    
                    case    BLINK2:
                        lcd.setCursor(2,0);
                        lcd.print("Mode: 40 Hz"); // LCD shows selected mode
                        previousTimeact2 = millis();
                        previousTime2 = micros(); 
                        
                    break;
                    
                    default:
                    break;
                    
                }//switch

            }//if button is pressed
            
        }//if now does not equal last
        
    }//if time for a read

}//buttonPressed

void setMode() 
{
    // All Off
    switch (funcState) 
    {
        case OFF:
            //do nothing
        break;
        
        case BLINK1:
            blinkled1();
        break;
        
        case BLINK2:
            blinkled2();
        break;
    }
}

void blinkled1() 
{
    currentTime1 = micros();    
    if( (currentTime1 - previousTime1) >= PERIOD1 ) 
    { 
        // defines the blinking frequency
        // save the last time you blinked the led
        previousTime1 = currentTime1;
        // if the led is off turn it on and vice-versa:
        if (ledState == LOW) 
            ledState = HIGH;
        else 
            ledState = LOW;
            
        // set the led with the ledState of the variable: 
        digitalWrite(ledPin, ledState);
        
    }//if

    if( (millis() - previousTimeact1) >= PERIODact ) 
    { 
        // checks if time after the start is equal or longer than 5 seconds to stop blinking
        ReturnToIdleState();
        
    }//if
    
}//blinkled1

void blinkled2() 
{
    currentTime2 = micros();
    if( (currentTime2 - previousTime2) >= PERIOD2 ) 
    { 
        // defines the blinking frequency
        // save the last time you blinked the led
        previousTime2 = currentTime2;
        // if the led is off turn it on and vice-versa:
        if (ledState == LOW) 
            ledState = HIGH;
        else 
            ledState = LOW;
            
        // set the led with the ledState of the variable:
        digitalWrite(ledPin, ledState);
    }//if
    
    if( (millis() - previousTimeact2) >= PERIODact ) 
    { 
        // checks if time after the start is equal or longer than 5 seconds to stop blinking
        ReturnToIdleState();
    }//if
    
}//blinkled2

void ReturnToIdleState( void )
{    
    lcd.clear();
    funcState = OFF; //go back to iddle screen
    digitalWrite(ledPin, LOW);
    lcd.setCursor(4,0);
    lcd.print("*Idle*"); // LCD main idle screen
    
}//ReturnToIdleState
1 Like

@ johnwasser
@ Blackfin
First of all i would like to thank both of you guys, @ johnwasser your comment just made my first code work smoothly tha way i needed.
And also special thanks to @Blackfin for your effort to help me, your code also works the way it was supposed to do. Unfortunately, I can't mark both of your replies as a solution, but it is finally solved. It has been very important all the replies i got from the community, as a beginer i'm learning with you guys, i hope in the future i'll be able to help others as far as possible, as i get knowlodge. Thanks!

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