Projects

The Heat Is On Thermostat - Part 1

DIY Digital Thermostat

Johann Wyss

Issue 33, April 2020

This article includes additional downloadable resources.
Please log in to access.

Log in

Build this Arduino-based digital thermostat with adjustable temperature, temperature display, and blackout protection.

BUILD TIME: 2 HOURS
SKILL LEVEL: INTERMEDIATE

This project will enable you to thermostatically control a fan, lamp, heater, etc. to maintain a temperature within a few degrees, regardless of the ambient temperature. It uses a waterproof sensor that can measure between -55°C and +125°C, and displays the current and trigger temperatures on an LCD screen. The temperatures can be adjusted easily using the push buttons, and the circuit also retains the temperature settings in case of any power outage.

You could use this project for comfort purposes by turning on a fan or heater depending on the temperature, use it to maintain the right temperature on your beer or cider brew, control an infrared heat lamp on a reptile enclosure, and much more.

We have split this project into two parts. In this issue, we will build and demonstrate the prototype, and in our next issue, we will design and build a compact and low-power circuit on a PCB, with a 3D enclosure to suit.

A LITTLE HISTORY

Arguably one of the most undervalued contributions to modern living and comfort is the humble thermostat. The thermostat device was first invented by Scottish Chemist Andrew Ure in 1830 and was a completely mechanical design consisting of a bi-metallic strip that bends as temperature changes allowing the strip to control a set of switch contacts. The switch could be used to turn off a heating element at some temperature to which the contacts have been set. Sadly, few people at the time saw the potential in the device and it saw very little use until the Swiss inventor Albert Butz innovated on the concept 55 years later in 1885. Ure’s original design was limited to working only at the specific temperature it was designed to operate at, as the metal strip itself was the actuator which was fixed in place a set distance from the contacts, and thus, no adjustment was possible. Butz revolutionised the device allowing the temperature at which the thermostat actuated to be changed. This made the device ideal to control the heating inside buildings using old coal fired furnaces. With the success of this device, more and more industries saw the potential of an automatic thermal regulator so the device became very popular.

Nowadays, the thermostat is used in automobiles, fridges, ovens, air conditioning, and many countless industrial processes from food and beverage production to horticulture. In fact, it’s quite likely that very few industrial processes today operate without some form of thermostat thermal regulation. Whilst today we generally use electrical thermostats, the the mechanical bi-metallic strip kind is still very common due to its low cost, impressive lifespan and reliable accuracy.

HOW IT WORKS

A thermostat is in its simplest form a switch that actuates a device when a temperature threshold is met. The thermostat operates like a switch which is a binary device, in the sense that thermostats only have two operation modes fully on or fully off. (This is something to keep in mind when someone in your household insists on turning the thermostat all the way up on a cold day. It does not actually heat a room any quicker than leaving it at the desired temperature). There is no control of the signal outside of these parameters. Until quite recently, most thermostats still used the original bi-metallic strip design very similar to the original design from Andrew Ure in the 1800s.

The principle of this strip is actually quite clever and simple. It uses two dissimilar metals such as steel and copper laminated together, each with a different coefficient of thermal expansion. This means the two metals expand at different rates, usually one expands a lot and the other very little.

Since the two metal strips are bound together, laminated by welding, brazing or riveting, any temperature change will cause the bimetallic strip to bend into a curve.

*Bi-Metallic strip formed for example from Steel & Brass.

The lower metallic strip expands much more than the higher strip. As it warms, it attempts to expand but the upper strip prevents this expansion due to its much lower expansion rate. This causes both strips to pull away from the contacts opening them. For instance, steel expands ~5µM for every metre of length per 1°C (~5µm/m°C) while brass expands ~10µm/m°C.

In this simple design, the fixed point can’t easily be changed, and thus, the temperature of switching is generally also fixed. However, that isn’t the only limitation. In such a system, it is very easy for the thermostat to oscillate rapidly between on and off states, as small variations in temperature can cause the contacts to open and close constantly. Opening contacts can cause electric arcing which can burn away the contact material leading to early failure of the contacts and a change in the operating temperature as the contact dimensions change. To overcome this, more modern mechanical thermostats used a coil of the bi-metallic strip, and a spring loaded contact set that snaps on and off and establishes hysteresis; a difference between turn on and turn off values.

