Digital Thermometer using LM35 and PIC Microcontroller

In this tutorial, we are making a Digital Thermometer using PIC microcontroller and LM35 Temperature Sensor. In this project, we will sense the temperature using LM35 and display it on 16×2 LCD. LM35 Temperature Sensor is accurate and cheaper and doesn’t require any external calibration. The output voltage is proportional to Celsius temperature scale and changes by 10mV per °C.

Digital Thermometer using LM35 and PIC microcontroller

Material Required

  • PicKit 3
  • LM35 Temperature Sensor
  • 16*2 LCD
  • PIC16F877A IC
  • 40 – Pin IC holder
  • Perf board
  • 20 MHz Crystal OSC
  • Female and Male Bergstick pins
  • 33pf Capacitor – 2Nos, 100uf and 10uf cap.
  • 680 ohm, 220 ohm, 10K and 560ohm Resistor
  • Potentiometer 10k
  • LED of any color
  • 1 Soldering kit
  • IC 7805
  • 12V Adapter
  • Connecting wires
  • Breadboard

LM35 Temperature Sensor:

LM35 temperature sensor has zero offset voltage, which means at 0°C the output will be 0V. The maximum voltage it can handle is 1.5V which means it can be able to sense a maximum temperature of 150°C (1.5V / 10mV).

Pin No Function Name
1 Supply voltage; 5V (+35V to -2V) Vcc
2 Output voltage (+6V to -1V) Output
3 Ground (0V) Ground

 

As we already told that LM35 gives analog output, so first we need to read that analog values using PIC Microcontroller and then we will convert them into digital values using ADC (Analog to Digital Conversion). So we will learn ADC in PIC Microcontroller before going any further.

ADC in PIC Microcontroller PIC16F877A:

There are many types of ADC available and each one has its own speed and resolution. The most common types of ADCs are flash, successive approximation, and sigma-delta. The type of ADC used in PIC16F877A is called as the Successive approximation ADC or SAR in short. So let’s learn a bit about SAR ADC before we start using it.

Successive Approximation ADC:  The SAR ADC works with the help of a comparator and some logic conversations. This type of ADC uses a reference voltage (which is variable) and compares the input voltage with the reference voltage using a comparator and difference, which will be a digital output, is saved from the Most significant bit (MSB).  The speed of the comparison depends on the Clock frequency (Fosc) on which the PIC is operating.

Now that we know some basics on ADC, lets open our datasheet and learn how to use the ADC on our PIC16F877A MCU. The PIC we are using has 10-bit 8-channel ADC. This means the output value of our ADC will be 0-1024 (2^10) and there are 8 pins (channels) on our MCU which can read analog voltage. The value 1024 is obtained by 2^10 since our ADC is 10 bit. The eight pins which can read the analog voltage are mentioned in the datasheet. Lets look at the picture below.

ADC-Pins- using PIC-Microcontroller

The analog channels AN0 to AN7 are highlighted for you. Only these pins will be able to read analog voltage. So before reading an input voltage we have to specify in our code which channel has to be used to read the input voltage.  In this tutorial we will use channel 4 with a potentiometer to read the analog voltage at this channel.

The A/D module has four registers which has to be configured to read data from the Input pins. These registers are:

• A/D Result High Register (ADRESH)

• A/D Result Low Register (ADRESL)

• A/D Control Register 0 (ADCON0)

• A/D Control Register 1 (ADCON1)

Digital-Thermometer-circuit-diagram-using-PIC-microcontroller

Code and Explanation

The complete code for this Digital Thermometer using LM35 and PIC microcontroller is given at the end. The code is self-explained with comment lines and just involves the concept of interfacing a LCD with PIC Microcontroller and Using ADC module in PIC Microcontroller which we have already covered in our previous tutorials of learning PIC Microcontrollers.

Here we are just showing the calculations done for reading the analog output voltage from LM35 and then converting it into temperature values. So here we are converting the ADC value from LM35 into the voltage and then voltage value into temperature. Therefore, after getting the value we have seperated every character for displaying on LCD.

        adc = (ADC_Read(4)); // Reading ADC values
        volt = adc*4.88281; // Convert it into the voltage
        temp=volt/10.0;  // Getting the temperature values
        temp1 = temp*100;
        c1 = (temp1/1000)%10;
        c2 = (temp1/100)%10;
        c3 = (temp1/10)%10;
        c4 = (temp1/1)%10;

Now in the below code, set the LCD cursor and then print the output value

        Lcd_Clear();
        Lcd_Set_Cursor(1,3);
        Lcd_Print_String("Temperature");
        Lcd_Set_Cursor(2,5);
        Lcd_Print_Char(c1+'0');
        Lcd_Print_Char(c2+'0');
        Lcd_Print_String(".");
        Lcd_Print_Char(c3+'0');
        Lcd_Print_Char(c4+'0');
        Lcd_Print_Char(0xDF);
        Lcd_Print_String("C");
        __delay_ms(3000);

Working of Digital Thermometer

Working of Digital Thermometer using Pic-microcontroller

 

After uploading the code in the PIC micro-controller, power up the circuit using 12v adapter. The analog output of LM35 temperature sensor is fed to the analog input channel of the PIC controller. As the temperature increase the ADC value will also increase. That ADC value is further converted into voltage by multiplying it with 4.88281. Then the voltage value is converted to corresponding character for displaying it into 16*2 LCD.

Code:

#define _XTAL_FREQ 20000000

