Geschicklichkeitsspiel Kugellabyrinth Mit Arduino Nano

by wopi in Circuits > Arduino

457 Views, 0 Favorites, 0 Comments

Geschicklichkeitsspiel Kugellabyrinth Mit Arduino Nano

IMG_4320.jpg
IMG_4281.jpg
IMG_4321.jpg

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

Joystick.jpg
Lichtschranke.jpg
7-Segment.jpg
Schraubklemen.jpg

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

IMG_4380.jpg
IMG_4381.jpg
IMG_4382.jpg
IMG_4383.jpg
IMG_4384.jpg
IMG_4390.jpg

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

IMG_4300.jpg
IMG_4298.jpg
IMG_4297.jpg

Die Bauteile für die Steuerung habe ich so auf der Grundplatte angeordnet, dass die Verkabelung möglichst kurz ist.

Die Schaltung

Schaltung 16.02.22.jpg
PrinzipLichtschranken.jpg
SteuerungLichtschranken.jpg

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.