Disable output at end of code using a timer

I have been working on this code for a while, hoping whatever I am missing is simple. Basically what I want to do is when I turn on my car, it turns on an output unless the car is moving of which it then disables the output. I pretty much got this far without much issue but now I want to add to the code and when the car is turned off, the output is enabled (High) for 20min then goes off (Low) and stays there until the car turns on again. See code below. The last part in bold I know is wrong but I can't figure out what should be here to achieve the last step. Thanks

#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
int switchState = 0;

Adafruit_MPU6050 mpu;

void setup(void) {
  Serial.begin(115200);

  // Try to initialize!
  if (!mpu.begin()) {
    Serial.println("Failed to find MPU6050 chip");
    while (1) {
      delay(10);
    }
  }
  Serial.println("MPU6050 Found!");

  // set accelerometer range to +-8G
  mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
  
  // set gyro range to +- 500 deg/s
  mpu.setGyroRange(MPU6050_RANGE_500_DEG);

  // set filter bandwidth to 21 Hz
  mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);

  delay(100);

  pinMode(13, OUTPUT);
  pinMode(2, INPUT);

}

void loop() {
  /* Get new sensor events with the readings */
  sensors_event_t a, g, temp;
  mpu.getEvent(&a, &g, &temp);

  /* Print out the values */
  Serial.print("Acceleration X: ");
  Serial.print(a.acceleration.x);
  Serial.print(", Y: ");
  Serial.print(a.acceleration.y);
  Serial.print(", Z: ");
  Serial.print(a.acceleration.z);
  Serial.println(" m/s^2");

  Serial.print("Rotation X: ");
  Serial.print(g.gyro.x);
  Serial.print(", Y: ");
  Serial.print(g.gyro.y);
  Serial.print(", Z: ");
  Serial.print(g.gyro.z);
  Serial.println(" rad/s");

  Serial.print("Temperature: ");
  Serial.print(temp.temperature);
  Serial.println(" degC");

  Serial.println("");
  delay(500);

switchState = digitalRead(2);
if (switchState == LOW){    //Initiates program when car ignition is on
  
} else if(abs(a.acceleration.x) + abs(a.acceleration.y) + abs(a.acceleration.z) > 13.0){
    digitalWrite(13, LOW); //If car is moving, disables Pin 13 turning off light
    delay(1000);
  }else{
    digitalWrite(13, HIGH); //If car is not moving, enables Pin 13 turning on light
  }
**if (switchState == HIGH){ //My attempt to to turn off Pin 13 once ignition is off**
  }
  digitalWrite(13, LOW);
}


I should say "the last part with ** is wrong" not bold. I thought it was going to bold the entry.

If you're going to do something involving twenty minutes, you will need to use millis to record when you last saw that the ignition was on. Then you can check if it's more than that long ago and turn off.

How is the Arduino is powered, and how is pin 2 determining the car is on or off?

Two of your if/else blocks are empty.

This

  digitalWrite(13, LOW);

is just at the bottom of loop(), where it gets executed every time loop() does. Loop.

So perhaps if you posted the best version of something that worked well, but does not yet have the additional feature you are trying to add, it would be easier to see what you need to do.

Also this

    digitalWrite(13, LOW); //If car is moving, disables Pin 13 turning off

is a bit confusing. What do HIGH and LOW mean when on pin 13? I know lighting the build-in LED, but what is the interpretation?

a7

The Adruino will be powered off of the battery, it doesn't put hardly any current so it can just stay live 24/7. Pin 2 would be connected to a source that is only live when the car is in accessory mode (turned on or running). I haven't diagramed out yet if this will be a relay or what but that is minor at this point.

Yeah, I know, here is the code with this removed.

#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
int switchState = 0;

Adafruit_MPU6050 mpu;

void setup(void) {
  Serial.begin(115200);

  // Try to initialize!
  if (!mpu.begin()) {
    Serial.println("Failed to find MPU6050 chip");
    while (1) {
      delay(10);
    }
  }
  Serial.println("MPU6050 Found!");

  // set accelerometer range to +-8G
  mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
  
  // set gyro range to +- 500 deg/s
  mpu.setGyroRange(MPU6050_RANGE_500_DEG);

  // set filter bandwidth to 21 Hz
  mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);

  delay(100);

  pinMode(13, OUTPUT);
  pinMode(2, INPUT);

}

