[Из песочницы] Управление сценариями освещения с помощью Arduino

Причины появления этого проекта по сути две. Одна — это систематически перегорающие лампы в прихожей. Вторая — это имеющийся опыт работы с системами автоматизации на базе промышленных программируемых контроллеров (проектирование систем АОВ, АХС, АСДУ и т. д.) и желание применить эти знания во благо своих домочадцев.

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

Схема подключения
схема


Также наглядным будет план помещения.

План прихожей
image


Итак в прихожей созданы 2 сценария освещения, дневной со светодиодной лентой на потолке и ночной, со светодиодной лентой на уровне пола. Оба режима управляются с помощью двух датчиков движения находящихся над дверью и рядом с зеркалом, как это видно из плана. Изначально был установлен только один датчик движения над дверью, но к моему удивлению периодически свет выключался при наличии людей в помещении. Оказалось что датчик не способен уловить небольшие манипуляции совершаемые у зеркала, тем более если объект находится к нему спиной и создает перед собой слепую зону. Вторым открытием для меня стало что некоторые люди, могут проводить у зеркала больше 1–2 минут (мне обычно хватает секунд 15). Первое решение, пришедшее в голову — это поставить более продвинутый датчик присутствия наподобие Esylux + реле, но стоимость подобного оборудования превысила бы стоимость всего остального проекта, по этому был выбран более простой вариант, установить второй датчик движения для «слепых зон».

Датчик движения был собран из модуля HC-SR501 + распаечная коробка.

Датчик движения
image
image


Алгоритм работы датчиков будет представлен в коде, но в двух словах для включения света достаточно срабатывания одного из датчиков, для выключения необходимо отсутствие сигнала от обоих.

Для потолка была выбрана лента SMD 5050, 300 светодиодов на 5 метров (12V 72W). Одноцветная с тепло-желтым оттенком. Лента включается через релейный модуль.

Для ночной подсветки была выбрана лента SMD 5050 RGB, 150 светодиодов на 5 метров. Задействуется только синий цвет (меньше проводов) Управление через транзисторный модуль L298N (обратите внимание на инверсию выходного сигнала в данном модуле значение 255 в программе будет выключать светодиоды). Яркость ленты выставлена на минимальную, чтобы не слепить глаза.

Led ленты
image
image


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

Корпус контроллера, реле и Транзисторного модуля я решил сделать сам из распаечной коробки, закрепив все оборудование на печатной плате, получилось вполне надежно. Сама коробка закрепляется на DIN рейку. На обратной стороне платы для уменьшения количества проводов есть спаянные соединения, также в плату припаяны клеммные колодки для вводного питания 12В, которым запитываются как сам контроллер так и ленты.

Контроллер
image
image


Ниже, код программы для Ардуино.

Код
#include "IRremote.h"
int calibrationTime = 10; 
byte CellingLed = 9; /* Лента потолка */ 
byte ledB = 6;  // лента пола
byte pirDoor = 5; // Датчик Движения
byte pirMirror = 4; //Датчик у Зеркала 
IRrecv irrecv(2); //  IR сенсор 
byte dayNight = 7; // режим управления, кнопка - день/ночь 
decode_results results;
byte irSignal = 0;
byte buttonstate; 
long unsigned int moveTime; // Время в которое принят сигнал отсутсвия движения
long unsigned int pause = 30000; // Пауза, после которой движение считается оконченным
boolean movementDetected = true; // true - движения нет
boolean moveFlag; // Сигнализирует о необходимости запомнить время начала отсутствия движения
void setup ()
{
  irrecv.enableIRIn();
  Serial.begin(9600);
  pinMode(CellingLed,OUTPUT);
  pinMode(ledB,OUTPUT);
  digitalWrite(dayNight, HIGH); // включение внутренней подтяжки на входе 7
  pinMode(pirDoor,INPUT);
  pinMode(pirMirror,INPUT);
  digitalWrite(pirDoor, LOW);
  digitalWrite(pirMirror, LOW);
// Калибровка датчиков движения
  Serial.print("Calibrating");
  for(int i = 0; i < calibrationTime; i++)
    {
    Serial.print(".");
    delay(1000);
  }
  Serial.println(" done");
  Serial.println("SENSOR ACTIVE");
  delay(50);
}

void loop()
{
if ( irrecv.decode( &results)){
  delay(300);
  if (results.value == 0xFF3AC5) {irSignal = 0;}  /* Задание заранее считанных ИК сигналов пульта */
  if (results.value == 0xFFBA45) {irSignal = 1;}

  irrecv.resume();  
}
// Селектор режима управления (день/ночь, IR вкл/выкл)
  byte DayNightSwitch = !digitalRead(dayNight);
  byte a;
  byte b;
    if (DayNightSwitch == LOW) {a=1;} else {a=0;}
    if (irSignal == 1)         {b=1;} else {b=0;}
    if (a==b) {buttonstate = 1;} 
    else      {buttonstate = 0;}
  byte pirstate;
    if (digitalRead(pirDoor) == HIGH || digitalRead(pirMirror) == HIGH)
        { pirstate = HIGH;}
    if (digitalRead(pirDoor) == LOW && digitalRead(pirMirror) == LOW)
        { pirstate = LOW;}


// лента пола
if(pirstate == HIGH && buttonstate == 0 )
{
 if(movementDetected)
 {
  movementDetected = false;
  Serial.println("Motion detected");
  delay(50); 
  }
  moveFlag = true;
  analogWrite(ledB,240);// приглушенный синий 
  digitalWrite(CellingLed, LOW);
}
if(pirstate == LOW & buttonstate == 0 )
{
  if(moveFlag)
  {
    moveTime = millis();
    moveFlag = false;
    }
  if(!movementDetected && millis() - moveTime > pause )
  {
    movementDetected = true;
    Serial.println("Motion finished");
    delay(50);    
    analogWrite(ledB,255);
    }  
}
// лента потолка 
if (pirstate == HIGH && buttonstate == 1 )
{
 
 if(movementDetected)
 {
   movementDetected = false;
   Serial.println("Motion detected");
   delay(50); 
  }
  moveFlag = true;
  digitalWrite(CellingLed,HIGH);
  analogWrite(ledB,255);
}
if (pirstate == LOW && buttonstate == 1 )
{
  if(moveFlag)
  {
    moveTime = millis();
    moveFlag = false;
    }
  if(!movementDetected && millis() - moveTime > pause )
  {
    movementDetected = true;
    Serial.println("Motion finished");
    delay(50);    
    digitalWrite(CellingLed,LOW);
    } 
}

}



Эта схема освещения работает у меня уже полгода, в целом устраивает своей функциональностью. Конечно, появляются идеи по модернизации, в частности, хотелось бы заменить пульт ДУ с инфракрасного на радио. Добавить несколько радиоуправляемых розеток. Также добавить веб-интерфейс для управления с мобильных устройств. Сторонние сервисы мне кажутся недостаточно надежными. В данный момент уже работает включение, выключение и индикация наличия движения, но это уже другая история.

Веб-интерфейс
image

© Geektimes