Advanced I/O

Lesson 98: Using pulseIn()

Learn how pulseIn() measures pulse width in microseconds, including timeout behavior and practical signal timing use cases.

Progress indicator

Lesson 98 of 98

Learning Objectives

  • Understand what pulseIn() measures and why pulse width matters in embedded systems.
  • Learn pulseIn() syntax with pin, state, and optional timeout.
  • Understand timeout behavior and how it prevents blocking too long.
  • Read pulseIn() return values correctly for real timing calculations.
  • Build beginner-friendly measurement flow for echo-style digital signals.
  • Avoid common pulse timing mistakes with unit conversion and invalid readings.

Concept Explanation

What is pulseIn()

pulseIn() measures how long a digital pin stays in a specific state (HIGH or LOW).

The result is returned in microseconds, so it is useful for timing-based sensors and signal width analysis.

pulseIn() Syntax

  • pulseIn(pin, value);
  • pulseIn(pin, value, timeout);

pin is the input pin, value is HIGH or LOW, and timeout is the maximum wait in microseconds.

If you omit timeout, the function may block for too long in real hardware noise/no-signal conditions, so beginner projects should prefer explicit timeout.

How pulseIn() Works

  1. Waits for the signal to enter the target state.
  2. Starts a timer when the target state begins.
  3. Stops the timer when the state ends.
  4. Returns measured pulse width in microseconds.

Measuring Pulse Width

Pulse width means how long the signal is HIGH (or LOW). Example: if HIGH lasts 1500 us, pulseIn() returns about 1500.

This is common in ultrasonic distance sensing where echo pulse duration maps to distance.

Practical thinking: smaller pulse width usually means shorter travel time and shorter measured distance; larger pulse width means longer travel time and larger distance.

Timeout Parameter Explained

Timeout protects your loop from waiting forever if no valid pulse arrives.

Example: pulseIn(ECHO_PIN, HIGH, 30000) waits up to 30,000 us (30 ms). If no pulse is completed, result is 0.

Choose timeout based on expected signal range. Too short can miss valid pulses; too long can reduce responsiveness.

pulseIn() Return Value

Returns pulse width in microseconds as an unsigned long value.

  • Non-zero value: pulse measured successfully.
  • Zero value: timeout/no valid pulse detected.

Many beginners forget to check zero before math. Always validate pulse width first, then calculate final value.

When to Use pulseIn()

  • Ultrasonic echo timing (distance measurement).
  • Measuring pulse-based sensor output width.
  • Basic digital signal timing analysis in beginner projects.
  • Quick prototyping where simple blocking timing is acceptable.

Example Code

This example sends a short trigger pulse and measures echo pulse width using pulseIn().

const int TRIG_PIN = 5;
const int ECHO_PIN = 18;

void setup() {
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  Serial.begin(115200);
}

void loop() {
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);

  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  unsigned long pulseWidth = pulseIn(ECHO_PIN, HIGH, 30000);
  float distanceCm = pulseWidth * 0.0343 / 2.0;

  Serial.print("Pulse width (us): ");
  Serial.println(pulseWidth);
  Serial.print("Distance (cm): ");
  Serial.println(distanceCm);

  delay(250);
}

Example Code Explanation

  1. TRIG_PIN sends a short trigger pulse to start the ultrasonic measurement cycle.
  2. ECHO_PIN receives the return pulse whose width encodes distance timing.
  3. In setup(), pin modes are configured and Serial logging is started.
  4. In loop(), trigger pin sends a short HIGH pulse (10 us).
  5. pulseIn(ECHO_PIN, HIGH, 30000) measures HIGH pulse width with 30 ms timeout.
  6. pulseWidth is stored as unsigned long to keep microsecond timing safe and non-negative.
  7. Measured pulse width is converted into distance using speed-of-sound factor (0.0343 cm/us).
  8. Division by 2.0 is required because sound travels to object and back.
  9. Serial monitor prints pulse width and distance so students can verify results.
  10. A small delay keeps the output readable and avoids overly fast repeated measurements.

What Happens Inside

  1. Board sends trigger pulse to start sensor measurement cycle.
  2. Program waits for echo pin to go HIGH.
  3. Internal timer starts while echo pin is HIGH.
  4. Timer stops when echo pin returns LOW.
  5. Measured microseconds are returned by pulseIn().
  6. Code checks and converts timing value into distance estimate.
  7. Serial output logs raw and converted values for debugging.
  8. Loop repeats to provide continuous live measurements.
Real-life analogy: Start a stopwatch when a signal appears, stop it when signal ends, then use elapsed time as measurement data.

Common Mistakes with pulseIn()

  • Not setting a timeout, causing long blocking waits when no pulse arrives.
  • Reading wrong pin mode (forgetting INPUT on echo pin).
  • Mixing milliseconds and microseconds in calculations.
  • Ignoring 0 return values and treating them as valid pulse measurements.
  • Using an unstable trigger sequence, which causes inconsistent echo timing.
  • Applying distance formulas without considering two-way signal travel.

Best Practices for pulseIn()

  • Always use timeout for stable and responsive loop behavior.
  • Use unsigned long for pulse width storage.
  • Handle zero returns explicitly as timeout/no-signal cases.
  • Log pulse values in Serial while calibrating thresholds and formulas.
  • Keep trigger pulse generation clean and consistent for reliable measurements.
  • Test with known fixed distances first before trusting dynamic real-time data.

Try it now

Open the simulator workspace and test pulse width changes to observe timing and distance-style output behavior.

Run in Simulator