-
Notifications
You must be signed in to change notification settings - Fork 0
/
IRremote.cpp
224 lines (208 loc) · 8.53 KB
/
IRremote.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
//******************************************************************************
// IRremote
// Version 2.0.1 June, 2015
// Copyright 2009 Ken Shirriff
// For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html
//
// Modified by Paul Stoffregen <paul@pjrc.com> to support other boards and timers
// Modified by Mitra Ardron <mitra@mitra.biz>
// Added Sanyo and Mitsubishi controllers
// Modified Sony to spot the repeat codes that some Sony's send
//
// Interrupt code based on NECIRrcv by Joe Knapp
// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556
// Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/
//
// JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post)
// LG added by Darryl Smith (based on the JVC protocol)
// Whynter A/C ARC-110WD added by Francesco Meschia
// RCMM protocol added by Richard Brinegar
//******************************************************************************
// Defining IR_GLOBAL here allows us to declare the instantiation of global variables
#define IR_GLOBAL
# include "IRremote.h"
# include "IRremoteInt.h"
#undef IR_GLOBAL
#ifndef IR_TIMER_USE_ESP32
#include <avr/interrupt.h>
#endif
//+=============================================================================
// The match functions were (apparently) originally MACROs to improve code speed
// (although this would have bloated the code) hence the names being CAPS
// A later release implemented debug output and so they needed to be converted
// to functions.
// I tried to implement a dual-compile mode (DEBUG/non-DEBUG) but for some
// reason, no matter what I did I could not get them to function as macros again.
// I have found a *lot* of bugs in the Arduino compiler over the last few weeks,
// and I am currently assuming that one of these bugs is my problem.
// I may revisit this code at a later date and look at the assembler produced
// in a hope of finding out what is going on, but for now they will remain as
// functions even in non-DEBUG mode
//
int MATCH (int measured, int desired)
{
DBG_PRINT(F("Testing: "));
DBG_PRINT(TICKS_LOW(desired), DEC);
DBG_PRINT(F(" >= "));
DBG_PRINT(measured, DEC);
DBG_PRINT(F(" <= "));
DBG_PRINT(TICKS_HIGH(desired), DEC);
bool passed = ((measured >= TICKS_LOW(desired)) && (measured <= TICKS_HIGH(desired)));
if (passed)
DBG_PRINTLN(F("?; passed"));
else
DBG_PRINTLN(F("?; FAILED"));
return passed;
}
//+========================================================
// Due to sensor lag, when received, Marks tend to be 100us too long
//
int MATCH_MARK (int measured_ticks, int desired_us)
{
DBG_PRINT(F("Testing mark (actual vs desired): "));
DBG_PRINT(measured_ticks * USECPERTICK, DEC);
DBG_PRINT(F("us vs "));
DBG_PRINT(desired_us, DEC);
DBG_PRINT("us");
DBG_PRINT(": ");
DBG_PRINT(TICKS_LOW(desired_us + MARK_EXCESS) * USECPERTICK, DEC);
DBG_PRINT(F(" >= "));
DBG_PRINT(measured_ticks * USECPERTICK, DEC);
DBG_PRINT(F(" <= "));
DBG_PRINT(TICKS_HIGH(desired_us + MARK_EXCESS) * USECPERTICK, DEC);
bool passed = ((measured_ticks >= TICKS_LOW (desired_us + MARK_EXCESS))
&& (measured_ticks <= TICKS_HIGH(desired_us + MARK_EXCESS)));
if (passed)
DBG_PRINTLN(F("?; passed"));
else
DBG_PRINTLN(F("?; FAILED"));
return passed;
}
//+========================================================
// Due to sensor lag, when received, Spaces tend to be 100us too short
//
int MATCH_SPACE (int measured_ticks, int desired_us)
{
DBG_PRINT(F("Testing space (actual vs desired): "));
DBG_PRINT(measured_ticks * USECPERTICK, DEC);
DBG_PRINT(F("us vs "));
DBG_PRINT(desired_us, DEC);
DBG_PRINT("us");
DBG_PRINT(": ");
DBG_PRINT(TICKS_LOW(desired_us - MARK_EXCESS) * USECPERTICK, DEC);
DBG_PRINT(F(" >= "));
DBG_PRINT(measured_ticks * USECPERTICK, DEC);
DBG_PRINT(F(" <= "));
DBG_PRINT(TICKS_HIGH(desired_us - MARK_EXCESS) * USECPERTICK, DEC);
bool passed = ((measured_ticks >= TICKS_LOW (desired_us - MARK_EXCESS))
&& (measured_ticks <= TICKS_HIGH(desired_us - MARK_EXCESS)));
if (passed)
DBG_PRINTLN(F("?; passed"));
else
DBG_PRINTLN(F("?; FAILED"));
return passed;
}
// added the MATCH_WITHIN function because the previous MATCH functions allows too large of an
// acceptable range for JVC, LG, NEC and RCMM protocols
int MATCH_WITHIN (int measured_ticks, int desired_us, int excess_us)
{
DBG_PRINT(F("Testing within (actual vs desired): "));
DBG_PRINT(measured_ticks * USECPERTICK, DEC);
DBG_PRINT(F("us vs "));
DBG_PRINT(desired_us, DEC);
DBG_PRINT("us");
DBG_PRINT(": ");
DBG_PRINT((desired_us - excess_us), DEC);
DBG_PRINT(F(" >= "));
DBG_PRINT(measured_ticks * USECPERTICK, DEC);
DBG_PRINT(F(" <= "));
DBG_PRINT((desired_us + excess_us), DEC);
bool passed = (((measured_ticks * USECPERTICK) >= (desired_us - excess_us))
&& ((measured_ticks * USECPERTICK) <= (desired_us + excess_us))) ;
if (passed)
DBG_PRINTLN(F("?; passed"));
else
DBG_PRINTLN(F("?; FAILED"));
return passed;
}
//+=============================================================================
// Interrupt Service Routine - Fires every 50uS
// TIMER2 interrupt code to collect raw data.
// Widths of alternating SPACE, MARK are recorded in rawbuf.
// Recorded in ticks of 50uS [microseconds, 0.000050 seconds]
// 'rawlen' counts the number of entries recorded so far.
// First entry is the SPACE between transmissions.
// As soon as a the first [SPACE] entry gets long:
// Ready is set; State switches to IDLE; Timing of SPACE continues.
// As soon as first MARK arrives:
// Gap width is recorded; Ready is cleared; New logging starts
//
#ifdef IR_TIMER_USE_ESP32
void IRTimer()
#else
ISR (TIMER_INTR_NAME)
#endif
{
TIMER_RESET;
// Read if IR Receiver -> SPACE [xmt LED off] or a MARK [xmt LED on]
// digitalRead() is very slow. Optimisation is possible, but makes the code unportable
uint8_t irdata = (uint8_t)digitalRead(irparams.recvpin);
irparams.timer++; // One more 50uS tick
if (irparams.rawlen >= RAWBUF) irparams.rcvstate = STATE_OVERFLOW ; // Buffer overflow
switch(irparams.rcvstate) {
//......................................................................
case STATE_IDLE: // In the middle of a gap
if (irdata == MARK) {
if (irparams.timer < GAP_TICKS) { // Not big enough to be a gap.
irparams.timer = 0;
} else {
// Gap just ended; Record duration; Start recording transmission
irparams.overflow = false;
irparams.rawlen = 0;
irparams.rawbuf[irparams.rawlen++] = irparams.timer;
irparams.timer = 0;
irparams.rcvstate = STATE_MARK;
}
}
break;
//......................................................................
case STATE_MARK: // Timing Mark
if (irdata == SPACE) { // Mark ended; Record time
irparams.rawbuf[irparams.rawlen++] = irparams.timer;
irparams.timer = 0;
irparams.rcvstate = STATE_SPACE;
}
break;
//......................................................................
case STATE_SPACE: // Timing Space
if (irdata == MARK) { // Space just ended; Record time
irparams.rawbuf[irparams.rawlen++] = irparams.timer;
irparams.timer = 0;
irparams.rcvstate = STATE_MARK;
} else if (irparams.timer > GAP_TICKS) { // Space
// A long Space, indicates gap between codes
// Flag the current code as ready for processing
// Switch to STOP
// Don't reset timer; keep counting Space width
irparams.rcvstate = STATE_STOP;
}
break;
//......................................................................
case STATE_STOP: // Waiting; Measuring Gap
if (irdata == MARK) irparams.timer = 0 ; // Reset gap timer
break;
//......................................................................
case STATE_OVERFLOW: // Flag up a read overflow; Stop the State Machine
irparams.overflow = true;
irparams.rcvstate = STATE_STOP;
break;
}
// If requested, flash LED while receiving IR data
if (irparams.blinkflag) {
if (irdata == MARK)
if (irparams.blinkpin) digitalWrite(irparams.blinkpin, HIGH); // Turn user defined pin LED on
else BLINKLED_ON() ; // if no user defined LED pin, turn default LED pin for the hardware on
else if (irparams.blinkpin) digitalWrite(irparams.blinkpin, LOW); // Turn user defined pin LED on
else BLINKLED_OFF() ; // if no user defined LED pin, turn default LED pin for the hardware on
}
}