Uni Projekt (Cocktail-Mischer)

Das Ding mit den 16.000 dürfte Deine Taktquelle für den AVR sein.
Ich sehe irgendwie nur eine Versorgung, die über den Spannungsregler des MEGA-Boards geht.
Das dürfte Dir das Genick brechen, denn....

  • Der Regler verbrät fast 7V (12V -> 5V)
  • Der USB-Port Deines Rechners liefert im Allgemeinen 500mA
  • Bei 7V * 500mA müsste der Regler 3,5W verbraten.
    Das kann der nicht

Versorge die externen Komponenten mit einem Step-Down-Regler aus den 12V

Hänge an den MEGA nur das Display und las den irgendwas für längere Zeit anzeigen und auf dem Seriellen Monitor lässt Du Dir jeden Neustart ausgeben.
Wenn der nicht neu startet, dann ist alles im grünen Bereich.
Erstmal.

Hi, danke für den Hinweis!
Also ich hatte mir im Endeffekt das selbe irgendwie schon gedacht😅 ich wollte das bisschen Platz auf der oberen Hutschiene links jetzt nutzen, um da einen DC/DC Wandler von 12 auf 5V reinzubauen, um alle externen Verbraucher mit Strom zu versorgen. Geht ja zum Glück in diesem Fall ganz gut, weil es nur ein Bauteil neu und eine Leitung ersetzen bedeutet. Ich würde dementsprechend einfach die roten Klemmen (5V), statt über den Arduino, über den Wandler Versorgen.

Jetzt frag ich mich nur, warum es vorher geklappt hat und jetzt nicht mehr? Vorher lief das Programm über den USB Port als Stromversorgung mit deutlich weniger Leistung. Jetzt hat der Arduino eine 12V Quelle bei 2A und da treten die Fehler auf, die ich vorher nicht hatte. Das macht für mich keinen Sinn.

Ich hab auch nur keinen Wandler eingebaut, weil es ja vorher auf dem Breadboard funktioniert hat und ich da dann Geld sparen wollte... Kann mir das nochmal einer erklären, wie das kann?

Problem mit der seriellen Ausgabe ist leider, dass das USB Kabel nicht zeitgleich mit der Breakoutplatine angeschlossen werden kann.

Bei der Versorgung direkt von USB wird die Peripherie aus dem USB versorgt.
Bei der Versorgung über Vin muss der Spannungsstabilisator auf dem Arduino die Differenz (12-5V) * Strom als Wärme verbraten und das kann er nicht abführen und steigt aus.

Gruß Tommy

Ich hatte Dir das vorgerechnet.
Selbst wenn Du die vollen 500mA aus dem USB nicht benötigt hast...
Rechnen wir mal mit 200mA.
12V - 5V = 7V * 0,2A macht 1,4W
Das kann nicht abgeführt werden, der Regler steigt aus, die Spannung bricht zusammen,
Und da das Ding recht schnell abkühlt, kommt der wieder und Du hast das Gefühl es wäre nur ein Reboot.

Ich verstehe durch die Erklärung definitiv warum der Arduino aussteigt und was ich jetzt machen kann, Dankeschön euch Beiden!
Das heißt also ich kann wahrscheinlich den Arduino besser über 5V am Vin Port betreiben als über 12V oder? Klar ich könnte mehr Verbraucher anschließen aber das wäre auch Quatsch oder?

Eigentlich brauche ich die 12V auch nur für meine Pumpen und Ventile.

NEIN!
VIN ist VIN und nicht 5V.
Da müssen mindestens 6,5V anliegen.
Schau Dir mal den Schaltplan zu dem Board an. Dann siehst Du, wieso :wink:

Wenn du den Arduino über USB versorgst dann wird der Linearregler überhaupt nicht akiv.
Weil die USB-Spannung ja schon genau 5V ist.

Ein Arduino ist KEINE Spannungsquelle!
Du solltest alle Komponenten aus einer eigenen Spannungsquelle mit Strom versorgen
und NICHT über den 5V-Pin des Arduinos.

Also einen Stepdownregler für den Arduino der auf 6,5 bis 7V eingestellt ist.
Und einen Stepdownregler der auf 4,95V eingestellt ist. Der versorgt den ganzen Rest.

Am Vin-Eingang befindet sich ein nicht Stepdown sondern ein Linear-Spannungsregler.
Ein Linearspannungsregler wandelt nicht benötigte "Spannung" in Wärme um.
Die Wärmeleistung ist abzubauende Überspannung mal Strom.

Bei 12V Eingangsspannung und 5V Ausgangsspannung macht das dann abzubauende Überspannung 12V - 5V = 7V. mal 0,2 A = 1,4 Watt.
Aber der Spannungsregler ist nun mal kein elektrischer Handwärmer sondern ein klitzekleines Bauteil dass auf seiner klitzekleinen Oberfläche noch nicht mal 1W Wärmeleistung abführen kann.

Und bevor er zu heiß wird schaltet sich der Linearregler ab damit er nicht kaputt geht.

Die Eingangsspannung muss ca. 1,5V höher sein als die Ausgangsspannung. Sonst funktioniert der Linearregler nicht richtig. Und das bedeutet um am Ausgang 5V zu haben müssen auf der Eingangsseite 6,5V anliegen.

Dann hast du natürlich trotzdem eine gewisse Wärmeleistung
6,5V - 5V = 1,5V * 0,2 A = 0,75 W und das kann dann der Regler geradeso als Wärmeleistung abführen.

Gibt es in deinem Projekt I2C?
Da dürfen die Leitungen maximal 50 cm lang sein. Besser kürzer als 30 cm.
Sonst wird die Datenübertragung störanfällig. Und bei I2C muss absolut jeder einzelne Pegelwechsel des Signals passen. Sonst hängt sich der I2C-Bus auf.

Das ist bereist seit langem geklärt....

Nein.

Sehr präzise. Gar nicht mehr zu übertreffen an Präzision.
Es hängt von einer Menge Faktoren ab. Leitungskapazität, Busfrequenz, evtl. Störeinflüsse, Wert der Pullup-Widerstände, Es soll Leute geben die einen 1m langen I2C-Bus am laufen haben.

