Pulse Counting with Interrupts and noisy signal

I have connected a HSV-300 bill acceptor and Adafruit 16X32 LED Matrix to my Arduino Uno. I will not include the LED stuff here as it is working. The problem is with the pulses from the bill collector. Due to the LED matrix, I can not use the hardware interrupt pins. I have my bill counter on pin 12.

When a bill is input into the bill acceptor, it sends a multiple of high pulses for each bill. So for a $1 bill, it would pulse once at 50ms. For a $5 bill, it would pulse 5 times at 50ms in between each pulse. So the pulses come in groups depending on the amount of the bill. The problem is that the pulses from the bill collector are noisy which it seems makes interrupts impossible.

I’m guessing are three approaches to my problem

  1. to use interrupts (preferred but unsure if it is possible due to the noisy inputs)
  2. create code to count the pulses in each group
  3. create a circuit to reduce the noisiness of the pulses

I have read this post and it is useful but not working as the output from my bill collector is noisy.
Counting pulses using interrupt and timer

When I use the interrupt solution (either on RISING, FALLING, or CHANGING), because of the noisy input, the ISR function it is continually called. Is there a way to prevent this?

// Connects a HSV-300 bill acceptor

//Includes
#include <EnableInterrupt.h>

//****************************************************************
// Constants //
// pin for the bill validator's credit(-) line
const int billValidatorPin = 12;
unsigned long duration;

//****************************************************************
// Variables //
volatile byte coinPulseCount=0;    // a counter to see how many times the pin has changed - which coin inserted
byte newCoinInserted; // a place to put our last coin pulse count
volatile unsigned long pulseTime;  //this stores the time of the last pulse.
   
//****************************************************************
//DEFINE FUNCTIONS
void coinpulse()
{
    coinPulseCount++;
    pulseTime = micros(); //store current time in pulseTime
    Serial.println("Pulse");
}
  
//****************************************************************
void setup() {

  // Pin setups for bill validator and button
  pinMode(billValidatorPin, INPUT);
 
  // Initialize serial ports for communication.
  Serial.begin(9600);
  Serial.println("Waiting for dollar...");
  //attachInterrupt(digitalPinToInterrupt(billValidatorPin), coinpulse, RISING); // attach a PinChange Interrupt to our pin on the rising edge and execute the function burpcount when that pin changes - ALWAYS CHECKING CHANGES

  enableInterrupt(billValidatorPin,coinpulse,FALLING);

}

//****************************************************************
void loop() 
{
 
  if (coinPulseCount >0 && micros() - pulseTime > 1000)    //if there is a coin count & the time between now and the last pulse is greater than 1/4 of a second - THE END OF BANK OF PULSES
    {
      newCoinInserted = coinPulseCount; //new variable to free up coinPulseCount on the interrupt.
      Serial.println("test");
      coinPulseCount = 0;                // clear pulse count ready for a new pulse on the interrupt.
    }
  
  //Proccess the coin inserted
  switch (newCoinInserted) 
    {
      case 1:    
        Serial.println("$1 inserted");
        newCoinInserted = 0;   
        break;
      case 5:  
        Serial.println("$5 inserted");
        newCoinInserted = 0;   
        break;  
      case 10:  
        Serial.println("$10 inserted");
        newCoinInserted = 0;   
        break;  
      case 20:  
        Serial.println("$20 inserted");
        newCoinInserted = 0;   
        break; 
     } 

}

This code works but can’t figure out how to program it to get the desired result. I have been able to get some success but not able to count the pulses in a group, just each pulse. Commenting out the enableInterrupt(billValidatorPin,coinpulse,FALLING); from above

void loop() {

  // get the duration of the pulse
  duration = pulseIn(billValidator, HIGH);
   
  // Receiving a dollar bill will create a pulse with a duration of 50ms.
  // NOTE: When there is no dollar bill pulse, I will receive a pulse of 8400 - 8600 on every loop.
  // This is the noisy input

  // Dollar received
  if(duration > 12000)
    {
    // Count dollar
    dollarCounter++;
    // Checking for understanding
    Serial.print("Dollar detected.\n Total: ");
    // Display new dollar count
    Serial.println(dollarCounter);
    } 

}

