Arduino Carrera-Bahn Projekt Rundenzeit Counter mit LCD

Ich weiß nicht, wie das bei euch ist, aber sobald dicke Regenschwaden über das Land ziehen und die Tage kürzer werden, wird es Zeit, die Carrera-Bahn aus dem Keller zu holen. Wie wäre es, wenn wir das gute Stück etwas upgraden würden? Z.B. mit Starting Lights, Stoppen der besten Rundenzeiten und einer Siegerehrung? Das folgende Projekt besteht aus Starting Lights mit fünf LEDs, drei LCD-Anzeigen für Spieler 1, Spieler 2 und eine Gesamtanzeige. Als Erstes stellt man die Rundenanzahl ein, drückt auf Start und der Countdown beginnt. Nun läuft das Rennen. Über Lichtschranken werden die Fahrzeuge getrackt. Dabei wird die aktuelle Runde, die Rundenzeit und die Zeit der schnellsten Runde auf den LCD-Displays für Spieler 1 und 2 ausgegeben. Das mittlere Display zeigt die Gesamtzeit an. In diesem Tutorial führe ich dich Schritt für Schritt durch den Arduino-Code. Lets go! Bauteile 1x 1x 3x LCD-Display (Ja, das D steht schon für Display, liest sich so aber besser ;-) ) 5x LED Rot (kann man auch parallel schalten, um Reihen zu realisieren. 5x Wiederstand 220 Ohm 3x Taster 1x Potentiometer z.B. 10k – 100k 2x IR Lichtschranken, Reflektierend (ich habe ähnliche verwendet, nicht genau die hier) Schaltung Arduino-Code #include <Wire.h> #include <LiquidCrystal_I2C.h> const int lightBarrier1Pin = 2; const int lightBarrier2Pin = 3; const int resetButtonPin = 4; const int potPin = 0; const int startLightPins[] = { 7, 8, 9, 10, 11 }; const int numStartLights = 5; int lightBarrier1State = 0; int lightBarrier2State = 0; int lightBarrier1LastState = 0; int lightBarrier2LastState = 0; int totalLaps = 10; int lapsPlayer1 = 0; int lapsPlayer2 = 0; LiquidCrystal_I2C lcd1(0x25, 16, 2); LiquidCrystal_I2C lcd2(0x27, 16, 2); LiquidCrystal_I2C lcd3(0x26, 16, 2); int theState = 1; unsigned long startTime = 0; unsigned long lapStartTimePlayer1 = 0; unsigned long bestLapPlayer1 = 0; unsigned long lapStartTimePlayer2 = 0; unsigned long bestLapPlayer2 = 0; unsigned long totalTime = 0; boolean interruptP1Triggered = false; boolean interruptP2Triggered = false; long theMinutes, theSeconds, theMillis; void setup() { Serial.begin(115200); pinMode(lightBarrier1Pin, INPUT_PULLUP); pinMode(lightBarrier2Pin, INPUT_PULLUP); pinMode(resetButtonPin, INPUT_PULLUP); lcd1.init(); lcd2.init(); lcd3.init(); lcd1.backlight(); lcd2.backlight(); lcd3.backlight(); for (int i = 0; i < numStartLights; i++) { pinMode(startLightPins[i], OUTPUT); } attachInterrupt(digitalPinToInterrupt(lightBarrier1Pin), lightBarrier1Interrupt, FALLING); attachInterrupt(digitalPinToInterrupt(lightBarrier2Pin), lightBarrier2Interrupt, FALLING); startTime = millis(); bestLapPlayer1 = 0; bestLapPlayer2 = 0; } void loop() { switch (theState) { case 0: // idle break; case 1: // select laps select_laps(); if (digitalRead(resetButtonPin) == LOW) { theState = 2; clear_game(); delay(500); } break; case 2: // get Ready blink 3 times get_ready(); break; case 3: // start lights count down start_lights(); break; case 4: // racing racing(); if (digitalRead(resetButtonPin) == LOW) { theState = 6; // Abbrechen clear_game(); delay(500); } break; case 5: // winner winner(); if (digitalRead(resetButtonPin) == LOW) { theState = 1; delay(500); lcds_clear(); } break; case 6: abort(); break; } delay(10); } void clear_game() { lcds_clear(); startTime = 0; lapStartTimePlayer1 = 0; bestLapPlayer1 = 0; lapStartTimePlayer2 = 0; bestLapPlayer2 = 0; totalTime = 0; lapsPlayer1 = 0 lapsPlayer2 = 0; } void lightBarrier1Interrupt() { interruptP1Triggered = true; } void lightBarrier2Interrupt() { interruptP2Triggered = true; } void select_laps() { lcd1.setCursor(0, 0); lcd1.print("Lap Count: "); int potValue = map(analogRead(potPin), 0, 1023, 0, 99); lcd1.setCursor(14, 0); lcd1.print(potValue < 10 ? " " : ""); lcd1.print(potValue); lcd1.setCursor(0, 1); lcd1.print("< Press Start >"); totalLaps = potValue; } void get_ready() { for (int i = 0; i < numStartLights; i++) { digitalWrite(startLightPins[i], HIGH); } lcd1.setCursor(0, 0); lcd1.print("GET READY!!!"); delay(2000); lcds_clear(); theState++; } void start_lights() { for (int i = 0; i < numStartLights; i++) { delay(1000); digitalWrite(startLightPins[i], LOW); } startTime = millis(); lapStartTimePlayer1 = millis(); lapStartTimePlayer2 = millis(); theState++; } void racing() { if (interruptP1Triggered == true) { lapsPlayer1++; lcd2.clear(); if ((bestLapPlayer1 > (millis() - lapStartTimePlayer1) || (bestLapPlayer1 == 0))) bestLapPlayer1 = millis() - lapStartTimePlayer1; lapStartTimePlayer1 = millis(); interruptP1Triggered = false; } if (interruptP2Triggered == true) { lapsPlayer2++; lcd3.clear(); if ((bestLapPlayer2 > (millis() - lapStartTimePlayer2) || (bestLapPlayer2 == 0))) bestLapPlayer2 = millis() - lapStartTimePlayer2; lapStartTimePlayer2 = millis(); interruptP2Triggered = false; } lcd1.setCursor(0, 0); lcd1.print("Time: "); long timeElapsed = millis() - startTime; convertMillisToTime(timeElapsed, theMinutes, theSeconds, theMillis); lcd1.print(theMinutes < 10 ? "0" : ""); lcd1.print(theMinutes); lcd1.print(":"); lcd1.print(theSeconds < 10 ? "0" : ""); lcd1.print(theSeconds); lcd1.print(":"); lcd1.print(theMillis); convertMillisToTime((millis() - lapStartTimePlayer1), theMinutes, theSeconds, theMillis); lcd2.setCursor(0, 0); lcd2.print(theMinutes < 10 ? "0" : ""); lcd2.print(theMinutes); lcd2.print(":"); lcd2.print(theSeconds < 10 ? "0" : ""); lcd2.print(theSeconds); lcd2.print(":"); lcd2.print(theMillis); lcd2.setCursor(14, 0); lcd2.print(lapsPlayer1 < 10 ? "0" : ""); lcd2.print(lapsPlayer1); convertMillisToTime(bestLapPlayer1, theMinutes, theSeconds, theMillis); lcd2.setCursor(0, 1); if (bestLapPlayer1 != 0) { lcd2.print(theMinutes < 10 ? "0" : ""); lcd2.print(theMinutes); lcd2.print(":"); lcd2.print(theSeconds < 10 ? "0" : ""); lcd2.print(theSeconds); lcd2.print(":"); lcd2.print(theMillis); lcd2.print(" < BEST"); } convertMillisToTime((millis() - lapStartTimePlayer2), theMinutes, theSeconds, theMillis); lcd3.setCursor(0, 0); lcd3.print(theMinutes < 10 ? "0" : ""); lcd3.print(theMinutes); lcd3.print(":"); lcd3.print(theSeconds < 10 ? "0" : ""); lcd3.print(theSeconds); lcd3.print(":"); lcd3.print(theMillis); lcd3.setCursor(14, 0); lcd3.print(lapsPlayer2 < 10 ? "0" : ""); lcd3.print(lapsPlayer2); convertMillisToTime(bestLapPlayer2, theMinutes, theSeconds, theMillis); lcd3.setCursor(0, 1); if (bestLapPlayer2 != 0) { lcd3.print(theMinutes < 10 ? "0" : ""); lcd3.print(theMinutes); lcd3.print(":"); lcd3.print(theSeconds < 10 ? "0" : ""); lcd3.print(theSeconds); lcd3.print(":"); lcd3.print(theMillis); lcd3.print(" < BEST"); } lightBarrier1LastState = lightBarrier1State; lightBarrier2LastState = lightBarrier2State; if ((lapsPlayer1 >= totalLaps) || (lapsPlayer2 >= totalLaps)) { totalTime = millis(); lcds_clear(); theState++; } } void convertMillisToTime(long milliseconds, long& minutes, long& seconds, long& millis) { minutes = milliseconds / 60000; seconds = (milliseconds / 1000) % 60; millis = milliseconds % 1000; } void winner() { lcd1.setCursor(0, 0); if (lapsPlayer1 >= totalLaps) { convertMillisToTime(bestLapPlayer1, theMinutes, theSeconds, theMillis); lcd1.print("BEST: "); lcd1.print(theMinutes < 10 ? "0" : ""); lcd1.print(theMinutes); lcd1.print(":"); lcd1.print(theSeconds < 10 ? "0" : ""); lcd1.print(theSeconds); lcd1.print(":"); lcd1.print(theMillis); if (millis() % 1000 < 500) { lcd2.clear(); } else { lcd2.setCursor(0, 0); lcd2.print("Congratulations"); lcd2.setCursor(0, 1); lcd2.print(" Player 1"); } } else { convertMillisToTime(bestLapPlayer2, theMinutes, theSeconds, theMillis); lcd1.print("BEST: "); lcd1.print(theMinutes < 10 ? "0" : ""); lcd1.print(theMinutes); lcd1.print(":"); lcd1.print(theSeconds < 10 ? "0" : ""); lcd1.print(theSeconds); lcd1.print(":"); lcd1.print(theMillis); if (millis() % 1000 < 500) { lcd3.clear(); } else { lcd3.setCursor(0, 0); lcd3.print("Congratulations"); lcd3.setCursor(0, 1); lcd3.print(" Player 2"); } } lcd1.setCursor(0, 1); lcd1.print("Time: "); long timeElapsed = totalTime - startTime; convertMillisToTime(timeElapsed, theMinutes, theSeconds, theMillis); lcd1.print(theMinutes < 10 ? "0" : ""); lcd1.print(theMinutes); lcd1.print(":"); lcd1.print(theSeconds < 10 ? "0" : ""); lcd1.print(theSeconds); lcd1.print(":"); lcd1.print(theMillis); } void abort() { lcds_clear(); delay(500); lcd1.setCursor(0, 0); lcd1.print("> Race aborded <"); delay(2000); lcds_clear(); theState = 1; } void lcds_clear() { lcd1.clear(); lcd2.clear(); lcd3.clear(); } Code-Erklärungen Dieser Code ermöglicht die Auswahl der zu fahrenden Runden, die Anzeige von Starting Lights mit Countdown, die Anzeige der schnellsten Runde für Spieler 1 und 2, die Gesamtzeit und die Anzeige des Gewinners. Lass uns den Code Schritt für Schritt durchgehen: Schritt 1: Import von Bibliotheken und Deklaration der Pins #include <Wire.h> #include <LiquidCrystal_I2C.h> const int lightBarrier1Pin = 2; const int lightBarrier2Pin = 3; const int resetButtonPin = 4; const int potPin = 0; const int startLightPins[] = { 7, 8, 9, 10, 11 }; const int numStartLights = 5; In diesem Abschnitt wird die erforderliche Bibliothek Wire für die Kommunikation mit dem I2C-LCD-Display und die Pins für die Lichtschranken, den Reset-Knopf, den Potentiometer und die Startlichter deklariert. Sie ist automatisch im Lieferumfang der Arduino-Software Schritt 2: Deklaration von Variablen und Objekten int lightBarrier1State = 0; int lightBarrier2State = 0; int lightBarrier1LastState = 0; int lightBarrier2LastState = 0; int totalLaps = 10; int lapsPlayer1 = 0; int lapsPlayer2 = 0; LiquidCrystal_I2C lcd1(0x25, 16, 2); LiquidCrystal_I2C lcd2(0x27, 16, 2); LiquidCrystal_I2C lcd3(0x26, 16, 2); int theState = 1; unsigned long startTime = 0; unsigned long lapStartTimePlayer1 = 0; unsigned long bestLapPlayer1 = 0; unsigned long lapStartTimePlayer2 = 0; unsigned long bestLapPlayer2 = 0; unsigned long totalTime = 0; boolean interruptP1Triggered = false; boolean interruptP2Triggered = false; long theMinutes, theSeconds, theMillis; Hier werden verschiedene Variablen für den Zustand des Spiels, die Zeiten, die Anzahl der Runden, die Lichtschranken und die LCD-Displays deklariert. Schritt 3: Setup-Funktion void setup() { // Initialisierung von Pins und Objekten pinMode(lightBarrier1Pin, INPUT_PULLUP); pinMode(lightBarrier2Pin, INPUT_PULLUP); pinMode(resetButtonPin, INPUT_PULLUP); lcd1.init(); lcd2.init(); lcd3.init(); lcd1.backlight(); lcd2.backlight(); lcd3.backlight(); for (int i = 0; i < numStartLights; i++) { pinMode(startLightPins[i], OUTPUT); } // Interrupts für die Lichtschranken aktivieren attachInterrupt(digitalPinToInterrupt(lightBarrier1Pin), lightBarrier1Interrupt, FALLING); attachInterrupt(digitalPinToInterrupt(lightBarrier2Pin), lightBarrier2Interrupt, FALLING); // Initialisierung von Startzeiten und besten Rundenzeiten startTime = millis(); bestLapPlayer1 = 0; bestLapPlayer2 = 0; } In der setup-Methode werden die Pins initialisiert, die Interrupts für die Lichtschranken aktiviert und die LCD-Displays initialisiert. Außerdem werden die Startzeiten und besten Rundenzeiten auf Null gesetzt. Schritt 4: Loop-Methode void loop() { switch (theState) { case 0: // idle // Keine Aktion break; case 1: // select laps select_laps(); if (digitalRead(resetButtonPin) == LOW) { theState = 2; clear_game(); delay(500); } break; case 2: // get Ready blink 3 times get_ready(); break; case 3: // start lights count down start_lights(); break; case 4: // racing racing(); if (digitalRead(resetButtonPin) == LOW) { theState = 6; // Abbrechen clear_game(); delay(500); } break; case 5: // winner winner(); if (digitalRead(resetButtonPin) == LOW) { theState = 1; delay(500); lcds_clear(); } break; case 6: abort(); break; } delay(10); } Die loop-Methode steuert den Hauptablauf des Spiels, abhängig von seinem Zustand theState. Hier werden Funktionen aufgerufen, die die Auswahl der Runden, die »Get Ready«-Anzeige, den Countdown der Startlichter, das Rennen selbst und die Anzeige des Gewinners handhaben. Schritt 5: Implementierung weiterer Funktionen Der Code enthält auch eine Reihe von Hilfsfunktionen, die in den oben genannten Schritten aufgerufen werden: clear_game(): Setzt das Spiel zurück und löscht alle Zeiten und Runden. lightBarrier1Interrupt() und lightBarrier2Interrupt(): Funktionen, die aufgerufen werden, wenn die Lichtschranken ausgelöst werden. select_laps(): Ermöglicht die Auswahl der zu fahrenden Runden über ein Potentiometer. get_ready(): Zeigt die Get Ready-Anzeige und blinkt die Startlichter. start_lights(): Startet den Countdown der Startlichter. racing(): Verwaltet den Rennablauf, zeichnet Zeiten, Runden und die beste Rundenzeit auf. convertMillisToTime(): Konvertiert Millisekunden in Minuten, Sekunden und Millisekunden. winner(): Zeigt den Gewinner und die Zeiten an. abort(): Beendet das Spiel vorzeitig. lcds_clear(): Löscht die Anzeigen auf den LCD-Displays. Jetzt weißt du, wie dieser Arduino-Code für die Carrera-Bahn-Steuerung funktioniert und wie er in verschiedene Abschnitte unterteilt ist, um die verschiedenen Aspekte des Spiels zu handhaben. Du kannst diesen Code verwenden und anpassen, um dein eigenes Carrera-Bahn-Projekt zu erstellen. Info: Was ist eine State Machine? Eine State Machine, oder zu Deutsch Zustandsmaschine, ist ein fundamentales Konzept in der Softwareentwicklung. In diesem Tutorial spielt sie eine zentrale Rolle bei der Realisierung der Funktionen für die Carrera-Bahn-Steuerung mithilfe des Arduino-Mikrocontrollers. switch (theState) { case 1: // select laps ... break; case 2: // get Ready blink 3 times ... break; case 3: // start lights count down ... break; case 4: // racing ... break; case 5: // winner ... break; case 6: // abort ... break; } Sie dient dazu, den Programmablauf in klar definierte Zustände zu gliedern. In diesem Projekt repräsentieren die Zustände verschiedene Phasen des Rennbahn-Erlebnisses, wie das Zählen der Runden, den Countdown und die Anzeige der Rundenzeiten. Die State Machine ermöglicht es dem Arduino, zwischen diesen Zuständen zu wechseln, abhängig von bestimmten Ereignissen, wie dem Drücken eines Tasters oder dem Passieren einer Lichtschranke. So behält das System stets den Überblick über den aktuellen Zustand des Rennens und kann entsprechend reagieren. Diese klare Strukturierung des Programms erleichtert nicht nur die Entwicklung und Wartung, sondern ermöglicht auch eine benutzerfreundliche Steuerung. Info: Was sind Interrupts und ihre Bedeutung in der Elektronik und Programmierung? Interrupts (Unterbrechungen), sind ein wichtiger Mechanismus in der Elektronik und Programmierung. Sie dienen dazu, den normalen Ablauf eines Mikrocontrollers oder Computers vorübergehend zu unterbrechen, um auf externe Ereignisse oder Signale zu reagieren. Hier sind einige Schlüsselaspekte von Interrupts und ihre Bedeutung: Ereignisbasierte Reaktion: Interrupts ermöglichen es einem System, unmittelbar auf bestimmte Ereignisse oder Signale zu reagieren, ohne den Hauptprogrammfluss zu unterbrechen. Dies ist besonders nützlich in Echtzeit-Anwendungen, bei denen es auf schnelle Reaktionen ankommt. Externe Signale: Interrupts werden oft genutzt, um auf externe Hardware-Signale wie Tastendrücke, Sensoreingaben oder Kommunikationsanfragen zu reagieren. Wenn ein solches Signal erkannt wird, kann der Mikrocontroller die aktuelle Aufgabe unterbrechen und eine vordefinierte Funktion ausführen. Ressourceneffizienz: Durch die Verwendung von Interrupts kann der Mikrocontroller Ressourcen sparen, da er nicht ständig auf bestimmte Ereignisse prüfen muss. Dies trägt zur Effizienz und Energieeinsparung bei. In diesem Tutorial wird die Carrera-Bahn-Steuerung mithilfe von Interrupts auf Lichtschranken reagieren. Sobald ein Auto eine Lichtschranke passiert, wird ein Interrupt ausgelöst, der die Rundenzeit zählt und den Rundenstand aktualisiert. Da die Autos sehr schnell sind, ist es wichtig, dass das System schnell reagiert (keine Garantie von meiner Seite aus). Der Beitrag Arduino Carrera-Bahn Projekt Rundenzeit Counter mit LCD erschien zuerst auf Arduino Tutorial.

zum Artikel gehen

Counter für Schleifen

{{/* Counter auf Null setzen */}}{{ $counter := 0 }}{{/* Counter um 1 erhöhen */}}{{ $counter = add $counter 1 }}

zum Artikel gehen

Sende a message from arduino nano to raspberry pi via 433 mhz rf module

I want to make a data transfer circuit using Arduino nano and Raspberry Pi. It will be done using 433 mhz rf module. If the password sent by Arduino is correct, Raspberry Pi will respond with a message stating the correct password... (Budget: $10 - $11 US

zum Artikel gehen

Mit Arduino Bewegungs- und Orientierungssensoren in Systeme integrieren

Entwickler müssen ihre Systeme zunehmend mit Orientierungs- und Bewegungsfunktionen ausstatten, sind aber unsicher, wo sie anfangen sollen. Ein Arduino könnte dabei helfen.

zum Artikel gehen

Selbst für Anfänger: Programmieren mit der Arduino Software leicht gemacht – Mit spannenden Projekten starten!

Selbst Arduino-Neulinge können mithilfe der richtigen Software und Anleitungen in die faszinierende Welt des Mikrocontrollers eintauchen. In diesem Artikel möchte ich dir zeigen, wie du Arduino programmieren kannst, um auf einfache Weise erstaunliche Proj

zum Artikel gehen

heise+ | Spracherkennung mit Arduino nano connect realisieren

Spracherkennung mit einem Arduino, das war bislang mangels Rechenpower unvereinbar. Doch der Nano RP2040 connect soll das packen. Wir zeigen, wie es geht.

zum Artikel gehen