Also bekommt man von dir noch eine Angabe in cm oder nur "les doch selber in der Spezifikation nach"?

Was möchtest Du?
Du hast eine Aussage getätigt und die ist einfach nur falsch.
Merkst Du ja selbst:

Es gibt sogar Leute die haben zig Meter am laufen.

Jedenfalls ist es falsch pauschal zu behaupten, dass bei I2C bei 50cm Schluss ist.

Mach doch sowas nicht!
Das ist doch....

Und sowas wird dann immer gleich den Moderatoren gemeldet....

Also um Verwirrungen zu vermeiden, was speziell ich bzw. wir verbauen: wir nutzen ein LCD Display mit I2C und ungefähr 50cm Kabel. Das hat zumindest so lange durchgehalten, bis der Arduino zu viel Wärme produziert hatte:)

Ich habe mittlerweile verstanden, warum ich den 5V Anschluss vom Arduino nicht nutzen sollte und warum ich mindestens 6.5V am Vin haben sollte.
Jetzt stellt sich mir die Frage, was ich wie verbinden soll und woher ich eine gescheite 6.5V-7.5V Stromversorgung her bekomme.

Ich habe mir überlegt, dass ja die 5V, 7.5V und 12V Kreise getrennt sein sollten. Ich hätte am 12V Kreis die beiden stepDown Regler und meine 12V Magnetventile und 12V Pumpen dran (jeweils 8stk).

Am 7V hätte ich halt den Arduino dran und am 5V hätte ich dann ja wieder quasi nichts dran, denn 1xPoti, 1xDisplay, 1xSummer, 1xRelaiskarte, 2xLED und 14xTaster bekomme ich ja eigentlich auch aus dem Arduino gespeist oder hab ich das jetzt ganz dolle durcheinander?

Wenn ich z.b. den Poti über externe 5V Speise und das 3. Kabel dann am Arduino habe, ist das doch auch nicht so ganz richtig oder? Ich weiß jetzt nicht genau, was ich da wie anklemmen soll :sweat_smile:

Wenn du DC-DC-Stepdown-regler hast die genug Strom liefern können dann kannst du die benutzen.

Wenn mehrere Spannungsquellen benutzt werden müssen die Minus-Pole dieser Spannungsquellen alle untereinander verbunden sein und auch mit GND des Arduino verbunden sein. Aber ohne das diese GND Verbindungen einen elektrischen Kreis bilden. (eine sogenannte ground-loop darf NICHT enstehen.)

Dadurch das die Minuspole der Spannungsquellen untereinander verbunden sind erhält man einen gemeinsamen Bezugspunkt. Man sagt die Minuspole liegen auf gleichem Potential.

Wenn dein Poti über einen Analogeingang 0V bis 5V liefern soll dann wird ein Ende des Potis an Minus, das andere Ende an +5V und der Schleiferkontakt an den AD-Wandler-Eingang angeschlossen. Und nur wenn das Poti so angeschlossen ist wird es korrekt funktionieren.
Gerade weil es einen gemeinsamen Minuspol gibt.

Ich hab mir mal erlaubt das mit den meisten der Teile in sowas wie einen Schaltplan zu packen.

Zur Legende:
Gelb sind meine Signalleitungen
Blau ist GND/0V
Rot sind 5V
Grün ist 6-7V
Schwarz sind 12V

Bauteile sind beschriftet und halbwegs ordentlich gezeichnet.

Würde mich über eine kurze Rückmeldung dazu freuen.

Nicht unbedingt.
Das Problem ist der Gesamtstromverbrauch des Arduino und allem was am 5V und am 3,3V pin angescchlossen ist.
Im Eurem Fall braucht das Relasimodul wenn alle Relais eingeschaltet sind 8x 70mA = 420mA. Das überlastet, wie bereits erklährt, den Spannungsstabilisator auf dem Arduino. Auch die Hintergrundbeleuchtung des Displys kann ein großer Stromverbraucher sein. Der liegt je nach LCD Modul wahrscheinlich zwischen 20mA und 150mA (genauer Wert aus Datenblatt oder durch Messung) .
Es ist möglich den Arduino über 12V an Vin oder Netzteilbuchse zu versorgen, die anderen 5V Verbraucher aber über ein 12 zu 5V DC/DC Wandler (Minuspol GND muß zusammengeschaltet sein). Das Potentiometer und den Taster würde ich da aber an das 5V Pin anschließen.
Diese Lösung verhindert Probleme mit den 5V des USB-Verbindung während des Uploads oder Fehlersuche.
Wenn der Auduino mit 5V an dem 5V Pin versorgt werden soll dann ist es das beste Ihr verwendet einen USB Kabel bei dem die 5V Ader (Rote Ader im Kabel) durchgeschnitten wurde.
siehe Stromversorgung über den 5V pin und programmieren über USB

Die Ausgänge der I2C Geräte können das Signal nur auf Masse ziehen. Der HIGH (hier 5V) Zustand wird durch einen Widerstand erzeugt. Dieser muß die Leitungskapazität und die Eingänge laden, daß sie auf 5V kommen. Das kann bei einem zu hochohmigen Pullupwiderstand, bei langen Kabeln und bei vielen I2C Geräten am Bus nicht in der zur verfügung stehenen Zeit passieren und so nicht alle HIGH-Signale erzeugt werden.
Hier beschreibt Nick Gammon das Problem schön verständlich:

Daß die 5V Versorgung einknickt hat wenig mit i2C zu tun, aber wenn die Spannung weg ist ist auch die Funktion des Controllers / Displays weg.

Siehe oben.
Es ist möglich und funktioniert auch zuverlässig den Arduino über 12V an Vin oder Netzteilbuchse zu versorgen, die anderen 5V Verbraucher aber über ein 12 zu 5V DC/DC Wandler (Minuspol GND muß zusammengeschaltet sein).

Die Relaiskarte und eventuell das Display brauchen viel Strom. Die würde ich auf alle fälle mit einem 5v Wandler versorgen.

