Бесконтактный выключатель освещения со звуковым эффектом на Arduino

Всем привет!
Сегодня я Вам расскажу про бесконтактный выключатель с звуковым эффектом, который был сделан мной 9 лет назад, а если быть точным то в январе 2012 года. Ссылку на ролик 2012 г. я выложил в комментарии под этим видео.
С тех пор выключатель трудится у меня круглыми сутками на протяжении 9 лет. Что самое интересное он За все это время, он не выходил из строя и даже ни разу не подвис, а также у него никогда не было ложных срабатываний. Вообщем он хорошо себя зарекомендовал и я с уверенностью могу его Вам рекомендовать для самостоятельной сборки.
Если Вам интересны все подробности, то прошу посмотреть этот ролик до конца и оценить его подпиской и лайком.

У меня в коридоре смонтировано 7 светильников.

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

Саму плату я спрятал в пространстве между гипсокартоном и потолком, благо места там больше чем достаточно.

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

Для того что бы дизайн выключателя не отличался от других установленных декоративных накладок в интерьере, я использовал из этой же серии телевизионную розетку, из которой выкинул все внутренности, а в отверстие вклеил круглое окошко вырезанное из фиолетового акрила.

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

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

Основой всего устройства является контроллер arduino Nano V.3, но можно так же использовать любые другие платы, с микроконтроллером Atmega328

ИК светодиод с фототранзистором можно взять от датчика препятствий, но не обязательно их выпаивать, достаточно перерезать лишние дорожки и припаять к ним 3 провода. Если у Вас есть откуда нибудь эти ранее выпаянные детали, то перед использованием, лучше сначала проверить их на работоспособность. Инфракрасный светодиод подключите к напряжению 5 В, через токоограничивающий резистор 120 Ом и посмотрите на него через камеру телефона, он должен светиться фиолетовым светом. Для проверки фототранзистора понадобится любой тестер с функцией прозвонки проводников. Переводим тестер в режим прозвонки, а выводы фототранзистора подключаем к щупам тестера. После чего нужно к нему в плотную поднести любой пульт от бытовой техники и нажать любую кнопку. В ответ раздастся прерывистый пищащий звук.

9 лет назад я не нашел подходящих твердотельных реле и мне пришлось их собирать самому из рассыпухи. Но на данный момент проще купить 8 канальный модуль твердотельных реле как на изображении, чем заниматься тратой времени на поиск этих компонентов.

Работает выключатель следующим образом
Arduino с выхода D5 постоянно выдает ШИМ сигнал с частотой примерно 977 Гц. К этому выходу через токоограничивающий резистор 82 Ом подключен светодиод, излучающий сигнал в инфракрасном диапазоне. Фототранзистор подключенный к входу D2 детектирует отраженный от руки ИК сигнал и проверяет его на достоверность и если сигнал из 20-ти или больше идущих подряд импульсов соответствует частоте 977 Гц, то тогда контроллер включает по очереди все 7 светильников и начинает воспроизводить звуковой эффект через ШИМ выход D11. Все тоже самое происходит и при выключении.

Воспроизведение звуков
Для воспроизведения звуковых эффектов используется формат WAV без сжатия, с частотой 16000 Гц и глубиной 8 бит , но при воспроизведении данного формата с использованием ШИМ, в аудио тракте наблюдается неприятный свист и шипение. По этому для для улучшения качества воспроизведения, я в коде использовал линейную интерполяцию. При которой, выборка семплов происходит на частоте 62.5 кГц и между оригинальными выборками вставляются еще 3 дополнительных семпла рассчитанных методом линейной интерполяции. Таким образом на выходе снижается шум квантования, пропадает свист, улучшается качество звука и для воспроизведения не обязательно использовать дополнительные RC фильтры.

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

Для конвертирования Wave файлов в Си код, можно воспользоваться онлайн конвертером

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

Компоненты для сборки
1 — Arduino Nano V.3
2 — Датчик препятствий
3 — 8-ми канальный модуль реле
4 — провода Dupont


Код для Arduino развернуть

#include "TimerOne.h"  
#include "PlaySound.h"   
#include "fife.h" // звуковой фаил тра-ля-ля
#include "off1.h" // звуковой фаил пиу

#define power_ir 180 // Мощность импульсов ИК передатчика, Влияет на расстояние срабатывания максимум=180. минимум=50. 
#define lamp_delay 200 // задержка между включениями ламп
#define lamp_num 7 // количество ламп от 1 до 7, от D13 до D19, если исп. 1 лампочка то она на D13 и т.д. в порядке увеличения

#define IR_send 5 // выход на ИК светодиод(передатчик). переназначать нельзя
#define IRrec 2 // выход на ИК фото-транзистор(приемник) переназначать нельзя


uint8_t state = 0; // статус выключателя
uint16_t Counter=0; // счетчик импульсов
volatile uint8_t timerCount=0; //счетчик длительности периода импульса
volatile uint8_t int_flag=0; // флаг или статус прерывания

