简体   繁体   中英

Arduino 'time out' function using a millis timer

I've not been programming for long and I just want to expand from electronic engineering with an Arduino UNO board.

I've started a new project based on the Secret Knock Detecting Door Lock by Steve Hoefer on Grathio and I'd like to implement the following:

( http://grathio.com/2009/11/secret_knock_detecting_door_lock/ ) ( http://grathio.com/assets/secret_knock_detector.pde )

Implementation

If the global value equals 0 and the valid knock patter is true then flash a yellow LED 4 times using millis rather than delay so that it can still 'listen'.

If another valid knock pattern is not heard within 6 seconds it will time out and reset global to 0 so that it can acknowledge the initial true pattern and flash the yellow LED.

If another valid knock pattern is heard withing 6 seconds, increment a counter.

If the counter equals 1, wait for another valid knock pattern and if true within 6 seconds, increment the counter again and don't flash the yellow LED.

Otherwise, time out and reset all values.

And so on until if the counter is greater than or equal to 4 trigger the master LED array.

Once is gets to 4 successful knocks, I'd like it to trigger the master LED array I've built.

Problems

This project was inspired by the test panels used on passenger airplanes. I've seen them a lot and thought it would be a good place to start and learn about timing.

There are a few problems as I don't wish to reset millis() every time and I'm using a button rather than the boolean within the knock detection script so I don't get lost in the code.

I understand this won't respond 50 seconds later and it's a beginners mistake but proves what I've got if I hold down the button. The code below also doesn't have a time out after the 1st digitalRead HIGH or true boolean (I am struggling with this).

Arduino sketch

  int inPin = 2;         // input pin switch
    int outPin = 3;       // output pin LED
    
    long currentTime = 0; // counter
    long nextTime = 0; // counter
    long lastTime = 0; // counter
    
    int patternCounter = 0; // build up 
    int globalValue = 0; // lock out
    int breakIn = 0; // waste of time?
    
    void setup()
    {
      pinMode(inPin, INPUT);
      pinMode(outPin, OUTPUT);
      Serial.begin(9600);
      Serial.println("GO");
    }
    
    void loop(){
    
      // boolean true, switch just for testing
      if (digitalRead(inPin)==HIGH&&globalValue==0&&breakIn==0) { 
        Serial.println("CLEARED 1st");
        delay (500); // flood protection
        globalValue++;
        breakIn++;
        if (globalValue>0&&breakIn>0){ 
          currentTime = millis(); // start a 'new' counter and 'listen'
          if (currentTime<6000) { // less than
            if (digitalRead(inPin)==HIGH) { // and true
              Serial.println("CLEARED 2nd"); // cleared the stage
              delay (500); // flood protection 
              patternCounter++;
            } // if counter less
          } // if true or high
          if (currentTime>6000) {
            Serial.println("TIMEOUT waiting 2nd"); // timed out
            globalValue = 0;
            patternCounter = 0;
            breakIn = 0;
          } // if more than
        } // global master
      }
    
      // 3rd attempt
      if (globalValue==1&&patternCounter==1){ // third round
        nextTime = millis(); // start a 'new' counter and 'listen'
        if (nextTime<6000) { // less than
          if (digitalRead(inPin)==HIGH) { // and true
            Serial.println("CLEARED 3rd");
            delay (500); // flood protection
            patternCounter++;
          } // if counter less
        } // if true or high
        if (nextTime>6000) {
          Serial.println("TIMEOUT waiting 3rd"); // timed out
          globalValue = 0;
          patternCounter = 0;
        } // if more than
       } // global master
    
      // 4th attempt and latch
      if (globalValue==1&&patternCounter==2){ // last round 
        lastTime = millis(); // start a 'new' counter and 'listen'
        if (lastTime<6000) { // less than
          if (digitalRead(inPin)==HIGH) { // and true
            digitalWrite(outPin, HIGH); // LED on
            Serial.println("CLEARED 4th ARRAY"); // cleared the stage
            delay(500); // flood protection
          } // true or high
        } // counter
        if (lastTime>6000) {
          Serial.println("TIMEOUT waiting 4th"); // timed out
          globalValue = 0;
          patternCounter = 0;
        } // if more than
       } // global and alarm
    
    
       } // loop end  

That's the current sketch, I understand the counters I've used are near pointless.

Any help would be greatly appreciated!

That is a lot to wade through so I may not understand your question but the bit of code below stands out as a problem:

   currentTime = millis(); // start a 'new' counter and 'listen'
      if (currentTime<6000) { // less than

      .....
      }

Do you understand that there is no "resetting" of millis() possible and that is merely a function that returns the number of milliseconds since the program launched? It will continue to increase as long as the program is running (until it rolls over but that is a separate problem). So in the above code 'currentTime' is only going to be < 6000 very, very briefly (6 seconds) and then never again (except for the rollover condition where millis resets).

So a typical way millis() is used to track time is, in setup , to store it's current value into a variable and add your timeout period value to it:

// timeoutAmount is defined at head of program. Let's say it is 6000 (6 seconds)
nextUpdate = millis() + timeoutAmount; 

Then in loop you can do the check:

if (millis() >= nextUpdate){
  nextUpdate = millis() + timeoutAmount; // set up the next timeout period

 // do whatever you want to do

}

Also be careful using delay() - it is easy to use for flow control but for any program with more than one thing going on it can lead to confusing and hard to solve problems.

Oh - there are more sophisticated ways of doing timing using the built-in timers on the chip to trigger interrupts but better to get the hang of things first.

I've come up with the following sketch after playing around with your help.

The sketch will almost do everything I wanted...

When it times out (T/O) after the 1st, 2nd (inCount = 1) or 3rd (inCount = 2) button press, I'd like it to revert back to the start without having to press it again and loop triggerFlash twice.

Either that or implementing another 'wait and listen' within the time out to move it to the 2nd (inCount = 1) etc but I think that may cause problems.

I know there's delay used within the flashes but that will be changed to millis(), I'm just trying to get the basic function and understanding.
const int switchPin = 2; // the number of the input pin const int BswitchPin = 4; // the number of the input pin const int outPin = 3; const int thePin = 5;

long startTime; // the value returned from millis when the switch is pressed
long escapeTime; // the value returned from millis when in time out
long duration;  // variable to store the duration

int inCount = 0;
int dupe = 0;

void setup()
{
  pinMode(switchPin, INPUT);
  pinMode(outPin, OUTPUT);
  pinMode(thePin, OUTPUT);
  digitalWrite(switchPin, HIGH); // turn on pull-up resistor
  Serial.begin(9600);
  Serial.println("Go");
  digitalWrite(outPin, HIGH);
}

void loop()
{
  if(inCount==0&&digitalRead(switchPin) == LOW)
  {
    // here if the switch is pressed
    startTime = millis();
    while(inCount==0&&digitalRead(switchPin) == LOW)
      ; // wait while the switch is still pressed
    long duration = millis() - startTime;
    if (duration<4000) {
      Serial.println("1");
      triggerFlash();
      inCount++;
    }
  } // master 1

  if (inCount>0&&inCount<4&&digitalRead(switchPin) == LOW) 
  {
    // here if the switch is pressed
    startTime = millis();
    while(inCount>0&&inCount<4&&digitalRead(switchPin) == LOW)
      ; // wait while the switch is still pressed
    long duration = millis() - startTime;
    delay(500); // flood protection
    if (duration>4000) { // script an escape here - formerly if (while will loop the condition)

      Serial.println("T/O");
      triggerFlash();
      inCount = 0;      
    }

    if (duration<4000) {
      dupe = inCount + 1;
      Serial.println(dupe);
      inCount++;
    }
  }
  if (inCount>=4) {
    digitalWrite(thePin, HIGH);
  }
} // loop

void triggerFlash() {
  int i = 0;
  for (i=0; i < 8; i++){   
    digitalWrite(outPin, LOW);
    delay(100);
    digitalWrite(outPin, HIGH);
    delay(100);
  } 
}

Any ideas are very appreciated! (edited with improved counting)

The above code is actually WRONG. Please be carefull with millis() as they rollover after some time. it is only long type. So if the millis+timeout is near max(long) and millis() will rollover and start counting from zero, the millis()>=nextupdate will be false even if the timeout actually occurs.

The correct way to do this is:

unsigned long start = millis();
unsigned long timeout = MY_TIMEOUT_HERE; 
...
//check if timeout occured
unisgned long now = millis();
unsigned long elapsed = now - start;
if(elapsed > timeout)
   //do whatever you need to do when timeout occurs

I just implement Arduino library. hope it help your problem. I made it to work like setTimeout and setInterval in javascript. You can download it here, Github

This is example of my code You can see it in action in Tinkercad

/*
  Author : Meng Inventor
  Contact : https://www.facebook.com/MLabpage
  15 July 2022
*/

#include "simple_scheduler.h"
#define LED1_PIN 7
#define LED2_PIN 6
#define LED3_PIN 5
#define GREEN_LED_PIN 4
Task_list job_queue;


void setup()
{

  Serial.begin(115200);

  pinMode(LED1_PIN, OUTPUT);
  pinMode(LED2_PIN, OUTPUT);
  pinMode(LED3_PIN, OUTPUT);
  pinMode(GREEN_LED_PIN, OUTPUT);

  // setInterval will run repeatly for every given time period (ms)
  job_queue.setInterval(blink_green, 1000);
  job_queue.setInterval(led1_on, 2000);
}

unsigned long timer = millis();
void loop()
{
  job_queue.update();
 
}
void led1_on(){
  digitalWrite(LED1_PIN, HIGH);
  job_queue.setTimeout(led1_off, 250); //setTimeout will run once after given time period (ms)
}
void led1_off(){
  digitalWrite(LED1_PIN, LOW);
  job_queue.setTimeout(led2_on, 250);//setTimeout will run once after given time period (ms)
}
void led2_on(){
  digitalWrite(LED2_PIN, HIGH);
  job_queue.setTimeout(led2_off, 250);//setTimeout will run once after given time period (ms)
}
void led2_off(){
  digitalWrite(LED2_PIN, LOW);
  job_queue.setTimeout(led3_on, 250);//setTimeout will run once after given time period (ms)
}

void led3_on(){
  digitalWrite(LED3_PIN, HIGH);
  job_queue.setTimeout(led3_off, 250);//setTimeout will run once after given time period (ms)
}
void led3_off(){
  digitalWrite(LED3_PIN, LOW);
}



void blink_green() {
  digitalWrite(GREEN_LED_PIN,HIGH);
  job_queue.setTimeout(blink_green_off, 500);
}

void blink_green_off() {
  digitalWrite(GREEN_LED_PIN,LOW);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM