PIR as switch for serial output

Hello everyone, hope today finds you well. Board: UNO R4. The good: I have a NC PIR set as a ON/OFF switch and have it functioning they way I would like. When PIR is activated it goes NO for three seconds, and 65 is sent to PIN 1 serial TX which activates a relay (closed) . When PIR goes back to NC, 97 is sent to PIN 1 serial TX which deactivates the relay (open). This issue I am having is that the board is constantly transmitting 97 on the TX while the PIR is in NC status. I have tried a hundred different things and can not figure out where I am going wrong. I would like it to only transmit 65 once when pir is activated, then when it deactivates three seconds later to transmit 97 one time, then wait for the next activation. I have messed around with many combinations of INPUT_PULLUP/PULLDOWN etc. (yes R4 has built-in PULLDOWN). Any assistance would be greatly appreciated.

void setup() {
    Serial1.begin(9600); //serial pins (TX 1) 9600baud
    pinMode(5,INPUT); //PIR SWITCH NC
}  

void loop() {
  if(digitalRead(5)==HIGH){  //if PIR is activated (NO)...
     Serial1.write(65); //send 65 over serial TX pin 1
     delay(3000); //Factory delay for PIR activation (NO) and deactivation (NC)
    }else if(digitalRead(5)==LOW);{  //When PIR deactivates (NC)...
     Serial1.write(97); //send 97 over serial TX pin 1
     delay(10);

    }
    
}

What you need in general is, or is a cousin to, state change detection.

Here it will suffice to have a variable that keeps track of what the last thing sent out was, and a means to keep the last thing that got sent out from being sent out again.

In pseudo code

  a new character is to be sent by the logic

  if the character is X
    if the last character was not X
         send X, and note that the last character sent was X

  if the character is Y
    if the last character was not Y
       send Y, and note that the last character sent was Y

I can't write and drop that in to your sketch and test it at this time.

But the idea is a variable to hold the last thing (motor is on or off, whatever) and when the logic wants to turn the motor on, it only does it if the motor is currently off, and vice versa.

So you won't get a constant stream of turning the motor on, or Xs or whatever.

Boolean variables (bool) are good for true/false and on/off things, if you were wanting to send a new character on,y if it was the last and you had a number of such characters, than a char variable could be set to the character that goes out as it goes out, and used to test against the next character to see if it is different and should too got out.

BTW instead of 65 and 97, it would read better if you used a character constant, viz:

     Serial1.write('A');  // single quoted A is just another number in C/C++

a7

Although @alto777's response makes sense, it probably isn't the best next step.

You need to detect when the PIR becomes NO and act, rather than act all the time it is NO.

Similarly the NC state

// a global variable
bool pirIsNO;

// in setup
  pirIsNO = digitalRead(5) == HIGH;

// later inside the loo p function
  if (digitalRead(5) == HIGH) {  // if PIR is activated (NO)...
    if (pirIsON == false) {
      Serial1.write('A'); //send 'A' over serial TX pin 1
      pirIsON = true;
    }
  } 
  else {       // just else is required if(digitalRead(5) == LOW);{  //When PIR deactivates (NC)...
    if (pirIsON == true) {
      Serial1.write('a'); //send 'a' over serial TX pin 1
      pirIsON = false;
    }
//     delay(10); take or leave.  pointless?
  }

I removed

// don/t need this, I don't think     delay(3000); //Factory delay for PIR activation (NO) and 

And I say required of the removal of the else/if. You've already read it as HIGH. If it wasn't HIGH it must be LOW. A good habit would be to not read an input until you need a new value.

Once a loop should suffice. Any logic informed by the PIR state should work with the same value for a pass through the loop. You read twice, it may not matter here, but with other inputs are more complicate systems, it needs to be done, or not done, deliberately.

If your test had involved a simple bit of logic

   if (onFire) {

   }
  else if (not onFire) {

  }

would be redundant and usually should be made just an else, with the same body of the removed if.

