-2

The following code is for a temperature controlled relay which turns a DC fan on and off when the temp threshold is breached. I'm using a DS1820 Temperature sensor and the code for this has several variables including WholeDegrees & FractDegrees Which are the whole number of degrees and the fractional number of degrees. I'm wanting to be able to set the threshold to lets say 23.25 Degrees so that when the temp rises above that then the fan comes on.

I'm trying to do this by using a two condition if statement howeveer, the FractDegrees are causing me a problem because if condition 1 is true and and the WholeDegrees is greater than 22 but the second condition the FractDegrees is lower than 25 then the relay turns the fan off again. So I get the fan turning on for a split second and then off, then on & off again which isn't a problem when the temperature sensor is at roughly the temp I want to set the threshold at but when it's at say 40 Degrees The fan cuts in and out whilst cooling until the sensor is roughly at the threshold value again.

I have kinda had a go already but failed which you might see in the //'d out code but is there a way that I can put these two values WholeDegrees & FractDegrees into a string so that I could have a string with the temp value and then use a single condition if statement to turn the fan on and off by checking the strings value instead?? I'd want the string to be "WholeDegrees.FractDegrees" So that I could just use the if statement to ask if the temp was greater than 23.25 degrees. The code I tried returned a value of "0.0" which obviously wasn't the temperature value but I couldn't understand why......

#include <OneWire.h>
int inPin = 10; // define D10 as input pin connecting to DS18S20 S pin
OneWire ds(inPin);
int relay1 = 8; // define relay Signal Pin to Digital Output 8
//String stringOne, stringTwo, stringThree;

void setup(void) {
  Serial.begin(115200);
  pinMode(relay1, OUTPUT);

}

void loop(void) {
  int HighByte, LowByte, TReading, SignBit, Tc_100, WholeDegrees, FractDegrees;
  byte i;
  byte present = 0;
  byte data[12];
  byte addr[8];
  //stringOne = String(WholeDegrees);
  //stringTwo = String(".");
  //stringThree = String(FractDegrees);

  if ( !ds.search(addr)) {
    ds.reset_search();
    return;
  }


  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1);

  delay(250);


  present = ds.reset();
  ds.select(addr);
  ds.write(0xBE);



  for ( i = 0; i < 9; i++) {
    data[i] = ds.read();
  }
  Serial.print("Temperature: ");
  LowByte = data[0];
  HighByte = data[1];
  TReading = (HighByte << 8) + LowByte;
  SignBit = TReading & 0x8000;
  if (SignBit)
  {
    TReading = (TReading ^ 0xffff) + 1;
  }
  Tc_100 = (6 * TReading) + TReading / 4;
  WholeDegrees = Tc_100 / 100;
  FractDegrees = Tc_100 % 100;
  if (SignBit)
  {
    Serial.print("-");
  }
  Serial.print(WholeDegrees);
  Serial.print(".");
  if (FractDegrees < 10)
  {
    Serial.print("0");
  }
  Serial.print(FractDegrees);
  Serial.print(" C\n");
  //int tempthresh;
  //tempthresh = (stringOne&&stringTwo&&stringThree);
  //stringOne += stringTwo;
  //stringOne += stringThree;
  //Serial.println(stringOne); // RETURNS VALUE OF 0.0 When I want Temperature Value to be returned
  //Change The Figure "22" To Adjust The Temperature Threshold To Turn Fan On.
  //Currently Set to Keep Temperature At Or Below 23.25 Degrees
  if ((WholeDegrees > 22)&&(FractDegrees >= 25)) digitalWrite(relay1, HIGH);
  else digitalWrite (relay1, LOW);
  delay(250);


}
  • For your application, a 1°C resolution is good enough. Even in a near boiling pot you will find temp. differences up to 3°C in the water. There is no one "room temp". – user31481 Jul 17 '17 at 9:59
1

In the Arduino IDE, in the Library Manager, are the OneWire library (by a lot of people including Paul Stoffregen) and the DallasTemperature library (by Miles Burton and others). You need both.

The DallasTemperature makes using the DS18B20 easier. Especially when there is more than one DS18B20 sensor connected. It can get the temperature by index or with the unique ID number that each DS18B20 has.
Start with the examples of the DallasTemperature that appear in the Arduino IDE menu, for example the "simple" example.

