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

в 7:12, , рубрики: arduino, diy или сделай сам, метки:

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

Основой выключателя является недавно купленный мной улучшенный клон контроллера Arduino, продающегося под названием Carduino Nano V.7
image


Работает выключатель так:
Arduino с выхода D5 постоянно выдает ШИМ сигнал с частотой 976Гц и со скважностью 50%. К выходу
D5 через токограничивающий резистор подключен светодиод, излучающий световой сигнал в инфракрасном диапазоне. Фототранзистор подключенный к входу Arduino D2 детектирует
отраженный от руки ИК сигнал. Arduino получает ИК сигнал, проверяет его на достоверность и если сигнал из 20 идущих подряд импульсов соответствует частоте 976Гц, то тогда контроллер включает синий светодиод (L) на выходе D13 Arduino и начинает воспроизводить звуковой эффект через выход SPK контроллера. Все тоже самое происходит и при выключении светодиода (L).

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

Схема простая для ее сборки я использовал
1-Carduino Nano V.7
2-IR светодиод из старого пульта ДУ от телевизора, светодиод нужно запаять в термоусадку, во избежании бокового излучения
3-Фототранзистор LTR-3208E
4-Динамическая головка из детской игрушки
5-Резисторы 10к и 68ом

image

Как работает схема собранная мной на макетной плате, можно посмотреть на видео.

Код для Arduino Nano:

#include <TimerOne.h>     
#include <avr/delay.h>
#include <avr/pgmspace.h>
#include "fife.h"
#include "hi.h"

////////переменные проигрывателя////////////////////////////////////
#define speakerPin 11
volatile uint16_t sample=0;
volatile uint8_t lastSample, FirstSample;
volatile byte new_data,future_data,old_data;
volatile byte stat=0;
unsigned char *wave;
unsigned int length;
////////переменные выключателя////////////////////////////////////
uint8_t state = 0;
volatile uint16_t timerCount, lengthImpuls;
volatile uint16_t Counter=0;
////////////начальная предустановка///////////////////////////////
void setup() 
{ 
  pinMode(speakerPin, OUTPUT);    //выход на динамик
  digitalWrite(speakerPin, LOW);   // на всякий случай, что бы не спалить динамик
  pinMode(2, INPUT);              // Вход, к фототранзистору 
 // digitalWrite(2, HIGH);          //Подключить подтягивающий резистор
  pinMode(13, OUTPUT);              //Лампочка
  pinMode(5, OUTPUT);               //выход ШИМ на ИК светодод
  TCCR0B = TCCR0B & 0b11111000 | 3;  //частота ШИМ 1000Гц
  analogWrite(5,128 );               //запустить ШИМ
  attachInterrupt(0, Ir_sens, RISING); //внешнее прерывание по фронту
  Timer1.initialize(10);              //инициализация таймера  
  Timer1.attachInterrupt(callback);   //прерывание таймера
} 
////////////обработка прерывания по таймеру///////////////////////////////
void callback() { timerCount++; }
////////////обработчик внешнего прерывания///////////////////////////////
void Ir_sens()
{ 
lengthImpuls = timerCount;
timerCount=0;
Counter++; 
}
///////////////////////Обработчик прерывания по совпадению с OCR2///////////////////
ISR(TIMER2_COMPA_vect) 
{        
 switch (stat)
  { 
    case 0:{
              old_data = pgm_read_byte(&wave[sample]);
              OCR2A = old_data; 
              stat=1;
              ++sample;  
              if (sample == length) stat=4;
              future_data = pgm_read_byte(&wave[sample]); 
              new_data = (old_data+future_data)/2; 
            }  
      break;
    case 1:  {OCR2A=(old_data+new_data)/2; stat=2; } 
      break;  
    case 2:   {OCR2A = new_data; stat=3; }
      break;    
    case 3:   {OCR2A=(new_data+future_data)/2; stat=0; } 
      break;
    case 4:   if(lastSample==0) stat=5; else {--lastSample; OCR2A=lastSample;} 
      break;      
    case 5:   stopPlayback(); 
      break;
  }    
}
////////////главный цикл приграммы///////////////////////////////
void loop() 
{ 
 if(lengthImpuls>105 || lengthImpuls<99) Counter=0;  
 if(lengthImpuls>99 && lengthImpuls<105 && Counter>20) 
 {  
    state=~state; 
    digitalWrite(13, state);    
    if(state>0) play_wave((unsigned char *)hi,  hi_length);  
    if(state==0) play_wave((unsigned char *)fife,  fife_length);  
   _delay_ms(200);    

   while(Counter>10)  
       {
         if(lengthImpuls>105 || lengthImpuls<99) Counter=0; 
       }             
    lengthImpuls=0;  
  }
}
////////////начать воспроизведение///////////////////////////////
void play_wave(unsigned char *wave_data, unsigned int wave_length)
{
 wave=wave_data; 
 length=wave_length;
 startPlayback(); 
}

void startPlayback()
{
    sample=0;
    stat=0;
    
    ASSR |=(1<<AS2);

   TCCR2A |= ((1<<COM2B1)|(0<<COM2B0)|(1<<COM2A1)|(0<<COM2A0)|(1<<WGM21)|(0<<WGM20)); 
    TCCR2B = ((0 << CS22) | (0 << CS21) | (1 << CS20) | (0<<WGM22) | (1<<FOC2A) |  (1<<FOC2B)); 

    lastSample = pgm_read_byte(&wave[length-1]); 

    TCNT2 = 0; 
    TIMSK2|=(1<<OCIE2A);
    sei();
   
     for (int i=0; i <50; i++)
     {
       new_data=i;
         stat=2;
          sample = 0;         	
      _delay_us(1);
     }
   stat=0;  
}
////////////остановить воспроизведение///////////////////////////////
void stopPlayback()
{
    TIMSK2&=(0<<OCIE2A); 
    TCCR2B &=(0<<CS10);
}

Скачать исходники одним файлом

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

Автор: CyberLab


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js