← До всіх проєктів
Освітлення Початковий

Нічник з датчиком руху: м'яке світло для коридору та сходів

Стрічка WS2812B плавно розгоряється теплим світлом, коли вночі хтось проходить, і сама гасне через хвилину. Фоторезистор не дає нічнику вмикатися вдень.

Складність
Початковий
Час
1 h
Категорія
Освітлення

Сценарій

Ніч, ви йдете на кухню. Щойно ступили в коридор — під плінтусом м'яко розгоряється тепле світло, не засліплюючи. Пройшли — через хвилину воно так само плавно гасне. Вдень нічник не реагує взагалі.

Компоненти

  • ESP32 DevKit
  • Стрічка WS2812B 5 В, 30–60 світлодіодів
  • PIR-датчик HC-SR501 або мініатюрний AM312
  • Фоторезистор (LDR) + резистор 10 кОм
  • Блок живлення 5 В 2 А, резистор 330 Ом у лінію даних, конденсатор 1000 мкФ

Підключення

Компонент ESP32
Стрічка DIN GPIO 16 через 330 Ом
PIR OUT GPIO 27
Фоторезистор 3V3 → LDR → GPIO 34 → 10 кОм → GND

Стрічку й ESP32 живіть від одного 5 В блока, земля спільна (детальніше про живлення стрічок — у проєкті WLED). Оскільки нічник світить на 20 % яскравості, 30 світлодіодів споживатимуть лише ~0.4 А.

Бібліотека

Adafruit NeoPixel.

Код

#include <Adafruit_NeoPixel.h>

const int LED_PIN = 16;
const int NUM_LEDS = 30;
const int PIR_PIN = 27;
const int LDR_PIN = 34;

const int DARK_THRESHOLD = 1200;        // 0..4095: менше — темніше
const unsigned long HOLD_MS = 60000;    // світити хвилину після останнього руху
const uint8_t MAX_LEVEL = 50;           // ~20 % яскравості — не сліпить уночі

Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);

bool isOn = false;
int level = 0;                          // поточна яскравість 0..MAX_LEVEL
unsigned long lastMotion = 0;
unsigned long lastFade = 0;

void render() {
  uint8_t l = strip.gamma8(map(level, 0, MAX_LEVEL, 0, 255)) * MAX_LEVEL / 255;
  // Теплий білий ~2200 K
  strip.fill(strip.Color(l, l * 45 / 100, l * 12 / 100));
  strip.show();
}

void setup() {
  Serial.begin(115200);
  pinMode(PIR_PIN, INPUT);
  strip.begin();
  strip.clear();
  strip.show();
}

void loop() {
  bool motion = digitalRead(PIR_PIN) == HIGH;

  // Вмикаємося лише в темряві. Якщо вже світимо — LDR бачить наше ж світло,
  // тому рух просто продовжує таймер.
  if (motion && (isOn || analogRead(LDR_PIN) < DARK_THRESHOLD)) {
    isOn = true;
    lastMotion = millis();
  }
  if (isOn && millis() - lastMotion > HOLD_MS) {
    isOn = false;
  }

  // Плавність: розгоряємося за ~1 с, гаснемо за ~3 с
  unsigned long step = isOn ? 20 : 60;
  if (millis() - lastFade > step) {
    lastFade = millis();
    int target = isOn ? MAX_LEVEL : 0;
    if (level != target) {
      level += level < target ? 1 : -1;
      render();
    }
  }
}

Налаштування

  • Поріг темряви. Тимчасово додайте в loop() рядок Serial.println(analogRead(LDR_PIN)); і подивіться значення ввечері при вимкненому світлі. Встановіть DARK_THRESHOLD трохи вище.
  • Колір. Для холоднішого світла збільште синю складову: strip.Color(l, l * 70 / 100, l * 40 / 100).
  • HC-SR501: поверніть потенціометр часу на мінімум — таймер тепер у коді.

Корисно знати

  • Датчик руху краще ставити на рівні 1–1.5 м або на стелі, а стрічку — під плінтусом чи сходинками.
  • gamma8() робить зміну яскравості рівномірною для ока: без гамма-корекції перші кроки виглядали б «стрибком».
  • Для сходів: запаліть світлодіоди хвилею знизу вгору або зверху вниз — залежно від того, який із двох PIR-датчиків спрацював першим.

Ідеї

  • Поставте стрічку на прошивку WLED і керуйте нею через Home Assistant, використовуючи PIR як тригер автоматизації.
  • «Світанок» у спальні: за 20 хв до будильника світло поступово розгоряється.