Become a subscriber (Free)

Join 29,000 other subscribers to receive subscriber sale discounts and other free resources.
Don't worry -- youre-mail address is totally secure. I promise to use it only to send you MicroZine.

Arduino Pulsein

The Arduino pulseIn function measures the time period of a pulse in microseconds and can measure pulses from 2-3us to 3minutes. It provides a simple way to measure signal periods on any digital pin. There are in fact two versions of the function explored below (pulseIn and pulseInLong). The functions themselves do not use any fancy internal hardware - they are wiritten in pure c code - although one of them is converted to assmbler code (see below) and both give good results.

Note: The state input to the function allow you to detect high or low pulses i.e. measure the high or the low period of the signal.

How does Arduino Pulsein work

There are two versions of pulsein:

  • pulseIn
  • pulseInLong

They both have the same specification and return the same result i.e. measure pulse from 2-3us to 3 minutes but the first one uses an assembler routine to make the measurement while the second one uses the Timer0 interrupt to calculate the result.

The first one: pulseIn, can be used if interrupts are turned off (and if they are off will return a more accurate result - since it won't be interrupted while measuring), whereas the second one can not be used unless interrupts are turned on!

TIP: Turn off interrupts for a more accurate pulseIn result.

Arduino Pulsein Source Code

You can find the source code for pulsein in the following directory:

C:/Program Files/Arduino/hardware/arduino/avr/cores/arduino/wiring_pulse.c

For the assembler version you can find the code in

C:/Program Files/Arduino/hardware/arduino/avr/cores/arduino/wiring_pulse.S

The following is the source code that is used to generate the auto assembler output:

 * The following routine was generated by avr-gcc 4.8.3 with the following parameters
 * -gstabs -Wa,-ahlmsd=output.lst -dp -fverbose-asm -O2
 * on the original C function
 * unsigned long pulseInSimpl(volatile uint8_t *port, uint8_t bit, uint8_t stateMask, unsigned long maxloops)
 * {
 *     unsigned long width = 0;
 *     // wait for any previous pulse to end
 *     while ((*port & bit) == stateMask)
 *         if (--maxloops == 0)
 *             return 0;
 *     // wait for the pulse to start
 *     while ((*port & bit) != stateMask)
 *         if (--maxloops == 0)
 *             return 0;
 *     // wait for the pulse to stop
 *     while ((*port & bit) == stateMask) {
 *         if (++width == maxloops)
 *             return 0;
 *     }
 *     return width;
 * }
 * some compiler outputs were removed but the rest of the code is untouched

After the above comment the assembler output is presented - this is auto generated assembler from the original C code shown above, so to understand what the assembler output is doing just read the above c code.

Note: Generating assembler in this way means it is not compiled by the C compiler and therefore means that it can not change when the C compiler is "improved". So timings will be consistent even if the C code or compiler changes things. The problem is that it will only operate on the avr so that is why the directory structure shows avr - other processors will require different equivalent code.

The code measures the pulse by incrementing the variable 'width' within the last while loop. The previous two while loops are there to prepare for measurement.

It is quite simple. if you think of a continuous square wave pulse, and lets say you want to find the period of the high part of the signal then you would set the variable 'state' (in the C code below) high which would set the corresponding bit in statemask high. The algorithm first finds an instance of the signal being high (if present_ but since you don't know if you found it in the middle of a high period you can not tell if you are at the start of the pulse. The algorithm then waits for low so that the next high is the start of a pulse. So that is what the 1st two while loops are doing preparing to find the start of the signal, and the third one performs the measurement while waiting for the pulse to go low again.

  1. While valid wait for invalid pulse polarity - Wait for end of previous pulse.
  2. While invalid wait for valid pulse polarity - Wait for pulse to start.
  3. While valid polarity increment width pulse - Wait for pulse to stop.

The operation is shown in the diagram below

arduino pulse operation showing signal input and algorithm operation

The function generated by the code is:


From the C part of the code we have the following prototype (or function definition):

unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)

in which you specify the pin to use as input and the pulse state required i.e. whether you are looking for an active high or active low pulse, and a timeout parameter in microseconds - the maximum time to wait for a pulse to arrive (in microseconds).

The full code is shown below and is used as a wrapper routine around the assembler code and defines bit, port and statemask as fixed quantities (so that the assembler code does not have to use digitalWrite etc. and is therefore fast).

A fudge factor of divide-by-16 takes care of converting from the timeout in microseconds to cycles through one loop through the assembler code - the comments say that the assembler routine takes approximately 16 cycles per iteration.

Warning: The timeout fudge factor (/16) will only allow this assembler version of pulseIn to operate correctly at 16MHz. This is probably why there is an interrupt driven version (pulseInLong) - since that version uses Timer0 it wont use a fudge factor.

At the end of the code, output from the assembler is cleaned up:in the event of a timeout in the assembler code t return zero width.

unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)
// cache the port and bit of the pin in order to speed up the
// pulse width measuring loop and achieve finer resolution.  calling
// digitalRead() instead yields much coarser resolution.
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
uint8_t stateMask = (state ? bit : 0);

