Курс Arduino с нуля · Датчики

Урок 15. Датчик света: ночник включается сам

Фоторезистор превращает освещённость в число. Собираем автоматический ночник, измеряем свои пороги и знакомимся с гистерезисом.

Урок 15 из 31⏱ примерно 50 минутОбновлено: 26 июля 2026

Что понадобится

  • Плата Uno из набора
  • Кабель USB
  • Макетная плата
  • Фоторезистор
  • Резистор 10 кОм
  • Светодиод (любого цвета)
  • Резистор 470 Ом
  • 5 проводов «папа-папа»

Все детали есть в обучающем наборе, по которому идёт курс.

В прошлом уроке ползунком двигали вы. А если поставить деталь, у которой сопротивление меняется само — от света, тепла или давления? Тогда получится настоящий датчик, и устройство начнёт реагировать на мир без единого прикосновения.

Сегодня соберём автоматический ночник: стало темно — светильник включился, рассвело — погас. И по дороге разберёмся с приёмом, без которого ни один настоящий датчик не работает нормально.

Знакомимся с фоторезистором

В наборе их целых пять — маленькие оранжевые диски с тёмной змейкой на лицевой стороне и двумя ножками. Змейка — это две гребёнки электродов, между которыми лежит светочувствительный слой.

Внешний видоранжевый диск со змейкойи две ножкиОбозначениеRрезистор, на которыйпоказывают две стрелки светаКак он себя ведётсопротивлениесвета больше →чем больше света,тем меньше сопротивление
Фоторезистор — это резистор, сопротивлением которого управляет свет.

Правило простое: чем больше света, тем меньше сопротивление. В темноте фоторезистор может иметь сотни килоом, на ярком свету — меньше килоома. Полярности у него нет: втыкайте любой ножкой куда угодно.

Почему сопротивление не измерить напрямую? Плата умеет измерять только напряжение. Поэтому сопротивление всегда превращают в напряжение — тем самым делителем, который мы разобрали в прошлом уроке. Только теперь вместо ползунка работает свет.

Делитель со светом вместо руки

Ставим фоторезистор сверху (к плюсу), а обычный резистор 10 кОм снизу (к земле). Точку между ними отдаём аналоговому входу:

Светло5 Вфоторезистор≈ 1 кОмA010 кОмanalogRead даёт ≈ 900Сумерки5 Вфоторезистор≈ 10 кОмA010 кОмanalogRead даёт ≈ 500Темно5 Вфоторезистор≈ 100 кОмA010 кОмanalogRead даёт ≈ 90Это тот же делитель, что и у крутилки: просто «ползунок» двигает свет, а не рука.
Сопротивление фоторезистора растёт в темноте — и напряжение в средней точке падает.

Получается удобная логика: светло — большое число, темно — маленькое. Если поменять детали местами, всё перевернётся, и это тоже нормально — просто условие в программе будет с другим знаком.

Почему именно 10 кОм? Делитель работает точнее всего, когда постоянный резистор близок к сопротивлению датчика в середине рабочего диапазона. Для комнатного освещения это как раз около 10 кОм. Поставите 1 кОм — прибор станет различать только яркий свет; поставите 100 кОм — только глубокую темноту.

Собираем схему

Отключите USB. Резистор нужен на 10 кОм (кольца коричневое, чёрное, оранжевое) — не перепутайте с 470 Ом для светодиода. Светодиод остаётся там же, где был.

++abcdefghij110207121517123451Фоторезистор: из шины «+» в столбик 7 (ряд b). Полярности у него нет.2Резистор 10 кОм: из того же столбика 7 (ряд c) — в шину «−».3Жёлтый провод: столбик 7 (ряд a) → вход A0. Это середина делителя.4Светодиод: вывод 10 → резистор 470 Ом → длинная ножка; короткая — в шину «−».5Питание макетки: 5V платы → шина «+», GND → шина «−».
Обе детали приходят в столбик 7 — это и есть середина делителя. Оттуда провод на A0.
UnoA0D10GND5 ВR1 фоторезисторR2 10 кОмVD1R3 470 ОмФоторезистор сверху, постоянный резистор снизу — тогда в темноте число на A0 маленькое.
Фоторезистор обозначают резистором в кружке, на который светят две стрелки.