Brummschleifen sind für Analogsignale schlimm da man die kleinste eingestreute Spannungen sich als leises Brummen im Lautsprecher Hörbar machen. Digitale Schaltungen sind prinzipbedingt viel robuster gegenüber Störungseinstreuungen und darum sind Masseschleifen viel unkritischer und meist zu vernachlässigen.

Schaltplan ist gut. Meinen Vorschlag hast Du bereits in meiner ersten Antwort hier bekommen.
Noch andere Vorschläge. Bitte verwende nicht Pin 13. An diesem hängt eine LED, die beim Einschalten vom Bootoader ein paar mal zu blinken gebracht wird. Nimm ein anderes Pin. Der Mega hat ja genug davon.

Grüße Uwe

Achtung.
Die Pins SDA und SCL sind neben VREF ein 2. mal herausgeführt um kompatibel mit anderen Arduino Modellen zu sein Damit I2C an den verschiedenen Modellen an den gleichen Pins ist.

Am Mega sind SDA und SCL an den Pins 20 und 21. Darum diese beiden Pins freilassen.

Grüße Uwe

Die Pins waren nur exemplarisch verwendet, aber danke für den Tipp!

Kurz dazu: im Test auf dem Breadboard hat es funktioniert die Anschlüsse zu nutzen. Aber ich werde es im Hinterkopf behalten. Wenn ich Zuhause bin kann ich wegen mir auch einmal den Quellcode teilen, damit ihr seht, worum es geht😅

#include <Wire.h>                           // Kommunikation über I2C
#include <LiquidCrystal_I2C.h>              // LCD Bibliothek

#define LED1_PIN 22                         // LED für Betrieb
#define LED2_PIN 23                         // LED für Abtropfschale

#define BUTTON1_PIN 34                      // Drucktaster für Zitronensaft
#define BUTTON2_PIN 32                      // Drucktaster für Vodka
#define BUTTON3_PIN 33                      // Drucktaster für O-Saft
#define BUTTON4_PIN 30                      // Drucktaster für Grenadine
#define BUTTON5_PIN 31                      // Drucktaster für Tequila
#define BUTTON6_PIN 28                      // Drucktaster für Cranberrysaft
#define BUTTON7_PIN 29                      // Drucktaster für Pfirsichlikör
#define BUTTON8_PIN 26                      // Drucktaster für Wasser
#define BUTTON9_PIN 27                      // Drucktaster für Reset
#define BUTTON10_PIN 24                     // Drucktaster für Abbruch
#define BUTTON11_PIN 25                     // Drucktaster für Dorfmische (Vodka-O -> 50/50)
#define BUTTON12_PIN 53                     // Drucktaster für Wartung
#define BUTTON13_PIN 47                     // Drucktaster für Reinigung

#define POTI_PIN A0                         // Poti zum Einstellen der Ausgabemenge

#define DRUCKSENSOR_PIN 44                  // Glas unter Auslauf

#define BUZZER_PIN 45                       // Pieper

#define ABTROPFSENSOR_PIN 46                // zwei unabhängige Drähte in einer Schüssel, wenn gewissen Füllstand erreicht, wird der Stromkreis geschlossen

#define RELAY1_PIN 35                       // Relais 
#define RELAY2_PIN 36
#define RELAY3_PIN 37
#define RELAY4_PIN 38
#define RELAY5_PIN 39
#define RELAY6_PIN 40
#define RELAY7_PIN 41
#define RELAY8_PIN 42

#define LCD_ADDRESS 0x27                                    // Definieren der Adresse
#define LCD_COLUMNS 20                                      // Definieren der Anzahl der Zeichen
#define LCD_ROWS    4                                       // Definieren der Anzahl der Zeilen
LiquidCrystal_I2C lcd(LCD_ADDRESS, LCD_COLUMNS, LCD_ROWS);  // Initialisiere das I2C-LCD mit der definierten Adresse und Größe

int PotiWert = 0;                                           // Poti/Ausgabelänge
unsigned long AusgabeZeit = 0;                              // Ausgabezeit
unsigned long HalbeAusgabeZeit = 0;                         // Ausgabezeit für 50/50 Mische

long Fuellstand1 = 0;                                       // Zähler1 für ml Ausgabe in Sekunden
long Fuellstand2 = 0;                  
long Fuellstand3 = 0;                  
long Fuellstand4 = 0;                  
long Fuellstand5 = 0;                  
long Fuellstand6 = 0;                  
long Fuellstand7 = 0;                  
long Fuellstand8 = 0;                  

int EchterFuellstand1 = 0;                                  // tatsächlicher Füllstand, 
int EchterFuellstand2 = 0;
int EchterFuellstand3 = 0;
int EchterFuellstand4 = 0;
int EchterFuellstand5 = 0;
int EchterFuellstand6 = 0;
int EchterFuellstand7 = 0;
int EchterFuellstand8 = 0;

// bitte niemals über 999ml eingeben, da sonst das Display Probleme macht.
// Maximaler Füllstand in ml
int MaximalMenge1 = 700;      // Zitronensaft         
int MaximalMenge2 = 700;      // Vodka
int MaximalMenge3 = 700;      // O-Saft
int MaximalMenge4 = 700;      // Grenadine
int MaximalMenge5 = 700;      // Tequila
int MaximalMenge6 = 700;      // Cranberrysaft
int MaximalMenge7 = 700;      // Pfirsichlikör
int MaximalMenge8 = 999;      // Wasser

int TrinkzielWasser = 1000;   // Trinkziel Wasser: 1L (1000ml)

int Bezuege = 0;              // Zählervariable, die hochgezählt wird, nach jedem Bezug 1-8 oder Dorfmische

bool Reset = false;           // wichtige Variable, die dafür sorgt, dass beim resetten nicht ausversehen ein Bezug startet

unsigned long InaktivitaetsTimer = 0;             // Inaktivitätstimer

unsigned long ReinigungsDauer = 10UL*1000*60;     // 10 min Reinigung/ Durchlaufen aller Pumpen


