Millis() code again

Hi Everyone!
Having some trouble with millis. Wanted to control a blower depending on a sensor. The sensor debounce time works well since it is from arduino sample code however blower() function time randomly changes. Sometimes it waits for the 5000 interval sometimes longer sometimes shorter. Probably something simple however I cant figure out what.

int firstState = LOW;
unsigned long previousMillis = 0;
unsigned long interval = 5000;

int sensState = HIGH;
int buttonState = LOW;
int lastButtonState = HIGH;

unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 5000;

const int vmot = 8;
const int atmo = 9;
const int gran = 10;
const int sens = A4;

void setup() {

  pinMode(vmot, OUTPUT);
  pinMode(atmo, OUTPUT);
  pinMode(gran, OUTPUT);
  pinMode(sens, INPUT_PULLUP);

}

void sensor() {
    
  int reading = digitalRead(sens);

  if (reading != lastButtonState) {
     lastDebounceTime = millis(); }
  if ((millis() - lastDebounceTime) > debounceDelay) {
     if (reading != buttonState) {
        buttonState = reading;
        if (buttonState == HIGH) {
           sensState = LOW;
         } else {
           sensState = HIGH;
         }
       }
     }
  lastButtonState = reading;

}

void blower() {

   if (sensState == LOW) {
  
       if (firstState == LOW) { 
          digitalWrite(vmot, HIGH);
          digitalWrite(atmo, HIGH);
          digitalWrite(gran, LOW);
          }
         
       if ((millis() - previousMillis) >= interval) {
          previousMillis = millis();
          if (digitalRead(vmot)== HIGH) {
             firstState = HIGH; }
          }

       if (firstState == HIGH) {
          digitalWrite(vmot, HIGH);
          digitalWrite(atmo, LOW);
          digitalWrite(gran, HIGH);
          }
       }
   
  else {
    
        if (firstState == HIGH) {   
           digitalWrite(atmo, HIGH);
           digitalWrite(vmot, HIGH);
           digitalWrite(gran, LOW); 
           }
           
        if ((millis() - previousMillis) >= interval) {
           previousMillis = millis();
           if (digitalRead(atmo)== HIGH) {
              firstState = LOW;  } 
           }
            
        if (firstState == LOW) {
           digitalWrite(vmot, LOW);
           digitalWrite(atmo, LOW);
           digitalWrite(gran, LOW);
           }
       }  
}
void loop() {

  sensor();
  blower();
  
}

You need one more variable to keep track of the “last” sensState and once it changes, you set previousMillis = millis() and you update the last sensState.

This should fix your timing issues.

Hi! Thank You for your reply. Do you mean something like this?

int lastSensState;

           if (sensState != lastSensState) {
     previousMillis = millis(); 
     sensState = lastSensState; }

I look forward to trying your code and trying @HazardsMind 's suggestion.

But I note that you currently make no use of serial printing to the IDE serial monitor.

Using print statements is your first and easiest tool for seeing what your code is actually doing.

Make your sketch talk! That's the first thing I will do if your sketch doesn't run right (I believe you, but trust and verify).

Print statements can verify the values of key variables and show whether or not they are informing flow through your code correctly.

Sprinkle them about and see what is happening.

HTH

a7

I have your code limping along nicely; it seems to function to your plan.

Note: I used a slide switch for my "sensor". There is no apparent reason for the verrrry loooong denounce time: it is the edges that are of interest. You may have reasons that are not evident to me, please say if.

Note: your logic is flawed in that it does not account for changes in the sensor state that come too frequently - you get stuck having done half of one of the two parts. This may be why increasing that debouce delay seemed to fix something.

So if you change the sensor every fifteen seconds or longer, you should see proper LED activity.

Here's the code. I did not need another variable, I just set the one you do use at the points in your code where the sesnor state changes. My comments are //...

I added serial printing and a few tricks to reduce the amount of printing. I added the setting of the timer variables. Otherwise it is your logic, your structure, your code.

int firstState = LOW;

unsigned long previousMillis = 0;
unsigned long interval = 5000;

static bool once;

int sensState = HIGH;
int buttonState = LOW;
int lastButtonState = HIGH;

unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;    //... 5000: annoying and absurd - sensor issue?

const int vmot = 8;
const int atmo = 9;
const int gran = 10;
const int sens = A4;

void setup() {
 Serial.begin(9600);
 Serial.println("\nhello, world\n");

 pinMode(vmot, OUTPUT);
 pinMode(atmo, OUTPUT);
 pinMode(gran, OUTPUT);
 pinMode(sens, INPUT_PULLUP);
}