A helical wound bi-metallic strip is mounted in the centre with a coil tied to a central point with a contact on the other end. The axis of the Helix can be rotated to change the position at which the contacts meet, meaning that the contacts can be adjusted to close at a desired temperature. This is called the 'Set Temperature'.

In the following diagram, the lever (1) changes the angle of the bi-metallic helix (2) moving the position of the moving contact (4) toward or away from the fixed contact (5). The magnet (6) provides an extra movement when contact (4) approaches contact (5) which hastens the closing of the contacts and avoids switch bounce and chatter. When the contacts are closed, it creates an electrical connection between the input (3) and output (5).

Heater thermostat and refrigeration thermostat are the same but the Bi-metal strip action is reversed. It's also possible to have one thermostat that has two complete sets in one device for heating and cooling.

As the temperature decreases, the magnet holds the contacts together until force from the bi-metallic helix is greater than the force from the magnet, allowing to contacts to open, and opens the contacts faster to avoid arcing. This is also a method of Hysteresis preventing contact chatter.

Whilst these mechanical thermostats are still widely used today, the world is slowly changing over to fully electronic / solid state thermostats, which brings us to our DIY Thermostat project.

We set ourselves some strict design guidelines for the operation of our thermostat. Since this is a project that people may use for important things such as pet care, etc., we wanted to incorporate features to ensure a stable operation. The most obvious being blackout protection. If the device loses power, we wanted to ensure that upon power up, the device will return to normal operation reducing the impact of the blackout as much as practical. This means that the default state for the project is an operational state, but perhaps more importantly, it has the ability to remember and recall the set temperature after a reboot.

We also wanted the ability to change the set temperature without requiring any reprogramming so that the project can be used in many different applications.

SENSOR

Our goal is to make an electronic version of the same device that will work with many times better precision and include the capability to control both a heating and cooling device so that you can maintain a temperature within a few degrees regardless of the ambient conditions.

For our project, we will use a temperature probe to sense the temperature rather than a bi-metallic strip. Since we are designing the project to work in as many environments as possible, we opted to use the waterproof DS18B20 Digital temperature sensor probe from Core Electronics. This sensor being waterproof, will allow the user to use it in wet areas such as fridges, liquids, pet enclosures and the like. The 90cm lead on the sensor also makes it ideal as the sensor and electronics can be mounted separately.

This is a 3 – 5V sensor making it ideal to use with a microcontroller and can sense between -55 and +125°C, so it’s more than capable for the average maker or hobbyist application. The sensor is reasonably accurate claiming specs ±0.5°C

The one-wire communication protocol is a little tricky to get your head around, but the listing for the device on the Core Electronics website links to the manufacturer’s wiki page dedicated to this sensor, which we will link to here: https://wiki.dfrobot.com/Waterproof_DS18B20_Digital_Temperature_Sensor__SKU_DFR0198_

This wiki also provides sample code for getting the sensor working which we used as a starting block for our project.

In effect, the sensor samples the temperature and converts the data into a 16-bit register with 12-bits used for the temperature and 5-bits used to denote if the number is positive or negative.

*The power on reset register value is at +85°C

To demonstrate this, let’s assume the reading is +125°C. The sensor will send the following to the internal 16-bit register [0000011111010000]. The sensor then sends 16-bits over the data line to the microcontroller, least significant bit first so the first bit of the output is 2^-4 and the last bit of the output 2^11. The 5-bits (2^7 to 2^11) labelled with a * denote if the value is a positive or negative number. Zeros in the last 5-bits tell the receiver that the number is positive while a 1 indicates that it is negative.

The first four bits (2^-4 to 2^-1) of the output are the fractional numbers. i.e. after the decimal point. To convert them, you can use the following chart.

In any binary number, any bit value is twice the value to the right of it and half the value to the left of it. Therefore, the bit to the right of binary '1' must be '0.5' (i.e. 1/2) and to the right of that is 0.25' (1/4) etc.

Since there are no bits high between 2^-4 and 2^-1, we know that we are dealing with a whole number without a fractional component. The next 7-bits 1111101 is equal to 125 in decimal, and the remaining bits 2^7 - 2^11 are zeroes, indicating that the number is positive. Thus, the output is 125.00.

