Graceful Shutdown of Pi in Vehicle using Arduino pro Mini

I'm trying to use my Arduino pro mini to gracefully shutdown my raspberry pi when the ignition trigger has gone low. Then remove power from the pi via solid state relay. Once the ignition is turned on, the pi power will be returned. At the same time while power is on, I'd like for it the touch of my display to be switched to the USB touch controller of my Pi.

I'm just wondering if I'm doing this state machine efficiently and if I'm missing anything. I read the pinned thread on millis() to come up with the below code.

Thank you!

/*
Timer circuit to gracefully shutdown the Pi and switch touch screen inputs.

*/

// constants won't change. They're used here to set pin numbers:
const int ignitionTrigger = 2;     // the number of the ignition trigger pin
const int touchTrigger = 3;       // the number of the touch trigger pin
const int piPowerRelay = 13;      // the number of the Solid State Relay
const int shutdownPi = 11;        //pin to RPi GPIO to tell it to shutdown gracefully
const int touchRelay1 = 4;         //pin to 4 channel touch relay
const int touchRelay2 = 5;
const int touchRelay3 = 6;
const int touchRelay4 = 7;


// variables will change:
bool ignitionLowCheck = false;         // variable for reading the ignition state
bool touchState = false;           // variable for reading touch relay state
bool shutdownReady = false;        // variable for reading pi graceful shutdown state
bool shutdownState =  false;

unsigned long ignitionMillis; // when button was released
unsigned long shutdownSentAt; // when shutdown command was issued
unsigned long powerRemovedAt; //when power was cut off from the Pi
unsigned long shutdownDelay = 1200000; // wait to send shutdown command
unsigned long powerRemoveDelay = 150000; // shut off Pi power after this time Give enough time for Pi to fully shutdown



void setup() {
  // initialize the Pi Power Relay pin as an output:
  pinMode(piPowerRelay, OUTPUT);
  // initialize digital shutdown command pin to tell the pi to shutdown before power is removed.
  pinMode(shutdownPi, OUTPUT);
  // initialize the touch relay pins as outputs
  pinMode(touchRelay1, OUTPUT);
  pinMode(touchRelay2, OUTPUT);
  pinMode(touchRelay3, OUTPUT);
  pinMode(touchRelay4, OUTPUT);
  // initialize the trigger pins as an input:
  pinMode(ignitionTrigger, INPUT);
  pinMode(touchTrigger, INPUT);
}

void loop() {
  // get the time at the start of this loop
  unsigned long currentMillis = millis(); 
  
  // check if the ignition is off. If it is, the ignitionTrigger is LOW:
  while (digitalRead(ignitionTrigger) == HIGH){
    unsigned long currentMillis = millis(); 
    // car is running
    digitalwrite(piPowerRelay, LOW); // turn pi on since ignition is on
    ignitionLowCheck = false; // be ready to check when ignition is low again. 
    // check if the mode switch has activated
    if (touchTrigger == HIGH) {
    // switch from radio touch source to RPi touch source
    digitalWrite(touchRelay1, HIGH);
    digitalWrite(touchRelay2, HIGH);
    digitalWrite(touchRelay3, HIGH);
    digitalWrite(touchRelay4, HIGH);
    } else {
    digitalWrite(touchRelay1, LOW);
    digitalWrite(touchRelay2, LOW);
    digitalWrite(touchRelay3, LOW);
    digitalWrite(touchRelay4, LOW);  
    }
  }
  
  if (!ignitionCheck) {
    if (digitalRead(ignitionTrigger) == LOW) {
      ignitionMillis = currentMillis;
      shutdownReady = true; //we're ready to shutdown
      ignitionLowCheck = true; //we've checked that the ignition is off and won't recheck until ignition is sent high.
  } }
  
  if (shutdownReady) {
    gracefulShutdown();
  
  }  
  
  if (shutdownState) {
    removePipower();
  }
  
} 

  
  


