Свет в комнате с любого пульта 2. Работа над ошибками


Краткий конспект.

Предыдущая статья+коллектив муськовчан = управление светом с любого пульта как это должно быть )).

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

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

1. Он должен работать с любого пульта в доме – это основное требование;
2. Выключатель должен работать и в обычном режиме, ибо, заходя в темную комнату, в руках пульта еще нет;
3. Решение от НООТЕХНИКИ, упомянутое uncle_sem и еще несколькими читателями, где длительное нажатие любой кнопки любого пульта инициирует выключатель, не подходит мне по причине необходимости иногда долго держать нажатыми кнопки увеличения/уменьшения громкости на телевизоре из-за зачастую огромной разницы в уровне звука в фильмах, скачанных с интернета. И еще, у меня активные колонки также управляются с пульта, и, значит, и там вылезет та же проблема. Это подтвердил опыт муськовчанина Lone. Но от самой идеи я кое-что взял на вооружение.
4. Выключатель должен быть обучаемым, это ценное предложение высказал пользователь Dimon_, предложив использовать мастер пульт, чтобы обучать выключатель без его демонтажа. Я пошел еще дальше, добавив от себя требование — без мастер пульта, без доп.кнопок и прочих приблуд, которые предложили некоторые муськовчане.

По другим конструктивным предложениям.
Заменять реле на твердотельное я не буду, так как смысла не вижу, хотя и заказал на Али такое для экспериментов. Спасибо за наводку пользователю serg_mur. Но те, кому нужно, могут применять и такое, в схеме ничего принципиально не меняется.
Схема
Схема не изменилась. Только по замечанию от kirich исправил в обозначении Grd на GND.


Про регулировку яркости смысла говорить нет, поскольку лампы накаливания не использую давно.

В выходной день я немного поправил скетч. Библиотеку EEPROM2.h брал тут.
Скетч

#include <IRremote.h>
#include <EEPROM2.h>

#define led 13     // светодиод
#define RECV_PIN 8 // нога на IRDA приемник
#define DP1 7      // нога на реле 
#define VIK 2      // нога на выключатель 
#define maxstack 8 // размер стека-1  
#define maxbut 20  // максимум запомненных кнопок

byte maxpults;
IRrecv irrecv(RECV_PIN);
decode_results results;
unsigned long butt[maxbut]; 
unsigned long buttstack[maxstack+1];
bool VikState;
bool flaglearn = false;

void ReleOff()
{  digitalWrite(DP1, HIGH);}

void setup()
{
 irrecv.enableIRIn();     // Старт ресивера IRDA
 pinMode(DP1, OUTPUT);    // выход на реле
 pinMode(VIK, INPUT);     // вход выключателя
 digitalWrite(VIK, HIGH); // включаем подтягивающий резистор
 VikState=digitalRead(VIK);
 YesRelay();
 ReleOff();
 pinMode(led, OUTPUT);    
 digitalWrite(led, LOW);   // гасим светодиод
  for (maxpults=0;maxpults<maxbut;maxpults++)
    {EEPROM_read(maxpults*4, butt[maxpults]);
     if (butt[maxpults]==0) break;}
}
 
void YesRelay()
{
		  digitalWrite(DP1,!digitalRead(DP1));
}

