Реле с дистанционным ИК управлением на ATtiny13A

Привет, Хабр!

Появилась необходимость выключения старой, но вполне рабочей акустической системы с пульта от телевизора, не вставая с дивана. Подумав, я решил использовать ИК приёмник, некогда выкрученный со старого телевизора. ИК приёмник оказался без опознавательных знаков. Определив выходы методом тыка выяснил, что он из серии TSOP4xxx, если верить картинке:



Погуглив и потренировавшись на Arduino UNO, используя этот код и удостоверившись в работоспособности датчика, я перешёл к переписыванию кода на ATtiny13. Перейдя к ней, понял, что очень сильно ограничен ресурсах, как во флеше, так и в оперативной памяти. По началу с трудом оптимизировал по размеру прошивки, контроллер все еще не работал, а когда понял, что памяти в коде используется намного больше 64 байт, пришлось конкретно взяться за оптимизацию. В итоге с горем пополам оптимизировал код и собрал прототип на макетке. Радовался, как ребенок! Оно мигало лампочками так, как мне надо.

Общий вид:
Макетка


Схема:
Спойлер


Пришло время переводить всю макетку в текстолит. Плату изготовлял методом ЛУТ. Первый блин, как говорится, комом. Сделав первую схему, напечатав и собрав, понял, что ничего не работает. Я неправильно подключил LM317T. К тому же, поломав пятаки и оторвав некоторые слишком тонкие дорожки, решил сделать вторую плату. В ней сделал дорожки 0,7мм и, увеличив пятаки, кое как справился с частью проблем. Тут тоже не обошлось без проблем, так как опять неправильно подключил LM317T, да еще и в прошлой версии платы сжег приёмник, подав на него 12В.

Кстати, питается это дело от 12В (был у меня маломощный трансформатор, вот его и задействовал). Выбор напряжения был обусловлен так же имеющимся в наличии реле на 12В. Для понижения напряжения для микроконтроллера до 5В используется стабилизатор LM317T, а для управления реле используется имеющийся под рукой npn транзистор КТ819.

Окончательная плата в SprintLayout:
Спойлер


Используемые детали:
  • Микроконтроллер ATtiny13A;
  • Резисторы номиналом 470, 1300, 2x330 и 90 Ом;
  • Транзистор КТ819;
  • Стабилизатор LM317T;
  • 2 светодиода красный и зеленый;
  • Приёмник серии TSOP4XXX или совместимый;
  • Конденсатор примерно на 200-220мкФ;
  • Диод 1N4001 или аналог.


Что касается кода.

Исходный вариант был очень «увесистый» и на ATtiny13 ни как не мог работать. Необходимо было избавиться от тяжелого двумерного массива. Код был очень странным: записывались так же «низкие» импульсы, но они никак не использовались. В общем, выкинул двумерный массив и этим освободил как минимум 64 байта оперативной памяти. Высчитывал сигналы на лету, но этого было мало и после добавления функционала таймера пришлось максимально урезать переменные.

Код для Arduino IDE
#define IRpin_PIN PINB
#define IRpin 2

#define rLedPin 3
#define gLedPin 4
#define relayPin 1

#define MAXPULSE 5000
#define NUMPULSES 32
#define RESOLUTION 2

#define timeN1 1800000
#define timeN2 3600000

#define timerInterval 500


bool relayState = false;
unsigned long timer = 0;
unsigned long shift = timeN1;//30 min timer by default
unsigned long previousMillis = 0;
bool timerN = false;
byte i = 0;


void setup() {
  //default states
  DDRB |= (1<<relayPin);
  DDRB |= (1<<rLedPin);
  DDRB |= (1<<gLedPin);

  PORTB &= ~(1<<relayPin);//relay off
  PORTB &= ~(1<<rLedPin);//red led off
  PORTB |=  (1<<gLedPin);//green led on

  /*
  //for debug
  Serial.begin(9600);
  Serial.println("Start | "+String(millis()));
  //*/

  /*
  //for debug without ir receiver
  pinMode(5, INPUT);
  pinMode(6, INPUT);
  //*/


}

void shutDown(){
  relayState = true;
  PORTB |=  (1<<relayPin);
  PORTB &= ~(1<<gLedPin);
  PORTB |=  (1<<rLedPin);
  //Serial.println("turining off |"+String(millis()));
}

void startUp(){
  relayState = false;
  PORTB &= ~(1<<relayPin);
  PORTB |=  (1<<gLedPin);
  PORTB &= ~(1<<rLedPin);
  //Serial.println("turining on |"+String(millis()));
}

void loop() {
  unsigned long irCode = listenForIR(); // Wait for an IR Code
  //Serial.println("ir code: "+String(irCode));
  if(irCode == 3359105948){//green button
    //Serial.println("Pressed green btn |"+String(millis()));
    if(timer == 0){//on off mode
      if(relayState == true){
        startUp();
      }else{
        shutDown();
      }
    }else{//cancel timer mode
      timer = 0;
      PORTB &= ~(1<<rLedPin);//turn off red led
      //Serial.println("timer canceled |"+String(millis()));
    }
  }//end green btn


  if(3359101868 == irCode){//red btn
    //Serial.println("pressed red btn |"+String(millis()));
    if(timer == 0){
      if(relayState == 0){
        timer = millis();
        //Serial.println("timer started |"+String(millis()));
      }/*else{
        Serial.println("already shutdown |"+String(millis()));
      }
      //*/

    }else{//changing time mode
      timerN = !timerN;
      if(timerN){
        //Serial.println("change 30sec |"+String(millis()));
        shift = timeN1;//30 min
      }else{
        //Serial.println("change 60sec |"+String(millis()));
        shift = timeN2;//60 min
      }
    }
  }//end red btn
} // loop end

void checkTimer(){
  unsigned long time = millis();
  if(time - previousMillis >= timerInterval || previousMillis > time ) {
    previousMillis =time;
    timer1();
  }
}

unsigned long  listenForIR() {// IR receive code
  byte currentpulse = 0; // index for pulses we're storing
  unsigned long irCode = 0; // Wait for an IR Code
  irCode = irCode << 1;

  while (true) {
    unsigned int pulse = 0;// temporary storage timing
    //bool true (HIGH)
    while (IRpin_PIN & _BV(IRpin)) { // got a high pulse (99% standby time have HIGH)
      if(++i > 150){//check timer every 150 iterations (high frequency break ir code timing)
        i = 0;
        checkTimer();
      }
      pulse++;
      delayMicroseconds(RESOLUTION);
      if (((pulse >= MAXPULSE) && (currentpulse != 0)) || currentpulse == NUMPULSES ) {
        return irCode;
      }
    }

	//make irCode
    irCode = irCode << 1;
    if ((pulse * RESOLUTION) > 0 && (pulse * RESOLUTION) < 500) {
      irCode |= 0;
    }else {
      irCode |= 1;
    }
    currentpulse++;
	pulse = 0;
        //bool false (LOW)
    while (!(IRpin_PIN & _BV(IRpin))) {//wait before new pulse
      //checkTimer();
      pulse++;
      delayMicroseconds(RESOLUTION);
      if (pulse >= MAXPULSE || currentpulse == NUMPULSES ) {
        //Serial.println(irCode);
        return irCode;
      }
    }
  }//end while(1)
  
}//end listenForIR 


//executing every timerInverval
void timer1() {
  if(timer != 0){
    if(timerN == true){//timeN1 or timeN2
      PORTB |= (1<<rLedPin);
    }else{//blinking 30min	
      PORTB ^= (1<<rLedPin);//invert
    }
    //Serial.println(String((timer+shift - millis())/1000));
  }

  if(timer != 0 &&(timer+shift < millis() || timer > millis())){
    timer = 0;
    shutDown();
  }
}



Видеодемонстрация:



Прошивал ATtiny13 я с помощью Arduino UNO, используя его как программатор, руководствуясь публикацией «Прошивка и программирование ATtiny13 при помощи Arduino». Для прошивки использовал 9,6МГц конфигурацию.

