-
Notifications
You must be signed in to change notification settings - Fork 1
/
IRremote.cpp
413 lines (373 loc) · 11.3 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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
/*
* IRremote
* Version 0.11 August, 2009
* Copyright 2009 Ken Shirriff
* For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html
*
* Modified by Paul Stoffregen <[email protected]> to support other boards and timers
* Modified by Mitra Ardron <[email protected]>
* Added Sanyo and Mitsubishi controllers
* Modified Sony to spot the repeat codes that some Sony's send
* Modified by Gaspard van Koningsveld to trim out IRrecv, not using PWM anymore, allow setting of IR LED pin, and make it compatible with the Spark Core v1.0 (STM32F103CB based)
* Added the Mitsubishi HVAC protocol from https://github.com/r45635/HVAC-IR-Control
*
* 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)
*/
#include "IRremote.h"
#include "application.h"
IRsend::IRsend(int irPin) : irPin(irPin) {};
void IRsend::sendNEC(unsigned long data, int nbits)
{
enableIROut(38);
mark(NEC_HDR_MARK);
space(NEC_HDR_SPACE);
for (int i = 0; i < nbits; i++) {
if (data & TOPBIT) {
mark(NEC_BIT_MARK);
space(NEC_ONE_SPACE);
}
else {
mark(NEC_BIT_MARK);
space(NEC_ZERO_SPACE);
}
data <<= 1;
}
mark(NEC_BIT_MARK);
space(0);
}
void IRsend::sendSony(unsigned long data, int nbits) {
enableIROut(40);
mark(SONY_HDR_MARK);
space(SONY_HDR_SPACE);
data = data << (32 - nbits);
for (int i = 0; i < nbits; i++) {
if (data & TOPBIT) {
mark(SONY_ONE_MARK);
space(SONY_HDR_SPACE);
}
else {
mark(SONY_ZERO_MARK);
space(SONY_HDR_SPACE);
}
data <<= 1;
}
}
void IRsend::sendRaw(unsigned int buf[], int len, int hz)
{
enableIROut(hz);
for (int i = 0; i < len; i++) {
if (i & 1) {
space(buf[i]);
}
else {
mark(buf[i]);
}
}
space(0); // Just to be sure
}
// Note: first bit must be a one (start bit)
void IRsend::sendRC5(unsigned long data, int nbits)
{
enableIROut(36);
data = data << (32 - nbits);
mark(RC5_T1); // First start bit
space(RC5_T1); // Second start bit
mark(RC5_T1); // Second start bit
for (int i = 0; i < nbits; i++) {
if (data & TOPBIT) {
space(RC5_T1); // 1 is space, then mark
mark(RC5_T1);
}
else {
mark(RC5_T1);
space(RC5_T1);
}
data <<= 1;
}
space(0); // Turn off at end
}
// Caller needs to take care of flipping the toggle bit
void IRsend::sendRC6(unsigned long data, int nbits)
{
enableIROut(36);
data = data << (32 - nbits);
mark(RC6_HDR_MARK);
space(RC6_HDR_SPACE);
mark(RC6_T1); // start bit
space(RC6_T1);
int t;
for (int i = 0; i < nbits; i++) {
if (i == 3) {
// double-wide trailer bit
t = 2 * RC6_T1;
}
else {
t = RC6_T1;
}
if (data & TOPBIT) {
mark(t);
space(t);
}
else {
space(t);
mark(t);
}
data <<= 1;
}
space(0); // Turn off at end
}
/* Sharp and DISH support by Todd Treece ( http://unionbridge.org/design/ircommand )
The Dish send function needs to be repeated 4 times, and the Sharp function
has the necessary repeat built in because of the need to invert the signal.
Sharp protocol documentation:
http://www.sbprojects.com/knowledge/ir/sharp.htm
Here are the LIRC files that I found that seem to match the remote codes
from the oscilloscope:
Sharp LCD TV:
http://lirc.sourceforge.net/remotes/sharp/GA538WJSA
DISH NETWORK (echostar 301):
http://lirc.sourceforge.net/remotes/echostar/301_501_3100_5100_58xx_59xx
For the DISH codes, only send the last for characters of the hex.
i.e. use 0x1C10 instead of 0x0000000000001C10 which is listed in the
linked LIRC file.
*/
void IRsend::sendSharp(unsigned long data, int nbits) {
unsigned long invertdata = data ^ SHARP_TOGGLE_MASK;
enableIROut(38);
for (int i = 0; i < nbits; i++) {
if (data & 0x4000) {
mark(SHARP_BIT_MARK);
space(SHARP_ONE_SPACE);
}
else {
mark(SHARP_BIT_MARK);
space(SHARP_ZERO_SPACE);
}
data <<= 1;
}
mark(SHARP_BIT_MARK);
space(SHARP_ZERO_SPACE);
delay(46);
for (int i = 0; i < nbits; i++) {
if (invertdata & 0x4000) {
mark(SHARP_BIT_MARK);
space(SHARP_ONE_SPACE);
}
else {
mark(SHARP_BIT_MARK);
space(SHARP_ZERO_SPACE);
}
invertdata <<= 1;
}
mark(SHARP_BIT_MARK);
space(SHARP_ZERO_SPACE);
delay(46);
}
void IRsend::sendDISH(unsigned long data, int nbits)
{
enableIROut(56);
mark(DISH_HDR_MARK);
space(DISH_HDR_SPACE);
for (int i = 0; i < nbits; i++) {
if (data & DISH_TOP_BIT) {
mark(DISH_BIT_MARK);
space(DISH_ONE_SPACE);
}
else {
mark(DISH_BIT_MARK);
space(DISH_ZERO_SPACE);
}
data <<= 1;
}
}
void IRsend::sendPanasonic(unsigned int address, unsigned long data) {
enableIROut(35);
mark(PANASONIC_HDR_MARK);
space(PANASONIC_HDR_SPACE);
for(int i=0;i<16;i++)
{
mark(PANASONIC_BIT_MARK);
if (address & 0x8000) {
space(PANASONIC_ONE_SPACE);
} else {
space(PANASONIC_ZERO_SPACE);
}
address <<= 1;
}
for (int i=0; i < 32; i++) {
mark(PANASONIC_BIT_MARK);
if (data & TOPBIT) {
space(PANASONIC_ONE_SPACE);
} else {
space(PANASONIC_ZERO_SPACE);
}
data <<= 1;
}
mark(PANASONIC_BIT_MARK);
space(0);
}
void IRsend::sendJVC(unsigned long data, int nbits, int repeat)
{
enableIROut(38);
data = data << (32 - nbits);
if (!repeat){
mark(JVC_HDR_MARK);
space(JVC_HDR_SPACE);
}
for (int i = 0; i < nbits; i++) {
if (data & TOPBIT) {
mark(JVC_BIT_MARK);
space(JVC_ONE_SPACE);
}
else {
mark(JVC_BIT_MARK);
space(JVC_ZERO_SPACE);
}
data <<= 1;
}
mark(JVC_BIT_MARK);
space(0);
}
void IRsend::sendHvacMitsubishi(
HvacMode HVAC_Mode, // Example HVAC_HOT HvacMitsubishiMode
int HVAC_Temp, // Example 21 (°c)
HvacFanMode HVAC_FanMode, // Example FAN_SPEED_AUTO HvacMitsubishiFanMode
HvacVanneMode HVAC_VanneMode, // Example VANNE_AUTO_MOVE HvacMitsubishiVanneMode
int OFF // Example false
)
{
#define HVAC_MITSUBISHI_DEBUG; // Un comment to access DEBUG information through Serial Interface
byte mask = 1; //our bitmask
byte data[18] = { 0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x08, 0x06, 0x30, 0x45, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F };
// data array is a valid trame, only byte to be chnaged will be updated.
byte i;
#ifdef HVAC_MITSUBISHI_DEBUG
Serial.println("Packet to send: ");
for (i = 0; i < 18; i++) {
Serial.print("_");
Serial.print(data[i], HEX);
}
Serial.println(".");
#endif
// Byte 6 - On / Off
if (OFF) {
data[5] = (byte) 0x0; // Turn OFF HVAC
} else {
data[5] = (byte) 0x20; // Tuen ON HVAC
}
// Byte 7 - Mode
switch (HVAC_Mode)
{
case HVAC_HOT: data[6] = (byte) 0x08; break;
case HVAC_COLD: data[6] = (byte) 0x18; break;
case HVAC_DRY: data[6] = (byte) 0x10; break;
case HVAC_AUTO: data[6] = (byte) 0x20; break;
default: break;
}
// Byte 8 - Temperature
// Check Min Max For Hot Mode
byte temperature;
if (HVAC_Temp > 31) { temperature = 31;}
else if (HVAC_Temp < 16) { temperature = 16; }
else { temperature = HVAC_Temp; };
data[7] = (byte) temperature - 16;
// Byte 10 - FAN / VANNE
switch (HVAC_FanMode)
{
case FAN_SPEED_1: data[9] = (byte) 0b00000001; break;
case FAN_SPEED_2: data[9] = (byte) 0b00000010; break;
case FAN_SPEED_3: data[9] = (byte) 0b00000011; break;
case FAN_SPEED_4: data[9] = (byte) 0b00000100; break;
case FAN_SPEED_5: data[9] = (byte) 0b00000100; break; //No FAN speed 5 for MITSUBISHI so it is consider as Speed 4
case FAN_SPEED_AUTO: data[9] = (byte) 0b10000000; break;
case FAN_SPEED_SILENT: data[9] = (byte) 0b00000101; break;
default: break;
}
switch (HVAC_VanneMode)
{
case VANNE_AUTO: data[9] = (byte) data[9] | 0b01000000; break;
case VANNE_H1: data[9] = (byte) data[9] | 0b01001000; break;
case VANNE_H2: data[9] = (byte) data[9] | 0b01010000; break;
case VANNE_H3: data[9] = (byte) data[9] | 0b01011000; break;
case VANNE_H4: data[9] = (byte) data[9] | 0b01100000; break;
case VANNE_H5: data[9] = (byte) data[9] | 0b01101000; break;
case VANNE_AUTO_MOVE: data[9] = (byte) data[9] | 0b01111000; break;
default: break;
}
// Byte 18 - CRC
data[17] = 0;
for (i = 0; i < 17; i++) {
data[17] = (byte) data[i] + data[17]; // CRC is a simple bits addition
}
#ifdef HVAC_MITSUBISHI_DEBUG
Serial.println("Packet to send: ");
for (i = 0; i < 18; i++) {
Serial.print("_"); Serial.print(data[i], HEX);
}
Serial.println(".");
for (i = 0; i < 18; i++) {
Serial.print(data[i], BIN); Serial.print(" ");
}
Serial.println(".");
#endif
enableIROut(38); // 38khz
space(0);
for (int j = 0; j < 2; j++) { // For Mitsubishi IR protocol we have to send two time the packet data
// Header for the Packet
mark(HVAC_MITSUBISHI_HDR_MARK);
space(HVAC_MITSUBISHI_HDR_SPACE);
for (i = 0; i < 18; i++) {
// Send all Bits from Byte Data in Reverse Order
for (mask = 0b00000001; mask > 0; mask <<= 1) { //iterate through bit mask
if (data[i] & mask) { // Bit ONE
mark(HVAC_MITSUBISHI_BIT_MARK);
space(HVAC_MITSUBISHI_ONE_SPACE);
}
else { // Bit ZERO
mark(HVAC_MITSUBISHI_BIT_MARK);
space(HVAC_MISTUBISHI_ZERO_SPACE);
}
//Next bits
}
}
// End of Packet and retransmission of the Packet
mark(HVAC_MITSUBISHI_RPT_MARK);
space(HVAC_MITSUBISHI_RPT_SPACE);
space(0); // Just to be sure
}
}
void IRsend::mark(int time) {
// Sends an IR mark (frequency burst output) for the specified number of microseconds.
noInterrupts();
while (time > 0) {
digitalWrite(irPin, HIGH); // this takes about 3 microseconds to happen
delayMicroseconds(burstWait);
digitalWrite(irPin, LOW); // this also takes about 3 microseconds
delayMicroseconds(burstWait);
time -= burstLength;
}
interrupts();
}
void IRsend::space(int time) {
// Sends an IR space (no output) for the specified number of microseconds.
digitalWrite(irPin, LOW); // Takes about 3 microsecondes
if (time > 3) {
delayMicroseconds(time - 3);
}
}
void IRsend::enableIROut(int khz) {
// Enables IR output. The khz value controls the modulation frequency in kilohertz.
// MAX frequency is 166khz.
pinMode(irPin, OUTPUT);
digitalWrite(irPin, LOW);
// This is the time to wait with the IR LED on and off to make the frequency, in microseconds.
// The - 3.0 at the end is because digitalWrite() takes about 3 microseconds. Info from:
// https://github.com/eflynch/sparkcoreiremitter/blob/master/ir_emitter/ir_emitter.ino
burstWait = round(1.0 / khz * 1000.0 / 2.0 - 3.0);
// This is the total time of a period, in microseconds.
burstLength = round(1.0 / khz * 1000.0);
}