I am not a EE so option 3 is a little difficult for me (adding a resistor, etc).

Thank you for any help.

Until you examine the noise on the pulses with an oscilloscope to see what the noise looks like and the frequency, you will never be able to reduce it or even see if changes you make to the circuit has any effect. This doesn’t take an EE, either.

Paul

Hi,
Try setting pin 12 as pinMode(billValidatorPin, INPUT_PULLUP); The pullup may clean your signal.

I have set

pinMode(billValidatorPin, INPUT_PULLUP);

but still the noise persists. I will put it on an oscilloscope tomorrow and report back.

I have changed my code to see how long the board thinks the noise pulses are:

void loop() 
{

  duration = pulseIn(billValidatorPin, HIGH);

  //val = digitalRead(billValidatorPin);
  
if (duration>= 20)
 {
   Serial.println(duration);
   
  }
  
  //Serial.println(val);
}

In the serial monitor I see (see attachment). It is just a a ton of values around 82XX. When I insert a bill, I get the same number, 82XX, but they come very fast.

Thank you for your continued help.

Hi,
I found a program that counts pulses coming from a bill acceptor. Here it is the link: ict-bill-acceptor-as-usb-keyboard/pulse_width_finder.ino at master · Juul/ict-bill-acceptor-as-usb-keyboard · GitHub

It may help you find the problem. One thing that I found that's there are no information about how it is worked and how to interface it to a micro. Hope the link will help you.

@tauro0221 that library worked great! Too bad it was not an interrupt but I'll work around it. Thank you for the find.

Hi,
I am glad that was able to use it for your project. I was looking for information on how to interface a micro to the money machine but didn't find anything. Then it show that web side and decided that you may use it. It is possible that it may be easy to use it with an interrupt. I like to use interrupt when talking to the outside world using a micro to control any machine. I learn helping other members with their projects problems..

raytroy:
@tauro0221 that library worked great! Too bad it was not an interrupt but I'll work around it. Thank you for the find.

I like messing with interrupts. Let me know if this modification of @taro0221 code works
use pin 2 if you are using an uno

#define INPUTPIN (2)

volatile int min_pulse_width; // the minimum pulse width to accept
volatile int debounce_speed; // ignore changes in input line state that happen faster than this
volatile int pulse_count; // how many pulses have been received so far in this pulse train

volatile unsigned long pulse_duration; // how long was the last pulse
volatile unsigned long pulse_begin; // when did the last pulse begin
volatile unsigned long curtime; // what is the current time
volatile int post_pulse_pause; // how long to wait after last pulse before sending pulse count

void BillCount() {
  curtime = millis();
  switch (digitalRead(INPUTPIN)) {
    case HIGH:
      pulse_begin = curtime;
      return;
    case LOW:
      pulse_duration = curtime - pulse_begin;
      if (pulse_duration > debounce_speed) {
        return;
      }
      if (pulse_duration > min_pulse_width) {
        pulse_count++;
      }
  }
}

void setup() {
  delay(2000);
  pinMode(INPUTPIN, INPUT);
  Serial.begin(115200);

  //  Keyboard.begin();
  pulse_begin = 0;
  min_pulse_width = 8;
  debounce_speed = 4;
  post_pulse_pause = 2000;

  pulse_count = 0;

  attachInterrupt(digitalPinToInterrupt(INPUTPIN),BillCount , CHANGE);
}

void loop() {
  if (pulse_count) {
    if ((millis() - pulse_begin) >= (post_pulse_pause)) { // Wait for 2 seconds to get the entire bill count
      Serial.print("Pule Count:");
      Serial.println(pulse_count);
      pulse_count = 0;
    }
  }
}

Z

@zhomeslice,

I don't have access to pin 2 as the 16X32 adafruit LED matrix is using it. I am interfacing this bill reader with this matrix to have the matrix display different things based on the amount of bill inserted.

I will try your code on pin 12. I tried the EnableInterrupt library to use pin 12 in the past but because of the signal bounce, it didn't work very well. The interrupt is constantly called, this might affect performance.

I will report tonight.

raytroy:
@zhomeslice,

I don't have access to pin 2 as the 16X32 adafruit LED matrix is using it. I am interfacing this bill reader with this matrix to have the matrix display different things based on the amount of bill inserted.

