Electronic – PIR sensor triggered by itself when using timer, works without timer

atmegaavrinfraredmicrocontrollerproximity-sensor

TL;DR

A PIR sensor is triggered by itself when using a timer to wait 2 seconds after the sensor is triggered.
Without using the timer to wait the sensor works as expected.

Background and setup

I have a PIR sensor connected to an
ATMega328p.
The PIR sensor has three pins; VCC, GND and AL. The AL pin uses open collector to show that motion is
detected.

What I have done is that I connected the AL pin to PC0 pin on my ATMega and set up the pin as input with
internal pull up resistor and a LED connected to PC5 to show when motion is detected:

void setup_default_values()
{
        DDRC |= (1 << PC5);
        DDRC &= ~(1 << PC0);

        PORTC |= (1 << PC0);
}

Then I have a function that checks when the PC0 pin is LOW:

void check_sensor()
{
        if (PINC & (1 << PC0)) {
                PORTC &= ~(1 << PC5);
        }else{
                PORTC |= (1 << PC5);
        }
}

This function lights up the LED when PC0 is LOW and turns it off when PC0 is HIGH.
This function is run every cycle of my main loop:

int main()
{
        setup_default_values();
        blink(4);

    for (;;) {
                check_sensor();
    }

    return 0;
}

Problem encountered

Everything works as expected, the LED lights up when I wave my hand infront of the sensor and it does not trigger
without motion. The sensor sets the AL pin LOW for about 100 – 200 ms.

The thing is that I don't want to use the signal for 2 seconds after motion has been detected. So I implemented
a timer and flag logic that ignores the signal during these 2 seconds. The problem is that now the LED lights up
by itself after the timer has stop counting and start accepting motion signals

Problematic setup

To use this timer and flag logic I added 2 LEDs, one that shows that the timer is active and one that shows when a
signal is registered:

volatile bool motion_detected;

void setup_default_values()
{
        DDRC |= (1 << PC5);
        DDRC |= (1 << PC4);
        DDRC |= (1 << PC3);
        DDRC &= ~(1 << PC0);

        PORTC |= (1 << PC0);

        TCCR1B |= (1 << CS12); // Timer 1B set up to use a prescaler of 256
        TCNT1 = 0; // Timer value set to 0

        motion_detected = false;
}

Then I new logic to the check_sensor function:

void check_sensor()
{
        if (PINC & (1 << PC0)) {
                PORTC &= ~(1 << PC5);
        }else{
                PORTC |= (1 << PC5);

                if (!motion_detected) {
                        PORTC |= (1 << PC3);
                        _delay_ms(50);
                        PORTC &= ~(1 << PC3);

                        PORTC |= (1 << PC4);
                        TCNT1 = 0;        
                        motion_detected = true;
                }
        }
}

When motion is detected the first time, this is what happens:

  • LED indicating signal from PIR is lit up
  • LED indicating signal acceptance it blinked once
  • LED indicating timer is active is lit up
  • Timer is reset and flag indicating motion was detected

Then there is a new function which handles the countdown:

#define F_CPU 1000000UL

/*
 * Timer increments per second
 *
 * This value represents how many increments the timer will do to the TCNT1 register
 * per second. 
 *
 * To produce this value you divide the clock frequency in hz with the prescaler amount.
 * For example: 
 * 1000000 / 256 = 3906
 */

#define TMR_INC_PER_SEC 3906

void check_timer()
{
        if (!motion_detected) return;

        if (TCNT1 >= TMR_INC_PER_SEC * 3) {
                PORTC &= ~(1 << PC4);
                motion_detected = false;
        }
}

int main()
{
        setup_default_values();
        blink(4);

    for (;;) {
                check_timer();
                check_sensor();
    }

    return 0;
}

This function should be ignored unless there is a motion detected. When countdown has
been completed timer indicating LED is turned off and motion flag is reset which indicates
that a new signal can be accepted.

Things I have tried

Software debounce

I have tried adding software debounce functionality where motion is only accepted if PC0 is LOW for
a defined amount of cycles. This did not change the faulty behavior.

Push button instead of PIR

I have tried removing the PIR and add a push button which is LOW on push down. When using the push button
the circuit and logic works as expected.

Schematics

Here is the schematics for the power supply. The circuit is connected to a Stabilized DC power supply where the +5V rail is actually around +6.5V (I don't know how to edit the text of a component in gschem). VCC is what I connect to my microcontroller and PIR-sensor, as you will see in the second schematic.

Power

Here is the schematics for the microcontroller, the LEDs and the PIR-sensor. Some pins are not shown in the schematics: RESET pin is connected to my programmer, same with MOSI, MISO and SCK.

uController

Best Answer

The typical dropout voltage of the 7805 is 2.0V but you only have 6.5V input so the regulator can't work properly. This could mean that any small current spikes in demand on the load side of the regulator eg when switching on LEDs would result in voltage ripples, despite the 10uF capacitor, and PIRs are very sensitive to voltage ripples.

I'd recommend raising the input to the 7805 to at least 7.0V and probably 8.0V to allow for variations in drop out voltage (2.0V is listed as typical, not maximum), and adding an RC filter to the supply to the PIR circuit. However, you'd need to size the R carefully to avoid reducing the PIR circuit supply to less than it's specified minimum (maybe an inductor would be more effective and less loss). Another option would be to use two 7805s, one for the micro and one for the PIR circuit, with common 0V, which would prevent noise from the micro side getting into the PIR circuit so much.