Analytical temperature display with the LM34

The LM34 is a precision Fahrenheit temperature sensor which outputs a linear DC voltage relative to its temperature (720mV = 72°F). It accepts a very wide range of input voltages and requires no extra components when connecting it to an Arduino’s analog pin.
Likewise, I’m sure you can hack this into a volt meter for a quick and accurate temperature readout since, unlike the common TMP36, no math is required other then to move the decimal one place to the left.

DSC_0093

After averaging hundreds or thousands of samples within a fraction of a second, this project outputs a remarkably stable temperature readout over serial and/or writes it to an optional LCD.

DSC_0080wp13

We chose the LM34 over the LM35, which is the Celsius derivative, to allow us to read a more practical range of temperatures. This is because our Arduino can’t easily read the negative analog voltages this chip is capable of outputting at sub-zero temperatures.
With the LM34, our temperature range  is 0°F to 110°F (-17°C to 43°c), which is perfect for most home uses. If we use the LM35 sensor (and alter the math conversions),  our range will become 32°F to 230°F (0°C to 110°c).
We are also using the Arduino’s internal analog reference of 1.1v to improve our  resolution at the cost of range, thus the reason our maximum sense temperature is 110°F/C.

Now make it!


Weather being powered from USB or battery, you’ll want to connect the Vcc pin (left) of the LM34 to the Vin pin of the Arduino. Also join the grounds (right). This sensor can handle up to 30 volts DC, so you shouldn’t have to worry about the input voltage. The Vout pin (middle) of the LM34 should be connected to an analog pin of the Arduino, in our case we will use A0.

If you want an LCD readout of the temperature, connect a 16×2 character LCD of your choice to the Arduino as per most LCD tutorials (12, 11, 5, 4, 3, 2).

FirstFritz-lm34wp

You can either make this a compact unit with a protoshield and some breadboard jumpers, or you can sprawl out the whole circuit across your desk. Just make sure your Vout/sense pin is not so long where the voltage drop comes into effect.

If you are feeling adventurous, you can even plug the LM34’s TO-92 package directly into the headers of an Arduino; just be careful not to cross the leads. You might even want to go all out and develop an even more compact case using a DIY bare bones Arduino or a small Arduino derivative, such as the Adafruit Pro Trinket, for a more permanent project solution.

Source code

Feel free to change some variables and experiment! This sketch’s somewhat descriptive comments should help you understand what’s going on.

/* 
 * This is a simple LCD thermometer that displays the temperature in
 * Fahrenheit, Celsius, and/or Kelvin on both an 16x2 LCD and through serial.
 * It uses the LM34 Precision Fahrenheit Temperature Sensor which outputs a
 * linear DC voltage relative to its temperature. (720mV = 72.0°F).
 * 
 * We are using the internal analog reference of 1.1v to improve our 
 * resolution at the cost of range. Consequently, our temperature range 
 * becomes 0°F to 110°F (-17°C to 43°c), which is perfect for most uses.
 *
 * We can't read negative voltages on the analog pin, so if we use the LM35 
 * Precision Celsius Temperature Sensor (and alter the float math variables), 
 * our range will become 32°F to 230°F (0°C to 110°c).
 * 
 * Visit thestuffwebuild.com for more.
 */

// We need the lcd library
#include <LiquidCrystal.h>

// Setup lcd pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// Serial-log a measurement every 'this many' milliseconds
//long interval = 0; //log asap (~22500 measurements an hour)
//long interval = 2000; //1800 measurements an hour
//long interval = 10000; //360 measurements an hour
long interval = 60000; //60 measurements an hour
//long interval = 3600000; //1 measurement an hour


// This is how many samples you will average per measurement. 
// Hint: if this is set at or near 1, you will have a slow recovery time.
unsigned long samples = 1000;

// Running-average factor; 1=disabled; approaching 0 means more averaging 
float alpha = 0.01;

// Set our analog reference voltage constant
float vRef = 1.1;

// Variable used for counting the samples for running average
int count;

// AnalogRead variable
int a;

// Running averaged analog variable
float ra;

// Output variables
float v, f, c, k;

// Time alive variables (max ~50 days ea. till overflow)
float h, m;

// Variable used to count how many measurements we have taken
unsigned long n;

// Variable to display the logging interval in seconds at startup
float intervaltosec;

// Variables used for timing the logging interval
unsigned long currentMillis;
unsigned long previousMillis;

// Used to make sure we have enough time to measure and write to the LCD
unsigned long dlyconst;

// Variable used to subtract setup time from logging time.
float ttb;

// We'll use the internal LED 13 to tell us when we are sampling
int led = 13;


//////////////////////////////////////////////////////////////////////////////
////////////////////////////// setup procedure ///////////////////////////////
//////////////////////////////////////////////////////////////////////////////
void setup() {

  // Set aRef to internal 1.1v
  analogReference(INTERNAL);
  // Sample A0 once to initialize new aRef.
  analogRead(0); 

  // Again, we'll use the internal LED for debugging, set it as an output
  pinMode(led, OUTPUT);  

  // Start serial
  Serial.begin(9600);

  // Start lcd
  lcd.begin(16, 2);

  // Write these lines to lcd
  // Note: the F("...") keeps the string in flash.
  lcd.setCursor(0, 0);
  lcd.print(F("Current")); 
  lcd.setCursor(0, 1);
  lcd.print(F("Temp:")); 

  // Convert interval milliseconds to seconds
  intervaltosec = interval / 1000.0;

  // Print what we are doing and that we are ready
  Serial.print(F("Ready: Logging a measurement every "));
  Serial.print(intervaltosec);
  Serial.println(F(" seconds."));
  Serial.println();

  // See how long it took to boot up
  ttb = millis();

  // Determine the time required to measure and use the LCD 
  dlyconst = samples / 12;
}

