Skip to content

Commit 829d8eb

Browse files
committed
Added support for decoding of "special" NEC repeats
1 parent 20b8cc8 commit 829d8eb

File tree

11 files changed

+82
-40
lines changed

11 files changed

+82
-40
lines changed

README.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ This library enables you to send and receive using infra-red signals on an Ardui
77
[![Commits since latest](https://img.shields.io/github/commits-since/Arduino-IRremote/Arduino-IRremote/latest)](https://github.com/Arduino-IRremote/Arduino-IRremote/commits/master)
88
[![Installation instructions](https://www.ardu-badge.com/badge/IRremote.svg?)](https://www.ardu-badge.com/IRremote)
99
[![LibraryBuild](https://github.com/Arduino-IRremote/Arduino-IRremote/workflows/LibraryBuild/badge.svg)](https://github.com/Arduino-IRremote/Arduino-IRremote/actions)
10+
[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua)
1011

1112
Available as [Arduino library "IRremote"](https://www.arduinolibraries.info/libraries/i-rremote).
1213

@@ -384,19 +385,19 @@ Modify them by enabling / disabling them, or change the values if applicable.
384385
| Name | Default value | Description |
385386
|-|-|-|
386387
| `RAW_BUFFER_LENGTH` | 100 | Buffer size of raw input buffer. Must be even! 100 is sufficient for *regular* protocols of up to 48 bits, but for most air conditioner protocols a value of up to 750 is required. Use the ReceiveDump example to find smallest value for your requirements. |
387-
| `IR_SEND_PIN` | disabled | If specified (as constant), reduces program size and improves send timing for AVR. If you want to use a runtime variable send pin e.g. with `setSendPin(uint8_t aSendPinNumber)` , you must disable this macro. |
388-
| `SEND_PWM_BY_TIMER` | disabled | Disable carrier PWM generation in software and use (restricted) hardware PWM. Enabled for ESP32 and RP2040 in all examples. |
389-
| `USE_NO_SEND_PWM` | disabled | Use no carrier PWM, just simulate an **active low** receiver signal. Overrides `SEND_PWM_BY_TIMER` definition. |
390-
| `USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN` | disabled | Use or simulate open drain output mode at send pin. **Attention, active state of open drain is LOW**, so connect the send LED between positive supply and send pin! |
391-
| `EXCLUDE_EXOTIC_PROTOCOLS` | disabled | If activated, BOSEWAVE, WHYNTER and LEGO_PF are excluded in `decode()` and in sending with `IrSender.write()`. Saves up to 650 bytes program memory. |
392-
| `EXCLUDE_UNIVERSAL_PROTOCOLS` | disabled | If activated, the universal decoder for pulse distance protocols and decodeHash (special decoder for all protocols) are excluded in `decode()`. Saves up to 1000 bytes program memory. |
388+
| `EXCLUDE_UNIVERSAL_PROTOCOLS` | disabled | Excludes the universal decoder for pulse distance protocols and decodeHash (special decoder for all protocols) from `decode()`. Saves up to 1000 bytes program memory. |
393389
| `DECODE_<Protocol name>` | all | Selection of individual protocol(s) to be decoded. You can specify multiple protocols. See [here](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/IRremote.hpp#L98-L121) |
394390
| `MARK_EXCESS_MICROS` | 20 | MARK_EXCESS_MICROS is subtracted from all marks and added to all spaces before decoding, to compensate for the signal forming of different IR receiver modules. |
395391
| `RECORD_GAP_MICROS` | 5000 | Minimum gap between IR transmissions, to detect the end of a protocol.<br/>Must be greater than any space of a protocol e.g. the NEC header space of 4500 µs.<br/>Must be smaller than any gap between a command and a repeat; e.g. the retransmission gap for Sony is around 24 ms.<br/>Keep in mind, that this is the delay between the end of the received command and the start of decoding. |
396-
| `FEEDBACK_LED_IS_ACTIVE_LOW` | disabled | Required on some boards (like my BluePill and my ESP8266 board), where the feedback LED is active low. |
397-
| `NO_LED_FEEDBACK_CODE` | disabled | This completely disables the LED feedback code for send and receive, thus saving around 100 bytes program memory for receiving, around 500 bytes for sending and halving the receiver ISR processing time. |
398392
| `IR_INPUT_IS_ACTIVE_HIGH` | disabled | Enable it if you use a RF receiver, which has an active HIGH output signal. |
393+
| `IR_SEND_PIN` | disabled | If specified (as constant), reduces program size and improves send timing for AVR. If you want to use a runtime variable send pin e.g. with `setSendPin(uint8_t aSendPinNumber)` , you must disable this macro. |
394+
| `SEND_PWM_BY_TIMER` | disabled | Disables carrier PWM generation in software and use (restricted) hardware PWM. Enabled for ESP32 and RP2040 in all examples. |
395+
| `USE_NO_SEND_PWM` | disabled | Uses no carrier PWM, just simulate an **active low** receiver signal. Overrides `SEND_PWM_BY_TIMER` definition. |
399396
| `IR_SEND_DUTY_CYCLE_PERCENT` | 30 | Duty cycle of IR send signal. |
397+
| `USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN` | disabled | Uses or simulates open drain output mode at send pin. **Attention, active state of open drain is LOW**, so connect the send LED between positive supply and send pin! |
398+
| `EXCLUDE_EXOTIC_PROTOCOLS` | disabled | Excludes BOSEWAVE, WHYNTER and LEGO_PF from `decode()` and from sending with `IrSender.write()`. Saves up to 650 bytes program memory. |
399+
| `FEEDBACK_LED_IS_ACTIVE_LOW` | disabled | Required on some boards (like my BluePill and my ESP8266 board), where the feedback LED is active low. |
400+
| `NO_LED_FEEDBACK_CODE` | disabled | Disables the LED feedback code for send and receive. Saves around 100 bytes program memory for receiving, around 500 bytes for sending and halving the receiver ISR processing time. |
400401
| `MICROS_PER_TICK` | 50 | Resolution of the raw input buffer data. Corresponds to 2 pulses of each 26.3 µs at 38 kHz. |
401402
| `TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING` | 25 | Relative tolerance (in percent) for matchTicks(), matchMark() and matchSpace() functions used for protocol decoding. |
402403
| `DEBUG` | disabled | Enables lots of lovely debug output. |
@@ -407,7 +408,8 @@ These next macros for **TinyIRReceiver** must be defined in your program before
407408
|-|-|-|
408409
| `IR_INPUT_PIN` | 2 | The pin number for TinyIRReceiver IR input, which gets compiled in. |
409410
| `IR_FEEDBACK_LED_PIN` | `LED_BUILTIN` | The pin number for TinyIRReceiver feedback LED, which gets compiled in. |
410-
| `NO_LED_FEEDBACK_CODE` | disabled | Enable it to disable the feedback LED function. Saves 14 bytes program memory. |
411+
| `NO_LED_FEEDBACK_CODE` | disabled | Disables the feedback LED function. Saves 14 bytes program memory. |
412+
| `DISABLE_NEC_SPECIAL_REPEAT_SUPPORT` | disabled | Disables the detection of full NEC frame repeats. Saves 40 bytes program memory. |
411413
412414
### Changing include (*.h) files with Arduino IDE
413415
First, use *Sketch > Show Sketch Folder (Ctrl+K)*.<br/>

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ See also the commit log at github: https://github.com/Arduino-IRremote/Arduino-I
1010
- Fixed potential bug in SendBiphase data for 1 bit.
1111
- Fixed bug in send for RP4020.
1212
- Fixed pin mapping problems especially for Teensy 2.0.
13+
- Added support for decoding of "special" NEC repeats.
1314

1415
## 3.7.1
1516
- SendRaw now supports bufferlenght > 255.

examples/AllProtocols/AllProtocols.ino

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ void setup() {
130130
IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);
131131

132132
Serial.print(F("Ready to receive IR signals of protocols: "));
133-
printActiveIRProtocols(&Serial);
133+
printActiveIRProtocols (&Serial);
134134
Serial.println(F("at pin " STR(IR_RECEIVE_PIN)));
135135

136136
#if FLASHEND >= 0x3FFF // For 16k flash or more, like ATtiny1604. Code does not fit in program memory of ATtiny85 etc.
@@ -267,7 +267,10 @@ void printIRResultOnLCD() {
267267
* Show or clear repetition flag
268268
*/
269269
myLCD.setCursor(15, 1);
270-
if (IrReceiver.decodedIRData.flags & (IRDATA_FLAGS_IS_REPEAT)) {
270+
if (IrReceiver.decodedIRData.flags & (IRDATA_FLAGS_IS_SPECIAL_REPEAT)) {
271+
myLCD.print('S');
272+
return; // Since it is a repetition, printed data has not changed
273+
} else if (IrReceiver.decodedIRData.flags & (IRDATA_FLAGS_IS_REPEAT)) {
271274
myLCD.print('R');
272275
return; // Since it is a repetition, printed data has not changed
273276
} else {

examples/IRDispatcherDemo/IRCommandDispatcher.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class IRCommandDispatcher {
8282
void checkAndCallCommand(bool aCallAlsoBlockingCommands);
8383

8484
void printIRCommandString(Print *aSerial);
85-
void setRequestToStopReceived();
85+
void setRequestToStopReceived(bool aRequestToStopReceived = true);
8686

8787
uint8_t currentBlockingCommandCalled = COMMAND_INVALID; // The code for the current called command
8888
bool executingBlockingCommand = false; // Lock for recursive calls of regular commands

examples/IRDispatcherDemo/IRCommandDispatcher.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,8 +316,8 @@ void IRCommandDispatcher::printIRCommandString(Print *aSerial) {
316316
aSerial->println(reinterpret_cast<const __FlashStringHelper*>(unknown));
317317
}
318318

319-
void IRCommandDispatcher::setRequestToStopReceived() {
320-
requestToStopReceived = true;
319+
void IRCommandDispatcher::setRequestToStopReceived(bool aRequestToStopReceived) {
320+
requestToStopReceived = aRequestToStopReceived;
321321
}
322322

323323
#if defined(LOCAL_DEBUG)

src/IRReceive.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -982,7 +982,11 @@ void printIRResultShort(Print *aSerial, IRData *aIRDataPtr, bool aPrintGap) {
982982
}
983983

984984
if (aIRDataPtr->flags & IRDATA_TOGGLE_BIT_MASK) {
985-
aSerial->print(F(" Toggle=1"));
985+
if(aIRDataPtr->protocol == NEC) {
986+
aSerial->print(F(" Special repeat"));
987+
} else {
988+
aSerial->print(F(" Toggle=1"));
989+
}
986990
}
987991
#if defined(DECODE_DISTANCE)
988992
}

src/IRremoteInt.h

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,16 @@ struct irparams_struct {
132132
#define IRDATA_FLAGS_IS_REPEAT 0x01
133133
#define IRDATA_FLAGS_IS_AUTO_REPEAT 0x02
134134
#define IRDATA_FLAGS_PARITY_FAILED 0x04 ///< the current (autorepeat) frame violated parity check
135-
#define IRDATA_TOGGLE_BIT_MASK 0x08 ///< is set if RC5 or RC6 toggle bit is set
135+
#define IRDATA_FLAGS_TOGGLE_BIT 0x08 ///< is set if RC5 or RC6 toggle bit is set
136+
#define IRDATA_FLAGS_IS_SPECIAL_REPEAT 0x08 ///< is set if we received a NEC special receive (full frame instead of repeat frame)
136137
#define IRDATA_FLAGS_EXTRA_INFO 0x10 ///< there is extra info not contained in address and data (e.g. Kaseikyo unknown vendor ID)
137138
#define IRDATA_FLAGS_WAS_OVERFLOW 0x40 ///< irparams.rawlen is 0 in this case to avoid endless OverflowFlag
138139
#define IRDATA_FLAGS_IS_LSB_FIRST 0x00
139140
#define IRDATA_FLAGS_IS_MSB_FIRST 0x80 ///< Just for info. Value is mainly determined by the protocol
140141

142+
// deprecated
143+
#define IRDATA_TOGGLE_BIT_MASK 0x08 ///< is set if RC5 or RC6 toggle bit is set
144+
141145
#define RAW_DATA_ARRAY_SIZE ((((RAW_BUFFER_LENGTH - 2) - 1) / 64) + 1) // The -2 is for initial gap + stop bit mark, 64 mark + spaces for 32 bit.
142146
/**
143147
* Data structure for the user application, available as decodedIRData.
@@ -214,17 +218,17 @@ class IRrecv {
214218
/*
215219
* Useful info and print functions
216220
*/
217-
void printIRResultShort(Print *aSerial);
218221
void printIRResultMinimal(Print *aSerial);
219222
void printIRResultRawFormatted(Print *aSerial, bool aOutputMicrosecondsInsteadOfTicks = true);
220223
void printIRResultAsCVariables(Print *aSerial);
221224

222225
/*
223-
* Next 3 functions are also available as non member functions
226+
* Next 4 functions are also available as non member functions
224227
*/
228+
void printIRResultShort(Print *aSerial);
225229
void printIRSendUsage(Print *aSerial);
226-
void printActiveIRProtocols(Print *aSerial);
227230
const __FlashStringHelper* getProtocolString();
231+
static void printActiveIRProtocols(Print *aSerial);
228232

229233
void compensateAndPrintIRResultAsCArray(Print *aSerial, bool aOutputMicrosecondsInsteadOfTicks = true);
230234
void compensateAndPrintIRResultAsPronto(Print *aSerial, unsigned int frequency = 38000U);
@@ -329,11 +333,12 @@ bool MATCH_SPACE(unsigned int measured_ticks, unsigned int desired_us);
329333

330334
int getMarkExcessMicros();
331335
/*
332-
* Next 3 functions are also available as member functions
336+
* Next 4 functions are also available as member functions
333337
*/
334-
void printActiveIRProtocols(Print *aSerial);
335338
void printIRResultShort(Print *aSerial, IRData *aIRDataPtr, bool aPrintGap);
336339
void printIRSendUsage(Print *aSerial, IRData *aIRDataPtr);
340+
const __FlashStringHelper* getProtocolString();
341+
void printActiveIRProtocols(Print *aSerial);
337342

338343
/****************************************************
339344
* Feedback LED related functions

src/TinyIRReceiver.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* TinyIRReceiver.h
33
*
44
*
5-
* Copyright (C) 2021 Armin Joachimsmeyer
5+
* Copyright (C) 2021-2022 Armin Joachimsmeyer
66
77
*
88
* This file is part of IRMP https://github.com/ukw100/IRMP.
@@ -28,6 +28,8 @@
2828

2929
#include <Arduino.h>
3030

31+
//#define DISABLE_NEC_SPECIAL_REPEAT_SUPPORT // Activating this disables detection of full NEC frame repeats. Saves 40 bytes program memory.
32+
3133
#include "LongUnion.h"
3234

3335
/** \addtogroup TinyReceiver Minimal receiver for NEC protocol
@@ -56,7 +58,9 @@ void handleReceivedTinyIRData(uint16_t aAddress, uint8_t aCommand, bool isRepeat
5658
#define NEC_ZERO_SPACE NEC_UNIT
5759

5860
#define NEC_REPEAT_HEADER_SPACE (4 * NEC_UNIT) // 2250
59-
#define NEC_REPEAT_PERIOD 110000 // Not used yet - Commands are repeated every 110 ms (measured from start to start) for as long as the key on the remote control is held down.
61+
#define NEC_REPEAT_PERIOD 110000 // Commands are repeated every 110 ms (measured from start to start) for as long as the key on the remote control is held down.
62+
#define NEC_MINIMAL_DURATION 49900 // NEC_HEADER_MARK + NEC_HEADER_SPACE + 32 * 2 * NEC_UNIT + NEC_UNIT // 2.5 because we assume more zeros than ones
63+
#define NEC_MAXIMUM_REPEAT_SPACE (NEC_REPEAT_PERIOD - NEC_MINIMAL_DURATION + 5) // 65 ms
6064

6165
/*
6266
* Macros for comparing timing values
@@ -90,7 +94,10 @@ struct TinyIRReceiverStruct {
9094
*/
9195
uint32_t IRRawDataMask;
9296
LongUnion IRRawData;
93-
bool IRRepeatDetected;
97+
bool IRRepeatFrameDetected;
98+
#if !defined(DISABLE_NEC_SPECIAL_REPEAT_SUPPORT)
99+
bool IRRepeatDistanceDetected;
100+
#endif
94101
};
95102

96103
/*
@@ -101,7 +108,7 @@ struct TinyIRReceiverCallbackDataStruct {
101108
uint16_t Address;
102109
uint8_t Command;
103110
bool isRepeat;
104-
bool justWritten;
111+
bool justWritten; // Is set true if new data is available. Used by the main loop, to avoid multiple evaluations of the same IR frame.
105112
};
106113

107114
void initPCIInterruptForTinyReceiver();

src/TinyIRReceiver.hpp

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949

5050
#include <Arduino.h>
5151

52+
// - DISABLE_NEC_SPECIAL_REPEAT_SUPPORT // Activating this disables detection of full NEC frame repeats. Saves 40 bytes program memory.
53+
5254
#include "TinyIRReceiver.h" // If not defined, it defines IR_INPUT_PIN, IR_FEEDBACK_LED_PIN and TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT
5355

5456
#include "digitalWriteFast.h"
@@ -107,9 +109,9 @@ TinyIRReceiverStruct TinyIRReceiverControl;
107109
* It is called every time a complete IR command or repeat was received.
108110
*/
109111
#if defined(ESP32) || defined(ESP8266)
110-
void IRAM_ATTR handleReceivedIRData(uint16_t aAddress, uint8_t aCommand, bool isRepetition);
112+
extern void IRAM_ATTR handleReceivedIRData(uint16_t aAddress, uint8_t aCommand, bool isRepetition);
111113
#else
112-
void handleReceivedIRData(uint16_t aAddress, uint8_t aCommand, bool isRepetition);
114+
extern void handleReceivedIRData(uint16_t aAddress, uint8_t aCommand, bool isRepetition);
113115
#endif
114116

115117
/**
@@ -140,7 +142,12 @@ void IRPinChangeInterruptHandler(void)
140142
* 1. compute microseconds after last change
141143
*/
142144
uint32_t tCurrentMicros = micros();
145+
#if defined(DISABLE_NEC_SPECIAL_REPEAT_SUPPORT)
143146
uint16_t tMicrosOfMarkOrSpace = tCurrentMicros - TinyIRReceiverControl.LastChangeMicros;
147+
#else
148+
uint32_t tMicrosOfMarkOrSpace32 = tCurrentMicros - TinyIRReceiverControl.LastChangeMicros;
149+
uint16_t tMicrosOfMarkOrSpace = tMicrosOfMarkOrSpace32;
150+
#endif
144151
TinyIRReceiverControl.LastChangeMicros = tCurrentMicros;
145152

146153
uint8_t tState = TinyIRReceiverControl.IRReceiverState;
@@ -165,6 +172,12 @@ void IRPinChangeInterruptHandler(void)
165172
if (tState == IR_RECEIVER_STATE_WAITING_FOR_START_MARK) {
166173
// We are at the beginning of the header mark, check timing at the next transition
167174
tState = IR_RECEIVER_STATE_WAITING_FOR_START_SPACE;
175+
TinyIRReceiverControl.IRRepeatFrameDetected = false; // If we do it here, it saves 4 bytes
176+
#if !defined(DISABLE_NEC_SPECIAL_REPEAT_SUPPORT)
177+
// Check for special repeat, where full frame is sent again after 110 ms
178+
// Must use 32 bit arithmetic here!
179+
TinyIRReceiverControl.IRRepeatDistanceDetected = (tMicrosOfMarkOrSpace32 < NEC_MAXIMUM_REPEAT_SPACE);
180+
#endif
168181
}
169182

170183
else if (tState == IR_RECEIVER_STATE_WAITING_FOR_FIRST_DATA_MARK) {
@@ -176,15 +189,14 @@ void IRPinChangeInterruptHandler(void)
176189
TinyIRReceiverControl.IRRawDataBitCounter = 0;
177190
TinyIRReceiverControl.IRRawData.ULong = 0;
178191
TinyIRReceiverControl.IRRawDataMask = 1;
179-
TinyIRReceiverControl.IRRepeatDetected = false;
180192
tState = IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE;
181193
} else if (tMicrosOfMarkOrSpace >= lowerValue25Percent(NEC_REPEAT_HEADER_SPACE)
182194
&& tMicrosOfMarkOrSpace <= upperValue25Percent(NEC_REPEAT_HEADER_SPACE)
183195
&& TinyIRReceiverControl.IRRawDataBitCounter >= NEC_BITS) {
184196
/*
185197
* We have a repeat header here and no broken receive before -> set repeat flag
186198
*/
187-
TinyIRReceiverControl.IRRepeatDetected = true;
199+
TinyIRReceiverControl.IRRepeatFrameDetected = true;
188200
tState = IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE;
189201
} else {
190202
// This parts are optimized by the compiler into jumps to one code :-)
@@ -240,9 +252,9 @@ void IRPinChangeInterruptHandler(void)
240252
if (tMicrosOfMarkOrSpace >= lowerValue50Percent(NEC_BIT_MARK)
241253
&& tMicrosOfMarkOrSpace <= upperValue50Percent(NEC_BIT_MARK)) {
242254
/*
243-
* We have a valid mark here, check for transmission complete
255+
* We have a valid mark here, check for transmission complete, i.e. the mark of the stop bit
244256
*/
245-
if (TinyIRReceiverControl.IRRawDataBitCounter >= NEC_BITS || TinyIRReceiverControl.IRRepeatDetected) {
257+
if (TinyIRReceiverControl.IRRawDataBitCounter >= NEC_BITS || TinyIRReceiverControl.IRRepeatFrameDetected) {
246258
/*
247259
* Code complete -> call callback, no parity check!
248260
*/
@@ -264,7 +276,11 @@ void IRPinChangeInterruptHandler(void)
264276
* Call user provided callback here
265277
*/
266278
handleReceivedTinyIRData(TinyIRReceiverControl.IRRawData.UWord.LowWord,
267-
TinyIRReceiverControl.IRRawData.UByte.MidHighByte, TinyIRReceiverControl.IRRepeatDetected);
279+
TinyIRReceiverControl.IRRawData.UByte.MidHighByte, (TinyIRReceiverControl.IRRepeatFrameDetected
280+
#if !defined(DISABLE_NEC_SPECIAL_REPEAT_SUPPORT)
281+
|| TinyIRReceiverControl.IRRepeatDistanceDetected
282+
#endif
283+
));
268284

269285
} else {
270286
// not finished yet

0 commit comments

Comments
 (0)