Timed Processes

When movement is detected, this thing needs to go on for three seconds after which it should not be triggered for ten seconds. But if nothing happens in two minutes, it should trigger by itself.

A random colleague student

The global behaviour of an installation based on a microcontroller, often includes timed processes. For instance, after a trigger, the input is blocked for a certain amount of time; if nothing happens for a while, generate an event; if within a certain time span a second trigger is sensed, change the behaviour; etc.

The most fundamental blink example suggests that timing can be controlled with a delay() function. However, the delay() has a fundamental issue which renders it useless in all of the situations described above: the program will proceed to the next line of code only when the wait is finished. In other words, during a delay(), all processing comes to a standstill. The function delay() could as well have been called holiday(). Therefore, ditch the delay().

The blink without delay and debounce examples introduce an approach to timing using the millis() function. It is an approach in which the passing of time is measured based on an onboard clock. When multiple overlapping time units are involved, this approach tends to become complex and difficult to manage. An alternative solution can be found in using the millisDelay function, which is part of an external library. This library can be found and installed through the Manage Libraries… menu option.

Detecting State Change

In order to connect an action to sensor input, it is important to be clear about what is the difference between a sensor measurement—e.g. a value from digitalRead()—and a state changement—e.g. when a button is pressed, while before it wasn’t. Sensor measurement produces a stream of values, while a state change should be able to detect when a button is pressed. A stream of values is what is produced in the Digital Read Serial example.

In order to detect a state change, the code needs to be extended in such a way that the current state can be compared with the previous. When considering that what is defined in the loop() routine represents a single lifespan of the loop, something needs to be handed to the collective unconsciousness—or, in terms of Arduino code, a global variable—before the current run of the loop dies in order to be retained.

const int ledPin = 13;
const int switchPin = 2;
bool beforeOpen = true;


void setup() {
  pinMode(switchPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
  Serial.begin(115200);
}

void loop() { // loop is (re)born
  bool nowClosed = digitalRead(switchPin) == 0;
  if (nowClosed && beforeOpen) {
    digitalWrite(ledPin, HIGH);
    Serial.println("switch closed");
    delay(5); // lazy debounce
  }
  else if (!nowClosed && !beforeOpen) {
    digitalWrite(ledPin, LOW);
    Serial.println("switch open");
    delay(5); // lazy debounce
  }
  beforeOpen = !nowClosed; // update global variable for next loop
} // loop dies

Temporarily Block Sensor Input

Temporarily block sensor input…

#include <millisDelay.h>

const int ledPin = 13;
const int switchPin = 2;
bool beforeOpen = true;
bool blocked = false;
int LED_time = 2000;
int switchCount = 0;

millisDelay ledDelay;

void setup() {
  pinMode(switchPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
  Serial.begin(115200);
}

void loop() {
  bool nowClosed = digitalRead(switchPin) == 0;
  if (nowClosed && beforeOpen && !blocked) {
    Serial.print("switch closed ");
    switchCount += 1;
    Serial.println(switchCount);
    digitalWrite(ledPin, HIGH);
    ledDelay.start(LED_time);
    blocked = true;
//    delay(5);
  }
//  else if (!nowClosed && !beforeOpen) {
//    Serial.println("switch open");
//    delay(5);
//  }
  if (ledDelay.justFinished()) {
    digitalWrite(ledPin, LOW);
    blocked = false;
  }
  beforeOpen = !nowClosed;
}

Auto-Trigger

Auto-trigger…

#include <millisDelay.h>

const int ledPin = 13;
const int switchPin = 2;
bool beforeOpen = true;
bool blocked = false;
bool autoMode = false;
bool autoReset = false;
int LED_time = 2000;
int auto_time = 10000;
int switchCount = 0;

millisDelay ledDelay;
millisDelay autoDelay;

void setup() {
  pinMode(switchPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
  Serial.begin(115200);
  autoDelay.start(auto_time);
}

void loop() {
  bool nowClosed = digitalRead(switchPin) == 0;
  if (nowClosed && beforeOpen && !blocked || autoMode) {
    digitalWrite(ledPin, HIGH);
    ledDelay.start(LED_time);
    if (autoDelay.isRunning()) {
      autoDelay.finish();
      autoReset = true;
    }
    switchCount += 1;
    if (Serial) {
      Serial.print("switch closed ");
      Serial.println(switchCount);
    }
    blocked = true;
    autoMode = false;
  }
  if (ledDelay.justFinished()) {
    digitalWrite(ledPin, LOW);
    blocked = false;
    autoDelay.start(auto_time);
  }
  if (autoDelay.justFinished()) {
    if (!autoReset) {
      autoMode = true;
    }
    autoReset = false;
  }

  beforeOpen = !nowClosed;
}