HC-SR04 Ultrasonic Sensor Interfacing with Pic Microcontroller – Distance Measurement

In this tutorial, we will learn to interface HC-SR04 ultrasonic sensor with pic microcontrollers and how to measure distance with the HC-SR04 ultrasonic sensor? There are a variety of Pic microcontroller chips available. But the concepts, you will learn to interface the HC-SR04 ultrasonic sensor with any pic microcontroller remain the same. This sonar sensor has important and useful applications for embedded systems projects such as contactless range meter, contactless water level measurement, and sonar system. Before we proceed further with this tutorial, we recommend you to read about ultrasonic sensors and its working here: 

Difference Between HC-SR04 and HY-SRF05

In this tutorial, we will be using HC-SR04 ultrasonic sensor but there is another version of this sensor which is HY-SRF05. Both sensors have the same electrical properties and can be used in replacement for each other except HY-SRF05 has one 1mm better precision than  HC-SR04. But 1mm is not an issue when measuring distance in the units of cm and meter. 

We will be using HC-SR04 for distance measurement. But the code works with the HY-SRF05 sensor also. PIC16F887 microcontroller will be used to read ultrasonic sensor output signals. Moreover, a 16×2 LCD is used to display the measured value of distance on an LCD.  

ultrasonic sensor interfacing with pic microcontroller

HC-SR04 Ultrasonic sensor Introduction

This is a contactless measurement sensor. It can measure distance up to 2cm – 400cm.  Its accuracy is about three millimeters which is very efficient in water level and depth measurement applications.

HC-SR04 Ultrasonic sensor Pinout

It consists of four pins. The following figure shows the pinout diagram.

HC-SR04 Ultrasonic Sensor Pinout diagram
Pin NameFunction
VccConnect 5 volt supply with this pin.
TriggerUsed to initiate distance ranging
EchoProvides output pulse signal
GroundConnect with ground terminal

Working

HC-SR04 ultrasonic sensor is consists of three main circuits. Description of each module is given below:

HC-SR04 ultrasonic sensor

Transmitter module

When we want to measure distance with HC-SR04, we provide 10 microseconds pulse to the trigger pin of the sensor. In response, The transmitter circuit sends eight 40KHz ultrasonic sound wave pulses through the air. Hence, the transmitter circuit converts the electrical signal into a 40KHz burst of 8 pulses.

Receiver module

When an ultrasonic transmitter circuit completes transmission of 8 pulses. The echo pin goes high. The echo pin remains active high until the ultrasonic sound wave does not reflect back to the receiver circuit after striking the object Under test. As soon as the ultrasonic signal received by the receiver circuit after striking with an object, the echo pin goes low.

How to Measure Distance with HC-SR04 Ultrasonic Sensor

To measure distance with the HC-SR04 ultrasonic sensor, we provide a 10us trigger signal from the pic microcontroller to trigger pin of the sensor. The width of the trigger signal should be at least 10 microseconds. After that, an ultrasonic transmitter transmits a burst of 8 ultrasonic sound waves and makes the echo output pin active high. 

If there is any obstacle in the front ultrasonic sensor and it is in the measurable range of sensor, the ultrasonic sound wave will reflect back to the HC-SR04 ultrasonic sensor.

When the receiver circuit receives the reflected signal ,the echo pin of the ultrasonic sensor goes low. Hence, a pulse is generated on the echo output pin.The time for which pulse remains high is equal to the total time taken by ultrasonic waves burst from sending and receiving back. That means the pulse duration is directly proportional to the distance.  

In short, we can measure the distance by measuring the time for which the output echo pin remains in logic high state (pulse duration or pulse width).

Ultrasonic sensor HC-SR04 working

We will use PIC16F887 microcontroller Timer module to measure the pulse duration.

How to convert Pulse Duration into Distance?

Let’s assume that we have measured the pulse duration using PIC16F887 timer. Now the question is how to convert the pulse duration (t) into distance. Well this is the easiest part of this tutorial. We can convert the pulse duration (t) into distance (S) using the famous equation of physics that is distance, time and speed equation. 

Distance (S) = Speed (v) * t 

Here v is the speed of ultrasonic waves in air. The speed of ultrasonic waves in air is equal to the speed of sound which is equal to 340 m/s (meter per second). Hence, the above equation becomes:

S = 340 * t  // distance in meter

The above equation will give distance output in units of meter. But, if you want the distance in centimeter units, multiply 340 with 100. Also, divide it by 2 to get one path distance.

S = ( 34000 * t ) / 2 //distance in centimeter

Timing Diagram

The following figure shows the timing diagram of HC-SR04 input and output signals.

timing diagram of ultrasonic sensor HC-SR04

According to the above diagram, we will send a trigger pulse of at least 10 microseconds to sensor from Pic microcontroller. Afterward, the sensor will generate 8 cycles of a sonic burst. This electronics burst comes back after colliding with obstacles.  The echo pin will produce a pulse of time taken by electronics burst to send and receive back.

To measure distance, we need to measure the time of the output pulse. After measuring the pulse time, we can easily calculate the distance by using the above given formula.

Ultrasonic sensor HC-SR04 interfacing with pic microcontroller

To measure output pulse duration we will use PIC16F877A timer. 

We have previously posted an article on how to use timers of pic microcontroller, you can refer to this article: 

To measure pulse duration with the PIC16887 timer, detect the rising edge of the pulse as soon as the rising edge is detected, enable the timer module and the timer will start counting. After that wait for the falling edge to occur. As soon as the falling edge is detected, disable the timer module. Now, save the content of the timer registers into a variable. This variable contains the number of ticks of the timer for the time duration of the output pulse.

To convert, these ticks into pulse duration, divide the number of ticks with the operating frequency of the timer module. Finally, this will give us the pulse duration. After that, multiply the pulse duration with the speed of light to get the measured distance from pulse duration. 

Schematic Diagram

The circuit given below shows the connections between Ultrasonic sensor HC-SR04, 16×2 LCD and Pic microcontroller. 16×2 LCD is used to display the measured distance value. If you do not know how to interface LCD with pic microcontroller. You may refer to this article: 

Ultrasonic sensor HC-SR04 interfacing with pic microcontroller

HC-SR04 Code for PIC Microcontroller

Code is written using Mikro c for pic and it will work perfectly with pic16f877a and pic16f887 microcontroller. The crystal oscillator frequency is 8HMz.

// LCD module connections
sbit LCD_RS at RD0_bit;
sbit LCD_EN at RD6_bit;
sbit LCD_D4 at RD5_bit;
sbit LCD_D5 at RD4_bit;
sbit LCD_D6 at RD3_bit;
sbit LCD_D7 at RD2_bit;

sbit LCD_RS_Direction at TRISD0_bit;
sbit LCD_EN_Direction at TRISD6_bit;
sbit LCD_D4_Direction at TRISD5_bit;
sbit LCD_D5_Direction at TRISD4_bit;
sbit LCD_D6_Direction at TRISD3_bit;
sbit LCD_D7_Direction at TRISD2_bit;
// End LCD module connections

/* variables to store pulse duration and to display string on LCD 8 */
int time_taken;
char txt[7];
/* Main code to take distance measurement and initializations */
void main() 
{

  TRISD=0x00;  /* initialize PORTD as digital ouput pin for LCD */
  Lcd_Init();   /* initialize LCD library functions */
  Lcd_Cmd(_LCD_CLEAR); /* Clear whatever is written on LCD */
  Lcd_Cmd(_LCD_CURSOR_OFF); /* Turn of LCD Cursor display */
  
  TRISB.B0 = 0; /* Set RB0 pin as a digital output pin */
  PORTB.B0 =0;  /* Initially sets the RB0 pin as activ low */

  TRISB.B4 = 0; /* Set RB4 pin as a digital input pin */
  PORTB.B4 =0;  /* Initially sets the RB4 pin as activ low */
  
  /* display "HC-SR04" on LCD */
  Lcd_Out(1,1,"HC-SR04");
  
  Delay_ms(2000); /* wait for one second */
  Lcd_Cmd(_LCD_CLEAR);  /* Clear text from LCD */
  
  T1CON = 0x10; /* Set timer1 prescaler value to 1:2 prscale and disable timer1 */
  
  while(1)
  {
     /* Initialize Timer1 register to zero */
     TMR1H = 0;
     TMR1L = 0;
     
     /* send 10us puls to triger pin of HC-SR04 from RB0 pin */
     PORTB.F0 = 1;
     Delay_us(10);
     PORTB.F0 = 0;
     
     while(!PORTB.F4);   /* wait till echo output signal goes high */
     T1CON.F0 = 1;    /* enable the Timer1 */
     while(PORTB.F4);  /* wait till echo output signal goes low */
     T1CON.F0 = 0; /*disable timer1 */

     time_taken= (TMR1L | (TMR1H<<8)); /*read the content of Timer1 registers */
     time_taken= (TMR1L | (TMR1H<<8))/58.82; /* convert duration into distance */
     
     /* This code block displays the measured distance value on LCD */
     if(time_taken>=0 && time_taken<=400)
     {
       IntToStr(time_taken,txt);
       Ltrim(txt);
       Lcd_Cmd(_LCD_CLEAR);
       Lcd_Out(1,1,"Distance = ");
       Lcd_Out(1,12,txt);
       Lcd_Out(1,15,"cm");
     }
    else
     {
       Lcd_Cmd(_LCD_CLEAR);
       Lcd_Out(1,1,"Out of Range");
     }
     Delay_ms(500);
  }
}