You also had a stray ';' that cut off the body anyway. Never executed.

    }else if(digitalRead(5)==LOW);{  // 

Nothing is tested at this time, but that last line is a clinker. Go to the IDE preferences and turn up all warnings and verbosity; you made an common mistake but it isn't an error - the compiler may just warn about this, but it def warns about other truly easy and dumb mistakes that can take some time for anyone to find otherwise. Heed the red ink, or try to understand what it is telling you at least.

HTH

a7

the extraneous semicolon doesn't explain everything

why not

bool active;

void loop() {
    if (active)  {
        if (digitalRead(5) == LOW)  { 
            Serial1.write(97);
            active = true;
        }
    }

    // if PIR is activated (NO)...
    else if(digitalRead(5) == HIGH)  {
        Serial1.write(65);
        active = false;
    }
}

void setup() {
    Serial1.begin(9600); //serial pins (TX 1) 9600baud
    pinMode(5,INPUT); //PIR SWITCH NC
}

The OP does not want to turn on and off a string of AAAAAAAAAAAAAAAAAA based on the reading of the digital pin.

a7

what do you think the code i posted does?

ok. the logic is inverted.
active should be set true where it's set false and visa versa.

It's actually clearer when you read the digital pin once, and use that to decide to switch from active to not active, and do anything required when switching.

This would make it conform to the idiomatic state change detection pattern:

  • If you see the PIR is on, and you were inactive, print 'A' and go active.

  • If you see the PIR is off, and you were active, print 'a' and go inactive.

That might make some mistakes easier to spot right away also.

a7

a7, your response is greatly appreciated. I have tried the code you posted and attempted some modifications to no avail. Keep getting Aa repeating on the serial monitor (RealTerm).

// a global variable
bool pirIsON;

void setup() {
  Serial1.begin(9600);
  pinMode(5, INPUT);
}

void loop() {
  pirIsON = digitalRead(5) == HIGH;

// later inside the loo p function
  if (digitalRead(5) == LOW) {  // if PIR is activated (NO)...
    if (pirIsON == false) {
      Serial1.write('A'); //send 'A' over serial TX pin 1
      pirIsON = true;
      delay(10);
    }
  }else 
    if (digitalRead(5) ==HIGH);{
    (pirIsON == true); {
      Serial1.write('a'); //send 'a' over serial TX pin 1
      pirIsON = false;
    } //     delay(10); take or leave.  pointless?
  }
} 

gcjr, I also appreciate your assistance. I have also modified and tried using the example you posted but keep getting A repeating for the duration of the 3 second PIR activation (NO) and no lowercase a at the deactivation (NC).

bool active;

void setup() {
    Serial1.begin(9600); //serial pins (TX 1) 9600baud
    pinMode(5,INPUT); //PIR SWITCH NC
}

void loop() {
    if (active)  {
        if (digitalRead(5) == LOW)  { 
            Serial1.write(97);
            active = true;
        }
    }

    // if PIR is activated (NO)...
    else if(digitalRead(5) == HIGH)  {
        Serial1.write(65);
        active = false;
    }
}


change setting of active from true to false and false to true

We like to help. I can't test now, but one line that you placed in the loop() should be in the setup() function

// in setup
  pirIsNO = digitalRead(5) == HIGH;

just to get synchronized. Sry if my incomplete instructions wasted any of your time.

@gcjr's code probably works with the correction advised, but is a bit confusing as it turns the normal order of things inside out.

The IPO model for process control is a good fit for the little sketches we write that do stuff like turn motors on and off, or LEDs, or react to sensors.

So in this case it would call for reading the PIR, then deciding on the basis of the history whether and what should be done, and then do that.

Sometimes the method is used, but in the name of <whatever> is isn't instantly obvious.

If I post code, I like to post code that I can test. Actually not just that I can test, but have, in fact, tested. When I am in the lab, I will see if I have otherwise mislead you.

a7

This is a real error, it has been mentioned. The semicolon is the end of the if statement.

In my version, I removed the error when I changed the logic a bit - you have put the error back in, or left it when you grabbed the code I wrote uncritically.

Here's a complete sketch. It is tested and works. I switched to serial 1 for the sketch and testing of the logic.

I used a bounceless pushbutton instead of a PIR, which is fine because the PIR has a clean output signal. Using and increasing the odd 10 millisecond delay to 20 would make it work for the bounciest pushbutton.

The simulator has a PIR sensor. I find it easier to work with a proxy, then in real life worry about using a real sensor. The same trick can be played with other sesnros digital and analog. Check out the wokwi simulator, it is a real time saver.

Try it here running the code below:


Wokwi_badge PIR State Change Detection


// https://forum.arduino.cc/t/pir-as-switch-for-serial-output/1224834
// https://wokwi.com/projects/390019934465917953

/// switched from serial1
/// pushbutton ot a PIR - pushbutton has bouncing turned off!

// a global variable flag
bool pirIsON;

void setup() {
  Serial.begin(9600);
  pinMode(5, INPUT_PULLUP);
  
  pirIsON = digitalRead(5) == HIGH;
}

void loop() {

  bool sensorActive = digitalRead(5) == HIGH;

  if (sensorActive) {  // if PIR is activated (NO)...
    if (pirIsON == false) {
      Serial.write('A'); //send 'A' over serial TX pin 1
      pirIsON = true;
    }
  } 
  else {       // just else is required if(digitalRead(5) == LOW);{  //When PIR deactivates (NC)...
    if (pirIsON == true) {
      Serial.write('a'); //send 'a' over serial TX pin 1
      pirIsON = false;
    }
//     delay(10); take or leave.  pointless?
  }
}

Please read through the code line by line, review the prosaic explanations of state change detection and of course look into the syntax of the *if* statement. ;-)