void sensor() {

 int reading = digitalRead(sens);

 if (reading != lastButtonState) {
    lastDebounceTime = millis(); }
 if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != buttonState) {
       buttonState = reading;
       if (buttonState == HIGH) {
Serial.println("senseState <- LOW");

//... fix: set previous millis() here
          previousMillis = millis();
once = false; // printer control
          sensState = LOW;
        } else {
Serial.println("senseState <- HIGH");

//... and here too
          previousMillis = millis();
once = false; // printer control
          sensState = HIGH;
        }
      }
    }
 lastButtonState = reading;

}

void blower() {



  if (sensState == LOW) {
      if (firstState == LOW) {
       if (!once) {
         Serial.println("group 1 sensState LOW to LEDs/n");
         once = true;
       }
         digitalWrite(vmot, HIGH);
         digitalWrite(atmo, HIGH);
         digitalWrite(gran, LOW);
      }

if (1) {    //... change to 0 once you believe the timer is functioning correctly
unsigned long elapsed = millis() - previousMillis;

Serial.print("                     firstState = ");
Serial.print(firstState);
Serial.print(" elapsed = ");
Serial.println(elapsed);
}

      if ((millis() - previousMillis) >= interval) {
       Serial.println("and sensState LOW firstState LOW timed out");
       once = false;
         previousMillis = millis();
         if (digitalRead(vmot)== HIGH) {
            firstState = HIGH; }
         }

      if (firstState == HIGH) {
       if (!once) {
         Serial.println("group 2 sensState LOW to LEDs/n");
         once = true;
       }
         digitalWrite(vmot, HIGH);
         digitalWrite(atmo, LOW);
         digitalWrite(gran, HIGH);
         }
      }

 else {

       if (firstState == HIGH) {

       if (!once) {
         Serial.println("group 1 sensState HIGH to LEDs/n");
         once = true;
       }

          digitalWrite(atmo, HIGH);
          digitalWrite(vmot, HIGH);
          digitalWrite(gran, LOW);
          }

if (1) {    //... change to 0 once you believe the timer is functioning
unsigned long elapsed = millis() - previousMillis;

Serial.print("                     firstState = ");
Serial.print(firstState);
Serial.print(" elapsed = ");
Serial.println(elapsed);
}

       if ((millis() - previousMillis) >= interval) {
       Serial.println("and sensState HIGH firstState HIGH timed out");
       once = false;
          previousMillis = millis();
          if (digitalRead(atmo)== HIGH) {
             firstState = LOW;  }
          }

       if (firstState == LOW) {
       if (!once) {
         Serial.println("group 2 sensState HIGH to LEDs/n");
         once = true;
       }
          digitalWrite(vmot, LOW);
          digitalWrite(atmo, LOW);
          digitalWrite(gran, LOW);
          }
      }
}

void loop() {

 sensor();
 blower();

 delay(100); // spam mitigation harmless in this sketch
}

TBH I would do this a bit differently, but you got so close I think it is worth fixing. You may hear from others all about how this "should" be done. They might write sketches that take different approaches. You may be in the hobby long enough so that one day you will embark on a learning journey all about that.

a7

Thank You for helping! I added previousMillis = millis(); in void sensor() and everything works as it should!

        if (buttonState == HIGH) {
           previousMillis = millis();
           sensState = LOW;
         } else {
           previousMillis = millis();
           sensState = HIGH;

The verrrry loooong debounce is a quick fix for mistriggering the sensor, really necessary in this application. Probably it could be done in some nicer way. I really would like to know how this "should" be done.

Thank You!!!

I would start with a written prose description. We can reverse engineer the desired behaviour, but a story makes it easier:

I have a sensor controlling a motor. The motor needs…

and so forth. Then a timing diagram, here's one I drew. If it looks like I drew it with my finger there's a reason for that.

That's where many would start before writing any code at all.

Mostly what I don't like about your original is the similarity between the two major code sections controlled by sensState. And the fact that the logic is obscured by the code.

BTW the sesnor denounce time is probably the easiest way to say "ignore sensor signals that don't last very long". It was just annoying during testing, life is very short.

For this project, what you might take away are just two things: printing is your friend - write printing into the original code, make it talk. You can even start with print statements that admit that something will be done here or there one day… nice reminders of to-do items. "Play reward tones here" kinda thing. Look where I added printing as I worked with you sketch. Analysis is fine, but printing will be the truth no matter you don't quite understand the logic. Those bogus timers proved their bogosity in an instant.

And second, the more time you spend exactly specifying the desired behaviour, the more the code will seem to practically write itself. Even though coding is fraught, deciding things before a single line of code is written makes the job easier.

HTH

a7

a7

Thank you very much for the advice! In fact I did use a lot of serial printing in testing a while ago. However then I simply got a bit lazy and started to test code in tinkercad and wokwi simply to check if it does what it suppose to do. Learned my lesson!
Thank you!

Very good. Does it do? Would you post what you have found to work?