Фотографии почти не делал, но что есть, то есть:
Фото
Из за другой распиновки запасного TSOP на второй версии, пришлось его перенести на проводки и закрепить клеем (позже просто прикрепил к корпусу).

Вторая плата сбоку:



Вторая плата сверху (ик датчик перенес):



Вторая плата снизу:



Конечное устройство:




Исходники и прошивка на яндекс диске.

Используемые материалы


myrobot.ru/wiki/index.php?n=Components.TSOP Всё об ИК-приёмнике «TSOP»
www.atmel.com/images/doc2535.pdf Даташит по ATtiny13
habrahabr.ru/post/234477 Инструкция по прошивке ATtiny13
payalo.at.ua/c_fuse/calc.html?part=ATtiny13A калькулятор фьюзов для ATtiny13
github.com/nathanchantrell/TinyPCRemote/tree/master/TinyPCRemote_CodeReader, код читалки кодов пульта который я взял за основу.

//UPD: Обновил схемы. Заменил конденсатор на реле диодом.
Поделиться публикацией

Похожие публикации

AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 39

    +5
    Конденсатор параллельно обмотке реле зачем? 8)
      +4
      Там еще R5 мелковат, как будто КТ819 — полевик, а не биполярник, а на место конденсатора C2 просится диод в обратном включении, чтобы КТ819 раньше времени не упокоился.
      Также не ясно, почему использована LM317 вместо 7805.
        +1
        LM317 был под рукой на момент идеи.
        Я подумал о диоде уже позже. Еще всё можно исправить.
        По поводу R5 можно подробнее? Я пока что учусь всему этому.
        mlu, кондер поставил что бы не так громко щелкало (мне показалось что помогло).
          0
          Обратитесь к учебному курсу, который подготовил dihalt:
          Управление мощной нагрузкой постоянного тока: 1, 2, 3
            0
            За ссылки спасибо! Посмотрю обязательно.
            0
            Кстати, судя по всему, конденсатор — электролит, да еще его при каждом выключении реле по нему обратной полярностью шарашит… Нехорошо это. Маловероятно, что несчастный рванет, но может и потечь когда-нибудь.
              0
              Да, это проблема. Пока как фикс можно конденсатор заменить диодом. Аппарат редко выключается по реле и думаю что ничего не случится, но для часто используемых вариантов надо будет пофиксить.
                0
                Обратным током, но не полярностью! Импульса обратного выброса не хватит чтобы переполюсовать конденсатор. А вот при открытии реле транзистор будет работать чисто на ESR конденсатора, и если поставить отличный электролит… то транзистор может вышибить. Но это не случится — источник питания слишком слаб чтобы обеспечить ток для пробоя динозавра КТ819, этому еще будет способствовать ограниченный ток базы…

                И кстати, КТ819 пишется без черточки.
                  0
                  Тогда и LM317 без неё. Спасибо.
                    0
                    Скажем так, спорить о том, кто в переходном процессе главнее (напряжение или ток), это все равно что спорить о первичности курицы или яйца (эти величины взаимосвязаны и рассматривать их нужно совместно, не зря же придумали использовать комплексные числа для этой цели). Из той же серии вопрос о том, что поражает человека — ток или напряжение.

                    Неприятность в том и состоит, что по конденсатору шарахают при каждом переключении реле. В сочетании с маленьким сопротивлением R5, мы имеем недостаточно открытый транзистор. Недооткрытый транзистор и конденсатор создают RC-цепь, которая растягивает включение реле, что автор и заметил как менее интенсивное щелканье при срабатывании.

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

                      +1
                      Фактически С2 с катушкой реле представляют собой контур, но добротность у него низкая — звенеть будет, но не так хорошо как голое реле.
                      Плавное включение и отключение реле кстати чревато быстрым износом контактов под действием тока — искрить будет дольше и контакты быстрее подгорят. На высоковольтных выключателях так там вообще стараются размыкать его на сверхзвуковых скоростях, из-за чего звук размыкания линейного выключателя 330КВ линии подобен пушечному выстрелу и слышен за пару километров.
            +1
            Тоже писал свой велосипед с декодированием команд пульта. Правда на ассемблере, код выполнялся в прерывании от TSOPа, ел пару байт ОЗУ, и никак не мешал контроллеру выполнять другие функции. Правда код «не по феншую», т.е. не по стандарту, просто снимал битовый слепок команды, а дальше он неспешно сравнивался с такими же слепками, снятыми в режиме обучения.
            Со всеми пультами, что были дома, работало. Точнее и девайс и сейчас работает.
              0
              Ассемблер у меня только в планах. Я сильно избалован perl, php, js. Про стандарт поверхностно почитал, а потом просто разобрался как готовый код работает и доработал для младшей модели, убрав всё лишнее.
              +2
              Можно было не играть в спарту, а взять тини45 или 85. Ну и диодик поставь на реле, а то у тебя контроллер вышибить может.
                +4
                Это увлекательно и хорошо дисциплинирует. Я получил удовольствие от разработки.
                  +4
                  И это самое главное. Если искать лёгкие пути, то проще купить готовый девайс у китайцев баксов за 10.
                    0
                    Баксов за 10 у китайцев будет что-то сильно круче :) Я ногда поражаюсь что у них за 10 баксов бывает :)
                      +1
                      Насколько круче, настолько же и глючней.
                  0
                  ну все таки сначала транзистор вышибет, а дальше как повезет.
                    0
                    МК оказался довольно живучим. Я уже на ту самую ногу управляющую транзистором подавал 12В. ATtiny нагрелась сильно, но ничего не страшного не случилось, хотя я немного испугался.
                      0
                      Очень может быть, что выгорели защитные диоды, оберегающие контроллер от статического электричества (подключены от каждой ноги на плюс и минус питания).
                        +2
                        Можно проверить :) Запитать пустой МК через GPIO и попробовать считать фуз биты.
                        0
                        Скорей всего случилось, просто внешне это незаметно. Уже сколько раз с такими микросхемами сталкивался — они работают, но у них наблюдается повышенный ток потребления — видать часть кристалла повреждена и ток просто протекает по неиспользуемым структурам. Когда такой ток будет приличным, со временем может выжечь до чего-то важного и перестанет работать.
                          0
                          Если что заменю другой микросхемой — у меня таких еще 9 штук. Надо же сжечь хоть какой то мк первый раз, а программатор с закрытыми глазами собираю уже. Думаю потом шилд сделать для программирования в виде Arduino as ISP.
                        0
                        С электролитом параллельно реле не вышибет. Мог бы вышибить при включении но слабый источник питания не даст — просто просядет.
                      0
                      Я обычно «прижимаю» Reset к +5В при помощи резистора на 10 кОм чтобы избежать невольных перегрузок микроконтроллера.
                      А ещё можно подцепить керамику на 0.1 мкФ(100 нФ).

                      image

                      А то микроконтроллер будет чуть что сразу «ловить» ресет, слегка коснулся чем-то порт Reset и перезагрузка МК гарантирована.
                        0
                        Напомнило универсальную платку одного хорошего дядьки. Посмотрите, может понравится или полезного чего почерпнете как из статьи и кода, так и из всего сайта. Сайт, как и дядька, весьма толковые.
                          +3
                          Если приёмник на окончательной плате можно развернуть, то минус две перемычки:

                          Если нельзя — то минус одна.
                            0
                            Спасибо. Обновил схемы. Перевернуть скорее всего нельзя т.к. приемник будет смотреть вглубь платы. Конечно можно согнуть ножки и повернуть, но зачем? Единственное что, можно было светодиоды и приёмник попробовать поменять местами.
                              0
                              Просто на видео приёмник смотрит вверх, т.е. ему всё равно, в какую сторону он впаян — поэтому и уточнил «если можно».
                                0
                                Понятен ваш ход мыслей. Я сначала хотел сверху поставить маленькую коробочку с приёмником, но решил что можно попробовать поместить в корпус и пихнул всё в окошко от сломанного переключателя стерео/моно. Правда пришлось все хомутами обмотать и изолентой.
                            0
                            Хочу реализовать почти идентичный проект — управление Н-мостом (драйвер двигателя TA7291P) при помощи пульта. При помощи примера IRrecvDemo узнал 16-ричные коды кнопок вверх и вниз: 0x40BFA857 и 0x40BF8877 соответственно. Перевёл в 10-ричную систему, получилось 1086302295 и 1086294135. Изменил соответственно код, 3-я нога приёмника у меня на 3-м пине (PINB4), получилось:
                            #define IRpin 4
                            

                            Записал прошивку — ноль реакции. К стати, прошиваю через USBasp программатор скомпилированный Arduino .hex файл. Пробовал устанавливать фьюзы на 9.6МГц:
                            avrdude -c usbasp -p t13 -U lfuse:w:0x7a:m
                            avrdude -c usbasp -p t13 -U hfuse:w:0xff:m
                            

                            Подскажите как можно отладить прошивку, хотя бы узнать в какие блоки кода выполняются, а какие нет? Может быть проблема банальная…
                              0
                              Добавил в качестве отладки мигание диодом остатка от деления на 10, в итоге диод начинает мигать как только я нажимаю на клавишу на пульте, но, вот что интересно, намигивает мне значение 2147483648 — т.е. минимальное значение signed long (какую бы кнопку я бы не нажимал), хотя irCode определено как:
                              unsigned long irCode = 0; // Wait for an IR Code
                              

                              Такое впечатление, что irCode где-то переваливает за максимальное значение unsigned long.
                                0
                                Долго мучался, но получилось!
                                Приведу только конечный код функции ожидания сигнала:

                                Функция listenForIR
                                unsigned long listenForIR() {
                                  byte currentpulse = 0;
                                  unsigned int pulse;  // temporary storage timing
                                  unsigned long irCode = 0;
                                
                                  while (true) {
                                    pulse = 0;
                                    while (IRpin_PIN & _BV(IRpin)) { // got a high pulse
                                      pulse++;
                                      delayMicroseconds(RESOLUTION);
                                      if (((pulse >= MAXPULSE) && (currentpulse >= 0)) || currentpulse == NUMPULSES) {
                                        if (currentpulse > 3) {
                                          if (lastcode != irCode) prevcode = lastcode;
                                          lastcode = irCode;
                                        } else if (currentpulse > 0) irCode = lastcode;
                                        return irCode;
                                      }
                                    }
                                    if (currentpulse > 1) {
                                      irCode = irCode << 1;
                                      if (pulse > 0 && (pulse * RESOLUTION) < 60) {
                                        irCode |= 0;
                                      } else {
                                        irCode |= 1;
                                      }
                                    }
                                    pulse = 0;
                                    while (!(IRpin_PIN & _BV(IRpin))) { // got a low pulse
                                      pulse++;
                                      delayMicroseconds(RESOLUTION);
                                      if (((pulse >= MAXPULSE) && (currentpulse >= 0)) || currentpulse == NUMPULSES) {
                                        if (currentpulse > 3) {
                                          if (lastcode != irCode) prevcode = lastcode;
                                          lastcode = irCode;
                                        } else if (currentpulse > 0) irCode = lastcode;
                                        return irCode;
                                      }
                                    }
                                    currentpulse++;
                                  }
                                }

                                  0
                                  Я так понимаю вы изменили resolution? Сейчас не могу сравнить толком с кодом из моего проекта по причине маленького экрана телефона и службы в армии. Возможно мой вариант либо быстрее либо дольше считывает код кнопки. Надо будет поэксперементировать потом, но это уже летом.
                                    0
                                    Да, методом научного тыка, а вернее методом научного мигания диодом отладочной информации (кол-во тактов между импульсами), было установлено, что значение нужно скорректировать, возможно это из-за того, что мне не удалось прошиться на 9.6МГц (хотя я вроде как попытался установить фьюзы — видимо безрезультатно).
                                      +1
                                      Кстати, я тогда 9.6МГц использовал из за того что в мк что то сжег и фьюзы не шились, а мк надо куда то применить. Хорошая пища для размышления, надо бы сократить потребление тока.
                                        0
                                        Сжёг фьюзы? Это ж на до так.

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                              Самое читаемое