0

I need to recognize a signal from an IR remote, not to use a library for working with IR remotes/receivers. How can when the button is clicked to display its name. I have PIC12F615 microcontroller with this code: code and 5 buttons connected to him and I need recognise which button pressed. Why NEC IR remote control decoder does not work, it doesn't set nec_ok to true.If output nec_state to the terminal, I get 0 1 0 1 0 1 and so on.

    boolean nec_ok = 0;
byte i, nec_state = 0, command, inv_command;
unsigned int address;
unsigned long nec_code;

void setup() {
Serial.begin(9600);
// Timer1 module configuration
TCCR1A = 0;
TCCR1B = 0; // Disable Timer1 module
TCNT1 = 0; // Set Timer1 preload value to 0 (reset)
TIMSK1 = 1; // enable Timer1 overflow interrupt
attachInterrupt(digitalPinToInterrupt(2), remote_read, CHANGE); // Enable external interrupt (INT0)
}

void remote_read() {
unsigned int timer_value;
if(nec_state != 0){
timer_value = TCNT1; // Store Timer1 value
TCNT1 = 0; // Reset Timer1
}
switch(nec_state){
case 0 : // Start receiving IR data (we're at the beginning of 9ms pulse)
TCNT1 = 0; // Reset Timer1
TCCR1B = 2; // Enable Timer1 module with 1/8 prescaler ( 2 ticks every 1 us)
nec_state = 1; // Next state: end of 9ms pulse (start of 4.5ms space)
i = 0;
return;
case 1 : // End of 9ms pulse
if((timer_value > 19000) || (timer_value < 17000)){ // Invalid interval ==> stop decoding and reset
nec_state = 0; // Reset decoding process
TCCR1B = 0; // Disable Timer1 module
}
else
nec_state = 2; // Next state: end of 4.5ms space (start of 562µs pulse)
return;
case 2 : // End of 4.5ms space
if((timer_value > 10000) || (timer_value < 8000)){
nec_state = 0; // Reset decoding process
TCCR1B = 0; // Disable Timer1 module
}
else
nec_state = 3; // Next state: end of 562µs pulse (start of 562µs or 1687µs space)
return;
case 3 : // End of 562µs pulse
if((timer_value > 1400) || (timer_value < 800)){ // Invalid interval ==> stop decoding and reset
TCCR1B = 0; // Disable Timer1 module
nec_state = 0; // Reset decoding process
}
else
nec_state = 4; // Next state: end of 562µs or 1687µs space
return;
case 4 : // End of 562µs or 1687µs space
if((timer_value > 3600) || (timer_value < 800)){ // Time interval invalid ==> stop decoding
TCCR1B = 0; // Disable Timer1 module
nec_state = 0; // Reset decoding process
return;
}
if( timer_value > 2000) // If space width > 1ms (short space)
bitSet(nec_code, (31 - i)); // Write 1 to bit (31 - i)
else // If space width < 1ms (long space)
bitClear(nec_code, (31 - i)); // Write 0 to bit (31 - i)
i++;
if(i > 31){ // If all bits are received
nec_ok = 1; // Decoding process OK
detachInterrupt(0); // Disable external interrupt (INT0)
return;
}
nec_state = 3; // Next state: end of 562µs pulse (start of 562µs or 1687µs space)
}
}

ISR(TIMER1_OVF_vect) { // Timer1 interrupt service routine (ISR)
nec_state = 0; // Reset decoding process
TCCR1B = 0; // Disable Timer1 module
}

void loop() {
Serial.println(nec_ok);
if(nec_ok){ // If the mcu receives NEC message with successful
nec_ok = 0; // Reset decoding process
nec_state = 0;
TCCR1B = 0; // Disable Timer1 module
address = nec_code >> 16;
command = nec_code >> 8;
inv_command = nec_code;


attachInterrupt(digitalPinToInterrupt(2), remote_read, CHANGE); // Enable external interrupt (INT0)
}
}

Schema: enter image description here