// convert the timeout from microseconds to a number of times through
// the initial loop; it takes approximately 16 clock cycles per iteration
unsigned long maxloops = microsecondsToClockCycles(timeout)/16;

unsigned long width = countPulseASM(portInputRegister(port), bit, stateMask, maxloops);

// prevent clockCyclesToMicroseconds to return bogus values if countPulseASM timed out
if (width)
return clockCyclesToMicroseconds(width * 16 + 16);
return 0;

Arduino unsigned long Pulsein

The function pulseInLong follows the same algorithm as the assembler code above working in a similar way to find the start of a valid pulse (see above description). The only difference is that the timing mechanism uses the value from micros() function. This function returns a value calculated from Timer0 and so pulseInLong requires that interrupts are active.

The timing mechanism uses the standard technique of storing time in an unsigned long and subtracting this from micros to get the elapsed time.

Note: PulseInLong will return a longer time period than pulseIn as it will be interrupted by running interrupts.
unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout)
// cache the port and bit of the pin in order to speed up the
// pulse width measuring loop and achieve finer resolution.  calling
// digitalRead() instead yields much coarser resolution.
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
uint8_t stateMask = (state ? bit : 0);

unsigned long startMicros = micros();

// wait for any previous pulse to end
while ((*portInputRegister(port) & bit) == stateMask) {
if (micros() - startMicros > timeout)
return 0;

// wait for the pulse to start
while ((*portInputRegister(port) & bit) != stateMask) {
if (micros() - startMicros > timeout)
return 0;

unsigned long start = micros();
// wait for the pulse to stop
while ((*portInputRegister(port) & bit) == stateMask) {
if (micros() - startMicros > timeout)
return 0;
return micros() - start;

Arduino Accuracy of Pulsein

You can increase the accuracy of pulseIn if you turn off interrupts during a measurement otherwise the code will get interrupted by any running interrupt e.g. Timer0, changing the pulse measurement time and consequent output. Note this is only for pulseIn and not for pulseInLong (pulseInLong relies on interrupts).

The accuracy of the algorithm is hard to say as there is a fudge factor of divide-by-16. The best you can say is that the code will return consistent results for the same signal. Also remember that if your arduino uses a resonator it won't be that accurate anyway.

Arduino Pulsein Sketch

The following sketch shows how to use pulseIn and allows you to compare the operation for different function versions.

The following sketch measures the high and low periods of a signal attached to pin 4 - you could use any digital pin. The code takes the average of 10 readings for the high period, and 10 readings for the low period, and averages each set of results. It then adds them and uses floating point to calculate the frequency of the input signal. Note the signal source is not a very stable one - just an RC oscillator at 1kHz output frequency.

// PulseIn Test program.
// Measures 10 high and 10 low periods 
// and calculates each average. Then calculates
// The freuqnecy of the signal.
// Two test controls are used:
// noPulseInterrupts when high stops interrupts during
//    the measurements.
// usePulseInLong when high switches to pulseInLong function.
// Input is a 0~5V 1kHz squarewave to INPUTPIN.
// Copyright John Main - Free for non commercial use.
void setup() {

#define INPUTPIN 4

// Controls
#define noPulseInterrupts 1
#define usePulseInLong    0

// Must have interrupts for pulseInLong
#if usePulseInLong 
  #define noPulseInterrupts 0

void loop() {
uint8_t i;
uint16_t avg1,avg2;
static unsigned int stop=0;
unsigned long val;
float f;

  if (stop==0) {
    for(i=0;i<10;i++) {
        if(noPulseInterrupts) noInterrupts();
           if (usePulseInLong) 
              val = pulseInLong(INPUTPIN,HIGH,10000);
              val = pulseIn(INPUTPIN,HIGH,10000);
        Serial.print(i);Serial.print(" ");
    Serial.print("Avg high:");Serial.print(avg1);
    for(i=0;i<10;i++) {

        if(noPulseInterrupts) noInterrupts();
           if (usePulseInLong) 
              val = pulseInLong(INPUTPIN,LOW,10000);
              val = pulseIn(INPUTPIN,LOW,10000);
        Serial.print(i);Serial.print(" ");
    Serial.print("Avg  low:");Serial.println(avg2);
    f =1.0 / ( (avg1 + avg2)*1e-6 );
    Serial.print("Frequency: "); 


  if ('1') stop = 0;

Pulsin Sketch output

The following results are for pulseIn with interrupts on.

0 497
1 497
2 497
3 497
4 491
5 492
6 491
7 491
8 491
9 491
Avg high:493
0 449
1 447
2 446
3 469
4 493
5 493
6 493
7 493
8 487
9 487
Avg  low:475

Frequency: 1033.06

The above results are for using pulseIn when interrupts are running i.e. inaccurate results occur.

The following set of results shows the difference between interrupts on and off (for pulseIn) and results for pulseInLong (which requires interrputs active). The input singnal is 1013 to 1014 Hz measured on the PIC frequency counter. It drifted up a little as the source is not high stability.

The most consistent results are for the assmbler version (pulsein) with interrupts off. When interrupts are on pulseIn results vary a lot. Interesingly, pulseInLong results are consistently stable and compare very well to pulseIn (ints off).

If you need to have interrupts active then pulseInLong will give acceptable results.

pulseIn no ints
Frequency: 1012.15
Frequency: 1012.15
Frequency: 1013.17
Frequency: 1013.17
Frequency: 1012.15

pulseIn Ints
Frequency: 1037.34
Frequency: 1040.58
Frequency: 1028.81
Frequency: 1037.34
Frequency: 1030.93

Frequency: 1015.23
Frequency: 1014.20
Frequency: 1018.33
Frequency: 1016.26
Frequency: 1016.26

New! Comments

Have your say about what you just read! Leave me a comment in the box below.

Privacy Policy | Contact | About Me

Site Map | Terms of Use

Visit our Facebook Page:

   Click Here

Recent Articles

  1. How to use the BMP280 Barometric Pressure chip with the Arduino

    How to use the BMP280 for weather pressure measurement or altitude change detection.

    Read more

  2. The TCS230 Color Sensing Chip: How it works and how to use it.

    How to use the TCS230 (/TCS3200) Color detector chip and easily add it to any of your projects.

    Read more

  3. All about the MC78M05BDTRKG Linear Voltage Regulator

    How to use MC78M05BDTRKG for maximum current without using too large a heatsink pad and how to select the optimum input voltage.

    Read more

  4. Fixed point: A fast and easy way to save tons of microcntroller flash memory

    How to use fixed point maths to save microcontroller memory by avoiding use of the floating point library.

    Read more

  5. How to use the DHT22 (or DHT11) with an Arduino; Full code, description and device differences.

    Essential Guide to the DHT22/11 humidity sensor with library code guide and Full Arduino Tutorial. Learn how to Easily determine Humidity, Dew point and Heat Index.

    Read more

  6. The Essential I2C Tutorial: All you need to know about I2C...

    I2C tutorial: Learn all about the 2 wire I2C serial protocol. Learn how easy it is to use, how it works and when to use it...

    Read more

Readers Comments

"I wanted to thank
you so so so much
for all the information
you have provided in
your site it's


- Ranish Pottath

"This site really is
the best and my favorite.
I find here many useful
projects and tips."

- Milan


"Awesome site,
very, very easy and nice
to navigate!"

- Matt

Learn Microcontrollers

"Interested in

Sign up for The
Free 7 day guide:


"I am a newbie to PIC
and I wanted to say
 how great your
site has been for me."

- Dave


"Your site is a great
and perfect work.

- Suresh


"I couldn't find the correct
words to define
yourweb site.

Very useful, uncovered,
honest and clear.

Thanks so much for
your time and works.

- Anon

Back to Top