I will try your code on pin 12. I tried the EnableInterrupt library to use pin 12 in the past but because of the signal bounce, it didn't work very well. The interrupt is constantly called, this might affect performance.

I will report tonight.

Here's some code that can work on any pin including analog pins!.
I have not tested it as I stripped this snip of code out of a personal custom Interrupt class I created and I'm familiar with.
This should work even with tones of interrupts. and it should be fast. additional tweaks can speed this up more.

/*
  This program will print the width of each pulse it receives on a new line.
  Use it to find the pulse width of a bill acceptor.
  Just set the min_pulse_width to something lower than what you expect the pulse width to be.
  It is not common to have pulses of less than 8 ms so I set it to 8.
*/

#define INPUTPIN (12)

volatile int min_pulse_width; // the minimum pulse width to acccept
volatile int debounce_speed; // ignore changes in input line state that happen faster than this
volatile int pulse_count; // how many pulses have been received so far in this pulse train

volatile unsigned long pulse_duration; // how long was the last pulse
volatile unsigned long pulse_begin; // when did the last pulse begin
volatile unsigned long curtime; // what is the current time
volatile int post_pulse_pause; // how long to wait after last pulse before sending pulse count


union Mask {
  volatile uint32_t All;
  volatile uint8_t  Port[4];
};
Mask _Pins;

volatile uint32_t _PinMask; //volatile uint8_t  rcPinMask[3]; // RC Pin Mask for active RC Pins
volatile uint32_t _PCintLast; //volatile uint8_t  PCintLast[3];         // looking fo pin changes using bianary
volatile uint32_t Interupted;
uint8_t  BitNumber[20] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21}; // Key = Pin Val = Position List of pins that could be used as ping inputs:

ISR(PCINT0_vect) { //this ISR  pins 8-13
  ISR_Exe();
}
void ISR_Exe() {
  _Pins.Port[0] = PIND; // 0-7
  _Pins.Port[1] = PINB; // 8-13
  _Pins.Port[2] = PINC; // A0-A6
  uint32_t _cTime = micros();
  _Pins.Port[3] = 1;    // Placeholder
  uint32_t _Changed = _Pins.All ^ _PCintLast;// doing a ^ between the current interruption and the last one indicates wich pin changed
  uint32_t _Masked = _Changed & _PinMask;// Has one of the pins we need changed
  if (!_Masked)return;                   //No Data move on
  // -------------------------------------------------------------
  // add functions here when pin interrupts are Triggered
  // We only have 1 pin to follow so no additional testing is needed.
  BillCount(_cTime, ((_Pins.All) >> (INPUTPIN)) & 0x01 );// Gets bit value of INPUTPIN (HIGH LOW) this can be any pin including analog
  // -------------------------------------------------------------
  Interupted++;
  _PCintLast = _Pins.All;          // we memorize the current state of all PINs in group
}

void BillCount(unsigned long curtime, int InPin) {
  switch (InPin) {
    case HIGH:
      pulse_begin = curtime;
      return;
    case LOW:
      pulse_duration = curtime - pulse_begin;
      if (pulse_duration > debounce_speed) {
        return;
      }
      if (pulse_duration > min_pulse_width) {
        pulse_count++;
      }
  }
}

void AddPin(uint8_t Pin, uint8_t InputType) {
  pinMode(Pin, InputType);// enable interrupt for pin...
  if (bitRead(_PinMask, Pin)) return;
  bitWrite(_PinMask, BitNumber[Pin], 1); // Set the bit in the mask
  *digitalPinToPCMSK(Pin) |= bit (digitalPinToPCMSKbit(Pin));  // enable pin
  PCIFR  |= bit (digitalPinToPCICRbit(Pin)); // clear any outstanding interrupt
  PCICR  |= bit (digitalPinToPCICRbit(Pin)); // enable interrupt for the group
  return ;
}

void setup() {
  delay(2000);
  //pinMode(INPUTPIN, INPUT);
  AddPin(INPUTPIN, INPUT);
  Serial.begin(115200);
  //  Keyboard.begin();
  pulse_begin = 0;
  min_pulse_width = 8;
  debounce_speed = 4;
  post_pulse_pause = 2000;
  pulse_count = 0;
}

