Proximity Sensor not picking up objects

Hello all,

First time poster here and just to say it, there is some great info around here.

I have a question about my configuration on a project I started for my little guys school halloween carnival.

I am using an "UNO" type board with 3 proximity sensors to sense "bean bags" as they pass through a game board.

These are the sensors I am using

NPN Type NO set

I have noticed when a bean bag passes by the sensor head very quickly, the the sensor side of the head displays the activity led, although the arduino does not pick up the input.

If I move the bag "more slowly" across the sensor (i ask the kids to throw the bag "not as hard"), the arduino picks up the activity just fine.

I am using the standard debounce code shown - here - and it debounces the signal input correctly. (delay set to 25ms)

My sketch code is very simple and simply debounces the input, increments a determined value for each prox sensor and sends the value to an MAX7xx LED panel.

There is some other frills to my sketch (push button value reset, push button scrolling message and 3 leds that blink at random)

Everything seems to work as desired except for when kids start hucking the bean bags through the game board and the counter values don't increment. (Even though I can see the prox sensor senses the passing bean bag)

Any insight on where to look will be greatly appreciated!

(The servo motor is not controlled through the arduino, it is simply powered by a 24v psu and directly controlled with a 100k ohm zero pot)

Game Board Quick Video 1

Game Board quick video 2

ProxSensor.pdf (836 KB)

Looks like a cool project- I'll probably copy it for next year.
Sketch? (See "How to use this forum for programming questions" at the top of the forum.)

25ms is a long time for debounce and the beanbag is probably in and out of the sensor view in much less than 25ms.

Do you even need debounce? Can you put an oscilloscope on the sensor output to see if or how dirty it is?

Hi,
Welcome to the forum.

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

How have you got the NPN outputs of the PE sensors wired?
Are you using pull-up resistors?

Thanks.. Tom.. :slight_smile:

Wow, thanks for the quick reply!

I do need the debounce, without it the input gets triggered 20-30 times with a quick pass of the hand/object.

I thought about adding a hardware debounce chip, but went for software debouncing.

Excuse the semi-messy code (attached and inserted), I was planning on cleaning up the multiple ifs with a case statement after everything was dialed in.

Also, excuse the section/variables that have to do with the high score reset, I nixed the idea and went a different route with the 2nd push button. (needs some cleaning)

Attached is a quick diagram of my config using "tinkercad"

Granted they do not have my exact prox sensors and I am not showing my 3 LEDs or MAX7 display, it is simply for reference :slight_smile:

Lastly, if someone can let me know that I am doing the int to string to buffer length to char array at the end of the code would ease my mind (it seems to work fine!)

Again thanks, I have a few hours in on this project! :slight_smile:

#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 8
#define CLK_PIN   13
#define DATA_PIN  11
#define CS_PIN    10

// HARDWARE SPI
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

// Scrolling parameters
uint8_t scrollSpeed = 0;    // default frame delay value
textEffect_t scrollEffect = PA_PRINT;
textPosition_t scrollAlign = PA_RIGHT;
uint16_t scrollPause = 500; // in milliseconds

// Global message buffers shared by Serial and Scrolling functions
#define  BUF_SIZE  75
char curMessage[BUF_SIZE] = { "" };
char newMessage[BUF_SIZE] = { "" };
bool newMessageAvailable = true;

//Current and High Score ints
int intCurrentScore = 0;
int intHighScore = 0;

//Push button reset for current score
int inPinCurrReset = 2;
int CurrResetVal = 0;
int PB1State = HIGH;
int PB1LastState = LOW;
unsigned long PB1Debounce = 0;

//Push button reset for high score
int inPinHighReset = 3;
int HighResetVal = 0;
int PB2State = HIGH;
int PB2LastState = LOW;
unsigned long PB2Debounce = 0;
int PB2Flag = 0;

//Proximity Sensor Inputs
int inProximitySensor1 = 4;
int Prox1InputVal = 0;
int Prox1State = HIGH;
int Prox1LastState = LOW;
unsigned long Prox1Debounce = 0;