void loop() {
  /* Get new sensor events with the readings */
  sensors_event_t a, g, temp;
  mpu.getEvent(&a, &g, &temp);

  /* Print out the values */
  Serial.print("Acceleration X: ");
  Serial.print(a.acceleration.x);
  Serial.print(", Y: ");
  Serial.print(a.acceleration.y);
  Serial.print(", Z: ");
  Serial.print(a.acceleration.z);
  Serial.println(" m/s^2");

  Serial.print("Rotation X: ");
  Serial.print(g.gyro.x);
  Serial.print(", Y: ");
  Serial.print(g.gyro.y);
  Serial.print(", Z: ");
  Serial.print(g.gyro.z);
  Serial.println(" rad/s");

  Serial.print("Temperature: ");
  Serial.print(temp.temperature);
  Serial.println(" degC");

  Serial.println("");
  delay(500);

switchState = digitalRead(2);
if (switchState == LOW){    //Initiates program when car ignition is on
  
} else if(abs(a.acceleration.x) + abs(a.acceleration.y) + abs(a.acceleration.z) > 13.0){
    digitalWrite(13, LOW); //If car is moving, disables Pin 13 turning off light
    delay(1000);
  }else{
    digitalWrite(13, HIGH); //If car is not moving, enables Pin 13 turning on light
  }
}

HIGH = Light On
LOW = Light Off

And something added. :expressionless:

THX, all clear now.

Just to be sure:

  • Car not moving, turns ON -> light goes on.

  • If /when car is moving, light turns off.

  • If car stops moving? Light goes back on? or does it stay off once car has moved?

  • the light comes (or stays) on for 20 minutes after the car turns OFF.

Sry if I could glean this from rereading and reading again your description, but if you can confirm this (or fix it in the same style) it will help. Me anyways.

a7

See above

Thanks I am looking at the millis feature now, trying to figure out how that works.

Updated code. Added the last line and a delay. I thought I could just add the delay here for keeping the light on for 10-20min but using delay in the code is screwing with other parts of it and hangs the code which is a problem because if the switchstate changes while this is processing the delay then it is ignored....so I may use an external delay unless I can figure out how to code for this. Thanks for everyone's help thus far.

#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
int switchState = 0;

Adafruit_MPU6050 mpu;

void setup(void) {
  Serial.begin(115200);

  // Try to initialize!
  if (!mpu.begin()) {
    Serial.println("Failed to find MPU6050 chip");
    while (1) {
      delay(10);
    }
  }
  Serial.println("MPU6050 Found!");

  // set accelerometer range to +-8G
  mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
  
  // set gyro range to +- 500 deg/s
  mpu.setGyroRange(MPU6050_RANGE_500_DEG);

  // set filter bandwidth to 21 Hz
  mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);

  delay(100);

  pinMode(13, OUTPUT);
  pinMode(2, INPUT);

}

void loop() {
  /* Get new sensor events with the readings */
  sensors_event_t a, g, temp;
  mpu.getEvent(&a, &g, &temp);

  /* Print out the values */
  Serial.print("Acceleration X: ");
  Serial.print(a.acceleration.x);
  Serial.print(", Y: ");
  Serial.print(a.acceleration.y);
  Serial.print(", Z: ");
  Serial.print(a.acceleration.z);
  Serial.println(" m/s^2");

  Serial.print("Rotation X: ");
  Serial.print(g.gyro.x);
  Serial.print(", Y: ");
  Serial.print(g.gyro.y);
  Serial.print(", Z: ");
  Serial.print(g.gyro.z);
  Serial.println(" rad/s");

  Serial.print("Temperature: ");
  Serial.print(temp.temperature);
  Serial.println(" degC");

  Serial.println("");
  delay(500);

switchState = digitalRead(2);
if (switchState == LOW){}    //Initiates program when car ignition is on
   else if(abs(a.acceleration.x) + abs(a.acceleration.y) + abs(a.acceleration.z) > 13.0)
   {
    digitalWrite(13, LOW); //If car is moving, disables Pin 13 turning off light
    delay(1000);
   }
   else{digitalWrite(13, HIGH);  //If car is not moving, enables Pin 13 turning on light
   }

if (switchState == HIGH){} //If car ignition is off
   else 
   { 
  delay(100);
  digitalWrite(13, LOW);
   }
}
void loop() {
  /* Get new sensor events with the readings */
  sensors_event_t a, g, temp;
  mpu.getEvent(&a, &g, &temp);

  Serial.println("Print out the values removed for now");
  delay(500);

  switchState = digitalRead(2);

  if (switchState == HIGH)   //If car ignition is on
  {
    if (abs(a.acceleration.x) + abs(a.acceleration.y) + abs(a.acceleration.z) > 13.0)
    {
      digitalWrite(13, LOW);  //If car is moving turn off light
      delay(1000);
    }
    else
    {
      digitalWrite(13, HIGH);  // If car is not moving turn on light
    }
  }

  if (switchState == LOW)      // If car ignition is off
  {
    delay(100);
    digitalWrite(13, LOW);
  }
}