void setup() 
{ 
  for (byte i=13; i<(lamp_num+13); i++) { pinMode(i, OUTPUT);}  // активируем выходы на Лампочки
  Lamp_Switch();                        //выкл свет  
  pinMode(IRrec, INPUT_PULLUP);          // Вход с подтягивающим резистором для фототранзистора
  pinMode(IR_send, OUTPUT);               //выход на ИК светодиод
  TCCR0B = TCCR0B & 0b11111000 | 3;     //задать частоту ШИМ 976.5625 Гц для ИК светодода
  analogWrite(IR_send,power_ir);          //запустить ИК передатчик 
  attachInterrupt(0, Ir_sens, FALLING); //внешнее прерывание по заднему фронту, так как фототранзистор инвертирует сигнал
  Timer1.initialize(102);               //инициализация таймера на 102мкс. если умножить на 10 то получим длительность периода частоты 980Гц
  Timer1.attachInterrupt(int_ir);    //обработчик прерываний таймера 
} 

void loop() 
{ 
    if( int_flag==3 ) //если было второе прерывание то проверяем длительность периода полученного импульса
    { 
      Timer1.restart();   // сбросить таймер счетчик на 0
      int_flag = 0;     // обнулить флаг прерывания
      if( timerCount<12 && timerCount>9 ) Counter++; else Counter=0; //если длительность в допуске то инкремент к счетчику импульсов
      timerCount=0;  // сбросить счетчик длительности периода импульса
    }  
    
  if( int_flag==1 ) //если было прерывание то начинаем отсчет длительности периода
  {
    Timer1.restart();   // сбросить таймер на 0
    int_flag=2;         // сбросить флаг прерывания
    timerCount=0;       // сбросить счетчик длительности периода импульса
  }
    
 
 if(Counter>20)     //если количество импульсов больше 20 то включить или выключить свет. 
  {                // Можно указать 200 но будет медленней срабатывать
    state=~state;   //статус выключателя
   // if(state>0) PlaySound::startPlayback((uint8_t *)fife, sizeof(fife));  //если включили, то тра-ля-ля 
    if(state==0) PlaySound::startPlayback((uint8_t *)off1,  sizeof(off1));  //если выключили, то пиу-ля-ля    
    Lamp_Switch();    // вкл/выкл свет   

    timerCount=0;   // сбросить счетчик длительности периода импульса
    Counter=0;      // обнулить счетчик импульсов
    int_flag = 0;     // обнулить флаг прерывания 
    Timer1.restart();  // сбросить таймер на 0
  }
}

//////////// обработка прерываний по таймеру ///////////////////////////////
void int_ir() 
{ 
  timerCount++;  //счетчик длительности периода
}  

//////////// обработчик внешнего прерывания ///////////////////////////////
void Ir_sens()
{ 
  int_flag++; // установить флаг прерывания
}

////////////// вкл/выкл свет //////////////////////////////////////
void Lamp_Switch()
{
  if(state==0)
  {
    for (byte i=13; i<(lamp_num+13); i++) 
    {
      digitalWrite(i, LOW);       //выключить свет
       delay(lamp_delay);
    } 
  }else{
         for (byte i=13; i<(lamp_num+13); i++) 
         {
           digitalWrite(i, HIGH);   //включить свет
            delay(lamp_delay);
         }  
        }       
}


Скачать скетч одним архивом

В этот раз я решил добавить все используемые библиотеки в папку со скетчем, а в самом скетче прописал их локальное использование. Теперь надеюсь у новичков будет меньше ко мне вопросов, по поводу ошибок при компилировании.
В коде вынесены несколько констант, которые можно изменить перед прошивкой.
Константа power_ir — отвечает за дистанцию срабатывания выключателя, может принимать значения от минимума 20 и до максимума 200. Требуемое Вам значение можно определить экспериментальным путем.
lamp_num — определяет количество используемых Вами ламп. Минимальное число лампочек не может быть меньше 1, а максимальное не более 7. Если подправить код то можно увеличить до 15.
lamp_delay — это задержка между последовательными включениями ламп, которая выражена в миллисекундах и может начинаться от 0 и до 4 294 967 295 мс. Хотя я не думаю, что такие огромные задержки кому то понадобятся )

Заключение.
В заключении хотелось бы добавить, что я очень удивлен, что микроконтроллер без WDT, за 9 лет ни разу не подвис. По этой же причине я не стал править код и добавлять в него WDT, так как Arduino со старыми bootloader не умеют работать с ним и их еще достаточно большое количество на руках у DIY сообщества

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

Поделиться
Отправить
Запинить
2 комментария
Горохов Валерий

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

Duino

Саму ошибку можете выложить?

yogesh Puri

Void Lamp_Switch (); code missing .
Please share full code. Thanks

Популярное