Conditional statement with two elements evaluates (wrongly) to TRUE

I approach this query with some trepidation, because I seem to antagonize the folks on this forum by asking questions in the wrong wayor posting code incorrectly, or GOK what else. (One person objected to my "using words of more than 2 syllables".) I am an 81 year old noob and am doing the best I can. If I have still not mastered this forum to the satisfaction of those who find me inappropriate, please - gently - instruct me.

I want to run code based on either one of two conditions being true. I have noodled around with syntax, but don't seem to be able to get it to work.
If I select either one of the two conditions, alone, it works, but if I try to get Condition1 OR condition2, it doesn't work.
I first combined the two conditions in a single "if" statement:

 if (((TimeNow - TimeStart) >= TimeLower)  ||  (digitalRead (BottomLevelSwitch) == LOW )) 

This did not give a compiler error message, but it didn't work.
(Basically the entire expression defaulted to TRUE on its first pass, even though neither condition was TRUE.)
I showed it to my son, who works in IT, and he could not see any obvious flaws in syntax, (which doesn't mean there isn't something which a third pair of eyes doesn't immediately pick up.)

Eventual work around, (which does work. With my frustrated annotation):

  case    ST_LOWERING:    
               Serial.print("State = ST_LOWERING: ");      

               /*  ARGH!  When I try to run if statement with two conditions separated by OR, (||)
                *   it doesn't work.   Crappy fix: run as two separate,
                *   sequential ifs, with Times at top of sequence */
                                                         
               if ((TimeNow - TimeStart) >= TimeLower)
                                              
              {
                digitalWrite(LowerPin, HIGH);      // Turn off Lower relay 
                    State = ST_LOWERED; 

              }                                   // end of if control by time
              

               if (BottomLevelRead == LOW) 
                       //When bottom switch is depressed, current flows to Gnd, and pin becomes LOW 
                       
                   {
                     digitalWrite(LowerPin, HIGH);      // Turn off Lower relay 
                     State = ST_LOWERED; 

                   }                                   // end of if triggered by switch
             
        break; 

I put this in a sketch, and it worked (printed OK):

void setup() {
  Serial.begin(115200);
  int TimeNow = 1; int TimeStart = 0; int TimeLower = 1; bool BottomLevelSwitch = LOW;
  if (((TimeNow - TimeStart) >= TimeLower)  ||  (digitalRead (BottomLevelSwitch) == LOW ))
    Serial.print("OK");
}
void loop() {}

The two conditions aren't identical. The combined version includes the digitalRead() function while the separated version seems to store that in a variable to be checked later. How can you be sure that the state of the digital input wasn't low at the moment the condition was checked?

This looks ok syntax-wise.

I did note that your "work-around" of 2 sequential if statements are not quite the same logic as the if statement above... specifically.

Can you please post your entire sketch (the one that doesn't work). It is always easiest to see what might be happening if we have all the code.

can you show us what you had written really when it failed?

for example if it was in a switch/case and you declared a local variable in the case then that's very likely the issue.

case    ST_LOWERING:    
  unsigned long     TimeNow = millis(); // <=== THIS IS NOT GOOD
  if (((TimeNow - TimeStart) >= TimeLower)  ||  (digitalRead (BottomLevelSwitch) == LOW )) {
    ...
  }

PS: that's why we say "Don't post snippets" (Snippets R Us!)

This should probably be:

if (digitalRead (BottomLevelSwitch) == LOW)

Many times the issue is in the code you DON'T post or the wiring.

What is the pinMode of BottomLevelSwitch? How is BottomLevelSwitch pin wired?

:+1:

You are doing just fine !


The more information we get from you, the more effective we can be in our responses.


Always show us a good schematic of your proposed circuit.
Show us a good image of your ‘actual’ wiring.
Give links to components.


In the Arduino IDE, use Ctrl T or CMD T to format your code then copy the complete sketch.

Use the </> icon from the ‘posting menu’ to attach the copied sketch.

1 Like

<3!

With this,

if (((TimeNow - TimeStart) >= TimeLower)  ||  (digitalRead (BottomLevelSwitch) == LOW ))

you're not checking the return value of digitalRead. Try this ...

if (((TimeNow - TimeStart) >= TimeLower) || !digitalRead (BottomLevelSwitch))

where the 2nd condition becomes true if BottomLevelSwitch goes LOW (reads 0) The conditional test should work because the ! (not) is on the left side of the condition which gets the returned value.

??? what do you mean

(digitalRead (BottomLevelSwitch) == LOW ) is a perfectly written condition returning a boolean and fully respecting types and not using hidden knowledge about HIGH and LOW

this !digitalRead (BottomLevelSwitch) is not really a great way as it makes undocumented assumption that LOW is compatible with an integral type and worth 0.

Uh-oh ... I've been using the ! operator extensively without issue. Int 0 has always evaluated to LOW or false (for me).

yes 0 has always been evaluated to false that's in the spec

the fact that LOW is the integer 0 is not documented though (besides by looking into the source code). so writing code dependant on this is not the best recommendation (as I posted recently in another thread, see Breakage caused by PinStatus and PinMode types · Issue #25 · arduino/ArduinoCore-API · GitHub to get a feel for what could have happened)

my point was more on the fact that you wrote

you're not checking the return value of digitalRead

I don't get that. OP is comparing the returned value with LOW

Yes, I get that. Sorry, my post was really misleading. However, placing digitalWrite on the right side of the condition should also work (but I still need the links(s) you've provided. (thanks).

@maxwelldm Are you using INPUT_PULLUP or external pullup for BottomLevelSwitch?

EDIT: How is your switch connected ... does it switch to VCC or GND when pressed or closed?

yes, sure it's not a best practice but will work too

I did not want to clutter up the issue with extraneous detail, since it seemed to me to a straight-forward issue of syntax. However, I will give you as much detail as I can. I had over-written the original code with the inelegant work-around, but have reconstituted it as best I can. The note by redcar that the original (digitalRead(BottomLevelSwitch)== LOW is not quite the same as the (later) (BottomLevelRead == LOW), is quite correct. I had added a variable to hold the value, in the hopes that this would fix the problem. It didn't.

It may be useful to specify what is happening here. There is an insulating curtain under the glazing in my solar greenhouse, which closes at night to retain the heat captured through the day. The two switches are micro-switches mounted on the tracks under the curtain. The lower level switch is closed when the curtain reaches the bottom limit. (The upper level switch will also be closed as soon as the curtain has started to descend.) With the raising, the upper switch will be closed until the curtain has rolled above it, and allowed it to open, (dropping the pin to LOW

Code:

/*
  Program flow:
  In loop, initialize State to ST_Init.  This then leads to  Switch, which is entered with State at ST_Init,
  and first time through gets set to ST_LOWERING. Program then goes back to  beginning of switch loop.
  This will drop through case ST_INIT into ST_LOWERING.  If conditions for this to run are met, this runs,
  State gets reset to ST_LOWERED, and loops back to beginning of Loop.

  This continues until and unless program drops through all levels of Switch, at which time,
  it drops through end of Switch loop, and returns back to beginning of main loop.  Correct?
*/

/*  Connections:

   Connect photo sensor:
   One side to 5V  (No polarity)
   Other leg to Pin A0 on Arduino
   10K pull-down resistor between return from photoresistor and Gnd

  Conect both limit switches with 10K resistors between 5V and control pin, with
  limit switch on far side of resistor, connected to Gnd.  (Ie. 10K resistor acts as pull-up)
  Top level switch is wired as Normally Open.  As long as curtain is higher than switch,
  (ie fully open), TopLevelSwitch pin,(Pin 7), will be HIGH.  (Will drop to LOW
  as soon as curtain starts to close.)
  Bottom Level switch similarly wired as Normally Open.  When curtain is fully down,
   BottomLevelSwitch pin, (Pin 8), will drop to LOW.
   (Both switches wll be closed, and both read pins will be LOW)
  When curtain is fully up, both switches will be open, and both pins will be HIGH

  Raise and Lower pins trigger relay to activate when they go LOW.
  (At rest, both pins are set to HiGH)

  Timing:  TimeNow is set to millis() at top of loop. TimeStart on firat iteration
  starts at blank value, so any test of timing will be false.  With trigger event,
  (ST-Raised, or ST-Lowered), StartTime gets set to current millis, pgm drops through to action,
  (Raising or Lowering), and start time remains fixed, while TimeNow augments with each iteration
  through the loop, until condition is met, at which point relay is turned off and pgm drops through
  to next state.

  To run display portion, connect Arduino, then choose Port,(7), under Tools
  Then, also under Tools, choose Serial Monitor, (or Ctrl-Shift-M)
*/
// VARIABLES

int
PhotocellPin = A0,    // Define input pin
TopLevelRead,         // Values of levels of switch pins
BottomLevelRead;

const float
LowerLightLevel = 300,
UpperLightLevel = 700;

const unsigned long
TimeRaise = 115000,       // Measured time, 1'55"
TimeLower = 130000;       // Increased Jan 6, 2023

const int             // Limit switches
TopLevelSwitch = 7, // These 2 are inputs & change with state of limit switches
BottomLevelSwitch = 8,
RaisePin = 2,      // These 2 control relays to run motor
LowerPin = 3;

unsigned long
TimeStart,
TimeNow;


enum           // States
{
  ST_INIT = 0,
  ST_RAISING,
  ST_RAISED,
  ST_LOWERING,
  ST_LOWERED
};

// End Variables

void setup( )
{
  //set control pins to OUTPUT, and initialise to HIGH. (They will default to LOW otherwise)
  digitalWrite(RaisePin, HIGH);
  digitalWrite(LowerPin, HIGH);

  pinMode(RaisePin, OUTPUT);
  pinMode(LowerPin, OUTPUT);
  pinMode (PhotocellPin, INPUT);
  pinMode (TopLevelSwitch, INPUT);
  pinMode (BottomLevelSwitch, INPUT);

  // Initialize display of readings
  Serial.begin(9600);

}                 // end of setup



void loop( void )
{
  static byte
  State = ST_INIT;

  TimeNow = millis();


  //Go get reading for LightValue
  int LightValue = analogRead(PhotocellPin);

  // Get values of read pins
  TopLevelRead = digitalRead(TopLevelSwitch),
  BottomLevelRead = digitalRead(BottomLevelSwitch),

  // Print light value to serial output, for debugging only
  Serial.print("Light Value: ");
  Serial.println(LightValue);
  Serial.print("TimeStart: ");
  Serial.print(TimeStart);
  Serial.print("  ");
  Serial.print("TimeNow: ");
  Serial.println(TimeNow);
  Serial.print("Top Switch:  ");
  Serial.print(TopLevelRead);
  Serial.print("  ");
  Serial.print("Bottom Switch:  ");
  Serial.println(BottomLevelRead);
  Serial.println (" ");
  delay (2000);



  // And now drop into case ST_INIT

  switch ( State )
  {
    case    ST_INIT:

      Serial.print("State = ST_INIT: ");


      // Initialize to "home" the curtain closed if it is not already at bottom
      if (( BottomLevelRead) == HIGH )    // Curtain is not at bottom .
      {
        digitalWrite (RaisePin, HIGH);      // Raise relay is off
        digitalWrite(LowerPin, LOW);         //Lower relay on. Curtain is going down

        TimeStart = TimeNow ;               // Set TimeStart at beginning of lowering

        State = ST_LOWERING;
      }                           //end of "if" testing whether curtain is at lower limit


      if (BottomLevelRead == LOW)      // Curtain is already closed and lower level switch is closed
      {
        digitalWrite(LowerPin, HIGH);             //Lower relay off, (which it should be by default anyway)
        digitalWrite (RaisePin, HIGH);

        State = ST_LOWERED;
      }                                          //end of "if" testing whether curtain has reached lower limit

      break;                                    // Exit from case


    case    ST_LOWERING:
      Serial.print("State = ST_LOWERING: ");

      if (((TimeNow - TimeStart) >= TimeLower)  ||  (digitalRead (BottomLevelSwitch) == LOW ))
      {
        digitalWrite(LowerPin, HIGH);      // Turn off Lower relay
        State = ST_LOWERED;

      }

      break;


    case    ST_LOWERED:
      Serial.print("State = ST_LOWERED ");
      if ( LightValue >= UpperLightLevel )  // Curtain is down, dawn has broken
      {
        TimeStart = TimeNow ;    //Set start time for raising
        digitalWrite(LowerPin, HIGH);
        digitalWrite(RaisePin, LOW);    //Raise relay on
        State = ST_RAISING;

      }                                   //end of 'if'

      break;

    case    ST_RAISING:
      Serial.print("State = ST_RAISING: ");

      if (((TimeNow - TimeStart) >= TimeRaise)  || (digitalRead (TopLevelSwitch) == LOW ))
      {
        digitalWrite(RaisePin, HIGH);      // Turn off Raise relay
        State = ST_RAISED;

      }                                  // end of 'if'
      break;

    case    ST_RAISED:
      Serial.print("State = ST_RAISED: ");

      if ( LightValue <= LowerLightLevel )   // Darkness has fallen
      {
        TimeStart = TimeNow;            // Set start time for lowering
        digitalWrite(LowerPin, LOW);    //Lowering relay on
        State = ST_LOWERING;

      }                           //  end of if


      break;

  }                                     //end of switch
}                                             // End of main loop

I have created a circuit diagram, (just below), but have not bothered to put in the detail of the relay circuit. Suffice it to say that the signals from the Raise and Lower pins trigger one or other of the paired relays, which are wired cross-wise, reversing the polarity of the current sent to the motor which raises and lowers the rolled-up curtain. I also removed the jumper on the relay module, and added an independent 5V power block to drive the relays themselves, (to avoid back-current interference). (And this part of the circuit has never been a problem.)

Circuit diagram:

And photograph, (for whatever you feel it is worth ) The various wires going off the block run to the photocell and the two switches.

(And, yes, the connector block has just come loose today. And the curtain is still going up and down, so the connections are still intact.)

Please draw out the interconnections from the Arduino 5v pin to Vcc on the relay module with the external relay power supply included.

So does it fail with that code? The logic feels ok if everything is wired appropriately and no bouncing (actually even bouncing should not be a problem)

Do you have high current through the breadboard?

You might want to compare your writing style with that of the norm.

For example:


const float
LowerLightLevel = 300,
UpperLightLevel = 700;

. . .

  static byte
  State = ST_INIT;

The above works but is a bit difficult the come to grips with.


A more standard way would be:

const float LowerLightLevel = 300;
const float UpperLightLevel = 700;

. . .

  static byte State = ST_INIT;

In the latter, a volunteer needs a quick glance to confirm formatting, the former they need to examine multiple lines.


Edit

Suggest you look at switch change in state instead of switch level.

case    ST_RAISING:
if (((TimeNow - TimeStart) >= TimeRaise)  || (digitalRead (TopLevelSwitch) == LOW ))

According to your circuit diagram, when the switch opens, the input gets pulled HIGH.

Review these changes to your sketch.

We can discuss things you do not understand.


//
// https://forum.arduino.cc/t/conditional-statement-with-two-elements-evaluates-wrongly-to-true/1074124?u=larryd
//

//********************************************^************************************************
// Version    YY/MM/DD    Description
// 1.00       23/01/07    Running sketch
// 1.10       23/01/08    Added comments
//
//
//

#define relayOn                      LOW
#define relayOff                     HIGH

#define switchClosed                 LOW
#define switchOpen                   HIGH

#define ENABLED                      true
#define DISABLED                     false

//****************************
const byte photocellPin            = A0;

const byte raiseRelay              = 2;    //LOW  turns ON  the relay
const byte lowerRelay              = 3;    //HIGH turns OFF the relay

const byte topSwitch               = 7;    //+5V---[10k Pullup]---[Input Pin]---[LimitSwitch]---GND
const byte bottomSwitch            = 8;

const byte heartbeatLED            = 13;   //+5V---[A->|-K]---[220R]---[Pin13]

byte topSwitchState;
byte bottomSwitchState;

const unsigned int lowerLightLevel = 300ul;
const unsigned int upperLightLevel = 700ul;

int lightValueLevel;

//****************************
//states in our machine
enum stateMachine
{
  ST_INIT,
  ST_RAISING,
  ST_RAISED,
  ST_LOWERING,
  ST_LOWERED
};

//create and initialize the Machine
stateMachine State = ST_INIT;

//****************************
//timing stuff

//const unsigned long timeRaise      = 115 * 1000ul; //115 seconds
//const unsigned long timeLower      = 130 * 1000ul; //130 seconds

const unsigned long timeRaise      = 10 * 1000ul;  //for testing 10 seconds <----<<<<
const unsigned long timeLower      = 15 * 1000ul;  //for testing 15 seconds <----<<<<

unsigned long heartBeatInterval    = 500ul;

unsigned long startTime;
unsigned long heartbeatTime;
unsigned long monitorTime;
unsigned long readPinsTime;
unsigned long machineTime;


//                                       s e t u p ( )
//********************************************^************************************************
void setup()
{
  Serial.begin(9600);

  digitalWrite(raiseRelay, relayOff);
  digitalWrite(lowerRelay, relayOff);

  pinMode(raiseRelay, OUTPUT);
  pinMode(lowerRelay, OUTPUT);

  pinMode(heartbeatLED, OUTPUT);

  pinMode (topSwitch, INPUT_PULLUP);
  pinMode (bottomSwitch, INPUT_PULLUP);

}//END of   setup()


//                                        l o o p ( )
//********************************************^************************************************
void loop()
{
  //*************************************         heartbeat  T I M E R
  //to see if the sketch has blocking code,
  //time to toggle the heartbeat LED ?
  if (millis() - heartbeatTime >= heartBeatInterval)
  {
    //restart the TIMER
    heartbeatTime = millis();

    //toggle the LED
    digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
  }

  //*************************************         read inputs  T I M E R
  //is it time to check the inputs ?
  if (millis() - readPinsTime >= 50ul)
  {
    //restart the TIMER
    readPinsTime = millis();

    //time to check our switches
    checkInputs();
  }

  //*************************************         serial monitor  T I M E R
  //is it time to print to the serial monitor ?
  if (millis() - monitorTime >= 2000ul)
  {
    //restart the TIMER
    monitorTime = millis();

    updateMonitor();
  }

  //*************************************         State Machine  T I M E R
  //is it time to service our State Machine ?
  if (millis() - machineTime >= 10ul)
  {
    //restart the TIMER
    machineTime = millis();

    checkMachine();
  }

} //END of   loop()


//                                c h e c k M a c h i n e ( )
//********************************************^************************************************
void checkMachine()
{
  //*************************************
  //State Machine
  switch (State)
  {
    //************************
    case ST_INIT:
      //at powerup check inputs
      checkInputs();

      //Initialize to "home" the curtain closed if it is not already at bottom
      //is the curtain at the bottom ?
      if (bottomSwitchState == switchOpen)
      {
        //make curtain go down
        digitalWrite (raiseRelay, relayOff);
        digitalWrite(lowerRelay, relayOn);

        //reset the TIMER
        startTime = millis();

        State = ST_LOWERING;
      }

      //the curtain is at the bottom
      else
      {
        //stop the motor
        digitalWrite(raiseRelay, relayOff);
        digitalWrite(lowerRelay, relayOff);

        State = ST_LOWERED;
      }

      break; //END of case

    //************************
    case ST_LOWERING:
      //normal flashing rate
      heartBeatInterval = 500ul;

      if (millis() - startTime >= timeLower)
      {
        //stop the motor
        digitalWrite(raiseRelay, relayOff);
        digitalWrite(lowerRelay, relayOff);

        State = ST_LOWERED;
      }

      break; //END of case

    //************************
    case ST_LOWERED:
      //************
      //the bottom switch should be closed at this point
      if (bottomSwitchState == switchOpen)
      {
        //flag this as an  E R R O R condition  <-----<<<<<
        //set the flashing rate to fast
        heartBeatInterval = 100ul;
      }

      else
      {
        //normal flashing rate
        heartBeatInterval = 500ul;
      }

      //************
      //the curtain is down, has dawn has broken ?
      if (lightValueLevel >= upperLightLevel)
      {
        //normal flashing rate
        heartBeatInterval = 500ul;

        //reset the TIMER
        startTime = millis();

        digitalWrite(raiseRelay, relayOn);
        digitalWrite(lowerRelay, relayOff);

        State = ST_RAISING;
      }

      break; //END of case

    //************************
    case ST_RAISING:
      if (millis() - startTime >= timeRaise)
      {
        //stop the motor
        digitalWrite(raiseRelay, relayOff);
        digitalWrite(lowerRelay, relayOff);

        State = ST_RAISED;
      }

      break; //END of case

    //************************
    case ST_RAISED:
      //************
      //the top switch should be closed at this point
      if (topSwitchState == switchOpen)
      {
        //flag this as an  E R R O R condition  <-----<<<<<
        //set the flashing rate to fast
        heartBeatInterval = 100ul;
      }

      else
      {
        //normal flashing rate
        heartBeatInterval = 500ul;
      }

      //************
      //the curtain is up, has darkness fallen ?
      if (lightValueLevel <= lowerLightLevel)
      {
        //normal flashing rate
        heartBeatInterval = 500ul;

        //restart the TIMER
        startTime = millis();

        digitalWrite(raiseRelay, relayOff);
        digitalWrite(lowerRelay, relayOn);

        State = ST_LOWERING;
      }

      break; //END of case

  } //end of switch/case

} //END of   checkMachine()


//                               u p d a t e M o n i t o r ( )
//********************************************^************************************************
void updateMonitor()
{
  //print to the serial monitor
  Serial.print("Light Value:\t");
  Serial.println(lightValueLevel);

  Serial.print("TimeStart:\t");
  Serial.print(startTime);
  Serial.print("\t");
  Serial.print("TimeNow:\t");
  Serial.println(millis());

  Serial.print("Top Switch:\t");
  Serial.print(topSwitchState);
  Serial.print("\t");
  Serial.print("Bottom Switch:\t");
  Serial.println(bottomSwitchState);

  Serial.println ("");

} //END of   updateMonitor()


//                                 c h e c k I n p u t s ( )
//********************************************^************************************************
void checkInputs()
{
  //*************************************                     L D R
  //read lightValueLevel
  lightValueLevel = analogRead(photocellPin);

  //*************************************                     t o p S w i t c h
  byte currentState = digitalRead(topSwitch);

  //************************
  //was there a change in state ?
  if (topSwitchState != currentState)
  {
    //update to the new state
    topSwitchState = currentState;

    //*************
    //was the switch closed ?
    if (currentState == switchClosed)
    {
      //what state is the machine in ?
      switch (State)
      {
        //******
        case ST_RAISING:
          //next state
          State = ST_RAISED;

          //stop the motor
          digitalWrite(raiseRelay, relayOff);
          digitalWrite(lowerRelay, relayOff);

          break; //END of case

      } //END of   switch/case
    }

    //*************
    //switch was opened
    else
    {
      //do nothing
    }

  } //END of this switch

  //*************************************                     b o t t o m S w i t c h
  currentState = digitalRead(bottomSwitch);

  //************************
  //was there a change in state ?
  if (bottomSwitchState != currentState)
  {
    //update to the new state
    bottomSwitchState = currentState;

    //*************
    //was the switch closed ?
    if (currentState == switchClosed)
    {
      //what state is the machine in ?
      switch (State)
      {
        //******
        case ST_LOWERING:
          //next state
          State = ST_LOWERED;

          //stop the motor
          digitalWrite(raiseRelay, relayOff);
          digitalWrite(lowerRelay, relayOff);

          break; //END of case

      } //END of   switch/case
    }

    //*************
    //switch was opened
    else
    {
      //do nothing
    }

  } //END of this switch

  //*************************************                     E R R O R
  //if both switches are closed, this is an ERROR
  if (bottomSwitchState == switchClosed && topSwitchState == switchClosed)
  {
    //set the flashing rate to fast
    heartBeatInterval = 100ul;

    //stop the motor
    digitalWrite(raiseRelay, relayOff);
    digitalWrite(lowerRelay, relayOff);
  }

} //END of   checkInputs()


//********************************************^************************************************


Edit

Updated the schematic: