Simple motion sensor output issue

I've got a PIR sensor and I am trying to display whether there is something in front of it using the serial monitor (eventually I want to print to an SD card, but I need to get the sensor running first). Here is my code:

 int PowerPin = 2;
int sensor = 3;
int state = LOW;
int val = 0;

void setup() {
//get power to the sensor through node 2
  pinMode(PowerPin, OUTPUT);
  digitalWrite(PowerPin, HIGH);
  //pinMode(sdcard, OUTPUT);
  pinMode(sensor, INPUT);
  Serial.begin(9600);
}

void loop() {
  val = digitalRead(sensor);
  if (val == HIGH) {
    if (state == LOW) {
      Serial.println("MOTION DETECTED!");
      state = HIGH;
    }
  }
  else {
    if (state == HIGH) {
      Serial.println("no more motion");
      state = LOW;
    }
  }
}

This only outputs a "MOTION DETECTED" upon startup and nothing after. I can't seem to understand where I'm wrong, but I'm sure its a stupid mistake. Can anyone point it out?

To put thing from the real world into logic code is the hardest part of programming.
When you have a good description, then half of the sketch is already written.

Can you describe in your own words what should be done ?

Are you trying to detect the event when the PIR sensor changes its output from low to high ? or both the events that the PIR sensor changes its output state ?
There is a standard solution for that: https://www.arduino.cc/en/Tutorial/BuiltInExamples/StateChangeDetection
I suggest to follow that example and call the variables 'state' for the new state and 'lastState' or 'previousState' or 'oldState' for the previous state.

[ADDED] I could not see the bug, so I decided to make it in Wokwi, and I still don't see the bug, because it works in Wokwi.
Start the simulation (with the start-button in the upper-middle of the screen), click on the PIR sensor, then click the button "Simulate motion", then wait about 5 seconds for it to turn off. The sketch behaves as expected.

"MOTION DETECTED!" appears first because state is initialized as LOW, and the if statement evaluated val as HIGH.

state is then set HIGH, so "MOTION DETECTED!" is not seen again because state is never set LOW again.

The else will only occur if the if fails, so "no more motion" is bypassed (and state is never set LOW (this is why "MOTION DETECTED!" is not seen again).

Do a little work on the 'if..else' or use two 'if' statements. That should get the results you seek.

what about the fact that the outer if-condition

void loop() {
  val = digitalRead(sensor);
  if (val == HIGH) {

evaluates to false as soon as the sensor-pin delivers a "LOW" to variable "val"
and then the else gets executed?

else {
    if (state == HIGH) {
      Serial.println("no more motion");
      state = LOW;
    }

which sets variable "state" to LOW
and additionally the WOKWI-simulation works as expected?

For analysing what is really happening in the TOs code running on the TO-hardware
Serial output will show it.

my guessing is:
the PIR-sensors output stays HIGH for 10 minutes because the high-time is adjusted to the max.

// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * 

// a detailed explanation how these macros work is given in this tutorial
// https://forum.arduino.cc/t/comfortable-serial-debug-output-short-to-write-fixed-text-name-and-content-of-any-variable-code-example/888298

#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);
// usage: dbg("1:my fixed text",myVariable);
// myVariable can be any variable or expression that is defined in scope

#define dbgi(myFixedText, variableName,timeInterval) \
  do { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  } while (false);
// usage: dbgi("2:my fixed text",myVariable,1000);
// myVariable can be any variable or expression that is defined in scope
// third parameter is the time in milliseconds that must pass by until the next time a 
// Serial.print is executed
// end of macros dbg and dbgi
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * 

int PowerPin = 2;
int sensor = 3;
int state = LOW;
int val = 0;

void setup() {
//get power to the sensor through node 2
  pinMode(PowerPin, OUTPUT);
  digitalWrite(PowerPin, HIGH);
  //pinMode(sdcard, OUTPUT);
  pinMode(sensor, INPUT);
  Serial.begin(9600);
}

void loop() {
  val = digitalRead(sensor);
  dbgi("1:",val,500);  // once every 500 milliseconds print value
  dbgi("2:",state,500);
  
  if (val == HIGH) {
    if (state == LOW) {
      Serial.println("MOTION DETECTED!");
      state = HIGH;
    }
  }
  else {
    if (state == HIGH) {
      Serial.println("no more motion");
      state = LOW;
    }
  }
}

YMMD :grinning:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.