void loop() {
  if (pulse_count) {
    if ((millis() - pulse_begin) >= (post_pulse_pause)) { // Wait for 1 second to get the entire bill count
      Serial.print("Pule Count:");
      Serial.println(pulse_count);
      pulse_count = 0;
    }
  }
}

Z

@zhomeslice, sorry your code did not work. Here is what worked for me without interrupt.

/*
 * This interfaces a Arduino Uno, ICT HSV-300 bill acceptor, found here, https://www.endtrading.com/hsv-300 and a Adafruit 16X32 LED Matrix found here, https://www.adafruit.com/product/420.
 * The tutorial on how to hook up the matrix to the Uno is here, https://learn.adafruit.com/32x16-32x32-rgb-led-matrix/
 * 
 * Feel free to contact ray@raytroyconsulting.com if any help is needed.  
 * 
 */

/*
  The library to interface with the ICT HSV-300 bill acceptor can be found here, https://github.com/Juul/ict-bill-acceptor-as-usb-keyboard.
  Courtesy Marc Juul of https://juul.io
  
  This program expects pulses of logic high coming from a bill acceptor into a GPIO pin on an arduino leonardo or anything with an Atema32u4 and an arduino compatible boot loader.
  It counts the number of pulses, pretends to be a usb keyboard and types the dollar amount followed by a newline after each bill has been accepted e.g: "5.00\n"
  
  
*/
//****************************************************************
//Includes
#include <Adafruit_GFX.h>   // Core graphics library
#include <RGBmatrixPanel.h> // Hardware-specific library

//Define
//LED Matrix Stuff
#define CLK 8  // MUST be on PORTB! (Use pin 11 on Mega)
#define LAT A3
#define OE  9
#define A   A0
#define B   A1
#define C   A2

#define INPIN (12)// The pin on the arduino where CREDIT (-) [Common] is connected

RGBmatrixPanel matrix(A, B, C, CLK, LAT, OE, false);

//****************************************************************
//Variables
int cents_per_pulse; // how many cents per pulse. for most bill acceptors this is 100 or $1 per pulse, but it can often be configured and coin acceptors will be different
int min_pulse_width; // the minimum pulse width to acccept
int max_pulse_width; // the maximum pulse width to accept
int debounce_speed; // ignore changes in input line state that happen faster than this
int pulse_count; // how many pulses have been received so far in this pulse train
int cents_received; // Counts how many cents have been received
unsigned long pulse_duration; // how long was the last pulse
unsigned long pulse_begin; // when did the last pulse begin
unsigned long pulse_end; // if they pulse was within min and max pulse width, when did it end
unsigned long curtime; // what is the current time
int post_pulse_pause; // how long to wait after last pulse before sending pulse count
int pulse_state; // what is the current input line state (1 for high, 0 for low)
int last_state; // what was the last input line state
int whole_dollars; // how many whole dollars were received
int remaining_cents; // how many remaining cents were received
char out_str[8];


//****************************************************************
//FUNCTIONS
// Similar to F(), but for PROGMEM string pointers rather than literals
#define F2(progmem_ptr) (const __FlashStringHelper *)progmem_ptr

void drawFaceCircle(){
  
  //Draw Face Circle
  matrix.drawCircle(15, 7, 7, matrix.Color333(7, 0, 0));

  //Draw Face Eyes
  matrix.drawPixel(12, 4, matrix.Color333(7, 0, 0));
  matrix.drawPixel(18, 4, matrix.Color333(7, 0, 0));

  //Draw Face Smile
  matrix.drawPixel(11, 8, matrix.Color333(7, 0, 0));
  matrix.drawPixel(11, 9, matrix.Color333(7, 0, 0));
  
  matrix.drawPixel(12, 10, matrix.Color333(7, 0, 0));
  
  matrix.drawPixel(13, 11, matrix.Color333(7, 0, 0));
  matrix.drawPixel(14, 11, matrix.Color333(7, 0, 0));
  matrix.drawPixel(15, 11, matrix.Color333(7, 0, 0));
  matrix.drawPixel(16, 11, matrix.Color333(7, 0, 0));
  matrix.drawPixel(17, 11, matrix.Color333(7, 0, 0));

  matrix.drawPixel(18, 10, matrix.Color333(7, 0, 0));
  
  matrix.drawPixel(19, 8, matrix.Color333(7, 0, 0));
  matrix.drawPixel(19, 9, matrix.Color333(7, 0, 0));
  
}


