Code not detecting input pin - sometimes

I have 2 IR beams in our hallway.
Each is connected to a separate input pin.
Logic is that, in the morning, when the kids walk from the sleeping area to the living area, they break the beams and the PIR (and other) sensors in the living area are detected.

However, what is happening is that sometimes, only 1 of the IR beams is registering.

I have run some tests and get the following :

  1. if the beams are both enabled, and I break one beam, either beam A or beam B seperately, it registers the correct beam as being broken ( for testing, displays in the Serial Monitor ) -- from this I assume that my wiring is all correct.

  2. if I break both beams, either at the same time, or within a fraction of a second of each other, then only the first beam broken registers.

I even added (duplicated) a block of code in case beam B was broken first, and something in the rest of the main code loop was affecting the result before it got back to the section for beam A ( so the checking sequence in the code is : A - B - A )

What I am seeing often in the Serial Monitor is beam A ( 1st code block ) and then beam A ( 2nd code block ) so it is not even seeing beam B.

Any ideas ?

/// Mega 2560

/// in the setup

int BeamHallway = 46;
int BeamHallway2 = 33;

pinMode(BeamHallway, INPUT);      
digitalWrite(BeamHallway, HIGH);  
pinMode(BeamHallway2, INPUT);      
digitalWrite(BeamHallway2, HIGH);  

/// in the loop

      if (ArmHallBeam == 1 && digitalRead(BeamHallway) == LOW){
        LogItWebI(57); //("Hallway Beam Sensors Motion 1.");
        HallBeamMil = currentMillis;
        if(HallBeamCount == 0) HallBeamCount = 1;
        if(HallBeamCount == 2) HallBeamCount = 3;
					Serial.print(currentMillis);
					Serial.print(" - HallBeamCount A1 = ");
					Serial.println(HallBeamCount);
      }
      if (ArmHallBeam == 1 && digitalRead(BeamHallway2) == LOW){
        LogItWebI(58); //("Hallway Beam Sensors Motion 2.");
        HallBeamMil = currentMillis;
        if(HallBeamCount == 0) HallBeamCount = 2;
        if(HallBeamCount == 1) HallBeamCount = 3;
					Serial.print(currentMillis);
					Serial.print(" - HallBeamCount B1 = ");
					Serial.println(HallBeamCount);
      }
      if (ArmHallBeam == 1 && digitalRead(BeamHallway) == LOW){
		LogItWebI(57); //("Hallway Beam Sensors Motion 1.");
		HallBeamMil = currentMillis;
		if(HallBeamCount == 0) HallBeamCount = 1;
		if(HallBeamCount == 2) HallBeamCount = 3;
					Serial.print(currentMillis);
					Serial.print(" - HallBeamCount A2 = ");
					Serial.println(HallBeamCount);
      }

      if (HallBeamCount == 3){
					Serial.print(currentMillis);
					Serial.print(" - HallBeamCount C = ");
					Serial.println(HallBeamCount);
		LogItWebI(59); //("BOTH Hallway Beam Sensors Activated.");
		LogItWebI(60); //("At Home - Hallway Beam Sensors Power De-Activated.");
		digitalWrite(BeamHallPower, LOW);   // disable the hallway beam
		LCDhall = 0;
		ArmHallBeam = 0;
		HallBeamCount = 0;
      }
  1. if I break both beams, either at the same time, or within a fraction of a second of each other, then only the first beam broken registers.

Then, you are not reading the sensors fast enough.

Diddling around writing to the Serial Monitor and your website will work only if you place the two beams about 50 feet apart. And travel between them at a crawl.

Thanks PaulS

I had already removed the website log by placing the log entries into an int array :

void LogItWebI(int LogNo){
  LogNext++;
  if(LogNext >= 29) LogNext = 29;
  LogData[LogNext] = LogNo;
  LogLastMil = millis();
  Serial.println(LogNo);
}

