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

asked May 6, 2023 at 8:57
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. Commented May 6, 2023 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; Commented May 6, 2023 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). Commented May 6, 2023 at 12:43
  • nothing changed, the same problem Commented May 6, 2023 at 12:46
  • 1
    why are you posting this question again? Commented May 6, 2023 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 ;
 }
}
answered May 7, 2023 at 3:31
13
  • For some reason it does not output anything to the console Commented May 7, 2023 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 Commented May 7, 2023 at 6:19
  • If using your code, print from console when button pressed: no code no code 1C0 1DF 3 E no code 1 Commented May 7, 2023 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. Commented May 7, 2023 at 6:59
  • I only momentarily pressing the button when testing, but it doesn't print anything Commented May 7, 2023 at 7:02

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.