Skip to content

Commit dfe66ad

Browse files
committed
Fix problems with broken receives #828
1 parent 65dee31 commit dfe66ad

File tree

3 files changed

+51
-31
lines changed

3 files changed

+51
-31
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,12 @@ Therefore you must change all `IrSender.begin(true);` by `IrSender.begin(IR_SEND
4848
In the new version you will send NEC commands not by 32 bit codes but by a (constant) 8 bit address and an 8 bit command.
4949

5050
# FAQ
51-
- IR does not work right when I use Neopixels (aka WS2811/WS2812/WS2812B)<br/>
51+
- IR does not work right when I use Neopixels (aka WS2811/WS2812/WS2812B) or other libraries blocking interrupts for a longer time (> 50 us).<br/>
5252
Whether you use the Adafruit Neopixel lib, or FastLED, interrupts get disabled on many lower end CPUs like the basic Arduinos for longer than 50 µs.
5353
In turn, this stops the IR interrupt handler from running when it needs to. There are some solutions to this on some processors,
5454
[see this page from Marc MERLIN](http://marc.merlins.org/perso/arduino/post_2017-04-03_Arduino-328P-Uno-Teensy3_1-ESP8266-ESP32-IR-and-Neopixels.html)
5555
- The default IR timer on AVR's is timer 2. Since the **Arduino Tone library** as well as **analogWrite() for pin 3 and pin 11** requires timer 2,
56-
this functionality cannot be used simultaneously. You can use tone() but after the tone has stopped, you must call `IrReceiver.start()` or better `IrReceiver.start(<microsecondsOfToneDuration>)` to restore the timer settings for receive.<br/>
56+
this functionality cannot be used simultaneously. You can use tone() but after the tone has stopped, you must call `IrReceiver.start()` or better `IrReceiver.start(<microsecondsOfToneDuration>)` to restore the timer settings for receive. Or you change the timer to timer 1 in private/IRTimer.cpp.h.<br/>
5757
If you can live with the NEC protocol, you can try the MinimalReceiver example, it requires no timer.
5858
- You can use **multiple IR receiver** by just connecting the output pins of several IR receivers together.
5959
The IR receivers use an NPN transistor as output device with just a 30k resistor to VCC.
@@ -133,7 +133,7 @@ Modify it by commenting them out or in, or change the values if applicable. Or d
133133
| `DO_NOT_USE_FEEDBACK_LED` | TinyIRReceiver.h | disabled | Enable it to disable the feedback LED function. |
134134

135135
### Modifying compile options with Arduino IDE
136-
First use *Sketch > Show Sketch Folder (Ctrl+K)*.<br/>
136+
First, use *Sketch > Show Sketch Folder (Ctrl+K)*.<br/>
137137
If you did not yet stored the example as your own sketch, then you are instantly in the right library folder.<br/>
138138
Otherwise you have to navigate to the parallel `libraries` folder and select the library you want to access.<br/>
139139
In both cases the library files itself are located in the `src` directory.<br/>

src/TinyIRReceiver.cpp.h

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
* @{
4747
*/
4848
//#define TRACE
49-
5049
TinyIRReceiverStruct TinyIRReceiverControl;
5150

5251
/**
@@ -70,7 +69,10 @@ ICACHE_RAM_ATTR
7069
IRAM_ATTR
7170
#endif
7271
void IRPinChangeInterruptHandler(void) {
73-
// save IR input level - negative logic, true means inactive / IR pause
72+
/*
73+
* Save IR input level
74+
* Negative logic, true / HIGH means inactive / IR space, LOW / false means IR mark.
75+
*/
7476
uint_fast8_t tIRLevel = digitalReadFast(IR_INPUT_PIN);
7577

7678
#if !defined(DO_NOT_USE_FEEDBACK_LED) && defined(IR_FEEDBACK_LED_PIN)
@@ -81,7 +83,7 @@ void IRPinChangeInterruptHandler(void) {
8183
* 1. compute microseconds after last change
8284
*/
8385
uint32_t tCurrentMicros = micros();
84-
uint16_t tDeltaMicros = tCurrentMicros - TinyIRReceiverControl.LastChangeMicros;
86+
uint16_t tMicrosOfMarkOrSpace = tCurrentMicros - TinyIRReceiverControl.LastChangeMicros;
8587
TinyIRReceiverControl.LastChangeMicros = tCurrentMicros;
8688

8789
uint8_t tState = TinyIRReceiverControl.IRReceiverState;
@@ -98,26 +100,34 @@ void IRPinChangeInterruptHandler(void) {
98100

99101
if (tIRLevel == LOW) {
100102
/*
101-
* We receive a signal now
103+
* We have a mark here
102104
*/
103-
if (tDeltaMicros > 2 * NEC_HEADER_MARK) {
105+
if (tMicrosOfMarkOrSpace > 2 * NEC_HEADER_MARK) {
104106
// timeout -> must reset state machine
105107
tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK;
106108
}
107109
if (tState == IR_RECEIVER_STATE_WAITING_FOR_START_MARK) {
110+
// We are at the beginning of the header mark, check timing at the next transition
108111
tState = IR_RECEIVER_STATE_WAITING_FOR_START_SPACE;
109112
}
110113

111114
else if (tState == IR_RECEIVER_STATE_WAITING_FOR_FIRST_DATA_MARK) {
112-
// Check start space length
113-
if (tDeltaMicros >= lowerValue25Percent(NEC_HEADER_SPACE) && tDeltaMicros <= upperValue25Percent(NEC_HEADER_SPACE)) {
114-
tState = IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE;
115+
if (tMicrosOfMarkOrSpace >= lowerValue25Percent(NEC_HEADER_SPACE)
116+
&& tMicrosOfMarkOrSpace <= upperValue25Percent(NEC_HEADER_SPACE)) {
117+
/*
118+
* We have a valid data header space here -> initialize data
119+
*/
115120
TinyIRReceiverControl.IRRawDataBitCounter = 0;
116121
TinyIRReceiverControl.IRRawData.ULong = 0;
117122
TinyIRReceiverControl.IRRawDataMask = 1;
118123
TinyIRReceiverControl.IRRepeatDetected = false;
119-
} else if (tDeltaMicros >= lowerValue25Percent(NEC_REPEAT_HEADER_SPACE)
120-
&& tDeltaMicros <= upperValue25Percent(NEC_REPEAT_HEADER_SPACE)) {
124+
tState = IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE;
125+
} else if (tMicrosOfMarkOrSpace >= lowerValue25Percent(NEC_REPEAT_HEADER_SPACE)
126+
&& tMicrosOfMarkOrSpace <= upperValue25Percent(NEC_REPEAT_HEADER_SPACE)
127+
&& TinyIRReceiverControl.IRRawDataBitCounter >= NEC_BITS) {
128+
/*
129+
* We have a repeat header here and no broken receive before -> set repeat flag
130+
*/
121131
TinyIRReceiverControl.IRRepeatDetected = true;
122132
tState = IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE;
123133
} else {
@@ -129,51 +139,56 @@ void IRPinChangeInterruptHandler(void) {
129139

130140
else if (tState == IR_RECEIVER_STATE_WAITING_FOR_DATA_MARK) {
131141
// Check data space length
132-
if (tDeltaMicros >= lowerValue(NEC_ZERO_SPACE) && tDeltaMicros <= upperValue(NEC_ONE_SPACE)) {
133-
// Here we have a valid bit
142+
if (tMicrosOfMarkOrSpace >= lowerValue(NEC_ZERO_SPACE) && tMicrosOfMarkOrSpace <= upperValue(NEC_ONE_SPACE)) {
143+
// We have a valid bit here
134144
tState = IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE;
135-
if (tDeltaMicros >= 2 * NEC_UNIT) {
145+
if (tMicrosOfMarkOrSpace >= 2 * NEC_UNIT) {
136146
// we received a 1
137147
TinyIRReceiverControl.IRRawData.ULong |= TinyIRReceiverControl.IRRawDataMask;
138148
} else {
139-
// we received a 0
149+
// we received a 0 - empty code for documentation
140150
}
151+
// prepare for next bit
141152
TinyIRReceiverControl.IRRawDataMask = TinyIRReceiverControl.IRRawDataMask << 1;
142153
TinyIRReceiverControl.IRRawDataBitCounter++;
143154
} else {
144155
// Wrong length -> reset state
145156
tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK;
146157
}
147158
} else {
148-
// error wrong state for the received level (should not happen!) -> reset state
159+
// error wrong state for the received level (should not happen!) -> reset state (2 checks costs 10 bytes programming space)
149160
tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK;
150161
}
151162
}
152163

153164
else {
154165
/*
155-
* We receive a space now
166+
* We have a space here
156167
*/
157168
if (tState == IR_RECEIVER_STATE_WAITING_FOR_START_SPACE) {
158-
// Check start bit length
159-
if (tDeltaMicros >= lowerValue25Percent(NEC_HEADER_MARK) && tDeltaMicros <= upperValue25Percent(NEC_HEADER_MARK)) {
169+
/*
170+
* Check length of header mark here
171+
*/
172+
if (tMicrosOfMarkOrSpace >= lowerValue25Percent(NEC_HEADER_MARK)
173+
&& tMicrosOfMarkOrSpace <= upperValue25Percent(NEC_HEADER_MARK)) {
160174
tState = IR_RECEIVER_STATE_WAITING_FOR_FIRST_DATA_MARK;
161175
} else {
162-
// Wrong length -> reset state
176+
// Wrong length of header mark -> reset state
163177
tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK;
164178
}
165179
}
166180

167181
else if (tState == IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE) {
168-
169-
// Check data bit length
170-
if (tDeltaMicros >= lowerValue(NEC_BIT_MARK) && tDeltaMicros <= upperValue(NEC_BIT_MARK)) {
182+
// Check data mark length
183+
if (tMicrosOfMarkOrSpace >= lowerValue(NEC_BIT_MARK) && tMicrosOfMarkOrSpace <= upperValue(NEC_BIT_MARK)) {
184+
/*
185+
* We have a valid mark here, check for transmission complete
186+
*/
171187
if (TinyIRReceiverControl.IRRawDataBitCounter >= NEC_BITS || TinyIRReceiverControl.IRRepeatDetected) {
172188
/*
173-
* Code complete -> call callback
174-
* No parity check
189+
* Code complete -> call callback, no parity check!
175190
*/
176-
// Set state for new start
191+
// Reset state for new start
177192
tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK;
178193
#if !defined(ARDUINO_ARCH_MBED)
179194
interrupts();
@@ -187,18 +202,22 @@ void IRPinChangeInterruptHandler(void) {
187202
TinyIRReceiverControl.IRRawData.UByte.MidLowByte = 0; // Address is the first 8 bit
188203
}
189204

205+
/*
206+
* Call user provided callback here
207+
*/
190208
handleReceivedTinyIRData(TinyIRReceiverControl.IRRawData.UWord.LowWord,
191209
TinyIRReceiverControl.IRRawData.UByte.MidHighByte, TinyIRReceiverControl.IRRepeatDetected);
192210

193211
} else {
212+
// not finished yet
194213
tState = IR_RECEIVER_STATE_WAITING_FOR_DATA_MARK;
195214
}
196215
} else {
197216
// Wrong length -> reset state
198217
tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK;
199218
}
200219
} else {
201-
// error wrong state for the received level (should not happen!) -> reset state
220+
// error wrong state for the received level (should not happen!) -> reset state (2 checks costs 10 bytes programming space)
202221
tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK;
203222
}
204223
}

src/ir_NEC.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,9 +316,10 @@ bool IRrecv::decodeNEC() {
316316

317317
/*
318318
* With Send sendNECMSB() you can send your old 32 bit codes.
319-
* To convert one into the other, you must reverse the byte positions and then reverse all positions of each byte.
319+
* To convert one into the other, you must reverse the byte positions and then reverse all bit positions of each byte.
320+
* Or write it as one binary string and reverse/mirror it.
320321
* Example:
321-
* 0xCB340102 byte reverse -> 0x020134CB bit reverse-> 40802CD3
322+
* 0xCB340102 byte reverse -> 02 01 34 CB bit reverse-> 40 80 2C D3. Bit reverse e.g. for CB = 1100 1011 -> (reverse/mirror bits) 1101 0011 = D3.
322323
*/
323324
void IRsend::sendNECMSB(uint32_t data, uint8_t nbits, bool repeat) {
324325
// Set IR carrier frequency

0 commit comments

Comments
 (0)