However, let’s consider if the output from the sensor was 1111111101011011 (as shown here). Bits 2^11 – 2^7 are all ones denoting that the number is a negative, and thus, we need to do a two’s complement on the whole part of the data. Bits 2^6 – 2^0 are the whole number that we need to perform two’s complement on and bits 2^-1 – 2^-4 are the fractional numbers.

To do this, invert each bit of the whole number so that a 1 becomes a 0 and vice versa.

To perform the two’s complement, we simply need to invert the relevant whole numbers binary representation. So, to convert that number, take the 7-bits from 2^6 – 2^0 and invert them, which is to say, rewrite the binary representation, replacing each one with a zero and each zero with a one.

This gives the result: 0001010 = 10

The four bits from 2^-1 – 2^-4 indicate the fractional component. To convert these, anytime you see a one in a position, multiply it by the position. i.e. 1 x 2^-1 = 0.5, thus, our decimal place is (0.5 + 0.125 + 0.0625) = 0.6875.

This provides the result of: -10.6875

Now, thankfully, all this is done for us in the provided library, which means we don’t need to mess with it. However, it’s still important to understand how the device functions for future use. With the provided library and subsequent example code, all we need to do is call a function that will return the temperature in degrees Celsius.

DISPLAY

The next step in the project was to decide which of the vast number of LCD displays available on the market we will use for the project. Essentially, all we need to display is the current temperature read by the sensor and the desired or set temperature.

Seeing that we didn’t need a high detail graphical LCD, we figured we would use the 1602 LCD which has 16 characters per line, meaning it has enough room to fit the required data and it is very inexpensive. This, combined with the fact that we had a few in our parts bin, made this the ideal candidate. This is an LCD we have used many times in the past as it has an awesome library included natively in the Arduino IDE that makes it very simple and quick to use.

To get started, you will first need to solder a set of pin headers to the LCD to allow it to easily be inserted into the breadboard. We will also use these headers on the final device to connect the LCD to the PCB.

I/O

For the user input to the device, we will use three tactile switches. These switches are inexpensive and will give the user the ability to increment, decrement and save the set temperature.

These buttons will all be held at 5V via a 10KΩ pull-up resistor. When the button is pressed, the pin of the microcontroller will be pulled to ground potential, and thus, there will be a 0V or LOW detected by the microcontroller. If you wanted, you could omit the pull-up resistors by using the internal pull-ups in the ATmega328 by changing the line:

pinMode(input[i], INPUT); //pins 7,8,9,10

to:

pinMode(input[i], INPUT_Pull-up); //pins 7,8,9,10

It’s completely up to you, however, a hardware pull-up is, generally speaking, much stronger than the internal software pull-ups, making them less susceptible to noise.

OUTPUT

For the output, we will use two LEDs - a red LED to indicate when the heating output is actuated and a blue LED to signify the cooling phase. In the final project, we will connect these signals to a set of relays and demonstrate the functionality of the device using a 12V fan as a cooling device and 12V halogen bulb as a heater. The outputs are an active high which will trigger the relay allowing current to flow from the COM through the N/O connection.

Warning: Whilst this system can be used to control mains 240V appliances, you will need a licensed electrician to perform any mains wiring. Mains electricity can kill and, as such, we will not be showing any mains wiring connections.

Theoretically, you could connect the cooling relay to a fridge and the heating relay to a heater band, and use the device to maintain the perfect fermentation / brewing temperature for beer or cider.

You could even use it to control an infrared heat lamp and cooling fan in a reptile enclosure to maintain the optimum temperature for your scaly pet. Basically, the only limit is the -55°C – +125°C working range of the temperature probe, which is well within the range that one would expect a hobbyist to be working in.

The Prototype:

ELECTRONICS

Parts Required:JaycarAltronicsCore Electronics
1 x Arduino Nano or equivalentXC4414Z6372A000005
1 x 1602 LCDQP5521Z7011A**DFR0063
1 x 10K PotentiometerRT4360R2480BCOM-09806
3 x Tactile SwitchesSP0600S1120ADA367
1 x Temperature Sensor DS18B20--DFR0198
1 x 5mm Red LEDZD0150Z0800COM-12062*
1 x 5mm Blue LEDZD0185Z0869COM-12062*
1 x 100µF Electrolytic CapacitorRE6130R5123CE05258
1 x 100nF Ceramic CapacitorRG5125R2865CE05188
1 x 4.7K Resistor*RR0588R7574COM-10969*
3 x 10K Resistors*RR0596R7058PRT-14491 or COM-10969*
3 x 330Ω Resistors*RR0560R7546PRT-14490 or COM-10969*