Проверьте перед включением: обе детали приходят в один и тот же столбик — именно там середина делителя; жёлтый провод идёт из этого же столбика на A0; резисторы не перепутаны местами.

Шаг 1. Узнаём свои числа

Самый важный шаг урока — и его почти всегда пропускают. Числа, которые выдаёт ваш датчик в вашей комнате, не совпадут ни с чьими другими. Они зависят от лампы, от окна, от времени суток. Поэтому сначала — измерить.

const int LIGHT = A0;

void setup() {
  Serial.begin(9600);
  Serial.println("Освещённость:");
}

void loop() {
  int light = analogRead(LIGHT);
  Serial.println(light);
  delay(200);
}

Прошейте, откройте монитор порта и запишите три числа:

  • обычный свет в комнате — сколько показывает;
  • накрыли датчик ладонью — сколько стало;
  • посветили фонариком телефона — до скольких подскочило.

Например, может получиться так: свет — 620, ладонь — 180, фонарик — 950. Ваши числа будут другими, и это нормально. Именно они дальше станут порогами в программе.

Загляните на вкладку «График». Медленно проводите ладонью над датчиком — и увидите живую кривую освещённости. Это лучший способ понять, как датчик реагирует: быстро ли, плавно ли, есть ли шум.

Шаг 2. Первый ночник

Теперь у нас есть числа, и можно назначить порог: темнее его — включаем свет.

const int LIGHT = A0;
const int LED = 10;

// ПОДБЕРИТЕ СВОЁ ЧИСЛО по монитору порта: середина между
// «светло в комнате» и «накрыл датчик ладонью».
const int DARK = 400;

