
đź”§ PIC Microcontroller Project: Digital Temperature Monitor with LM35 and LCD
1. Introduction
In this project, we’ll build a simple digital thermometer using a PIC16F877A microcontroller. It will read temperature from an LM35 sensor and display the value in Celsius on a 16×2 LCD. This kind of setup is commonly used in temperature monitoring systems like weather stations, greenhouses, or home automation systems.
2. Learning Objectives
By completing this project, you will:
- Learn how to connect and use an analog temperature sensor with a PIC microcontroller.
- Understand how the Analog-to-Digital Converter (ADC) works on the PIC16F877A.
- Interface a 16×2 character LCD using 4-bit mode.
- Write and structure embedded C code using MPLAB X and the XC8 compiler.
- Test, troubleshoot, and modify your embedded system for real-world use.
3. Components and Tools Needed
You’ll need the following components and tools:
- One PIC16F877A microcontroller
- One LM35 analog temperature sensor
- One 16×2 character LCD (HD44780 compatible)
- One 10k potentiometer (for adjusting LCD contrast)
- One 20MHz crystal oscillator
- Two 22pF ceramic capacitors (for the crystal)
- Breadboard and jumper wires
- Regulated 5V power supply
- MPLAB X IDE and XC8 compiler
- PIC programmer (such as PICkit3)
4. Concept Overview
The LM35 outputs a voltage that increases linearly with temperature. For example, 250 millivolts means 25 degrees Celsius. The microcontroller reads this analog signal through its ADC module and converts it into a digital number. We then convert that number into a readable temperature and show it on the LCD screen.
The LCD is operated in 4-bit mode to save microcontroller pins. This means we send data in two parts (high nibble and low nibble) using only four data lines instead of eight.
5. Step-by-Step Instructions
Hardware Setup
Connecting the LCD:
- Connect the LCD’s VSS pin to ground and VDD pin to 5V.
- The VO pin (for contrast) should connect to the middle pin of a 10k potentiometer, with the outer pins connected to 5V and ground.
- RS (Register Select) connects to RC0 on the PIC.
- RW (Read/Write) connects to ground (since we only write to the display).
- E (Enable) connects to RC1.
- Data pins D4 to D7 on the LCD connect to RC2 to RC5 on the PIC.
- Connect the LCD’s backlight (if available) through a current-limiting resistor to 5V.
Connecting the LM35:
- Pin 1 of the LM35 connects to 5V.
- Pin 2 (the output) connects to RA0 on the PIC (analog channel 0).
- Pin 3 connects to ground.
Connecting the crystal:
- Place the 20MHz crystal between the OSC1 and OSC2 pins (pins 13 and 14).
- Add a 22pF capacitor from each of these pins to ground.
Powering the system:
- Make sure your PIC microcontroller receives a stable 5V regulated power supply.
- Ensure all grounds are connected together.
Code (XC8 for MPLAB X)
Below is the full C code with comments:
#define _XTAL_FREQ 20000000
#include <xc.h>
#include <stdio.h>
// Configuration bits
#pragma config FOSC = HS
#pragma config WDTE = OFF
#pragma config PWRTE = OFF
#pragma config BOREN = ON
#pragma config LVP = OFF
#pragma config CPD = OFF
#pragma config WRT = OFF
#pragma config CP = OFF
// Function declarations
void ADC_Init(void);
unsigned int ADC_Read(unsigned char);
void LCD_Init(void);
void LCD_Command(unsigned char);
void LCD_Char(unsigned char);
void LCD_String(const char*);
void main(void) {
char buffer[16];
unsigned int adc_value;
float temperature;
TRISA = 0x01; // RA0 as input
TRISC = 0x00; // PORTC as output for LCD
ADC_Init();
LCD_Init();
while (1) {
adc_value = ADC_Read(0); // Read from analog channel 0
temperature = (adc_value * 5.0 / 1023.0) * 100.0;
LCD_Command(0x80); // Move to first line
sprintf(buffer, "Temp: %.2f C", temperature);
LCD_String(buffer);
__delay_ms(1000);
}
}
void ADC_Init() {
ADCON0 = 0x41; // Turn on ADC, select channel 0
ADCON1 = 0x80; // Right-justified result, Vref = VDD
}
unsigned int ADC_Read(unsigned char channel) {
if (channel > 7) return 0;
ADCON0 &= 0xC7; // Clear current channel
ADCON0 |= (channel << 3); // Select desired channel
__delay_ms(2); // Wait for acquisition
GO_nDONE = 1; // Start conversion
while (GO_nDONE); // Wait until done
return ((ADRESH << 8) + ADRESL); // Combine 10-bit result
}
void LCD_Command(unsigned char cmd) {
PORTC &= 0xF8; // Clear control bits
PORTC |= (cmd >> 4); // Send high nibble
PORTC |= 0x04; __delay_ms(2); PORTC &= ~0x04;
PORTC &= 0xF8;
PORTC |= (cmd & 0x0F); // Send low nibble
PORTC |= 0x04; __delay_ms(2); PORTC &= ~0x04;
}
void LCD_Char(unsigned char data) {
PORTC |= 0x01; // RS = 1 for data
PORTC &= ~0x02; // RW = 0 to write
PORTC |= (data >> 4); PORTC |= 0x04; __delay_ms(2); PORTC &= ~0x04;
PORTC &= 0xF8;
PORTC |= (data & 0x0F); PORTC |= 0x04; __delay_ms(2); PORTC &= ~0x04;
}
void LCD_String(const char *str) {
while (*str) {
LCD_Char(*str++);
}
}
void LCD_Init() {
__delay_ms(20);
LCD_Command(0x02); // 4-bit mode
LCD_Command(0x28); // 2-line display, 5x7 font
LCD_Command(0x0C); // Display ON
LCD_Command(0x06); // Entry mode set
LCD_Command(0x01); // Clear display
}
6. Testing and Debugging Tips
- If the LCD only shows blocks, check the contrast by adjusting the potentiometer and confirm the Enable and RS pins are correctly connected.
- If no temperature reading appears or the value is always zero, check the LM35 sensor’s output with a multimeter and ensure the RA0 pin is set as an input.
- If temperature readings flicker, try placing a small capacitor (like 100nF) between the LM35 output and ground to reduce noise.
- Confirm the microcontroller’s oscillator is working by measuring the OSC pins with an oscilloscope if available.
- Double-check ADC configuration and that the ADCON0 and ADCON1 settings match the hardware.
7. Project Extensions
Once the basic version is working, you can add more features:
- Add a push button to switch between Celsius and Fahrenheit.
- Trigger a buzzer or LED when the temperature exceeds a limit.
- Log temperature values to EEPROM or an SD card.
- Send temperature data wirelessly using an ESP8266 Wi-Fi module.
- Optimize for low power by putting the microcontroller to sleep between readings.