Parts Required:

* Quantity required, may only be sold in packs. Breadboard and prototyping hardware also required.

We followed our usual process of building the prototype on a breadboard using an Arduino Nano microcontroller development board as the microcontroller. This is an ideal setup, as the Nano fits perfectly into the breadboard and is very easy to program via USB. This allows us to program and debug on the fly without needing to swap ICs or expensive programming hardware. For the final project, we will replace the Nano with a bare ATmega328P microcontroller to reduce costs.

Note: We encountered some significant intermittent reliability issues when using the device powered from the USB. Intermittently, the circuit would behave as if it were floating and not correctly grounded, despite being correctly grounded and having additional bypass capacitors. This would only occur when the device was powered via the USB. Touching a Vcc or GND wire would cause the system to regain stability and function correctly. Currently, we are unable to explain this, but it is likely a ground loop issue with the USB being referenced to ground / Earth potential. If you’re having similar issues, we suggest powering the Nano with 8V – 12V on the Vin pin with a non-ground referenced power supply, this rectified the issues for us.

SENSOR

The sensor has three wires a red, black and yellow. The red wire connects to Vcc / 5V the black to GND and the yellow is the signal wire that goes to digital pin 7 on the Arduino Nano.

Note: The signal pin requires a strong (low value) pull-up resistor, as the sensor operates on parasitic power, which means the sensor requires the data line to provide additional power during the temperature conversion process when the device is not transmitting or receiving data.

Circuit recommendation taken directly from the DS18B20 datasheet.

Without this pull-up, the sensor will not function correctly. To aid in connecting the sensor to the breadboard, we opted to solder a 3-pin header to the sensor which we insulated using heatshrink.

LCD SCREEN

The LCD is connected in the following configuration:

Note: A and K on the LCD are only required if you wish to have the backlight illuminated. These can be omitted if you don’t require this feature.

POTENTIOMETER

The potentiometer is used for contrast adjustment on the LCD, without a voltage on the Vo pin the LCD display isn’t likely to display anything as the contrast ratio will either be too low showing nothing or two high showing coloured squares. The easiest way to create this voltage is by using a potentiometer with pin 3 connected to Vcc pin two (the wiper) connected to Vo on the LCD and pin 1 connected to GND. This will give you the ability to adjust the voltage at the Vo pin until the digits are readable. You simply use a screwdriver to turn the potentiometer while watching the LCD, when it becomes readable you stop.

SWITCHES / BUTTONS

The three switches are wired with one side connected with a 10KΩ pull-up resistor to Vcc and the other tied to ground potential. The side with the pull-up resistor is also connected to the Arduino Nano’s digital pins. The three switches / buttons allow the user to decrement the set value, increment the set value, and save the set value to the EEPROM.

The button makes an electrical connection between the diagonally opposite legs of the switch when the button is depressed. So, when you press the button, pins 1 and 4 become electrically connected, as do pins 3 and 2, 1 and 3, and 2 and 4. You can see this demonstrated in the diagrams below.

Note: Pins on the same side of the device are always electrically connected, thus pins 1 and 2 or 3 and 4 are electrically connected together. Generally speaking, the tactile switch will only fit into a breadboard or correctly laid out PCB in the correct orientation, but regardless, you should still be mindful of this.

If you have any doubt of your switch’s pinout you can use the continuity feature of your digital multimeter (DMM) to verify the pins you need to use. If you are unsure how to do this, select the continuity mode (looks similar to the WiFi symbol and usually shares the same position as the resistance Ω functions). If your meter has it, press the mode button on the meter to select the audible continuity function. Connect the probes to the COM and voltage, resistance, temp and frequency input. With the circuit under test de-energised (not powered), attach either probe to the pins of the switch and press the tactile switch’s button. If you have a matching pair, the DMM will produce an audible beep, and usually display 0’s on the screen.

CAPACITORS

We have included a 100μF and 100nF capacitor to the Vcc / 5V line of the circuit to help smooth out the supply voltage. We added these as we were having intermittent floating issues when powering the circuit via the 5V USB input of the Arduino Nano. Adding these capacitors and powering the circuit from an external 5V supply removed the issue and allowed for normal operation. However, we still had issues when powering the device directly from the Arduino Nano’s USB input.

