Функция map(): перекладываем диапазоны

Датчик говорит «687». Светодиод понимает «0–255». Сервопривод — «0–180 градусов». Нужен переводчик — и это функция map().

map() — это пропорция в одну строку. Она растягивает или сжимает один числовой диапазон в другой, сохраняя соотношения. Без неё датчики и моторы говорят на разных языках.

Сейчас свяжем потенциометр (0–1023) с яркостью светодиода (0–255): крутишь ручку — меняется свет. Для этого числа надо «перевести». Это самая используемая функция в реальных проектах.

Что делает map

  Вход:   0 ......................... 1023   (потенциометр)
          |                            |
          v                            v
  Выход:  0 ......................... 255    (яркость ШИМ)

  Пример: 512 (середина входа) -> ~127 (середина выхода)

Синтаксис: map(значение, изНач, изКонец, вНач, вКонец). Она вычисляет, где значение стоит во входном диапазоне, и ставит его в ту же относительную точку выходного.

Код

const int LED = 9;

void setup() {
  pinMode(LED, OUTPUT);
}

void loop() {
  int raw = analogRead(A0);              // 0..1023
  int bright = map(raw, 0, 1023, 0, 255); // -> 0..255
  analogWrite(LED, bright);
}

Как работает под капотом

map — это просто пропорция: выход = вНач + (значение − изНач) * (вКонец − вНач) / (изКонец − изНач). Важно: map работает с целыми числами и отбрасывает дробную часть (не округляет!). Поэтому иногда результат на единицу «не дотягивает». Ещё map не обрезает выход: если вход вышел за границы, выход тоже вылетит за пределы. Для защиты используют constrain.

# Та же логика на Python: своя реализация map() и constrain()
def arduino_map(x, in_min, in_max, out_min, out_max):
    # как в Arduino: целочисленно, без округления
    return (x - in_min) * (out_max - out_min) // (in_max - in_min) + out_min

def constrain(x, low, high):
    return max(low, min(high, x))

for raw in (0, 256, 512, 768, 1023, 1100):
    b = arduino_map(raw, 0, 1023, 0, 255)
    b = constrain(b, 0, 255)   # защита от выхода за диапазон
    print("потенциометр", raw, "-> яркость", b)

Запусти и заметь: вход 1100 (выше максимума) без constrain дал бы >255, но мы его подрезали. Это спасает analogWrite от мусора.

Частые ошибки

  • Путают порядок аргументов. Сначала весь входной диапазон, потом весь выходной. Перепутаешь — абсурдные значения.
  • Забыли constrain. Шумный датчик выдаёт 1025 — map выдаёт 256 — analogWrite «срывается».
  • Ждут округления. map режет дробную часть. Для точности считай во float вручную.

Best practices

  • Почти всегда оборачивай map в constrain: constrain(map(...), мин, макс).
  • map можно «переворачивать»: map(raw, 0, 1023, 255, 0) — больше крутишь, тусклее свет.
  • Один и тот же приём свяжет датчик с градусами сервопривода (0–180) — запомни шаблон.

Калибровка: подгоняем под реальность

В жизни датчик почти никогда не выдаёт идеальные 0 и 1023. Реальный потенциометр может давать, скажем, от 8 до 1015; фоторезистор в твоей комнате — от 200 (темно) до 750 (ярко). Если в map() подставить теоретические 0–1023, ты не используешь весь диапазон выхода. Поэтому опытные мейкеры калибруют: смотрят в монитор порта реальные минимум и максимум своего датчика и подставляют именно их: map(raw, 200, 750, 0, 255). Тогда вся шкала работает «от края до края».

Это подводит к важному принципу: числа из теории — отправная точка, а не истина. Реальная электроника всегда чуть отличается от учебника из-за разброса деталей, температуры и наводок. Хороший проект почти всегда включает короткий этап калибровки — пару минут с монитором порта, чтобы узнать настоящие границы. После связки analogRead + калибровка + constrain(map(...)) твои датчики будут вести себя предсказуемо, а не «как повезёт».

Итоги

map() — это пропорция, переводящая один диапазон в другой; работает целочисленно и не обрезает выход, поэтому дружит с constrain. Теперь потенциометр управляет яркостью. Со связкой analogRead + map + analogWrite ты готов к настоящим датчикам.

Проверьте себя
1. Что произойдёт, если значение на входе map() выйдет за указанный входной диапазон?
Amap обрежет его автоматически
BВыход тоже выйдет за пределы выходного диапазона
CПрограмма упадёт
DВернётся 0
2. Как map() обращается с дробной частью результата?
AОкругляет к ближайшему
BОтбрасывает (работает целочисленно)
CВозвращает float
DУдваивает