Make an output pin behave like a monostable multivibrator

I wish to make an arduino output pin, say 13 go high after an IR sensor senses an obstacle at pin 2 (input pin). pin 13 should stay high for 5 seconds and go low even if the obstacle is still present. To make pin 13 high again, the obstacle should move away for a second and come back.
below is my intended Sketch:

const int IRval = 2; //Pin2 as IR sense  pin.
const int LEDpin =  13; //Pin13 as Output pin.
int Time = 5000;        // time delay.

void setup() {
  pinMode(LEDpin, OUTPUT);
  pinMode(IRval, INPUT);
}

void loop() {
  if (digitalRead(IRval) == LOW) {
    digitalWrite(LEDpin, HIGH);
    delay(Time);
    digitalWrite(LEDpin, LOW);
  }
  else {
    digitalWrite(LEDpin, LOW);
  }
}

How does your "intended" sketch not meet your expectations? Have you tested it?

Yes please.
so far as there is an obstacle pin 13 stays high, but i want it (pin 13) to low after 5 seconds even if the obstacle is still present.
thanks

Hint: how would you know how long ago some event has happened in the past, if you recorded the time then?

Because, you are just asking it to act differently, depending on whether something happened more than, or less than, 5 seconds ago.

Did you try to Google, "Arduino monostable multivibrator"?

Also, do you want your monostable multivibrator to be a re-triggerable monostable multivibrator? :slight_smile:

1 Like

The pin DOES goes LOW, but if the object is still present, the pin is almost instantaneously set HIGH again. Add another delay() to see the effect.

  if (digitalRead(IRval) == LOW) {
    digitalWrite(LEDpin, HIGH);
    delay(Time);
    digitalWrite(LEDpin, LOW);
    delay(1000);
  }

You need to more clearly define what you want the Arduino program to do.

1 Like

I moved your topic to an appropriate forum category @kofii .

In the future, please take some time to pick the forum category that best suits the subject of your topic. There is an "About the _____ category" topic at the top of each category that explains its purpose.

This is an important part of responsible forum usage, as explained in the "How to get the best out of this forum" guide. The guide contains a lot of other useful information. Please read it.

Thanks in advance for your cooperation.

1 Like

Hello kofii

Welcome to the worldbest Arduino forum ever.

Post a time diagram showing all the transition states and their dependencies.

Have a nice day and enjoy coding in C++.

2 Likes

You could use a simple finite state machine model. The states could be :

  1. waiting_for_detection
  2. detected_led_on
  3. detected_led_off
  4. no_detection_wait.

The device is normally in state 1). On triggering the IR detector the state changes to 2) and the led is switched on. After 5 seconds in state 2) the led is switched off and the state changes to 3). If in state 3) the IR detector is no longer triggered the state changes to 4). If in state 4) there is a re-trigger, the state changes back to 3). If, however, the state is 4) for more than 1 second then the state changes to 1).

1 Like

That would be a possibility:

#include <Arduino.h>

//////////////////////////////////////////////////
// Object definitions
//////////////////////////////////////////////////
class Timer {
  public:
    void start() {
      timeStamp = millis();
    }
    bool operator()(const unsigned long duration) const {
      return (millis() - timeStamp >= duration) ? true : false;
    }

  private:
    unsigned long timeStamp {0};
};

class DurationSwitch {
  public:
    template <size_t MAX>
    DurationSwitch(const unsigned long (&stArray)[MAX]) : switchingTimes {stArray}, maxIndex {MAX - 1} {}
    byte next() {
      index = (index < maxIndex) ? index + 1 : 0;
      return index;
    }
    unsigned long signalDuration() const {
      return switchingTimes[index];
    }

  private:
    const unsigned long *switchingTimes;
    byte maxIndex;
    byte index {0};
};

enum class LedSwitchState : uint8_t { off = 0, start, on, locked };

//////////////////////////////////////////////////
// Global constants
//////////////////////////////////////////////////
constexpr byte IR_PIN {2};
constexpr byte LED_SWITCH_PIN {13};

//////////////////////////////////////////////////
// Global variables and instances
//////////////////////////////////////////////////

// LED light duration 5 seconds and 1 second lock against switching on again. 
const unsigned long msTimes[] {5000, 1000};  
DurationSwitch swLed(msTimes);
LedSwitchState ledState {LedSwitchState::off};
Timer timer;

//////////////////////////////////////////////////
// Main Program
//////////////////////////////////////////////////

