// ===============================
// Pin Definitions
// ===============================
#define LED_RUN 2
#define LED_IDLE 3
#define LED_FAULT 4
#define STARTER_RELAY1 5
#define ENGINE_RUN 6
#define SENSE 7
#define KEY_RUN 8
#define KEY_START 9
#define EMERG_STOP_BTN 10
// ===============================
// Timing Constants
// ===============================
const unsigned long CRANK_TIME = 5000; // 5 seconds
const unsigned long WAIT_TIME = 10000; // 10 seconds
const unsigned long BLINK_TIME = 500; // 500ms blink
// ===============================
// State Machine
// ===============================
enum State {
IDLE,
CRANKING,
WAIT_FOR_RUN,
RUNNING,
FAULT,
EMERGENCY
};
State state = IDLE;
unsigned long stateStartTime = 0;
unsigned long blinkTimer = 0;
int attemptCount = 0;
bool blinkState = false;
bool emergencyLatched = false;
// ===============================
// Setup
// ===============================
void setup() {
pinMode(LED_RUN, OUTPUT);
pinMode(LED_IDLE, OUTPUT);
pinMode(LED_FAULT, OUTPUT);
pinMode(STARTER_RELAY1, OUTPUT);
pinMode(ENGINE_RUN, OUTPUT);
pinMode(SENSE, INPUT);
pinMode(KEY_RUN, INPUT_PULLUP);
pinMode(KEY_START, INPUT_PULLUP);
pinMode(EMERG_STOP_BTN, INPUT_PULLUP);
resetOutputs();
}
// ===============================
// Main Loop
// ===============================
void loop() {
bool sense = digitalRead(SENSE) == LOW; // Active LOW
bool keyRun = digitalRead(KEY_RUN) == LOW;
bool keyStart = digitalRead(KEY_START) == LOW;
bool emerg = digitalRead(EMERG_STOP_BTN) == LOW;
unsigned long now = millis();
// ============================
// Emergency Stop Latch
// ============================
if (emerg) {
emergencyLatched = true;
state = EMERGENCY;
}
if (emergencyLatched) {
handleEmergency(now);
return;
}
// ============================
// State Machine
// ============================
switch (state) {
case IDLE:
resetOutputs();
digitalWrite(LED_IDLE, HIGH);
attemptCount = 0;
if (keyRun) {
digitalWrite(ENGINE_RUN, HIGH);
if (keyStart) {
state = CRANKING;
stateStartTime = now;
}
}
break;
case CRANKING:
digitalWrite(ENGINE_RUN, HIGH);
digitalWrite(STARTER_RELAY1, HIGH);
if (sense) {
digitalWrite(STARTER_RELAY1, LOW);
state = RUNNING;
}
else if (now - stateStartTime >= CRANK_TIME) {
digitalWrite(STARTER_RELAY1, LOW);
state = WAIT_FOR_RUN;
stateStartTime = now;
}
break;
case WAIT_FOR_RUN:
digitalWrite(ENGINE_RUN, HIGH);
if (sense) {
state = RUNNING;
}
else if (now - stateStartTime >= WAIT_TIME) {
attemptCount++;
if (attemptCount >= 4) {
state = FAULT;
} else {
state = CRANKING;
stateStartTime = now;
}
}
break;
case RUNNING:
digitalWrite(ENGINE_RUN, HIGH);
digitalWrite(LED_RUN, HIGH);
if (!keyRun) {
state = IDLE;
}
break;
case FAULT:
digitalWrite(LED_FAULT, HIGH);
digitalWrite(ENGINE_RUN, LOW);
if (!keyRun) {
state = IDLE;
}
break;
case EMERGENCY:
// handled separately
break;
}
}
// ===============================
// Emergency Handler
// ===============================
void handleEmergency(unsigned long now) {
digitalWrite(ENGINE_RUN, LOW);
digitalWrite(STARTER_RELAY1, LOW);
digitalWrite(LED_IDLE, LOW);
digitalWrite(LED_RUN, LOW);
if (now - blinkTimer >= BLINK_TIME) {
blinkTimer = now;
blinkState = !blinkState;
digitalWrite(LED_FAULT, blinkState);
}
}
// ===============================
// Reset All Outputs
// ===============================
void resetOutputs() {
digitalWrite(LED_RUN, LOW);
digitalWrite(LED_IDLE, LOW);
digitalWrite(LED_FAULT, LOW);
digitalWrite(STARTER_RELAY1, LOW);
digitalWrite(ENGINE_RUN, LOW);
}