IR sensor к Arduino

igormwd
Offline
Зарегистрирован: 22.03.2013
int irPin     = 4;       //Sensor pin 1 wired to Arduino's pin D4

int start_bit = 4500;    //Start bit threshold (Microseconds)
int bin_1     = 1600;    //Binary 1 threshold (Microseconds)
int bin_0     = 560;     //Binary 0 threshold (Microseconds)

void setup() {
  pinMode(irPin, INPUT);

  Serial.begin(9600);
  Serial.println("IR/Serial Initialized: ");
}

void loop() {
  int key = getIRKey();   //Fetch the key
  Serial.println( key );
}

int getIRKey() {
  int data[33];
  int i;

  while(pulseIn(irPin, LOW) < start_bit); //Wait for a start bit
  
  for(i = 0 ; i < 32 ; i++)
    data[i] = pulseIn(irPin, HIGH);      //Start measuring bits, I only want low pulses
  
  for(i = 0 ; i < 32 ; i++) {           //Parse them	    
    //Serial.println( data[i] );          // debug
    if(data[i] > bin_1)                 //is it a 1?
      data[i] = 1;
    else if(data[i] > bin_0)            //is it a 0?
      data[i] = 0;
    //else
      //return -1;                        //Flag the data as invalid; I don't know what it is! Return -1 on invalid data
  }

  int result = 0;
  for(i = 0 ; i < 32 ; i++)             //Convert data bits to integer
    if(data[i] == 1) result |= (1<<i);

  return result;                        //Return key number
}

Задача такая: Нужно получать команды с пульта ДУ.  Пульт компании NEC.

Да, я знаю существую библиотеки например IRremote, но дело приобретает другой характер, цель программы перенести на Attiny85 - которая с этой библиотекой выдает кучу ошибок. Ниже код который без проблем выполняется на Attiny85, но вот декодирование не правильное. В интернете нашел протокол расшифровки сигнала NEC пультов(http://www.sbprojects.com/knowledge/ir/nec.php) но никак не могу разобраться..

 

maksim
Offline
Зарегистрирован: 12.02.2012

Нашел старенький неудаленный скейтч, в котором делал так:

int  i = 0;
int  BIT = 0;
boolean DATA = 0;
boolean COMANDS[6] = {
  1,1,1,1,1,1};
const boolean BASE[6][16] = {
  {1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1}, // OK
  {0,0,1,0,0,0,0,0,1,1,0,1,1,1,1,1}, // ESC
  {1,1,0,1,0,0,0,0,0,0,1,0,1,1,1,1}, // UP
  {0,0,0,0,0,0,1,0,1,1,1,1,1,1,0,1}, // DOUN
  {1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1}, // LEFT
  {0,0,1,1,0,0,0,0,1,1,0,0,1,1,1,1}, // RIGHT
};


void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
}

void loop() {
  RECIVE();
}

void RECIVE() {
  if(pulseIn(2, CHANGE)){
    i = 0;
    while(i < 8){
      BIT = pulseIn(2, CHANGE);
      if(BIT  > 300 && BIT < 700){
        i++;
      }
      else{
        return;
      }
    } 
    i = 0;
    while(i < 8){
      BIT = pulseIn(2, CHANGE);
      if(BIT  > 1400 && BIT < 1800){
        i++;
      }
      else{
        return;
      }
    } 
    i = 0;
    while(i < 16){
      BIT = pulseIn(2, CHANGE);
      if(BIT  > 300 && BIT < 700){
        DATA = 0;
      }
      if(BIT  > 1400 && BIT < 1800){
        DATA = 1;
      }
      DECOND();
      i++;
    }
    COMAND();
  }

}


void DECOND() {
  int j = 0;
  while(j < 6){
    if(BASE[j][i] != DATA){
      COMANDS[j] = 0;
    }    
    j++;
  }
}

void COMAND(){
  digitalWrite(13, 1);
  int i = 0;
  while(i < 6){
    if(COMANDS[i] == 1){
      Serial.println(i, DEC);
    }
    COMANDS[i] = 1;
    i++;
  }
  digitalWrite(13, 0);
}

Так же рекомендую отказаться от pulseIn() и перейти на прерывание и micros(). Да и еще вспомнил у этого пульта была приамбула 8 нулей, 8 единиц а только потом 16 бит данных.

igormwd
Offline
Зарегистрирован: 22.03.2013
pinMode(13, OUTPUT);

как я понял это у вас отправка через МК, а мне получать надо..

