Checking If sensor reading is in threshold... do something


#1

Friends,

I am trying to write a code to read an external sensor, in this case a temperature sensor, and check if that reading is in range. If it turns out that the variable is out range I would like to trigger other functions to correct it.

here is loop;

void loop() {
    delay(5000);
    TempThreshold( TempHigh, TempLow, TempRead);
    
    if(TempFlagA==true){
        Serial.println("in range, no actions taken");
    }
    if((TempFlagA==false) && (TempFlagB==0)){
        Serial.println("Temp low, heater turned on");
    }
    if((TempFlagA==false) && (TempFlagB==0)){
        Serial.println("Temp high, add more water");
    }
}

here is the function;

void TempThreshold(int hi, int low, int reading){
    if((hi>=reading) && (low<=reading)){
        Serial.println("In");
        TempFlagA=true;
        TempFlagB=-1;
    }
    if(hi<reading){
        Serial.println("out-hi");
        TempFlagA=false;
        TempFlagB=1;
    }
    if(low>reading){
        Serial.println("out-low");
        TempFlagA=false;
        TempFlagA=0;
            
    }
}

obviously this isn’t reading external data, i just built this to see if I could do it. I know the function works, somewhat, because the serial print statements work when I change the number for the read temperature.

The problem i am having is the statements in the loop don’t print after the statements in the functions. Any help is greatly appreciated, feel free to correct my code if their are poor decisions i have made.

Thanks in advance!


#2

First, with mutually exclusive cases (as yours are) use if () ... else if () ... constructs. Once you found one case the others should be impossible.
Second, you are setting TempFlagA = 0 in the third case of your function - I guess it should rather be TempFlagB = 0, right?
Third, in loop() you’re testing for the same condition for two different cases.

And for clarity, I’d do away with TempFlagA and only use TempFlagB with a more intuitive value set (-1 low, 0 in range, +1 high).
These cases can then be distinguished via switch() case.

Additionally, I’d return the outcome of the test from the function rather than using globals.


#3

Lets restructure the code a bit so we can test everything at once.

Code:

boolean TempFlagA = false;
byte TempFlagB = 0;
int TempRead = 0;
int TempHigh = 120;
int TempLow = 60;

// Test the conditions once
int runOnce = 0;

// Print the result of the conditions
void printCurrentConditions() {
  if (TempFlagA==true) {
    Serial.println("in range, no actions taken");
  }

  if ((TempFlagA==false) && (TempFlagB==0)) {
    Serial.println("Temp low, heater turned on");
  }

  if ((TempFlagA==false) && (TempFlagB==0)) {
    Serial.println("Temp high, add more water");
  }
}

// Check our temperature readings
void TempThreshold(int hi, int low, int reading){
    if((hi>=reading) && (low<=reading)){
        Serial.println("In");
        TempFlagA=true;
        TempFlagB=-1;
    }
    if(hi<reading){
        Serial.println("out-hi");
        TempFlagA=false;
        TempFlagB=1;
    }
    if(low>reading){
        Serial.println("out-low");
        TempFlagA=false;
        TempFlagA=0;
            
    }
}

void setup() {

}

// Run though the test cases
void loop() {
  // Only do this once
  if (runOnce == 0) {

    // Case 1: in range
    TempRead = 80;
    Serial.printlnf("Test case #1: (%d)", TempRead);
    TempThreshold(TempHigh, TempLow, TempRead);
    printCurrentConditions();
    Serial.println();

    // Case 2: out-hi?
    TempRead = 121;
    Serial.printlnf("Test case #2: (%d)", TempRead);
    TempThreshold(TempHigh, TempLow, TempRead);
    printCurrentConditions();
    Serial.println();

    // Case 3: out-low?
    TempRead = 58;
    Serial.printlnf("Test case #3: (%d)", TempRead);
    TempThreshold(TempHigh, TempLow, TempRead);
    printCurrentConditions();

    runOnce = 1;
  }
}

Indeed, we have a problem. The in range is fine. Output:

$ particle serial monitor --follow
Polling for available serial device...
Opening serial monitor for com port: "/dev/cu.usbmodem1411"
Serial monitor opened successfully:
Test case #1: (80)
In
in range, no actions taken

Test case #2: (121)
out-hi

Test case #3: (58)
out-low

For your “out-hi” test, you set TempFlagB to 1. None of the other conditions match that as your two statements test for TempFlagA and TempFlagB == 0. Being TempFlagB is 1, these will never match. Actually, the two tests are identical! :neutral_face:

The third test in TempThreshold even has a bug. See:

        TempFlagA=false;
        TempFlagA=0;

