Geschicklichkeitsspiel Kugellabyrinth Mit Arduino Nano
457 Views, 0 Favorites, 0 Comments
Geschicklichkeitsspiel Kugellabyrinth Mit Arduino Nano
Das Kugellabyrinth habe ich für unser Enkelkind schon einmal gebaut, aber aus Sperrholz und mit Servomotoren. Das sah nicht sehr schön aus, und die Servos waren nicht wirklich gut zu steuern. Deshalb habe ich alles nochmals umgebaut und mit Plexiglas erstellt, und Schrittmotoren verwendet. Außerdem wird die Spieldauer laufend auf einer 7-Segment Anzeige dargestellt und durch jeweils eine Lichtschranke bei Start und Ziel gestartet/beendet. Nach Spielende wird die Spieldauer für 5 Sek. angezeigt und danach für 5 Sek. der Highscore .
Supplies
BenötigteTeile:
- 2 x Fototransistoren
- 2 x Dioden
- 1 x Joystick
- 1 x 4-stellige 7Segment Anzeige TM1637
- 1 x Arduino Nano
- 1 x IC4093 NAND
- 2 x Schrittmotoren 28BYJ-48
- 2 x Motortreiber ULN2003
- 26 Schraubklemmen
- Plexiglas 4 mm
- Plexiglas 2 mm
- Widerstände 2 x 220R ; 2x 100K
- Aluminiumprofil
- Steuerkabel 6-adrig
Equipment:
- Lötstation
- Bohrvorrichtung
- Abisolierzange
- Säge
- Kunststoffkleber
- Heißklerbepistole
- Holzleim
Mechanischer Aufbau
Als Unterlage habe ich eine verleimte mehrschichtige Holzplatte 245x340mm verwendet. Der Schrittmotor für die untere Ebene wurde an eine Plexiglasscheibe montiert, die ihrerseits an einen auf die Grundplatte geleimten Holzblock geschraubt ist. Beim Motor für die obere Ebene wurden an dieser Stelle Alu-Winkel verwendet. Die Gegenlager der Ebenen zu den Motoren sind Plexiglasscheiben, in die ein Spalt gefräßt wurde, der in der Breite der abgeflachten Motorwelle entspricht. Damit kann das Drehmoment der Motorwelle leicht auf die Ebene übertragen werden. Das Lager auf der dem Motor gegenüberliegenden Seite besteht aus einem kurzen Stück Messinggewindestange mit zwei Muttern. Die einzelnen Teile aus Plexiglas habe ich mit einem Drehmel mit Kreissägevorsatz ausgeschnitten. Die Beiden Plexiglasebenen haben die Maße 190 x 190 x 3,6mm, die Teile zur Bildung des Labyrinths sind 10mm hoch und 1,8 mm stark.
Bauteile Der Steuerung
Die Bauteile für die Steuerung habe ich so auf der Grundplatte angeordnet, dass die Verkabelung möglichst kurz ist.
Die Schaltung
Die Steuerung besteht aus dem Arduino Nano, dem Joystick, der 7-Segment Anzeige, den Lichtschranken und dem IC4093 Modul. Das Modul mit dem IC 4093 wurde hinzugefügt, weil die Lichtschranken beim Durchlaufen der Stahlkugel kein eindeutiges 0/1-Signal an den Arduino übergeben haben. Mit dem IC 4093 ist dies gewährleistet.
Der Code
/****************************************************************** Created with PROGRAMINO IDE for Arduino - 30.01.2022 Project : SCHRITTMOTOR MIT jOYSTICK STEUERN Libraries : AccelStepper.h / TM1637Display.h / EEPROM.h Author : ahmedazouz => Snippets ""Labyrinth mit Servomotoren"" starthardware => Snippets "Zeitmessung" BEASTIDREES62 => Snippets "Stepper mit Joystick steuern" Wolfgang Keller => - Verknüpfung der Programmteile und Anpassungen an Steppermotoren - einfügen Highscore- Ausgabe 4 Digit Display - einfügen laufende Zeitausgabe und Endzeit über 4 Digit Display - Auslesen Lichtschranken über IC 74HC132N Beschreibung : Die Stepper werden mit dem Joystick rechts/links gesteuert Pin-Belegung für Arduino >Nano Version 03_7 : läuft - Geschwindigkeit durch Joystick regelbar - in x und y-Richtung mit Begrenzung - Zeitanzeige funktioniert - Lichtschranke funktioniert - Joystick dreht in alle Richtungen richtig - Highscore und EEPROM schreiben - Highscore wird z.Zt. noch im setup auf null gesetzt, muß später entfernt werden ******************************************************************/ #include <AccelStepper.h> #include <TM1637Display.h> #include <EEPROM.h> #define CLK 3 // für Anzeige #define DIO 2 // für Anzeige #define numberOfSeconds(xyz) ((xyz / 1000) % 60) #define numberOfMinutes(xyz) (((xyz / 1000) / 60) % 60) TM1637Display display(CLK, DIO); // Motor_y pin definitions: #define motoryPin1 6 // IN1 on the ULN2003 driver #define motoryPin2 7 // IN2 on the ULN2003 driver #define motoryPin3 8 // IN3 on the ULN2003 driver #define motoryPin4 9 // IN4 on the ULN2003 driver // Motor_x pin definitions: #define motorxPin1 10 // IN1 on the ULN2003 driver #define motorxPin2 11 // IN2 on the ULN2003 driver #define motorxPin3 12 // IN3 on the ULN2003 driver #define motorxPin4 13 // IN4 on the ULN2003 driver // Define the AccelStepper interface type; 4 wire motor in half step mode: #define MotorInterfaceType 8 // Define a stepper and the pins it will use AccelStepper stepper_y = AccelStepper(MotorInterfaceType, motoryPin1, motoryPin3, motoryPin2, motoryPin4); AccelStepper stepper_x = AccelStepper(MotorInterfaceType, motorxPin1, motorxPin3, motorxPin2, motorxPin4); // joystick pot output is connected to Arduino A0 #define joystick_y A0 #define joystick_x A1 int val_y = 0; int val_x = 0; int range = 24; int center = range/2; // resting position value int threshold = range/12; // resting threshold unsigned long int hscore = 0; const int beginn = 4 ; // Pin 4 Start const int ende = 5 ; // Pin 5 Ende int anzeigeDauer_endeZeit = 5000; // millisekunden int stateStart; int programState = 0; unsigned long startZeit = 0; unsigned long myTime; long zeitAngehalten; unsigned long myTimer1=0; unsigned long int endZeit = 0; unsigned long pauseEnde = 0; unsigned long pauseHscore = 0 ; unsigned long int umrech = 1000; unsigned long int eprom_score = 0; void setup() { Serial.begin(9600); pinMode(beginn,INPUT); // von Lichtsachranke pinMode(ende,INPUT); // von Lichtschranke display.setBrightness(1); //EEPROM.Write nach der Erstinstallation entfernen, damit der hscore nicht nach jedem //Ein-Ausschalten verschwindet //EEPROM.write(0,hscore) ; // Eprom = 0 setzen } void loop() { stepper_y.setMaxSpeed(500); stepper_y.setAcceleration(100); stepper_x.setMaxSpeed(500); stepper_x.setAcceleration(200); // read analog value from the potentiometer val_x = readAxis(joystick_x); // XAxlePin = A0 val_y = readAxis(joystick_y); // XAxlePin = A1 if ((val_y == 0) && (val_x == 0)) { stepper_y.stop(); stepper_x.stop(); } else { // move the motory in the links direction while ((val_y > 0) && (stepper_y.currentPosition() >=-140)) // vorne runter { // map the speed between 0 and 300 rpm int speed_ = map(val_y, 0, 24, 0, 300); // set motor speed stepper_y.setSpeed(-speed_); stepper_y.runSpeed(); // move the motor (1 step) every loop val_y = readAxis(joystick_y); } // move the motory in the rechts direction while ((val_y < 0 ) && (stepper_y.currentPosition() <= 140)) // hinten runter { // map the speed between 0 and 300 rpm int speed_ = map(val_y, 0, -24, 0, 300); // set motor speed stepper_y.setSpeed(speed_); stepper_y.runSpeed(); // move the motor (1 step) every loop val_y = readAxis(joystick_y); } // move the motorx in the links direction while ((val_x > 0) && (stepper_x.currentPosition() >= -140)) // links { // map the speed between 0 and 300 rpm int speed_ = map(val_x, 0, 24, 0, 300); // set motor speed stepper_x.setSpeed(-speed_); stepper_x.runSpeed(); // move the motor (1 step) every loop val_x = readAxis(joystick_x); } // move the motorx in the rechts direction while ((val_x < 0) && (stepper_x.currentPosition() <= 140)) // rechts { // map the speed between 0 and 300 rpm int speed_ = map(val_x, 0,-24, 0, 300); // set motor speed stepper_x.setSpeed(speed_); stepper_x.runSpeed(); // move the motor (1 step) every loop val_x = readAxis(joystick_x); } } zeitmessung(); } //####################################################################### void zeitmessung() { stateStart = digitalRead(beginn); zeitAngehalten = digitalRead(ende); switch (programState) { case 0: // gestoppt, Anzeige auf Null gestellt, warten auf startpin startZeit = 0; display.showNumberDecEx(0, 0b01000000, true, 2, 2); display.showNumberDecEx(0, 0b01000000, true, 2, 0); // start if (stateStart == LOW) { // Lichtschranke unterbrochen startZeit = millis(); programState = 1; } break; case 1: // gestartet, Anzeige wird hochgezält, warten auf endePin showTime(millis()); // stop if (zeitAngehalten == LOW) { // Lichtschranke unterbrochen programState = 3; } break; case 3: // gestoppt, End-Anzeige showTime(millis()); myTimer1 = millis(); pauseEnde = anzeigeDauer_endeZeit + myTimer1 ; while (millis() < pauseEnde ) // Anzeigedauer Endezeit für 5 Sekunden { Serial.print("case 3 millis = "); Serial.println(millis()); Serial.println(); } programState = 4; break; case 4: // highscore-Anzeige highscore(); myTimer1 = millis(); pauseHscore = anzeigeDauer_endeZeit + myTimer1; while (millis() < pauseHscore ) // Anzeigedauer aktueller Highscore { Serial.print("case 4 millis = "); Serial.println(millis()); Serial.println(); } programState = 0; break; } } //############### High Score ###################################### void highscore() { endZeit = myTime/1000; // im EPROM nur Sekunden gespeichert, max 255 oder 4 min 25 Sek hscore = EEPROM.read(0); if (hscore == 0) // beim ersten Durchlauf ist hscore = 0 { EEPROM.write(0, endZeit); // Endzeit in sekunden als hscore in EPROM schreiben } else if ( endZeit < hscore) { EEPROM.write(0,endZeit) ; // Endzeit in sekunden als hscore in EPROM schreiben } eprom_score = EEPROM.read(0); // für Anzeige in millisekunden numrechnen hscore = eprom_score * umrech; int seconds = numberOfSeconds(hscore); int minutes = numberOfMinutes(hscore); display.showNumberDecEx(seconds, 0, true, 2, 2); // Anzeige hscore Sekunden if (seconds % 2 == 0) { display.showNumberDecEx(minutes, 0b01000000, true, 2, 0); // Anzeige hscore Minuten } else { display.showNumberDecEx(minutes, 0, true, 2, 0); // Anzeige hscore Minuten } } //####################################################### void showTime(long theTime) { // über den Aufruf showTime(millis()) werden die aktuellen millis an theTime übergeben myTime = theTime - startZeit; int seconds = numberOfSeconds(myTime); int minutes = numberOfMinutes(myTime); display.showNumberDecEx(seconds, 0, true, 2, 2); if (seconds % 2 == 0) { display.showNumberDecEx(minutes, 0b01000000, true, 2, 0); } else { display.showNumberDecEx(minutes, 0, true, 2, 0); } } //######################################### int readAxis(int thisAxis) { // read the analog input: int reading = analogRead(thisAxis); // map the reading from the analog input range to the output range: reading = map(reading, 0, 1023, 0, range); // der Wert von "reading" liegt zwischen 0...1023 und soll // auf den Bereich 0....range(24) umgerechnet werden // if the output reading is outside from the // rest position threshold, use it: int distance = reading - center; // center = if (abs(distance) < threshold) { // threshold = 3 distance = 0; } // return the distance for this axis: return distance; }
Im Programm kann die Geschwindigkeit (setMaxSpeed) und die Beschleunigung (setAcceleration) der Motoren eingestellt werden. EEPROM.write(0,hscore) darf nur beim der ersten Programmübertragung ausgeführt werden. Anschließend muß das Programm ein zweites mal übertragen werden, aber ohne EEPROM.write(0,hscore), damit der Highscore nicht jedes mal auf Null gestellt wird, wenn der Arduino an Spannung gelegt wird. Mit "stepper_y.currentPosition() >=-140" oder "stepper_y.currentPosition() >=140" wird die maximale Neigung der Ebenen festgelgt, d.h. die Motoren halten an, auch wenn der Joystick weiter betätigt wird. Dies verhindert, dass die Nullstellung der Motoren durch Anfahren an Hindernisse verstellt wird. Damit die Nullstellung nicht verändert wird, dürfen die Ebenen auch nicht von Hand verstellt werden.