Münzzählung per Lichtschranke

Hallo. Ich baue einen Spielautomaten mit Tokenauswurf. Dazu wirft man Euros hinein, und soll den jeweiligen Gegenwert in Tokens ausgeworfen bekommen durch einschalten eines Motors, der die Tokens kontinuierlich auswirft. Die Auszahlüberwachung wollte ich mit einer Lichtschranke umsetzen, die die herausfallenden Münzen zählt, bis der eingeworfene Wert erreicht ist, und dann den Motor abschaltet.Ich weiß leider nicht, wie ich das programmieren soll. Ich habe einen Nano und einen großen Mega gekauft, dazu Relais, und Lichtschranken. Vielleicht findet sich ja ein Crack hier, der einem Neuling bei so einem Projekt den geistign Horiont erweitern und mir das Thema verständlich erklären kann. Lesen allein hilft leider nicht immer. :frowning: und es ist schon ein halbes Jahr Baustopp, weil ich alleine nicht weiterkomme....

Ich wäre in der Region Hannover auch besuchbar, um sich ein Bild von dem Automaten zu machen. Oder Whats App oder so...

Danke, ChrisC

Wie sicher sollen die Euros erkannt werden? Lochmarkenfänger? Basteleien haben hier mit Arduino nichts zu tun, fertige Münzprüfer gibt es ggf. aus ausgeschlachteten Automaten..

Die Ausgabe von Münzen ist weniger kritisch, dafür reicht eine horizontale Lochscheibe, bei der eine Münze in jedes Loch paßt und dann beim Weiterdrehen in den Ausgabeschacht fällt.

Ggf. muß noch geregelt werden, was passieren soll, wenn nicht mehr genügend Münzen im Ausgabestapel verfügbar sind.

:warning:
Im englischen Teil des Forum müssen die Beiträge und Diskussionen in englischer Sprache verfasst werden.
Deswegen wurde diese Diskussion in den deutschen Teil des Forums verschoben.
mfg ein Moderator.

Für den Münzeinwurf habe ich einen Münzprüfer gekauft, den viele hier für Verkaufsautomaten verwenden. Und du hast recht, ich habe so eine Ausgabescheibe gebaut, allerdings aus Holz, 48cm gross und 12mm dick, weil im Speicher dann viele Münzen liegen sollen. Ich würde daunter die Lichtschranke bauen, die die fallenden Münzen zählt. Wenn der Speicher leer ist, melden sich die Spieler bei mir :slight_smile: Ich versuche, hier ein paar Bilder zu senden.

Dazu wäre es schon notwendig zu erläutern, was Du verwendest.
Ein Stepper ist was anderes als ein DC-Motor hinter nem Relais.

Das ganze ist gar nicht so Problembehaftet.
Wenn Du den Code für den Münzer zeigst, lässt sich darauf aufbauen.

Dies hier hat ein Bekannter geschrieben, aber irgendwie funktioniert es nicht.


#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>


// ########## INIT DISPLAY ##########

// LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address UNO
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address MEGA2560 ; PIN 20 SDA, PIN 21SCL 
//                    0x3F correct? see https://www.youtube.com/watch?v=B8DNokj9LnY


// ############ specific configurations ####################

unsigned long idlePeriod = 10000; // time in ms between idle messages or shutdown e.g. 180000  

// debug modus: if you need 10 vendors and one additonal PIN (TX0 / Digital PIN 1 which should normally not be used) on UNO turn debug mode to 0 which disables serial 
// I use this pin to send a message to my smarthome system for any purchased product
bool debug = true;
bool special_coin_insert = false;
 //          if (!debug) {
 //         const int homematic_pin = 1;   
 //          }
unsigned long idleTimerMillis = 0;
int randomNumber = 0;

// ########## Buttons ##########
const int prog_up_button          = A1;
const int prog_down_button        = A2;
const int prog_next_button        = A3;
const int start_button            = A4;
const int conf_button             = A5;

// ########## Sensors ##########
const int tilt_sensor      = A13;
const int ball_sensor      = A14;
const int game_coin_sensor = A15;


// ########## INIT VALUES ##########
// #max
// #PREIS
 
int GameCoinPrice = 10; // default price  
int conveyorItems = 999;
int issuecount = 0;
int target_coins = 0;

// ########## INIT COIN ACCEPTOR ##########

const int coinInt = 2; // attach coinInt to interrupt pin 0 = digital pin 2 = digitalPinToInterrupt(2) . (interrupt pin 1 = digital pin 3 = digitalPinToInterrupt(3))
const int coinIntSensor = 3; // attach coinInt to interrupt pin 0 = digital pin 2 = digitalPinToInterrupt(2) . (interrupt pin 1 = digital pin 3 = digitalPinToInterrupt(3))

// set the coinsCurrentValue to a volatile float
// volatile as this variable changes any time the Interrupt is triggered
volatile int coinsCurrentValue = 0;
int coinsChange = 0; // a coin has been inserted flag

unsigned long currentMillis = 0;
unsigned long oldMillis = 0;

int pulsecount;

//const int relays[15] = {22,24,26,28,30,32,34,36,38,40,42,44,46,48,50}; // mega: 15 boxes


const int r_klappe_ok    = 22;
const int r_klappe_tilt  = 24;
const int r_extra_ball   = 26;
const int r_special_coin = 28;

const int hopper         = 36;

// ########## END OF INIT ##########

// ########## SETUP ##########

void setup() {

if (debug) {
  Serial.begin(9600); // start serial communication
  Serial.println("Setup gestartet");
  }

  pinMode(prog_up_button,    INPUT_PULLUP);
  pinMode(prog_down_button,  INPUT_PULLUP);
  pinMode(prog_next_button,  INPUT_PULLUP);

  pinMode(start_button,      INPUT_PULLUP);
  pinMode(conf_button,     INPUT_PULLUP);  
  
  pinMode(tilt_sensor,       INPUT_PULLUP);   
  pinMode(ball_sensor,       INPUT_PULLUP);   
  pinMode(game_coin_sensor,  INPUT_PULLUP);     
   
  pulsecount = 0;
  digitalWrite(r_klappe_ok, HIGH);
  pinMode     (r_klappe_ok, OUTPUT);
  digitalWrite(r_klappe_tilt, HIGH);
  pinMode     (r_klappe_tilt, OUTPUT);  
  digitalWrite(r_extra_ball, HIGH);
  pinMode     (r_extra_ball, OUTPUT); 
  digitalWrite(r_special_coin, HIGH);
  pinMode     (r_special_coin, OUTPUT);  
  digitalWrite(hopper, HIGH);
  pinMode     (hopper, OUTPUT);  
 

   
  lcd.begin(16, 2); // set up the LCD's number of columns and rows
  lcd.print("Bitte warten..."); // Print wait message to the LCD
   if (debug) {
  Serial.println("Warten auf Muenzpruefer");
  }
  delay(3000); // don't start main loop until we're sure that the coin selector has started

  // if coinInt goes HIGH (a Pulse), call the coinInserted function
  // an attachInterrupt will always trigger, even if your using delays
  //  attachInterrupt(digitalPinToInterrupt(2), coinInserted, RISING);
  attachInterrupt(digitalPinToInterrupt(coinInt), coinInserted, RISING);

// (wird direkt in der Routine gemacht:)
 // attachInterrupt(digitalPinToInterrupt(coinIntSensor), coinIssued, RISING);

  
 coinsCurrentValue = 0;
  lcd.print("Guthaben loeschen.");
delay(200);
if (debug) {
  Serial.println("Bereit");
}  
  lcd.clear();
  lcd.print("Bereit.");
}

// ############ coin issued - Lichtschranke ########################
void coinIssued() {
// jetzt erstmal keine weiteren Störungen bitte....
detachInterrupt(digitalPinToInterrupt(coinIntSensor));

if (target_coins > issuecount)
  {
   coinsCurrentValue = coinsCurrentValue - GameCoinPrice; // reduce balance
  }

 if (target_coins +1 > issuecount)
  {
   conveyorItems--; // reduce items
   issuecount ++;  // count issued coins
  if (debug) {
   Serial.println(" coin issued: + 1 ");  
   Serial.println(issuecount);    
  } //debug
  } //if
else
 {
  digitalWrite(hopper, HIGH);
  if (debug) {
   Serial.println(" Hopper aus (interupt) ");  
  } // debug
   } //else

// ab hier gern weitere Münzen zählen, die ausgegeben werden:
attachInterrupt(digitalPinToInterrupt(coinIntSensor), coinIssued, FALLING);
}

// ########## COIN INSERT ##########

// This function is called by interrupt every time we receives a pulse from the coin acceptor
void coinInserted() {
coinsChange = 1; // flag that there has been a coin inserted

  currentMillis = millis();
  int difference = currentMillis - oldMillis;

//  Serial.print("difference: ");
if (debug) {
  Serial.println(difference); 
}
  oldMillis = currentMillis;
  pulsecount++;

// new coin? start to count from beginning....   
if (difference > 134 or difference < 50 ) {
    if (pulsecount > 1) {
       pulsecount = 1;
     }
   }

if (difference < 135) {
switch (pulsecount) {
case 2: coinsCurrentValue = coinsCurrentValue + 10;  
        break; 
case 3: coinsCurrentValue = coinsCurrentValue + 10;  
        break; 
case 4: coinsCurrentValue = coinsCurrentValue + 30;  
        break; 
case 5: coinsCurrentValue = coinsCurrentValue + 50;  
        break; 
case 6: coinsCurrentValue = coinsCurrentValue + 100;  
        break;         
case 7: coinsCurrentValue = coinsCurrentValue + 0;  
        special_coin_insert = true;
        break;         
        
   } // switch
  } // if
    idleTimerMillis = millis();
if (debug) {    
  Serial.print ("(/coinsInserted)neuer Wert: ");
  Serial.println (  coinsCurrentValue);
}
}

// ########## Change Coins ##########
void IssueGameCoins() {

 if (debug) {
  Serial.print(" IssueGameCoins() ");
 }
  //relais bzw. Hopper direkt ansteuern
    
  digitalWrite(hopper, LOW); // Hoppermotor starten
  attachInterrupt(digitalPinToInterrupt(coinIntSensor), coinIssued, RISING);
   lcd.clear();    
   lcd.setCursor(0, 0);    
   lcd.print("*Muenzausgabe*");        
  delay(3000);
  lcd.setCursor(0, 1);    
  lcd.print(" Bitte warten   ");  
  delay(3000);
  lcd.setCursor(0, 0);    
  lcd.print("Viel Spass!    ");        
  delay(3000);
  lcd.setCursor(0, 0);    
  lcd.print(" Coins oben     ");  
  lcd.setCursor(0, 1);    
  lcd.print(" einwerfen!    ");  
  delay(3000);
  digitalWrite(hopper, HIGH); // Hoppermotor wieder aus falls er nicht schon aus ist
  // und Zählen der Impulse beenden
  detachInterrupt(digitalPinToInterrupt(coinIntSensor)); 
  if (debug) {
  Serial.println("  (Zeit ist um) ");
}    

}

// ########## LCD IDLE ##########

// messages on LCD while idle
void idle() {
    
  if ((millis() > idleTimerMillis + idlePeriod) && (coinsCurrentValue < 1) ) {       
    idleTimerMillis = millis();
    randomNumber = random(0, 5); // random number from 0 to 4
    lcd.clear();
    if (randomNumber == 0) {
      lcd.setCursor(0, 0);
 // #PREIS
      lcd.print(" mach mit!");
      lcd.setCursor(0, 1);
      lcd.print("  ");
    } else if (randomNumber == 1) {
      lcd.setCursor(0, 0);
      lcd.print("Pruefe dein ");
      lcd.setCursor(0, 1);
      lcd.print("Geschick. ");
    } else if (randomNumber == 2) {
      lcd.setCursor(0, 0);
      lcd.print("Viel Spass!");
      lcd.setCursor(0, 1);
      lcd.print(" ");
    } else if (randomNumber == 3) {
      lcd.setCursor(0, 0);
  // #PREIS
      lcd.print("Willkommen!");
      lcd.setCursor(0, 1);
      lcd.print("   ");
    } else if (randomNumber == 4) {
      lcd.setCursor(0, 0);
      lcd.print("Spielmuenze ");
      lcd.setCursor(0, 1);
      lcd.print("oben einwerfen.");
    }
 
   // clear lcd 1000 msec before idle periode expires and no coins have been thrown
  if ((millis() > idleTimerMillis + idlePeriod - 1000) && (coinsCurrentValue < 1)) {
    lcd.clear();
    }
   }
  }

// ########## SET VALUES ##########

void set_values() {
  if (debug) {
  Serial.println("Gerätekonfiguration gestartet");
  }
  
// set Anzahl
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("set Zaehler 999");
    conveyorItems = 999;  
    delay(2000);
  
// Preis ändern
// Setup Preise Button 1 = runterzählen, Button 2 = raufzählen in 10er Schritten, Button 3 = Ende und Anzeige
  
    int scope = 0;
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(" Preis ");
    lcd.setCursor(0, 1);
        lcd.print(GameCoinPrice / 100.00);
    lcd.print("     ");
    while (scope == 0) {
      if (digitalRead(prog_down_button) == LOW && GameCoinPrice >= 10) {
        GameCoinPrice = GameCoinPrice - 10;
        displayConfPrice();
        delay(500);
      }
      if (digitalRead(prog_down_button) == LOW && GameCoinPrice > 0 && GameCoinPrice < 10) {
        GameCoinPrice = 0;
        displayConfPrice();
        delay(500);
      }
      if (digitalRead(prog_up_button) == LOW && GameCoinPrice < 2000) {
        GameCoinPrice = GameCoinPrice + 10;
        displayConfPrice();
        delay(500);
      }
      if (digitalRead(prog_next_button) == LOW) {
  if (debug) {
        Serial.print("Neuer Preis:  ");
        Serial.println(GameCoinPrice / 100.00);
  }        
        scope++;
        delay(200);
      } //prog_next_button
    } // while scope

 coinsCurrentValue = 0;
 //displayBalance();
 
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Gespeichert");
  delay(200);
  displayBalance();
}  //set_values

// ########## LCD MESSAGES ##########

void displayBalance() {
  if (debug) {
  Serial.print("Guthaben:  ");
  Serial.println(coinsCurrentValue / 100.00);
  }  
  lcd.clear(); // reset LCD
  lcd.setCursor(0, 0);
  lcd.print("Guthaben");
  lcd.setCursor(0, 1); // set cursor to LCD row 2 column 1 (starting with 0)
  lcd.write(byte(0)); // display EURO symbol
  lcd.print(" ");
  lcd.print(coinsCurrentValue / 100.00); // display current balance
}
// ###################################################
void displayPrice(int currentPrice) {
  if (debug) {
  Serial.print("Spielmuenzenpreis:  ");
  Serial.println(currentPrice / 100.00);
  }  
  lcd.clear(); // reset LCD
  lcd.setCursor(0, 0);
  lcd.print("Preis");
  lcd.setCursor(0, 1); // set cursor to LCD row 2 column 1 (starting with 0)
  lcd.print(" ");
  lcd.print(currentPrice / 100.00);
  if (coinsCurrentValue > 0) {
    delay(1000);
    displayBalance();
  }
}
// ###################################################
void displayEmpty() {
  if (debug) {
  Serial.println("Muenzen alle");
  }  
  lcd.clear(); // reset LCD
  lcd.setCursor(0, 0);
  lcd.print("Leider leer :(");
  lcd.setCursor(0, 1); // set cursor to LCD row 2 column 1 (starting with 0)
  lcd.print(" ");
  if (coinsCurrentValue > 0) {
    delay(1000);
    displayBalance();
  }
}
// ###################################################
void displayConfPrice() {
  lcd.setCursor(0, 1);
  lcd.print(GameCoinPrice / 100.00);
  lcd.print("     ");
}

// ########## MAIN LOOP ##########

void loop() {
// ## Config Button pressed ##
  if (digitalRead(conf_button) == LOW) {
if (debug) {
   Serial.print("DigitalRead(conf_button): LOW");
           }
   set_values();
   delay(1000);
  } //config_button pressed

// ## game_coin_sensor detects game coin - das Spiel beginnt, Klappe bereit ##
  if (digitalRead(game_coin_sensor) == LOW) {
if (debug) {
   Serial.print("game coins sensor detects gamecoin: LOW");
           }
  digitalWrite(r_klappe_ok, LOW); // Klappe bereit
  lcd.clear();    
          lcd.setCursor(0, 0);    
          lcd.print("Coin eingeworfen,");  
          lcd.setCursor(0, 1);    
          lcd.print(" los geht's!  ");  
  delay(1000);
  digitalWrite(r_klappe_ok, HIGH); // Klappe ist bereit, Spule kann aus..

  } //game_coin_sensor

// ## TILT sensor  ##
  if (digitalRead(tilt_sensor) == LOW) {
if (debug) {
   Serial.print("TILT!!: LOW");
           }
  digitalWrite(r_klappe_tilt, LOW); // Tilt - alle Münzen fallen ins Depot!
  delay(1000);
  digitalWrite(r_klappe_tilt, HIGH); // Tilt-Klappe ist ausgelöst, weiter geht es mit dem Einwurf (game_coin_sensor)
  lcd.clear();    
          lcd.setCursor(0, 0);    
          lcd.print("*TILT !!  ;o( ");  
          lcd.setCursor(0, 1);    
          lcd.print(" Spiel beendet. ");  
  } //Tilt sensor

// ## Ball sensor  ##
  if (digitalRead(ball_sensor) == LOW) {
if (debug) {
   Serial.print("Ball Sensor - Freispiel: LOW");
           }
  digitalWrite(r_extra_ball, LOW); // Extraball ausgeben
  delay(1000);
  digitalWrite(r_extra_ball, HIGH); // Extraball ist ausgegeben
    delay(500);
          lcd.clear();    
          lcd.setCursor(0, 0);    
          lcd.print("*Extraball! ");  
          lcd.setCursor(0, 1);    
          lcd.print(" Freispiel? ");  
  
  digitalWrite(r_special_coin, LOW); // Spezialmünze ausgeben
  delay(1000);
  digitalWrite(r_special_coin, HIGH); // Spezialmünze ist ausgegeben
  } //Ball sensor


  idle();
  // check if a coin has been inserted
 if (coinsChange == 1) {  
  if (special_coin_insert)
      {
       special_coin_insert = false;        
//       digitalWrite(r_special_coin, LOW); // Spezialmünze ausgeben weil eine eingesteckt wurde
//       delay(1000);
//       digitalWrite(r_special_coin, HIGH); // Spezialmünze ist ausgegeben 
      lcd.clear();    
          lcd.setCursor(0, 0);    
          lcd.print("*Sondermuenze: ");  
          lcd.setCursor(0, 1);    
          lcd.print(" Freispiele!");  
          delay(2000);
      }  // Sonderverarbeitung der Sondermünze
      
   coinsChange = 0; // unflag that a coin has been inserted
   displayBalance(); // display current balance
    }

  // ********** BUTTON PRESSED **********
  
    if (digitalRead(start_button) == LOW) { // start button pressed (GameCoins ausgeben)
    if (debug) {
    Serial.print(" items ");
    Serial.print(conveyorItems);
    Serial.print(" cval ");
    Serial.print(coinsCurrentValue);
     Serial.print(" price ");
    Serial.print(GameCoinPrice); 
           }  
           
      if (conveyorItems > 0) {
        if (coinsCurrentValue < GameCoinPrice) { // insufficient balance - display price
          Serial.print(" insuf ");        
          displayPrice(GameCoinPrice);
          idleTimerMillis = millis();
          delay(200);
        }
        if ((coinsCurrentValue > GameCoinPrice - 1) ) { // sufficient balance
          if (debug) {
          Serial.print(" suf ");        
          }
          target_coins = coinsCurrentValue / GameCoinPrice;        
          issuecount = 0;   
          IssueGameCoins();               
              if (coinsCurrentValue < 0) {
            coinsCurrentValue = 0;  // correct float rounding error
              }
  
          displayBalance(); // display current balance
          idleTimerMillis = millis(); // reset idle timer        
          delay(2000);
        } // sufficient balance
       }  // items > 0
      } // start_button pressed
    }       // loop

Der Motor, den ich verwenden möchte icst ein 12v Motor, der nur über ein Relais ein und aus geschaltet werden soll. :slight_smile:

gehe mal bitte mit dem bleistift unten rechts unter Deinem Post in selbigen und markiere den gesamten Code.
Dann klickst mit der Maus oben auf das </> Symbol.
Sonst ist das nicht lesbar!

Ich fürchte, Du hast das Prinzip nicht verstanden.

Wie willst Du dafür sorgen, daß die Münzen einzeln an der Lichtschranke vorbeifallen?

Das habe ich schon gelöst. Der Motor treibt die Scheibe an und solange sie sich langsam dreht, fallen einzeln die Münzen raus und rollen durch die Lichtschranke. Die muss dann die Münzen nur "zählen" damit der Arduino den Motor dann ausschaltet, wenn die Anzahl erreicht ist.

Welche Münzen der User einwirft ist also egal?
Im Extremfall nur 1 Cent-Münzen?

Gruß Tommy

Warum nicht?

Mich würde ja eher der Code interessieren. Aber den bekomme ich leider nicht gelesen.
Wenn Du, kannst Du ihn befreien?

Guten Morgen. Habe den Code oben geändert, wie beschrieben. Hoffe, er ist jetzt lesbar.Ich wollte maximal 10 cent Münzen annehmen, dazu eine Sondermünze, die den gleichen Auszahlungswert hat wie eine 2 Euro Münze. Zur Zeit hatte ich den Einzelwert auf 10 cent gedacht: 2 Euro = 20 Token, 10 cent = 1 Token

Der User sieht ein Display, auf dem der eingeworfene Wert steht. Da der Automat micht zurückkgeben kann, darf es keinen Restbetrag geben, daher 10 cent pro Münze.Nach Einwurf drückt er den Auszahlknopf und der Motor soll angehen :slight_smile:

Dies hier hat ein Bekannter geschrieben, aber irgendwie funktioniert es nicht.

ich kann mir nicht helfen, aber der Code ist doch kein "Geldwechsler".

Entweder schreibt dir dein Bekannter ein passendes Programm oder wenn du von uns Hilfe brauchst, dann poste:

  • Einen Schaltplan aus dem wir erkennen was wo wie angeschlossen ist
  • Bilder die die Funktionsweise deiner Elemente unterstützend erklären
  • Einen Programmablaufplan aus dem hervorgeht, welche Funktionalität gewünscht ist.

Ansonsten wird das ein Thread mit 200 Posts ohne dass du was funktionierendes hast.

Meines erachtens brauchst du mehrere separate Komponenten - und auch Testsketche - die zunächst mal alle separat funktionieren müssen bevor man das alles zusammenkocht. Sonst kommt genau so ein Monster raus mit Delay und Interrupt das keiner mehr lesen kann, offenbar der Ersteller auch nicht mehr.

edit:
wenn ich mir das so überlege braucht es

  • Einzahlung verarbeiten - Das Zählergebnis deines Münzzähler übernehmen. Wie schaut da der Sketch dazu aus?
  • Auszahlung errechnen (also den Splitt des Einzahlungsbetrages in die zwei Denominationen 2EUR und 0.1 EUR. Ideal wäre es wenn du den Füllstand deiner zwei Denominationen weist, also wie viele 2EUR und 0.1 EUR Token du noch hast. Damit du schon vorher weist ob du dem Kunden überhaupt bedienen kannst.
  • Token auszahlen - mit Fehlerhandling was tun wenns irgendwo hakt. Das kann auch nur ein ein "Bitte Franzi Rufen" am LCD sein. Aber dann soll Franzi in der Lage sein zu sehen wie viel der Kunde eingezahlt hat und wie viel er schon herausbekommen hat. Sonnst hilf Franzi auch nicht.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.