void setup() 
{
  Serial.begin(9600);                             // Initialisiere die serielle Kommunikation, Alternativ wenn zu langsam 115200 nehmen wenn Komplikationen wieder 9600
  lcd.init();                                     // Initialisiere das LCD
  lcd.backlight();                                // Schalte die Hintergrundbeleuchtung ein
  lcd.clear();
  lcd.setCursor(0, 0);                            // Erstes Textfeld oben links
  lcd.print("Laden...");                          // muss oben stehen, damit das überhaupt angezeigt wird, da es sonst durch das Hauptprogramm in "Bereit" über geht


  pinMode(LED1_PIN, OUTPUT);                      // LEDs
  pinMode(LED2_PIN, OUTPUT);
  pinMode(BUTTON1_PIN, INPUT);                    // Taster
  pinMode(BUTTON2_PIN, INPUT);
  pinMode(BUTTON3_PIN, INPUT);
  pinMode(BUTTON4_PIN, INPUT);
  pinMode(BUTTON5_PIN, INPUT);
  pinMode(BUTTON6_PIN, INPUT);
  pinMode(BUTTON7_PIN, INPUT);
  pinMode(BUTTON8_PIN, INPUT);
  pinMode(BUTTON9_PIN, INPUT);                    // Reset
  pinMode(BUTTON10_PIN, INPUT);                   // Abbruch
  pinMode(BUTTON11_PIN, INPUT);                   // Mische
  pinMode(BUTTON12_PIN, INPUT);                   // Wartung
  pinMode(BUTTON13_PIN, INPUT);                   // Reinigung
  pinMode(DRUCKSENSOR_PIN, INPUT);                // Drucksensor
  pinMode(ABTROPFSENSOR_PIN, INPUT);              // Abtropfschale
  pinMode(BUZZER_PIN, OUTPUT);                    // Buzzer
  pinMode(RELAY1_PIN, OUTPUT);                    // Relais
  pinMode(RELAY2_PIN, OUTPUT);
  pinMode(RELAY3_PIN, OUTPUT);
  pinMode(RELAY4_PIN, OUTPUT);
  pinMode(RELAY5_PIN, OUTPUT);
  pinMode(RELAY6_PIN, OUTPUT);
  pinMode(RELAY7_PIN, OUTPUT);
  pinMode(RELAY8_PIN, OUTPUT);


  digitalWrite(RELAY1_PIN, LOW);                  // einmal vorab alle Relais auf Ausgangszustand setzen
  digitalWrite(RELAY2_PIN, LOW);
  digitalWrite(RELAY3_PIN, LOW);
  digitalWrite(RELAY4_PIN, LOW);
  digitalWrite(RELAY5_PIN, LOW);
  digitalWrite(RELAY6_PIN, LOW);
  digitalWrite(RELAY7_PIN, LOW);
  digitalWrite(RELAY8_PIN, LOW);

  delay(500);                                     // Delay soll einfach dafür sogen, dass es den klassischen anschein hat, die Anlage müsse hochfahren
  lcd.clear();                                    // Display einmal ganz leeren
  lcd.setCursor(0, 0);                            // Erstes Textfeld oben links
  lcd.print("Bereit!");                           // muss ganz unten im Setup stehen, damit klar ist, dass das Setup durch ist
  tone(BUZZER_PIN, 5000, 500);                    // Piepton, danit man weiß, dass Anlage betriebsbereit ist
}


// TasterHandhabung
void TasterHandhabung(int TasterPin, unsigned long ausgabeZeit, int relayPin, const char* bezugName, long &fuellstand, long echterFuellstand)
// Diese Funktion steuert die Ausgabe eines Getränks über einen bestimmten Taster.
// Die Ausgabe erfolgt nur, wenn bestimmte Bedingungen erfüllt sind (z.B. Taster gedrückt, Glas unter Ausgabe, Abtropfschale nicht voll).
// Eingabeparameter:
// - TasterPin: Der Pin, an dem der Taster angeschlossen ist.
// - ausgabeZeit: Dauer der Ausgabe in Millisekunden.
// - relayPin: Der Pin, der das Relais für die Getränkeförderung steuert.
// - bezugName: Name des Getränks, das ausgegeben wird, zur Anzeige auf dem LCD.
// - fuellstand: Referenz auf den aktuellen Füllstand des ausgegebenen Getränks.
// - echterFuellstand: Der tatsächliche Füllstand des Getränks vor der Ausgabe.
{
  // Überprüfung, ob der Taster gedrückt ist, das Glas unter der Ausgabe ist, die Abtropfschale nicht voll ist und kein Reset erfolgt ist.
  if (digitalRead(TasterPin) == HIGH && digitalRead(DRUCKSENSOR_PIN) == HIGH && digitalRead(ABTROPFSENSOR_PIN) == LOW && !Reset)
  {
    InaktivitaetsTimer = millis(); 						          // Aktualisierung des Inaktivitäts-Timers, um die Aktivität zu erfassen.
    Bezuege++; 									                        // Erhöhen des Bezugszähler um 1, um die Anzahl der Ausgaben zu verfolgen.
    lcd.clear(); 								                        // LCD-Display löschen.
    lcd.setCursor(0, 0);                            
    lcd.print(bezugName); 							                // Anzeige des Getränkenamens auf dem LCD, der Name wird automatisch aus dem Hauptprogramm übernommen.
    lcd.setCursor(0, 1);
    lcd.print("Verbleibende Zeit:"); 						        // Anzeige der verbleibenden Zeit für die Ausgabe.
    unsigned long BeginnAusgabe = millis(); 				  	// Startzeitpunkt der Ausgabe speichern.
    // Schleife, die die Ausgabe steuert, bis die festgelegte Ausgabezeit erreicht ist.
    while (millis() - BeginnAusgabe < ausgabeZeit)
    {
      // Überprüfung, ob der Abbruchknopf gedrückt wurde oder die Abtropfschale voll ist.
      if (digitalRead(BUTTON10_PIN) == HIGH || digitalRead(ABTROPFSENSOR_PIN) == HIGH)
      {  
        break; // Vorzeitiger Abbruch der Ausgabe, die Schleife wird beendet und die Ausgabe gestoppt.
      }
      unsigned long vergangeneZeit = millis() - BeginnAusgabe; 			    // Berechnung der vergangenen Zeit seit Beginn der Ausgabe.
      unsigned long verbleibendeZeit = ausgabeZeit - vergangeneZeit; 		// Berechnung der verbleibenden Zeit für die Ausgabe.
      lcd.setCursor(0, 2);
      lcd.print(verbleibendeZeit / 1000); 					                    // Anzeige der verbleibenden Zeit in Sekunden.
      lcd.print(" Sekunden");
      lcd.display(); 								                                    // Aktualisieren des LCD-Displays.
      digitalWrite(LED1_PIN, HIGH); 					                        	// LED einschalten, um die aktive Ausgabe anzuzeigen.
      digitalWrite(relayPin, HIGH); 						                        // Einschalten des Relais, um das Getränk auszugeben.
    }  
    unsigned long vergangeneZeit = millis() - BeginnAusgabe; 			      // Erneute Berechnung der vergangenen Zeit nach der Ausgabe.
    fuellstand += vergangeneZeit; 						                          // Hinzufügen der vergangenen Zeit zum Füllstand des Getränks.
    digitalWrite(LED1_PIN, LOW); 						                            // LED ausschalten, um das Ende der Ausgabe anzuzeigen.
    digitalWrite(relayPin, LOW); 						                            // Relais ausschalten, um die Getränkeförderung zu stoppen.
    tone(BUZZER_PIN, 5000, 500); 						                            // Kurzer Piepton als akustisches Signal am Ende der Ausgabe.
    lcd.clear(); 								                                        // LCD-Display löschen.
    lcd.setCursor(0, 0);
    lcd.print("Verbleibende Menge:"); 						                      // Anzeige des verbleibenden Füllstands des Getränks.
    lcd.setCursor(0, 1);
    berechneEchterFuellstand(); 						                            // Berechnung des tatsächlichen Füllstands nach der Ausgabe.
    lcd.print(echterFuellstand - (fuellstand / 1000)); 				          // Anzeige des verbleibenden Füllstands in Millilitern. 
    lcd.print(" ml");
    delay(3000); 								                                        // Kurze Verzögerung von 3 Sekunden, um dem Benutzer Zeit zu geben, die Anzeige zu lesen.
    lcd.clear(); 								                                        
    lcd.setCursor(0, 0);                            
    lcd.print("Bereit!"); 							                                // Anzeige, dass das System bereit für die nächste Ausgabe ist.                      
  }
}