int inProximitySensor2 = 5;
int Prox2InputVal = 0;
int Prox2State = HIGH;
int Prox2LastState = LOW;
unsigned long Prox2Debounce = 0;

int inProximitySensor3 = 6;
int Prox3InputVal = 0;
int Prox3State = HIGH;
int Prox3LastState = LOW;
unsigned long Prox3Debounce = 0;

//debounce delay & milli variables
unsigned long ulCurrentMilli;
unsigned long ulDebounceDelay = 20;

//Stings for LED screen
String strLEDString;
String strHighScore;
String strCurrentScore;
int intStringLength;

//LED Star Light Outputs & millis
int ouLED1 = 7;
unsigned long ulLEDMilli1 = 1000;
unsigned long ulStartMilli1;

int ouLED2 = 8;
unsigned long ulLEDMilli2 = 1500;
unsigned long ulStartMilli2;

int ouLED3 = 9;
unsigned long ulLEDMilli3 = 1750;
unsigned long ulStartMilli3;

//debug string
String strDebug;

void setup()
{
  Serial.begin(57600);

  P.begin();
  P.displayText(curMessage, scrollAlign, scrollSpeed, scrollPause, scrollEffect, scrollEffect);

  pinMode(inPinCurrReset, INPUT);
  pinMode(inPinHighReset, INPUT);
  pinMode(inProximitySensor1, INPUT);
  pinMode(inProximitySensor2, INPUT);
  pinMode(inProximitySensor3, INPUT);
  pinMode(ouLED1, OUTPUT);
  pinMode(ouLED2, OUTPUT);
  pinMode(ouLED3, OUTPUT);

  newMessageAvailable = true;
}

void readSerial(void)
{
  static char *cp = newMessage;

  while (Serial.available())
  {
    *cp = (char)Serial.read();
    if ((*cp == '\n') || (cp - newMessage >= BUF_SIZE - 2)) // end of message character or full buffer
    {
      *cp = '\0'; // end the string
      // restart the index for next filling spree and flag we have a message waiting
      cp = newMessage;
      newMessageAvailable = true;
    }
    else  // move char pointer to next position
      cp++;
  }

}