void gracefulShutdown() {
    // wait 20 minutes and gracefully shutdown pi:
    if ((unsigned long)(currentMillis - ignitionMillis) >= shutdownDelay) {
    digitalWrite(shutdownPi, HIGH);
    shutdownState = true;  
    shutdownSentAt = currentMillis;
    shutdownReady = false; // don't return until ignition is low again
    // wait 2 minutes after shutdown command send and remove power from pi

void removePiPower() {
    if ((unsigned long)(currentMillis - shutdownSentAt) >= powerRemoveDelay) {
      digitalwrite(piPowerRelay, LOW);
      shutdownState = false;
  }
}

ethicaldesign: I'm just wondering if I'm doing this state machine efficiently and if I'm missing anything.

What happens when you try it?

The Arduino system is great for learning-by-doing

...R

  pinMode(ignitionTrigger, INPUT);
  pinMode(touchTrigger, INPUT);

How are these "triggers" wired? Do they hold the pins HIGH or LOW ALL THE TIME? Or, when the ignition is turned off, does the pin float?

  unsigned long currentMillis = millis();

  // check if the ignition is off. If it is, the ignitionTrigger is LOW:
  while (digitalRead(ignitionTrigger) == HIGH){
    unsigned long currentMillis = millis();

Having two local variables with the same name is a dumb idea.

    digitalwrite(piPowerRelay, LOW); // turn pi on since ignition is on

Why do you need to keep turning the pin on?

const int touchTrigger = 3; // the number of the touch trigger pin

    if (touchTrigger == HIGH) {

Three will NEVER equal 1, so you might as well delete the entire if statement and body, and make the else part unconditional.

Your code won't even compile, so you wasted your time, and ours, posting it.

Robin2:
What happens when you try it?

The Arduino system is great for learning-by-doing

…R

I’m about to have all the parts in to do just that. I’m trying to avoid having to take my car dash apart to make code or wiring changes as often. Spend more time on design and less on troubleshooting.

PaulS:  pinMode(ignitionTrigger, INPUT);  pinMode(touchTrigger, INPUT);

How are these "triggers" wired? Do they hold the pins HIGH or LOW ALL THE TIME? Or, when the ignition is turned off, does the pin float?

The ignition trigger is held HIGH as long as the ignition is on. The Touch trigger is a momentary button.

PaulS: ```  unsigned long currentMillis = millis();

 // check if the ignition is off. If it is, the ignitionTrigger is LOW:  while (digitalRead(ignitionTrigger) == HIGH){    unsigned long currentMillis = millis();



Having two local variables with the same name is a dumb idea.

My goal of putting the currentMillis = millis in both the main loop and the while loop is that I wanted time to still be kept while in the while loop/ignition is On.

PaulS:    digitalwrite(piPowerRelay, LOW); // turn pi on since ignition is on

Why do you need to keep turning the pin on?

Good point! I can set an if statement here to check if the pi has been turned on!

PaulS: const int touchTrigger = 3; // the number of the touch trigger pin

    if (touchTrigger == HIGH) {

Three will NEVER equal 1, so you might as well delete the entire if statement and body, and make the else part unconditional.

I forgot the digitalRead function around the touchTrigger pin.

PaulS: Your code won't even compile, so you wasted your time, and ours, posting it.

I never said it was ready to compile? Isn't the point of posting a programming question to help guide me to a compilable, logical, and efficient state? The tone you set in your response is more destructive than constructive. (calling me dumb). But I guess thanks. smh at trying to get help on this forum.

calling me dumb

There is a difference between calling you dumb and saying that you had a dumb idea, stupid. 8)

If you want to change the value in currentMillis in the while loop, do that. Don't create another variable of the same name.

 unsigned long currentMillis = millis();

  // check if the ignition is off. If it is, the ignitionTrigger is LOW:
  while (digitalRead(ignitionTrigger) == HIGH){
    currentMillis = millis();

I never said it was ready to compile?

You didn't tell us that it wasn't going to compile, either. If you KNOW that it won't compile, why not make the trivial change so that it will?