How Code Works? 

First, we define the PIC16F877A microcontroller pins to which 16×2 LCD data and command pins are connected. In mikroC for pic compiler, LCD library provides all functions to print data on the screen. We just need to define the connection of LCD pins with pic microcontroller pins. These lines define the connection of PORTD pins RD5-RD2 with D4-D7 pins of LCD. Also, connect RS pin with RD0 and EN pin with RD6. 

// LCD module connections
sbit LCD_RS at RD0_bit;
sbit LCD_EN at RD6_bit;
sbit LCD_D4 at RD5_bit;
sbit LCD_D5 at RD4_bit;
sbit LCD_D6 at RD3_bit;
sbit LCD_D7 at RD2_bit;

sbit LCD_RS_Direction at TRISD0_bit;
sbit LCD_EN_Direction at TRISD6_bit;
sbit LCD_D4_Direction at TRISD5_bit;
sbit LCD_D5_Direction at TRISD4_bit;
sbit LCD_D6_Direction at TRISD3_bit;
sbit LCD_D7_Direction at TRISD2_bit;
// End LCD module connections

Enable GPIO Pins

Initialize the LCD library and set the PORTD as a digital output pin. If you don’t know how to use GPIO Ports of pic microcontrollers, you can refer to this article:

 TRISD=0x00;  /* initialize PORTD as digital ouput pin for LCD */
 Lcd_Init();   /* initialize LCD library functions */
 Lcd_Cmd(_LCD_CLEAR); /* Clear whatever is written on LCD */
 Lcd_Cmd(_LCD_CURSOR_OFF); /* Turn of LCD Cursor display */

RB0 is used to provide 10us trigger signal to HC-SR04 trigger pin and RB4 pin is used to detect positive and negative edges of echo output signal of ultrasonic sensor. Therefore, we initialize the RB0 pin as a digital output pin and RB4 pin as a digital input pin.

TRISB.B0 = 0; /* Set RB0 pin as a digital output pin */
PORTB.B0 =0;  /* Initially sets the RB0 pin as activ low */


TRISB.B4 = 0; /* Set RB4 pin as a digital input pin */
PORTB.B4 =0;  /* Initially sets the RB4 pin as activ low */
  

Timer1 Configuration for Distance Measurement

PIC16F877A microcontroller has three timer modules such as Timer0, Timer1, and Timer2. We used Timer1 in this project. Timer1 is a 16-bit timer and it can be used either in timer or counter mode. It consists of two 8-bit registers namely; TMR1H, TMR1L. TMR1H holds the most significant 8-bits and TMR1L holds the least significant 8-bits. Hence, the Timer1 can count from 0x0000 to 0xFFFF. 

Set Timer1 Mode

In this project, we used Timer1 in timer mode. Because in timer mode, the timer register values increments on every instruction cycle. Therefore, we can measure the time from timer register values. 

T1CON register is used to configure Timer1 either as a timer or counter. The mode of the timer is selected with the TMR1CS bit1 of the T1CON register. Setting TMR1CS bit to 0 enables the timer mode, and setting TMR1CS but to 1, enables the counter mode.  We are using Timer1 in the timer mode. Therefore, set the TMR1CS bit to 0 and it will select the internal clock (FOSC/4) for Timer1. 

Timer1 Enable Bit

TMR1ON bit0 enables or disables the Timer counter. At the start, disable the Timer1 by clearing the TM1ON bit. Because we will enable it when we want to measure the pulse width of HC-SR04 echo output signal. 

Setting Prescaler value

Bit4 and 5 of the T1CON register configure the Prescaler value for Timer1. Prescaler is used to scale down the selected operating frequency for timers. For example, with TMR1CS bit1 of the T1CON register, we selected FOSC/4 operating frequency for Timer1. Here, FOSC is the operating frequency of the PIC16F877A microcontroller. For instance, the crystal oscillator we used in this project is of 8MHz. Hence, the operating frequency for Timer1 will be 8MHz/4 = 2MHz. 

But we can further scale down the operating frequency of Timer1 by using a prescale value. Bit4 and 5 of T1CON register can be initialized to set prescale value 1, 2, 4, 8. In this project, we use a prescaler value of 2. Hence, the operating frequency of Timer1 will be 1MHz. In other words, the Timer1 increments its value after every 1/1000000 second or 1 microsecond. 

This line configures Timer1 in timer mode, an operating frequency of 1MHz, and Timer1 remains disable. 

T1CON = 0x10; /* Set timer1 prescaler value to 1:2 prscale and disable timer1 */

Pulse Duration to Distance conversion 

As we mentioned earlier, the count value of timer can be converted into time duration by multiplying it with time of each instruction cycle. For example, we take the content of the Timer1 register and save them into a variable “time_taken”. 

time_taken = (TMR1L | (TMR1H<<8)); 

Now the variable ‘timer_taken’ contains the count value of Timer1. To convert this count value into time, we simply multiply it with the time of the Timer1 instruction cycle. In other words.  divide it with the operating frequency of Timer1 which we calculated in the last section. 

time_taken =  time_taken * 1 / Timer1 frequency * prescaler 
           = (time_taken * 1 /  2MHz * 2   // Timer1 Frequency = Fosc / 4
           = time_taken  * 1/ 1MHz 
           = time_taken  * 1/1000000Hz
           =time_taken * 1 microsecond

Now by using distance,  time and speed formula that we derived earlier:

S = ( 34000 * t ) / 2
    =  34000 * time_taken * 1us  / 2 
    = time_taken * 0.034 / 2  =  time_taken * 0.017
    = time_taken / 58.58

Inside the while(1) loop, we take measurement of distance after every one second. 

Before taking pulse output from HC-SR04, we Initialize both upper and lower Timer1 registers zero     

TMR1H = 0;
TMR1L = 0;

After that, the PIC16F877A microcontroller provides a 10us pulse to trigger the pin of HC-SR04 from the RB0 pin. 

PORTB.F0 = 1;
Delay_us(10);
PORTB.F0 = 0;

As soon as PIC16F877A, send 10 microseconds pulse to the trigger pin of the Ultrasonic sensor. The sensor echo output becomes active high and this while condition becomes false. 

while(!PORTB.F4);   /* wait till echo output signal goes high */

After that we enable the Timer1 counting by setting TMR1ON bit to 1. Because we used the Timer1 in timer mode, it will start to increment its value according to its operating frequency. 

T1CON.F0 = 1;    /* enable the Timer1 */

PIC16F877A stops its execution in this while loop until PB4 pin does not detect active low echo pulse signal. 

while(PORTB.F4);  /* wait till echo output signal goes low */

After that disable the Timer1 counting. 

T1CON.F0 = 0; /*disable timer1 */

Now take the content of timer registers and save them in the “time taken” variable. 

time_taken= (TMR1L | (TMR1H<<8)); /*read the content of Timer1 registers */

Convert time into distance with units of centimeters. 

time_taken= (TMR1L | (TMR1H<<8))/58.82; /* convert duration into distance */

Video Demo

Related Tutorials and Projects:

35 thoughts on “HC-SR04 Ultrasonic Sensor Interfacing with Pic Microcontroller – Distance Measurement”

  1. I can design it for you contact me at proteus But this message appears
    Unable to open HEX file ‘UltraSonicSensor.HEX’. [SONAR1]
    Real Time Simulation failed to start.

    Reply
  2. hi can i ask how about to combine the ultrasonic sensor and servo motor with pic 16F877A microcontroller? i’ve been searching it and i can’t find one. maybe you can help me?

    Reply
  3. Hi,I read your new stuff named “Ultrasonic sensor interfacing with pic microcontroller : distance measurement” like every week.Your humoristic style is awesome, keep up the good work! And you can look our website about free proxy.

    Reply
  4. it is showing 1error..
    ***Error 128 “main.c”Line 2(13,15):A #Device required before this line..
    please give a solution for this problem!!!!

    Reply
    • Hi VIJAY

      You can change this code for PIC18F8722 just by configuring timers and other registers according to its datasheet

      Reply
  5. Thanks for the nice sharing.
    But I think the line “TRISB.B4 =0” should be “TRISB.B4=1” since you have set it as an input pin and not output.

    Reply

Leave a Comment