I find it easier to read when you aren't including {} empty statements and therefor reverse logic. The above is identical in function to your latest code.

I assume the delay(100); in the "if ignition is off" section is where you tried a delay of 20 minutes.

Your analysis of why this won't work satisfactorily is correct.

HTH

a7

Thanks

It's too late here for me to help more right now. Soon enough ppl are going to be telling you about "state machines", also know as "finite-state machines" or FSMs.

If you want to get a head start, google those terms and poke around a bit. See if you get some inspiration. Don't be intimidated, it is hard only until it is easy. You can look at them in general, or google a bit harder and find dozens of Arduino examples.

I encourage you to stay the course with code. Adding an external delay device might be more in your comfort zone right now, but if you are planning on going any further with programming, this is an ideal and simple case to be solved with an FSM.

Part of it will be getting very specific about the way your system is to respond to all the different conditions that may obtain. Which was why I asked at least one question I could not discern from going over your prose, and why I asked you to verify my understanding of your requirements. Long english descriptions can be, or seem, ambiguous or underspecified. I did, trust me, "see above".

a7

1 Like

Maybe below pseudo code can get you on the right track

void loop()
{
  // variable to remember the last time that a movement was detected
  static uint32_t lastMovementTime;

  // if a movement was detected
  if(movementCondition == true)
  {
    // 'reset' the timer
    lastMovementTime = millis();
  }

  // if no movement detected for someDuration
  if(millis() - lastMovementTime > someDuration)
  {
    // do what needs to be done
  }
}
1 Like

I thougt about a finite state machine

compiles, but untested

/*
  https://forum.arduino.cc/t/disable-output-at-end-of-code-using-a-timer/900440

  Basically what I want to do is when I turn on my car,
  it turns on an output unless the car is moving of which it then disables the output.

  I want to add to the code and when the car is turned off, the output is enabled (High) for 20min then goes off (Low)
  and stays there until the car turns on again.

  example for a finite state machine
*/

enum class State {OFF,        // car is closed for longer than n minutes
                  IGNITION,   // ignition on
                  MOVING,     // car moves
                  STOPPED     // car was stopped, timer is running
                 } state;

int switchState = 0;
bool isCarMoving = false;
const byte outputPin = 13;
const byte ignitionPin = A0;
const byte sensorPin = A1; // others might not have a sensor

const uint32_t interval = 1 * 60 * 1000UL; // durance of the nonblocking delay - should be finally 20 * 60 * 1000UL


void setup(void) {
  Serial.begin(115200);
  pinMode(outputPin, OUTPUT);
  pinMode(ignitionPin, INPUT);  // HIGH active
  pinMode(sensorPin, INPUT);    // HIGH active
}

void loop() {
  switchState = digitalRead(ignitionPin);
  isCarMoving = digitalRead(sensorPin);  // replace this with reading MPU6050

  static State previousState = State::OFF;
  static uint32_t previousMillis = 0;
  switch (state)
  {
    case State::OFF :
      // e.g. blink a security LED here ...
      // otherwise - just do nothing
      break;

    case State::IGNITION :
      if (isCarMoving)
      {
        Serial.println(F("unless the car is moving of which it then disables the output."));
        digitalWrite(outputPin, LOW);
        state = State::MOVING;
      }
      if (digitalRead(outputPin) == LOW)
      {
        Serial.println(F("I want to do is when I turn on my car, it turns on an output"));
        digitalWrite(outputPin, HIGH);
      }

      break;

    case State::MOVING :
      if (switchState == LOW)
      {
        Serial.println(F("when the car is turned off, the output is enabled (High)"));
        // start timer
        previousMillis = millis();
        digitalWrite(outputPin, HIGH);
        state = State::STOPPED;
      }
      break;

    case State::STOPPED :
      if (millis() - previousMillis > interval)
      {
        Serial.println(F("for 20min then goes off (Low) and stays there until the car turns on again"));
        digitalWrite(outputPin, LOW);
        state = State::OFF;
      }
      break;
  }
}

