Bird
Raised Fist0
Arduinoprogramming~15 mins

micros() for microsecond precision in Arduino - Deep Dive

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Overview - micros() for microsecond precision
What is it?
The micros() function in Arduino returns the number of microseconds since the program started running. It gives a very precise time measurement, counting in millionths of a second. This helps you measure very short time intervals or create precise delays. It is useful when milliseconds are not accurate enough.
Why it matters
Without micros(), you would only have the millis() function, which counts in milliseconds and is not precise enough for many tasks like measuring sensor pulses or timing fast events. Micros() lets you track time with much finer detail, enabling better control and accuracy in your projects. Without it, many real-time applications would be clumsy or impossible.
Where it fits
Before learning micros(), you should understand basic Arduino programming, including the setup() and loop() functions and how to use millis() for timing. After mastering micros(), you can explore interrupts, pulseIn(), and advanced timing techniques for precise control in embedded systems.
Mental Model
Core Idea
Micros() counts tiny slices of time—microseconds—since your Arduino started, letting you measure very short durations precisely.
Think of it like...
Imagine a stopwatch that not only counts seconds but also ticks every tiny grain of sand falling through an hourglass, letting you see moments much smaller than a second.
┌───────────────────────────────┐
│ Arduino Program Starts         │
├───────────────────────────────┤
│ micros() counts microseconds   │
│ ↑                             │
│ 0 → 1 → 2 → 3 → ... (μs)      │
└───────────────────────────────┘
Build-Up - 6 Steps
1
FoundationUnderstanding Time Units in Arduino
🤔
Concept: Learn the difference between milliseconds and microseconds as units of time measurement.
Arduino has two main time functions: millis() returns time in milliseconds (thousandths of a second), and micros() returns time in microseconds (millionths of a second). Knowing these units helps you choose the right function for your timing needs.
Result
You understand that 1 millisecond = 1000 microseconds, so micros() is 1000 times more precise than millis().
Understanding time units is key to choosing the right precision for your project and avoiding timing errors.
2
FoundationBasic Usage of micros() Function
🤔
Concept: Learn how to call micros() and read its value to measure elapsed time.
Use micros() anywhere in your code to get the current time in microseconds since the Arduino started. For example: unsigned long start = micros(); // do something unsigned long duration = micros() - start; This measures how long 'something' took in microseconds.
Result
You can measure short time intervals precisely by subtracting two micros() readings.
Knowing how to read micros() values lets you measure and control very fast events.
3
IntermediateHandling micros() Overflow Correctly
🤔Before reading on: do you think micros() can count forever without resetting? Commit to yes or no.
Concept: Learn that micros() resets after about 70 minutes and how to handle this overflow safely.
Micros() returns an unsigned long that overflows (resets to zero) after approximately 70 minutes (about 4,294,967,295 microseconds). When subtracting two micros() values, the unsigned math handles overflow correctly if you write: unsigned long elapsed = micros() - previousTime; This works even if micros() has reset.
Result
Your timing code remains accurate even when micros() resets after overflow.
Understanding overflow behavior prevents bugs in long-running Arduino programs.
4
IntermediateLimitations of micros() Precision
🤔Before reading on: do you think micros() always counts every single microsecond perfectly? Commit to yes or no.
Concept: Learn that micros() increments in steps of 4 microseconds on 16 MHz Arduino boards, not every single microsecond.
On standard Arduino boards running at 16 MHz, micros() increases in steps of 4 microseconds, not 1. This means the smallest measurable time difference is 4 microseconds. Faster boards or different clocks may have different resolutions.
Result
You know the real precision limit of micros() on your board and avoid expecting impossible accuracy.
Knowing the hardware limits helps you set realistic expectations for timing precision.
5
AdvancedUsing micros() for Pulse Width Measurement
🤔Before reading on: do you think micros() can measure very short pulses accurately? Commit to yes or no.
Concept: Learn how to use micros() to measure the length of electrical pulses precisely.
You can measure pulse widths by recording micros() at the start and end of a pulse. For example, in an interrupt or polling loop: unsigned long start = micros(); // wait for pulse to end unsigned long end = micros(); unsigned long pulseWidth = end - start; This is useful for sensors like ultrasonic distance sensors or IR receivers.
Result
You can measure pulse durations with microsecond precision, enabling accurate sensor readings.
Using micros() for pulse timing unlocks many real-world sensor applications.
6
ExpertMicros() Internal Timer and Interrupt Interaction
🤔Before reading on: do you think micros() is always perfectly accurate even during interrupts? Commit to yes or no.
Concept: Understand how micros() relies on hardware timers and how interrupts can affect its accuracy.
Micros() uses a hardware timer and an interrupt to count microseconds. If interrupts are disabled for too long, micros() may not update correctly, causing timing errors. Also, micros() increments in steps due to timer prescaling. Advanced users can configure timers directly for custom precision or use hardware counters.
Result
You understand the limits of micros() accuracy and how to improve timing by managing interrupts or timers.
Knowing micros() internals helps avoid subtle bugs in time-critical applications.
Under the Hood
Micros() works by counting timer ticks from a hardware timer configured to increment every 4 microseconds on a 16 MHz Arduino. An interrupt service routine updates a counter variable each time the timer overflows, combining this with the current timer count to calculate total microseconds elapsed. The function returns this combined value as an unsigned long.
Why designed this way?
The design balances precision and CPU load by using hardware timers and interrupts instead of busy-wait loops. Counting every microsecond directly would require a faster timer or more CPU cycles, which was not feasible on early Arduino hardware. Using a prescaled timer and interrupts provides good precision with minimal overhead.
┌───────────────┐      ┌───────────────┐      ┌───────────────┐
│ Hardware      │      │ Timer Overflow│      │ micros()      │
│ Timer (counts │─────▶│ Interrupt     │─────▶│ Returns       │
│ every 4 μs)   │      │ Service       │      │ combined time │
└───────────────┘      │ Routine       │      └───────────────┘
                       └───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does micros() count every single microsecond on a 16 MHz Arduino? Commit to yes or no.