void iflearn(unsigned long code) // проверка на включение режима обучения
{
  for (byte i=0;i<maxstack;i++) // суем в стек LIFO код клавиши
      buttstack[maxstack-i]=buttstack[maxstack-i-1];
  buttstack[0]=code;  
  //если в стеке последовательность 3-х разных кнопок 3 раза, то переход в режим программирования
  if ((buttstack[0]==buttstack[3]) && (buttstack[0]==buttstack[6]) 
     && (buttstack[1]==buttstack[4]) && (buttstack[1]==buttstack[7])
     && (buttstack[2]==buttstack[5]) && (buttstack[2]==buttstack[8]) && (buttstack[0]!=buttstack[1])
     && (buttstack[0]!=buttstack[2]) && (buttstack[1]!=buttstack[2]))
      { // переход в режим программирования
       digitalWrite(led, HIGH);
       flaglearn=true;
        for (byte i=0;i<maxstack+1;i++) // чистим стек
        buttstack[i]=0;
        maxpults=0;
      } 
}     
void loop() 
{              
  if (VikState != digitalRead(VIK)) // если изменилось состояние кнопки выключателя
     {YesRelay(); 
      VikState=!VikState;}
  if (irrecv.decode(&results)) 
  {
   if (flaglearn) { // если флаг обучения взведен, входим в режим обучения пульта
                   butt[maxpults]=results.value;
                   if ((maxpults>0) && (butt[maxpults]==butt[maxpults-1]) || (maxpults==maxbut))
                      { // режим обучения отключаем при дублировании последней кнопки
                        flaglearn=false;
                        digitalWrite(led, LOW);
                        for (byte i=0;i<maxpults;i++) // записываем коды в EEPROM
                            EEPROM_write(i*4, butt[i]);
                        butt[maxpults]=0; //    
                        EEPROM_write(maxpults*4, butt[maxpults]);
                      } else
                        maxpults++;
                  } else
   { // обычный режим ловли кода кнопки пульта
   if (results.value!=0){iflearn(results.value);}
     for (byte i=0;i<maxpults;i++) // сравним полученный код с каждым из пультов
      {
        if (results.value==butt[i]) 
          { YesRelay();  // если код совпал, переключаем реле
            break;     } //  и выходим из цикла 
      }  
   }   
    delay(400); // убираем дребезг  
    irrecv.resume();
  }
}


В самой схеме не менял ничего, хотя одна мысль о добавлении баззера появилась. Как теперь все работает?

Теперь собранная схема с новой прошивкой может обучаться с любого пульта. Для этого нужно последовательно нажать 3 разные кнопки на пульте и затем повторить эту процедуру еще 2 раза. Например, нажимаем кнопки ( 1, 2, 3) — (1, 2, 3) — ( 1, 2, 3). Набрать такую комбинацию случайно весьма проблематично, так что это аналог цифрового замка. После этого выключатель переходит в режим обучения. В данном скетче об этом сигнализирует светодиод, что присутствует на самой Ардуине, но логичнее будет включать баззер (пищалку), поскольку в закрытом выключателе именно сигнализация писком выглядит наиболее логичной. Баззер у меня был куплен ранее, брал здесь. Однако он оказался неисправным, поэтому я заказал на днях еще пару здесь, а пока обхожусь светодиодом.
Итак, как только выключатель перешел в режим обучения, достаем свои пульты и начинаем знакомство. Берем первый пульт и нажимаем кнопку, которую и назначаем ответственной за свет, затем следующий пульт и так далее. Можно, конечно, и на одном пульте назначить несколько кнопок. Закончить обучение можно, еще раз нажав последнюю назначенную кнопку. Тогда светодиод гаснет (пищалка молкнет) и выключатель переходит в обычный режим работы. Обучение закончится автоматически и при достижении максимального числа запомненных кнопок (в данном скетче 20). Коды пультов записываются в энергонезависимую память EEPROM, поэтому при отключении электричества все коды сохранятся.
Вот собственно и все. Сам выключатель также инвертирует состояние реле. Можно еще добавить управление не одной, а двумя группами ламп, (такое пожелание было высказано в личке), а в остальном, ИМХО, проект можно передавать в эксплуатацию. Или у кого-то еще будут интересные мысли?
Как это работает на видео:

Забыл здесь, правда пощелкать выключателем, но он так-же работает, как и в видео прошлой статьи.

P.S. Если у кого будут посты типа: весь этот функционал реализуется на гораздо меньшем количестве компонентов и гораздо проще, прошу сразу давать ссылочки на такую реализацию. Но при этом прошу не забывать, что в моей задаче выключатель еще и должен работать так же, как и ранее.
Планирую купить +40 Добавить в избранное +28 +51
+
avatar
  • OkeaH
  • 02 марта 2016, 21:30