///////////////////////////////////////////////////////////////////////////////
////////////////////////////// logging procedure //////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void logging(){

  // Add one to "n"
  n++;

  // Calculate number of minutes alive (hijack "currentMillis" for accuracy)
  m = (currentMillis - ttb) / 60000.00;

  // Calculate number of hours alive
  h = m / 60.00;

  // Serial print all values
  // Hint: for logging, you can comment in or out strings you want/don't want. 

  // Number of logged measurements 
  Serial.print(F(" Log="));
  Serial.print(n);

  // Minutes alive 
  //Serial.print(F(" Minutes="));
  //Serial.print(m, 2);

  // Hours alive 
  Serial.print(F(" Hours="));
  Serial.print(h, 2);

  // Raw analog value
  //Serial.print(F(" Raw_analog="));
  //Serial.print(a);

  // Running average analog value
  //Serial.print(F(" Avgeraged_analog="));
  //Serial.print(ra, 4);

  // Volts on analog pin
  //Serial.print(F(" Voltage="));
  //Serial.print(v, 4);

  // Fahrenheit
  Serial.print(F(" Fahrenheit="));
  Serial.print(f, 4);

  // Celsius
  Serial.print(F(" Celsius="));
  Serial.print(c, 4);

  // Kelvin
  //Serial.print(F(" Kelvin="));
  //Serial.print(k, 4);

  // New line
  Serial.println();

}


//////////////////////////////////////////////////////////////////////////////
///////////////////////////// lcdprint procedure /////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// (Runs as fast as possible until "dlyconst" milliseconds is reached.)
void lcdprint(){

  //-------------------------------------------------------------------
  //----------------------------lcd print f----------------------------
  //-------------------------------------------------------------------

  // Set our f cursor position depending on the size of our number f.
  lcd.setCursor(((f >= 100 || f < -10)?7:8), 0);
  // This extra space only required if f falls between here:
  if( f < 10 && f >= 0 ) lcd.print(' ');

  // Common lcd print code
  lcd.print (' ');
  lcd.print (f);
  lcd.print ((char)223); // ° symbol
  lcd.print ('F');

  //-------------------------------------------------------------------
  //----------------------------lcd print c----------------------------
  //-------------------------------------------------------------------

  // Set our c cursor position depending on the size of our number c.
  lcd.setCursor(((c >= 100 || c < -10)?7:8), 1);
  // This extra space only required if c falls between here: 
  if(c < 10 && c >= 0) lcd.print(' ');

  // Common lcd print code
  lcd.print (' ');
  lcd.print (c);
  lcd.print ((char)223); // ° symbol
  lcd.print ('C');

  /*
   //------------------------------------------------------------------
   //---------------------------lcd print k----------------------------
   //------------------------------------------------------------------
   
   // Our range in Kelvin is 255-316°K; we never need to move the decimal.
   lcd.setCursor(7, 1);
   
   // Common lcd print code
   lcd.print (' ');
   lcd.print (k);
   lcd.print ((char)223); // ° symbol
   lcd.print ('K');
   */
}


//////////////////////////////////////////////////////////////////////////////
///////////////////////////// sampcalc procedure /////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// (sampcalc = sample and calculate) 
void sampcalc(){

  // Turn on led here, we begin sampling
  digitalWrite(led, HIGH);  

  // Average "samples" samples to eliminate error and recovery time
  // Hint: it takes roughly 160ms to average 1000 samples.
  while (count < samples) {

    // Read analog
    a = analogRead(0);

    // Find running average  
    ra = alpha * a + (1-alpha) * ra; 

    // Increment "count" by one
    count++;
  }

  // Turn off led here, we are done sampling
  digitalWrite(led, LOW);  

  // Reset count
  count = 0;

  // Analog average to voltage math
  v = (ra * vRef) / 1024.00;

  // v to f math
  f = v * 100.00;

  // f to c math
  c = (f - 32) * 0.555555;

  // f to k math (because why not)
  k = (f + 459.67) * 0.555555;
}


//////////////////////////////////////////////////////////////////////////////
/////////////////////////////// loop procedure ///////////////////////////////
//////////////////////////////////////////////////////////////////////////////
void loop() {

  // Check the time
  currentMillis = millis();

  // See if we have time to write to the LCD
  if(currentMillis - previousMillis < interval - dlyconst) { 

    // Take a sample
    sampcalc();

    // Commence printing procedure
    lcdprint();
  }

  // See if it is time to log something to serial
  if(currentMillis - previousMillis >= interval || n == 0) { 

    // Set previousMillis to currentMillis so we know next when to log again.
    previousMillis = currentMillis; 

    // Take a sample
    sampcalc();

    // Commence serial logging feature
    logging();  

    // Might as well print to the LCD while we are here
    lcdprint();
  }

}


Leave a Reply

Your email address will not be published. Required fields are marked *