void loop()
{

  CurrResetVal = digitalRead(inPinCurrReset);
  HighResetVal = digitalRead(inPinHighReset);
  Prox1InputVal = digitalRead(inProximitySensor1);
  Prox2InputVal = digitalRead(inProximitySensor2);
  Prox3InputVal = digitalRead(inProximitySensor3);

  //get current milli timer
  ulCurrentMilli = millis();

  //LED1
  if (ulCurrentMilli - ulStartMilli1 >= ulLEDMilli1)
  {
    digitalWrite(ouLED1, !digitalRead(ouLED1));
    ulStartMilli1 = ulCurrentMilli;
  }

  //LED2
  if (ulCurrentMilli - ulStartMilli2 >= ulLEDMilli2)
  {
    digitalWrite(ouLED2, !digitalRead(ouLED2));
    ulStartMilli2 = ulCurrentMilli;
  }

  //LED3
  if (ulCurrentMilli - ulStartMilli3 >= ulLEDMilli3)
  {
    digitalWrite(ouLED3, !digitalRead(ouLED3));
    ulStartMilli3 = ulCurrentMilli;
  }

  //Proximity Sensor 1
  if (Prox1InputVal != Prox1LastState)
  {
    Prox1Debounce = ulCurrentMilli;
  }
  if ((ulCurrentMilli - Prox1Debounce) > ulDebounceDelay)
  {
    if (Prox1InputVal != Prox1State)
    {
      Prox1State = Prox1InputVal;
      if (Prox1State == HIGH)
      {
        intCurrentScore = intCurrentScore + 5;
        newMessageAvailable = true;
      }
    }
  }
  Prox1LastState = Prox1InputVal;

  //Proximity Sensor 2
  if (Prox2InputVal != Prox2LastState)
  {
    Prox2Debounce = ulCurrentMilli;
  }
  if ((ulCurrentMilli - Prox2Debounce) > ulDebounceDelay)
  {
    if (Prox2InputVal != Prox2State)
    {
      Prox2State = Prox2InputVal;
      if (Prox2State == HIGH)
      {
        intCurrentScore = intCurrentScore + 10;
        newMessageAvailable = true;
      }
    }
  }
  Prox2LastState = Prox2InputVal;

  //Proximity Sensor 3
  if (Prox3InputVal != Prox3LastState)
  {
    Prox3Debounce = ulCurrentMilli;
  }
  if ((ulCurrentMilli - Prox3Debounce) > ulDebounceDelay)
  {
    if (Prox3InputVal != Prox3State)
    {
      Prox3State = Prox3InputVal;
      if (Prox3State == HIGH)
      {
        intCurrentScore = intCurrentScore + 25;
        newMessageAvailable = true;
      }
    }
  }
  Prox3LastState = Prox3InputVal;

  //Current Score Reset
  if (CurrResetVal != PB1LastState)
  {
    PB1Debounce = ulCurrentMilli;
  }
  if ((ulCurrentMilli - PB1Debounce) > ulDebounceDelay)
  {
    if (CurrResetVal != PB1State)
    {
      PB1State = CurrResetVal;
      if (PB1State == HIGH)
      {
        intCurrentScore = 0;
        newMessageAvailable = true;
      }
    }
  }
  PB1LastState = CurrResetVal;

  //High and Current Score Reset
  if (HighResetVal != PB2LastState)
  {
    PB2Debounce = ulCurrentMilli;
  }
  if ((ulCurrentMilli - PB2Debounce) > ulDebounceDelay)
  {
    if (HighResetVal != PB2State)
    {
      PB2State = HighResetVal;
      if (PB2State == HIGH)
      {
        PB2Flag = 1;
        intCurrentScore = 0;
        intHighScore = 0;
        newMessageAvailable = true;
      }
    }
  }
  PB2LastState = HighResetVal;

  //High Score logic
  if (intCurrentScore > intHighScore)
  {
    intHighScore = intCurrentScore;
    newMessageAvailable = true;
  }

  if (P.displayAnimate())
  {
    if (newMessageAvailable)
    {
      //convert ints to strings
      strHighScore = String(intHighScore);
      strCurrentScore = String(intCurrentScore);

      if (PB2Flag == 1)
      {
        // Scrolling parameters
        uint8_t scrollSpeed = 20; 
        textEffect_t scrollEffect = PA_SCROLL_LEFT;
        textPosition_t scrollAlign = PA_LEFT;
        uint16_t scrollPause = 500; // in milliseconds
        P.displayText(curMessage, scrollAlign, scrollSpeed, scrollPause, scrollEffect, scrollEffect);
        strLEDString = "Second Grade Rulez!";
        PB2Flag = 0;
      }
        else
        {
          uint8_t scrollSpeed = 0;   
          textEffect_t scrollEffect = PA_PRINT;
          textPosition_t scrollAlign = PA_RIGHT;
          uint16_t scrollPause = 500; // in milliseconds
          P.displayText(curMessage, scrollAlign, scrollSpeed, scrollPause, scrollEffect, scrollEffect);
          strLEDString = strCurrentScore;
        }
      
      //find the length of the string for buff size of char array
      intStringLength = strLEDString.length() + 1;
      newMessage[intStringLength];

      //convert string to char array with string length
      strLEDString.toCharArray(newMessage, intStringLength);

      strcpy(curMessage, newMessage);
      newMessageAvailable = false;
    }
    P.displayReset();
  }
  readSerial();
}

Hi,
Thanks for the info;
OPs circuit;


I understand the edit error of no connection between pin2 and its pushbutton.

Thanks.. Tom... :slight_smile:

Debounce should not be an issue for even very fast moving bean bags. You detect the first rising or falling edge (that's the bean bag passing), then ignore other edges for some time (can be until the bean bag is long gone, doesn't matter).

That said, this is an optical break beam sensor, it should not show bounce to begin with. If you count 20-30 triggers for a single quick pass you probably have your edge detection wrong.

The missing of a fast moving bean bag may have to do with the sensor itself, having a built-in delay triggering only when it's covered for at least a certain time, a common way to prevent false triggering. You have to find the sensor's data sheet for more information on its working.