+1
О вторая серия по управлению светом. Сколько всего серий планируется? Можно еще wifi к арудинке прикрутить и управлять со смартфона.
+
avatar
  • klop
  • 02 марта 2016, 22:00
0
Больше не планируется. Со смарта выключать свет — мазохизм ИМХО.
+
avatar
  • ksiman
  • 02 марта 2016, 22:03
0
А голосовое управление прикрутить?
+
avatar
  • klop
  • 02 марта 2016, 22:05
+2
А если телек начнет через голосовое управлять? )))
+
avatar
+8
+
avatar
  • wd40
  • 02 марта 2016, 22:38
+2
Ехал я тут в машине со включенным Яндекс-Навигатором. И начал коллеге что-то про Яндекс рассказывать. А в Я.Навигаторе «яндекс» — активирующее слово. Сделать-то он ничего не сделал, но поиск постоянно запускал. =)
+
avatar
  • FloX
  • 03 марта 2016, 06:24
+2
я выключаю. броадлинком, лениво ходить по дому и смотреть что включено, а что нет и выключать
взял телефон, щелкнул, все выключилось
+
avatar
  • Naevus
  • 03 марта 2016, 12:15
+2
Вторая серия и во второй раз уже не сдержался: Весь функционал можно сразу на wifi делать — esp8266 прекрасно обойдется и без ардуины. В проекте, на который ссылался я в обзоре есть и ir приемник и реле и управление через gpio. Т.е. ничего программировать не надо (ну почти — логику «шатного» выключателя наверное надо будет прописать в логический блок). Управление по wifi — бонусом. Там же — настройка и обновление прошивки. Еще в плюс — меньший размер (меньше микро и нано точно). Эх, знал бы, что klop пойдет на второй круг — отписался бы в первом обзоре — глядишь и круг бы оказался другим :)
ps Задумался… а не проще ли размещать девайс в плафоне люстры? Да и пультом целиться в выключаемое устройство логичнее, чем в выключатель… Надо будет обдумать такой вариант :)
+
avatar
+3
Замечательно! Плюс.
+
avatar
  • ksiman
  • 02 марта 2016, 21:46
+3
Вторая версия программной доработки заметно лучше изначального варианта.
+
avatar
  • ksiman
  • 02 марта 2016, 22:00
0
Ещё-бы сделать, чтобы без нулевого провода как-то работало… было-бы ещё лучше.
+
avatar
  • klop
  • 02 марта 2016, 22:04
0
Да бы… Но без него тока если огород городить на люстре. Иначе никак.
+
avatar
  • ksiman
  • 02 марта 2016, 22:09
0
Иначе никак.
Можно иначе, но придётся от электромагнитного реле отказываться
+
avatar
  • klop
  • 02 марта 2016, 22:14
0
А можно подробнее?
+
avatar
  • ksiman
  • 02 марта 2016, 22:19
+1
Можно питание брать за счёт падения напряжения на ключевом симисторе. На этом принципе работают например устройства плавного пуска для галогенок
+
avatar
  • klop
  • 02 марта 2016, 22:23
0
Увы, в электронике я не силен. Даже ничего не понял из написанного Вами (. Значит судьба мне тянуть нуль к выключателю.
+
avatar
  • ksiman
  • 02 марта 2016, 22:25
+1
Можно найти схему плавного пуска и на её базе сделать
Вот

При этом не нужны будут БП и реле
3 вариант будет? :)
+
avatar
  • AlexZZZ
  • 02 марта 2016, 22:32
0
А ардуину от чего питать без бп?
+
avatar
  • ksiman
  • 02 марта 2016, 22:32
