// =================================================================================== // // Raspberry Pi 3 Model B Power Manager: // // This code controls the start-up and shut-down of the attached Raspberry Pi // micro-computer based on the position of the early Corvette C4's (1984 to 1989) // ignition switch. // // The ignition switch is always in one of the three following positions: // // OFF or ACCESSORY or RUN. // // When in OFF position, the ignition switch monitor is LOW. // When in ACCESSORY position, the ignition switch monitor is HIGH. // When in RUN position, the ignition switch monitor is HIGH. // // When the ignition switch enters the ACCESSORY or RUN position, the program // powers-up the the Raspberry. // // When the ignition switch enters the OFF position, the program enters a grace // period wherein the Raspberry Pi remains powered-on in case the driver returns // the ignition switch to the ACCESSORY or RUN position. This allows the ignition // switch to transition from ACCESSORY thru OFF to RUN or vice-versa from RUN thru // OFF to ACCESSORY without triggering a shutdown of the Raspberry Pi. // // If the ignition switch re-enters the ACCESSORY or RUN state before the grace // period expires, no shutdown of the Raspberry Pi is attempted. // // If the grace period expires without the ignition switch returning to the // ACCESSORY or RUN position, an attempt to shutdown the Raspberry Pi is made. // // When a shutdown attemp is made, the program enters a shutdown period wherein the // Raspberry Pi is monitored for a successful soft shutdown. If the Raspberry Pi // signals that a soft shutdown is complete, the Raspberry Pi is then powered-off. // A soft shutdown is always preferred over a hard shutdown. // // If the shutdown period expires, and the Raspberry Pi has not signaled that a soft // shutdown has completed, the Raspberry Pi is powered-off for a hard shutdown. A // hard shutdown should be avoided but if a soft shutdown does not occur, the // Raspberry Pi is powered-off to avoid draining the car battery. // // Compile as: // Board: "Adafruit Trinket 16MHz" // Programmer: "USBTinyISP" // // =================================================================================== // =================================================================================== // class clsInterval { // This class allows an interval period to be set in seconds. When the Elapsed // property is queried, the class returns False if the interval has not elapsed // or it returns True if the interval has elapsed. // // Once instantiated, the Seconds property must be called to set the interval // value in seconds. // // Periodically calling the Elapsed property will return True or False // depending upon whether or not the interval has elapsed. private: unsigned long _Seconds; unsigned long _Millis; public: void Seconds(unsigned long Seconds) { _Seconds = Seconds * 1000; _Millis = millis(); } boolean Elapsed() { unsigned long Now = millis(); if ((unsigned long)(Now - _Millis) >= _Seconds) { return true; } else { return false; } } }; // Define input pins: // const int PIN_I_RASPBERRY_PI_SHUTDOWN_MONITOR = 2; const int PIN_I_IGNITION_SWITCH_MONITOR = 4; // Define output pins: // const int PIN_O_RASPBERRY_PI_SHUTDOWN_REQUEST = 1; const int PIN_O_POWER_RELAY_CONTROL = 0; // Define timeouts (in seconds): // const int GRACE_PERIOD = 10; const int SHUTDOWN_PERIOD = 90; // Instantiate an interval object. // clsInterval objInterval; // =================================================================================== // void setup() { // Set mode of IO pins. // pinMode(PIN_I_IGNITION_SWITCH_MONITOR, INPUT); pinMode(PIN_O_POWER_RELAY_CONTROL, OUTPUT); pinMode(PIN_I_RASPBERRY_PI_SHUTDOWN_MONITOR, INPUT); pinMode(PIN_O_RASPBERRY_PI_SHUTDOWN_REQUEST, OUTPUT); // Set default states of IO pins. // digitalWrite(PIN_I_IGNITION_SWITCH_MONITOR, LOW); digitalWrite(PIN_O_POWER_RELAY_CONTROL, LOW); digitalWrite(PIN_I_RASPBERRY_PI_SHUTDOWN_MONITOR, LOW); digitalWrite(PIN_O_RASPBERRY_PI_SHUTDOWN_REQUEST, LOW); } // =================================================================================== // void loop() { // What position is the ignition switch in? // switch(ReadIgnitionSwitch()) { case HIGH: Handle_IgnitionON(); break; case LOW: Handle_IgnitionOFF(); break; } } // =================================================================================== // void Handle_IgnitionON() { // Is relay already on? // if(digitalRead(PIN_O_POWER_RELAY_CONTROL) == HIGH) return; // Turn relay on. // digitalWrite(PIN_O_POWER_RELAY_CONTROL, HIGH); } // =================================================================================== // void Handle_IgnitionOFF() { // Is relay already off? // if(digitalRead(PIN_O_POWER_RELAY_CONTROL) == LOW) return; // Check if ignition returns to the ACCESSORY or RUN state within // the grace period. // objInterval.Seconds(GRACE_PERIOD); while(true) { // Check if ignition switch goes high. // if (ReadIgnitionSwitch() == HIGH) { return; } // Check if timeout interval has elapsed. // if (objInterval.Elapsed() == true) { break; } } // Set Raspberry Pi shutdown signal. // digitalWrite(PIN_O_RASPBERRY_PI_SHUTDOWN_REQUEST, HIGH); // Loop until Raspberry Pi shuts down or shutdown period elapses. // objInterval.Seconds(SHUTDOWN_PERIOD); while(true) { // Check for Raspberry Pi shutdown completed signal. // if(digitalRead(PIN_I_RASPBERRY_PI_SHUTDOWN_MONITOR) == HIGH) { // Turn power relay off. // digitalWrite(PIN_O_POWER_RELAY_CONTROL, LOW); // Unset Raspberry Pi shutdown signal. // digitalWrite(PIN_O_RASPBERRY_PI_SHUTDOWN_REQUEST, LOW); return; } // Check if timeout interval has elapsed. // if (objInterval.Elapsed() == true) { // Turn power relay off. // digitalWrite(PIN_O_POWER_RELAY_CONTROL, LOW); // Unset Raspberry Pi shutdown signal. // digitalWrite(PIN_O_RASPBERRY_PI_SHUTDOWN_REQUEST, LOW); return; } } } // =================================================================================== // int ReadIgnitionSwitch() { unsigned int IgnitionSwitchState; // What state is the ignition switch in? // IgnitionSwitchState = digitalRead(PIN_I_IGNITION_SWITCH_MONITOR); // Wait 250ms for pin noise to subside. // delay(250); // Is pin still in same state? // if(digitalRead(PIN_I_IGNITION_SWITCH_MONITOR) == IgnitionSwitchState) { // Yes, return the state. return IgnitionSwitchState; } else { // No, return a undefined state. return -1; } }