maksim
Offline
Зарегистрирован: 12.02.2012

Вы плохо поняли. А еще раз и на всегда запомните - все выводы МК по умолчанию настроены на вход (с дуинами есть исключения).

igormwd
Offline
Зарегистрирован: 22.03.2013
pulseIn(2, CHANGE)

maksim, извиняюсь, уже все в голове перемешалось, уже сутки как не сплю, наверно мне поспать нажно пойти..

maksim
Offline
Зарегистрирован: 12.02.2012

На вашем месте я бы сделал как-то так:

volatile uint32_t comand = 0;

void setup() 
{ 
  digitalWrite(2, 1);  
  attachInterrupt(0, recive, CHANGE);
  Serial.begin(9600);
}

void loop() 
{
  if(comand) 
  {
    Serial.println(comand, HEX);  
    comand = 0;
  }
}

void recive()
{
  static bool buff[32];
  static byte i;
  static uint32_t time_prev;
  uint32_t time = micros()-time_prev;
  time_prev = micros();
  if(time > 4000 && time < 5000) i = 0; 
  else 
  {
    i = 100;
    return;
  }
  if(time  > 300 && time < 700) buff[i] = 0;
  else if(time  > 1400 && time < 1800) buff[i] = 1;
  else 
  {
    i = 100;
    return;
  }
  i++;
  if(i == 32) 
  {
    for(i = 0 ; i < 32 ; i++) if(buff[i]) comand |= (1<<i);
    i = 100;
  }
}

 

igormwd
Offline
Зарегистрирован: 22.03.2013

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



int start_bit = 2200;    //Start bit threshold (Microseconds)
int bin_1     = 1000;    //Binary 1 threshold (Microseconds)
int bin_0     = 400;     //Binary 0 threshold (Microseconds)