That's a lot of code. As there are three detectors I would use arrays for that, calling a function three times to check each individual sensor. Makes code shorter and more maintainable.

For your particular problem: get it to count bean bags with a single sensor reliably first. Remove all other code, so you know that there's no interference by other parts.

I find this bit most interesting;

void loop()
{

  //Proximity Sensor 1
 if (Prox1InputVal != Prox1LastState)
  {
    Prox1Debounce = ulCurrentMilli;
  }
  if ((ulCurrentMilli - Prox1Debounce) > ulDebounceDelay)
  {
    if (Prox1InputVal != Prox1State)
    {
      Prox1State = Prox1InputVal;
      if (Prox1State == HIGH)
      {
        intCurrentScore = intCurrentScore + 5;
        newMessageAvailable = true;
      }
    }
  }
  Prox1LastState = Prox1InputVal;

If we consider;

 //Proximity Sensor 1
  if (Prox1InputVal != Prox1LastState)
  {
    Prox1Debounce = ulCurrentMilli;
  }

What happens if the signal is bouncing wildly, say 0,1,0,0,1,0,1,0,1,0 all within 20ms while the bean bag is passing. This would potentially screw up your debounce so that

if ((ulCurrentMilli - Prox1Debounce) > ulDebounceDelay)

Actually takes much longer than your intended debounce delay.

Also

if (Prox1InputVal != Prox1State)
    {
      Prox1State = Prox1InputVal;

After the initial bean bag pass doesn't the state of Prox1State = high? So it will only trigger once Prox1InputVal is low first to satisfy the if statement. Then the proximity sensor has to go through the debounce again before Prox1InputVal is high and Prox1State is low?

Does that make sense? I think you're possibly over filtering and need to dumb it down a little;

Read sensor
If sensor high, record time of read
If time of read is greater than lastTime of read + debounceDelay
Increase score
lastTime = newsensorHighTime

I'd definitely focus on the debounce logic which has been taken from: https://www.arduino.cc/en/tutorial/debounce

(a) It is designed for a button wired on the HIGH side with a pulldown resistor. The NPN output of the sensor using an external pull up resistor is wired on the LOW side. The difference is in whether a detection is read as HIGH or a LOW on the pin.

(b) It requires a signal to be stable for a specific time before accepting it. That is probably not appropriate here and is more appropriate for dealing with spurious electrical noise. It also introduces a significant latency. Better in this case would be to immediately accept the first output from the sensor and act on it, then set a timer and, until the timer expires (say after 25mS), refuse to accept any further outputs from the sensor.

There is also quite some repetition in the OP's code.

Thanks all, I’ll give it some thought and work and reply back...

Much appreciate the input and constructive criticism :slight_smile:

Hi,
You need to write some code that JUST detects one input and updates a counter.
NOTHING more in the code, so you can begin to identify the origin of the problem.

Have you tried different materials for the bean bags?

Thanks.. Tom.. :slight_smile:

6v6gt:
I'd definitely focus on the debounce logic which has been taken from: https://www.arduino.cc/en/tutorial/debounce

(a) It is designed for a button wired on the HIGH side with a pulldown resistor. The NPN output of the sensor using an external pull up resistor is wired on the LOW side. The difference is in whether a detection is read as HIGH or a LOW on the pin.

Ah ha!

This was the fix :art:

I was looking for a high signal when in reality it is a low signal.

The only reason it was counting on a "slow" break in the beam was there was enough time to pass for the low signal to return to a high and the debounce timer elapsed and the logic statement picked it up..

Thanks again all

For anyone in the future interested, this was the fix --

  //Proximity Sensor 1
  if (Prox1InputVal == LOW && Prox1OkToPass == 1)
  {
    Prox1OkToPass = 0;
    intCurrentScore = intCurrentScore + 5;
    Prox1Debounce = ulCurrentMilli;
    newMessageAvailable = true;
  }
  
  if ((ulCurrentMilli - Prox1Debounce) > ulProxDebounceDelay && Prox1OkToPass == 0 && Prox1InputVal == HIGH)
  {
    Prox1OkToPass = 1;
  }