void checkDetection(DurationSwitch &ds, Timer &t, LedSwitchState& lss) {
  switch (lss) {
    case LedSwitchState::start:
      t.start();
      lss = LedSwitchState::on;
      digitalWrite(LED_SWITCH_PIN, HIGH);
      Serial.print(F("LED lights up for: "));
      Serial.print(ds.signalDuration());
      Serial.println(F(" ms"));
      break;
    case LedSwitchState::on:
      if (t(ds.signalDuration())) {
        digitalWrite(LED_SWITCH_PIN, LOW);
        lss = LedSwitchState::locked;
        ds.next();
        Serial.print(F("LED is locked: "));
        Serial.print(ds.signalDuration());
        Serial.println(F(" ms"));
        t.start();
      }
      break;
    case LedSwitchState::locked:
      if (t(swLed.signalDuration())) {
        lss = LedSwitchState::off;
        ds.next();
        Serial.println(F("Ready"));
      }
      break;
    default: break;
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(IR_PIN, INPUT);   // IR signal simulated by a button
  pinMode(LED_SWITCH_PIN, OUTPUT);
}

void loop() {
  static bool isReleased {true};
  static byte irVal {HIGH};

  irVal = digitalRead(IR_PIN);
  if (!irVal && ledState == LedSwitchState::off) { 
    if (isReleased) { ledState = LedSwitchState::start; }
    isReleased = false;
  } else if (irVal) {
    delay(20);  // Debounce;
    if (ledState == LedSwitchState::off)  { isReleased = true; }
  }
  checkDetection(swLed, timer, ledState);
}

your applciation is pretty typical. So I decided to write a demo-code for it and to add it to my state-machine tutorial.

In the WOKSim the PIR-Sensor has a Ontime of 15 seconds.
(Can be adjusted in the JSON-tab)
to demonstrate led goes off after 5 seconds code keeps on waiting until PIR is low
and then waits an additional second.

const byte IR_Pin =  2; //Pin2 as IR sense  pin.
const byte LEDpin = 13; //Pin13 as Output pin.
const byte heartBeatLED_Pin = 12;

enum myStates {
  waiting_for_detection,
  detected_led_on,
  detected_led_off,
  no_detection_wait
} myStateVar;

unsigned long myTimer = 0;       // Timer-variables MUST be of type unsigned long
const unsigned long LED_OnTime = 5000;
const unsigned long noObstacleWaitTime = 1000;

void setup() {
  Serial.begin(115200);
  Serial.println( F("Setup-Start") );
  PrintFileNameDateTime();

  pinMode(IR_Pin, INPUT);
  pinMode(LEDpin, OUTPUT);
  pinMode(heartBeatLED_Pin, OUTPUT);

  myStateVar = waiting_for_detection;
}


void loop() {
  BlinkHeartBeatLED(heartBeatLED_Pin, 250);

  myMonoStableStateMachine();

}


void myMonoStableStateMachine() {

  switch (myStateVar) {

    case waiting_for_detection:
      if (digitalRead(IR_Pin) == HIGH ) {
        Serial.println( F("object detected switching on LED") );
        myTimer = millis(); // store snapshot of time
        digitalWrite(LEDpin, HIGH);
        myStateVar = detected_led_on;
      }
      break; // IMMIDIATELY jump down to END-OF-SWITCH


    case detected_led_on:
      Print_a_dot(500);
      if ( TimePeriodIsOver(myTimer, LED_OnTime) ) {
        Serial.println( F("LED ON-time over switching LED off") );
        digitalWrite(LEDpin, LOW);
        myStateVar = detected_led_off;
      }
      break; // IMMIDIATELY jump down to END-OF-SWITCH


    case detected_led_off:
      Print_a_dot(500);
      if (digitalRead(IR_Pin) == LOW ) {
        Serial.println( F("No object start ignore-waiting") );
        myTimer = millis();
        myStateVar = no_detection_wait;
      }
      break; // IMMIDIATELY jump down to END-OF-SWITCH


    case no_detection_wait:
      Print_a_dot(500);
      if ( TimePeriodIsOver(myTimer, noObstacleWaitTime) ) {
        Serial.println( F("ignore-waiting is over start wait for detection") );
        Serial.println();
        Serial.println();
        myStateVar = waiting_for_detection;
      }
      break; // IMMIDIATELY jump down to END-OF-SWITCH

  } // END-OF-SWITCH
}

void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("  compiled ") );
  Serial.print( F(__DATE__) );
  Serial.print( F(" ") );
  Serial.println( F(__TIME__) );
}


// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}

void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);

  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}

void Print_a_dot(unsigned long interval) {
  static unsigned long myWaitTimer;

  if ( TimePeriodIsOver (myWaitTimer,interval) ) {
    Serial.print(".");
  }
}

best regards Stefan

P.S. Some time ago I coded a Monoflop Demo-code

2 Likes

Hello kofii

Consider this small straight foward coded monostable multivibrator.

