IRremote
IRSend.hpp
Go to the documentation of this file.
1 /*
2  * IRSend.hpp
3  *
4  * Contains common functions for sending
5  *
6  * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
7  *
8  ************************************************************************************
9  * MIT License
10  *
11  * Copyright (c) 2009-2023 Ken Shirriff, Rafi Khan, Armin Joachimsmeyer
12  *
13  * Permission is hereby granted, free of charge, to any person obtaining a copy
14  * of this software and associated documentation files (the "Software"), to deal
15  * in the Software without restriction, including without limitation the rights
16  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17  * copies of the Software, and to permit persons to whom the Software is furnished
18  * to do so, subject to the following conditions:
19  *
20  * The above copyright notice and this permission notice shall be included in all
21  * copies or substantial portions of the Software.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
24  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
25  * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
26  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
27  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
28  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29  *
30  ************************************************************************************
31  */
32 #ifndef _IR_SEND_HPP
33 #define _IR_SEND_HPP
34 
35 #if defined(DEBUG) && !defined(LOCAL_DEBUG)
36 #define LOCAL_DEBUG
37 #else
38 //#define LOCAL_DEBUG // This enables debug output only for this file
39 #endif
40 
41 #if defined(TRACE) && !defined(LOCAL_TRACE)
42 #define LOCAL_TRACE
43 #else
44 //#define LOCAL_TRACE // This enables debug output only for this file
45 #endif
46 
47 /*
48  * Low level hardware timing measurement
49  */
50 //#define _IR_MEASURE_TIMING // for mark()
51 //#define _IR_TIMING_TEST_PIN 7 // "pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);" is executed at begin()
52 //
53 /*
54  * This improves readability of code by avoiding a lot of #if defined clauses
55  */
56 #if defined(IR_SEND_PIN)
57 #define sendPin IR_SEND_PIN
58 #endif
59 
64 // The sender instance
66 
67 IRsend::IRsend() { // @suppress("Class members should be properly initialized")
68 #if !defined(IR_SEND_PIN)
69  sendPin = 0;
70 #endif
71 
72 #if !defined(NO_LED_FEEDBACK_CODE)
74 #endif
75 }
76 
77 #if defined(IR_SEND_PIN)
78 
82 void IRsend::begin(){
83 # if !defined(NO_LED_FEEDBACK_CODE)
85 # endif
86 #if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
87  pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);
88 #endif
89 }
90 
96 void IRsend::begin(bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin) {
97 #if !defined(NO_LED_FEEDBACK_CODE)
98  uint_fast8_t tEnableLEDFeedback = DO_NOT_ENABLE_LED_FEEDBACK;
99  if(aEnableLEDFeedback) {
100  tEnableLEDFeedback = LED_FEEDBACK_ENABLED_FOR_SEND;
101  }
102  setLEDFeedback(aFeedbackLEDPin, tEnableLEDFeedback);
103 #else
104  (void) aEnableLEDFeedback;
105  (void) aFeedbackLEDPin;
106 #endif
107 }
108 
109 #else // defined(IR_SEND_PIN)
110 IRsend::IRsend(uint_fast8_t aSendPin) { // @suppress("Class members should be properly initialized")
111  sendPin = aSendPin;
112 # if !defined(NO_LED_FEEDBACK_CODE)
114 # endif
115 }
116 
121 void IRsend::begin(uint_fast8_t aSendPin) {
122  sendPin = aSendPin;
123 # if !defined(NO_LED_FEEDBACK_CODE)
125 # endif
126 }
127 
128 void IRsend::setSendPin(uint_fast8_t aSendPin) {
129  sendPin = aSendPin;
130 }
131 
138 void IRsend::begin(uint_fast8_t aSendPin, bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin) {
139 #if defined(IR_SEND_PIN)
140  (void) aSendPin; // for backwards compatibility
141 #else
142  sendPin = aSendPin;
143 #endif
144 
145 #if !defined(NO_LED_FEEDBACK_CODE)
146  uint_fast8_t tEnableLEDFeedback = DO_NOT_ENABLE_LED_FEEDBACK;
147  if (aEnableLEDFeedback) {
148  tEnableLEDFeedback = LED_FEEDBACK_ENABLED_FOR_SEND;
149  }
150  setLEDFeedback(aFeedbackLEDPin, tEnableLEDFeedback);
151 #else
152  (void) aEnableLEDFeedback;
153  (void) aFeedbackLEDPin;
154 #endif
155 }
156 #endif // defined(IR_SEND_PIN)
157 
170 size_t IRsend::write(IRData *aIRSendData, int_fast8_t aNumberOfRepeats) {
171 
172  auto tProtocol = aIRSendData->protocol;
173  auto tAddress = aIRSendData->address;
174  auto tCommand = aIRSendData->command;
175  bool tIsRepeat = (aIRSendData->flags & IRDATA_FLAGS_IS_REPEAT);
176  if (tIsRepeat) {
177  aNumberOfRepeats = -1; // if aNumberOfRepeats < 0 then only a special repeat frame will be sent
178  }
179 // switch (tProtocol) { // 26 bytes bigger than if, else if, else
180 // case NEC:
181 // sendNEC(tAddress, tCommand, aNumberOfRepeats, tSendRepeat);
182 // break;
183 // case SAMSUNG:
184 // sendSamsung(tAddress, tCommand, aNumberOfRepeats);
185 // break;
186 // case SONY:
187 // sendSony(tAddress, tCommand, aNumberOfRepeats, aIRSendData->numberOfBits);
188 // break;
189 // case PANASONIC:
190 // sendPanasonic(tAddress, tCommand, aNumberOfRepeats);
191 // break;
192 // case DENON:
193 // sendDenon(tAddress, tCommand, aNumberOfRepeats);
194 // break;
195 // case SHARP:
196 // sendSharp(tAddress, tCommand, aNumberOfRepeats);
197 // break;
198 // case JVC:
199 // sendJVC((uint8_t) tAddress, (uint8_t) tCommand, aNumberOfRepeats); // casts are required to specify the right function
200 // break;
201 // case RC5:
202 // sendRC5(tAddress, tCommand, aNumberOfRepeats, !tSendRepeat); // No toggle for repeats
203 // break;
204 // case RC6:
205 // // No toggle for repeats// sendRC6(tAddress, tCommand, aNumberOfRepeats, !tSendRepeat); // No toggle for repeats
206 // break;
207 // default:
208 // break;
209 // }
210 
211  /*
212  * Order of protocols is in guessed relevance :-)
213  */
214  if (tProtocol == NEC) {
215  sendNEC(tAddress, tCommand, aNumberOfRepeats);
216 
217  } else if (tProtocol == SAMSUNG) {
218  sendSamsung(tAddress, tCommand, aNumberOfRepeats);
219 
220  } else if (tProtocol == SAMSUNG48) {
221  sendSamsung48(tAddress, tCommand, aNumberOfRepeats);
222 
223  } else if (tProtocol == SAMSUNGLG) {
224  sendSamsungLG(tAddress, tCommand, aNumberOfRepeats);
225 
226  } else if (tProtocol == SONY) {
227  sendSony(tAddress, tCommand, aNumberOfRepeats, aIRSendData->numberOfBits);
228 
229  } else if (tProtocol == PANASONIC) {
230  sendPanasonic(tAddress, tCommand, aNumberOfRepeats);
231 
232  } else if (tProtocol == DENON) {
233  sendDenon(tAddress, tCommand, aNumberOfRepeats);
234 
235  } else if (tProtocol == SHARP) {
236  sendSharp(tAddress, tCommand, aNumberOfRepeats);
237 
238  } else if (tProtocol == LG) {
239  sendLG(tAddress, tCommand, aNumberOfRepeats);
240 
241  } else if (tProtocol == JVC) {
242  sendJVC((uint8_t) tAddress, (uint8_t) tCommand, aNumberOfRepeats); // casts are required to specify the right function
243 
244  } else if (tProtocol == RC5) {
245  sendRC5(tAddress, tCommand, aNumberOfRepeats, !tIsRepeat); // No toggle for repeats
246 
247  } else if (tProtocol == RC6) {
248  sendRC6(tAddress, tCommand, aNumberOfRepeats, !tIsRepeat); // No toggle for repeats
249 
250  } else if (tProtocol == KASEIKYO_JVC) {
251  sendKaseikyo_JVC(tAddress, tCommand, aNumberOfRepeats);
252 
253  } else if (tProtocol == KASEIKYO_DENON) {
254  sendKaseikyo_Denon(tAddress, tCommand, aNumberOfRepeats);
255 
256  } else if (tProtocol == KASEIKYO_SHARP) {
257  sendKaseikyo_Sharp(tAddress, tCommand, aNumberOfRepeats);
258 
259  } else if (tProtocol == KASEIKYO_MITSUBISHI) {
260  sendKaseikyo_Mitsubishi(tAddress, tCommand, aNumberOfRepeats);
261 
262  } else if (tProtocol == NEC2) {
263  sendNEC2(tAddress, tCommand, aNumberOfRepeats);
264 
265  } else if (tProtocol == ONKYO) {
266  sendOnkyo(tAddress, tCommand, aNumberOfRepeats);
267 
268  } else if (tProtocol == APPLE) {
269  sendApple(tAddress, tCommand, aNumberOfRepeats);
270 
271 #if !defined(EXCLUDE_EXOTIC_PROTOCOLS)
272  } else if (tProtocol == BOSEWAVE) {
273  sendBoseWave(tCommand, aNumberOfRepeats);
274 
275  } else if (tProtocol == MAGIQUEST) {
276  // we have a 32 bit ID/address
277  sendMagiQuest(aIRSendData->decodedRawData, tCommand);
278 
279  } else if (tProtocol == FAST) {
280  // We have only 8 bit command
281  sendFAST(tCommand, aNumberOfRepeats);
282 
283  } else if (tProtocol == LEGO_PF) {
284  sendLegoPowerFunctions(tAddress, tCommand, tCommand >> 4, tIsRepeat); // send 5 autorepeats
285 #endif
286 
287  } else {
288  return 0; // Not supported by write. E.g for BANG_OLUFSEN
289  }
290  return 1;
291 }
292 
298 size_t IRsend::write(decode_type_t aProtocol, uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) {
299 
300 // switch (aProtocol) { // 26 bytes bigger than if, else if, else
301 // case NEC:
302 // sendNEC(aAddress, aCommand, aNumberOfRepeats, tSendRepeat);
303 // break;
304 // case SAMSUNG:
305 // sendSamsung(aAddress, aCommand, aNumberOfRepeats);
306 // break;
307 // case SONY:
308 // sendSony(aAddress, aCommand, aNumberOfRepeats, aIRSendData->numberOfBits);
309 // break;
310 // case PANASONIC:
311 // sendPanasonic(aAddress, aCommand, aNumberOfRepeats);
312 // break;
313 // case DENON:
314 // sendDenon(aAddress, aCommand, aNumberOfRepeats);
315 // break;
316 // case SHARP:
317 // sendSharp(aAddress, aCommand, aNumberOfRepeats);
318 // break;
319 // case JVC:
320 // sendJVC((uint8_t) aAddress, (uint8_t) aCommand, aNumberOfRepeats); // casts are required to specify the right function
321 // break;
322 // case RC5:
323 // sendRC5(aAddress, aCommand, aNumberOfRepeats, !tSendRepeat); // No toggle for repeats
324 // break;
325 // case RC6:
326 // // No toggle for repeats// sendRC6(aAddress, aCommand, aNumberOfRepeats, !tSendRepeat); // No toggle for repeats
327 // break;
328 // default:
329 // break;
330 // }
331 
332  /*
333  * Order of protocols is in guessed relevance :-)
334  */
335  if (aProtocol == NEC) {
336  sendNEC(aAddress, aCommand, aNumberOfRepeats);
337 
338  } else if (aProtocol == SAMSUNG) {
340 
341  } else if (aProtocol == SAMSUNG48) {
343 
344  } else if (aProtocol == SAMSUNGLG) {
346 
347  } else if (aProtocol == SONY) {
349 
350  } else if (aProtocol == PANASONIC) {
352 
353  } else if (aProtocol == DENON) {
354  sendDenon(aAddress, aCommand, aNumberOfRepeats);
355 
356  } else if (aProtocol == SHARP) {
357  sendSharp(aAddress, aCommand, aNumberOfRepeats);
358 
359  } else if (aProtocol == LG) {
360  sendLG(aAddress, aCommand, aNumberOfRepeats);
361 
362  } else if (aProtocol == JVC) {
363  sendJVC((uint8_t) aAddress, (uint8_t) aCommand, aNumberOfRepeats); // casts are required to specify the right function
364 
365  } else if (aProtocol == RC5) {
366  sendRC5(aAddress, aCommand, aNumberOfRepeats, (aNumberOfRepeats > 0)); // No toggle for repeats
367 
368  } else if (aProtocol == RC6) {
369  sendRC6(aAddress, aCommand, aNumberOfRepeats, (aNumberOfRepeats > 0)); // No toggle for repeats
370 
371  } else if (aProtocol == KASEIKYO_JVC) {
373 
374  } else if (aProtocol == KASEIKYO_DENON) {
376 
377  } else if (aProtocol == KASEIKYO_SHARP) {
379 
380  } else if (aProtocol == KASEIKYO_MITSUBISHI) {
382 
383  } else if (aProtocol == NEC2) {
384  sendNEC2(aAddress, aCommand, aNumberOfRepeats);
385 
386  } else if (aProtocol == ONKYO) {
387  sendOnkyo(aAddress, aCommand, aNumberOfRepeats);
388 
389  } else if (aProtocol == APPLE) {
390  sendApple(aAddress, aCommand, aNumberOfRepeats);
391 
392 #if !defined(EXCLUDE_EXOTIC_PROTOCOLS)
393  } else if (aProtocol == BOSEWAVE) {
395 
396  } else if (aProtocol == FAST) {
397  // We have only 8 bit command
399 
400  } else if (aProtocol == LEGO_PF) {
401  sendLegoPowerFunctions(aAddress, aCommand, aCommand >> 4, (aNumberOfRepeats < 0)); // send 5 autorepeats, except for dedicated repeats
402 #endif
403 
404  } else {
405  return 0; // Not supported by write. E.g for BANG_OLUFSEN
406  }
407  return 1;
408 }
409 
414 void IRsend::sendRaw(const uint16_t aBufferWithMicroseconds[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz) {
415 // Set IR carrier frequency
416  enableIROut(aIRFrequencyKilohertz);
417 
418  /*
419  * Raw data starts with a mark.
420  */
421  for (uint_fast16_t i = 0; i < aLengthOfBuffer; i++) {
422  if (i & 1) {
423  // Odd
424  space(aBufferWithMicroseconds[i]);
425  } else {
426  mark(aBufferWithMicroseconds[i]);
427  }
428  }
429 }
430 
435 void IRsend::sendRaw(const uint8_t aBufferWithTicks[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz) {
436 // Set IR carrier frequency
437  enableIROut(aIRFrequencyKilohertz);
438 
439  for (uint_fast16_t i = 0; i < aLengthOfBuffer; i++) {
440  if (i & 1) {
441  // Odd
442  space(aBufferWithTicks[i] * MICROS_PER_TICK);
443  } else {
444  mark(aBufferWithTicks[i] * MICROS_PER_TICK);
445  }
446  }
447  IRLedOff(); // Always end with the LED off
448 }
449 
454 void IRsend::sendRaw_P(const uint16_t aBufferWithMicroseconds[], uint_fast16_t aLengthOfBuffer,
455  uint_fast8_t aIRFrequencyKilohertz) {
456 #if !defined(__AVR__)
457  sendRaw(aBufferWithMicroseconds, aLengthOfBuffer, aIRFrequencyKilohertz); // Let the function work for non AVR platforms
458 #else
459 // Set IR carrier frequency
460  enableIROut(aIRFrequencyKilohertz);
461  /*
462  * Raw data starts with a mark
463  */
464  for (uint_fast16_t i = 0; i < aLengthOfBuffer; i++) {
465  auto duration = pgm_read_word(&aBufferWithMicroseconds[i]);
466  if (i & 1) {
467  // Odd
468  space(duration);
469 # if defined(LOCAL_DEBUG)
470  Serial.print(F("S="));
471 # endif
472  } else {
473  mark(duration);
474 # if defined(LOCAL_DEBUG)
475  Serial.print(F("M="));
476 # endif
477  }
478 # if defined(LOCAL_DEBUG)
479  Serial.println(duration);
480 # endif
481  }
482 #endif
483 }
484 
489 void IRsend::sendRaw_P(const uint8_t aBufferWithTicks[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz) {
490 #if !defined(__AVR__)
491  sendRaw(aBufferWithTicks, aLengthOfBuffer, aIRFrequencyKilohertz); // Let the function work for non AVR platforms
492 #else
493 // Set IR carrier frequency
494  enableIROut(aIRFrequencyKilohertz);
495 
496  uint_fast16_t duration;
497  for (uint_fast16_t i = 0; i < aLengthOfBuffer; i++) {
498  duration = pgm_read_byte(&aBufferWithTicks[i]) * (uint_fast16_t) MICROS_PER_TICK;
499  if (i & 1) {
500  // Odd
501  space(duration);
502 # if defined(LOCAL_DEBUG)
503  Serial.print(F("S="));
504 # endif
505  } else {
506  mark(duration);
507 # if defined(LOCAL_DEBUG)
508  Serial.print(F("M="));
509 # endif
510  }
511  }
512  IRLedOff(); // Always end with the LED off
513 # if defined(LOCAL_DEBUG)
514  Serial.println(duration);
515 # endif
516 #endif
517 }
518 
525 void IRsend::sendPulseDistanceWidthFromArray(uint_fast8_t aFrequencyKHz, uint16_t aHeaderMarkMicros, uint16_t aHeaderSpaceMicros,
526  uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, uint16_t aZeroSpaceMicros,
527  IRRawDataType *aDecodedRawDataArray, uint16_t aNumberOfBits, bool aMSBFirst, bool aSendStopBit,
528  uint16_t aRepeatPeriodMillis, int_fast8_t aNumberOfRepeats) {
529  uint8_t tFlags = 0;
530  if (aMSBFirst) {
531  tFlags = PROTOCOL_IS_MSB_FIRST;
532  }
533  (void) aSendStopBit;
534 
535  sendPulseDistanceWidthFromArray(aFrequencyKHz, aHeaderMarkMicros, aHeaderSpaceMicros, aOneMarkMicros, aOneSpaceMicros,
536  aZeroMarkMicros, aZeroSpaceMicros, aDecodedRawDataArray, aNumberOfBits, tFlags, aRepeatPeriodMillis, aNumberOfRepeats);
537 }
538 
539 void IRsend::sendPulseDistanceWidthFromArray(uint_fast8_t aFrequencyKHz, DistanceWidthTimingInfoStruct *aDistanceWidthTimingInfo,
540  IRRawDataType *aDecodedRawDataArray, uint16_t aNumberOfBits, uint8_t aFlags, uint16_t aRepeatPeriodMillis,
541  int_fast8_t aNumberOfRepeats) {
542  sendPulseDistanceWidthFromArray(aFrequencyKHz, aDistanceWidthTimingInfo->HeaderMarkMicros,
543  aDistanceWidthTimingInfo->HeaderSpaceMicros, aDistanceWidthTimingInfo->OneMarkMicros,
544  aDistanceWidthTimingInfo->OneSpaceMicros, aDistanceWidthTimingInfo->ZeroMarkMicros,
545  aDistanceWidthTimingInfo->ZeroSpaceMicros, aDecodedRawDataArray, aNumberOfBits, aFlags, aRepeatPeriodMillis,
547 }
548 
549 void IRsend::sendPulseDistanceWidthFromArray(uint_fast8_t aFrequencyKHz, uint16_t aHeaderMarkMicros, uint16_t aHeaderSpaceMicros,
550  uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, uint16_t aZeroSpaceMicros,
551  IRRawDataType *aDecodedRawDataArray, uint16_t aNumberOfBits, uint8_t aFlags, uint16_t aRepeatPeriodMillis,
552  int_fast8_t aNumberOfRepeats) {
553 
554  // Set IR carrier frequency
555  enableIROut(aFrequencyKHz);
556 
557  uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
558  uint_fast8_t tNumberOf32Or64BitChunks = ((aNumberOfBits - 1) / BITS_IN_RAW_DATA_TYPE) + 1;
559 
560 #if defined(LOCAL_DEBUG)
561  // fist data
562  Serial.print(F("Data[0]=0x"));
563  Serial.print(aDecodedRawDataArray[0], HEX);
564  if (tNumberOf32Or64BitChunks > 1) {
565  Serial.print(F(" Data[1]=0x"));
566  Serial.print(aDecodedRawDataArray[1], HEX);
567  }
568  Serial.print(F(" #="));
569  Serial.println(aNumberOfBits);
570  Serial.flush();
571 #endif
572 
573  while (tNumberOfCommands > 0) {
574  unsigned long tStartOfFrameMillis = millis();
575 
576  // Header
577  mark(aHeaderMarkMicros);
578  space(aHeaderSpaceMicros);
579 
580  for (uint_fast8_t i = 0; i < tNumberOf32Or64BitChunks; ++i) {
581  uint8_t tNumberOfBitsForOneSend;
582 
583  // Manage stop bit
584  uint8_t tFlags;
585  if (i == (tNumberOf32Or64BitChunks - 1)) {
586  // End of data
587  tNumberOfBitsForOneSend = aNumberOfBits;
588  tFlags = aFlags;
589  } else {
590  // intermediate data
591  tNumberOfBitsForOneSend = BITS_IN_RAW_DATA_TYPE;
592  tFlags = aFlags | SUPPRESS_STOP_BIT_FOR_THIS_DATA; // No stop bit for leading data
593  }
594 
595  sendPulseDistanceWidthData(aOneMarkMicros, aOneSpaceMicros, aZeroMarkMicros, aZeroSpaceMicros, aDecodedRawDataArray[i],
596  tNumberOfBitsForOneSend, tFlags);
597  aNumberOfBits -= BITS_IN_RAW_DATA_TYPE;
598  }
599 
600  tNumberOfCommands--;
601  // skip last delay!
602  if (tNumberOfCommands > 0) {
603  /*
604  * Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration.
605  */
606  auto tFrameDurationMillis = millis() - tStartOfFrameMillis;
607  if (aRepeatPeriodMillis > tFrameDurationMillis) {
608  delay(aRepeatPeriodMillis - tFrameDurationMillis);
609  }
610  }
611  }
612 }
613 
621  IRRawDataType *aDecodedRawDataArray, uint16_t aNumberOfBits, int_fast8_t aNumberOfRepeats) {
622 
623 // Calling sendPulseDistanceWidthFromArray() costs 68 bytes program memory compared to the implementation below
624 // sendPulseDistanceWidthFromArray(aProtocolConstants->FrequencyKHz, aProtocolConstants->DistanceWidthTimingInfo.HeaderMarkMicros,
625 // aProtocolConstants->DistanceWidthTimingInfo.HeaderSpaceMicros,
626 // aProtocolConstants->DistanceWidthTimingInfo.OneMarkMicros, aProtocolConstants->DistanceWidthTimingInfo.OneSpaceMicros,
627 // aProtocolConstants->DistanceWidthTimingInfo.ZeroMarkMicros, aProtocolConstants->DistanceWidthTimingInfo.ZeroSpaceMicros,
628 // aDecodedRawDataArray, aNumberOfBits, aProtocolConstants->Flags, aProtocolConstants->RepeatPeriodMillis,
629 // aNumberOfRepeats);
630  // Set IR carrier frequency
631  enableIROut(aProtocolConstants->FrequencyKHz);
632 
633  uint_fast8_t tNumberOf32Or64BitChunks = ((aNumberOfBits - 1) / BITS_IN_RAW_DATA_TYPE) + 1;
634 
635 #if defined(LOCAL_DEBUG)
636  // fist data
637  Serial.print(F("Data[0]=0x"));
638  Serial.print(aDecodedRawDataArray[0], HEX);
639  if (tNumberOf32Or64BitChunks > 1) {
640  Serial.print(F(" Data[1]=0x"));
641  Serial.print(aDecodedRawDataArray[1], HEX);
642  }
643  Serial.print(F(" #="));
644  Serial.println(aNumberOfBits);
645  Serial.flush();
646 #endif
647 
648  uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
649  while (tNumberOfCommands > 0) {
650  auto tStartOfFrameMillis = millis();
651  auto tNumberOfBits = aNumberOfBits; // refresh value for repeats
652 
653  // Header
654  mark(aProtocolConstants->DistanceWidthTimingInfo.HeaderMarkMicros);
655  space(aProtocolConstants->DistanceWidthTimingInfo.HeaderSpaceMicros);
656  uint8_t tOriginalFlags = aProtocolConstants->Flags;
657 
658  for (uint_fast8_t i = 0; i < tNumberOf32Or64BitChunks; ++i) {
659  uint8_t tNumberOfBitsForOneSend;
660 
661  uint8_t tFlags;
662  if (i == (tNumberOf32Or64BitChunks - 1)) {
663  // End of data
664  tNumberOfBitsForOneSend = tNumberOfBits;
665  tFlags = tOriginalFlags;
666  } else {
667  // intermediate data
668  tNumberOfBitsForOneSend = BITS_IN_RAW_DATA_TYPE;
669  tFlags = tOriginalFlags | SUPPRESS_STOP_BIT_FOR_THIS_DATA; // No stop bit for leading data
670  }
671 
673  aProtocolConstants->DistanceWidthTimingInfo.OneSpaceMicros,
674  aProtocolConstants->DistanceWidthTimingInfo.ZeroMarkMicros,
675  aProtocolConstants->DistanceWidthTimingInfo.ZeroSpaceMicros, aDecodedRawDataArray[i], tNumberOfBitsForOneSend,
676  tFlags);
677  tNumberOfBits -= BITS_IN_RAW_DATA_TYPE;
678  }
679 
680  tNumberOfCommands--;
681  // skip last delay!
682  if (tNumberOfCommands > 0) {
683  /*
684  * Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration.
685  */
686  auto tFrameDurationMillis = millis() - tStartOfFrameMillis;
687  if (aProtocolConstants->RepeatPeriodMillis > tFrameDurationMillis) {
688  delay(aProtocolConstants->RepeatPeriodMillis - tFrameDurationMillis);
689  }
690  }
691  }
692 }
693 
703  uint_fast8_t aNumberOfBits, int_fast8_t aNumberOfRepeats) {
704 
705 #if defined(LOCAL_DEBUG)
706  Serial.print(F("Data=0x"));
707  Serial.print(aData, HEX);
708  Serial.print(F(" #="));
709  Serial.println(aNumberOfBits);
710  Serial.flush();
711 #endif
712 
713  if (aNumberOfRepeats < 0) {
714  if (aProtocolConstants->SpecialSendRepeatFunction != NULL) {
715  /*
716  * Send only a special repeat and return
717  */
718  aProtocolConstants->SpecialSendRepeatFunction();
719  return;
720  } else {
721  // Send only one plain frame (as repeat)
722  aNumberOfRepeats = 0;
723  }
724  }
725 
726  // Set IR carrier frequency
727  enableIROut(aProtocolConstants->FrequencyKHz);
728 
729  uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
730  while (tNumberOfCommands > 0) {
731  unsigned long tStartOfFrameMillis = millis();
732 
733  if (tNumberOfCommands < ((uint_fast8_t) aNumberOfRepeats + 1) && aProtocolConstants->SpecialSendRepeatFunction != NULL) {
734  // send special repeat, if specified and we are not in the first loop
735  aProtocolConstants->SpecialSendRepeatFunction();
736  } else {
737  /*
738  * Send Header and regular frame
739  */
740  mark(aProtocolConstants->DistanceWidthTimingInfo.HeaderMarkMicros);
741  space(aProtocolConstants->DistanceWidthTimingInfo.HeaderSpaceMicros);
742  sendPulseDistanceWidthData(aProtocolConstants, aData, aNumberOfBits);
743  }
744 
745  tNumberOfCommands--;
746  // skip last delay!
747  if (tNumberOfCommands > 0) {
748  auto tCurrentFrameDurationMillis = millis() - tStartOfFrameMillis;
749  /*
750  * Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration.
751  */
752  if (aProtocolConstants->RepeatPeriodMillis > tCurrentFrameDurationMillis) {
753  delay(aProtocolConstants->RepeatPeriodMillis - tCurrentFrameDurationMillis);
754  }
755  }
756  }
757 }
758 
768 void IRsend::sendPulseDistanceWidth(uint_fast8_t aFrequencyKHz, uint16_t aHeaderMarkMicros, uint16_t aHeaderSpaceMicros,
769  uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, uint16_t aZeroSpaceMicros, IRRawDataType aData,
770  uint_fast8_t aNumberOfBits, bool aMSBFirst, bool aSendStopBit, uint16_t aRepeatPeriodMillis, int_fast8_t aNumberOfRepeats,
771  void (*aSpecialSendRepeatFunction)()) {
772  uint8_t tFlags = 0;
773  if (aMSBFirst) {
774  tFlags = PROTOCOL_IS_MSB_FIRST;
775  }
776  (void) aSendStopBit;
777  sendPulseDistanceWidth(aFrequencyKHz, aHeaderMarkMicros, aHeaderSpaceMicros, aOneMarkMicros, aOneSpaceMicros, aZeroMarkMicros,
778  aZeroSpaceMicros, aData, aNumberOfBits, tFlags, aRepeatPeriodMillis, aNumberOfRepeats, aSpecialSendRepeatFunction);
779 
780 }
781 void IRsend::sendPulseDistanceWidth(uint_fast8_t aFrequencyKHz, uint16_t aHeaderMarkMicros, uint16_t aHeaderSpaceMicros,
782  uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, uint16_t aZeroSpaceMicros, IRRawDataType aData,
783  uint_fast8_t aNumberOfBits, uint8_t aFlags, uint16_t aRepeatPeriodMillis, int_fast8_t aNumberOfRepeats,
784  void (*aSpecialSendRepeatFunction)()) {
785 
786  if (aNumberOfRepeats < 0) {
787  if (aSpecialSendRepeatFunction != NULL) {
788  aSpecialSendRepeatFunction();
789  return;
790  } else {
791  aNumberOfRepeats = 0; // send a plain frame as repeat
792  }
793  }
794 
795  // Set IR carrier frequency
796  enableIROut(aFrequencyKHz);
797 
798  uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
799  while (tNumberOfCommands > 0) {
800  unsigned long tStartOfFrameMillis = millis();
801 
802  if (tNumberOfCommands < ((uint_fast8_t) aNumberOfRepeats + 1) && aSpecialSendRepeatFunction != NULL) {
803  // send special repeat
804  aSpecialSendRepeatFunction();
805  } else {
806  // Header and regular frame
807  mark(aHeaderMarkMicros);
808  space(aHeaderSpaceMicros);
809  sendPulseDistanceWidthData(aOneMarkMicros, aOneSpaceMicros, aZeroMarkMicros, aZeroSpaceMicros, aData, aNumberOfBits,
810  aFlags);
811  }
812 
813  tNumberOfCommands--;
814  // skip last delay!
815  if (tNumberOfCommands > 0) {
816  /*
817  * Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration.
818  */
819  auto tFrameDurationMillis = millis() - tStartOfFrameMillis;
820  if (aRepeatPeriodMillis > tFrameDurationMillis) {
821  delay(aRepeatPeriodMillis - tFrameDurationMillis);
822  }
823  }
824  }
825 }
826 
833  uint_fast8_t aNumberOfBits) {
834 
836  aProtocolConstants->DistanceWidthTimingInfo.OneSpaceMicros, aProtocolConstants->DistanceWidthTimingInfo.ZeroMarkMicros,
837  aProtocolConstants->DistanceWidthTimingInfo.ZeroSpaceMicros, aData, aNumberOfBits, aProtocolConstants->Flags);
838 }
839 
844 void IRsend::sendPulseDistanceWidthData(uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros,
845  uint16_t aZeroSpaceMicros, IRRawDataType aData, uint_fast8_t aNumberOfBits, bool aMSBFirst, bool aSendStopBit) {
846  uint8_t tFlags = 0;
847  if (aMSBFirst) {
848  tFlags = PROTOCOL_IS_MSB_FIRST;
849  }
850  (void) aSendStopBit;
851  sendPulseDistanceWidthData(aOneMarkMicros, aOneSpaceMicros, aZeroMarkMicros, aZeroSpaceMicros, aData, aNumberOfBits, tFlags);
852 }
853 void IRsend::sendPulseDistanceWidthData(uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros,
854  uint16_t aZeroSpaceMicros, IRRawDataType aData, uint_fast8_t aNumberOfBits, uint8_t aFlags) {
855 
856 #if defined(LOCAL_DEBUG)
857  Serial.print(aData, HEX);
858  Serial.print('|');
859  Serial.println(aNumberOfBits);
860  Serial.flush();
861 #endif
862 
863  // For MSBFirst, send data from MSB to LSB until mask bit is shifted out
864  IRRawDataType tMask = 1ULL << (aNumberOfBits - 1);
865  for (uint_fast8_t i = aNumberOfBits; i > 0; i--) {
866  if (((aFlags & PROTOCOL_IS_MSB_FIRST) && (aData & tMask)) || (!(aFlags & PROTOCOL_IS_MSB_FIRST) && (aData & 1))) {
867 #if defined(LOCAL_TRACE)
868  Serial.print('1');
869 #endif
870  mark(aOneMarkMicros);
871  space(aOneSpaceMicros);
872  } else {
873 #if defined(LOCAL_TRACE)
874  Serial.print('0');
875 #endif
876  mark(aZeroMarkMicros);
877  space(aZeroSpaceMicros);
878  }
879  if (aFlags & PROTOCOL_IS_MSB_FIRST) {
880  tMask >>= 1;
881  } else {
882  aData >>= 1;
883  }
884  }
885  /*
886  * Stop bit is sent for all pulse distance protocols i.e. aOneMarkMicros == aZeroMarkMicros.
887  * Therefore it is not sent for Sony and Magiquest :-)
888  */
889  if (!(aFlags & SUPPRESS_STOP_BIT_FOR_THIS_DATA) && aOneMarkMicros == aZeroMarkMicros) {
890  // Send stop bit here
891 #if defined(LOCAL_TRACE)
892  Serial.print('S');
893 #endif
894  mark(aZeroMarkMicros); // Use aZeroMarkMicros for stop bits. This seems to be correct for all protocols :-)
895  }
896 #if defined(LOCAL_TRACE)
897  Serial.println();
898 #endif
899 }
900 
909 void IRsend::sendBiphaseData(uint16_t aBiphaseTimeUnit, uint32_t aData, uint_fast8_t aNumberOfBits) {
910 
911  IR_TRACE_PRINT(F("0x"));
912  IR_TRACE_PRINT(aData, HEX);
913 
914 #if defined(LOCAL_TRACE)
915  Serial.print('S');
916 #endif
917 
918 // Data - Biphase code MSB first
919 // prepare for start with sending the start bit, which is 1
920  uint32_t tMask = 1UL << aNumberOfBits; // mask is now set for the virtual start bit
921  uint_fast8_t tLastBitValue = 1; // Start bit is a 1
922  bool tNextBitIsOne = 1; // Start bit is a 1
923  for (uint_fast8_t i = aNumberOfBits + 1; i > 0; i--) {
924  bool tCurrentBitIsOne = tNextBitIsOne;
925  tMask >>= 1;
926  tNextBitIsOne = ((aData & tMask) != 0) || (i == 1); // true for last bit to avoid extension of mark
927  if (tCurrentBitIsOne) {
928 #if defined(LOCAL_TRACE)
929  Serial.print('1');
930 #endif
931  space(aBiphaseTimeUnit);
932  if (tNextBitIsOne) {
933  mark(aBiphaseTimeUnit);
934  } else {
935  // if next bit is 0, extend the current mark in order to generate a continuous signal without short breaks
936  mark(2 * aBiphaseTimeUnit);
937  }
938  tLastBitValue = 1;
939 
940  } else {
941 #if defined(LOCAL_TRACE)
942  Serial.print('0');
943 #endif
944  if (!tLastBitValue) {
945  mark(aBiphaseTimeUnit);
946  }
947  space(aBiphaseTimeUnit);
948  tLastBitValue = 0;
949  }
950  }
951  IR_TRACE_PRINTLN(F(""));
952 }
953 
969 void IRsend::mark(uint16_t aMarkMicros) {
970 
971 #if defined(SEND_PWM_BY_TIMER) || defined(USE_NO_SEND_PWM)
972 # if !defined(NO_LED_FEEDBACK_CODE)
974  setFeedbackLED(true);
975  }
976 # endif
977 #endif
978 
979 #if defined(SEND_PWM_BY_TIMER)
980  /*
981  * Generate hardware PWM signal
982  */
983  enableSendPWMByTimer(); // Enable timer or ledcWrite() generated PWM output
984  customDelayMicroseconds(aMarkMicros);
985  IRLedOff(); // disables hardware PWM and manages feedback LED
986  return;
987 
988 #elif defined(USE_NO_SEND_PWM)
989  /*
990  * Here we generate no carrier PWM, just simulate an active low receiver signal.
991  */
992 # if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) && !defined(OUTPUT_OPEN_DRAIN)
993  pinModeFast(sendPin, OUTPUT); // active state for mimicking open drain
994 # else
995  digitalWriteFast(sendPin, LOW); // Set output to active low.
996 # endif
997 
998  customDelayMicroseconds(aMarkMicros);
999  IRLedOff();
1000 # if !defined(NO_LED_FEEDBACK_CODE)
1002  setFeedbackLED(false);
1003  }
1004  return;
1005 # endif
1006 
1007 #else // defined(SEND_PWM_BY_TIMER)
1008  /*
1009  * Generate PWM by bit banging
1010  */
1011  unsigned long tStartMicros = micros();
1012  unsigned long tNextPeriodEnding = tStartMicros;
1013  unsigned long tMicros;
1014 # if !defined(NO_LED_FEEDBACK_CODE)
1015  bool FeedbackLedIsActive = false;
1016 # endif
1017 
1018  do {
1019 // digitalToggleFast(_IR_TIMING_TEST_PIN);
1020  /*
1021  * Output the PWM pulse
1022  */
1023  noInterrupts(); // do not let interrupts extend the short on period
1024 # if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN)
1025 # if defined(OUTPUT_OPEN_DRAIN)
1026  digitalWriteFast(sendPin, LOW); // set output with pin mode OUTPUT_OPEN_DRAIN to active low
1027 # else
1028  pinModeFast(sendPin, OUTPUT); // active state for mimicking open drain
1029 # endif
1030 # else
1031  // 3.5 us from FeedbackLed on to pin setting. 5.7 us from call of mark() to pin setting incl. setting of feedback pin.
1032  // 4.3 us from do{ to pin setting if sendPin is no constant
1033  digitalWriteFast(sendPin, HIGH);
1034 # endif
1035  delayMicroseconds (periodOnTimeMicros); // On time is 8 us for 30% duty cycle. This is normally implemented by a blocking wait.
1036 
1037  /*
1038  * Output the PWM pause
1039  */
1040 # if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) && !defined(OUTPUT_OPEN_DRAIN)
1041 # if defined(OUTPUT_OPEN_DRAIN)
1042  digitalWriteFast(sendPin, HIGH); // Set output with pin mode OUTPUT_OPEN_DRAIN to inactive high.
1043 # else
1044  pinModeFast(sendPin, INPUT); // to mimic the open drain inactive state
1045 # endif
1046 
1047 # else
1048  digitalWriteFast(sendPin, LOW);
1049 # endif
1050  /*
1051  * Enable interrupts at start of the longer off period. Required at least to keep micros correct.
1052  * If receive interrupt is still active, it takes 3.4 us from now until receive ISR is active (for 7 us + pop's)
1053  */
1054  interrupts();
1055 
1056 # if !defined(NO_LED_FEEDBACK_CODE)
1057  /*
1058  * Delayed call of setFeedbackLED() to get better startup timing, especially required for consecutive marks
1059  */
1060  if (!FeedbackLedIsActive) {
1061  FeedbackLedIsActive = true;
1063  setFeedbackLED(true);
1064  }
1065  }
1066 # endif
1067  /*
1068  * PWM pause timing
1069  * Measured delta between pause duration values are 13 us for a 16 MHz Uno (from 13 to 26), if interrupts are disabled below
1070  * Measured delta between pause duration values are 20 us for a 16 MHz Uno (from 7.8 to 28), if interrupts are not disabled below
1071  * Minimal pause duration is 5.2 us with NO_LED_FEEDBACK_CODE enabled
1072  * and 8.1 us with NO_LED_FEEDBACK_CODE disabled.
1073  */
1074  tNextPeriodEnding += periodTimeMicros;
1075 #if defined(__AVR__) // micros() for STM sometimes give decreasing values if interrupts are disabled. See https://github.com/stm32duino/Arduino_Core_STM32/issues/1680
1076  noInterrupts(); // disable interrupts (especially the 20 us receive interrupts) only at start of the PWM pause. Otherwise it may extend the pause too much.
1077 #endif
1078  do {
1079 #if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
1080  digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles
1081 #endif
1082  /*
1083  * For AVR @16MHz we have only 4 us resolution.
1084  * The duration of the micros() call itself is 3 us.
1085  * It takes 0.9 us from signal going low here.
1086  * The rest of the loop takes 1.2 us with NO_LED_FEEDBACK_CODE enabled
1087  * and 3 us with NO_LED_FEEDBACK_CODE disabled.
1088  */
1089 #if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
1090  digitalWriteFast(_IR_TIMING_TEST_PIN, LOW); // 2 clock cycles
1091 #endif
1092  /*
1093  * Exit the forever loop if aMarkMicros has reached
1094  */
1095  tMicros = micros();
1096  uint16_t tDeltaMicros = tMicros - tStartMicros;
1097 #if defined(__AVR__)
1098  // reset feedback led in the last pause before end
1099 // tDeltaMicros += (160 / CLOCKS_PER_MICRO); // adding this once increases program size, so do it below !
1100 # if !defined(NO_LED_FEEDBACK_CODE)
1101  if (tDeltaMicros >= aMarkMicros - (30 + (112 / CLOCKS_PER_MICRO))) { // 30 to be constant. Using periodTimeMicros increases program size too much.
1103  setFeedbackLED(false);
1104  }
1105  }
1106 # endif
1107  // Just getting variables and check for end condition takes minimal 3.8 us
1108  if (tDeltaMicros >= aMarkMicros - (112 / CLOCKS_PER_MICRO)) { // To compensate for call duration - 112 is an empirical value
1109 #else
1110  if (tDeltaMicros >= aMarkMicros) {
1111 # if !defined(NO_LED_FEEDBACK_CODE)
1113  setFeedbackLED(false);
1114  }
1115 # endif
1116 #endif
1117 #if defined(__AVR__)
1118  interrupts();
1119 #endif
1120  return;
1121  }
1122  } while (tMicros < tNextPeriodEnding);
1123  } while (true);
1124 # endif
1125 }
1126 
1133 #if defined(SEND_PWM_BY_TIMER)
1134  disableSendPWMByTimer(); // Disable PWM output
1135 #elif defined(USE_NO_SEND_PWM)
1136 # if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) && !defined(OUTPUT_OPEN_DRAIN)
1137  digitalWriteFast(sendPin, LOW); // prepare for all next active states.
1138  pinModeFast(sendPin, INPUT);// inactive state for open drain
1139 # else
1140  digitalWriteFast(sendPin, HIGH); // Set output to inactive high.
1141 # endif
1142 #else
1143 # if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN)
1144 # if defined(OUTPUT_OPEN_DRAIN)
1145  digitalWriteFast(sendPin, HIGH); // Set output to inactive high.
1146 # else
1147  pinModeFast(sendPin, INPUT); // inactive state to mimic open drain
1148 # endif
1149 # else
1150  digitalWriteFast(sendPin, LOW);
1151 # endif
1152 #endif
1153 
1154 #if !defined(NO_LED_FEEDBACK_CODE)
1156  setFeedbackLED(false);
1157  }
1158 #endif
1159 }
1160 
1165 void IRsend::space(uint16_t aSpaceMicros) {
1166  customDelayMicroseconds(aSpaceMicros);
1167 }
1168 
1173 void IRsend::customDelayMicroseconds(unsigned long aMicroseconds) {
1174 #if defined(ESP32) || defined(ESP8266)
1175  // from https://github.com/crankyoldgit/IRremoteESP8266/blob/00b27cc7ea2e7ac1e48e91740723c805a38728e0/src/IRsend.cpp#L123
1176  // Invoke a delay(), where possible, to avoid triggering the WDT.
1177  // see https://github.com/Arduino-IRremote/Arduino-IRremote/issues/1114 for the reason of checking for > 16383)
1178  // delayMicroseconds() is only accurate to 16383 us. Ref: https://www.arduino.cc/en/Reference/delayMicroseconds
1179  if (aMicroseconds > 16383) {
1180  delay(aMicroseconds / 1000UL); // Delay for as many whole milliseconds as we can.
1181  // Delay the remaining sub-millisecond.
1182  delayMicroseconds(static_cast<uint16_t>(aMicroseconds % 1000UL));
1183  } else {
1184  delayMicroseconds(aMicroseconds);
1185  }
1186 #else
1187 
1188 # if defined(__AVR__)
1189  unsigned long start = micros() - (64 / clockCyclesPerMicrosecond()); // - (64 / clockCyclesPerMicrosecond()) for reduced resolution and additional overhead
1190 # else
1191  unsigned long start = micros();
1192 # endif
1193 // overflow invariant comparison :-)
1194  while (micros() - start < aMicroseconds) {
1195  }
1196 #endif
1197 }
1198 
1205 void IRsend::enableIROut(uint_fast8_t aFrequencyKHz) {
1206 #if defined(SEND_PWM_BY_TIMER)
1207  timerConfigForSend(aFrequencyKHz); // must set output pin mode and disable receive interrupt if required, e.g. uses the same resource
1208 
1209 #elif defined(USE_NO_SEND_PWM)
1210  (void) aFrequencyKHz;
1211 
1212 #else
1213  periodTimeMicros = (1000U + (aFrequencyKHz / 2)) / aFrequencyKHz; // rounded value -> 26 for 38.46 kHz, 27 for 37.04 kHz, 25 for 40 kHz.
1214 # if defined(IR_SEND_PIN)
1215  periodOnTimeMicros = (((periodTimeMicros * IR_SEND_DUTY_CYCLE_PERCENT) + 50) / 100U); // +50 for rounding -> 830/100 for 30% and 16 MHz
1216 # else
1217 // Heuristics! We require a nanosecond correction for "slow" digitalWrite() functions
1218  periodOnTimeMicros = (((periodTimeMicros * IR_SEND_DUTY_CYCLE_PERCENT) + 50 - (PULSE_CORRECTION_NANOS / 10)) / 100U); // +50 for rounding -> 530/100 for 30% and 16 MHz
1219 # endif
1220 #endif // defined(SEND_PWM_BY_TIMER)
1221 
1222 #if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) && defined(OUTPUT_OPEN_DRAIN) // the mode INPUT for mimicking open drain is set at IRLedOff()
1223 # if defined(IR_SEND_PIN)
1224  pinModeFast(IR_SEND_PIN, OUTPUT_OPEN_DRAIN);
1225 # else
1226  pinModeFast(sendPin, OUTPUT_OPEN_DRAIN);
1227 # endif
1228 #else
1229 
1230 // For Non AVR platforms pin mode for SEND_PWM_BY_TIMER must be handled by the timerConfigForSend() function
1231 // because ESP 2.0.2 ledcWrite does not work if pin mode is set, and RP2040 requires gpio_set_function(IR_SEND_PIN, GPIO_FUNC_PWM);
1232 # if defined(__AVR__) || !defined(SEND_PWM_BY_TIMER)
1233 # if defined(IR_SEND_PIN)
1234  pinModeFast(IR_SEND_PIN, OUTPUT);
1235 # else
1236  pinModeFast(sendPin, OUTPUT);
1237 # endif
1238 # endif
1239 #endif // defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN)
1240 }
1241 
1242 #if defined(SEND_PWM_BY_TIMER)
1243 // Used for Bang&Olufsen
1244 void IRsend::enableHighFrequencyIROut(uint_fast16_t aFrequencyKHz) {
1245  timerConfigForSend(aFrequencyKHz); // must set output pin mode and disable receive interrupt if required, e.g. uses the same resource
1246  // For Non AVR platforms pin mode for SEND_PWM_BY_TIMER must be handled by the timerConfigForSend() function
1247  // because ESP 2.0.2 ledcWrite does not work if pin mode is set, and RP2040 requires gpio_set_function(IR_SEND_PIN, GPIO_FUNC_PWM);
1248 # if defined(__AVR__)
1249 # if defined(IR_SEND_PIN)
1250  pinModeFast(IR_SEND_PIN, OUTPUT);
1251 # else
1252  pinModeFast(sendPin, OUTPUT);
1253 # endif
1254 # endif
1255 }
1256 #endif
1257 
1259  return PULSE_CORRECTION_NANOS;
1260 }
1261 
1263 #if defined(_IR_MEASURE_TIMING)
1264 #undef _IR_MEASURE_TIMING
1265 #endif
1266 #if defined(LOCAL_TRACE)
1267 #undef LOCAL_TRACE
1268 #endif
1269 #if defined(LOCAL_DEBUG)
1270 #undef LOCAL_DEBUG
1271 #endif
1272 #endif // _IR_SEND_HPP
IRData::address
uint16_t address
Decoded address, Distance protocol (tMarkTicksLong (if tMarkTicksLong == 0, then tMarkTicksShort) << ...
Definition: IRProtocol.h:109
MICROS_PER_TICK
#define MICROS_PER_TICK
microseconds per clock interrupt tick
Definition: IRremote.hpp:249
SUPPRESS_STOP_BIT_FOR_THIS_DATA
#define SUPPRESS_STOP_BIT_FOR_THIS_DATA
Definition: IRProtocol.h:144
BITS_IN_RAW_DATA_TYPE
#define BITS_IN_RAW_DATA_TYPE
Definition: IRremoteInt.h:118
ONKYO
@ ONKYO
Definition: IRProtocol.h:51
DistanceWidthTimingInfoStruct::HeaderMarkMicros
uint16_t HeaderMarkMicros
Definition: IRProtocol.h:79
IRsend::sendApple
void sendApple(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats)
Definition: ir_NEC.hpp:207
IRsend::sendMagiQuest
void sendMagiQuest(uint32_t aWandId, uint16_t aMagnitude)
Definition: ir_MagiQuest.hpp:118
KASEIKYO_DENON
@ KASEIKYO_DENON
Definition: IRProtocol.h:54
PROTOCOL_IS_MSB_FIRST
#define PROTOCOL_IS_MSB_FIRST
Definition: IRProtocol.h:145
setFeedbackLED
void setFeedbackLED(bool aSwitchLedOn)
Flash LED while receiving or sending IR data.
Definition: IRFeedbackLED.hpp:108
JVC
@ JVC
Definition: IRProtocol.h:46
IRData::numberOfBits
uint16_t numberOfBits
Number of bits received for data (address + command + parity) - to determine protocol length if diffe...
Definition: IRProtocol.h:118
IRsend::aNumberOfRepeats
void int_fast8_t aNumberOfRepeats
Definition: IRremoteInt.h:520
IRsend::sendPin
uint8_t sendPin
Definition: IRremoteInt.h:615
IRsend::sendJVC
void sendJVC(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats)
The JVC protocol repeats by skipping the header mark and space -> this leads to a poor repeat detecti...
Definition: ir_JVC.hpp:92
IRsend::sendKaseikyo_Sharp
void sendKaseikyo_Sharp(uint16_t aAddress, uint8_t aData, int_fast8_t aNumberOfRepeats)
Stub using Kaseikyo with SHARP_VENDOR_ID_CODE.
Definition: ir_Kaseikyo.hpp:185
KASEIKYO_SHARP
@ KASEIKYO_SHARP
Definition: IRProtocol.h:55
FAST
@ FAST
Definition: IRProtocol.h:71
IRsend::setSendPin
void setSendPin(uint_fast8_t aSendPin)
Definition: IRSend.hpp:128
pinModeFast
#define pinModeFast
Definition: digitalWriteFast.h:371
IRsend::sendBoseWave
void sendBoseWave(uint8_t aCommand, int_fast8_t aNumberOfRepeats=NO_REPEATS)
Definition: ir_BoseWave.hpp:57
IRsend::sendSony
void sendSony(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, uint8_t numberOfBits=12)
Definition: ir_Sony.hpp:103
digitalWriteFast
#define digitalWriteFast
Definition: digitalWriteFast.h:339
PULSE_CORRECTION_NANOS
#define PULSE_CORRECTION_NANOS
Define to disable carrier PWM generation in software and use (restricted) hardware PWM.
Definition: IRremote.hpp:234
IRsend::mark
void mark(uint16_t aMarkMicros)
Sends an IR mark for the specified number of microseconds.
Definition: IRSend.hpp:969
SONY
@ SONY
Definition: IRProtocol.h:64
KASEIKYO_JVC
@ KASEIKYO_JVC
Definition: IRProtocol.h:56
PulseDistanceWidthProtocolConstants::SpecialSendRepeatFunction
void(* SpecialSendRepeatFunction)()
Definition: IRProtocol.h:138
IRsend::sendSamsung48
void sendSamsung48(uint16_t aAddress, uint32_t aCommand, int_fast8_t aNumberOfRepeats)
Here we send Samsung48 We send 2 x (8 bit command and then ~command)
Definition: ir_Samsung.hpp:213
IRsend::sendRC6
void sendRC6(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aEnableAutomaticToggle=true)
Assemble raw data for RC6 from parameters and toggle state and send We do not wait for the minimal tr...
Definition: ir_RC5_RC6.hpp:350
IRDATA_FLAGS_IS_REPEAT
#define IRDATA_FLAGS_IS_REPEAT
The gap between the preceding frame is as smaller than the maximum gap expected for a repeat....
Definition: IRProtocol.h:91
decode_type_t
decode_type_t
An enum consisting of all supported formats.
Definition: IRProtocol.h:40
IRsend::customDelayMicroseconds
static void customDelayMicroseconds(unsigned long aMicroseconds)
Custom delay function that circumvents Arduino's delayMicroseconds 16 bit limit and is (mostly) not e...
Definition: IRSend.hpp:1173
IRsend::sendPulseDistanceWidth
void sendPulseDistanceWidth(PulseDistanceWidthProtocolConstants *aProtocolConstants, IRRawDataType aData, uint_fast8_t aNumberOfBits, int_fast8_t aNumberOfRepeats)
Sends PulseDistance frames and repeats and enables receiver again.
Definition: IRSend.hpp:702
IRsend::IRsend
IRsend()
Definition: IRSend.hpp:67
SAMSUNG
@ SAMSUNG
Definition: IRProtocol.h:60
BOSEWAVE
@ BOSEWAVE
Definition: IRProtocol.h:67
DistanceWidthTimingInfoStruct::OneSpaceMicros
uint16_t OneSpaceMicros
Definition: IRProtocol.h:82
IRData::decodedRawData
IRRawDataType decodedRawData
Up to 32/64 bit decoded raw data, to be used for send functions.
Definition: IRProtocol.h:112
IRsend::sendRaw_P
void sendRaw_P(const uint8_t aBufferWithTicks[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz)
New function using an 8 byte tick (50 us) timing array in FLASH to save program memory Raw data start...
Definition: IRSend.hpp:489
IRsend
Main class for sending IR signals.
Definition: IRremoteInt.h:419
DistanceWidthTimingInfoStruct::ZeroMarkMicros
uint16_t ZeroMarkMicros
Definition: IRProtocol.h:83
IR_TRACE_PRINT
#define IR_TRACE_PRINT(...)
Definition: IRremoteInt.h:148
MAGIQUEST
@ MAGIQUEST
Definition: IRProtocol.h:69
NEC2
@ NEC2
Definition: IRProtocol.h:50
PulseDistanceWidthProtocolConstants::Flags
uint8_t Flags
Definition: IRProtocol.h:136
IRsend::sendSamsung
void sendSamsung(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats)
Here we send Samsung32 If we get a command < 0x100, we send command and then ~command If we get an ad...
Definition: ir_Samsung.hpp:167
IRsend::sendNEC2
void sendNEC2(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats)
Definition: ir_NEC.hpp:185
DistanceWidthTimingInfoStruct
Definition: IRProtocol.h:78
IRsend::sendRaw
void sendRaw(const uint8_t aBufferWithTicks[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz)
Function using an 8 byte tick timing array to save program memory Raw data starts with a Mark.
Definition: IRSend.hpp:435
PulseDistanceWidthProtocolConstants
Definition: IRProtocol.h:132
FeedbackLEDControlStruct::LedFeedbackEnabled
uint8_t LedFeedbackEnabled
LED_FEEDBACK_ENABLED_FOR_RECEIVE or LED_FEEDBACK_ENABLED_FOR_SEND -> enable blinking of pin on IR pro...
Definition: IRFeedbackLED.hpp:44
PANASONIC
@ PANASONIC
Definition: IRProtocol.h:52
timerConfigForSend
void timerConfigForSend(uint16_t aFrequencyKHz)
IF PWM should be generated not by software, but by a timer, this function sets output pin mode,...
Definition: IRTimer.hpp:127
IRData
Data structure for the user application, available as decodedIRData.
Definition: IRProtocol.h:107
IR_SEND_PIN
#define IR_SEND_PIN
Definition: TinyIRSender.hpp:65
IRData::flags
uint8_t flags
IRDATA_FLAGS_IS_REPEAT, IRDATA_FLAGS_WAS_OVERFLOW etc. See IRDATA_FLAGS_* definitions above.
Definition: IRProtocol.h:119
IRsend::sendSamsungLG
void sendSamsungLG(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats)
Definition: ir_Samsung.hpp:145
LED_FEEDBACK_ENABLED_FOR_SEND
#define LED_FEEDBACK_ENABLED_FOR_SEND
Definition: IRremoteInt.h:357
DistanceWidthTimingInfoStruct::OneMarkMicros
uint16_t OneMarkMicros
Definition: IRProtocol.h:81
DO_NOT_ENABLE_LED_FEEDBACK
#define DO_NOT_ENABLE_LED_FEEDBACK
Definition: IRremoteInt.h:354
IRsend::sendSharp
void sendSharp(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats)
Definition: ir_Denon.hpp:116
IRsend::begin
void begin(uint_fast8_t aSendPin)
Initializes the send pin and enable LED feedback with board specific FEEDBACK_LED_ON() and FEEDBACK_L...
Definition: IRSend.hpp:121
IRsend::sendKaseikyo_JVC
void sendKaseikyo_JVC(uint16_t aAddress, uint8_t aData, int_fast8_t aNumberOfRepeats)
Stub using Kaseikyo with JVC_VENDOR_ID_CODE.
Definition: ir_Kaseikyo.hpp:192
IRsend::periodOnTimeMicros
uint16_t periodOnTimeMicros
Definition: IRremoteInt.h:618
IRData::command
uint16_t command
Decoded command, Distance protocol (tMarkTicksShort << 8) | tSpaceTicksShort.
Definition: IRProtocol.h:110
NEC
@ NEC
Definition: IRProtocol.h:49
IRsend::sendPanasonic
void sendPanasonic(uint16_t aAddress, uint8_t aData, int_fast8_t aNumberOfRepeats)
Stub using Kaseikyo with PANASONIC_VENDOR_ID_CODE.
Definition: ir_Kaseikyo.hpp:164
APPLE
@ APPLE
Definition: IRProtocol.h:44
IRsend::getPulseCorrectionNanos
uint16_t getPulseCorrectionNanos()
Definition: IRSend.hpp:1258
IRsend::write
size_t write(IRData *aIRSendData, int_fast8_t aNumberOfRepeats=NO_REPEATS)
Interprets and sends a IRData structure.
Definition: IRSend.hpp:170
IRsend::sendDenon
void sendDenon(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aSendSharp=false)
Definition: ir_Denon.hpp:120
LEGO_PF
@ LEGO_PF
Definition: IRProtocol.h:68
DistanceWidthTimingInfoStruct::ZeroSpaceMicros
uint16_t ZeroSpaceMicros
Definition: IRProtocol.h:84
IRRawDataType
uint32_t IRRawDataType
Definition: IRremoteInt.h:117
IR_TRACE_PRINTLN
#define IR_TRACE_PRINTLN(...)
Definition: IRremoteInt.h:149
KASEIKYO_MITSUBISHI
@ KASEIKYO_MITSUBISHI
Definition: IRProtocol.h:57
DENON
@ DENON
Definition: IRProtocol.h:45
enableSendPWMByTimer
void enableSendPWMByTimer()
Enables output of the PWM signal of the timer at the timer pin.
Definition: IRTimer.hpp:133
IrSender
IRsend IrSender
Definition: IRSend.hpp:65
IR_SEND_DUTY_CYCLE_PERCENT
#define IR_SEND_DUTY_CYCLE_PERCENT
Duty cycle in percent for sent signals.
Definition: IRremote.hpp:242
SIRCS_12_PROTOCOL
#define SIRCS_12_PROTOCOL
Definition: IRProtocol.h:74
RC5
@ RC5
Definition: IRProtocol.h:58
PulseDistanceWidthProtocolConstants::DistanceWidthTimingInfo
DistanceWidthTimingInfoStruct DistanceWidthTimingInfo
Definition: IRProtocol.h:135
IRsend::periodTimeMicros
uint16_t periodTimeMicros
Definition: IRremoteInt.h:617
IRsend::space
static void space(uint16_t aSpaceMicros)
Sends an IR space for the specified number of microseconds.
Definition: IRSend.hpp:1165
IRsend::sendRC5
void sendRC5(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aEnableAutomaticToggle=true)
Definition: ir_RC5_RC6.hpp:103
FeedbackLEDControl
struct FeedbackLEDControlStruct FeedbackLEDControl
The feedback LED control instance.
Definition: IRFeedbackLED.hpp:47
disableSendPWMByTimer
void disableSendPWMByTimer()
Disables output of the PWM signal of the timer at the timer pin and set it to inactive.
Definition: IRTimer.hpp:138
IRsend::sendKaseikyo_Mitsubishi
void sendKaseikyo_Mitsubishi(uint16_t aAddress, uint8_t aData, int_fast8_t aNumberOfRepeats)
Stub using Kaseikyo with MITSUBISHI_VENDOR_ID_CODE.
Definition: ir_Kaseikyo.hpp:178
RC6
@ RC6
Definition: IRProtocol.h:59
IRsend::IRLedOff
void IRLedOff()
Just switch the IR sending LED off to send an IR space A space is "no output", so the PWM output is d...
Definition: IRSend.hpp:1132
IRsend::sendPulseDistanceWidthFromArray
void sendPulseDistanceWidthFromArray(uint_fast8_t aFrequencyKHz, uint16_t aHeaderMarkMicros, uint16_t aHeaderSpaceMicros, uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, uint16_t aZeroSpaceMicros, IRRawDataType *aDecodedRawDataArray, uint16_t aNumberOfBits, uint8_t aFlags, uint16_t aRepeatPeriodMillis, int_fast8_t aNumberOfRepeats)
Definition: IRSend.hpp:549
sendFAST
void sendFAST(uint8_t aSendPin, uint16_t aCommand, uint_fast8_t aNumberOfRepeats=0)
Definition: TinyIRSender.hpp:294
IRsend::sendPulseDistanceWidthData
void sendPulseDistanceWidthData(PulseDistanceWidthProtocolConstants *aProtocolConstants, IRRawDataType aData, uint_fast8_t aNumberOfBits)
Sends PulseDistance data The output always ends with a space Each additional call costs 16 bytes prog...
Definition: IRSend.hpp:832
IRsend::sendOnkyo
void sendOnkyo(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats)
Definition: ir_NEC.hpp:195
PulseDistanceWidthProtocolConstants::RepeatPeriodMillis
unsigned int RepeatPeriodMillis
Definition: IRProtocol.h:137
IRsend::sendLegoPowerFunctions
void sendLegoPowerFunctions(uint8_t aChannel, uint8_t tCommand, uint8_t aMode, bool aDoSend5Times=true)
Definition: ir_Lego.hpp:96
USE_DEFAULT_FEEDBACK_LED_PIN
#define USE_DEFAULT_FEEDBACK_LED_PIN
Definition: IRremoteInt.h:64
SAMSUNGLG
@ SAMSUNGLG
Definition: IRProtocol.h:61
IRData::protocol
decode_type_t protocol
UNKNOWN, NEC, SONY, RC5, PULSE_DISTANCE, ...
Definition: IRProtocol.h:108
IRsend::sendBiphaseData
void sendBiphaseData(uint16_t aBiphaseTimeUnit, uint32_t aData, uint_fast8_t aNumberOfBits)
Sends Biphase data MSB first Always send start bit, do not send the trailing space of the start bit 0...
Definition: IRSend.hpp:909
SAMSUNG48
@ SAMSUNG48
Definition: IRProtocol.h:62
IRsend::sendLG
void sendLG(uint8_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats)
LG uses the NEC repeat.
Definition: ir_LG.hpp:165
PulseDistanceWidthProtocolConstants::FrequencyKHz
uint_fast8_t FrequencyKHz
Definition: IRProtocol.h:134
DistanceWidthTimingInfoStruct::HeaderSpaceMicros
uint16_t HeaderSpaceMicros
Definition: IRProtocol.h:80
IRsend::aCommand
void aCommand
Definition: IRremoteInt.h:582
LG
@ LG
Definition: IRProtocol.h:47
IRsend::enableIROut
void enableIROut(uint_fast8_t aFrequencyKHz)
Enables IR output.
Definition: IRSend.hpp:1205
IRsend::sendKaseikyo_Denon
void sendKaseikyo_Denon(uint16_t aAddress, uint8_t aData, int_fast8_t aNumberOfRepeats)
Stub using Kaseikyo with DENON_VENDOR_ID_CODE.
Definition: ir_Kaseikyo.hpp:171
setLEDFeedback
void setLEDFeedback(uint8_t aFeedbackLEDPin, uint8_t aEnableLEDFeedback)
Enable blinking of feedback LED (LED_BUILTIN is taken as default) on IR sending and receiving Cannot ...
Definition: IRFeedbackLED.hpp:56
SHARP
@ SHARP
Definition: IRProtocol.h:63
IRsend::sendNEC
void sendNEC(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats)
NEC Send frame and special repeats There is NO delay after the last sent repeat!
Definition: ir_NEC.hpp:176