Built-in project · Arduino Uno R3

Thermometer on an Arduino Uno

A TMP36 on A0 and the temperature on a 16x2 LCD in four-bit mode, with the high and low on the second row. The whole build, an Arduino Uno and 2 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Intermediate About 20 minutes to build and run. Runs in your browser. Free, and no account needed.

The parts retail for about $37. On Mokxi they're free.

Step by step, with the wiring and the common mistakes: Arduino TMP36 Temperature Sensor in °F and °C

The circuit runs here once the page has loaded.

What you'll learn

  • Reading an analog temperature sensor
  • Converting ADC counts to millivolts and degrees
  • Averaging samples to reduce noise
  • Integer math instead of floating point
  • Tracking a minimum and maximum
  • Difficulty: Intermediate
  • Time: About 20 minutes
  • Board: Arduino Uno R3
  • Parts: 4

How it works

A TMP36 sensor on A0 outputs a voltage that rises 10 mV for each degree Celsius, with 500 mV at zero degrees. The sketch reads it 16 times, averages the readings, and converts to tenths of a degree using whole number math.

The 16x2 LCD shows the current temperature on the top row and the lowest and highest seen since power on on the bottom row. Drag the sensor's slider in the simulator to change the weather.

Parts list

4 parts, plus the jumper wires. They retail for about $37; on Mokxi they're free.

Kits that can build it

These 2 starter kits have every part in the circuit. Each kit's page lists what else it can build.

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
Character LCD, 16x2 pin RSArduino Uno R3 pin 12
Character LCD, 16x2 pin EArduino Uno R3 pin 11
Character LCD, 16x2 pin D4Arduino Uno R3 pin 5
Character LCD, 16x2 pin D5Arduino Uno R3 pin 4
Character LCD, 16x2 pin D6Arduino Uno R3 pin 3
Character LCD, 16x2 pin D7Arduino Uno R3 pin 2
Temperature sensor, TMP36 pin VSArduino Uno R3 pin 5V
Character LCD, 16x2 pin VDDArduino Uno R3 pin 5V
Character LCD, 16x2 pin AArduino Uno R3 pin 5V
Temperature sensor, TMP36 pin GNDArduino Uno R3 pin GND
Character LCD, 16x2 pin VSSArduino Uno R3 pin GND
Character LCD, 16x2 pin V0Arduino Uno R3 pin GND
Character LCD, 16x2 pin RWArduino Uno R3 pin GND
Character LCD, 16x2 pin KArduino Uno R3 pin GND
Temperature sensor, TMP36 pin VOUTArduino Uno R3 pin A0

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Uno R3
// Thermometer: a TMP36 on the analogue input, the reading on a 16x2 LCD.
//
//   A0      the TMP36's middle leg; its outer legs go to 5 V and GND
//   LCD     RS -> pin 12, E -> pin 11, D4..D7 -> pins 5, 4, 3, 2
//           RW -> GND, V0 -> GND for full contrast, A -> 5 V, K -> GND
//
// Two parts and no bus: the sensor is a voltage and the display is six wires.
// Drag the slider on the sensor to change the weather.
//
// # The sum, in whole numbers
//
// A TMP36 puts out 500 mV at zero degrees and moves 10 mV for every degree
// above it. The ADC gives 0 to 1023 against the 5 V reference, so
//
//   millivolts = reading * 5000 / 1024
//   celsius    = (millivolts - 500) / 10
//
// There is no soft-float runtime on this board, so none of that is done in
// floating point and none of it needs to be. 5000/1024 is exactly 625/128, and
// 10 mV a degree means one millivolt is one *tenth* of a degree, so the whole
// conversion is
//
//   tenths of a degree = reading * 625 / 128 - 500
//
// in 32-bit integers, and it is exact rather than nearly right.
//
// The offset is the trick of the part. It is what lets a sensor on a single
// supply read below freezing, and it is the thing every first sketch gets
// wrong by leaving it out.
//
// # Why the reading is averaged
//
// The ADC's last bit is noise, and one count is about five tenths of a degree
// here, so an unfiltered display flickers between two readings forever. Sixteen
// samples added up and shifted right by four is a divide-free mean.
//
// The second line is the lowest and highest it has seen since it was switched
// on, which is what turns a thermometer into something worth watching.

#include "mokxi_lcd1602.h"

const int SENSOR = A0;

Lcd1602 lcd(12, 11, 5, 4, 3, 2);

int16_t lowest = 32000;
int16_t highest = -32000;

/** Sixteen conversions, averaged, as tenths of a degree Celsius. */
static int16_t readTenths() {
  uint16_t total = 0;
  for (uint8_t i = 0; i < 16; i++) {
    total = (uint16_t)(total + (uint16_t)analogRead(SENSOR));
  }
  uint16_t mean = (uint16_t)(total >> 4);
  // 5000 mV over 1024 counts is 625/128, in 32 bits because 1023 * 625 does
  // not fit in sixteen. One millivolt above the 500 mV offset is a tenth of a
  // degree, so the millivolts are the tenths.
  int32_t millivolts = ((int32_t)mean * 625) / 128;
  return (int16_t)(millivolts - 500);
}

void setup() {
  lcd.begin();
  lcd.print("Mokxi thermo");
  Serial.begin(115200);
  Serial.println("Mokxi Arduino Uno: TMP36 thermometer on a 1602 LCD");
  Serial.println("drag the slider on the sensor to change the temperature");
  delay(600);
  lcd.clear();
}

void loop() {
  int16_t tenths = readTenths();
  if (tenths < lowest) {
    lowest = tenths;
  }
  if (tenths > highest) {
    highest = tenths;
  }

  lcd.setCursor(0, 0);
  lcd.print("Temp  ");
  lcd.printFixed(tenths, 1);
  lcd.print(" C");
  lcd.padLine();

  lcd.setCursor(0, 1);
  lcd.print("lo ");
  lcd.printFixed(lowest / 10, 0);
  lcd.print(" hi ");
  lcd.printFixed(highest / 10, 0);
  lcd.padLine();

  static int16_t said = -32000;
  if (tenths != said) {
    said = tenths;
    Serial.print("temperature ");
    Serial.print((int)(tenths / 10));
    Serial.print(".");
    Serial.print((int)(tenths < 0 ? -tenths % 10 : tenths % 10));
    Serial.println(" C");
  }

  delay(250);
}

Code walkthrough

The key functions of the sketch, and what each one does.

readTenths sketch.ino

Adds 16 analogRead samples and shifts right by 4 to get the mean. It multiplies by 625 and divides by 128 to get millivolts, then subtracts 500. Because 10 mV is one degree, the result is in tenths of a degree.

/** Sixteen conversions, averaged, as tenths of a degree Celsius. */
static int16_t readTenths() {
  uint16_t total = 0;
  for (uint8_t i = 0; i < 16; i++) {
    total = (uint16_t)(total + (uint16_t)analogRead(SENSOR));
  }
  uint16_t mean = (uint16_t)(total >> 4);
  // 5000 mV over 1024 counts is 625/128, in 32 bits because 1023 * 625 does
  // not fit in sixteen. One millivolt above the 500 mV offset is a tenth of a
  // degree, so the millivolts are the tenths.
  int32_t millivolts = ((int32_t)mean * 625) / 128;
  return (int16_t)(millivolts - 500);
}

loop sketch.ino

Calls readTenths, updates lowest and highest, and redraws both LCD rows. It prints to the serial monitor only when the reading changes, then waits 250 ms.

void loop() {
  int16_t tenths = readTenths();
  if (tenths < lowest) {
    lowest = tenths;
  }
  if (tenths > highest) {
    highest = tenths;
  }

  lcd.setCursor(0, 0);
  lcd.print("Temp  ");
  lcd.printFixed(tenths, 1);
  lcd.print(" C");
  lcd.padLine();

  lcd.setCursor(0, 1);
  lcd.print("lo ");
  lcd.printFixed(lowest / 10, 0);
  lcd.print(" hi ");
  lcd.printFixed(highest / 10, 0);
  lcd.padLine();

  static int16_t said = -32000;
  if (tenths != said) {
    said = tenths;
    Serial.print("temperature ");
    Serial.print((int)(tenths / 10));
    Serial.print(".");
  // ... 6 more lines in the editor

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • The reading is 50 degrees too high. This project
    Subtract the 500 mV offset. The TMP36 reads 0.5 V at zero degrees.
  • Values flicker. This project
    Keep the 16 sample average in readTenths.
  • The LCD shows blocks or nothing. This project
    Check RS on pin 12, E on pin 11, D4 to D7 on pins 5 to 2, and V0 on GND.
  • The part gets very hot, and the reading is nonsense. Temperature sensor
    The legs are reversed. With the flat face toward you, the order is +Vs, Vout, GND from left to right. Fix it right away, because a reversed TMP36 heats quickly.
  • The temperature is off by a lot or negative. Temperature sensor
    Output is 0.5 V at 0 degrees C plus 10 mV per degree. So C = (volts minus 0.5) times 100. Compute volts from the ADC using the right reference, 5 V or 3.3 V, not always 5.
  • The screen lights up but shows only a row of solid boxes, or nothing at all. Character LCD
    Contrast pin V0 needs a voltage. Connect the middle pin of a 10 k pot to V0, with its ends on 5 V and GND. Also confirm that the code calls lcd.begin(16, 2).
  • Garbled characters or a blank display even with good contrast. Character LCD
    In 4-bit mode only D4 to D7 are used, and D0 to D3 stay unconnected. Tie RW to GND. Match the pins in LiquidCrystal(rs, en, d4, d5, d6, d7) exactly to your wiring.
  • Upload fails or nothing runs after wiring pins 0 and 1 to a module. Arduino Uno R3
    Pins 0 and 1 are the USB serial port. Move the module to other pins, or unplug it while uploading. Also check the right board is selected.

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.