//****************************************************************
void setup() {

  //Basic stuff 
  pinMode(INPIN, INPUT);
  Serial.begin(115200); // You can comment all the Keyboard lines and uncomment all the serial lines to make it print to serial instead (useful for debugging)
  Serial.println("Waiting for bill");

  //Set up matrix panel
  matrix.begin();
  //matrix.setCursor(0, 4);
  matrix.setTextWrap(false); // Allow text to run off right edge
  matrix.setTextSize(1);

  //Set filters for bill acceptor
  pulse_begin = 0;
  last_state = 0;
  min_pulse_width = 45;
  max_pulse_width = 65;
  debounce_speed = 3;
  post_pulse_pause = 360;
  cents_per_pulse = 100;
  pulse_end = 0;
  pulse_count = 0;
  cents_received = 0;
  
}


//****************************************************************
void loop() {
  
  pulse_state = digitalRead(INPIN);
  Serial.println(pulse_state);
  
  curtime = millis();
  
  if((pulse_state == 1) && (last_state == 0)) { // this means we entered a new pulse
    pulse_begin = curtime; // save the begin time of the pulse
    last_state = 1; // set the previous state
  } else if((pulse_state == 0) && (last_state == 1)) { // this means a pulse just ended
    pulse_duration = curtime - pulse_begin; // calculate pulse duration
    if(pulse_duration > debounce_speed) { // ensure that we don't change state for very short (false) pulses (this is called debouncing)
      last_state = 0;
    }
    if((pulse_duration > min_pulse_width) && (pulse_duration < max_pulse_width)) { // check if the pulse width is between the minimum and maximum
      pulse_end = curtime; // save the end time of the pulse
      pulse_count++; // increment the pulse counter
    }
  }
  
  if((pulse_end > 0) && (curtime - pulse_end > post_pulse_pause)) { // check if we've waited long enough that we don't expect any further pulses to be forthcoming

    cents_received += pulse_count * cents_per_pulse; // count the cents

    whole_dollars = cents_received / 100;
    remaining_cents = cents_received % 100;
    
//    if(remaining_cents < 10) {
//      snprintf(out_str, 8, "%d.0%d\n", whole_dollars, remaining_cents);
//    } else {
//      snprintf(out_str, 8, "%d.%d\n", whole_dollars, remaining_cents);
//    }
    
    //Serial.print(out_str);

    switch (whole_dollars) 
    {
      case 1:    
        Serial.println("$1 Inserted"); 
        // do something
        break;
      case 5:  
        Serial.println("$5 Inserted");
        // do something
        break;  
      case 10:  
        Serial.println("$10 Inserted");
        // do something
        break;  
      case 20:  
        Serial.println("$20 Inserted");   
        // do something
        break; 
     } 
        
    cents_received = 0; // reset cents_received so it's ready for next payment

    pulse_end = 0; // reset pulse_end so it's ready for next payment
    pulse_count = 0; // reset pulse_count so it's ready for next payment
  }

  drawFaceCircle();

}

As you know that there will be at least 50 msecs between pulses you could build in a debounce interval so that when an interrupt is detected the first thing it does is disable further interrupts on its pin and start a timer which will trigger annother interrupt after (say) 30 millisecs. The timer interrupt will turn the pin interrupt back on after (hopefully) the bounces have stopped. Be careful to clear the interrupt flag immediately before re-enabling the interrupt.

...R

Hi,
Just an advice/recommend that when needed to clean a signal from a sensor I used a schmitt trigger optocouple. One that it is possible to help you to clean the input signal it is an H11L1M. Also It will isolate the money machine and the micro.

I could not get the interrupt to work. @robin2 maybe a code sample?

This is a program I had working a few months ago. I have edited it a bit before posting to remove irrelevant parts that were not used. I have compiled this version successfully but I have not tested it and I don’t now have any convenient hardware for testing it.

The relevant parts from your point of view are the final 3 functions.

#define motorCWpin 5
#define motorCCWpin 6
#define potPin A0