0
От сети как по схеме выше — там тоже контроллер.
Но к сожалению, данная реализация плохо работает со светодиодными лампами :(
+
avatar
0
Данная схемотехническая реализация плохо работает? Или плавный пуск плохо работает?
+
avatar
  • ksiman
  • 03 марта 2016, 11:15
0
За счёт потребления тока, через лампочку протекает небольшой паразитный ток, который вызывает моргание светодиодной лампы в выключенном состоянии дистанционного выключателя
+
avatar
  • klop
  • 02 марта 2016, 22:37
0
«Как это сложно, Веничка...». Не могу браться за то, чего не понимаю. А это тот случай, увы. А если еще и со светодиодами проблемы ожидаются, то и нафик тогда…
+
avatar
  • ksiman
  • 02 марта 2016, 22:40
0
Всё равно у Вас получилось нормально :)
+
avatar
  • klop
  • 02 марта 2016, 22:44
0
Спасибо. Доброе слово от уважаемого человека дорогого стоит.
+
avatar
0
Я так понимаю, что на R4C4R6 реализован детектор нуля? Не подскажете логику его работы? Почему именно такие номиналы?
+
avatar
  • ksiman
  • 03 марта 2016, 11:17
0
Да, это детектор ноля. Номиналы подобрал разработчик по своим критериям
+
avatar
+1
как правильно сказал ksiman — второй вариант сильно лучше. думается, алгоритм перехода в режим обучения тоже неплох. разве что к питанию прикопаться можно — но тут уж ничего не поделаешь, особенно в смысле проблем со светодиодами…
короче от меня заслуженный плюс! ;)
+
avatar
  • skeptik
  • 03 марта 2016, 00:32
0
А баззер точно не рабочий? А то я на свой тоже так думал, а он, оказалось, пищал, если на ножку ардуины к которой он подключён не high, а low посылать. Видимо, на баззаре транзистор не той системы стоит :-) может у вас тот же случай?
+
avatar
  • klop
  • 03 марта 2016, 13:36
0
Увы, не рабочий…
+
avatar
  • Torpp
  • 03 марта 2016, 13:32
0
а как это применить с двухклавишным выключателем?
+
avatar
  • klop
  • 03 марта 2016, 13:46
0
Для двуклавишного скетч чуток подправить, двойное реле поставить вместо одинарного. На схеме на Ардуине еще одна нога задействуется. А логика управления с пульта — это уж каждый решает, как лучше. Возможно, с пульта кому-то совсем не нужно второй группой ламп управлять, кому-то хочется 2 кнопки на пульте для раздельного независимого управления группами, а кому-то покажется проще одной кнопкой перебор состояний или временнАя комбинация.
+
avatar
  • llams
  • 04 марта 2016, 04:08
0
Автор молодец.

Не знаю как свернуть под спойлер. :-)

#include <IRremote.h>
#include <EEPROM2.h>

#define RECV_PIN 8
#define led 13
#define DP1 7
#define VIK 2

byte maxpults;
IRrecv irrecv(RECV_PIN);
decode_results results;

#define maxstack 8
#define maxbut 20 // максимум пультов

unsigned long butt[maxbut]; 
unsigned long buttstack[maxstack+1];
//int ukaz;
bool LampState = false;
bool VikState;
bool flaglearn = false;

void setup() {
	irrecv.enableIRIn();    // Старт ресивера IRDA
	pinMode(DP1, OUTPUT);    // выход на реле
	pinMode(VIK, INPUT);     // вход выключателя
	digitalWrite(VIK, HIGH); // включаем подтягивающий резистор
	VikState=digitalRead(VIK);
	digitalWrite(DP1, HIGH);
	pinMode(led, OUTPUT);    
	digitalWrite(led, LOW);   // гасим светодиод
	for (maxpults=0;maxpults<maxbut;maxpults++){
		EEPROM_read(maxpults*4, butt[maxpults]);
		if (butt[maxpults]==0) break;
	}
}

void YesRelay() {
	LampState = !LampState; // инвертируем состояние реле
	digitalWrite(DP1,LampState);
}