это протокол Sony (http://www.sbprojects.com/knowledge/ir/sirc.php) - то мой код куда быстрее на кнопки пульта реагирует..

igormwd
Offline
Зарегистрирован: 22.03.2013

maksim, ваш последний код вообще не срабатывает....

maksim
Offline
Зарегистрирован: 12.02.2012

А должен? ... как-то так ... это означает что вот вам пример как реализовать прием на прерываниях, вы ведь даже не знаете какие на самом деле интервалы времени у вашего пульта и не знает длинну пакета.

igormwd
Offline
Зарегистрирован: 22.03.2013

пакет найти то я нашел..

 

619212
8960
4468
588
264
868
256
868
264
820
256
880
864
832
876
268
852
564
165
224
272
538
852
902
610
39796
8980
2236
596
276
412
428
252
420

вот он от 4468 до 39796, и написан фильтрацию чтобы я только работал именно с этим диаппазоном. вот что у меня получилось

volatile uint32_t comand = 0;
int i = 0;
bool IsStart, IsEnd = false;
byte buff[32];
String StrText;

void setup() { 
  digitalWrite(2, 1);
  attachInterrupt(0, recive, CHANGE);
  Serial.begin(9600);
}

void loop() {
  if(comand) {
    Serial.println(comand);
    comand = 0;
  }
  if(StrText != "") {
    Serial.println(StrText);
    StrText = "";
  }
}

void recive() {
  static uint32_t time_prev;
  uint32_t time = micros()-time_prev;
  time_prev = micros();
  StrText = String(time);
  
  if(time > 4000 && time < 5000 && IsStart == false) {
    IsStart = true;
    IsEnd = false;
    i = 0;
  }
  if(time > 39000 && IsEnd == false) {
    IsStart = false;
    IsEnd = true;
  }
  
  if(IsStart == true && IsEnd == false) {

    //StrText = String( time );
    if( time > 1000 ) {
      //buff[i] = 1;
      //comand = 1;
      i++;
    } else {
      //buff[i] = 0;
      //comand = 1;
      i++;
    }
    
    //StrText = String( i );
  }
  
  //StrText = String( i );
  
  
  
  
//  if(IsStart == false && IsEnd == true ) {
//    for(int j = 1 ; j < 30 ; j++) {
//      if(buff[j] == 1) {
//        comand += 1;
//      }
//    }
//    IsStart = IsEnd = false;
//  }
  
  
}

 

но никак понять не могу, хочу сделать вывод переменной i - он выводит бред!!! до 22 норм ально нумерует а потом 25, 28 и далее хаотично. пробовал переменную i засунуть в ОЗУ (volatile) - такой же результат...............

step962
Offline
Зарегистрирован: 23.05.2011

Вы инициализировали последовательный порт со скоростью 9600 бит/с - то есть около 1000 символов/сек.

если состояние отслеживаемого сигнала меняется со скоростью не более 1000 фронтов в секунду, то вы будете более-менее успевать пропихивать всю генерируемую информацию через com-порт, да и то при условии, что при каждом вызове Serial.print отправляется один символ.

А если фронтов больше, чем 1000 в секунду?

А если отправляется чуть больше одного символа (например Serial.println() отправит как минимум три символа - собственно символ и еще байты CR и LF)?

И в том и в другом случае постепенно буфер COM-порта переполнится и начнутся пропуски в передаче. И вы получите то, что получили.

Так что попробуйте поднять скорость передачи - ну хотя бы до 19200.

igormwd
Offline
Зарегистрирован: 22.03.2013

step962, спасибо за подробные разьяснения, поднял до 57600 чтобы наверняка и правда ведь, а я совсем что то на это внимания и не обратил..

igormwd
Offline
Зарегистрирован: 22.03.2013

скетч написал, на самой ардуине отлично работает!!!!!!!! теперь буду переносить на Attiny85, вчера правда перенес но понял что с 8Mhz - не катит, не все данные  в порт выводит хоть даже и со скоростью 115200, буду добавлять кварцевый резонатор в схему на 16 или 20 Mhz, как все сделаю отпишусь с примерами...

igormwd
Offline
Зарегистрирован: 22.03.2013

Итак...

Скетч написал, как и обещал размещаю... Работает он отлично только с дополнительным кварцевым резонатором на 20Mhz, т.к. 8Mhz встроенный - выводил каждый раз новые результаты, по всей видимости не успевал принимать столь крутейший поток данных. Опять хочу сказать, в этом деле я не профи и вообще я только месяца 3 с ардуино в руках, вообще в целом электроника это как хобби у меня, в детстве игрался и вот сейчас решил поиграться после 12 лет спустя...

для подключния квацевого вот схема:

 

С1 и С2 = 22pF. Оказывается без них о вообще ведет себя как хочет.. XTAL2 - PB3(2 ножка), XTAL1 - PB4(3 ножка)

н и сам скетч, вставляю самую последнюю свою рабочую версию



#include <SoftwareSerial.h>

//SoftwareSerial mySerial(0, 1);

volatile uint32_t comand = 0;
bool IsStart, IsEnd = false;
int PinLed = 0;

void setup() { 
  //mySerial.begin(57600);
  pinMode(PinLed, OUTPUT);
  digitalWrite(PinLed, HIGH);
  
  digitalWrite(2, HIGH);
  attachInterrupt(0, recive, CHANGE);
  
  delay(1000);
  digitalWrite(PinLed, LOW);
}

void loop() {
  if(comand != 0 && IsStart == false && IsEnd == false ) {
  //if(comand != 0) {
    if( comand == 2818572970 )
      digitalWrite(PinLed, HIGH);
    if( comand == 671122090 )
      digitalWrite(PinLed, LOW);
    //mySerial.println(comand);
    comand = 0;
  }
}

void recive() {
  static uint32_t time_prev;
  uint32_t time = micros()-time_prev;
  time_prev = micros();
  
  //comand = time;
  
  if( time > 4000 && time < 4600 && IsStart == false && IsEnd == false ) {
    IsStart = true;
    IsEnd = false;
    //comand = time;
    return;
  }
  
  if( time > 3000 && IsStart == true && IsEnd == false ) {
    IsStart = false;
    IsEnd = true;
  }
  
  if( IsStart == true && IsEnd == false ) {
    if( time > 1200 && time < 1700 ) {
      comand = (comand << 1) | 1;
    } else if( time > 20 && time < 800 ) {
      comand <<= 1;
    }
    
    //comand = time;
  }
  
  if(IsStart == false && IsEnd == true ) {
    IsStart = IsEnd = false;
  }  
}

SoftwareSerial - оставил чтобы кто-нибудь не тратил время на писалки, раскомментировал и продебаггил.

Еще хотелось бы всем сказать спасибо кто принял участие в этой теме - без вас я бы сидел и дальше собирал, особенно maksim'у!!!

hugoboss317
Offline
Зарегистрирован: 21.03.2013

Хорошая темка. У меня пара вопросов. Первый к автору. Какой размер скетча получается для тиньки и SoftwareSerial  поделешься?

P.S. Все тиньку по умолчанию прошиты на максимальные 8 МГц от внутреннего кварца и при этом его работа действительно получается нестабильной.  Можно уменьшить до 4 или даже 1 если этого достаточно, но на 4МГц тоже бывает подлагивает. При установке внешнего на 20 МГЦ резко снижается диапазон рабочих напряжений о чём говорится и в даташите и МК становится ужасно капризный. Ставил на машину, тинька жила своей жизнью. Правда простейший фильтр (диод и ёмкость) решил проблему, но я решил не рисковать и остановился на 12 МГц истессно от внешнего кварца. Проблем не было больше.

Ещё есть такой вопрос:

Есть ИК-приёмник, пульт ДУ, МК, светодиод. Через дуинку посылаю в порт код кнопки пульта, затем этот код вписываю в скетчь, нажимаю кнопку пульта - диод включается, нажимаю ещё раз - выключается. Всё великолепно. Вопрос такой - как сделать чтоб можно было обучить контроллер не зная кода кнопок пульта, чтоб МК читал код и вносил его в память и в последствии реагировал на этод код. Желательно чтоб при снятии питания с МК не нужно было его переобучать.

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

Вот скетч для имеющегося кода :



int led_1Pin = 2;
#include <IRremote.h>
int RECV_PIN = 11;
IRrecv irrecv(RECV_PIN);
decode_results results;
int a = 0;
void setup (){
  irrecv.enableIRIn();
  
  pinMode(led_1Pin, OUTPUT);
}
void loop() {
  if (irrecv.decode(&results)) {
delay(300);
if (results.value == 0xFF00906F) {a=a+1;}

if (a==1){digitalWrite(led_1Pin, HIGH);} else {digitalWrite(led_1Pin, LOW); a=0;}
{ 
delay(50);
}
irrecv.resume(); 

  }

}

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

 

igormwd
Offline
Зарегистрирован: 22.03.2013

SoftwareSerial я использовал стандартный который идет вместе в библиотеке к Attiny85 (http://code.google.com/p/arduino-tiny/downloads/list)

 

The following Arduino commands should be supported:
pinMode()
digitalWrite()
digitalRead()
analogRead()
analogWrite()
shiftOut()
pulseIn()
millis()
micros()
delay()
delayMicroseconds()
SoftwareSerial (has been updated in Arduino 1.0)

 

"Все тиньку по умолчанию прошиты на максимальные 8 МГц" - мне изначально пришли все 10 штук на 1Mhz.. Определил по стандартному скетчу Blink - светодиод мигал на других частотах очень долго мигал, а на 1Mhz нормально.

По поводу уменьшений я не знаю, но на 1Mhz  - SoftwareSerial точно не работает! пишет надо минимум 8Mhz. 

На 12Mhz я не пробовал, у меня библиотеки только на 1Mhz, 8Mhz и 20Mhz - с фьюзами у меня нет опыта пока еще.. так что не рискую ))

