๐Ÿ”Œ Project: Temperature Monitoring System Using LM35 and PIC16F877A
๐Ÿ”Œ Project: Temperature Monitoring System Using LM35 and PIC16F877A

๐Ÿ”Œ Project: Temperature Monitoring System Using LM35 and PIC16F877A

โœ… 1. Introduction

This project demonstrates how to use the PIC16F877A microcontroller to read temperature from an LM35 sensor and display it on a 16×2 LCD. The analog signal from the sensor is processed using the microcontroller’s built-in ADC (Analog-to-Digital Converter).

This is a practical starting point for understanding data acquisition and display systems.

๐ŸŽฏ 2. Learning Objectives

  • Learn how to configure and use the ADC of the PIC16F877A.
  • Interface and read data from an analog temperature sensor (LM35).
  • Display processed data on a 16×2 LCD in 4-bit mode.
  • Understand analog-to-digital voltage scaling and conversion.
  • Gain experience in debugging embedded hardware and code.

๐Ÿงฐ 3. Tools and Components

Hardware Required

  • PIC16F877A microcontroller
  • LM35 analog temperature sensor
  • 16×2 LCD display (HD44780-compatible)
  • 10kฮฉ potentiometer (for LCD contrast)
  • Breadboard and jumper wires
  • 5V regulated power supply
  • 20 MHz crystal oscillator (or 4 MHz alternative)
  • Two 22pF capacitors
  • MPLAB-compatible PIC programmer (e.g., PICkit 3 or 4)

Software Required

  • MPLAB X IDE
  • MPLAB XC8 compiler
  • Proteus (for simulation, optional)

๐Ÿ“š 4. Background Concepts

ADC (Analog-to-Digital Converter): A module that converts an analog voltage into a binary number. The PIC16F877A provides a 10-bit ADC, meaning the analog range (0โ€“5V) is converted into a digital value between 0 and 1023.

LM35 Temperature Sensor: Outputs 10 mV per ยฐC. So, 250 mV = 25ยฐC.

LCD 16×2 (HD44780): Displays characters in two lines. We use 4-bit mode to save microcontroller I/O pins.

๐Ÿ–ผ๏ธ 5. Circuit Wiring Guide

LM35 Sensor

  • VCC โ†’ +5V
  • GND โ†’ GND
  • Vout โ†’ RA0 (AN0) of PIC16F877A

LCD Display Connections (4-bit Mode)

  • RS โ†’ RC0
  • RW โ†’ GND (write only)
  • E โ†’ RC1
  • D4 โ†’ RC2
  • D5 โ†’ RC3
  • D6 โ†’ RC4
  • D7 โ†’ RC5
  • VSS โ†’ GND
  • VDD โ†’ +5V
  • VEE โ†’ Middle pin of 10k potentiometer (contrast control)

Oscillator and Power

  • 20 MHz crystal connected to OSC1/OSC2 with two 22pF capacitors to GND
  • All unused pins set as digital outputs or inputs as needed

๐Ÿงพ 6. Source Code (XC8) โ€“ Fully Commented

#include <xc.h>
#define _XTAL_FREQ 20000000  // Define clock frequency for delays

// Configuration bits
#pragma config FOSC = HS
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#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();
unsigned int ADC_Read(unsigned char);
void LCD_Init();
void LCD_Command(unsigned char);
void LCD_Char(unsigned char);
void LCD_String(const char *);
void LCD_Clear();
void LCD_Set_Cursor(unsigned char, unsigned char);

// Main function
void main(void) {
    unsigned int temp_adc;
    float temperature;
    char tempStr[16];

    ADC_Init();      // Initialize ADC
    LCD_Init();      // Initialize LCD

    LCD_String("Temp Monitor");

    while(1) {
        temp_adc = ADC_Read(0);              // Read ADC from channel 0 (RA0)
        temperature = (temp_adc * 4.88);     // Convert ADC value to millivolts
        temperature = temperature / 10.0;    // Convert mV to ยฐC (LM35)

        LCD_Set_Cursor(2, 1);
        LCD_String("Temp: ");
        sprintf(tempStr, "%.2f C", temperature);
        LCD_String(tempStr);

        __delay_ms(1000);
    }
}

