// Used to debounce GPIO Digital Input - https://github.com/thomasfredericks/Bounce2
#include "Bounce2.h"
// Used for Pushbutton handling - https://github.com/poelstra/arduino-multi-button
#include "PinButton.h"
// output assignments
const int ledStatus8 = 8; // Status LED
const int ledFBremse9 = 9; // Status LED Feststellbremse
const int RelStandlicht10 = 10; // Relais 1 - Standlicht
const int RelAbblendlicht11 = 11; // Relais 2 - Abblendlicht
const int RelFernlicht12 = 12; // Relais 3 - Fernlicht
const int RelBlinkerLinks13 = 13; // Relais 4 - Blinker Links
const int RelBlinkerRechts14 = 14; // Relais 5 - Blinker Rechts
const int RelBremslicht15 = 15; // Relais 6 - Bremslicht
const int RelHorn16 = 16; // Relais 7 - Horn
const int RelRes23 = 23; // Relais 8 - Reserve for Special Effects
const int Out1Motor18 = 18; // H-Bridge output 1
const int Out2Motor19 = 19; // H-Bridge output 2
// input assignment
const int InAnalog6 = 6; // Voltage sensing soldered to A6
// Variables for Battery Voltage sensing and calculation
float vout = 0.0;
float vin = 0.0;
float R1 = 100000.0; // resistance of R1 (100K)
float R2 = 10000.0; // resistance of R2 (10K)
int value = 0;
int BatLOWcounter = 0; // Number of Status LED flashes if BAT is LOW
bool BatLOW = false; // used to handle low battery status during runtime (under load)
bool BatidleLOW = false; // used to handle low battery status during startup
bool charging = false; // used to manage charging state
// Used for smoothing of analog voltage reading on Pin A7 - copied from https://www.arduino.cc/en/Tutorial/BuiltInExamples/Smoothing
const int numReadings = 10;
float readings[numReadings]; // the readings from the analog input
int readIndex = 0; // the index of the current reading
float total = 0; // the running total
float average = 0; // the average
// Voltage Reading Interval
const unsigned long vreadingInterval = 1000;
unsigned long vreadingpreviousMillis = 0;
// Used to trigger startup/shutdown light sequence
bool startup = true;
// Security Blinker Sequence
int securityblink = 0;
const unsigned long securityblinkInterval = 385;
unsigned long securityblinkpreviousMillis = 0;
unsigned long securityblinkpreviousMillis2 = 0;
// Blinker Interval
const unsigned long BlinkerInterval = 750;
unsigned long BlinkerpreviousMillis = 0;
// double Blinker Interval
const unsigned long doubleBlinkerInterval = 385;
// quad Blinker Interval
const unsigned long quadBlinkerInterval = 192;
unsigned long quadBlinkerpreviousMillis = 0;
unsigned long quadblinkerstate = 0;
// Fast Blinker Interval
const unsigned long FBlinkerInterval = 250;
unsigned long FBlinkerpreviousMillis = 0;
// Flash Interval
const unsigned long FlashInterval = 100;
unsigned long FlashpreviousMillis = 0;
// Brake Light Interval
const unsigned long brakelightInterval = 192;
unsigned long brakelightpreviousMillis = 0;
unsigned long brakelightpreviousMillis2 = 0;
// Ignition Status
bool ignition = false;
// Status LED Flashing (used for bat low indication)
unsigned long statusledpreviousMillis = 0;
unsigned long statusledflashpreviousMillis = 0;
// Brake Motor Runtime
const unsigned long brakeengageruntime = 7000;
const unsigned long brakedisengageruntime = 5000;
unsigned long brakemotormillis = 0;
// Brake LED flash interval
const unsigned long brakeflashInterval = 100;
unsigned long brakeflashpreviousMillis = 0;
// Brake status
bool brakeactivated = false; // Dashboard Switch status
bool brakeengaged = false; // engaged status
bool brakeworking = false; // runtime status
// Flash Counter for Startup Blinking in low battery condition
int flashcounter = 0;
// variable for Blinkerstate
int Blinkerstate = 0;
bool RelBlinkerLinks13state = false;
bool RelBlinkerRechts14state = false;
// used to toggle Relais 8 (Special Effects)
bool rel8state = false;
// Blinker Button input using Multi Button library
PinButton buttonBlinker(6);
// Instantiate Bounce2 inputs:
Bounce2::Button switchzuendung = Bounce2::Button();
Bounce2::Button switchabblendlicht = Bounce2::Button();
Bounce2::Button switchfernlicht = Bounce2::Button();
Bounce2::Button switchfeststellbremse = Bounce2::Button();
Bounce2::Button sensorbremse = Bounce2::Button(); // used for brake light
Bounce2::Button buttonhorn = Bounce2::Button();
Bounce2::Button sensorbremsefest = Bounce2::Button(); // used to fastblink Brakelight
#define d0 0 // Zündung Schlüssel Schalter
#define d1 1 // Abblendlicht Schalter
#define d2 2 // Fernlicht Schalter
#define d3 3 // Feststellbremse Schalter
// #define d4 4 // reserve I/O --> USB connector
// #define d17 17 // reserve I/O --> USB connector
#define d5 5 // Bremse Sensor
#define d7 7 // Horn Taster
#define d24 24 // Bremse Sensor - Fest gedrückt
void setup() {
// initialize input:
switchzuendung.attach(d0, INPUT_PULLUP);
switchzuendung.interval(5);
switchzuendung.setPressedState(LOW);
switchabblendlicht.attach(d1, INPUT_PULLUP);
switchabblendlicht.interval(5);
switchabblendlicht.setPressedState(LOW);
switchfernlicht.attach(d2, INPUT_PULLUP);
switchfernlicht.interval(5);
switchfernlicht.setPressedState(LOW);
switchfeststellbremse.attach(d3, INPUT_PULLUP);
switchfeststellbremse.interval(5);
switchfeststellbremse.setPressedState(LOW);
sensorbremse.attach(d5, INPUT_PULLUP);
sensorbremse.interval(5);
sensorbremse.setPressedState(LOW);
buttonhorn.attach(d7, INPUT_PULLUP);
buttonhorn.interval(5);
buttonhorn.setPressedState(LOW);
sensorbremsefest.attach(d24, INPUT_PULLUP);
sensorbremsefest.interval(5);
sensorbremsefest.setPressedState(LOW);
// use 3.3V reference voltage for ADC measurement. Pin VREF & 3.3V must be bridged
analogReference(EXTERNAL);
// set analog input mode for voltage sensing
pinMode(InAnalog6, INPUT);
// initialize output
pinMode(ledStatus8, OUTPUT);
pinMode(ledFBremse9, OUTPUT);
pinMode(RelStandlicht10, OUTPUT);
pinMode(RelAbblendlicht11, OUTPUT);
pinMode(RelFernlicht12, OUTPUT);
pinMode(RelBlinkerLinks13, OUTPUT);
pinMode(RelBlinkerRechts14, OUTPUT);
pinMode(RelBremslicht15, OUTPUT);
pinMode(RelHorn16, OUTPUT);
pinMode(RelRes23, OUTPUT);
pinMode(Out1Motor18, OUTPUT);
pinMode(Out2Motor19, OUTPUT);
// Initial Battery Voltage reading and Calculation during setup - copied from https://projecthub.arduino.cc/SetNFix/arduino-voltmeter-3bf7f4#section6
delay(800);
value = analogRead(InAnalog6);
vout = (value * 3.30) / 1024.0;
vin = vout / (R2 / (R1 + R2));
if (vin < 0.09) {
vin = 0.0;
}
// initialize all the readings to 0 for Voltage reading on Pin A6:
for (int thisReading = 0; thisReading < numReadings; thisReading++) {
readings[thisReading] = 0;
}
vin = 12.80; // Use this to set voltage level for testing in WOKWI (only 5V source)
// set the runtime voltage variable = initial reading to bridge the gap until the 10s array is calculated/smoothed.
average = vin;
// Blink three times when Controller Starts, and Battery is FULL
if (vin >= 12.65) {
while (flashcounter <= 2) {
digitalWrite(RelBlinkerLinks13, HIGH);
digitalWrite(RelBlinkerRechts14, HIGH);
delay(300);
digitalWrite(RelBlinkerLinks13, LOW);
digitalWrite(RelBlinkerRechts14, LOW);
delay(300);
flashcounter++;
}
}
// Blink twice when Controller Starts, and Battery is 50%
else if (vin >= 12.30) {
while (flashcounter <= 1) {
digitalWrite(RelBlinkerLinks13, HIGH);
digitalWrite(RelBlinkerRechts14, HIGH);
delay(300);
digitalWrite(RelBlinkerLinks13, LOW);
digitalWrite(RelBlinkerRechts14, LOW);
delay(300);
flashcounter++;
}
}
// Flash Status LED when Controller Starts, and Battery is LOW
else if (vin < 12.00) {
BatidleLOW = true;
while (BatLOWcounter <= 20) {
digitalWrite(ledStatus8, HIGH);
delay(100);
digitalWrite(ledStatus8, LOW);
delay(200);
BatLOWcounter++;
}
}
// Guess Brake status after powerloss
if (sensorbremsefest.isPressed() == true && switchfeststellbremse.isPressed() == true) {
brakeengaged = true;
}
// Activate for debugging - Caution: serial interface is physically connected to zuendung & abblendlicht switches (TX,RX). When using serial; do not use Abblendlicht.
// Serial.begin(9600);
}
// -------------------------------------- LOOP -------------------------------------------------------------
void loop() {
// continuously read input states
switchzuendung.update();
switchabblendlicht.update();
switchfernlicht.update();
switchfeststellbremse.update();
sensorbremse.update();
buttonBlinker.update();
buttonhorn.update();
sensorbremsefest.update();
// Park Brake Switch Reading ---------------------------------------
if (switchfeststellbremse.isPressed() == true) {
brakeactivated = true;
} else {
brakeactivated = false;
}
// Millisecond counter since controller boot. used for timers
unsigned long currentMillis = millis();
// -------------------------------------- BATTERY MONITOR ----------------------------------------------------
// continuous Battery Voltage reading and Calculation - copied from https://projecthub.arduino.cc/SetNFix/arduino-voltmeter-3bf7f4#section6
value = analogRead(InAnalog6);
vout = (value * 3.30) / 1024.0;
vin = vout / (R2 / (R1 + R2));
if (vin < 0.09) {
vin = 0.0;
}
vin = 12.80; // Use this to set voltage level for testing in WOKWI (only 5V source)
// store every 1s the voltage reading from vin. calculate average value after 10 readings.
if (currentMillis - vreadingpreviousMillis >= vreadingInterval) {
vreadingpreviousMillis = currentMillis;
// subtract the last reading:
total = total - readings[readIndex];
// read from the sensor:
readings[readIndex] = vin;
// add the reading to the total:
total = total + readings[readIndex];
// advance to the next position in the array:
readIndex = readIndex + 1;
}
// if we're at the end of the array...
if (readIndex >= numReadings) {
average = total / numReadings;
// Serial.println(average);
// ...wrap around to the beginning:
readIndex = 0;
}
// 11.70V is ok under load. all functions enabled.
if (average >= 11.70) {
BatLOW = false;
// 11.40V is low for AGM batteries under load. Lights will be disabled (values below are for error correction)
} else if (average <= 11.20) {
BatLOW = true;
}
// Re-enable Lights & Horn if charging is detected during runtime
if (average >= 13.40) {
BatidleLOW = false;
charging = true;
}
if (average <= 13.25) {
charging = false;
}
// ----------------------------------- Blinker Patterns -------------------------------------------------------
// disable blinker states if startup/shutdown sequence is active
if (securityblink == 0 && ignition == true) {
switch (Blinkerstate) {
case 0:
// Turn off Blinker -------------------------------------------------
digitalWrite(RelBlinkerLinks13, LOW);
digitalWrite(RelBlinkerRechts14, LOW);
RelBlinkerLinks13state = LOW;
RelBlinkerRechts14state = LOW;
break;
case 1:
// ---------------- Blink left & right twice alternating ---------------------------------------
if (quadblinkerstate <= 3) {
if (currentMillis - quadBlinkerpreviousMillis >= quadBlinkerInterval) {
quadBlinkerpreviousMillis = currentMillis;
if (RelBlinkerLinks13state == LOW) {
RelBlinkerLinks13state = HIGH;
} else {
RelBlinkerLinks13state = LOW;
}
quadblinkerstate = quadblinkerstate + 1;
}
}
if (quadblinkerstate >= 4 && quadblinkerstate <= 7) {
if (currentMillis - quadBlinkerpreviousMillis >= quadBlinkerInterval) {
quadBlinkerpreviousMillis = currentMillis;
if (RelBlinkerRechts14state == LOW) {
RelBlinkerRechts14state = HIGH;
} else {
RelBlinkerRechts14state = LOW;
}
quadblinkerstate = quadblinkerstate + 1;
}
}
if (quadblinkerstate >= 8) {
quadblinkerstate = 0;
}
digitalWrite(RelBlinkerLinks13, RelBlinkerLinks13state);
digitalWrite(RelBlinkerRechts14, RelBlinkerRechts14state);
break;
case 2:
// --------------- Blink fast left & right alternating --------------------------------------
if (currentMillis - BlinkerpreviousMillis >= doubleBlinkerInterval) {
BlinkerpreviousMillis = currentMillis;
if (RelBlinkerLinks13state == LOW) {
RelBlinkerLinks13state = HIGH;
} else {
RelBlinkerLinks13state = LOW;
}
if (RelBlinkerLinks13state == LOW) {
RelBlinkerRechts14state = HIGH;
} else {
RelBlinkerRechts14state = LOW;
}
digitalWrite(RelBlinkerLinks13, RelBlinkerLinks13state);
digitalWrite(RelBlinkerRechts14, RelBlinkerRechts14state);
}
break;
case 3:
// ---------------- Blink fast left & right simultaneously ---------------------------------------
if (currentMillis - BlinkerpreviousMillis >= doubleBlinkerInterval) {
BlinkerpreviousMillis = currentMillis;
if (RelBlinkerLinks13state == LOW) {
RelBlinkerLinks13state = HIGH;
} else {
RelBlinkerLinks13state = LOW;
}
if (RelBlinkerRechts14state == LOW) {
RelBlinkerRechts14state = HIGH;
} else {
RelBlinkerRechts14state = LOW;
}
digitalWrite(RelBlinkerLinks13, RelBlinkerLinks13state);
digitalWrite(RelBlinkerRechts14, RelBlinkerRechts14state);
}
break;
case 4:
// ---------------- Blink left & right simultaneously ---------------------------------------
if (currentMillis - BlinkerpreviousMillis >= BlinkerInterval) {
BlinkerpreviousMillis = currentMillis;
if (RelBlinkerLinks13state == LOW) {
RelBlinkerLinks13state = HIGH;
} else {
RelBlinkerLinks13state = LOW;
}
if (RelBlinkerRechts14state == LOW) {
RelBlinkerRechts14state = HIGH;
} else {
RelBlinkerRechts14state = LOW;
}
digitalWrite(RelBlinkerLinks13, RelBlinkerLinks13state);
digitalWrite(RelBlinkerRechts14, RelBlinkerRechts14state);
}
break;
case 5:
// --------------- Blink left & right alternating -----------------------------------------
if (currentMillis - BlinkerpreviousMillis >= BlinkerInterval) {
BlinkerpreviousMillis = currentMillis;
if (RelBlinkerLinks13state == LOW) {
RelBlinkerLinks13state = HIGH;
} else {
RelBlinkerLinks13state = LOW;
}
if (RelBlinkerLinks13state == LOW) {
RelBlinkerRechts14state = HIGH;
} else {
RelBlinkerRechts14state = LOW;
}
digitalWrite(RelBlinkerLinks13, RelBlinkerLinks13state);
digitalWrite(RelBlinkerRechts14, RelBlinkerRechts14state);
}
break;
case 6:
// ---------------- Turn on Blinker left & right permanently ------------------------------
digitalWrite(RelBlinkerLinks13, HIGH);
digitalWrite(RelBlinkerRechts14, HIGH);
break;
}
}
// --------------------------------------- Zuendung ON & Park Brake control ---------------------------------------------------
// Enable all functions if Ignition key is turned
if (switchzuendung.isPressed() == true) {
ignition = true;
// ------------------ Blink twice on startup when Park Brake is engaged
if (startup == true && brakeengaged == true) {
if (securityblink <= 3) {
if (currentMillis - securityblinkpreviousMillis >= securityblinkInterval) {
securityblinkpreviousMillis = currentMillis;
RelBlinkerLinks13state = !RelBlinkerLinks13state;
RelBlinkerRechts14state = !RelBlinkerRechts14state;
securityblink++;
}
digitalWrite(RelBlinkerLinks13, RelBlinkerLinks13state);
digitalWrite(RelBlinkerRechts14, RelBlinkerRechts14state);
} else {
securityblink = 0;
startup = false;
}
} else {
startup = false;
}
// Disable All Functions if Ignition is off, and brake system is steady
} else if (switchzuendung.isPressed() == false) {
ignition = false;
}
if (ignition == true | brakeworking == true) {
// Brake engage procedures -----------------------------------------------------------------------
if (brakeactivated == true && brakeengaged == false) {
// working state: brake motor working to engage brake
if (brakeworking == false) {
brakemotormillis = currentMillis;
}
if (currentMillis - brakemotormillis <= brakeengageruntime) {
brakeworking = true;
digitalWrite(Out1Motor18, HIGH);
digitalWrite(Out2Motor19, LOW);
} else {
brakeworking = false;
brakeengaged = true;
}
}
// steady state: brake fully engaged
if (brakeactivated == true && brakeengaged == true) {
brakeworking = false;
digitalWrite(Out1Motor18, LOW);
digitalWrite(Out2Motor19, LOW);
digitalWrite(ledFBremse9, HIGH);
digitalWrite(RelBremslicht15, LOW);
}
// Brake disengage procedures -----------------------------------------------------------------------
if (brakeactivated == false && brakeengaged == true) {
// working state: brake motor working to disengage brake
if (brakeworking == false) {
brakemotormillis = currentMillis;
}
if (currentMillis - brakemotormillis <= brakedisengageruntime) {
brakeworking = true;
digitalWrite(Out1Motor18, LOW);
digitalWrite(Out2Motor19, HIGH);
} else {
brakeworking = false;
brakeengaged = false;
}
}
// steady state: brake fully disengaged
if (brakeactivated == false && brakeengaged == false) {
brakeworking = false;
digitalWrite(Out1Motor18, LOW);
digitalWrite(Out2Motor19, LOW);
digitalWrite(ledFBremse9, LOW);
}
// Fast Blink FBremse LED & Brake Lights while working
if (brakeworking == true) {
if (currentMillis - brakelightpreviousMillis >= doubleBlinkerInterval) {
brakelightpreviousMillis = currentMillis;
digitalWrite(RelBremslicht15, HIGH);
digitalWrite(ledFBremse9, HIGH);
brakelightpreviousMillis2 = currentMillis;
} else if (currentMillis - brakelightpreviousMillis2 >= brakelightInterval) {
digitalWrite(RelBremslicht15, LOW);
digitalWrite(ledFBremse9, LOW);
}
}
// --------------------------------------- LIGHT & Horn Control ---------------------------------------------------
// Lights are only enabled if Battery is not LOW
if (BatLOW == false && BatidleLOW == false) {
// Turn on Status LED & Standlicht permanently ----------------
digitalWrite(ledStatus8, HIGH);
digitalWrite(RelStandlicht10, HIGH);
// Abblendlicht -----------------------------------------------
if (switchabblendlicht.isPressed() == true) {
digitalWrite(RelAbblendlicht11, HIGH);
} else {
digitalWrite(RelAbblendlicht11, LOW);
}
// Fernlicht --------------------------------------------------
if (switchfernlicht.isPressed() == true) {
digitalWrite(RelFernlicht12, HIGH);
} else {
digitalWrite(RelFernlicht12, LOW);
}
// Bremslicht -------------------------------------------------
if (brakeengaged == false && brakeworking == false) {
if (sensorbremse.isPressed() == false) {
digitalWrite(RelBremslicht15, HIGH);
} else {
digitalWrite(RelBremslicht15, LOW);
}
}
// Horn -----------------------------------------------
if (buttonhorn.isPressed() == true) {
digitalWrite(RelHorn16, HIGH);
} else {
digitalWrite(RelHorn16, LOW);
}
// Blinker State --------------------------------------
if (buttonBlinker.isClick()) {
Blinkerstate = Blinkerstate + 1;
if (Blinkerstate > 6) {
Blinkerstate = 0;
}
RelBlinkerLinks13state = LOW;
RelBlinkerRechts14state = LOW;
quadblinkerstate = 0;
}
// Blinker off
if (buttonBlinker.isDoubleClick()) {
Blinkerstate = 0;
}
// Reserve IO (Relais 8 for special effects) ---------
if (buttonBlinker.isLongClick()) {
rel8state = !rel8state;
digitalWrite(RelRes23, rel8state);
}
// ------------------------------------- BATTERY Safe mode -----------------------------------------------------
} else if (BatLOW == true | BatidleLOW == true) {
// Turn off Blinker
Blinkerstate = 0;
quadblinkerstate = 0;
// Turn off all Lights
digitalWrite(RelStandlicht10, LOW);
digitalWrite(RelAbblendlicht11, LOW);
digitalWrite(RelFernlicht12, LOW);
digitalWrite(RelBlinkerLinks13, LOW);
digitalWrite(RelBlinkerRechts14, LOW);
digitalWrite(RelBremslicht15, LOW);
digitalWrite(RelHorn16, LOW);
digitalWrite(RelRes23, LOW);
// Blink ledStatus8 fast if Battery is low and Zuendung is on
if (currentMillis - statusledpreviousMillis >= FBlinkerInterval) {
statusledpreviousMillis = currentMillis;
digitalWrite(ledStatus8, HIGH);
statusledflashpreviousMillis = currentMillis;
} else if (currentMillis - statusledflashpreviousMillis >= FlashInterval) {
digitalWrite(ledStatus8, LOW);
}
}
} else if (ignition == false && brakeworking == false) {
// ---------------------------------------- SHUTDOWN sequence --------------------------------------------------------
// ------------------ Blink twice on shutdown when Park Brake is engaged
if (brakeengaged == true && startup == false) {
if (securityblink <= 2) {
if (currentMillis - securityblinkpreviousMillis >= securityblinkInterval) {
securityblinkpreviousMillis = currentMillis;
digitalWrite(RelBlinkerLinks13, LOW);
digitalWrite(RelBlinkerRechts14, LOW);
securityblinkpreviousMillis2 = currentMillis;
securityblink++;
} else if (currentMillis - securityblinkpreviousMillis2 >= brakelightInterval) {
digitalWrite(RelBlinkerLinks13, HIGH);
digitalWrite(RelBlinkerRechts14, HIGH);
}
} else {
securityblink = 0;
startup = true;
}
} else {
startup = true;
// Turn off all outputs
digitalWrite(Out1Motor18, LOW);
digitalWrite(Out2Motor19, LOW);
digitalWrite(RelStandlicht10, LOW);
digitalWrite(RelAbblendlicht11, LOW);
digitalWrite(RelFernlicht12, LOW);
digitalWrite(RelBremslicht15, LOW);
digitalWrite(RelHorn16, LOW);
digitalWrite(RelRes23, LOW);
digitalWrite(RelBlinkerLinks13, LOW);
digitalWrite(RelBlinkerRechts14, LOW);
Blinkerstate = 0;
quadblinkerstate = 0;
RelBlinkerLinks13state = false;
RelBlinkerRechts14state = false;
// ----------------------------- IDLE Activities -------------------------------------------------------------
// Blink Status LED when Zuendung is turned off. & Blink FBremse LED when fbremse is active.
if (currentMillis - BlinkerpreviousMillis >= BlinkerInterval) {
BlinkerpreviousMillis = currentMillis;
digitalWrite(ledStatus8, HIGH);
if (brakeengaged == true) {
digitalWrite(ledFBremse9, HIGH);
}
FlashpreviousMillis = currentMillis;
} else if (currentMillis - FlashpreviousMillis >= FlashInterval) {
digitalWrite(ledStatus8, LOW);
if (brakeengaged == true) {
digitalWrite(ledFBremse9, LOW);
}
}
}
}
}Zuendung
Blinker
Feststellbremse
status
Scheinwerfer
abblend
Standlicht
Horn Button
Bremse
Horn