А IRremote.h - у меня почему не работает, там куча ошибок вылазит, поэтому и решил писать свою, благо maksim в этом очень помог! А с диодом аналогичная работа, вы должны не код передавать а именно формат протокола пульта, т.е. импульсы. Чтобы их увидеть, надо расскоментировать //comand = time; в 38 строке моего последнего кода и ты увидишь их через SoftwareSerial при приеме с пульта. первое число не обращай внимания, должно начаться с 9000, затем 4000 и далее до >3000 идут как раз данные, но тебе надо все передавать (кроме первого показания как уже сказал). смотри вобщем протокол (http://www.sbprojects.com/knowledge/ir/nec.php)

hugoboss317
Offline
Зарегистрирован: 21.03.2013

igormwd пишет:
Мне изначально пришли все 10 штук на 1Mhz.. Определил по стандартному скетчу Blink - светодиод мигал на других частотах очень долго мигал, а на 1Mhz нормально.

есть такой младший бит CKDIV8. Я не помню чтоб на тиньке его устанавливал (активировал) но когда считал из контроллера, был нолик. Он делит тактовую частоту на 8, т.е. МК работает на 8 Мгц, а фактически получается что 1. есть вероятность что CKDIV8 по умолчанию тоже активен.

igormwd
Offline
Зарегистрирован: 22.03.2013

CKDIV8 насколько я понял когда читал форумы про AtTiny85 - это когда вы програмно меняете частоту, а не изначальную делаете фьюзами через "Записать загрузчик"

maksim
Offline
Зарегистрирован: 12.02.2012

Плохо читали это именно фьюз-бит, предназначен для деления на 8 тактовой частоты. По умолчанию включен.