void setup() {
  pinMode(LED, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  int light = analogRead(LIGHT);

  if (light < DARK) {
    digitalWrite(LED, HIGH);   // стало темно — включаем свет
  } else {
    digitalWrite(LED, LOW);    // светло — свет не нужен
  }

  Serial.println(light);
  delay(100);
}

Число DARK возьмите своё: примерно посередине между «нормальный свет» и «накрыл ладонью». В нашем примере это было бы около 400.

Прошейте и накройте датчик ладонью — светодиод загорится. Уберите руку — погаснет. Устройство работает само, без кнопок. Поздравляем: вы собрали свой первый прибор с датчиком.

Шаг 3. Лампа дёргается — и это не поломка

А теперь поднесите ладонь медленно, остановившись ровно на границе срабатывания. Светодиод начнёт нервно моргать: то включится, то выключится, несколько раз в секунду.

Причина знакомая: любой датчик слегка шумит, а мы принимаем решение по одному-единственному числу. Когда освещённость топчется вокруг порога, малейшее дрожание перебрасывает её то выше, то ниже.

Один порогпорогосвещённостьлампадёргается на границеДва порогавключитьвыключитьосвещённостьлампавключилась один раз
Один порог — лампа мечется на границе. Два порога — включается один раз и спокойно горит.

Лечится это красивым приёмом: сделать два порога вместо одного. Включать, когда стало заметно темнее, а выключать — только когда стало заметно светлее. Между ними — «зона покоя», в которой ничего не меняется.

const int LIGHT = A0;
const int LED = 10;

// Включаем, когда стало темнее 380, а выключаем только когда
// стало светлее 460. Между ними — «зона покоя».
const int TURN_ON = 380;
const int TURN_OFF = 460;

bool lampOn = false;

void setup() {
  pinMode(LED, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  int light = analogRead(LIGHT);

  if (!lampOn && light < TURN_ON) {
    lampOn = true;
    Serial.println("темнеет — включаю");
  }

  if (lampOn && light > TURN_OFF) {
    lampOn = false;
    Serial.println("светло — выключаю");
  }

  digitalWrite(LED, lampOn);
  delay(50);
}

Приём называется гистерезисом. Слово громкое, а идея простая: у прибора появляется характер — он не меняет решение из-за мелочей. Ровно так работает термостат в утюге, холодильник и обогреватель: включаются при 18 градусах, выключаются при 22, а не дёргаются вокруг двадцати.

Почему условия написаны с lampOn? Потому что решение зависит не только от освещённости, но и от того, что происходит сейчас. Пока лампа выключена, нас интересует только нижний порог; пока горит — только верхний. Программа помнит своё состояние — тот же приём, что и с режимами в тринадцатом уроке.

Шаг 4. Плавный ночник

«Включено или выключено» — грубовато для света. Сделаем как в хороших светильниках: чем темнее в комнате, тем ярче горит лампа.

const int LIGHT = A0;
const int LED = 10;

// Границы, снятые по монитору порта: как бывает у вас темно и светло.
const int DARK = 200;
const int BRIGHT = 700;

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

void loop() {
  int light = analogRead(LIGHT);

  // Диапазон перевёрнут: маленькая освещённость — большая яркость.
  int value = map(light, DARK, BRIGHT, 255, 0);

  // constrain не даёт значению выйти за пределы 0…255.
  value = constrain(value, 0, 255);

  analogWrite(LED, value);
  delay(20);
}

Здесь map работает наоборот: диапазон яркости записан от 255 к 0. Значит, тёмному значению датчика соответствует полная яркость, а светлому — ноль. Ровно об этом мы говорили в прошлом уроке.

И новая команда — constrain. Она удерживает значение в заданных границах: что меньше нуля, станет нулём, что больше 255 — станет 255. Без неё в очень яркий полдень map выдал бы отрицательное число, и analogWrite повёл бы себя странно.

Правило на будущее. Всё, что пришло из внешнего мира, ограничивайте. Датчик может выдать больше или меньше, чем вы ожидали: солнце светит ярче лампы, провод дрогнул, деталь нагрелась. Одна строчка constrain спасает от целого класса странных багов.

Не работает? Разбираемся

  • Число не меняется, что ни делай. Проверьте, что обе детали приходят в один столбик и что провод на A0 идёт именно из этого столбика. Если делитель разорван, вход снова «висит в воздухе».
  • Значение всегда около 1023 или около 0. Скорее всего, вместо 10 кОм стоит 470 Ом или наоборот перепутаны местами фоторезистор и резистор. Сравните кольца: 10 кОм — коричневое, чёрное, оранжевое.
  • Светодиод горит всегда. Порог DARK взят «из интернета», а не из вашего монитора. Померьте свои числа и поставьте своё.
  • Свет включается, когда светло, и наоборот. Либо детали в делителе поменяны местами, либо в условии нужен знак «больше» вместо «меньше». И то и другое — нормально, выберите, что понятнее.
  • Лампа моргает на границе. Это тот самый случай, ради которого мы делали гистерезис. Разведите пороги подальше друг от друга.
  • Ночник реагирует на самого себя. Классика жанра: светодиод светит на фоторезистор, тот видит свет и выключает лампу. Разверните детали в разные стороны или отодвиньте подальше.

Задания для тренировки

  • РазминкаПусть плата печатает в монитор не число, а слово: «день», «сумерки» или «ночь». Печатать только при смене состояния.
  • ПосложнееСделайте, чтобы ночник включался не мгновенно, а плавно разгорался за пару секунд — и так же плавно гас. Подсказка: храните текущую яркость в переменной и двигайте её к цели по одному шагу за проход.
  • Со звёздочкой ⭐Избавьтесь от «магических чисел»: пусть устройство само измеряет самое тёмное и самое светлое при включении, а потом работает по этим границам. Разбор ниже.
Показать разбор «самокалибровка»

Идея взрослая и очень полезная: прибор не должен требовать, чтобы его настраивали вручную под каждую комнату. Пусть при включении он несколько секунд смотрит по сторонам и сам решает, что считать темнотой, а что светом.

const int LIGHT = A0;
const int LED = 10;

int minLight = 1023;
int maxLight = 0;

void setup() {
  pinMode(LED, OUTPUT);
  Serial.begin(9600);
  Serial.println("Калибровка: 5 секунд закрывайте и открывайте датчик...");

  // Пять секунд запоминаем, каким бывает самое тёмное и самое светлое.
  unsigned long start = millis();
  while (millis() - start < 5000) {
    int light = analogRead(LIGHT);

    if (light < minLight) {
      minLight = light;
    }
    if (light > maxLight) {
      maxLight = light;
    }
  }

  Serial.print("темнее всего: ");
  Serial.println(minLight);
  Serial.print("светлее всего: ");
  Serial.println(maxLight);
  Serial.println("Готово, работаю.");
}

void loop() {
  int light = analogRead(LIGHT);

  int value = map(light, minLight, maxLight, 255, 0);
  analogWrite(LED, constrain(value, 0, 255));

  delay(20);
}

Вся калибровка живёт в setup() — она выполняется один раз. Внутри работает while: цикл крутится, пока с начала не прошло 5000 миллисекунд, и всё это время запоминает рекордно тёмное и рекордно светлое значения.

Обратите внимание на начальные значения: minLight = 1023 и maxLight = 0. Они специально выставлены «наоборот», чтобы первое же измерение их сбило. Если начать с нулей, минимум так и останется нулём.

Так устроены настоящие приборы: датчик линии у робота калибруют, проведя его над чёрным и белым; микрофон подстраивается под тишину в комнате. Один раз научили — дальше работает где угодно.

Что мы узнали

  • Фоторезистор — резистор, которым управляет свет: больше света — меньше сопротивление.
  • Сопротивление превращают в напряжение делителем; постоянный резистор берут близким к рабочему сопротивлению датчика — здесь 10 кОм.
  • С новым датчиком всегда начинайте с измерения своих чисел в мониторе порта. Чужие пороги не работают.
  • Гистерезис — два порога вместо одного — избавляет прибор от дрожания на границе.
  • constrain удерживает значение в границах и спасает от неожиданных данных с датчика.

Наш ночник реагирует на свет — то есть на величину, которая меняется плавно. Но бывают датчики-«кнопки»: они не измеряют, а просто замыкаются, когда что-то произошло. В следующем уроке возьмём сразу два таких — датчики наклона и вибрации — и соберём из них сигнализацию.

Частые вопросы

Как работает фоторезистор?

Это резистор, сопротивление которого зависит от света: чем ярче, тем меньше сопротивление. В темноте оно может быть в сотню раз больше, чем на солнце. Полярности у фоторезистора нет, подключать его можно любой стороной.

Какой резистор ставить в пару к фоторезистору?

Обычно 10 кОм — он близок к сопротивлению фоторезистора при комнатном освещении, поэтому делитель даёт самый широкий размах показаний. С резистором 1 кОм прибор станет различать только яркий свет, со 100 кОм — только темноту.

Почему у меня другие числа, чем в уроке?

Так и должно быть: показания зависят от конкретного датчика, лампы, окна и времени суток. Поэтому в уроке пороги не даются готовыми — их измеряют в мониторе порта для своей комнаты.

Что такое гистерезис и зачем он нужен?

Это два порога вместо одного: включаем при одном значении, выключаем при заметно другом. Между ними прибор ничего не меняет. Без гистерезиса лампа начинает нервно моргать, когда освещённость топчется около порога.

← Все уроки курса