Программно регулируемая люстра на Arduino

Давно гуляла в голове идея света, плавно регулируемого, еще и дистанционно.

В то время светодиоды были непопулярны, дороги, а на лампах накаливания делать не хотелось, ибо напряжение и потребление.
И вот настал день…

Недавно закончил альфа-версию интеллектуальной люстры, какая только может получиться, если сисадмин берет в руки паяльник и С++.

Прежде чем немного поделюсь технологией, расскажу, что она умеет, и чего мне не удалось добиться.
— При включении, люстра включается на максимум (как не удивительно), т.е. ее можно использовать как обыкновенную люстру с физической коммутацией;
— Люстра так же управляется дистанционно с пульта управления (четырехканальный для Arduino);
— Яркость света можно регулировать плавно (относительно) назад-вперед при помощи двух кнопок. При этом регулировка цикличная, т.е. если сделать ярче максимума — свет потухнет (сбросится на 0), а если сделать меньше минимума — включится на максимум (таким образом одним нажатием кнопки на пульте, люстру можно включать и выключать)
— Спустя полчаса после включения, люстра немного сбрасывает яркость. Яркий свет нужен не всегда, а этот костыль немного охладит систему. После сброса яркости, ее можно заново выставить на максимум, и опять на полчаса.
— У люстры есть неизбежный таймер отключения. По умолчанию он составляет один час. При помощи пульта (нажатия одной из кнопок) может устанавливаться на полчаса, два часа, либо обратно на час. Но это не тупой таймер, потому что:
— У люстры есть датчик движения, соответственно таймер считает время после последнего замеченного движения;
— В ночное время суток, при обнаружении движения, люстра включается на очень маленькую яркость на протяжении одной минуты. В дневное время суток это не работает, поскольку в люстре стоит датчик света. Движений нужно несколько (секунд 5), чтобы люстра не включалась, когда я переворачиваюсь в кровати;
— Фича включения люстры в ночное время суток отключается при помощи кнопки на пульте, поскольку понятие «ночное время суток» для датчика света и для человека немного различается;
— При достижении таймаута отключения — свет плавно начинает гаситься, и если в это время произойдет движение — свет вернется на свое старое значение;
— Люстра умеет индицировать свое состояние при помощи светодиодика, а то и самой люстры.

Есть желание читать дальше? Тогда вперед.

Для подсветки были выбраны обычные китайские светодиоды, 20 ватт, теплого белого свечения




Для управления логической частью была выбрана Arduino Mini


Датчиком движения служит ЭТО


А датчиком света вот ЭТО


Пульт ДУ использовался вот такой


Драйвер обычный китайский.


Не буду загромождать читателей бесполезными теоретическими ненужными схемами, подключение каждого блока донельзя примитивное.
Датчик для пульта управления имеет четыре выхода, которые возвращают единицу при нажатии кнопки на пульте. Их мы подключаем к цифровым входам. Датчик движения тоже. Датчик света подключаем к аналоговому входу A0.
Плафон был взят дешевый и тоже китайский.

Отдельного внимания заслуживает диммер, регулируемый программным методом. В интернете куча схем, куча готовых приборов. Я пошел простым путем, и поставил один единственный полевой транзистор, IRF840. К слову говоря, работает.

Прошу прощения за нарисованную от руки схему, в схемах увы я не спец. Принцип подключения вот такой:


Прошу прощения за код, увы в программировании я тоже не спец:
<code class="cpp">// Специальная либа, которая сокращает латентность при использовании digitalRead\digitalWrite. #include <digitalWriteFast.h> int led = 7; // Пин, куда подключен индикаторный светодиод int mov = 5; // Пин, куда подключен датчик движения int tra = 9; // Пин, куда подключен затвор полевого транзистора int up = 12; // Пин, куда подключен вывод А int down = 11; // Пин куда подключен вывод B int swi = 10; // Пин, куда подключен вывод C int swi1 = 6; // Пин, куда подключен вывод D String btn; int tmp; int mv; int timer,inttimer; // Переменные таймеров int br = 255;    // Яркость светодиода по умолчанию int cur = 255; // Сервисная переменная яркости int stepp = 5;    // На сколько изменять яркость int moved; // Признак работы после движения int out = 3600; // Таймаут, после которого свет выключается int svet=60; // На сколько включать свет ночью при движении void setup()  {    pinMode(led, OUTPUT);   pinMode(tra, OUTPUT);   pinMode(13, OUTPUT);   pinMode(mov, INPUT);   pinMode(up, INPUT);   pinMode(down, INPUT);   pinMode(swi, INPUT);   timer = 0;   Serial.begin(9600);    analogWrite(tra,255);     }   void loop()  {  // Наращиваем таймеры timer++;   inttimer++; int sensorValue = analogRead(A0); // Читаем яркость света mv = mv + digitalReadFast(mov); if (mv == 7) {mv=0;} // Считаем сколько было движений, и сбрасываем, если их например 7 btn = buttonpressed(); // Считываем, какая кнопка была нажата на пульте // Если была нажата кнопка, то формируем действия if (btn != "" ) { if (br>10) {stepp=10;} // Если текущее значение больше 10, тогда шаг изменения ставим 10. Это делается для более заметного изменения яркости if (btn == "a") {br = br+stepp;}   if (btn == "b") {br = br-stepp;} if (btn == "c") { // Если нажатие на клавишу выбора таймаутов, то if (out == 7200) {out = 3600;blink(8,50);delay(1000);} else // Если текущий таймаут два часа, то сбрасываем на час, и блимкаем 8 раз if (out == 1800) {out = 7200;blink(0,2000);delay(1000);} else // Если текущий таймаут полчаса, то ставим на два, и блимкаем один длинный раз if (out == 3600) {out = 1800;blink(50,15);delay(1000);} // Если текущий таймаут час, то ставим на полчаса и мерцаем индикатором } if (br<0) {br = 255;} // Если порог яркости занижен, то ставим на максимум (включаем свет) if (br>255) {br = 0;} // Если превышен порог яркости, то сбрасываем на 0 (отключаем свет) cur=br; analogWrite(tra,br);  // Устанавливаем напряжение на ШИМ-выходе Ардуины, и соответственно на затворе транзиcтора // Сбрасываем переменные после нажатия каждой кнопки timer=0;  inttimer=0; moved=0; delay(250); }  // Если было зафиксировано движение, то начинаем отсчет заново if (digitalReadFast(mov) == 1) { timer=0; digitalWriteFast(13,HIGH); } else {  digitalWriteFast(13,LOW); }  if ((br>240)and(inttimer==1800)) {br=180;cur=180;analogWrite(tra,cur);} // Если прошло полчаса, и яркость люстры высокая, то сбрасываем ее немного  // По достижению определенного таймера, плавно гасим свет, но восстанавливаем, если в этот момент засечено движение if (timer==out) { for(int i = br;i>=0;i--) { analogWrite(tra,i);  br=0; if (digitalReadFast(mov)==1) { timer=0; br=cur; analogWrite(tra,cur);  break; } delay(100); } }  // Если сработал таймер ночного светильника, то выключаем свет if ((timer==svet)and(moved == 1)) { br=0; moved=0; mv=0; cur=0; analogWrite(tra,br);  }  // Если были движение и яркость светильника - ноль, то включаем свет на пятерочку и сбрасываем таймер if ((mv>5) and (br==0) and (sensorValue>1020)) { br=5; cur=5; timer = 0; moved=1; mv=0; analogWrite(tra,br);   }   }  void blink(int count, int del) { for (int i=0; i <= count; i++){ // sets the value (range from 0 to 255): delay(del);                        digitalWriteFast(led, HIGH);          delay(del);                        digitalWriteFast(led, LOW);              }  }  String buttonpressed() // Функция-костыль для определения нажатия кнопки пульта + пауза { btn = ""; for (int i=0; i <= 1000; i++){ if (digitalReadFast(down) == 1) {btn = "b";} if (digitalReadFast(up) == 1) {btn = "a";} if (digitalReadFast(swi) == 1) {btn = "c";} if (digitalReadFast(swi1) == 1) {btn = "d";} if (btn != "") {blink(0,20);break;} delay(1); } return btn; } </code>

Немного рабочего процесса:









Немного видео с демонстрацией:


Немного разочарований:
— Конструкция, которая была снята на видео — неработоспособна. Два 20-ваттных светодиодов теплого света раскочегарили радиатор до такой степени, что расплавилось олово, которым к ним были припаяны провода. Заменил два 20-ваттных теплых, на четыре 10-ваттных нейтральных и ввел костыль с понижением яркости.
— Нейтральные белые светодиоды — гадость, для глаз неприятная.
— Не смог придумать эмуляцию зажимания клавиши, как на компьютерных клавиатурах. Из-за за этого, свет изменяется рывками, поскольку delay — фиксированный.
— Пульт ДУ периодически проглючивает, а именно не ловится приемником, посему зажегшийся индикатор на пульте — не гарантия доставки сигнала. Именно для этого пришлось ввести индикатор на люстру
— Массивного радиатора для двух 20-ваттных светодиодов — оказалось мало, либо они работали в экстремальном режиме. Завтра замеряю токи, и попробую поставить резисторы.
— Плафон — полная гадость, поскольку в него видны все сопли на проводах. Планирую заказать матовый акрил, и сделать рассеянный белый свет.
— Не нашел на рынке КРЕН5А (невероятно, но факт), из-за этого для Ардуины пришлось ставить доп. блок питания, а хотелось питать ее от драйвера.
— Сопли — это нехорошо, Ардуина тоже. Вытравил плату для атмеги, приедет программатор — избавлюсь от лишних соплей.

Спасибо, что прочитали до конца.

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

Источник: habrahabr.ru/post/247151/


Комментарии