The array only dumps the data to the web site after a period of inactivity ( 10 seconds ), so that shouldn't be affecting the reading.

Next I will isolate all the code for these beams to a separate sketch to determine if the serial print is affecting the reading.

Thanks for the pointers.

Next I will isolate all the code for these beams to a separate sketch to determine if the serial print is affecting the reading.

For testing, two LEDs with current limiting resistors are much faster than serial output. Presumably, the end product will not have debug output, so don't put it in to start with.

If I may suggest, put each beam on an interrupt input (see attachInterrupt). Those are handled very quickly. Inside the ISR make a note of the time (from millis() or micros() calls) and set a flag.

Then the main loop can just check those flags.

Purely as an example (I already had this written):

const byte LED = 12;
const float DISTANCE = 0.9; // m

unsigned long startTime;
volatile unsigned long elapsedTime;
volatile boolean done;
boolean started;

void ballPassesGate1 ()
{
  startTime = micros (); 
  started = true;
    
  digitalWrite (LED, HIGH);  
}  // end of ballPassesGate1

void ballPassesGate2 ()
{
  if (!started)
    return;
    
  elapsedTime = micros () - startTime;  
  done = true;
  started = false;
  
  digitalWrite (LED, LOW);  
}  // end of ballPassesGate2


void setup ()
{
  Serial.begin (115200);
  Serial.println ("Timer sketch started.");  
  pinMode (LED, OUTPUT);
  attachInterrupt (0, ballPassesGate1, FALLING);
  attachInterrupt (1, ballPassesGate2, FALLING);
}  // end of setup

void loop ()
  {
  if (!done)
    return;
    
  Serial.print ("Time taken = ");
  Serial.print (elapsedTime);
  Serial.println (" uS");
  float secs = float (elapsedTime) / 1.0e6;
  float velocity = DISTANCE / secs;
  Serial.print ("Time taken = ");
  Serial.print (secs);
  Serial.println (" seconds.");
  Serial.print ("Average velocity = ");
  Serial.print (velocity);
  Serial.println (" m/s.");
  Serial.println ();
  
  done = false;
  }  // end of loop

That times how long a marble takes to run down a ramp. Yours will be a bit different because you are timing the reverse direction as well, but it shows how you handle the interrupts. This was written for a Uno.

Hi Nick

Thanks for the idea and example. I think you have just found the perfect solution for this specific application.

Just 1 question :

in your sample code, you didn't use "volatile" when declaring 'startTime' and 'started', but did for 'elapsedTime' and 'done'

May I ask why ?

The reference material on the Arduino site says :
"You should declare as volatile any variables that you modify within the attached function."

Also, should the setup include code to specify the interrupt pin as an input, or does the attachInterrupt automatically make it an input pin ?

pinMode(BeamHallway, INPUT);      
digitalWrite(BeamHallway, HIGH);

I don't totally agree with the reference on that point. In my own reference page:

A fair way down is a discussion about volatile variables.

I believe you only need to make volatile variables which are shared between ISRs and non-ISR code (main code). That is so that the compiler knows that the variables may change without warning, and not to cache them in registers.

But inside an ISR, the compiler has to load the variable every time (it can hardly be cached since last time) so variables only used inside an ISR don't need to be volatile. And I think I can safely say that if shared between one ISR and another (only) the same principle applies.

DaveO:
Also, should the setup include code to specify the interrupt pin as an input, or does the attachInterrupt automatically make it an input pin ?

All pins default to inputs.

OK. What about setting the internal pull-up ? or is that also enabled by default ?

No it is not. The default is the "minimal harm". Pins set to inputs without pull-ups. Sort of like a blank slate. Which you write on. :wink:

OK. So logic would be best to set the pin as an input and set to High to enable the internal pull-up before enabling the interrupt.

That way the pin is in the expected state before the interrupt is active.

Many Thanks for all the pointers and feedback.