In this project, different GPIO of the microcontroller are connected to different Relays. The GPIO’s are controlled using Bluetooth communication. The user can send the data to the device using their mobile device such as a computer or a mobile phone.
Controller used: PIC16F877a
Bluetooth module: HC-05
Line of Sight Range ( Max. ): up to 10 feet
Updates
Rev 1: –
1. Added ICSP
2. Added Power Indicator LED to the schematics
3. Added Graphic Lines to Page 1 of Schematics
3. Added Text to Page 1 of Schematics
NOTE on ICSP:
The ICSP pins PGD and PGC are shared with the LCD.
Remove LCD before doing programming.
The ICSP does not provide power.
Provide power to the board after connecting to PICkit 2 programmer.
Schematic Diagram
Circuit Board Images
Working Video
Code
main.hboard.huart.hLCD_16x2.h
/*
* File: main.c
* Author: abhay
*
* Created on July 14, 2023, 12:05 AM
*/
// PIC16F877A Configuration Bit Settings
// 'C' source line config statements
// CONFIG
#pragma config FOSC = HS // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#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)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#define _XTAL_FREQ 16000000
#include <xc.h>
#include <pic16f877a.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include<string.h>
#include "board.h"
#include "LCD_16x2.h"
#include "uart.h"
#define Relay_1_ON '2'
#define Relay_1_OFF '3'
#define Relay_2_ON '5'
#define Relay_2_OFF '6'
void GPIO_init(void);
void clear_flag(void);
void ACTION(void);
void Ux_Menu_design(void);
void relay_on_off(char Relay, char onoff);
uint8_t RX_chr;
void __interrupt() myISR() {
if (RCIF == 1) {
if (RCSTAbits.OERR) {
CREN = 0;
NOP();
CREN = 1;
}
RX_chr = RCREG;
RCIF = 0;
}
}
/*
*
*/
int main(int argc, char** argv) {
GPIO_init();
UART_init(9600);
InitLCD(); // Initialize LCD in 8bit mode
//const char *msg = "Hello World!";
// const char *msg1 = "Abhay";
//WriteStringToLCD(msg1);
char lcd_buff1[16] = "Abhay Kant";
char lcd_buff2[16] = "Kant";
ClearLCDScreen();
lcd_set_cursor(1, 1);
WriteDataToLCD('E');
WriteDataToLCD('X');
WriteDataToLCD('A');
WriteDataToLCD('S');
WriteDataToLCD('U');
WriteDataToLCD('B');
WriteDataToLCD('.');
WriteDataToLCD('C');
WriteDataToLCD('O');
WriteDataToLCD('M');
lcd_set_cursor(2, 1);
WriteStringToLCD(lcd_buff1);
__delay_ms(2000);
ClearLCDScreen();
Ux_Menu_design();
/*
* Interrupt Setup START
*/
INTCONbits.GIE = 1;
INTCONbits.PEIE = 1;
PIE1bits.TXIE = 0;
PIE1bits.RCIE = 1;
/*
* Interrupt Setup END
*/
/*
* Menu Design
* 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
*1 R 1 O N R X {}
*2 R 2 O F F m e n u : 0
*
* Relay 1 ON: 2
* Relay 1 OFF: 3
* Relay 2 ON: 5
* Relay 2 OFF: 6
* Menu : 9 // send menu to the bluetooth terminal
*/
while (1) {
ACTION();
}
return (EXIT_SUCCESS);
}
unsigned char msgACK[16] = "Received";
void ACTION() {
// Display the received character
if (RX_chr != 0) {
lcd_set_cursor(1, 13);
WriteDataToLCD(RX_chr);
}
if (RX_chr == Relay_1_ON) {
relay_on_off(1, 1);
txstr(msgACK);
clear_flag();
}
if (RX_chr == Relay_1_OFF) {
relay_on_off(1, 0);
txstr(msgACK);
clear_flag();
}
if (RX_chr == Relay_2_ON) {
relay_on_off(2, 1);
txstr(msgACK);
clear_flag();
}
if (RX_chr == Relay_2_OFF) {
relay_on_off(2, 0);
txstr(msgACK);
clear_flag();
}
if (RX_chr == '9') {
PORTDbits.RD1 = 1;
txstr(msgACK);
clear_flag();
}
if (RX_chr == '0') {
PORTDbits.RD1 = 0;
txstr(msgACK);
clear_flag();
}
}
void clear_flag(void) {
RX_chr = 0;
}
// char *msgUxDesign2 = "1234567890123456";
const char *msgUxDesign1 = "R1 Rx";
const char *msgUxDesign2 = "R2 23 45 90";
void Ux_Menu_design() {
lcd_set_cursor(1, 1);
WriteStringToLCD(msgUxDesign1);
lcd_set_cursor(2, 1);
WriteStringToLCD(msgUxDesign2);
}
const char *msgON = "ON ";
const char *msgOFF = "OFF";
void relay_on_off(char Relay, char onoff) {
if (Relay == 1) {
if (onoff == 1) {
lcd_set_cursor(1, 3);
WriteStringToLCD(msgON);
Relay_1a_PIN = 1;
Relay_1b_PIN = 0;
} else if (onoff == 0) {
lcd_set_cursor(1, 3);
WriteStringToLCD(msgOFF);
Relay_1a_PIN = 0;
Relay_1b_PIN = 0;
}
} else if (Relay == 2) {
if (onoff == 1) {
lcd_set_cursor(2, 3);
WriteStringToLCD(msgON);
Relay_2a_PIN = 1;
Relay_2b_PIN = 0;
} else if (onoff == 0) {
lcd_set_cursor(2, 3);
WriteStringToLCD(msgOFF);
Relay_2a_PIN = 0;
Relay_2b_PIN = 0;
}
}
}
void GPIO_init(void) {
/*
* TRIS = Data Direction Register
* 0 = OUTPUT
* 1 = INPUT
* TRISD &= ~(1 << 1); // LED RD1 as OUTPUT
* TRISD1 = 0; // RD1 as OUTPUT
*/
LED_PORT_DIR &= ~(1 << 1);
LED_PIN = 0;
Relay_1a_DIR &= ~(1 << 0);
Relay_1a_PIN = 0;
Relay_1b_DIR &= ~(1 << 2);
Relay_1b_PIN = 0;
Relay_2a_DIR &= ~(1 << 4);
Relay_2a_PIN = 0;
Relay_2b_DIR &= ~(1 << 5);
Relay_2b_PIN = 0;
/*
* LCD Pins Initialize
*/
TRISB = 0;
/*
* UART Pins Initialize
*/
TRISCbits.TRISC6 = 0; // TX
TRISCbits.TRISC7 = 1; //RX
}
/*
* File: board.h
* Author: abhay
*
* Created on July 14, 2023, 12:41 AM
*/
#ifndef BOARD_H
#define BOARD_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* LED
* color: RED
* @param PORT: D
* @param PIN: 1
*/
#define LED_PORT_DIR TRISD
#define LED_Port PORTD
#define LED_PIN PORTDbits.RD1
#define Relay_1a_DIR TRISD
#define Relay_1a_Port PORTD
#define Relay_1a_PIN PORTDbits.RD0
#define Relay_1b_DIR TRISD
#define Relay_1b_Port PORTD
#define Relay_1b_PIN PORTDbits.RD2
#define Relay_2a_DIR TRISD
#define Relay_2a_Port PORTD
#define Relay_2a_PIN PORTDbits.RD4
#define Relay_2b_DIR TRISD
#define Relay_2b_Port PORTD
#define Relay_2b_PIN PORTDbits.RD5
#ifdef __cplusplus
}
#endif
#endif /* BOARD_H */
/*
* File: uart.h
* Author: abhay
*
* Created on July 17, 2023, 9:42 AM
*/
#ifndef UART_H
#define UART_H
#ifdef __cplusplus
extern "C" {
#endif
#define F_CPU 16000000
#define SPBRG_LOW_BRGH_value(x) ((F_CPU/(x * 64)) - 1)
#define SPBRG_HIGH_BRGH_value(x) ((F_CPU/(x * 16)) - 1)
void UART_init(uint16_t Baud_Rate) {
TXSTAbits.CSRC = 0;
TXSTAbits.TX9 = 0;
TXSTAbits.TXEN = 1;
TXSTAbits.SYNC = 0;
/* BRGH - High Baud Rate Select Bit
* 1: High Speed
* 0: Low Speed
*/
TXSTAbits.BRGH = 1;
TXSTAbits.TRMT = 0;
TXSTAbits.TX9D = 0;
RCSTAbits.SPEN = 1;
RCSTAbits.RX9 = 0;
RCSTAbits.SREN = 0;
RCSTAbits.CREN = 1;
RCSTAbits.ADDEN = 0;
RCSTAbits.FERR = 0;
RCSTAbits.OERR = 0;
RCSTAbits.RX9D = 0;
/*
* Baud Rate Formula
* Asynchronous
* Baud Rate = Fosc / (64 (x + 1))
* Baud_Rate x (64 (x+1)) = FOSC
* SPBRG = (x) = ( Fosc / ( Baud_Rate * 64 ) ) - 1
*
* For Badu Rate = 9600
* SPBRG = 103 when BRGH = 1
*/
SPBRG = 103;
TXIF = RCIF = 0;
}
void tx(unsigned char a) {
while (TXIF == 0); // Wait till the transmitter register becomes empty
TXIF = 0; // Clear transmitter flag
TXREG = a; // load the char to be transmitted into transmit reg
}
unsigned char rx() {
while (!RCIF);
RCIF = 0;
return RCREG;
}
void txstrArray(unsigned char *s) {
while (*s) {
tx(*s++);
}
}
void txstr(unsigned char *s) {
while (*s) {
tx(*s++);
__delay_us(10);
}
}
#ifdef __cplusplus
}
#endif
#endif /* UART_H */
/*
* File: LCD_16x2.h
* Author: abhay
*
* Created on July 16, 2023, 6:21 PM
*/
#ifndef LCD_16X2_H
#define LCD_16X2_H
#include "board.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
*Sr.No. Hex Code Command to LCD instruction Register
1 01 Clear display screen
2 02 Return home
3 04 Decrement cursor (shift cursor to left)
4 06 Increment cursor (shift cursor to right)
5 05 Shift display right
6 07 Shift display left
7 08 Display off, cursor off
8 0A Display off, cursor on
9 0C Display on, cursor off
10 0E Display on, cursor blinking off
11 0F Display on, cursor blinking on
12 10 Shift cursor position to left
13 14 Shift the cursor position to the right
14 18 Shift the entire display to the left
15 1C Shift the entire display to the right
16 80 Force cursor to the beginning ( 1st line)
17 C0 Force cursor to the beginning ( 2nd line)
18 38 2 lines and 5×7 matrix
*/
// Define Pins
#define LCD_RS RB1 // RS pin for LCD
#define LCD_RW RB2 // RS pin for LCD
#define LCD_E RB3 // Enable pin for LCD
#define LCD_Data_Bus_D4 RB4 // Data bus bit 4
#define LCD_Data_Bus_D5 RB5 // Data bus bit 5
#define LCD_Data_Bus_D6 RB6 // Data bus bit 6
#define LCD_Data_Bus_D7 RB7 // Data bus bit 7
// Define Pins direction registrers
#define LCD_E_Dir TRISB3
#define LCD_RS_Dir TRISB1
#define LCD_RW_Dir TRISB2
#define LCD_Data_Bus_Dir_D4 TRISB4
#define LCD_Data_Bus_Dir_D5 TRISB5
#define LCD_Data_Bus_Dir_D6 TRISB6
#define LCD_Data_Bus_Dir_D7 TRISB7
// Constants
#define E_Delay 1000
// Function Declarations
void WriteCommandToLCD(unsigned char);
void WriteDataToLCD(char);
void InitLCD(void);
void WriteStringToLCD(const char*);
void ClearLCDScreen(void);
void ToggleEpinOfLCD(void);
void ToggleEpinOfLCD(void) {
LCD_E = 1; // Give a pulse on E pin
__delay_us(E_Delay); // so that LCD can latch the
LCD_E = 0; // data from data bus
__delay_us(E_Delay);
}
void WriteCommandToLCD(unsigned char Command) {
LCD_RS = 0; // It is a command
PORTB &= 0x0F; // Make Data pins zero
PORTB |= (Command & 0xF0); // Write Upper nibble of data
ToggleEpinOfLCD(); // Give pulse on E pin
PORTB &= 0x0F; // Make Data pins zero
PORTB |= ((Command << 4)&0xF0); // Write Lower nibble of data
ToggleEpinOfLCD(); // Give pulse on E pin
}
void WriteDataToLCD(char LCDChar) {
LCD_RS = 1; // It is data
PORTB &= 0x0F; // Make Data pins zero
PORTB |= (LCDChar & 0xF0); // Write Upper nibble of data
ToggleEpinOfLCD(); // Give pulse on E pin
PORTB &= 0x0F; // Make Data pins zero
PORTB |= ((LCDChar << 4)&0xF0); // Write Lower nibble of data
ToggleEpinOfLCD(); // Give pulse on E pin
}
void InitLCD(void) {
// Firstly make all pins output
LCD_E = 0; // E = 0
LCD_RS = 0; // RS = 0
LCD_Data_Bus_D4 = 0; // Data bus = 0
LCD_Data_Bus_D5 = 0; // Data bus = 0
LCD_Data_Bus_D6 = 0; // Data bus = 0
LCD_Data_Bus_D7 = 0; // Data bus = 0
LCD_E_Dir = 0; // Make Output
LCD_RS_Dir = 0; // Make Output
LCD_RW_Dir = 0;
LCD_RW = 0;
LCD_Data_Bus_Dir_D4 = 0; // Make Output
LCD_Data_Bus_Dir_D5 = 0; // Make Output
LCD_Data_Bus_Dir_D6 = 0; // Make Output
LCD_Data_Bus_Dir_D7 = 0; // Make Output
///////////////// Reset process from datasheet //////////////
__delay_ms(40);
PORTB &= 0x0F; // Make Data pins zero
PORTB |= 0x30; // Write 0x3 value on data bus
ToggleEpinOfLCD(); // Give pulse on E pin
__delay_ms(6);
PORTB &= 0x0F; // Make Data pins zero
PORTB |= 0x30; // Write 0x3 value on data bus
ToggleEpinOfLCD(); // Give pulse on E pin
__delay_us(300);
PORTB &= 0x0F; // Make Data pins zero
PORTB |= 0x30; // Write 0x3 value on data bus
ToggleEpinOfLCD(); // Give pulse on E pin
__delay_ms(2);
PORTB &= 0x0F; // Make Data pins zero
PORTB |= 0x20; // Write 0x2 value on data bus
ToggleEpinOfLCD(); // Give pulse on E pin
__delay_ms(2);
/////////////// Reset Process End ////////////////
WriteCommandToLCD(0x28); //function set
WriteCommandToLCD(0x0c); //display on,cursor off,blink off
WriteCommandToLCD(0x01); //clear display
WriteCommandToLCD(0x06); //entry mode, set increment
WriteCommandToLCD(0x0e); //display on,cursor on,blink off
WriteCommandToLCD(0x0f); //display on,cursor on,blink on
}
void WriteStringToLCD(const char *s) {
while (*s) {
WriteDataToLCD(*s++); // print first character on LCD
}
}
void ClearLCDScreen(void) // Clear the Screen and return cursor to zero position
{
WriteCommandToLCD(0x01); // Clear the screen
__delay_ms(2); // Delay for cursor to return at zero position
}
void lcd_set_cursor(uint8_t row, uint8_t col) {
if (row == 1) {
WriteCommandToLCD(0x80 | (col - 1));
} else if (row == 2) {
WriteCommandToLCD(0xC0 | (col - 1));
}
}
#ifdef __cplusplus
}
#endif
#endif /* LCD_16X2_H */