void iflearn(unsigned long code) { // проверка на включение режима обучения
	for (byte i=0;i<maxstack;i++) buttstack[maxstack-i]=buttstack[maxstack-i-1]; // суем в стек LIFO код клавиши
	buttstack[0]=code;  
	//если в стеке последовательность 3-х разных кнопок 3 раза, то переход в режим программирования
	if ((buttstack[0]==buttstack[3]) && (buttstack[0]==buttstack[6]) && (buttstack[1]==buttstack[4]) && (buttstack[1]==buttstack[7]) && (buttstack[2]==buttstack[5]) && (buttstack[2]==buttstack[8]) && (buttstack[0]!=buttstack[1]) && (buttstack[0]!=buttstack[2]) && (buttstack[1]!=buttstack[2])) { // переход в режим программирования
	digitalWrite(led, HIGH);
	flaglearn=true;
	for (byte i=0;i<maxstack+1;i++) buttstack[i]=0;// чистим стек
	maxpults=0;
	} 
}

void loop() {              
	if (VikState != digitalRead(2)) { // если изменилось состояние кнопки выключателя
		YesRelay(); 
		VikState=!VikState;
	}
	if (irrecv.decode(&results)){
		// если флаг обучения взведен, входим в режим обучения пульта
		if (flaglearn){ 
			butt[maxpults]=results.value;
			// режим обучения отключаем при дублировании последней кнопки
			if ((maxpults>0) && (butt[maxpults]==butt[maxpults-1]) || (maxpults==maxbut-1)) {
				flaglearn=false;
				digitalWrite(led, LOW);
				for (byte i=0;i<maxpults;i++) EEPROM_write(i*4, butt[i]); //записываем коды в EEPROM
				butt[maxpults]=0;
				EEPROM_write(maxpults*4, butt[maxpults]); //   
			}
			else maxpults++;
		}
		// обычный режим ловли кода кнопки пульта
		else{ 
			if (results.value!=0) iflearn(results.value);
			for (byte i=0;i<maxpults;i++){ // сравним полученный код с каждым из пультов
				if (results.value==butt[i]){ // если код совпал, переключаем реле
					YesRelay();
					break; //  и выходим из цикла
				}
			}  
		}
		delay(400); // убираем дребезг  
		irrecv.resume();
	}
}
+
avatar
  • klop
  • 04 марта 2016, 08:44
0
Еще уж замените if (VikState != digitalRead(2)) на if (VikState != digitalRead(VIK))
))
+
avatar
  • llams
  • 04 марта 2016, 09:30
0
Ааа, точно. Пропустил.
+
avatar
  • ACE
  • 05 марта 2016, 17:07
0
В прошлый раз уже писали, но повторюсь.
pinMode(VIK, INPUT); // вход выключателя
digitalWrite(VIK, HIGH); // включаем подтягивающий резистор
У вас, судя по схеме, уже есть внешний подтягивающий резистор на 10К, причём к минусу (GND). А вы программно включаете внутренний подтягивающий резистор, который тянет к плюсу питания. Сопротивление внутреннего резистора достаточно велико и не влияет на работу, пока. Но лучше так не делать. Ну и для понимания на будущее.

А так отлично, у меня похоже сделано, в быту очень удобно оказалось.
+
avatar
  • Scr77
  • 23 апреля 2019, 11:27
0
Вот с двумя переключателями, но что-то с выключателями не до конца получилось.
Если кто может подсказать — подскажите пожалуйста.

#include <IRremote.h>
#include <EEPROM2.h>

#define RECV_PIN 8
#define led 13
#define DP1 7
#define DP2 6
#define VIK1 2
#define VIK2 3

byte maxpults;
IRrecv irrecv(RECV_PIN);
decode_results results;

#define maxstack 8
#define maxbut 3 

unsigned long butt[maxbut]; 
unsigned long buttstack[maxstack+1];

bool Lamp1State = false;
bool Vik1State;
bool Lamp2State = false;
bool Vik2State;
bool flaglearn = false;
byte j = 0;