THE CODE

The temperature sensing function for this code was sourced from the DFrobot page for the sensor that you can find here: https://wiki.dfrobot.com/Waterproof_DS18B20_Digital_Temperature_Sensor__SKU_DFR0198_

You will need to install the onewire Arduino library, which you can find at Paul Stoffregen’s Github page here: https://github.com/PaulStoffregen/OneWire

It’s also possible to install the library directly through the Arduino IDE library manager as shown here.

We also recommend that you check out the Dallas Semiconductor’s 1-Wire Protocol tutorial on the Arduino playground here: https://playground.arduino.cc/Learning/OneWire/

The liquid crystal library is a standard included library, and as such, you don’t need to manually download or install its library. Likewise, the EEPROM library is also a standard included library and does not need downloading. This library allows us to easily save and recall values from the EEPROM.

The ATmega328P microcontroller at the heart of the Arduino Nano has three types of memory:

  • 32Kb of Flash memory for program space. This is the memory that contains your program / sketch as well as the Arduino Bootloader.
  • 8Kb of static random-access memory (SRAM), which is where the memory locations for the variables used in the program are created and manipulated.
  • 1Kb of electronically erasable programable read only memory (EEPROM). This memory space can be used to store long term data.

Both the EEPROM and flash memory are non-volatile memory, which means that the data stored in them is retained even after a power cycle, whereas, the information stored in the SRAM is lost anytime power is removed. You can only write to the flash memory / PROGMEM during the program burn time, and thus, it’s not possible to manipulate the data once the program is running. Other micros, AVRs for example, may also have EEPROM but check before using them.

This means that in order to store the desired temperature, we need to be able to read and write to the EEPROM. Lucky for us, this is a trivial task thanks to the EEPROM library.

To read from the EEPROM we can simply call the EEPROM read function:

EEPROM.read(x); 

Where: x is an 8-bit value between 0 and 255, representing the address in the EEPROM that we are requesting the data from. For us, we will arbitrarily pick address 100.

Similarly, we can write to the EEPROM using the EPPROM write function:

EEPROM.write(x, y);

Where x is the address and y is the value we wish to store in that address.

The address 100, like all blank EEPROM addresses, is populated with the value 255. In this project, we have limited the temperature range inside of the sketch to match the hardware range of the sensor itself. The program is unable to set the temperature lower than -55°C or higher than 125°C. This means it’s not possible to have a set temperature of 255, and thus, we need to manually change this value to a value within the expected range before running the final program.

To do this, you can run the following code:

#include <EEPROM.h>
int value;
int count =1;
void setup()
{
  Serial.begin(9600);
}
void loop()
{
  while(count == 1){
  EEPROM.write(100, 25);
  Serial.print(EEPROM.read(100));
  Serial.println();
  delay(5000);
  count = 2;
  }

This populates the EEPROM address 100 with the value 25 so that in the next program the set temperature will be 25°C, which can then be changed by the user to whatever temperature (in the range of the sensor) they require.

You may notice that we used a while loop to limit the number of times we write to the address to once. This is because the EEPROM has a limited number of write cycles / lifespan. The datasheet states “Write/Erase Cycles: 10,000 Flash/100,000 EEPROM”. This means the microcontroller is only guaranteed to handle 100,000 erases / writes to any EEPROM address. You should be careful how you write your program, as an improperly written code could easily use up that lifespan in a matter of seconds destroying that address.

Note: In hindsight, we should have added the EEPROM.write(100,25); line of code to the Setup part of the code, to make it more efficient.

For us, we have written the code so that we only write to the address after we hold down the OK button and if the new value is different from the previous value. This is perfectly acceptable for this type of device as it would usually only ever expect a handful of set temperature requests over its lifetime. With that said, even if you changed the set temperature once every day, it would still take over 270 years to exceed the guaranteed write lifespan of the device.

If you’re intending on writing to the EEPROM much more often than this, you need to program in a way that the address will change to the next possible address when the 100,000 cycles have been exceeded. This is called EEPROM wear levelling.

We have broken the code into separate functions for each of the processes. Reading buttons, reading temperature, programming set temperature, comparing current temperature against set temperature and changing output states, and showing the current temp and set temp on the display.

The code is available for download from our website. We will briefly discuss the key functions here and how they work.

READING BUTTONS

void button() {
  int up = digitalRead(increment);
  if (up != prevUpState) {
    lastDebounceTime = millis(); 
  }
  if ((millis() - lastDebounceTime) > 
debounceDelay) {
    if (up != upState) {
      upState = up;
      if ((upState == HIGH) && (setTemp 
< maxTemp)) {
        setTemp = setTemp + 1;
      } 
    }
  }
  prevUpState = up;
  int down = digitalRead(decrement);
  if (down != prevDownState) {
    lastDebounceTime = millis();
  }
  if ((millis() - lastDebounceTime) > 
debounceDelay) {
    if (down != downState) {
      downState = down;
      if ((downState == HIGH) && 
(setTemp < maxTemp)) {
        setTemp = setTemp - 1;
      }
    }
  }
  prevDownState = down;
  if ((setTemp < minTemp) || (setTemp > maxTemp)) {
    lcd.clear();
    lcd.setCursor(4, 0);
    lcd.print("Temp out");
    lcd.setCursor(4, 1);
    lcd.print("of range");
    delay(3000);
  } 
}

This simple function detects changes of state with the increment and decrement buttons. If the increment button has been pulled to ground (low), the program will increment the set temperature by one degree, but only if the set temperature is less than the maximum temperature of the temperature probe saved in the variable maxTemp which is 125°C. Likewise, it does the same with the decrement button, decreasing the setTemp value by one, making sure the temperature is not lower than the minTemp or -55°C. If the reading is outside of this range, the LCD will display “Temp out of range” for three seconds.

This also includes a debounce solution that checks if the state of each button had changed and ignore subsequent presses for a small period after. This prevents flooding by only acting if the button has changed state, and prevents bouncing by ignoring any subsequent changes of state for 50 milliseconds.

Note: If time permitted, we would have used an array to avoid calling the debouncing code for each button, and avoid twice the variables.

READING TEMPERATURE

The reading temperature function is quite long and comes directly for the example code for the sensor. As such, to save space, you can simply follow the fully commented code provided in that example.

SAVING SET VALUE TO EEPROM

void progTemp() {
  pressedTime = millis();
  while (digitalRead(okState) == LOW) {
    if ((millis() - pressedTime) >= 5000) {
      if ((setTemp != EEPROM.read(100)) && (setTemp <= maxTemp) && (setTemp > minTemp)) {
        EEPROM.write(100, setTemp);
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("New Temp");
        lcd.setCursor(5, 1);
        lcd.print("Saved!");
        delay(3000);
      }
      else {
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("New Temp");
        lcd.setCursor(0, 1);
        lcd.print("Not Saved!");
        delay(3000); 
      }
    }
  }

This function is called in the main loop and is entirely ignored if the OK button is not pulled low to ground potential. However, if the OK button is being pressed when the program calls this function, the program is locked into a while loop for the duration the button is low. If this time exceeds 5000 miliseconds, and the set value is not already written to the EEPROM or outside of the capabilities of the sensor (i.e. less than -55 or greater than 125), the program will save the new set value to the EEPROM. After writing the new set value to the EEPROM, the LCD will display “New Temp Saved” for three seconds.

If the value is the same as the value in the EEPROM, or outside of the range of the sensor, the LCD will display “New Temp Not Saved” for three seconds. This was done to ensure that the device is only used within the specs of the sensor and to reduce the number of EEPROM writes.

Note: the device will work at whatever the current set temp is at all times and does not require you to save the set temp. However, you must save the set temp to the EEPROM for the device to resume from the set temp after a restart.

COMPARE FUNCTION

void compare() {
  if (Temperature > (setTemp + hysteresis)) {
    digitalWrite(cool, HIGH);
    digitalWrite(heat, LOW);
  }
  else if (Temperature < (setTemp - hysteresis)) {
    digitalWrite(heat, HIGH);
    digitalWrite(cool, LOW);
  }
  else {
    digitalWrite(heat, LOW);
    digitalWrite(cool, LOW);
  }

A very simple function that compares the current temperature against the set temperature, plus a hysteresis value to prevent oscillations.

If the current temperature is higher than the set temp, plus the hysteresis value, the heating output is pulled low and the cooling output is pulled high.

Conversely, if the current temperature is lower than the set temperature, minus the hysteresis, the heating output is pulled high and the cooling output is pulled low.

This provides the binary output we expect from a thermostat. We added an else statement to ensure that under any other unexpected condition the outputs are both low, preventing any uncontrolled heating or cooling.

>h1>DISPLAY FUNCTION

Finally, we want to display the current temperature and set temperature on the LCD screen. For this, we call the showDisplay() function using the following if

statement:</p>
if (count > 5) {
  showDisplay();

We do this to reduce the flickering on the LCD, as rapidly clearing and writing values to the LCD usually results in an obvious dimming and pulsating text. This for loop just makes it so the data is only written to the LCD once every 5-cycles of the program. You can change the number of cycles to suit your requirements but five was for us the sweet spot between response and flicker.

Note: It is possible to only clear portions of the LCD rather than clearing the entire LCD. This may help to reduce some of the flicker.

void showDisplay() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Set Temp ");
  lcd.print(setTemp);
  lcd.print((char)223);
  lcd.setCursor(0, 1);
  lcd.print("Cur Temp ");
  lcd.print(Temperature);
  lcd.print((char)223);
  count = 0;

In this function, we are simply writing the values to the LCD by calling functions from the LCD library.

  • Lcd.clear() removes all data from the LCD screen.
  • lcd.setCursor(x, y); forces the cursor to the desired position so that the text will start there where X = character position in the row, and Y = column.

This is a 1602 LCD, which means it’s a 16 column 2 row LCD.

  • lcd.print(“***”); will print whatever characters are between the quotation marks.
  • lcd.print(Temperature); Will print the value of the variable mentioned in this case the value of temperature.
  • lcd.print((char)223); prints the degrees symbol.
  • count = 0; simply resets the count to zero so that the counter is reset.

The remainder of the code is fairly self-explanatory, and you should have minimal troubles following it.

TROUBLESHOOTING

Testing the device is very straightforward, but there are a number of potential issues that we will briefly discuss here:

UP & DOWN BUTTONS NOT WORKING: If your increment and decrement buttons are not functioning correctly, check the orientation of the switches to ensure the signal wire to the microcontroller is attached to the pull-up resistor (The switch side and not tied to Vcc). Likewise, make sure that the switch is pulling the pin to ground when the button is depressed. You can do this using your DMM. In voltage mode, put the negative probe (black) to the low side of the button and the positive probe (red) to the bottom of the pull-up resistor. With the button not pressed, the DMM should read 5V. With the button pressed, it should read 0V.

LCD NOT DISPLAYING CORRECTLY: If the LCD is not displaying anything or showing dark blocks, this generally means that the contrast is set too low or too high. Double check the LCD connections to the potentiometer. If the connections are correct, use a screwdriver to adjust the potentiometer until the text becomes visible. If that does not rectify the issue, carefully double check and verify each of the LCD connections.

CURRENT TEMP -1000: If the current temperature reads -1000, or other nonsensical value, double check the connections to the sensor.

CURRENT TEMP +85°: When you apply power to the project, it will always read 85°C briefly. This is because the sensor has a power on reset value of +85°C. This will usually be replaced very quickly after the sensor takes another reading, however, if it stays as 85°C on the LCD, there may be an issue with the sensor or the connections to it. Double check that there is 5V between the data pin and ground, and that the pull-up resistor is around 5KΩ.

POWER OBSERVATIONS

We tested the current draw on our prototype. With 4.965V power directly to the Arduino 5V pin, the device has a current draw of just 16.58mA to 18.25mA. This is without the relay modules connected and using the Arduino Nano. The final device in next month’s issue will use the bare ATmega238P microcontroller, which will lower the current demands for the main build.

WHERE TO FROM HERE?

This will conclude the prototype and part one of this project. In the next issue, we will modify the circuit to free the project from the Arduino microcontroller development board, which will reduce the cost of the final product and allow you to put your Arduino into your next prototype. We will discuss how to attach a relay or alternate output methods, and also show you the process of creating the PCB using EAGLE PCB design and exporting Gerber files that you can send off to your preferred PCB manufacturer. We will also design an enclosure for the project and discuss the final assembly.

Part 2

Johann Wyss

Johann Wyss

Staff technical writer