void Dorfmische(unsigned long ausgabeZeit, long &fuellstand2, long &fuellstand3)
// Funktion zur Ausgabe einer "Dorfmische", einer Mischung von zwei Getränken.
// Die Ausgabe erfolgt nur, wenn bestimmte Bedingungen erfüllt sind.
// Eingabeparameter:
// - ausgabeZeit: Die Dauer, für die die Ausgabe erfolgt (in Millisekunden).
// - fuellstand2: Referenz auf den Füllstand des ersten Getränks (z. B. Vodka).
// - fuellstand3: Referenz auf den Füllstand des zweiten Getränks (z. B. Orangensaft).
{
  // Überprüfen, ob der Startknopf gedrückt ist, der Drucksensor aktiv ist, 
  // die Abtropfschale leer ist und kein Reset erfolgt ist.
  if (digitalRead(BUTTON11_PIN) == HIGH && digitalRead(DRUCKSENSOR_PIN) == HIGH && digitalRead(ABTROPFSENSOR_PIN) == LOW && !Reset) 
  { 
    InaktivitaetsTimer = millis(); 						        // Aktualisieren des Inaktivitäts-Timers
    Bezuege += 2; 								                    // Erhöhen des Bezugszähler
    unsigned long BeginnAusgabe = millis(); 					// Startzeitpunkt der Ausgabe speichern
    lcd.clear();
    lcd.setCursor(0, 0);      							          // erste Zeile                      
    lcd.print("Dorfmische"); 							            // Anzeige des Getränkenamens
    lcd.setCursor(0, 1);							                // zweite Zeile
    lcd.print("Verbleibende Zeit:");
    // Schleife zur Steuerung der Ausgabe, bis die vorgegebene Ausgabezeit erreicht ist
    while (millis() - BeginnAusgabe < ausgabeZeit)  
    {
      // Überprüfung, ob der Abbruchknopf gedrückt wurde oder die Abtropfschale voll ist
      if (digitalRead(BUTTON10_PIN) == HIGH || digitalRead(ABTROPFSENSOR_PIN) == HIGH)    
      {  
        break; // Abbrechen der Ausgabe
      }
      unsigned long vergangeneZeit = millis() - BeginnAusgabe; 			      // Vergangene Zeit seit Beginn der Ausgabe
      unsigned long verbleibendeZeit = ausgabeZeit - vergangeneZeit; 	  	// Berechnung der verbleibenden Zeit
      lcd.setCursor(0, 2);
      lcd.print(verbleibendeZeit / 1000); 					                      // Verbleibende Zeit in Sekunden anzeigen
      lcd.print(" Sekunden");
      lcd.display();
      digitalWrite(LED1_PIN, HIGH); 						                          // LED einschalten
      digitalWrite(RELAY2_PIN, HIGH); 						                        // Relais für erstes Getränk einschalten
      digitalWrite(RELAY3_PIN, HIGH); 						                        // Relais für zweites Getränk einschalten
    }
    unsigned long vergangeneZeit = millis() - BeginnAusgabe; 			        // Gesamte vergangene Zeit der Ausgabe
    fuellstand2 += vergangeneZeit; 						                            // Aktualisierung des Füllstands für das erste Getränk
    fuellstand3 += vergangeneZeit; 						                            // Aktualisierung des Füllstands für das zweite Getränk
    digitalWrite(LED1_PIN, LOW); 						                              // LED ausschalten
    digitalWrite(RELAY2_PIN, LOW); 						                            // Relais für erstes Getränk ausschalten
    digitalWrite(RELAY3_PIN, LOW); 						                            // Relais für zweites Getränk ausschalten
    tone(BUZZER_PIN, 5000, 500); 						                              // Kurzer Piepton als Signal am Ende der Ausgabe
    berechneEchterFuellstand(); 						                              // Berechnung des tatsächlichen Füllstands der Getränke
    lcd.clear();
    lcd.setCursor(0, 0);                            
    lcd.print("Rest Vodka:");
    lcd.setCursor(0, 1);                            
    lcd.print(EchterFuellstand2 - fuellstand2 / 1000); 				            // Anzeigen des verbleibenden Füllstands von Vodka in Millilitern
    lcd.print(" ml");
    lcd.setCursor(0, 2);                            
    lcd.print("Rest O-Saft:");
    lcd.setCursor(0, 3);                            
    lcd.print(EchterFuellstand3 - fuellstand3 / 1000); 				            // Anzeigen des verbleibenden Füllstands von Orangensaft in Millilitern
    lcd.print(" ml");
    delay(4000); 								                                          // Wartezeit von 4 Sekunden, bevor das Display zurückgesetzt wird
    lcd.clear();
    lcd.setCursor(0, 0);                            
    lcd.print("Bereit!"); 							                                  // Anzeige, dass das System wieder bereit ist
  }
}


// Reinigungsprogramm
void Reinigung()
// Die Funktion `Reinigung` steuert den Reinigungsprozess des Geräts.
// Sie prüft, ob die Bedingungen für die Reinigung erfüllt sind, und führt diese dann durch.
{
  if (digitalRead(BUTTON13_PIN) == HIGH && digitalRead(ABTROPFSENSOR_PIN) == LOW)         // Überprüft, ob der Reinigungsbutton gedrückt und die Abtropfschale leer ist.
  {
    InaktivitaetsTimer = millis();  						                                          // Setzt den Inaktivitätstimer auf die aktuelle Zeit 
    unsigned long BeginnAusgabe = millis();  					                                    // Startzeit der Reinigung wird festgehalten
    while (millis() - BeginnAusgabe < ReinigungsDauer)                                    // Diese Schleife läuft für die Dauer der Reinigung, es sei denn, ein Abbruch wird festgestellt.
    {
      if (digitalRead(BUTTON10_PIN) == HIGH || digitalRead(ABTROPFSENSOR_PIN) == HIGH)    // Überprüfung des Abbruchschalters und der Abtropfschale während der Ausgabezeit
      {  
        break; // Wenn ein Abbruch festgestellt wird, wird die Schleife vorzeitig beendet.
      }
      // Aktiviert die Relais für die Reinigungsvorgänge
      digitalWrite(RELAY1_PIN, HIGH);
      digitalWrite(RELAY2_PIN, HIGH);
      digitalWrite(RELAY3_PIN, HIGH);
      digitalWrite(RELAY4_PIN, HIGH);
      digitalWrite(RELAY5_PIN, HIGH);
      digitalWrite(RELAY6_PIN, HIGH);
      digitalWrite(RELAY7_PIN, HIGH);
      digitalWrite(RELAY8_PIN, HIGH);
    }
    // Schaltet alle Relais wieder aus, nachdem die Reinigung beendet ist oder abgebrochen wurde.
    digitalWrite(RELAY1_PIN, LOW);
    digitalWrite(RELAY2_PIN, LOW);
    digitalWrite(RELAY3_PIN, LOW);
    digitalWrite(RELAY4_PIN, LOW);
    digitalWrite(RELAY5_PIN, LOW);
    digitalWrite(RELAY6_PIN, LOW);
    digitalWrite(RELAY7_PIN, LOW);
    digitalWrite(RELAY8_PIN, LOW);
    tone(BUZZER_PIN, 10000, 500); 						 // Gibt einen Signalton aus, um anzuzeigen, dass die Reinigung beendet ist.
  }
}


// Füllstandswarnung /-überprüfung
void FuellstandWarnung(long echterFuellstand, unsigned long maxMenge, const char* bezugName) // Diese Funktion hat keine Rückgabeparameter.
// Die Funktion `FuellstandWarnung` überprüft den aktuellen Füllstand und zeigt eine Warnung auf dem LCD an, wenn der Füllstand unter 50 liegt.
// Eingabeparameter:
// - echterFuellstand (long): Der aktuelle Füllstand, der überprüft werden soll.
// - maxMenge (unsigned long): Die maximale Menge, die für den aktuellen Bezug erlaubt ist (nicht verwendet in dieser Funktion).
// - bezugName (const char*): Der Name des Getränks oder der Ressource, deren Füllstand überprüft wird.
{
  if (echterFuellstand < 50)  							 // Wenn der Füllstand unter 50 liegt, wird eine Warnung auf dem LCD angezeigt.
  {
    lcd.setCursor(0, 1);                     // Setzt den Cursor auf die zweite Zeile des LCDs
    lcd.print(bezugName);                    // Zeigt den Namen der Ressource oder des Getränks an
    lcd.setCursor(0, 2);                     // Setzt den Cursor auf die dritte Zeile des LCDs
    lcd.print("ist leer");                   // Zeigt die Warnung "ist leer" an
  } 
}


// Reinigungsempfehlung
void checkReinigung()
{
  if(Bezuege == 30)
  {
    tone(BUZZER_PIN, 7000, 500);
    lcd.clear();
    lcd.setCursor(0, 0);                            
    lcd.print("denk an Reinigung");
    lcd.setCursor(0, 1);                            
    lcd.print("und Abtropfschale!");
    delay(2000);
    Bezuege ++;
    lcd.clear();
    lcd.setCursor(0, 0);                            
    lcd.print("Bereit!");
  }
}


// Resetfunktion der Füllstände 1-7 
void Auffuellen(int TasterPin, long &fuellstand)
// Die Resetfunktion ist zum Resetten der Füllstände, damit man nach dem Wechseln einer Flasche den neuen Füllstand wieeder drin hat.
// Umgesetzt bzw. wichtig war: sperren anderer Funktionen, damit diese nicht ausversehen starten und da das Drücken ebenfalls eine Aktion darstellt, sollte der Inaktivitätstimer auf Null zurückgesetzt werden.
{
  if(digitalRead(BUTTON9_PIN) == HIGH)  // Startbedingung
  {
    InaktivitaetsTimer = millis();      // Rücksetzen des Inaktivitätstimers auf Null
    Reset = true;                       // Sperrvariable, als Bedingung in anderen Codeabschnitten, solange true, wie der Taster gedrückt ist
  }
  else
  {
    Reset = false;                      // Sperrvariable zurücksetzen, wenn der Taster nicht gedrückt ist
  }
  if(digitalRead(BUTTON9_PIN) == HIGH && digitalRead(TasterPin) == HIGH)
  {
    fuellstand = 0;                     // hier passiert das eigentliche zurücksetzen der Füllstände, entsprechend der Eingabe
  }
}