//https://forum.arduino.cc/t/make-an-output-pin-behave-like-a-monostable-multivibrator/1155428
//https://europe1.discourse-cdn.com/arduino/original/4X/7/e/0/7e0ee1e51f1df32e30893550c85f0dd33244fb0e.jpeg
#define ProjectName "Make an output pin behave like a monostable multivibrator"
// make variables
constexpr uint8_t ButtonPin {A0};
constexpr uint8_t LedPin {9};
constexpr uint32_t IntervalMillis = 5000;
uint32_t previousMillis;
uint8_t controlTimer = LOW;
// make application support
void heartBeat(int LedPin, uint32_t currentMillis)
{
  static bool setUp = false;
  if (!setUp) pinMode (LedPin, OUTPUT), setUp = !setUp;
  digitalWrite(LedPin, (currentMillis / 500) % 2);
}
// make application
void setup()
{
  Serial.begin(115200);
  Serial.println(ProjectName);
  pinMode(ButtonPin, INPUT_PULLUP);
  pinMode(LedPin, OUTPUT);
  digitalWrite(LedPin, HIGH);
  delay(1000);
  digitalWrite(LedPin, LOW);
}
void loop()
{
  uint32_t currentMillis = millis();
  heartBeat(LED_BUILTIN, currentMillis);
  if ((digitalRead(ButtonPin)?LOW:HIGH)==HIGH and controlTimer==LOW)
  {
    previousMillis=currentMillis;
    controlTimer=HIGH;
    digitalWrite(LedPin, HIGH);
  }
 if (currentMillis - previousMillis >= IntervalMillis and controlTimer) 
 {
  controlTimer=LOW;
  digitalWrite(LedPin, LOW);
 }
}

Have a nice day and enjoy coding in C++.

This is the state model as far as I have interpreted the OP's requirements and should also match the description in post #8. On quick inspection, not all the solutions presented so far have respected the "1 second" condition following the switching off of the LED.

There are lots of such state machine templates, however, sometimes the compexity of the chosen state machine engine can obscure the basic simplicity of the approach, at least when trying to convince a new user of the benefits of this type of solution.

1 Like

Thanks but still it does not meet the requirement.

Thanks
I will copy and paste, then try it out and see.
the code looks complicated to me as a newbie.
Your effort is much appreciated.

1 Like

The questioner has to do something to the programme, otherwise it would be far too simple. :nerd_face:

Ok, let´s go.

This sketch realizes the "1 second" afterrun condition as requested.

//https://forum.arduino.cc/t/make-an-output-pin-behave-like-a-monostable-multivibrator/1155428
//https://europe1.discourse-cdn.com/arduino/original/4X/7/e/0/7e0ee1e51f1df32e30893550c85f0dd33244fb0e.jpeg
#define ProjectName "Make an output pin behave like a monostable multivibrator"
// make names
enum RunAfterRun {Run, AfterRun};
// make variables
constexpr uint8_t ButtonPin {A0};
constexpr uint8_t LedPin {9};
constexpr uint32_t IntervalMillis[] {5000, 1000};
uint32_t previousMillis;
uint8_t controlTimer = LOW;
uint8_t runAfterRun= Run;
// make application support
void heartBeat(int LedPin, uint32_t currentMillis)
{
  static bool setUp = false;
  if (!setUp) pinMode (LedPin, OUTPUT), setUp = !setUp;
  digitalWrite(LedPin, (currentMillis / 500) % 2);
}
// make application
void setup()
{
  Serial.begin(115200);
  Serial.println(ProjectName);
  pinMode(ButtonPin, INPUT_PULLUP);
  pinMode(LedPin, OUTPUT);
  digitalWrite(LedPin, HIGH);
  delay(1000);
  digitalWrite(LedPin, LOW);
}
void loop()
{
  uint32_t currentMillis = millis();
  heartBeat(LED_BUILTIN, currentMillis);
  if ((digitalRead(ButtonPin) ? LOW : HIGH) == HIGH and controlTimer == LOW)
  {
    previousMillis = currentMillis;
    controlTimer = HIGH;
    digitalWrite(LedPin, HIGH);
  }
  if (currentMillis - previousMillis >= IntervalMillis[runAfterRun] and controlTimer)
  {
    if (runAfterRun == Run)
    {
      digitalWrite(LedPin, LOW);
      previousMillis=currentMillis;
      runAfterRun = AfterRun;
    }
    else
    {
      controlTimer = LOW;
      runAfterRun = Run;
    }
  }
}

Have a nice day and enjoy coding in C++.

The code I posted was intended merely to point out the error in your thinking.

2 Likes

This code

  • uses the small onboard-LED the same way as your original code
  • meet your requirements
  • has a lot of comments that explain how the code works
  • does print to the serial monitor how the code proceeds

Just put a real simulated LED and resistor on pin 13.

I speak for old people who cannot see the tiny simulated LED. Is it even working?

How long after the LED at pin 13 goes off before we can trigger it on again with motion?

a7

this is here

one of the rare cases where posting a picture explains more than a code-section

WOKWI serial output

Setup-Start
Code running comes from file 
/tmp/build-9RVG5H/sketch/sketch.ino
  compiled Aug  6 2023 12:56:37
object detected switching on LED
...........LED ON-time over switching LED off
...................No object start ignore-waiting
...ignore-waiting is over start wait for detection

best regards Stefan