int potVal = 0;
int motorPower = 0;

// variables for ISR
volatile unsigned long tempIsrRevMicros = 0;
volatile unsigned long isrRevMicros = 0;
volatile unsigned long isrRevCount = 0;
volatile bool newRevMicros = true;
volatile byte isrPinVal;
volatile byte prevIsrPinVal;

volatile unsigned long startTimerInterval;
volatile unsigned long timerInterval;


const byte bufLength = 50;
unsigned long revMicrosBuf[bufLength];
volatile byte bufIndex = 0;

unsigned long totalRevs;

unsigned long revMicros = 654321L;
unsigned long prevRevMicros;
unsigned long latestRevMicros;

unsigned long revMicrosB = 123456L;
unsigned long prevRevMicrosB;
unsigned long latestRevMicrosB;
unsigned long revCountB;

byte prevOptVal;
byte optVal;

int reqSpeed;
int actSpeed;
int prevActSpeed;
int controlErr;
int speedChange;
long controlErrFactor = 32L;
long speedChangeFactor = 96L;
long ctrlAdj;
long chgAdj;

unsigned long currentMillis;
unsigned long prevShowMillis;
unsigned long showIntervalMillis = 300;

unsigned long prevMotorMillis;
unsigned long motorIntervalMillis = 30;

unsigned long revTimeoutMillis;
unsigned long revTimeoutVal = 550;



void setup() {
    Serial.begin(9600);
    Serial.println("Starting MotorRevBounceTest.ino");
    pinMode(motorCWpin, OUTPUT);
    pinMode(motorCCWpin, OUTPUT);
    digitalWrite(motorCWpin, LOW);
    initTimer1();

    attachInterrupt(0, revMicrosISR, RISING);
}

void loop() {
    currentMillis = millis();
    calcRevMicros();
    controlMotor();
    showData();
}

void showData() {
    if (bufIndex >= 49) {
        for (byte n = 0; n < 50; n++) {
            Serial.println(revMicrosBuf[n]);
        }
        Serial.println();
        Serial.println("==============");
        Serial.println();
        bufIndex = 0;
    }
}


void readPot() {
    potVal = analogRead(potPin);
    reqSpeed = (1023 - potVal) * 5 + 800;
    if (potVal < 20) {
        reqSpeed = 30000;
    }
}

void controlMotor() {
    if (currentMillis  - prevMotorMillis >= motorIntervalMillis) {
        prevMotorMillis += motorIntervalMillis;

        readPot();

        motorPower = potVal /4; // for testing
        analogWrite(motorCWpin, motorPower);
    }
}



void calcRevMicros() {
    if (newRevMicros == true) {
        prevRevMicros = latestRevMicros;
        noInterrupts();
            latestRevMicros = isrRevMicros;
            totalRevs = isrRevCount;
            newRevMicros = false;
        interrupts();
        revMicros = latestRevMicros - prevRevMicros;
        revMicrosBuf[bufIndex] = revMicros;
        bufIndex ++;
        if (bufIndex >= 49) {
            bufIndex = 49;
        }
    }
}

void revMicrosISR() {
        EIMSK &= 0b11111110; // stop int0 interrupt
        isrRevMicros = micros();
        isrRevCount ++;
        newRevMicros = true;
        TCNT1 = 0; // clear the Timer counter
        OCR1A = 64; // set the max count
        TIFR1 |= 0b00000010; // clear the timer interrupt flag
        TIMSK1 |= 0b00000010; // enable the timer interrupt
}

void initTimer1() {
    // note this does not enable the timer interrupt
    TCCR1A = 0;
    TCCR1B = 0;
    TCCR1B |= 0b00001101; // turn on CTC mode and prescale /256
    TCNT1  = 0; // reset counter
    // how many count cycles?
    // prescale 1024 gives 64 µsecs per count
    // I want intervals from 6000 to 10000 microsecs
    // 6000 would require a count of 94 and 10000 would require 156
    // using an 8MHz clock these would be 47 and 78

}

ISR(TIMER1_COMPA_vect) {
    TIMSK1 &= 0b11111101;  // disable timer compare interrupt
    EIFR |= 0b00000001;  // clear int0 Flag
    EIMSK |= 0b00000001; // enable int0 interrupt
}

…R