void setup() 
{       irrecv.enableIRIn();
        pinMode(DP1, OUTPUT);
        pinMode(VIK1, INPUT);
        pinMode(DP2, OUTPUT);
        pinMode(VIK2, INPUT);

        Vik1State=digitalRead(VIK1);
        Vik2State=digitalRead(VIK2);
        pinMode(led, OUTPUT);    
        digitalWrite(led, LOW);   // гасим светодиод
        for (maxpults=0;maxpults<maxbut;maxpults++)
        {       EEPROM_read(maxpults*4, butt[maxpults]);
                if (butt[maxpults]==0) break;
        }
}
void YesRelay1() 
{       Lamp1State = !Lamp1State;
        digitalWrite(DP2,LOW);
        digitalWrite(DP1,Lamp1State);
}
void YesRelay2() 
{       Lamp2State = !Lamp2State;
        digitalWrite(DP1,LOW);
        digitalWrite(DP2,Lamp2State);
}
void iflearn(unsigned long code)
{       for (byte i=0;i<maxstack;i++) buttstack[maxstack-i]=buttstack[maxstack-i-1]; // суем в стек LIFO код клавиши
        buttstack[0]=code;  
        //если в стеке последовательность 3-х разных кнопок 3 раза, то переход в режим программирования
        if ((buttstack[0]==buttstack[3])&&(buttstack[0]==buttstack[6])&&(buttstack[1]==buttstack[4])&&(buttstack[1]==buttstack[7])&&(buttstack[2]==buttstack[5])&&(buttstack[2]==buttstack[8]) && (buttstack[0]!=buttstack[1]) && (buttstack[0]!=buttstack[2]) && (buttstack[1]!=buttstack[2]))
        { // переход в режим программирования
          digitalWrite(led, HIGH);
          flaglearn=true;
          for (byte i=0;i<maxstack+1;i++) buttstack[i]=0;// чистим стек
          maxpults=0;
        } 
}
void loop()               
{       if (Vik1State != digitalRead(VIK1))  // если изменилось состояние кнопки выключателя1
        {       YesRelay1(); 
                Vik1State =! Vik1State;
        }
        if (Vik2State != digitalRead(VIK2))  // если изменилось состояние кнопки выключателя2
        {       YesRelay2(); 
                Vik2State =! Vik2State;
        }        
        if (irrecv.decode(&results))
        {       // если флаг обучения взведен, входим в режим обучения пульта
                if (flaglearn) 
                {       butt[maxpults]=results.value;
                        // режим обучения отключаем при дублировании последней кнопки
                        if ((maxpults>0) && (butt[maxpults]==butt[maxpults-1]) || (maxpults==maxbut-1)) 
                        {       flaglearn=false;
                                digitalWrite(led, LOW);
                                for (byte i=0;i<maxpults;i++) EEPROM_write(i*4, butt[i]); //записываем коды в EEPROM
                                butt[maxpults]=0;
                                EEPROM_write(maxpults*4, butt[maxpults]); //   
                        }
                        else maxpults++;
                }
                // обычный режим ловли кода кнопки пульта
                else
                {   if (results.value!=0) iflearn(results.value);
                        for (byte i=0;i<maxpults;i++) // сравним полученный код с каждым из пультов
                        {        if (results.value==butt[i]) // если код совпал, переключаем реле
                                 {    if (j>2) j=0;
                                      switch(j)
                                      { case 0: digitalWrite(DP1, HIGH);
                                                digitalWrite(DP2, LOW);
                                                break;
                                        case 1: digitalWrite(DP1, LOW);
                                                digitalWrite(DP2, HIGH);
                                                break;
                                        case 2: digitalWrite(DP1, LOW);
                                                digitalWrite(DP2, LOW);
                                      }
                                      j++;  
                                      break;
                                 }
                        }   
                }
                delay(400); // убираем дребезг 
                irrecv.resume();
        }
}
+
avatar
  • Scr77
  • 23 апреля 2019, 12:19
0
ещё бы таймер отключения нагрузок сюда прикрутить — часа так на 2-4, вообще тогда супер было бы.
Вдруг так свет забыл выключить, а он сам потухнет по таймеру.
+
avatar
  • Scr77
  • 23 апреля 2019, 12:24
0
интересно, если прошивать эту прогу через ардуино на atmega8 влезет ли программа во флеш-память (8028 байт получилось с библиотеками)?