The Core Syntax: Quick Reference

When scaling analog inputs to usable digital outputs, the mapping function in Arduino is the most frequently utilized utility in a maker's toolkit. Under the hood, it performs linear interpolation to re-map a number from one range to another. Whether you are converting a 10-bit ADC reading to an 8-bit PWM signal or translating voltage drops into temperature metrics, understanding its exact behavior is critical for precision electronics.

Standard Function Signature

long map(long value, long fromLow, long fromHigh, long toLow, long toHigh)
  • value: The raw input number to map (e.g., analogRead pin value).
  • fromLow / fromHigh: The lower and upper bounds of the value's current range.
  • toLow / toHigh: The lower and upper bounds of the target range.
2026 Hardware Note: With the widespread adoption of the Arduino Uno R4 WiFi and Minima, makers must remember that the default ADC resolution is 14-bit (0–16383), not the legacy 10-bit (0–1023) of the Uno R3. Always verify your board's analogReadResolution() before hardcoding your fromHigh parameter.

Frequently Asked Questions (FAQ)

1. Why does the mapping function truncate my decimal values?

The most common bug encountered by intermediate developers is unexpected rounding. The Arduino map() function relies entirely on integer math. According to standard C++ integer arithmetic rules, any fractional remainder generated during the division step is silently discarded (truncated toward zero), not rounded to the nearest whole number.

The Fix: If you are mapping a 3.3V reference ADC to a precise temperature scale where a 0.5-degree loss causes system failure, do not use the native function. Use the custom floating-point alternative provided at the end of this guide.

2. What happens if the input value exceeds the 'from' range?

A massive edge case that ruins motor controllers and LED drivers is assuming map() acts as a clamp. It does not. The mapping function in Arduino will happily extrapolate beyond your defined boundaries.

For example, if you map a potentiometer reading (0 to 1023) to a PWM output (0 to 255), and a noisy signal spikes to 1100, the function will output 274. Since standard 8-bit PWM caps at 255, passing 274 to analogWrite() can cause undefined behavior or wrap-around glitches depending on the specific AVR or ARM timer registry.

The Fix: Always pair your mapping function with constrain():

int raw = analogRead(A0);
int scaled = map(raw, 0, 1023, 0, 255);
int safePWM = constrain(scaled, 0, 255);

3. Can I map a range in reverse?

Yes. If you have a sensor where a higher voltage indicates a lower physical measurement (like certain NTC thermistor voltage divider setups or ultrasonic proximity sensors), simply swap the toLow and toHigh parameters. Passing map(val, 0, 1023, 255, 0) perfectly inverts the scale without requiring manual subtraction.

Real-World Sensor Mapping Matrix

Below is a quick-reference table for common sensor configurations encountered in modern prototyping and industrial IoT setups.

Sensor / Input Hardware Context Input Range Target Range Use Case
10k Potentiometer Uno R3 (10-bit ADC) 0 to 1023 0 to 255 LED Dimming / Motor Speed
LDR (Voltage Divider) Uno R4 WiFi (14-bit ADC) 0 to 16383 0 to 100 Ambient Light Percentage
MQ-135 Air Quality ESP32 (12-bit ADC) 0 to 4095 0 to 5000 Estimated PPM Output
Industrial 4-20mA 250-ohm Shunt (1-5V) 205 to 1023 0 to 100 PLC-style Percentage Scaling

Deep Dive: Industrial 4-20mA Loop Scaling

In professional automation, 4-20mA current loops are the standard for noise-immune sensor transmission. To read this with a standard Arduino Uno, you pass the current through a precision 250-ohm shunt resistor (typically a 1% tolerance metal film resistor costing around $0.15). This generates a 1V to 5V drop.

However, 4mA represents 0% of your sensor's capacity, and 20mA represents 100%. A 1V drop on a 5V Arduino ADC translates to a raw value of roughly 205. A 5V drop translates to 1023.

Therefore, the correct mapping function call for an industrial pressure transmitter is:

// 4mA (205) = 0%, 20mA (1023) = 100%
int pressurePercent = map(analogRead(A0), 205, 1023, 0, 100);

If your raw reading drops below 205, it indicates a broken wire or a sensor fault (since a healthy loop should never drop below 4mA). This is a built-in hardware diagnostic feature of the 4-20mA standard that the mapping function helps you expose logically.

The Floating-Point Alternative (Precision Mapping)

Because the native Arduino map() reference relies on 32-bit signed integers (long), it is useless for high-precision scientific instruments where fractional scaling is required. If you are mapping a 3.3V ADC reading to a precise barometric pressure range, integer truncation will introduce unacceptable stair-stepping errors.

Use this custom floating-point function for DSP (Digital Signal Processing) and high-resolution ADCs like the ADS1115:

float mapFloat(float x, float in_min, float in_max, float out_min, float out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

Performance Warning: Floating-point math is computationally expensive on 8-bit AVR microcontrollers (like the ATmega328P). Executing mapFloat() takes roughly 40-50 microseconds, compared to the native integer map() which executes in under 5 microseconds. If you are running high-frequency PID control loops on an older Uno R3, stick to integer math and scale your target ranges up by a factor of 100 to simulate decimal precision without the CPU penalty.

Summary Checklist for Makers

  • Verify ADC Resolution: Are you on an R3 (10-bit), ESP32 (12-bit), or R4 (14-bit)?
  • Clamp Your Outputs: Always use constrain() to prevent extrapolation overflow.
  • Check Your Math Type: Use mapFloat() for scientific precision, but respect CPU cycle limits on legacy AVR boards.
  • Validate Hardware Bounds: Ensure your fromLow and fromHigh reflect actual physical voltage limits, accounting for voltage divider tolerances and reference voltage drift.