Common Belief:Micros() counts every single microsecond perfectly.
Tap to reveal reality
Reality:Micros() increments in 4-microsecond steps on 16 MHz Arduino boards due to timer prescaling.
Why it matters:Expecting 1-microsecond precision can lead to incorrect timing assumptions and bugs in fast timing applications.
Quick: Can micros() overflow cause your timing calculations to break if you don't handle it? Commit to yes or no.
Common Belief:Micros() overflow will break timing calculations unless you reset the Arduino.
Tap to reveal reality
Reality:Unsigned arithmetic in Arduino handles micros() overflow correctly if you subtract times properly.
Why it matters:Not understanding overflow handling can cause bugs in long-running programs that measure elapsed time.
Quick: Does disabling interrupts affect micros() timing accuracy? Commit to yes or no.
Common Belief:Micros() timing is always accurate regardless of interrupts.
Tap to reveal reality
Reality:Disabling interrupts for long periods can delay timer updates, causing micros() to report incorrect times temporarily.
Why it matters:Ignoring this can cause subtle timing errors in critical real-time applications.
Quick: Is micros() suitable for measuring time intervals longer than 70 minutes? Commit to yes or no.
Common Belief:Micros() can measure any length of time without issues.
Tap to reveal reality
Reality:Micros() overflows approximately every 70 minutes, so long intervals require careful handling.
Why it matters:Failing to handle overflow can cause incorrect timing results in long-running systems.
Expert Zone
1
Micros() precision depends on the CPU clock and timer prescaler; changing clock speed or timer settings affects timing resolution.
2
Using micros() inside interrupt service routines requires care to avoid race conditions or inconsistent readings.
3
Advanced users can reconfigure hardware timers to improve micros() precision or implement custom timing functions.
When NOT to use
Avoid using micros() for extremely high-frequency timing or when nanosecond precision is required; use dedicated hardware timers or external timing ICs instead. For very long durations, consider millis() combined with overflow handling or real-time clocks.
Production Patterns
In real-world Arduino projects, micros() is used for pulse width measurement, sensor timing, debouncing buttons with microsecond accuracy, and profiling code execution time. It is often combined with interrupts and hardware timers for precise control.
Connections
Hardware Timers
Micros() is built on hardware timers that count clock cycles.
Understanding hardware timers clarifies how micros() achieves microsecond precision and its limitations.
Interrupts
Micros() relies on timer overflow interrupts to update its count.
Knowing how interrupts work helps you understand micros() accuracy and how disabling interrupts affects timing.
Stopwatch Timing in Sports
Both measure elapsed time precisely to track performance.
Just like a stopwatch measures time in seconds and fractions, micros() measures tiny time slices to track events in electronics.
Common Pitfalls
#1Expecting micros() to count every microsecond on a 16 MHz Arduino.
Wrong approach:unsigned long start = micros(); // do something unsigned long duration = micros() - start; // expecting duration to be exact microseconds
Correct approach:unsigned long start = micros(); // do something unsigned long duration = micros() - start; // remember duration increments in 4 μs steps on 16 MHz boards
Root cause:Misunderstanding that micros() increments in 4-microsecond steps due to timer prescaling.
#2Not handling micros() overflow in long-running timing code.
Wrong approach:if (micros() < previousTime) { // wrong: no overflow handling elapsed = 0; } else { elapsed = micros() - previousTime; }
Correct approach:elapsed = micros() - previousTime; // unsigned math handles overflow correctly
Root cause:Not knowing that unsigned subtraction automatically handles overflow.
#3Disabling interrupts for long periods causing micros() to freeze.
Wrong approach:noInterrupts(); // long blocking code interrupts(); unsigned long time = micros();
Correct approach:Keep interrupt disable periods very short or avoid disabling interrupts when precise timing is needed.
Root cause:Not realizing micros() depends on interrupts to update its count.
Key Takeaways
Micros() returns the number of microseconds since the Arduino started, providing much finer timing precision than millis().
On standard 16 MHz Arduino boards, micros() increments in 4-microsecond steps, not every single microsecond.
Unsigned arithmetic in Arduino handles micros() overflow automatically, so subtracting two micros() values works even after overflow.
Micros() relies on hardware timers and interrupts, so disabling interrupts for long periods can cause timing inaccuracies.
Using micros() correctly enables precise measurement of short time intervals, pulse widths, and performance profiling in Arduino projects.

Practice

(1/5)
1. What does the Arduino function micros() return?
easy
A. The number of microseconds since the program started
B. The number of milliseconds since the program started
C. The current time in seconds
D. The number of seconds since the last reset

Solution

  1. Step 1: Understand the purpose of micros()

    The micros() function returns the time in microseconds since the Arduino program began running.
  2. Step 2: Compare options with the function's behavior

    Only The number of microseconds since the program started correctly states it returns microseconds since start. Others mention milliseconds or seconds, which are incorrect.
  3. Final Answer:

    The number of microseconds since the program started -> Option A
  4. Quick Check:

    micros() = microseconds since start [OK]
Hint: Remember micros() counts microseconds from program start [OK]
Common Mistakes:
  • Confusing micros() with millis()
  • Thinking it returns seconds
  • Assuming it resets every second
2. Which of the following is the correct way to store the current microsecond count in a variable?
easy
A. unsigned long time = micros();
B. int time = micros();
C. float time = micros();
D. long time = micros();

Solution

  1. Step 1: Identify the data type returned by micros()

    The micros() function returns an unsigned long integer representing microseconds.
  2. Step 2: Match the correct variable type to store the value

    Only unsigned long can hold the large values from micros() without overflow or sign issues.
  3. Final Answer:

    unsigned long time = micros(); -> Option A
  4. Quick Check:

    Use unsigned long for micros() values [OK]
Hint: Use unsigned long to store micros() values safely [OK]
Common Mistakes:
  • Using int which is too small
  • Using float which loses precision
  • Using signed long which can cause negative values
3. What will be the output of this Arduino code snippet?
unsigned long start = micros();
// some delay here
unsigned long end = micros();
unsigned long diff = end - start;
Serial.println(diff);
Assuming the delay is about 500 microseconds.
medium
A. Always zero
B. A number close to 500000
C. A negative number
D. A number close to 500

Solution

  1. Step 1: Understand the timing measurement

    The code measures the time difference in microseconds between two calls to micros().
  2. Step 2: Interpret the delay and difference calculation

    If the delay is about 500 microseconds, the difference diff will be close to 500, printed as a positive number.
  3. Final Answer:

    A number close to 500 -> Option D
  4. Quick Check:

    diff = end - start ≈ 500 [OK]
Hint: Subtract micros() values to get elapsed microseconds [OK]
Common Mistakes:
  • Expecting milliseconds instead of microseconds
  • Thinking difference can be negative
  • Confusing delay units
4. Identify the error in this Arduino code snippet:
unsigned long start = micros();
// some code
unsigned long end = micros();
int elapsed = end - start;
Serial.println(elapsed);
medium
A. micros() cannot be assigned to unsigned long
B. Using int for elapsed can cause overflow
C. Serial.println cannot print integers
D. Subtracting micros() values is invalid

Solution

  1. Step 1: Check variable types for time difference

    The difference between two micros() values can be very large, exceeding the range of int.
  2. Step 2: Understand overflow risk

    Using int (usually 16-bit) can cause overflow and incorrect negative values. It should be unsigned long.
  3. Final Answer:

    Using int for elapsed can cause overflow -> Option B
  4. Quick Check:

    Use unsigned long for elapsed time to avoid overflow [OK]
Hint: Use unsigned long, not int, for time differences [OK]
Common Mistakes:
  • Using int instead of unsigned long
  • Thinking micros() returns signed values
  • Assuming Serial.println can't print integers
5. You want to measure how long a button is pressed in microseconds using micros(). Which approach correctly handles the timing even if the program runs longer than 70 minutes (when micros() overflows)?
hard
A. Ignore overflow because it never affects timing
B. Reset the Arduino every 60 minutes to avoid overflow
C. Store start time, then calculate elapsed as micros() - start using unsigned long subtraction
D. Use millis() instead because it never overflows

Solution

  1. Step 1: Understand micros() overflow behavior

    micros() overflows roughly every 70 minutes, wrapping back to zero.
  2. Step 2: Use unsigned long subtraction to handle overflow

    Unsigned subtraction correctly calculates elapsed time even if overflow happens, so micros() - start works safely.
  3. Final Answer:

    Store start time, then calculate elapsed as micros() - start using unsigned long subtraction -> Option C
  4. Quick Check:

    Unsigned subtraction handles micros() overflow correctly [OK]
Hint: Use unsigned subtraction to handle micros() overflow safely [OK]
Common Mistakes:
  • Thinking micros() never overflows
  • Using millis() which has lower precision
  • Resetting Arduino unnecessarily