HTH

a7

@gcjr with a few tweaks, this is working great. Thanks for the input!

bool active;

void setup() {
    Serial1.begin(9600); //serial pins (TX 1) 9600baud
    pinMode(5,INPUT); //PIR SWITCH NC
}
void loop() {
    if (active)  {
        if (digitalRead(5) == LOW)  { 
            Serial1.write(97);
            active = false;
            
        }
    }

    // if PIR is activated (NO)...
    else (active);
        if(digitalRead(5) == HIGH)  {
        Serial1.write(65);
        active = true;
        delay(3000);
    }
}


Thank you for the work you have put into this. I have learned a lot and look forward to using this with my project.

It's usually a thankless task, so you are welcome.

The code you posted does not seem to fulfill the requirements. Of course it may now be doing what you want it to, if so then never mind.

I used the IDE Autoformat tool. It can show errors sometimes, here is what your sketch says (again, to test I just used Serial, not Serial1, and a pushbutton not a PIR

bool active;

void setup() {
  Serial.begin(9600);
  pinMode(5, INPUT_PULLUP); 
}

void loop() {
  if (active) {
    if (digitalRead(5) == LOW)  {
      Serial.write(97);
      active = false;
    }
  }
  else (active);     // this else body statement is syntactically valid and does absolutely nothing.

  
  if (digitalRead(5) == HIGH)  {
    Serial.write(65);
    active = true;
    delay(3000);
  } 

}

It will print 'A' every three seconds while the PIR is in one state, then when the PIR changes, it will print 'a' just once.

Please review both complete solutions you have been offered - read them line by line and see why they work.

Until you can write well-formatted code automatically, it is a good idea to use the Autoformat tool, both for yourself and for those you ask to read your code.

I was actually stopping by to comment on the equivalence of the offered solutions. Explicitly or implicity. in both there are two statements of the form

  if (a)
    if (b) {

    }

which is logically equivalent to

  if (a && b) {

  }

So the entire logic is

  if (active && digitalRead(5) == LOW) {
// go inactive and print something
  }

  if (not active && digitalRead(5) == HIGH) {
// go active and print something else
  }

since && the and operator is commutative, we have essentially the same logic.

With the exception of the repeated reading of the digital pin, which I don't even want to bother checking to see how it could ever be a problem, as it is better to solicit inputs once at the top of the loop, and use variables those readings inform for dealing with them subsequently.

Both offered solutions fail with pushbutton input due to contact bouncing. Debouncing pushbuttons is necessary, usually. The PIR does not have brief periods where it is switching rapidly as if it were a mechanical switch, so no need to address that here.

a7

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