Then you have the temperature as a float, and you can compare it with 23.25.

Even the OneWire library comes with the DS18x20_Temperature example that shows how to calculate the temperature as a float.

  • Thank you I'll have a look at that. I never thought to check the examples in the libraries themselves. – ArDweNOme Jul 16 '17 at 21:42
1

As has been pointed out looking for a temperature to that level of accuracy on a sensor that is only accurate to 1 degrees in an application which only needs accuracies of a few degrees is just adding extra complications for no real world benefit. In this situation just looking at the whole number part is good enough.

But ignoring the final application this is still a programming question which should be answered.

Using strings to compare numerical values is a very very bad idea. Even ignoring the wasted memory and CPU time involved converting things to a string performing a string to string comparison to evaluate the value is often problematic at best. Normally you'd do it by converting the string to a float.

Which brings us to the easiest solution, as Jot indicated, use a float:

float temp = WholeDegrees + FractDegrees/100.0;
if (temp >= 23.25) { ... }

That's by far the clearest and easiest way to perform the calculation but floats are CPU intensive. There are other, faster ways to do it.

You could do everything in 100ths of a degree, this is a better because ints are a lot quicker to work with and take up less memory than floats. But unless you add comments it's not quite as clear as the float version.

int temp = WholeDegrees*100 + FractDegrees;
if (temp >= 2325) { ... }

Or finally you could do something like your original attempt but fixing the logic. This would probably be the fastest and lowest memory of the lot since it only involved 8 bit calculations which on an 8 bit processor is a big benefit. But of the 3 it's also the least obvious what is happening.

if ( (WholeDegrees > 23) || ( (WholeDegrees == 23) && (FractDegrees >= 25)) ) { ... }
0

This code goes to some lengths to separate the temperature into whole- and fractional-degrees, and to do it in floating point. Unless there is more to the job that isn't shown in this code and requires either of those representations, you can do the whole job with integers, much more simply.

The DS18x20 series return temperatures as fixed-point integers. The "DS1820" that you referred to has only 1/2 degC resolution so testing to 0.25 degrees won't be useful. It delivers the result with 1 fraction-bit (binary point to the left of bit-0). This is equivalent to the number of 1/2-degreesC:

DS1820
+---------------+---------------+---------------+---------------+
|   |   |   |   |   |   |   |   |   |etc|16 | 8 | 4 | 2 | 1 |.5 |
+---------------+---------------+---------------+-----------^---+

                                                 binary point

If you're using the DS18b20 or DS18s20, the result is delivered with 4 fraction bits, binary point to the left of bit-3, or the number of 1/16-degreesC:

DS18x20
+---------------+---------------+---------------+---------------+
|   |   |   |   |   |   |etc|16 | 8 | 4 | 2 | 1 |.5 |.25|   |   |
+---------------+---------------+---------------^---------------+
                                     binary point       .125 .0625

To detect a temperature threshold you only need to compare that single 16 bit number to the equivalent 16-bit representation of the threshold value.

Using your threshold of 22.25 degC, you'd compare to "44" for a DS1820: (2 * 22.25). The remaining fraction, .5, gets truncated off. Or compare to "356" for one of the DS18x20 types: (22.25 * 16).

This function to converts a 4-place fixed point number to an ASCII decimal string for printing:

/*
 * Name:          q12p4toa
 * 
 * Function:      Converts a q12p4 binary number to ASCII decimal
 * 
 * Description:   Convert a 4 binary-places signed fixed point number (Q12.4) to
 *                an ASCII string as: sdddd.dddd
 *                Up to 3 left zeroes are replaced with spaces; 4 decimal places
 *        are always generated.
 * 
 * Parameters:    pbuf            - -> array of at least 10+1 bytes
 *            q12p4         - A fixed-point number to 4 binary places
 * 
 * Returns:       pbuf
 * 
 */

char *q12p4toa(char *pbuf, int16_t fixp)
{
   // If neg, write the sign and negate the number
   *pbuf++ = ' ';      // don't use this leading space if sending to Blynk
   if( fixp < 0 ){
      *pbuf++ = '-';
      fixp = -fixp;
   }
   // Handle the rest as an unsigned int
   (void)snprintf(pbuf, 9+1, "%d.%04d",
          fixp >> 4, (uint16_t)(10000L/16 * (fixp & 0xF)));
   return(pbuf);
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.