// Resetfunktion Wasser (Füllstand 8)
void WasserAuffuellen(long &fuellstand)
// hier ist das selbe umgesetzt wie in void Auffuellen, nur um einen Abschnitt erweitert, dass es eine weitere Variable gibt, die dafür sorgt, dass ein beliebiges Trinkziel oberhalb von der Füllmenge einer Flasche Wasser erreicht werden kann.
{
  if(digitalRead(BUTTON9_PIN) == HIGH)
  {
    InaktivitaetsTimer = millis();
    Reset = true;
  }
  else
  {
    Reset = false;
  }
  if(digitalRead(BUTTON9_PIN) == HIGH && digitalRead(BUTTON8_PIN) == HIGH)
  {
    TrinkzielWasser -= fuellstand; // Hier wird die Variable um das inkremental verringert, was der Flasche tatsächlich entnommen wurde, auch wenn schon z.B. nach der Hälfte einer Flasche diese gewechselt würde
    fuellstand = 0;
  }
}


// Wartungsprogramm
void Wartung()
{
  if(digitalRead(BUTTON12_PIN) == HIGH)
  {
    InaktivitaetsTimer = millis();
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Bei Problemen:");
    lcd.setCursor(0, 1);
    lcd.print("Bitte 123456667890");
    lcd.setCursor(0, 2);
    lcd.print("anrufen!");
    lcd.setCursor(0, 3);
    lcd.print("Bezuege: ");
    lcd.print(Bezuege);
    delay(3000);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Zitronensaft: ");
    lcd.print(EchterFuellstand1);
    lcd.print("ml");
    lcd.setCursor(0, 1);
    lcd.print("Vodka: ");
    lcd.print(EchterFuellstand2);
    lcd.print("ml");
    lcd.setCursor(0, 2);
    lcd.print("O-Saft: ");
    lcd.print(EchterFuellstand3);
    lcd.print("ml");
    lcd.setCursor(0, 3);
    lcd.print("Grenadine: ");
    lcd.print(EchterFuellstand4);
    lcd.print("ml");
    delay(4000);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Tequila: ");
    lcd.print(EchterFuellstand5);
    lcd.print("ml");
    lcd.setCursor(0, 1);
    lcd.print("Cranberrysaft: ");
    lcd.print(EchterFuellstand6);
    lcd.print("ml");
    lcd.setCursor(0, 2);
    lcd.print("Pfirsichlikor: ");
    lcd.print(EchterFuellstand7);
    lcd.print("ml");
    lcd.setCursor(0, 3);
    lcd.print("Wasser: ");
    lcd.print(EchterFuellstand8);
    lcd.print("ml");
    delay(4000);
    lcd.clear();
    lcd.setCursor(0, 0);                            
    lcd.print("Bereit!");
  }
}


// Inaktivitätstimer
void checkAktivitaet()
{
  if(millis() - InaktivitaetsTimer >= 10UL * 60 * 1000)
  {
    tone(BUZZER_PIN, 7000, 500);  // der Ton ist echt sehr grell und nervig
    delay(1000);
    tone(BUZZER_PIN, 7000, 500);
    delay(1000);
    tone(BUZZER_PIN, 7000, 500);
    delay(1000);
    tone(BUZZER_PIN, 7000, 500);
    delay(1000);
    tone(BUZZER_PIN, 7000, 500);
    delay(1000);
    tone(BUZZER_PIN, 7000, 500);
    delay(1000);
    tone(BUZZER_PIN, 7000, 500);
    delay(1000);
    tone(BUZZER_PIN, 7000, 500);
    delay(1000);
    tone(BUZZER_PIN, 7000, 500);
    delay(1000);
    tone(BUZZER_PIN, 7000, 500);
    delay(1000);
    InaktivitaetsTimer = millis();
  }
}


// tatsächlichen Füllstand berechnen
void berechneEchterFuellstand()
{
  EchterFuellstand1 = MaximalMenge1 - Fuellstand1 / 1000; // Annahme: 1ml/sek deswegen die 1000
  EchterFuellstand2 = MaximalMenge2 - Fuellstand2 / 1000;
  EchterFuellstand3 = MaximalMenge3 - Fuellstand3 / 1000;
  EchterFuellstand4 = MaximalMenge4 - Fuellstand4 / 1000;
  EchterFuellstand5 = MaximalMenge5 - Fuellstand5 / 1000;
  EchterFuellstand6 = MaximalMenge6 - Fuellstand6 / 1000;
  EchterFuellstand7 = MaximalMenge7 - Fuellstand7 / 1000;
  EchterFuellstand8 = MaximalMenge8 - Fuellstand8 / 1000;
}


// Trinkziel für Wasser überprüfen
void checkTrinkzielWasser()
// Funktion sorft dafür, dass wenn genug Wasser entnommen wurde, die meldung "Trinkziel erreicht!"" auf dem Display auftaucht.
{
  if(TrinkzielWasser - (Fuellstand8 / 1000) < 0)
  {
    lcd.setCursor(0, 3);                        // in der untersten Zeile (4) erscheint die Meldung                        
    lcd.print("Trinkziel erreicht!");
  }
}


// Vorschau Ausgabemenge
void zeigeAusgabemenge()
// Die Funktion verbessert das Bedienen der Anlage erheblich, da man jetzt vorher weiß, wie viel anschließend aus der Maschine kommt.
{
  lcd.setCursor(0, 1);            // in der zweiten Zeile, unter Bereit!, wird die ausgewählte Füllmenge angezeigt
  lcd.print("Ausgabemenge: ");
  lcd.print(AusgabeZeit / 1000);
  lcd.print("ml ");              // das Leerzeichen ist sehr wichtig, da ansonsten das "l" nicht überschrieben wird, wenn die Ausgabemenge unter 10 ml fällt
}