// ADC Initialization
void ADC_Init() {
    ADCON0 = 0x41;  // Turn on ADC, select channel 0
    ADCON1 = 0x80;  // Right justify result, Vref = VDD
    TRISA0 = 1;     // Set RA0 as input
}

// ADC Read Function
unsigned int ADC_Read(unsigned char channel) {
    if(channel > 7) return 0;

    ADCON0 &= 0xC5;                   // Clear channel selection bits
    ADCON0 |= (channel << 3);        // Select desired channel
    __delay_ms(2);                   // Acquisition time
    GO_nDONE = 1;                    // Start conversion
    while(GO_nDONE);                 // Wait until done
    return ((ADRESH << 8) + ADRESL); // Combine 10-bit result
}

// LCD Initialization
void LCD_Init() {
    TRISC = 0x00;  // Configure PORTC as output
    __delay_ms(20);

    LCD_Command(0x02); // Initialize in 4-bit mode
    LCD_Command(0x28); // 2-line, 5x7 font
    LCD_Command(0x0C); // Display ON, cursor OFF
    LCD_Command(0x06); // Auto increment cursor
    LCD_Command(0x01); // Clear screen
}

// LCD Send Command
void LCD_Command(unsigned char cmd) {
    PORTC = (PORTC & 0x0F) | (cmd & 0xF0);
    RC0 = 0; RC1 = 1; __delay_ms(1); RC1 = 0;

    PORTC = (PORTC & 0x0F) | (cmd << 4);
    RC0 = 0; RC1 = 1; __delay_ms(1); RC1 = 0;

    __delay_ms(2);
}

// LCD Print Character
void LCD_Char(unsigned char data) {
    PORTC = (PORTC & 0x0F) | (data & 0xF0);
    RC0 = 1; RC1 = 1; __delay_ms(1); RC1 = 0;

    PORTC = (PORTC & 0x0F) | (data << 4);
    RC0 = 1; RC1 = 1; __delay_ms(1); RC1 = 0;

    __delay_ms(2);
}

// LCD Print String
void LCD_String(const char *str) {
    while(*str) {
        LCD_Char(*str++);
    }
}

// LCD Set Cursor Position
void LCD_Set_Cursor(unsigned char row, unsigned char column) {
    unsigned char pos = (row == 1) ? 0x80 + column - 1 : 0xC0 + column - 1;
    LCD_Command(pos);
}

// Clear LCD
void LCD_Clear() {
    LCD_Command(0x01);
    __delay_ms(2);
}

๐Ÿ”ง 7. Troubleshooting and Debugging

LCD not displaying characters Check:

  • LCD contrast via potentiometer
  • Wiring of data and control lines
  • Delay timing and initialization steps

Incorrect temperature readings Check:

  • LM35 placement and output voltage using a multimeter
  • Conversion formula (ADC value ร— 4.88 mV, then รท 10)

No ADC conversion Check:

  • ADCON0 and ADCON1 settings
  • RA0 must be input
  • Ensure channel select bits are correct

Garbage characters on LCD Check:

  • 4-bit initialization sequence
  • Correct delays between nibbles
  • Proper pull-down or pull-up configurations if required

๐Ÿš€ 8. Extensions and Improvements

  • Add buzzer alert: Use another pin (e.g., RD0) to drive a buzzer when temperature exceeds a threshold.
  • UART output: Send temperature readings to a PC or IoT server using UART.
  • Data logging: Store values to EEPROM with timestamps using a real-time clock (DS1307).
  • Graphical Interface: Use a Nokia 5110 or OLED display for better visuals.