This should get you close... In order to speed up loop(), I also removed the delay(500) and only spit out the data every second. You can also adjust the delayTime for testing purposes, unless you like to hang around for 20 minutes :slight_smile:

#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>

const int ignitionPin = 2;
const int outputPin = 13;
const float accTriggerLevel = 13.0;
const unsigned long delayTime = 1000UL * 60 * 20;   // twenty minutes

unsigned long startTime;

enum { CAR_OFF, CAR_IDLE, CAR_MOVING, CAR_STOPPED, CAR_DELAY_OFF };
int state;

Adafruit_MPU6050 mpu;

void setup(void) {
  Serial.begin(115200);

  // Try to initialize!
  if (!mpu.begin()) {
    Serial.println("Failed to find MPU6050 chip");
    while (1) {
      delay(10);
    }
  }
  Serial.println("MPU6050 Found!");

  // set accelerometer range to +-8G
  mpu.setAccelerometerRange(MPU6050_RANGE_8_G);

  // set gyro range to +- 500 deg/s
  mpu.setGyroRange(MPU6050_RANGE_500_DEG);

  // set filter bandwidth to 21 Hz
  mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);

  delay(100);

  pinMode(outputPin, OUTPUT);
  pinMode(ignitionPin, INPUT);

}

void loop() {
  /* Get new sensor events with the readings */
  sensors_event_t a, g, temp;
  mpu.getEvent(&a, &g, &temp);

  static unsigned long lastReportTime;

  // only report every second
  if ( millis() - lastReportTime >= 1000 ) {
    lastReportTime = millis();
    
    /* Print out the values */
    Serial.print("Acceleration X: ");
    Serial.print(a.acceleration.x);
    Serial.print(", Y: ");
    Serial.print(a.acceleration.y);
    Serial.print(", Z: ");
    Serial.print(a.acceleration.z);
    Serial.println(" m/s^2");
  
    Serial.print("Rotation X: ");
    Serial.print(g.gyro.x);
    Serial.print(", Y: ");
    Serial.print(g.gyro.y);
    Serial.print(", Z: ");
    Serial.print(g.gyro.z);
    Serial.println(" rad/s");
  
    Serial.print("Temperature: ");
    Serial.print(temp.temperature);
    Serial.println(" degC");
  
    Serial.println("");
  }
  
  float totalAcc = abs(a.acceleration.x) + abs(a.acceleration.y) + abs(a.acceleration.z);
  int ignitionState = digitalRead(ignitionPin);

  switch ( state ) {
    case CAR_OFF:
      // car is off, check to see if it is on and react
      digitalWrite(outputPin, LOW);
      if ( ignitionState == HIGH ) {
        // car is on
        digitalWrite(outputPin, HIGH);
        state = CAR_IDLE;
      }
      break;

    case CAR_IDLE:
      // car is ON but not moving
      if (totalAcc > accTriggerLevel) {
        state = CAR_MOVING;
      }
      if (ignitionState == LOW ) {
        // car turned off
        state = CAR_OFF;
      }
      break;

    case CAR_MOVING:
      digitalWrite(outputPin, LOW);
      if (totalAcc <= accTriggerLevel) {
        // car has stopped
        state = CAR_STOPPED;
      }
      break;

    case CAR_STOPPED:
      if (totalAcc > accTriggerLevel) {
        state = CAR_MOVING;
      }
      if (ignitionState == LOW ) {
        // car turned off
        state = CAR_DELAY_OFF;
        startTime = millis();
        digitalWrite(outputPin, HIGH);
      }
      break;

    case CAR_DELAY_OFF:
      // check timer and, if expired, we are OFF
      if ( millis() - startTime >= delayTime ) {
        state = CAR_OFF;
      }
      if (ignitionState == HIGH) {
        // car turned back on so reset
        state = CAR_OFF;
      }
      break;
  }
}

Remove annoying light goes off before it goes on when you restart the car during CAR_DELAY_OFF…

a7