20
  • There are some coding issues to clear up before proceeding much further. Variables set in an ISR but used in the loop() should be volatile, e.g. nec_ok. Ensure also that compiler warnings are enabled in the IDE and clean up as required. There are probably other issues.
    – 6v6gt
    May 6 at 12:11
  • changed variables to volatile, if a warning is displayed: warning: 'timer_value' may be used uninitialized in this function, note: 'timer_value' was declared here unsigned int timer_value;
    – Good York
    May 6 at 12:20
  • unsigned int timer_value; make this static as so static unsigned int timer_value = 0; . As static, it is initialised only once and holds its value between invocations of the calling routine (which is probably what you want here).
    – 6v6gt
    May 6 at 12:43
  • nothing changed, the same problem
    – Good York
    May 6 at 12:46
  • 1
    why are you posting this question again?
    – jsotola
    May 6 at 17:35

1 Answer 1

1

External interrupt version of NEC protocol decoder using micros() for timing instead of Timer 1. Drops NEC repeat codes, Otherwise, in the style of the code in the question.

volatile boolean nec_ok = 0;
byte command, inv_command;
volatile byte nec_state = 0 ;
unsigned int address;
volatile unsigned long nec_code;
volatile uint32_t errFlag = 0 ;
volatile uint32_t decodeStartAtMs = 0 ;


void setup() {
  Serial.begin(9600);
  pinMode( 2, INPUT_PULLUP ) ;
  attachInterrupt(digitalPinToInterrupt(2), remote_read, CHANGE); // Enable external interrupt (INT0)
}

void remote_read() {

  static byte i = 0 ;
  static bool wasSpace;
  static unsigned long enteredAtUs = 0 ;
  static unsigned long pulseLengthLastUs ;

  wasSpace = ! digitalRead( 2 ) ;
  pulseLengthLastUs = micros() - enteredAtUs; // Calculate pulse length
  enteredAtUs = micros(); // Store current time for next entry


  switch (nec_state) {

    case 0 : // Wait here until 9ms pulse found )
      if ( pulseLengthLastUs > 8000 && pulseLengthLastUs < 10000 ) {  // got a 9ms pulse
        decodeStartAtMs = millis() ;  // for timeout
        nec_state = 1 ;
        nec_code = 0 ;
        i = 0 ;
      }
      break;

    case 1 : // handle 4.5ms space
      /*
      if ((pulseLengthLastUs > 2000) && (pulseLengthLastUs < 2500)) {
          // silently drop repeat codes !!!
          nec_state = 0; // Reset decoding process
      }
      else 
      */
      if ((pulseLengthLastUs > 5500) || (pulseLengthLastUs < 3500)) {
        errFlag = 10000000 + pulseLengthLastUs ;
        nec_state = 0; // Reset decoding process
      }
      else
        nec_state = 2; // Next state: end of 562µs pulse (start of 562µs or 1687µs space)
      break;


    case 2 : // handle 562µs or 1687µs space
      if ((pulseLengthLastUs > 1900) || (pulseLengthLastUs < 400)) { // Time interval invalid ==> stop decoding
        errFlag = 20000000 + pulseLengthLastUs ;
        nec_state = 0; // Reset decoding process
        break;
      }

      if ( wasSpace ) {
        // we are interested only in the space length
        if ( pulseLengthLastUs > 1000) // If space width > 1ms (short space)
          bitSet(nec_code, (31 - i)); // Write 1 to bit (31 - i)
        else // If space width < 1ms (long space)
          bitClear(nec_code, (31 - i)); // Write 0 to bit (31 - i)
        i++;
        if (i > 31) { // If all bits are received
          i = 0 ;
          nec_ok = 1; // Decoding process OK
          detachInterrupt(0); // Disable external interrupt (INT0)
        }
      }
      break;
  }
}

