Skip to content

Commit 48fb51a

Browse files
committed
Added IRDecoder
1 parent a60e353 commit 48fb51a

File tree

2 files changed

+194
-0
lines changed

2 files changed

+194
-0
lines changed

src/IRdecoder.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#include <IRdecoder.h>
2+
3+
/* Interprets an IR remote with NEC encoding. See IRdecoder.h for more explanation. */
4+
5+
void handleIRsensor(void)
6+
{
7+
decoder.handleIRsensor();
8+
}
9+
10+
void IRDecoder::init(void)
11+
{
12+
pinMode(irPin, INPUT);
13+
attachInterrupt(digitalPinToInterrupt(irPin), ::handleIRsensor, CHANGE);
14+
}
15+
16+
void IRDecoder::handleIRsensor(void)
17+
{
18+
uint32_t currUS = micros();
19+
20+
if(!digitalRead(irPin)) // FALLING edge
21+
{
22+
fallingEdge = currUS;
23+
}
24+
25+
else // RISING edge
26+
{
27+
risingEdge = currUS;
28+
29+
//and process
30+
uint32_t delta = risingEdge - fallingEdge; //length of pulse, in us
31+
uint32_t codeLength = risingEdge - lastRisingEdge;
32+
lastRisingEdge = risingEdge;
33+
34+
if(delta > 8500 && delta < 9500) // received a start pulse
35+
{
36+
index = 0;
37+
state = IR_PREAMBLE;
38+
return;
39+
}
40+
41+
/* a pulse is supposed to be 562.5 us, but it varies based on the RC device, plus
42+
* the receiver is NOT optimized for IR remotes --
43+
* it's actually optimized for sensitivity. So I set the maximum accepted pulse
44+
* length to 700us. On the ESP32 (others?), the pulse is sometimes coming in at ~515 us,
45+
* so lowering the bottom to 500.
46+
*/
47+
48+
else if(delta < 500 || delta > 700) // pulse wasn't right length => error
49+
{
50+
state = IR_ERROR;
51+
return;
52+
}
53+
54+
else if(state == IR_PREAMBLE)
55+
{
56+
//preamble is 4.5 ms, but we have to add the ~560 us burst to it
57+
if(codeLength < 5300 && codeLength > 4800) //preamble
58+
{
59+
currCode = 0;
60+
state = IR_ACTIVE;
61+
}
62+
63+
//repeat code is a 2.25 ms space + 560 us burst
64+
else if(codeLength < 3300 && codeLength > 2700) //repeat code
65+
{
66+
state = IR_REPEAT;
67+
if(((currCode ^ (currCode >> 8)) & 0x00ff0000) != 0x00ff0000) {state = IR_ERROR;} //but recheck code!
68+
else lastReceiveTime = millis(); //not really used
69+
}
70+
}
71+
72+
else if(state == IR_ACTIVE)
73+
{
74+
//short burst is nominally 1.125 ms
75+
if(codeLength < 1300 && codeLength > 900) //short = 0
76+
{
77+
index++;
78+
}
79+
80+
//long burst is nominally 2.25 ms
81+
else if(codeLength < 2500 && codeLength > 2000) //long = 1
82+
{
83+
currCode += ((uint32_t)1 << index);
84+
index++;
85+
}
86+
87+
else //error
88+
{
89+
state = IR_ERROR;
90+
}
91+
92+
if(index == 32) //full set of bits
93+
{
94+
//first, check for errors (kinda' ugly, but it works)
95+
if(((currCode ^ (currCode >> 8)) & 0x00ff0000) != 0x00ff0000) {state = IR_ERROR;}
96+
97+
else //we're good to go
98+
{
99+
state = IR_COMPLETE;
100+
lastReceiveTime = millis();
101+
}
102+
}
103+
}
104+
}
105+
}

src/IRdecoder.h

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#include <Arduino.h>
2+
3+
/*
4+
* A class to interpret IR remotes with NEC encoding. NEC encoding sends four bytes:
5+
*
6+
* [device ID, ~divice ID, key code, ~key code]
7+
*
8+
* Sending the inverse allow for easy error checking (and reduces saturation in the receiver).
9+
*
10+
* Codes are send in little endian; this library reverses upon reception, so the first bit received
11+
* is in the LSB of currCode. That means that the key code is found in bits [23..16] of currCode
12+
*
13+
* https://techdocs.altium.com/display/FPGA/NEC+Infrared+Transmission+Protocol
14+
*
15+
* This does not interpret the codes into which key was pressed. That needs to be
16+
* mapped on a remote by remote basis.
17+
*/
18+
19+
/*
20+
* TO DO:
21+
* - (MED) Possibly indicate repeat code instead of just "refreshing" the latest code?
22+
* - (LOW) Consider a better way to indicate that a valid code has been entered;
23+
* returning -1 is OK if we return a 16-bit number (so 255 is a valid code),
24+
* but there may be a more elegant solution.
25+
* - (MED) Add ability to use PCINT library to this generic library. Use a derived class?
26+
*/
27+
28+
class IRDecoder
29+
{
30+
private:
31+
enum IR_STATE
32+
{
33+
IR_READY, //idle, returns to this state after you request a code
34+
IR_PREAMBLE, //received the start burst, waiting for first bit
35+
IR_REPEAT, //received repeat code (part of NEC protocol); last code will be returned
36+
IR_ACTIVE, //have some bits, but not yet complete
37+
IR_COMPLETE, //a valid code has been received
38+
IR_ERROR
39+
}; //an error occurred; won't return a valid code
40+
41+
uint8_t irPin;
42+
43+
IR_STATE state = IR_READY; //a simple state machine for managing reception
44+
45+
volatile uint32_t lastReceiveTime = 0; //not really used -- could be used to sunset codes
46+
47+
volatile uint32_t currCode = 0; //the most recently received valid code
48+
volatile uint8_t index = 0; //for tracking which bit we're on
49+
50+
volatile uint32_t fallingEdge = 0;
51+
volatile uint32_t risingEdge = 0;
52+
53+
volatile uint32_t lastRisingEdge = 0; //used for tracking spacing between rising edges, i.e., bit value
54+
55+
public:
56+
IRDecoder(uint8_t pin) : irPin(pin) {}
57+
void init(void); //call this in the setup()
58+
void handleIRsensor(void); //ISR
59+
60+
uint32_t getCode(void) //returns the most recent valid code; returns zero if there was an error or nothing new
61+
{
62+
if (state == IR_COMPLETE || state == IR_REPEAT)
63+
{
64+
state = IR_READY;
65+
return currCode;
66+
}
67+
else
68+
return 0;
69+
}
70+
71+
int16_t getKeyCode(bool acceptRepeat = false) //returns the most recent key code; returns -1 on error (not sure if 0 can be a code or not!!!)
72+
{
73+
if (state == IR_COMPLETE || (acceptRepeat == true && state == IR_REPEAT))
74+
{
75+
state = IR_READY;
76+
return (currCode >> 16) & 0x0ff;
77+
}
78+
//else if(state == IR_ERROR) return currCode; //for debugging, if needed
79+
else
80+
return -1;
81+
}
82+
};
83+
84+
class IRDecoder32U4 : public IRDecoder
85+
{
86+
IRDecoder32U4(uint8_t pin) : IRDecoder(pin) {}
87+
};
88+
89+
extern IRDecoder decoder;

0 commit comments

Comments
 (0)