okay, if you can notice that small of a blink on a corner case...

    case CAR_DELAY_OFF:
      // check timer and, if expired, we are OFF
      if ( millis() - startTime >= delayTime ) {
        state = CAR_OFF;
      }
      if (ignitionState == HIGH) {
        // car turned back on so reset
        state = CAR_IDLE;
      }
      break;

And wokwi.com:

A few minor tweaks, but looking good.

You can click on the accelerometer to simulate motion.

# include <Adafruit_MPU6050.h>
# include <Adafruit_Sensor.h>
# include <Wire.h>

const int ignitionPin = 2;
const int outputPin = 13;
const float accTriggerLevel = 13.0;

// const unsigned long delayTime = 1000UL * 60 * 20;   // twenty minutes
const unsigned long delayTime = 1000UL * 5;   // five seconds

unsigned long startTime;

enum { CAR_OFF, CAR_IDLE, CAR_MOVING, CAR_STOPPED, CAR_DELAY_OFF };
int state;

Adafruit_MPU6050 mpu;

void setup(void) {
  Serial.begin(115200);

  // Try to initialize!
  if (!mpu.begin()) {
    Serial.println("Failed to find MPU6050 chip");
    while (1) {
      delay(10);
    }
  }
  Serial.println("MPU6050 Found!");

  // set accelerometer range to +-8G
  mpu.setAccelerometerRange(MPU6050_RANGE_8_G);

  // set gyro range to +- 500 deg/s
  mpu.setGyroRange(MPU6050_RANGE_500_DEG);

  // set filter bandwidth to 21 Hz
  mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);

  delay(100);

  pinMode(outputPin, OUTPUT);
  pinMode(ignitionPin, INPUT_PULLUP);

}

void loop() {
  /* Get new sensor events with the readings */
  sensors_event_t a, g, temp;
  mpu.getEvent(&a, &g, &temp);

  static unsigned long lastReportTime;

  // only report every second
  if ( millis() - lastReportTime >= 5000 ) {
    lastReportTime = millis();
    
    /* Print out the values 
    Serial.print("Acceleration X: ");
    Serial.print(a.acceleration.x);
    Serial.print(", Y: ");
    Serial.print(a.acceleration.y);
    Serial.print(", Z: ");
    Serial.print(a.acceleration.z);
    Serial.println(" m/s^2");
  
    Serial.print("Rotation X: ");
    Serial.print(g.gyro.x);
    Serial.print(", Y: ");
    Serial.print(g.gyro.y);
    Serial.print(", Z: ");
    Serial.print(g.gyro.z);
    Serial.println(" rad/s");
  
    Serial.print("Temperature: ");
    Serial.print(temp.temperature);
    Serial.println(" degC");
  
    Serial.println("");
    */
  }
  
  float totalAcc = abs(a.acceleration.x) + abs(a.acceleration.y) + abs(a.acceleration.z);
  int ignitionState = digitalRead(ignitionPin);

  switch ( state ) {
  case CAR_OFF:
  // car is off, check to see if it is on and react

    if ( ignitionState == HIGH ) {
    // car is on
      digitalWrite(outputPin, HIGH);
      state = CAR_IDLE;
    }
    else digitalWrite(outputPin, LOW);

  break;
    case CAR_IDLE:
      // car is ON but not moving
      if (totalAcc > accTriggerLevel) {
        state = CAR_MOVING;
      }
      if (ignitionState == LOW ) {
        // car turned off
        state = CAR_DELAY_OFF;
        startTime = millis();
      }
      break;

    case CAR_MOVING:
      digitalWrite(outputPin, LOW);
      if (totalAcc <= accTriggerLevel) {
        // car has stopped
        state = CAR_STOPPED;
      }
      break;

    case CAR_STOPPED:
      if (totalAcc > accTriggerLevel) {
        state = CAR_MOVING;
        Serial.println("car moving again");
      }
      if (ignitionState == LOW ) {
        // car turned off
        state = CAR_DELAY_OFF;
        startTime = millis();
        digitalWrite(outputPin, HIGH);
      }
      else digitalWrite(outputPin, HIGH);

      break;

    case CAR_DELAY_OFF:
      // check timer and, if expired, we are OFF
      if ( millis() - startTime >= delayTime ) {
        state = CAR_OFF;
      }
      if (ignitionState == HIGH) {
        // car turned back on so reset
        state = CAR_OFF;
      }
      break;
  }
}

Casual testing only…

a7