How to background a sleep in arduino?

2019-08-27 23:08发布

问题:

I am just starting with arduino and I already love it. One of the projects I would like to try deals with the follwing pseudo-code flow:

def switch():
    set_pin_2_to_HIGH
    wait_10_seconds
    set_pin_2_to_LOW

loop()
  // some time sensitive code, which should be executed on a
  // reasonably regular cycle
  if_pin_1_is_HIGH:
     switch()
  // some more code where the state of pin_2 will be tested, that code also need
  // to run in a reasonably regular cycle

I therefore need the loop to loop without stopping 10 seconds in switch() when the condition is met. In other words, can a function be backgrounded, or is there a mechanism similar to e.g. after() in Tkinter ?

I found a clever workaround where one "manually" checks time intervals but hope for a more arduinoic solution.

回答1:

Maybe you could try the other way around. Instead of following a "non-blocking sleep" concept you can use time-division multiplexing or in other words.. threading.

Using threads for blinking may sound overcomplicated but protothreads work well in handling not time-critical tasks. Quick introduction and arduino port can be found here: http://waynemay.com/threading-in-arduino/ Post from the link shows how to implement continuous blinking which is not exactly what you are looking for but your problem can be solved by handling blinking sequences.

The idea how threads can be used to handle blink sequences is that you modify a global variable (required blinks count) and use threads which take care of blinking without any attention from your logic and any blocking calls. Threads also decrement the variable each time blink is performed.

Here is a sample of what I use to handle blinks using threads:

#include <graham-pt.h>

#define STATUS_PIN           13
#define SENSING_PIN          2

#define STATUS_BLINK_UPDT_INTRVL 100
#define STATUS_BLINK_ON_DURATION_INTRVL 100
#define STATUS_BLINK_OFF_DURATION_INTRVL 10
#define STATUS_BLINK_SEQUENCE_PAUSE_INTRVL 30

int statusBlinksCnt = 0;
unsigned long nowTs, lastSenseTs = 0;

struct pt ptIoThd, ptStatusBlinkOnThd, ptStatusBlinkOffThd;

/* statusBlinkOnThd turns on status led and control status blinking sequences
 */
static int statusBlinkOnThd(struct pt *pt, unsigned long _now, unsigned long _interval){
  static unsigned long lastUpdtTs = 0;
  static unsigned int onIntrvlsCnt = 0;
  static int constantIntrvlsCnt = 0; 
  static int sequencePauseDuration = -1; 
  PT_BEGIN(pt);

  while(1){
    PT_WAIT_UNTIL(pt, abs(_now - lastUpdtTs) >= _interval);  // millis() will overflow after 59 days so abs val must be calculated; one inconsistent sleep between scans avary 2 months is negible
    lastUpdtTs = _now;
    if (statusBlinksCnt == 0){
      digitalWrite(STATUS_PIN, LOW);
    }
    else{
      if (digitalRead(STATUS_PIN) == HIGH)
        onIntrvlsCnt++;
      if (statusBlinksCnt < constantIntrvlsCnt){
        constantIntrvlsCnt = statusBlinksCnt;   
      } 
    }

    if (onIntrvlsCnt >= STATUS_BLINK_ON_DURATION_INTRVL){
      digitalWrite(STATUS_PIN, LOW);
      onIntrvlsCnt = 0;
      if(statusBlinksCnt>0){
        statusBlinksCnt--;
      }
      else if (statusBlinksCnt<0)
        statusBlinksCnt++;

      if (statusBlinksCnt == 0){ //end of blinking sequence
        if (constantIntrvlsCnt < 0){
          sequencePauseDuration = STATUS_BLINK_SEQUENCE_PAUSE_INTRVL;
        }
      }
    }

    if (sequencePauseDuration > 0){
      sequencePauseDuration--;
      if(sequencePauseDuration==0){
        sequencePauseDuration = -1;
        statusBlinksCnt = constantIntrvlsCnt;
      }
    }


  }

  PT_END(pt);
}

/* statusBlinkOffThd turns off status led when blinking takes place
 */
static int statusBlinkOffThd(struct pt *pt, unsigned long _now, unsigned long _interval){
  static unsigned long lastUpdtTs = 0;
  static unsigned int offIntrvlsCnt = 0; 
  PT_BEGIN(pt);

  while(1){
    PT_WAIT_UNTIL(pt, abs(_now - lastUpdtTs) >= _interval);  // millis() will overflow after 59 days so abs val must be calculated; one inconsistent sleep between scans avary 2 months is negible
    lastUpdtTs = _now;
    if (statusBlinksCnt == 0){
      digitalWrite(STATUS_PIN, LOW);
    }
    else{
      if (digitalRead(STATUS_PIN) == LOW)
        offIntrvlsCnt++;
    }

    if (offIntrvlsCnt >= STATUS_BLINK_OFF_DURATION_INTRVL){
      digitalWrite(STATUS_PIN, HIGH);
      offIntrvlsCnt = 0;
    }
  }
  PT_END(pt);
}

void setup(){
  pinMode(SENSING_PIN, INPUT_PULLUP);
  pinMode(STATUS_PIN, OUTPUT);
  PT_INIT( &ptStatusBlinkOnThd );
  PT_INIT( &ptStatusBlinkOffThd );
  statusBlinksCnt = 2;
}

void loop(){
  nowTs = millis();

  // updating threads
  statusBlinkOnThd(&ptStatusBlinkOnThd, nowTs, STATUS_BLINK_UPDT_INTRVL);
  statusBlinkOffThd(&ptStatusBlinkOffThd, nowTs, STATUS_BLINK_UPDT_INTRVL);

  // debounce digital read by setting statusBlinkCnt indirectly - after certain delay after switch is pressed and hold
  if (digitalRead(SENSING_PIN) == LOW){
    if (lastSenseTs == 0){
     lastSenseTs = nowTs;
    }
  }
  else{
    lastSenseTs = 0;
  }

 //simple bouncing; may not work on millis counter overflow every ~59 days
  if(lastSenseTs > 0)
     if(nowTs - lastSenseTs > 50){
       statusBlinksCnt = 1; 
       lastSenseTs = 0;             // should be removed if blinking should be continuous when input is kept at LOW 
     }
}

The code above makes two 10 second blinks on start up with 1 second pause in between. If you set statusBlinksCnt to a positive value that many blinks will be performed by the blinking threads. If you set statusBlinksCnt to a negative value your arduino will keep blinking defined sequence of blinks continuously with pause between sequences defined in STATUS_BLINK_SEQUENCE_PAUSE_INTRVL. Off, on and pause durations can be calculated by multiplying defines by STATUS_BLINK_UPDT_INTRVL expressed in milliseconds.

I didn't have time (and breadboard nearby..) to test the input pin reading and trivial denouncing but it should work however I just noticed you want to sense HIGH state whereas my code uses pull-up on input and blinks on the LOW input state.