void loop() {

  if ( errFlag > 0 ) {
    Serial.print( "Error: " ) ;
    Serial.println( errFlag ) ;
    errFlag = 0 ;
  }

  if ( nec_state > 0 && millis() - decodeStartAtMs > 90 ) {
    Serial.println( "Timeout" ) ;
    nec_state = 0 ;
  }


  if (nec_ok) {
    Serial.print( "Code received = " ) ;
    Serial.println( nec_code, HEX ) ;

    address = (nec_code >> 16) & 0xFF; // Extract address from received code
    inv_command = (nec_code >> 8) & 0xFF; // Extract inverted command from received code
    command = nec_code & 0xFF; // Extract command from received code
    if (command == ((~inv_command) & 0xFF)) { // Verify command validity
      Serial.print("Received valid NEC code: ");
      Serial.print(address, HEX); Serial.print(", ");
      Serial.print(command, HEX); Serial.print(", ");
      Serial.println(inv_command, HEX);
    }
    else {
      Serial.println("Received invalid NEC code!");
    }
    nec_ok = 0; // Reset decoding process
    nec_state = 0; // Reset decoding state
    attachInterrupt(digitalPinToInterrupt(2), remote_read, CHANGE); // Re-enable external interrupt (INT0)
  }

}

This utility can be used to verify the output of an IR receiver showing the timings of the pulses and spaces to 4us resolution.

// protocol analyser utility 
// intended to dump timings to the serial console for example from demodulated NEC IR codes
// Connect the IR receiver output to pin 2


const uint8_t DATA_SIZE = 100 ;

struct intervalRead_t {
  uint32_t durationUs ;
  bool space ;
} ;

intervalRead_t intervalRead[ DATA_SIZE ] ;

volatile unsigned long enteredAtUs = 0 ;
volatile uint8_t index = 0 ;
volatile bool firstPulse = true ;


void saveTimes() {

  bool wasSpace;
  uint32_t pulseLengthLastUs ;

  wasSpace = ! digitalRead( 2 ) ;
  pulseLengthLastUs = micros() - enteredAtUs; // Calculate pulse length
  enteredAtUs = micros(); // Store current time for next entry

  if ( firstPulse ) {
    firstPulse = false ;
    index = 0 ;
  }
  else {
    if ( index < DATA_SIZE  )  {
      intervalRead[ index ].durationUs = pulseLengthLastUs ;
      intervalRead[ index ].space = wasSpace ;
      index++ ;
    }
  }
}

void setup() {
  Serial.begin(9600);
  pinMode( 2, INPUT_PULLUP ) ;
  attachInterrupt(digitalPinToInterrupt(2), saveTimes, CHANGE); // Enable external interrupt (INT0)
}

void loop() {
  if ( !firstPulse && micros() - enteredAtUs > 500000UL  ) {
    for ( uint8_t i = 0; i < index ; i++ ) {
      Serial.print( i ) ;
      Serial.print( "\tinterval= " ) ;
      Serial.print( intervalRead[ i ].durationUs ) ;
      Serial.print( "us  \t") ;
      Serial.print( intervalRead[ i ].space ? "space" : "pulse" ) ;
      Serial.println( ) ;
    }
    Serial.println( ) ;
    firstPulse = true ;
  }
}

13
  • For some reason it does not output anything to the console
    – Good York
    May 7 at 6:13
  • nec_state always zero and pulseLengthLastUs 238000 380 176 196 176 196 184 192 184 192 176 196 184 960 1072 1132 376 804 2292 2676 1552 2676 1500 1604 40988 304 196 176 196 176 196 176 836 2800
    – Good York
    May 7 at 6:19
  • If using your code, print from console when button pressed: no code no code 1C0 1DF 3 E no code 1
    – Good York
    May 7 at 6:42
  • How did you get those figures? You can't put a print() statement in function remote_read() because it is an ISR and will any way mess up the timings especially when printing so slowly at 9600 baud. The PIC software appears to generate an NEC repeat code if the button is held in. The code I supplied ignores these. Try only momentarily pressing the button when testing.
    – 6v6gt
    May 7 at 6:59
  • I only momentarily pressing the button when testing, but it doesn't print anything
    – Good York
    May 7 at 7:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.