So, this first sets TempFlagA to false, but then you set it to zero (0) which in the boolean sense is false anyway. I think you meant TempFlagB?

I am going to make some assumptions here, correct me if they are wrong.

  • If the temperature is too cold, you want to turn on a heater.
  • If the temperature is too high, you want to add more water.

Some additional thoughts are:

  • If the temperature was ever out of range, we want to turn things off if they were on

You will need to check the state of things to see if conditions have changed?

First things first, let’s fix what you have here to turn the water on or turn the heater on.

Small changes to the code, you almost had it:

boolean TempFlagA = false;
byte TempFlagB = 0;
int TempRead = 0;
int TempHigh = 120;
int TempLow = 60;

// Test the conditions once
int runOnce = 0;

// Print the result of the conditions
void printCurrentConditions() {
  if (TempFlagA==true) {
    Serial.println("in range, no actions taken");
  }

  if ((TempFlagA==false) && (TempFlagB==0)) {
    Serial.println("Temp low, heater turned on");
  }

  // TempFlagB you might need to test for ==1?
  //if ((TempFlagA==false) && (TempFlagB==0)) {
  if ((TempFlagA==false) && (TempFlagB==1)) {
    Serial.println("Temp high, add more water");
  }
}

// Check our temperature readings
void TempThreshold(int hi, int low, int reading){
    if((hi>=reading) && (low<=reading)){
        Serial.println("In");
        TempFlagA=true;
        TempFlagB=-1;
    }
    if(hi<reading){
        Serial.println("out-hi");
        TempFlagA=false;
        TempFlagB=1;
    }
    if(low>reading){
        Serial.println("out-low");
        TempFlagA=false;
        // This might be a bug
        //TempFlagA=0; 
        TempFlagB=0;
            
    }
}

void setup() {

}

// Run though the test cases
void loop() {
  // Only do this once
  if (runOnce == 0) {

    // Case 1: in range
    TempRead = 80;
    Serial.printlnf("Test case #1: (%d)", TempRead);
    TempThreshold(TempHigh, TempLow, TempRead);
    printCurrentConditions();
    Serial.println();

    // Case 2: out-hi?
    TempRead = 121;
    Serial.printlnf("Test case #2: (%d)", TempRead);
    TempThreshold(TempHigh, TempLow, TempRead);
    printCurrentConditions();
    Serial.println();

    // Case 3: out-low?
    TempRead = 58;
    Serial.printlnf("Test case #3: (%d)", TempRead);
    TempThreshold(TempHigh, TempLow, TempRead);
    printCurrentConditions();

    runOnce = 1;
  }
}

Output:

Serial monitor opened successfully:
Test case #1: (80)
In
in range, no actions taken

Test case #2: (121)
out-hi
Temp high, add more water

Test case #3: (58)
out-low
Temp low, heater turned on

So, but if the temperature is out of range and you have the heater on or water on, when the temperature goes back into range you may want to add more logic in:

    if((hi>=reading) && (low<=reading)){
        Serial.println("In");
        TempFlagA=true;
        TempFlagB=-1;
    }
// We might have a water source or heater turned on, if we go back
// into range we should turn those sources off.
if (TempFlagA==false) {
  if (TempFlagB == 0) {
    Serial.println("Turning heater off.");
  }
  if (TempFlagB == 1) {
    Serial.println("Turning water off.");
  }
}
// now clear the flags
        TempFlagA=true;
        TempFlagB=-1;

There are multiple ways to get yourself into trouble here. Work on it some more and give us a shout.


#4

Your code would be simpler and more readable if you follow @ScruffR’s advice to return a value from your function, and use a switch case in loop to determine what action to take. Unless you need the global variables for some other purpose, there would be no need for either TempFlagA or TempFlagB. Basically, something like this,

int TempThreshold(int hi, int low, int reading) {
    if (reading < low) {
        return -1;
    }else if (reading > hi) {
        return 1;
    }else {
        return 0; // must be in range if neither of the above two conditions are met
    }
}


void loop() {
    
    int result = TempThreshold( TempHigh, TempLow, TempRead);
    
    switch (result) {
        
        case -1:
            //do what you want when the temperature is below the range
            break;
        case 0:
            //do what you want when the temperature is in range
            break;
        case 1:
            //do what you want when the temperature is above the range
            break;
    }
}

#5

You guys are great, and thats why i love this forum.

I will mess around with my code using the advice from above, and keep you guys posted. Most of my coding experience is in MatLab so its really helpful to learn how things are done in different languages. Usually the same concept but the syntax gives me the most headaches.

Thanks again.