#define RS RD2
#define EN RD3
#define D4 RD4
#define D5 RD5
#define D6 RD6
#define D7 RD7

#include <xc.h>

#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config BOREN = ON       // Brown-out Reset Enable bit (BOR enabled)
#pragma config LVP = OFF        // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

void ADC_Initialize()
{
ADCON0 = 0b01000001; //ADC ON and Fosc/16 is selected
ADCON1 = 0b11000000; // Internal reference voltage is selected
}

unsigned int ADC_Read(unsigned char channel)
{
ADCON0 &= 0x11000101; //Clearing the Channel Selection Bits
ADCON0 |= channel<<3; //Setting the required Bits
__delay_ms(2); //Acquisition time to charge hold capacitor
GO_nDONE = 1; //Initializes A/D Conversion
while(GO_nDONE); //Wait for A/D Conversion to complete
return ((ADRESH<<8)+ADRESL); //Returns Result
}

//LCD Functions Developed by Circuit Digest.
void Lcd_SetBit(char data_bit) //Based on the Hex value Set the Bits of the Data Lines
{
if(data_bit& 1)
D4 = 1;
else
D4 = 0;

if(data_bit& 2)
D5 = 1;
else
D5 = 0;

if(data_bit& 4)
D6 = 1;
else
D6 = 0;

if(data_bit& 8)
D7 = 1;
else
D7 = 0;
}

void Lcd_Cmd(char a)
{
RS = 0;
Lcd_SetBit(a); //Incoming Hex value
EN  = 1;
__delay_ms(4);
EN  = 0;
}

Lcd_Clear()
{
Lcd_Cmd(0); //Clear the LCD
Lcd_Cmd(1); //Move the curser to first position
}

void Lcd_Set_Cursor(char a, char b)
{
char temp,z,y;
if(a== 1)
{
temp = 0x80 + b – 1; //80H is used to move the curser
z = temp>>4; //Lower 8-bits
y = temp & 0x0F; //Upper 8-bits
Lcd_Cmd(z); //Set Row
Lcd_Cmd(y); //Set Column
}
else if(a== 2)
{
temp = 0xC0 + b – 1;
z = temp>>4; //Lower 8-bits
y = temp & 0x0F; //Upper 8-bits
Lcd_Cmd(z); //Set Row
Lcd_Cmd(y); //Set Column
}
}

void Lcd_Start()
{
Lcd_SetBit(0x00);
for(int i=1065244; i<=0; i–)  NOP();
Lcd_Cmd(0x03);
__delay_ms(5);
Lcd_Cmd(0x03);
__delay_ms(11);
Lcd_Cmd(0x03);
Lcd_Cmd(0x02); //02H is used for Return home -> Clears the RAM and initializes the LCD
Lcd_Cmd(0x02); //02H is used for Return home -> Clears the RAM and initializes the LCD
Lcd_Cmd(0x08); //Select Row 1
Lcd_Cmd(0x00); //Clear Row 1 Display
Lcd_Cmd(0x0C); //Select Row 2
Lcd_Cmd(0x00); //Clear Row 2 Display
Lcd_Cmd(0x06);
}

void Lcd_Print_Char(char data)  //Send 8-bits through 4-bit mode
{
char Lower_Nibble,Upper_Nibble;
Lower_Nibble = data&0x0F;
Upper_Nibble = data&0xF0;
RS = 1;             // => RS = 1
Lcd_SetBit(Upper_Nibble>>4);             //Send upper half by shifting by 4
EN = 1;
for(int i=2130483; i<=0; i–)  NOP();
EN = 0;
Lcd_SetBit(Lower_Nibble); //Send Lower half
EN = 1;
for(int i=2130483; i<=0; i–)  NOP();
EN = 0;
}

void Lcd_Print_String(char *a)
{
int i;
for(i=0;a[i]!=’\0′;i++)
Lcd_Print_Char(a[i]);  //Split the string using pointers and call the Char function
}

int main()
{
float adc;
float volt, temp;
int c1, c2, c3, c4, temp1;
ADC_Initialize();

unsigned int a;
TRISD = 0x00;
Lcd_Start();
while(1)
{
adc = (ADC_Read(4)); // Reading ADC values
volt = adc*4.88281; // Convert it into the voltage
temp=volt/10.0;  // Getting the temperature values
temp1 = temp*100;

c1 = (temp1/1000)%10;
c2 = (temp1/100)%10;
c3 = (temp1/10)%10;
c4 = (temp1/1)%10;

Lcd_Clear();
Lcd_Set_Cursor(1,3);
Lcd_Print_String(“Temperature”);
Lcd_Set_Cursor(2,5);
Lcd_Print_Char(c1+’0′);
Lcd_Print_Char(c2+’0′);
Lcd_Print_String(“.”);
Lcd_Print_Char(c3+’0′);
Lcd_Print_Char(c4+’0′);
Lcd_Print_Char(0xDF);
Lcd_Print_String(“C”);
__delay_ms(3000);
}
return 0;
}

About The Author

Ibrar Ayyub

I am an experienced technical writer holding a Master's degree in computer science from BZU Multan, Pakistan University. With a background spanning various industries, particularly in home automation and engineering, I have honed my skills in crafting clear and concise content. Proficient in leveraging infographics and diagrams, I strive to simplify complex concepts for readers. My strength lies in thorough research and presenting information in a structured and logical format.

Follow Us:
LinkedinTwitter