// Hauptprogramm
void loop()                                          
{
  // Poti und Umrechnung
  PotiWert = analogRead(POTI_PIN);                    // Wert vom Poti auslesen
  AusgabeZeit = map(PotiWert, 0, 1023, 0, 200000);    // Potentiometer auf 200ml = 200 000 ms, als maximale Füllmenge eingestellt.
  HalbeAusgabeZeit = (AusgabeZeit / 2);               // Ausgabezeit nur für Dorfmische relevant.
  zeigeAusgabemenge();                                // Vorschau für anstehende Ausgabemenge

  // Abtropfschale-Sensor und LED
  if (digitalRead(ABTROPFSENSOR_PIN) == HIGH) 
  {
    digitalWrite(LED2_PIN, HIGH);
    lcd.setCursor(0, 0);                            
    lcd.print("Abtropfschale leeren");
    delay(1000);                      // dadurch flackert es "cool" das ist kein Bug sondern ein Feature:D
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Bereit!");  
  } 
  else 
  {
    digitalWrite(LED2_PIN, LOW);
  }
  
  // Tasterhandhabung aufrufen
  TasterHandhabung(BUTTON1_PIN, AusgabeZeit, RELAY1_PIN, "Zitronensaft", Fuellstand1, EchterFuellstand1);                  
  TasterHandhabung(BUTTON2_PIN, AusgabeZeit, RELAY2_PIN, "Vodka", Fuellstand2, EchterFuellstand2); 
  TasterHandhabung(BUTTON3_PIN, AusgabeZeit, RELAY3_PIN, "O-Saft", Fuellstand3, EchterFuellstand3);
  TasterHandhabung(BUTTON4_PIN, AusgabeZeit, RELAY4_PIN, "Grenadine", Fuellstand4, EchterFuellstand4);
  TasterHandhabung(BUTTON5_PIN, AusgabeZeit, RELAY5_PIN, "Tequila", Fuellstand5, EchterFuellstand5);
  TasterHandhabung(BUTTON6_PIN, AusgabeZeit, RELAY6_PIN, "Cranberrysaft", Fuellstand6, EchterFuellstand6);
  TasterHandhabung(BUTTON7_PIN, AusgabeZeit, RELAY7_PIN, "Pfirsichlikoer", Fuellstand7, EchterFuellstand7);
  TasterHandhabung(BUTTON8_PIN, AusgabeZeit, RELAY8_PIN, "Wasser", Fuellstand8, EchterFuellstand8);

  Dorfmische(HalbeAusgabeZeit, Fuellstand2, Fuellstand3);

  berechneEchterFuellstand();

  // Resetfunktion aufrufen
  Auffuellen(BUTTON1_PIN, Fuellstand1);
  Auffuellen(BUTTON2_PIN, Fuellstand2);
  Auffuellen(BUTTON3_PIN, Fuellstand3);
  Auffuellen(BUTTON4_PIN, Fuellstand4);
  Auffuellen(BUTTON5_PIN, Fuellstand5);
  Auffuellen(BUTTON6_PIN, Fuellstand6);
  Auffuellen(BUTTON7_PIN, Fuellstand7);
  WasserAuffuellen(Fuellstand8);

  FuellstandWarnung(EchterFuellstand1, MaximalMenge1, "Zitronensaft");
  FuellstandWarnung(EchterFuellstand2, MaximalMenge2, "Vodka");
  FuellstandWarnung(EchterFuellstand3, MaximalMenge3, "O-Saft");
  FuellstandWarnung(EchterFuellstand4, MaximalMenge4, "Grenadine");
  FuellstandWarnung(EchterFuellstand5, MaximalMenge5, "Tequila");
  FuellstandWarnung(EchterFuellstand6, MaximalMenge6, "Cranberrysaft");
  FuellstandWarnung(EchterFuellstand7, MaximalMenge7, "Pfirsichlikoer");
  FuellstandWarnung(EchterFuellstand8, MaximalMenge8, "Wasser");
  
  Wartung();

  Reinigung();              

  checkReinigung();         // überprüfen, ob schon 30 Bezüge erfolgt sind
  checkAktivitaet();        // überprüfen, ob Inaktivitätszeit überschritten wurde
  checkTrinkzielWasser();   // überprüfen, ob Mindest-/Zielmenge Wasser getrunken wurde
}
                         

Nicht wundern, dass so viel und oft doppelt kommentiert wurde, das ist eine Vorgabe der Uni. Einige Features mussten wir uns aus dem Allerwertesten ziehen, da wir sonst nicht genug Zielkriterien hätten.

Update bezüglich der Schaltung: Ich habe jetzt das Display und die Relaiskarte aus einer externen Stromquelle mit 5V gespeist, den Arduino speise ich jetzt via Vin mit 6,7V und den Rest wie gehabt. Jetzt läuft es!

Nun war ich wieder voreilig und habe ein paar nervige Fehler entdeckt: Einige Programmabschnitte, die zum starten nur eine Bedingung erfordern, starten zufällig von alleine. Da ich noch keine Taster mit Pull-Down eingebaut habe, glaube ich die PINs floaten einfach. Eine Berührung der Platine mit dem Finger reicht aus um es auszulösen :slight_smile:
Das Display läuft einwandfrei und ohne zu zicken. die Kabellänge sind übrigens 100cm, da hatte ich mich wohl ein wenig verschätzt :smiley: Es mag aber auch daran liegen, dass ich 0,5mm^2 Leitungen nutzen aber keine Ahnung.

Außerdem einmal der Klemmplan (ohne Brücken, aber die blauen sind alle verbunden, die schwarzen auch und bei den roten alle, bis auf die letzte):

zu guter Letzt sollte ich noch sagen, dass ich mit dem Code noch nicht fertig bin und noch an einigen Stellen optimierungsbedarf sehe. Wenn ihr da aber noch grobe Fehler seht oder anstehende Komplikationen, gerne melden :slight_smile: