Coin acceptor and coin hopper problems

Hi all,

Firstly I am new here and have been trying to get some code working for 2 weeks now! I only started coding on Arduino 2 weeks ago.

In short, I am using a coin acceptor to accept coins (working fine) which uses an interrupt pin to detect impulses from the acceptor. The basis of the thing is a money changer.

The coin hopper then pays out coins, but either theres something wrong with my code, or its getting random impulses probably caused by grounding issues I assume.

I just wanted to check first that my code all makes sense before I start going down other routes. The code is probably not very good, and could do with some improvements. I've read through many other posts, and taken tips from lots of them to add to my code, but I'm still seeing strange results.

Code posted below.

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

// variable use to measure the intervals inbetween impulses
int i = 0;
// Number of impulses detected
int impulsCount = 0;
// Sum of all the coins inserted
float coin_inserted = 0.00;
int coins_paid = 0;
int coins_to_pay = 0;
const int hopperInt = 10;
byte hopperDebounceDelay = 100;
long lastHopperDebounceTime = 0;
int previousMillis;
int interval = 500;
const byte interruptPin = 2;
volatile byte state = LOW;
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

void setup() {
  digitalWrite (8,HIGH);
  Serial.begin(9600);
  pinMode(10, INPUT_PULLUP);
  pinMode(8, OUTPUT);
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), incomingImpuls, RISING);

  lcd.begin(16, 2);
  lcd.setCursor(1,0);
  lcd.print("CHANGE MACHINE");
  lcd.setCursor(1,1);
  delay(1000);
  lcd.print("MAGGA'S ARCADE");
  delay(3000);
  lcd.clear();
  lcd.setCursor(1,0);
  lcd.print("INITIALISING...");
  delay(3000);
  lcd.clear();
  lcd.setCursor(1,0);
  lcd.print("INSERT COIN(S)");
  lcd.setCursor(1,1);
  lcd.print("FOR 2P CHANGE");

}

void incomingImpuls()
{
  impulsCount=impulsCount+1;
  Serial.println("Impulse Detected");
  i=0;
}

void loop() {
  if (coins_to_pay == 0)
  {
    i=i+1;
    Serial.print("i=");
    Serial.print(i);
    Serial.print(" Impulses:");
    Serial.println(impulsCount);
      if (impulsCount==1){
        coin_inserted=0.10;
        coins_to_pay=5;
        impulsCount=0;
        Serial.print(" Impulses During Loop: ");
        Serial.println(impulsCount);
      }
      if (impulsCount==2){
        coin_inserted=0.20;
        coins_to_pay=10;
        impulsCount=0;
        Serial.print(" Impulses During Loop: ");
        Serial.println(impulsCount);
      }
      if (impulsCount==3){
        coin_inserted=0.50;
        coins_to_pay=25;
        impulsCount=0;
        Serial.print(" Impulses During Loop: ");
        Serial.println(impulsCount);
      }
      if (impulsCount==4){
        coin_inserted=1.00;
        coins_to_pay=50;
        impulsCount=0;
        Serial.print(" Impulses During Loop: ");
        Serial.println(impulsCount);
      }
      if (impulsCount==5){
        coin_inserted=2.00;
        coins_to_pay=100;
        impulsCount=0;
        Serial.print("Impulses During Loop: ");
        Serial.println(impulsCount);
      }
      if (coins_to_pay>0)
      {
        Serial.print("Coin Inserted: ");
        Serial.println(coin_inserted);
        Serial.print("Impulses After Coin Insert: ");
        Serial.println(impulsCount);
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("Inserted:");
        lcd.setCursor(10,0);
        lcd.print(coin_inserted);
      }
    }
    else
    {
      // Coins still to pay, so activate hopper
      Serial.print("Impulses Before Payout: ");
      Serial.println(impulsCount);
      digitalWrite(8, LOW);
      int HopperSense = digitalRead(7);
        if (digitalRead(hopperInt) == LOW && ((millis() - lastHopperDebounceTime) > hopperDebounceDelay))
        {
        lastHopperDebounceTime = millis();
        Serial.println("2p Paid");
        Serial.print("Impulses After Payout: ");
        Serial.println(impulsCount);
        coins_paid = coins_paid+1;
        //Serial.println(coins_paid);
        lcd.setCursor(2,1);
        lcd.print("2ps Paid: ");
        lcd.setCursor(12,1);
        lcd.print(coins_paid);
        
        if(coins_paid == coins_to_pay)
        {
          coin_inserted = 0.00;
          coins_paid = 0;
          coins_to_pay = 0;
          impulsCount=0;
          delay(150);
          digitalWrite(8, HIGH);
          delay(3000);
          lcd.clear();
          lcd.setCursor(1,0);
          lcd.print("INSERT COIN(S)");
          lcd.setCursor(1,1);
          lcd.print("FOR 2P CHANGE");
        }
  }
}
}

And readout from serial goes something like this...

i=98 Impulses:0
i=99 Impulses:0
i=10I Impulses:2
Coin Inserted: 0.20
Impulses After Coin Insert: 0
Impulses Before Payout: 0
ImpuIses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
Impulses Before Payout: 1
ImpuIses Before Payout: 3
Impulses Before Payout: 3
Impulses Before Payout: 3
Impulses Before Payout: 3
Impulses Before Payout: 3
Impulses Before Payout: 3
Impulses Before Payout: 3
Impulses Before Payout: 3
Impulses Before Payout: 3
Impulses Before Payout: 3
Impulses Before Payout: 3
Impulses Before Payout: 3
Impulses Before Payout: 3

First mistake…
Don’t Serial.print within the ISR.
Can you post a schematic of how everything is powered and connected - every wire.

Hi, @maggajames
Welcome to the forum

Thanks for posting your code in tags, it makes it so much easier to read. :+1:

A circuit diagram would be good and if possible an image(s) so we can see your component layout.

Thanks.. Tom.... :smiley: :coffee: :australia: :+1:

Hi, @maggajames
Welcome to the forum

Thanks for posting code Tags
Can u pls share circuit diagram

It is unusual to use four different LCD libraries at the same time.

Thanks

The aim was to work our where the impulses were happening

Is there a good site I can use to do a wiring diagram?

Thanks a lot

I had a hard time getting the LCD to work initially.

Funnily enough, this is the only part that works 100%!

I’ll try and remove some once I’ve got the rest working.

Yes, your desk - a photo of a hand drawn schematic is fine :wink:

:joy: okay, will do

Both of the global variables i and impulsCount are shared between the ISR and loop(). You must declare these with the volatile keyword and disable interrupts in loop() before you read or modify these variables. Using interrupts requires an understanding of how interrupts work and where they are appropriate to use.

It is highly unlikely you need to use an interrupt for this application. I recommend reading the pin at the beginning of loop().

Thanks for this.

What is the downside of using an interrupt vs just reading the pin, out of interest?

Using an interrupt you must consider that the interrupt can happen at any moment and account for that in your code. Furthermore, an Arduino UNO, for example, is an 8-bit controller. That means that an interrupt could happen in the middle of you setting or reading a multibyte variable such as an integer giving you an incorrect reading!

Assuming you don't have delays in your code then polling the pin in loop() should satisfy the timing requirements for most any application and you don't have to worry with the quirks of using interrupts.

Interrupts are usually reserved for very stringent timing requirements. For example, reading data from a communication interface before the internal buffer fills.

Thank you.

I will make some changes to that and also post wiring diagram if still not working

How far apart are the pulses? This will dictate whether interrupts are necessary or not.

You can change the setting to either 50ms 100ms or 200ms

It seems to be behaving more as expected without the interrupt, thank you.

I'm struggling with the loop now for counting the number of coins, it always counts it as 1 impulse, so 0.10 in my code. I know why it's doing it, but I can't work out how to do the loop so that it counts all the impulses before it gets to the payout stage.

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

// variable use to measure the intervals inbetween impulses
int i = 0;
// Number of impulses detected
//int impulsCount = 0;
// Sum of all the coins inserted
float coin_inserted = 0.00;
int coins_paid = 0;
int coins_to_pay = 0;
const int hopperInt = 10;
byte hopperDebounceDelay = 100;
long lastHopperDebounceTime = 0;
int previousMillis;
int interval = 500;
const byte interruptPin = 2;
volatile byte state = LOW;
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

void setup() {
  digitalWrite (8,HIGH);
  Serial.begin(9600);
  pinMode(10, INPUT_PULLUP);
  pinMode(8, OUTPUT);
  pinMode(2, INPUT_PULLUP);
  //(digitalPinToInterrupt(interruptPin), incomingImpuls, RISING);

  lcd.begin(16, 2);
  lcd.setCursor(1,0);
  lcd.print("CHANGE MACHINE");
  lcd.setCursor(1,1);
  delay(1000);
  lcd.print("MAGGA'S ARCADE");
  delay(3000);
  lcd.clear();
  lcd.setCursor(1,0);
  lcd.print("INITIALISING...");
  delay(3000);
  lcd.clear();
  lcd.setCursor(1,0);
  lcd.print("INSERT COIN(S)");
  lcd.setCursor(1,1);
  lcd.print("FOR 2P CHANGE");

}

/*void incomingImpuls()
{
  impulsCount=impulsCount+1;
  Serial.println("Impulse Detected");
  i=0;
}*/

void loop() {
  int CoinVal = digitalRead(2);
  int HopperSense = digitalRead(7);
  if (coins_to_pay == 0)
  {
    if (CoinVal == LOW)
    {
      i=i+1;
      Serial.print("i=");
      Serial.print(i);
      Serial.print(" Impulses:");
      Serial.println(i);
        if (i==1){
          coin_inserted=0.10;
          coins_to_pay=5;
        }
        if (i==2){
          coin_inserted=0.20;
          coins_to_pay=10;
        }
        if (i==3){
          coin_inserted=0.50;
          coins_to_pay=25;
        }
        if (i==4){
          coin_inserted=1.00;
          coins_to_pay=50;
        }
        if (i==5){
          coin_inserted=2.00;
          coins_to_pay=100;
        }

      }
       if (coins_to_pay>0)
      {
        Serial.print("Coin Inserted: ");
        Serial.println(coin_inserted);
        Serial.print("Impulses After Coin Insert: ");
        Serial.println(i);
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("Inserted:");
        lcd.setCursor(10,0);
        lcd.print(coin_inserted);
      }
    }
    else
    {
      // Coins still to pay, so activate hopper
      Serial.print("Impulses Before Payout: ");
      Serial.println(i);
      digitalWrite(8, LOW);
      //int HopperSense = digitalRead(7);
        if (digitalRead(hopperInt) == LOW && ((millis() - lastHopperDebounceTime) > hopperDebounceDelay))
        {
        lastHopperDebounceTime = millis();
        Serial.println("2p Paid");
        coins_paid = coins_paid+1;
        lcd.setCursor(2,1);
        lcd.print("2ps Paid: ");
        lcd.setCursor(12,1);
        lcd.print(coins_paid);
        
        if(coins_paid == coins_to_pay)
        {
          coin_inserted = 0.00;
          coins_paid = 0;
          coins_to_pay = 0;
          i=0;
          delay(100);
          digitalWrite(8, HIGH);
          delay(3000);
          lcd.clear();
          lcd.setCursor(1,0);
          lcd.print("INSERT COIN(S)");
          lcd.setCursor(1,1);
          lcd.print("FOR 2P CHANGE");
        }
  }
}
}

You need to not start the dispensing of coins until some small period of time has elapsed since the last 'coin' pulse arrived. If the "small period of time" is very small you will start dispensing when the first coin pulse arrives. If the period is a little longer than the time between coin pulses you will start dispensing after the first coin and before the second. A longer period will allow the user to insert multiple coins before the dispensing begins.

I think three seconds is a good period. It gives the user time to insert multiple coins while not delaying dispensing very long.

Be sure you handle the case of more coins being deposited while dispensing.

Note the time (millis()) the latest coin pulse arrived and delay the starting of the dispense cycle until that time is at least three seconds in the past:
millis() - lastCoinPulseTime >= 3000

Thank you.

I had thought it was something to do with this, I had already started writing some code similar to what you've put above.

The only problem I'm having now is if you insert the same coin twice, the second time round, it detects as the next higher denomination of coin, so i must be 1 too high on the 2nd loop.

int CoinVal = digitalRead(2);
    if (CoinVal == LOW)
    {
      i=i+1;
      timeLastPulse = millis();
      Serial.print("i=");
      Serial.println(i);

      }
      long timeFromLastPulse = millis() - timeLastPulse;
      Serial.print("timeFromLastPulse");
      Serial.println(timeFromLastPulse);
      if (i > 0 && timeFromLastPulse > 200)
      {
        if (i==1){
            coin_inserted=0.10;
            coins_to_pay=5;
          }
          if (i==2){
            coin_inserted=0.20;
            coins_to_pay=10;
          }
          if (i==3){
            coin_inserted=0.50;
            coins_to_pay=25;
          }
          if (i==4){
            coin_inserted=1.00;
            coins_to_pay=50;
          }
          if (i==5){
            coin_inserted=2.00;
            coins_to_pay=100;
          }
          i=0;
      }
       if (coins_to_pay>0)
      {
        Serial.print("Coin Inserted: ");
        Serial.println(coin_inserted);
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("Inserted:");
        lcd.setCursor(10,0);
        lcd.print(coin_inserted);
      }
    }

Actually, it's not always the second time round, it seems random. i is 1 higher than it should be some of the time, but most of the time it is correct.

Hi again all.

Ok so I have got a bit further with this, the only issue I seem to have left is that sometimes a coin as recognised as the next value up, so it seems to be getting a random extra pulse from somewhere. It only happens some of the time.

Here is the code and the serial output...

#include <LCD.h>
//#include <LiquidCrystal.h>
#include <LiquidCrystal_I2C.h>
#include <LiquidCrystal_SI2C.h>
#include <Wire.h>

// variable use to measure the intervals inbetween impulses
int i = 0;
float coin_inserted = 0.00;
int coins_paid = 0;
int coins_to_pay = 0;
const int hopperInt = 10;
byte hopperDebounceDelay = 200;
long lastHopperDebounceTime = 0;
int previousMillis;
int interval = 500;
long timeLastPulse = 0;
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

void setup() {
  digitalWrite (8,HIGH);
  Serial.begin(9600);
  pinMode(10, INPUT_PULLUP);
  pinMode(8, OUTPUT);
  pinMode(2, INPUT_PULLUP);

  lcd.begin(16, 2);
  lcd.setCursor(1,0);
  lcd.print("CHANGE MACHINE");
  lcd.setCursor(1,1);
  delay(1000);
  lcd.print("MAGGA'S ARCADE");
  delay(3000);
  lcd.clear();
  lcd.setCursor(1,0);
  lcd.print("INITIALISING...");
  delay(3000);
  lcd.clear();
  lcd.setCursor(1,0);
  lcd.print("INSERT COIN(S)");
  lcd.setCursor(1,1);
  lcd.print("FOR 2P CHANGE");

}

void loop() {
  int HopperSense = digitalRead(7);
  if (coins_to_pay == 0)
  {
    int CoinVal = digitalRead(2);
    if (CoinVal == LOW)
    {
      i=i+1;
      timeLastPulse = millis();
      Serial.print("i=");
      Serial.println(i);

      }
      long timeFromLastPulse = millis() - timeLastPulse;
      Serial.print("timeFromLastPulse");
      Serial.println(timeFromLastPulse);
      if (i > 0 && timeFromLastPulse > 150)
      {
        if (i==1){
            coin_inserted=0.10;
            coins_to_pay=5;
          }
          if (i==2){
            coin_inserted=0.20;
            coins_to_pay=10;
          }
          if (i==3){
            coin_inserted=0.50;
            coins_to_pay=25;
          }
          if (i==4){
            coin_inserted=1.00;
            coins_to_pay=50;
          }
          if (i==5){
            coin_inserted=2.00;
            coins_to_pay=100;
          }
          i=0;
      }
       if (coins_to_pay>0)
      {
        Serial.print("Coin Inserted: ");
        Serial.println(coin_inserted);
        lcd.clear();
        lcd.setCursor(2,0);
        lcd.print("Credit:");
        lcd.setCursor(10,0);
        lcd.print(coin_inserted);
      }
    }
    else
    {
      // Coins still to pay, so activate hopper
      digitalWrite(8, LOW);
        if (digitalRead(hopperInt) == LOW && ((millis() - lastHopperDebounceTime) > hopperDebounceDelay))
        {
        lastHopperDebounceTime = millis();
        Serial.print("2ps Paid: ");
        coins_paid = coins_paid+1;
        Serial.println(coins_paid);
        lcd.setCursor(10,0);
        if(coin_inserted>0)
        {
          coin_inserted=(coin_inserted-0.02);
        }
        lcd.print(coin_inserted);
        lcd.setCursor(2,1);
        lcd.print("2ps Paid: ");
        lcd.setCursor(12,1);
        lcd.print(coins_paid);
        
        if(coins_paid == coins_to_pay)
        {
          coin_inserted = 0.00;
          coins_paid = 0;
          coins_to_pay = 0;
          i=0;
          delay(150);
          digitalWrite(8, HIGH);
          delay(3000);
          lcd.clear();
          lcd.setCursor(1,0);
          lcd.print("INSERT COIN(S)");
          lcd.setCursor(1,1);
          lcd.print("FOR 2P CHANGE");
        }
  }
}
}

Serial Output:

timeFromLastPulse14485
i=1
timeFromLastPulse5
i=2
timeFromLastPulse5
timeFromLastPulse27
timeFromLastPulse48
timeFromLastPulse70
timeFromLastPulse92
i=3
timeFromLastPulse5
timeFromLastPulse25
timeFromLastPulse48
timeFromLastPulse69
timeFromLastPulse91
timeFromLastPulse114
i=4
timeFromLastPulse5
timeFromLastPulse26
timeFromLastPulse48
timeFromLastPulse70
timeFromLastPulse91
timeFromLastPulse114
timeFromLastPulse136
timeFromLastPulse159
Coin Inserted: 1.00
2ps Paid: 1
2ps Paid: 2
2ps Paid: 3
2ps Paid: 4
2ps Paid: 5
2ps Paid: 6
2ps Paid: 7
2ps Paid: 8
2ps Paid: 9
2ps Paid: 10
2ps Paid: 11
2ps Paid: 12
2ps Paid: 13
2ps Paid: 14
2ps Paid: 15
2ps Paid: 16
2ps Paid: 17
2ps Paid: 18
2ps Paid: 19
2ps Paid: 20
2ps Paid: 21
2ps Paid: 22
2ps Paid: 23
2ps Paid: 24
2ps Paid: 25
2ps Paid: 26
2ps Paid: 27
2ps Paid: 28
2ps Paid: 29
2ps Paid: 30
2ps Paid: 31
2ps Paid: 32
2ps Paid: 33
2ps Paid: 34
2ps Paid: 35
2ps Paid: 36
2ps Paid: 37
2ps Paid: 38
2ps Paid: 39
2ps Paid: 40
2ps Paid: 41
2ps Paid: 42
2ps Paid: 43
2ps Paid: 44
2ps Paid: 45
2ps Paid: 46
2ps Paid: 47
2ps Paid: 48
2ps Paid: 49
2ps Paid: 50
timeFromLastPulse14328

Any ideas what might be causing this?

Thanks in advance!

Feel like I’m being needy here but can anyone offer any guidance on this at all?

Thanks so much