#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <SPI.h>
#include <SD.h>
#define I2C_ADDR 0x27
#define LCD_COLUMNS 20
#define LCD_LINES 4
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);
File kalibrierungFile;
const int chipSelect = 53; // CS-Pin für SD-Modul
const byte numRows = 4;
const byte numCols = 4;
char keymap[numRows][numCols] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
byte rowPins[numRows] = {9, 8, 7, 6};
byte colPins[numCols] = {5, 4, 3, 2};
Keypad keypad = Keypad(makeKeymap(keymap), rowPins, colPins, numRows, numCols);
const int relayPins[] = {10, 11, 12, 13};
const char* pumpNames[] = {"A", "B", "C", "D"};
int relayIndex = 0;
unsigned long relayDurations[4] = {100000, 100000, 100000, 100000}; // default 100 seconds for each pump
unsigned long relayStartTime = 0;
unsigned long lastInputTime = 0; // used to detect key press duration
unsigned long inputNumber = 0; // stores the input for duration
unsigned long relayReactionDurations[4] = {0, 0, 0, 0}; // Reaktionszeit in Millisekunden für jede Pumpe
float ph4, ph7, ph10; // Kalibrierungswerte
const int pH_sensorPin = A1;
unsigned long lastUpdateTime = 0; // Letztes Update in Millisekunden
const unsigned long updateInterval = 1000; // Update-Intervall in Millisekunden (1 Sekunde)
enum State {
MAIN_MENU,
SEC_MENU,
TRD_MENU,
CALIB,
SINGLE_PUMP_SELECTION,
SINGLE_PUMP_DURATION,
RUNNING_SINGLE_PUMP,
PROGRAM_DURATION_INPUT,
CONFIRM_START_PROGRAM,
RUNNING_PROGRAM,
AUTO_PROGRAM,
PUMP_FINISHED,
REACTION_TIME_INPUT,
RUNNING_REACTION_TIME,
PUMP_COMPLETED
};
enum PumpOrReaction {
PUMP,
REACTION
};
PumpOrReaction pumpOrReactionMode = PUMP;
State currentState = MAIN_MENU;
unsigned long timeDiff(unsigned long current, unsigned long previous) {
if (current >= previous) {
return (current - previous);
} else {
return (4294967295UL - previous + current); // account for overflow
}
}
void setup() {
lcd.init();
Serial.begin(9600);
lcd.backlight();
if (!SD.begin(chipSelect)) {
lcd.print("SD fail");
return;
}
for (int i = 0; i < 4; i++) {
pinMode(relayPins[i], OUTPUT);
digitalWrite(relayPins[i], LOW);
}
//kalibrieren();
lcd.clear();
lcd.setCursor(2,0);
lcd.print("Pumpenbuddy V1.0");
Serial.println("LCD: Hauptmenue");
lcd.setCursor(4,1);
lcd.print("Hallo lieber");
lcd.setCursor(4,2);
lcd.print("Pumpenfreund");
lcd.setCursor(0,3);
lcd.print("Gleich gehts los ...");
delay(5000);
displayMainMenu();
}
void showCountdown(int seconds, const char* bufferLabel) {
lcd.setCursor(0, 1);
lcd.print(bufferLabel);
for(int i = seconds; i > 0; i--) {
lcd.setCursor(0, 3);
lcd.print(" ");
lcd.setCursor(0, 3);
lcd.print(i);
lcd.print(" ");
lcd.setCursor(4, 3);
lcd.print("Sek. verbleibend");
delay(1000);
}
}
void kalibrieren() {
kalibrierungFile = SD.open("kalibrierung.txt", FILE_WRITE); // Datei öffnen oder erstellen
lcd.clear();
lcd.print("Kalibrierung...");
// Kalibrierung für pH 4
showCountdown(40, "pH 4 Puffer ");
ph4 = analogRead(A1); // pH-Sonde an A1 angeschlossen
kalibrierungFile.print("pH 4: ");
kalibrierungFile.println(ph4);
// Kalibrierung für pH 7
showCountdown(40, "pH 7 Puffer ");
ph7 = analogRead(A1);
kalibrierungFile.print("pH 7: ");
kalibrierungFile.println(ph7);
// Kalibrierung für pH 10
showCountdown(40, "pH 10 Puffer");
ph10 = analogRead(A1);
kalibrierungFile.print("pH 10: ");
kalibrierungFile.println(ph10);
kalibrierungFile.close(); // Datei schließen
lcd.clear();
lcd.setCursor(0, 1);
lcd.print("Kalibriert");
delay(10000); // 10-Sekunden-Pause
// Zurück zum Hauptmenü
currentState = MAIN_MENU;
displayMainMenu();
}
void loop() {
char customKey = keypad.getKey();
if (customKey) {
handleKeyPress(customKey);
}
if (currentState == RUNNING_SINGLE_PUMP || currentState == RUNNING_PROGRAM) {
long timeLeft = relayDurations[relayIndex] - timeDiff(millis(), relayStartTime);
if (timeLeft <= 0) {
digitalWrite(relayPins[relayIndex], LOW); // Beenden Sie die aktuelle Pumpe.
if (currentState == RUNNING_SINGLE_PUMP) {
endPumpProcess();
} else if (currentState == RUNNING_PROGRAM) {
long timeLeft;
if (pumpOrReactionMode == PUMP) {
timeLeft = relayDurations[relayIndex] - timeDiff(millis(), relayStartTime);
} else { // REACTION
timeLeft = relayReactionDurations[relayIndex] - timeDiff(millis(), relayStartTime);
}
if (timeLeft <= 0) {
if (pumpOrReactionMode == PUMP) {
digitalWrite(relayPins[relayIndex], LOW);
pumpOrReactionMode = REACTION;
relayStartTime = millis(); // Starten Sie die Reaktionszeit.
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Reaktionszeit ");
lcd.print(pumpNames[relayIndex]); // Starten Sie die Reaktionszeit.
} else { // REACTION
relayIndex++;
if (relayIndex >= 4) {
currentState = PUMP_COMPLETED;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Ablauf abgeschlossen");
lcd.setCursor(0, 1);
lcd.print("*: Hauptmenu");
} else {
pumpOrReactionMode = PUMP;
relayStartTime = millis();
digitalWrite(relayPins[relayIndex], HIGH); // Starten Sie die nächste Pumpe.
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Pumpe ");
lcd.print(pumpNames[relayIndex]);
lcd.setCursor(8, 0);
lcd.print("gestartet");
lcd.setCursor(11, 2);
lcd.print("*:Abbruch");
}
}
} else {
lcd.setCursor(0, 3);
lcd.print(timeLeft / 1000);
lcd.print(" Sek. ");
}
}
} else {
lcd.setCursor(0, 3);
lcd.print(timeLeft / 1000);
lcd.print(" Sek. ");
}
}
}
float interpolatePH(int rawValue) {
float phValue;
if (rawValue <= ph7) {
// Interpolation zwischen pH 4 und pH 7
phValue = 4 + (7 - 4) * ((float)(rawValue - ph4) / (ph7 - ph4));
} else {
// Interpolation zwischen pH 7 und pH 10
phValue = 7 + (10 - 7) * ((float)(rawValue - ph7) / (ph10 - ph7));
}
return phValue;
}
void displayPH() {
unsigned long lastUpdateTime = 0;
unsigned long updateInterval = 1000; // Aktualisierung jede Sekunde
char key;
while (true) { // Endlosschleife, bis der Benutzer die Schleife verlässt
if (keypad.getKey()) {
key = keypad.getKey(); // Hypothetisch, ersetzen Sie dies durch Ihren eigenen Key-Reading-Code
if (key == '*') {
currentState = MAIN_MENU;
displayMainMenu();
return; // Verlassen der Schleife und Rückkehr zum Hauptmenü
}
}
unsigned long currentMillis = millis();
if (currentMillis - lastUpdateTime >= updateInterval) {
lastUpdateTime = currentMillis;
// Ihr Code zum Lesen und Anzeigen des pH-Wertes hier...
int sensorValue = analogRead(A1);
float pH = interpolatePH(sensorValue);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("pH-Wert: ");
lcd.print(pH, 2);
lcd.setCursor(0, 3);
lcd.print("*: Hauptmenue");
}
delay(50); // Kurze Verzögerung, um den Mikrocontroller nicht zu überlasten
}
}
void handleKeyPress(char key) {
switch (currentState) {
case MAIN_MENU:
if (key == '1') {
currentState = SINGLE_PUMP_SELECTION;
Serial.println("Single Pump");
displaySinglePumpeSelection();
} else if (key == '2') {
relayIndex = 0;
currentState = PROGRAM_DURATION_INPUT;
Serial.println("Indiv.Prog.");
displayProgramDurationInput();
} else if (key == '3') {
currentState = AUTO_PROGRAM;
Serial.println("Fixed Prog.");
startAutoProgram();
} else if (key == '4') {
currentState = SEC_MENU;
Serial.println("Sec_menu");
displaySecMenu();
}
break;
case SEC_MENU:
if (key == '1') {
currentState = CALIB;
Serial.println("pH calib");
kalibrieren();
} else if (key == '2') {
relayIndex = 0;
//currentState = PROGRAM_DURATION_INPUT;
Serial.println("pH show");
displayPH();
} else if (key == '3') {
currentState = TRD_MENU;
Serial.println("Trd Menu");
displayTrdMenu();
} else if (key == '*') {
currentState = MAIN_MENU;
Serial.println("back");
displayMainMenu();
}
break;
case TRD_MENU:
if (key == '1') {
//currentState = SINGLE_PUMP_SELECTION;
Serial.println("Platzhalter 1");
//displaySinglePumpeSelection();
} else if (key == '2') {
relayIndex = 0;
//currentState = PROGRAM_DURATION_INPUT;
Serial.println("Platzhalter 2");
//displayProgramDurationInput();
} else if (key == '3') {
//currentState = TRD_MENU;
Serial.println("Platzhalter 3");
//displayTrdMenu();
} else if (key == '*') {
currentState = MAIN_MENU;
Serial.println("back");
displayMainMenu();
}
break;
case SINGLE_PUMP_SELECTION:
if (key >= 'A' && key <= 'D') {
relayIndex = key - 'A';
currentState = SINGLE_PUMP_DURATION;
displaySinglePumpDuration();
}
break;
case SINGLE_PUMP_DURATION:
if (key >= '0' && key <= '9') {
inputNumber = inputNumber * 10 + (key - '0');
lcd.setCursor(0, 2);
lcd.print(inputNumber);
lcd.print(" Sek. ");
} else if (key == '#') {
relayDurations[relayIndex] = inputNumber * 1000;
inputNumber = 0;
currentState = RUNNING_SINGLE_PUMP;
relayStartTime = millis();
digitalWrite(relayPins[relayIndex], HIGH);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Pumpe ");
lcd.print(pumpNames[relayIndex]);
lcd.setCursor(8, 0);
lcd.print("gestartet");
lcd.setCursor(11, 2);
lcd.print("*:Abbruch");
} else if (key == '*') {
inputNumber = 0;
currentState = MAIN_MENU;
displayMainMenu();
}
break;
case RUNNING_SINGLE_PUMP:
if (key == '*') {
digitalWrite(relayPins[relayIndex], LOW);
inputNumber = 0;
currentState = MAIN_MENU;
displayMainMenu();
}
break;
case PROGRAM_DURATION_INPUT:
if (key >= '0' && key <= '9') {
inputNumber = inputNumber * 10 + (key - '0');
lcd.setCursor(0, 2);
lcd.print(inputNumber);
lcd.print(" Sek. ");
} else if (key == '#') {
relayDurations[relayIndex] = inputNumber * 1000;
inputNumber = 0;
currentState = REACTION_TIME_INPUT; // Änderung hier: Wechsel zu Reaktionszeit-Eingabe
displayReactionTimeInput();
} else if (key == '*') {
inputNumber = 0;
currentState = MAIN_MENU;
displayMainMenu();
}
break;
case CONFIRM_START_PROGRAM:
if (key == '#') {
relayStartTime = millis();
relayIndex = 0; // Starten Sie mit der ersten Pumpe.
digitalWrite(relayPins[relayIndex], HIGH);
currentState = RUNNING_PROGRAM;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Pumpe A gestartet");
lcd.setCursor(11, 2);
lcd.print("*:Abbruch");
} else if (key == '*') {
currentState = MAIN_MENU;
displayMainMenu();
}
break;
case REACTION_TIME_INPUT:
if (key >= '0' && key <= '9') {
inputNumber = inputNumber * 10 + (key - '0');
lcd.setCursor(0, 2);
lcd.print(inputNumber);
lcd.print(" Sek. ");
} else if (key == '#') {
relayReactionDurations[relayIndex] = inputNumber * 1000;
inputNumber = 0;
relayIndex++;
if (relayIndex >= 4) {
currentState = CONFIRM_START_PROGRAM;
displayConfirmStartProgram();
} else {
currentState = PROGRAM_DURATION_INPUT; // Änderung hier: Wechsel zur nächsten Pumpe
displayProgramDurationInput();
}
} else if (key == '*') {
inputNumber = 0;
currentState = MAIN_MENU;
displayMainMenu();
}
break;
case RUNNING_PROGRAM:
if (key == '*') {
currentState = MAIN_MENU;
displayMainMenu();
}
break;
case PUMP_COMPLETED:
if (key == '*') {
currentState = MAIN_MENU;
displayMainMenu();
}
break;
case PUMP_FINISHED:
if (key == '*') {
currentState = MAIN_MENU;
displayMainMenu();
}
break;
case AUTO_PROGRAM:
if (key == '#') {
relayIndex = 0;
relayDurations[0] = 5000; // Laufzeit von Pumpe A
relayReactionDurations[0] = 3000; // Reaktionszeit nach Pumpe A
relayDurations[1] = 10000; // Laufzeit von Pumpe B
relayReactionDurations[1] = 3000; // Reaktionszeit nach Pumpe B
relayDurations[2] = 15000; // Laufzeit von Pumpe C
relayReactionDurations[2] = 3000; // Reaktionszeit nach Pumpe C
relayDurations[3] = 20000; // Laufzeit von Pumpe D
relayReactionDurations[3] = 3000; // Reaktionszeit nach Pumpe D
relayStartTime = millis();
digitalWrite(relayPins[relayIndex], HIGH);
currentState = RUNNING_PROGRAM;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Pumpe A gestartet");
lcd.setCursor(11, 2);
lcd.print("*:Abbruch");
}
else if (key == '*') {
currentState = MAIN_MENU;
displayMainMenu();
}
break;
}
}
void displayMainMenu() {
relayIndex = 0;
inputNumber = 0;
for (int i = 0; i < 4; i++) {
relayDurations[i] = 100000; // Setzen Sie alle Dauerwerte zurück.
digitalWrite(relayPins[i], LOW); // Stellen Sie sicher, dass alle Pumpen ausgeschaltet sind.
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("1: Einzelne Pumpe");
lcd.setCursor(0, 1);
lcd.print("2: Indiv. Ablauf");
lcd.setCursor(0, 2);
lcd.print("3: Fester Ablauf");
lcd.setCursor(0, 3);
lcd.print("4: weiter ...");
}
void displaySecMenu() {
relayIndex = 0;
inputNumber = 0;
for (int i = 0; i < 4; i++) {
relayDurations[i] = 100000; // Setzen Sie alle Dauerwerte zurück.
digitalWrite(relayPins[i], LOW); // Stellen Sie sicher, dass alle Pumpen ausgeschaltet sind.
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("1: pH Kalibrieren");
lcd.setCursor(0, 1);
lcd.print("2: pH Anzeigen");
lcd.setCursor(0, 2);
lcd.print("3: pH Steuerung");
lcd.setCursor(0, 3);
lcd.print("*: Hauptmenue");
}
void displayTrdMenu() {
relayIndex = 0;
inputNumber = 0;
for (int i = 0; i < 4; i++) {
relayDurations[i] = 100000; // Setzen Sie alle Dauerwerte zurück.
digitalWrite(relayPins[i], LOW); // Stellen Sie sicher, dass alle Pumpen ausgeschaltet sind.
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("1: Platzhalter");
lcd.setCursor(0, 1);
lcd.print("2: Platzhalter");
lcd.setCursor(0, 2);
lcd.print("3: Platzhalter");
lcd.setCursor(0, 3);
lcd.print("* Hauptmenue");
}
void endPumpProcess() {
digitalWrite(relayPins[relayIndex], LOW);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Pumpvorgang beendet");
lcd.setCursor(0, 1);
lcd.print("Pumpe ");
lcd.print(pumpNames[relayIndex]);
lcd.setCursor(0, 2);
lcd.print("*: Hauptmenu");
currentState = PUMP_FINISHED;
}
void displayReactionTimeInput() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Reaktionszeit ");
lcd.print(pumpNames[relayIndex]);
lcd.setCursor(0, 1);
lcd.print("Zeit in Sek.:");
lcd.setCursor(0, 3);
lcd.print("*: Abbruch #: Start");
}
void displaySinglePumpeSelection() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Welche Pumpe soll");
lcd.setCursor(0, 1);
lcd.print("gestartet werden?");
lcd.setCursor(5, 3);
lcd.print("A, B, C, D");
}
void displaySinglePumpDuration() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Pumpe ");
lcd.print(pumpNames[relayIndex]);
lcd.setCursor(0, 1);
lcd.print("Zeit in Sek.:");
lcd.setCursor(0, 3);
lcd.print("*: Abbruch #: Start");
}
void displayProgramDurationInput() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Pumpe ");
lcd.print(pumpNames[relayIndex]);
lcd.setCursor(0, 1);
lcd.print("Zeit in Sek.:");
lcd.setCursor(0, 3);
lcd.print("*: Abbruch #: Start");
}
void displayConfirmStartProgram() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Programm starten?");
lcd.setCursor(0, 2);
lcd.print("*: Abbruch #: Start");
}
void startAutoProgram() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Fester Ablauf");
lcd.setCursor(0, 2);
lcd.print("*: Abbruch #: Start");
}