diff --git a/Doxyfile b/Doxyfile
index 0ce0d4b..a879240 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -58,7 +58,7 @@ PROJECT_LOGO =
# entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used.
-OUTPUT_DIRECTORY = ..\Arduino-IRremote_gh-pages
+OUTPUT_DIRECTORY = E:\WORKSPACE_ARDUINO\lib\Arduino-IRremote_gh-pages
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
# directories (in 2 levels) under the output directory of each output format and
diff --git a/README.md b/README.md
index 9ed35c9..3c7847a 100644
--- a/README.md
+++ b/README.md
@@ -598,7 +598,9 @@ A simple example can be tested online with [WOKWI](https://wokwi.com/projects/33
#### TinyReceiver + TinySender
If **code size** or **timer usage** matters, look at these examples.
-The **[TinyReceiver](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/TinyReceiver/TinyReceiver.ino)** example uses the **TinyIRReceiver** library which can **only receive NEC, Extended NEC, ONKYO and FAST protocols, but does not require any timer**. They use pin change interrupt for on the fly decoding, which is the reason for the restricted protocol choice.
+The **[TinyReceiver](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/TinyReceiver/TinyReceiver.ino)** example uses the **TinyIRReceiver** library
+which can **only receive NEC, Extended NEC, ONKYO and FAST protocols, but does not require any timer**.
+They use pin change interrupt for on the fly decoding, which is the reason for the restricted protocol choice.
TinyReceiver can be tested online with [WOKWI](https://wokwi.com/arduino/projects/339264565653013075).
The **[TinySender](https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/examples/TinySender/TinySender.ino)** example uses the **TinyIRSender** library which can **only send NEC, ONKYO and FAST protocols**.
@@ -718,13 +720,15 @@ These next macros for **TinyIRReceiver** must be defined in your program before
| `NO_LED_FEEDBACK_CODE` | disabled | Disables the feedback LED function. Saves 14 bytes program memory. |
| `DISABLE_PARITY_CHECKS` | disabled | Disables the addres and command parity checks. Saves 48 bytes program memory. |
| `USE_EXTENDED_NEC_PROTOCOL` | disabled | Like NEC, but take the 16 bit address as one 16 bit value and not as 8 bit normal and 8 bit inverted value. |
-| `USE_ONKYO_PROTOCOL` | disabled | Like NEC, but take both the 16 bit address and command each as one 16 bit value and not as 8 bit normal and 8 bit inverted value. |
+| `USE_ONKYO_PROTOCOL` | disabled | Like NEC, but take the 16 bit address and command each as one 16 bit value and not as 8 bit normal and 8 bit inverted value. |
| `USE_FAST_PROTOCOL` | disabled | Use FAST protocol (no address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command) instead of NEC. |
| `ENABLE_NEC2_REPEATS` | disabled | Instead of sending / receiving the NEC special repeat code, send / receive the original frame for repeat. |
| `USE_CALLBACK_FOR_TINY_RECEIVER` | disabled | Call the fixed function `void handleReceivedTinyIRData()` each time a frame or repeat is received. |
The next macro for **IRCommandDispatcher** must be defined in your program before the line `#include ` to take effect.
+| `USE_TINY_IR_RECEIVER` | disabled | Use [TinyReceiver](https://github.com/Arduino-IRremote/Arduino-IRremote#tinyreceiver--tinysender) for receiving IR codes. |
| `IR_COMMAND_HAS_MORE_THAN_8_BIT` | disabled | Enables mapping and dispatching of IR commands consisting of more than 8 bits. Saves up to 160 bytes program memory and 4 bytes RAM + 1 byte RAM per mapping entry. |
+| `BUZZER_PIN` | | If `USE_TINY_IR_RECEIVER` is enabled, the pin to be used for the optional 50 ms buzzer feedback before executing a command. Other IR libraries than Tiny are not compatible with tone() command. |
### Changing include (*.h) files with Arduino IDE
First, use *Sketch > Show Sketch Folder (Ctrl+K)*.
diff --git a/changelog.md b/changelog.md
index 2bf36f7..0f1f2e9 100644
--- a/changelog.md
+++ b/changelog.md
@@ -2,6 +2,10 @@
The latest version may not be released!
See also the commit log at github: https://github.com/Arduino-IRremote/Arduino-IRremote/commits/master
+# 4.2.2
+- Added convenience function isIRReceiverAttachedForTinyReceiver().
+- Added Extended NEC Protocol to TinyIR by Butzerb
+
# 4.2.1
- Fix wrong type of tEnableLEDFeedback in IRSend.hpp and IRReceive.hpp.
- TinyReceiver 2.0
diff --git a/examples/AllProtocolsOnLCD/ADCUtils.h b/examples/AllProtocolsOnLCD/ADCUtils.h
index d677fd3..ce283c2 100644
--- a/examples/AllProtocolsOnLCD/ADCUtils.h
+++ b/examples/AllProtocolsOnLCD/ADCUtils.h
@@ -85,6 +85,7 @@
#define ADC_TEMPERATURE_CHANNEL_MUX 15
#define ADC_1_1_VOLT_CHANNEL_MUX 12
#define ADC_GND_CHANNEL_MUX 13
+#define ADC_CHANNEL_MUX_MASK 0x0F
#elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
#define ADC_ISCR_CHANNEL_MUX 3
@@ -92,20 +93,25 @@
#define ADC_1_1_VOLT_CHANNEL_MUX 12
#define ADC_GND_CHANNEL_MUX 14
#define ADC_VCC_4TH_CHANNEL_MUX 13
+#define ADC_CHANNEL_MUX_MASK 0x1F
#elif defined(__AVR_ATmega328P__)
#define ADC_TEMPERATURE_CHANNEL_MUX 8
#define ADC_1_1_VOLT_CHANNEL_MUX 14
#define ADC_GND_CHANNEL_MUX 15
+#define ADC_CHANNEL_MUX_MASK 0x0F
#elif defined(__AVR_ATmega32U4__)
#define ADC_TEMPERATURE_CHANNEL_MUX 0x27
#define ADC_1_1_VOLT_CHANNEL_MUX 0x1E
#define ADC_GND_CHANNEL_MUX 0x1F
+#define ADC_CHANNEL_MUX_MASK 0x3F
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__)
#define ADC_1_1_VOLT_CHANNEL_MUX 0x1E
#define ADC_GND_CHANNEL_MUX 0x1F
+#define ADC_CHANNEL_MUX_MASK 0x1F
+
#define INTERNAL INTERNAL1V1
#else
@@ -164,7 +170,10 @@ uint16_t waitAndReadADCChannelWithReferenceAndRestoreADMUXAndReference(uint8_t a
uint16_t readADCChannelWithOversample(uint8_t aADCChannelNumber, uint8_t aOversampleExponent);
void setADCChannelAndReferenceForNextConversion(uint8_t aADCChannelNumber, uint8_t aReference);
uint16_t readADCChannelWithReferenceOversampleFast(uint8_t aADCChannelNumber, uint8_t aReference, uint8_t aOversampleExponent);
-uint16_t readADCChannelWithReferenceMultiSamples(uint8_t aADCChannelNumber, uint8_t aReference, uint8_t aNumberOfSamples);
+uint32_t readADCChannelMultiSamples(uint8_t aPrescale, uint16_t aNumberOfSamples);
+uint16_t readADCChannelMultiSamplesWithReference(uint8_t aADCChannelNumber, uint8_t aReference, uint8_t aNumberOfSamples);
+uint32_t readADCChannelMultiSamplesWithReferenceAndPrescaler(uint8_t aADCChannelNumber, uint8_t aReference, uint8_t aPrescale,
+ uint16_t aNumberOfSamples);
uint16_t readADCChannelWithReferenceMax(uint8_t aADCChannelNumber, uint8_t aReference, uint16_t aNumberOfSamples);
uint16_t readADCChannelWithReferenceMaxMicros(uint8_t aADCChannelNumber, uint8_t aReference, uint16_t aMicrosecondsToAquire);
uint16_t readUntil4ConsecutiveValuesAreEqual(uint8_t aADCChannelNumber, uint8_t aReference, uint8_t aDelay,
@@ -192,7 +201,8 @@ float getCPUTemperatureSimple(void);
float getCPUTemperature(void);
float getTemperature(void) __attribute__ ((deprecated ("Renamed to getCPUTemperature()"))); // deprecated
-bool isVCCUSBPowered() ;
+bool isVCCUSBPowered();
+bool isVCCUSBPowered(Print *aSerial);
bool isVCCUndervoltageMultipleTimes();
void resetCounterForVCCUndervoltageMultipleTimes();
bool isVCCUndervoltage();
diff --git a/examples/AllProtocolsOnLCD/ADCUtils.hpp b/examples/AllProtocolsOnLCD/ADCUtils.hpp
index 19744a8..0cff66e 100644
--- a/examples/AllProtocolsOnLCD/ADCUtils.hpp
+++ b/examples/AllProtocolsOnLCD/ADCUtils.hpp
@@ -58,7 +58,7 @@ union WordUnionForADCUtils {
* Enable this to see information on each call.
* Since there should be no library which uses Serial, it should only be enabled for development purposes.
*/
-#if defined(DEBUG)
+#if defined(DEBUG) && !defined(LOCAL_DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
@@ -161,12 +161,15 @@ uint8_t checkAndWaitForReferenceAndChannelToSwitch(uint8_t aADCChannelNumber, ui
if ((tOldADMUX & MASK_FOR_ADC_REFERENCE) != tNewReference && (aReference == INTERNAL || aReference == INTERNAL2V56)) {
#else
if ((tOldADMUX & MASK_FOR_ADC_REFERENCE) != tNewReference && aReference == INTERNAL) {
+#endif
+#if defined(LOCAL_DEBUG)
+ Serial.println(F("Switch from DEFAULT to INTERNAL"));
#endif
/*
* Switch reference from DEFAULT to INTERNAL
*/
delayMicroseconds(8000); // experimental value is >= 7600 us for Nano board and 6200 for Uno board
- } else if ((tOldADMUX & 0x0F) != aADCChannelNumber) {
+ } else if ((tOldADMUX & ADC_CHANNEL_MUX_MASK) != aADCChannelNumber) {
if (aADCChannelNumber == ADC_1_1_VOLT_CHANNEL_MUX) {
/*
* Internal 1.1 Volt channel requires <= 200 us for Nano board
@@ -249,9 +252,10 @@ uint16_t readADCChannelWithReferenceOversampleFast(uint8_t aADCChannelNumber, ui
/*
* Returns sum of all sample values
- * Conversion time is defined as 0.104 milliseconds for 16 MHz Arduino by ADC_PRESCALE in ADCUtils.h.
+ * Conversion time is defined as 0.104 milliseconds for 16 MHz Arduino by ADC_PRESCALE (=ADC_PRESCALE128) in ADCUtils.h.
+ * @ param aNumberOfSamples If > 64 an overflow may occur.
*/
-uint16_t readADCChannelWithReferenceMultiSamples(uint8_t aADCChannelNumber, uint8_t aReference, uint8_t aNumberOfSamples) {
+uint16_t readADCChannelMultiSamplesWithReference(uint8_t aADCChannelNumber, uint8_t aReference, uint8_t aNumberOfSamples) {
uint16_t tSumValue = 0;
ADMUX = aADCChannelNumber | (aReference << SHIFT_VALUE_FOR_REFERENCE);
@@ -275,6 +279,65 @@ uint16_t readADCChannelWithReferenceMultiSamples(uint8_t aADCChannelNumber, uint
return tSumValue;
}
+/*
+ * Returns sum of all sample values
+ * Conversion time is defined as 0.104 milliseconds for 16 MHz Arduino for ADC_PRESCALE128 in ADCUtils.h.
+ * @ param aPrescale can be one of ADC_PRESCALE2, ADC_PRESCALE4, 8, 16, 32, 64, 128.
+ * ADC_PRESCALE32 is recommended for excellent linearity and fast readout of 26 microseconds
+ * @ param aNumberOfSamples If > 16k an overflow may occur.
+ */
+uint32_t readADCChannelMultiSamplesWithReferenceAndPrescaler(uint8_t aADCChannelNumber, uint8_t aReference, uint8_t aPrescale,
+ uint16_t aNumberOfSamples) {
+ uint32_t tSumValue = 0;
+ ADMUX = aADCChannelNumber | (aReference << SHIFT_VALUE_FOR_REFERENCE);
+
+ ADCSRB = 0; // Free running mode. Only active if ADATE is set to 1.
+ // ADSC-StartConversion ADATE-AutoTriggerEnable ADIF-Reset Interrupt Flag
+ ADCSRA = (_BV(ADEN) | _BV(ADSC) | _BV(ADATE) | _BV(ADIF) | aPrescale);
+
+ for (uint16_t i = 0; i < aNumberOfSamples; i++) {
+ /*
+ * wait for free running conversion to finish.
+ * Do not wait for ADSC here, since ADSC is only low for 1 ADC Clock cycle on free running conversion.
+ */
+ loop_until_bit_is_set(ADCSRA, ADIF);
+
+ ADCSRA |= _BV(ADIF); // clear bit to enable recognizing next conversion has finished
+ // Add value
+ tSumValue += ADCL | (ADCH << 8); // using WordUnionForADCUtils does not save space here
+ // tSumValue += (ADCH << 8) | ADCL; // this does NOT work!
+ }
+ ADCSRA &= ~_BV(ADATE); // Disable auto-triggering (free running mode)
+ return tSumValue;
+}
+
+/*
+ * Returns sum of all sample values
+ * Assumes, that channel and reference are still set to the right values
+ * @ param aNumberOfSamples If > 16k an overflow may occur.
+ */
+uint32_t readADCChannelMultiSamples(uint8_t aPrescale, uint16_t aNumberOfSamples) {
+ uint32_t tSumValue = 0;
+
+ ADCSRB = 0; // Free running mode. Only active if ADATE is set to 1.
+ // ADSC-StartConversion ADATE-AutoTriggerEnable ADIF-Reset Interrupt Flag
+ ADCSRA = (_BV(ADEN) | _BV(ADSC) | _BV(ADATE) | _BV(ADIF) | aPrescale);
+
+ for (uint16_t i = 0; i < aNumberOfSamples; i++) {
+ /*
+ * wait for free running conversion to finish.
+ * Do not wait for ADSC here, since ADSC is only low for 1 ADC Clock cycle on free running conversion.
+ */
+ loop_until_bit_is_set(ADCSRA, ADIF);
+
+ ADCSRA |= _BV(ADIF); // clear bit to enable recognizing next conversion has finished
+ // Add value
+ tSumValue += ADCL | (ADCH << 8); // using WordUnionForADCUtils does not save space here
+ // tSumValue += (ADCH << 8) | ADCL; // this does NOT work!
+ }
+ ADCSRA &= ~_BV(ADATE); // Disable auto-triggering (free running mode)
+ return tSumValue;
+}
/*
* use ADC_PRESCALE32 which gives 26 us conversion time and good linearity
* @return the maximum value of aNumberOfSamples samples.
@@ -408,7 +471,7 @@ uint16_t readUntil4ConsecutiveValuesAreEqual(uint8_t aADCChannelNumber, uint8_t
*/
float getVCCVoltageSimple(void) {
// use AVCC with (optional) external capacitor at AREF pin as reference
- float tVCC = readADCChannelWithReferenceMultiSamples(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 4);
+ float tVCC = readADCChannelMultiSamplesWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 4);
return ((1023 * 1.1 * 4) / tVCC);
}
@@ -419,7 +482,7 @@ float getVCCVoltageSimple(void) {
*/
uint16_t getVCCVoltageMillivoltSimple(void) {
// use AVCC with external capacitor at AREF pin as reference
- uint16_t tVCC = readADCChannelWithReferenceMultiSamples(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 4);
+ uint16_t tVCC = readADCChannelMultiSamplesWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 4);
return ((1023L * ADC_INTERNAL_REFERENCE_MILLIVOLT * 4) / tVCC);
}
@@ -459,6 +522,9 @@ uint16_t getVCCVoltageMillivolt(void) {
return ((1023L * ADC_INTERNAL_REFERENCE_MILLIVOLT) / tVCC);
}
+/*
+ * Does not set sVCCVoltageMillivolt
+ */
uint16_t printVCCVoltageMillivolt(Print *aSerial) {
aSerial->print(F("VCC="));
uint16_t tVCCVoltageMillivolt = getVCCVoltageMillivolt();
@@ -480,7 +546,7 @@ void readAndPrintVCCVoltageMillivolt(Print *aSerial) {
*/
void readVCCVoltageSimple(void) {
// use AVCC with (optional) external capacitor at AREF pin as reference
- float tVCC = readADCChannelWithReferenceMultiSamples(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 4);
+ float tVCC = readADCChannelMultiSamplesWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 4);
sVCCVoltage = (1023 * (((float) ADC_INTERNAL_REFERENCE_MILLIVOLT) / 1000) * 4) / tVCC;
}
@@ -491,7 +557,7 @@ void readVCCVoltageSimple(void) {
*/
void readVCCVoltageMillivoltSimple(void) {
// use AVCC with external capacitor at AREF pin as reference
- uint16_t tVCCVoltageMillivoltRaw = readADCChannelWithReferenceMultiSamples(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 4);
+ uint16_t tVCCVoltageMillivoltRaw = readADCChannelMultiSamplesWithReference(ADC_1_1_VOLT_CHANNEL_MUX, DEFAULT, 4);
sVCCVoltageMillivolt = (1023L * ADC_INTERNAL_REFERENCE_MILLIVOLT * 4) / tVCCVoltageMillivoltRaw;
}
@@ -548,6 +614,25 @@ bool isVCCUSBPowered() {
&& sVCCVoltageMillivolt < VOLTAGE_USB_POWERED_UPPER_THRESHOLD_MILLIVOLT);
}
+/*
+ * Return true if sVCCVoltageMillivolt is > 4.3 V and < 4.95 V
+ */
+bool isVCCUSBPowered(Print *aSerial) {
+ readVCCVoltageMillivolt();
+ aSerial->print(F("USB powered is "));
+ bool tReturnValue;
+ if (VOLTAGE_USB_POWERED_LOWER_THRESHOLD_MILLIVOLT
+ < sVCCVoltageMillivolt&& sVCCVoltageMillivolt < VOLTAGE_USB_POWERED_UPPER_THRESHOLD_MILLIVOLT) {
+ tReturnValue = true;
+ aSerial->print(F("true "));
+ } else {
+ tReturnValue = false;
+ aSerial->print(F("false "));
+ }
+ printVCCVoltageMillivolt(aSerial);
+ return tReturnValue;
+}
+
/*
* @ return true only once, when VCC_UNDERVOLTAGE_CHECKS_BEFORE_STOP (6) times voltage too low -> shutdown
*/
@@ -685,7 +770,7 @@ float getCPUTemperature(void) {
}
#else // defined(ADC_UTILS_ARE_AVAILABLE)
-// Dummy definition of functions defined in ADCUtils to compile examples without errors
+// Dummy definition of functions defined in ADCUtils to compile examples for non AVR platforms without errors
/*
* Persistent storage for VCC value
*/
diff --git a/examples/IRDispatcherDemo/IRCommandDispatcher.h b/examples/IRDispatcherDemo/IRCommandDispatcher.h
index e3f4612..83a98b2 100644
--- a/examples/IRDispatcherDemo/IRCommandDispatcher.h
+++ b/examples/IRDispatcherDemo/IRCommandDispatcher.h
@@ -5,14 +5,14 @@
*
* To run this example you need to install the "IRremote" or "IRMP" library under "Tools -> Manage Libraries..." or "Ctrl+Shift+I"
*
- * Copyright (C) 2019-2021 Armin Joachimsmeyer
+ * Copyright (C) 2019-2024 Armin Joachimsmeyer
* armin.joachimsmeyer@gmail.com
*
* This file is part of ServoEasing https://github.com/ArminJo/ServoEasing.
* This file is part of IRMP https://github.com/IRMP-org/IRMP.
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
- * ServoEasing is free software: you can redistribute it and/or modify
+ * IRCommandDispatcher is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
@@ -38,6 +38,22 @@
#define IR_COMMAND_FLAG_REPEATABLE 0x01 // repeat accepted
#define IR_COMMAND_FLAG_NON_BLOCKING 0x02 // Non blocking (short) command that can be processed any time and may interrupt other IR commands - used for stop, set direction etc.
#define IR_COMMAND_FLAG_REPEATABLE_NON_BLOCKING (IR_COMMAND_FLAG_REPEATABLE | IR_COMMAND_FLAG_NON_BLOCKING)
+#define IR_COMMAND_FLAG_BEEP 0x04 // Do a single short beep before executing command. May not be useful for short or repeating commands.
+#define IR_COMMAND_FLAG_BLOCKING_BEEP (IR_COMMAND_FLAG_BLOCKING | IR_COMMAND_FLAG_BEEP)
+
+
+#if !defined(IS_STOP_REQUESTED)
+#define IS_STOP_REQUESTED IRDispatcher.requestToStopReceived
+#endif
+#if !defined(RETURN_IF_STOP)
+#define RETURN_IF_STOP if (IRDispatcher.requestToStopReceived) return
+#endif
+#if !defined(BREAK_IF_STOP)
+#define BREAK_IF_STOP if (IRDispatcher.requestToStopReceived) break
+#endif
+#if !defined(DELAY_AND_RETURN_IF_STOP)
+#define DELAY_AND_RETURN_IF_STOP(aDurationMillis) if (IRDispatcher.delayAndCheckForStop(aDurationMillis)) return
+#endif
// Basic mapping structure
struct IRToCommandMappingStruct {
@@ -72,13 +88,10 @@ struct IRDataForCommandDispatcherStruct {
#define COMMAND_EMPTY 0xFF // code no command
#endif
-#define RETURN_IF_STOP if (IRDispatcher.requestToStopReceived) return
-#define BREAK_IF_STOP if (IRDispatcher.requestToStopReceived) break
-#define DELAY_AND_RETURN_IF_STOP(aDurationMillis) if (IRDispatcher.delayAndCheckForStop(aDurationMillis)) return
-
class IRCommandDispatcher {
public:
void init();
+ void printIRInfo(Print *aSerial);
bool checkAndRunNonBlockingCommands();
bool checkAndRunSuspendedBlockingCommands();
@@ -107,7 +120,8 @@ public:
bool justCalledBlockingCommand = false; // Flag that a blocking command was received and called - is set before call of command
/*
* Flag for running blocking commands to terminate. To check, you can use "if (IRDispatcher.requestToStopReceived) return;" (available as macro RETURN_IF_STOP).
- * Is reset by next IR command received. Can be reset by main loop, if command has stopped.
+ * It is set if a blocking IR command received, which cannot be executed directly. Can be reset by main loop, if command has stopped.
+ * It is reset before executing a blocking command.
*/
volatile bool requestToStopReceived;
/*
diff --git a/examples/IRDispatcherDemo/IRCommandDispatcher.hpp b/examples/IRDispatcherDemo/IRCommandDispatcher.hpp
index a11174e..701103a 100644
--- a/examples/IRDispatcherDemo/IRCommandDispatcher.hpp
+++ b/examples/IRDispatcherDemo/IRCommandDispatcher.hpp
@@ -11,14 +11,14 @@
* A blocking command is stored and sets a stop flag for an already running blocking function to terminate.
* The blocking command can in turn be executed by main loop by calling IRDispatcher.checkAndRunSuspendedBlockingCommands().
*
- * Copyright (C) 2019-2022 Armin Joachimsmeyer
+ * Copyright (C) 2019-2024 Armin Joachimsmeyer
* armin.joachimsmeyer@gmail.com
*
* This file is part of ServoEasing https://github.com/ArminJo/ServoEasing.
* This file is part of IRMP https://github.com/IRMP-org/IRMP.
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
- * ServoEasing is free software: you can redistribute it and/or modify
+ * IRCommandDispatcher is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
@@ -31,6 +31,14 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
+
+/*
+ * Program behavior is modified by the following macros
+ * USE_TINY_IR_RECEIVER
+ * USE_IRMP_LIBRARY
+ * IR_COMMAND_HAS_MORE_THAN_8_BIT
+ */
+
#ifndef _IR_COMMAND_DISPATCHER_HPP
#define _IR_COMMAND_DISPATCHER_HPP
@@ -38,6 +46,10 @@
#include "IRCommandDispatcher.h"
+#if !defined(STR_HELPER)
+#define STR_HELPER(x) #x
+#define STR(x) STR_HELPER(x)
+#endif
/*
* Enable this to see information on each call.
* Since there should be no library which uses Serial, it should only be enabled for development purposes.
@@ -73,6 +85,17 @@ void IRCommandDispatcher::init() {
initPCIInterruptForTinyReceiver();
}
+/*
+ * @return true, if IR Receiver is attached
+ */
+void IRCommandDispatcher::printIRInfo(Print *aSerial) {
+ aSerial->println();
+ // For available IR commands see IRCommandMapping.h https://github.com/ArminJo/PWMMotorControl/blob/master/examples/SmartCarFollower/IRCommandMapping.h
+ aSerial->print(F("Listening to IR remote of type "));
+ aSerial->print(IR_REMOTE_NAME);
+ aSerial->println(F(" at pin " STR(IR_RECEIVE_PIN)));
+}
+
/*
* This is the TinyReceiver callback function, which is called if a complete command was received
* It checks for right address and then call the dispatcher
@@ -153,11 +176,11 @@ void handleReceivedIRData() {
#endif // elif defined(USE_IRMP_LIBRARY)
/*
- * The main dispatcher function
- * Sets flags justCalledRegularIRCommand, executingBlockingCommand
- * @param aCallBlockingCommandImmediately Run blocking command directly, otherwise set request to stop to true
- * and store command for main loop to execute by checkAndRunSuspendedBlockingCommands().
- * Should be false if called by ISR.
+ * The main dispatcher function called by IR-ISR, main loop and checkAndRunSuspendedBlockingCommands()
+ * Non blocking commands are executed directly, blocking commands are executed if enabled by parameter and no other command is just running.
+ * Otherwise request to stop (requestToStopReceived) is set and command is stored for main loop to be later execute by checkAndRunSuspendedBlockingCommands().
+ * Sets flags justCalledRegularIRCommand, executingBlockingCommand, requestToStopReceived
+ * @param aCallBlockingCommandImmediately Run blocking command directly, if no other command is just running. Should be false if called by ISR in order not to block ISR.
*/
void IRCommandDispatcher::checkAndCallCommand(bool aCallBlockingCommandImmediately) {
if (IRReceivedData.command == COMMAND_EMPTY) {
@@ -200,21 +223,31 @@ void IRCommandDispatcher::checkAndCallCommand(bool aCallBlockingCommandImmediate
}
/*
- * lets start a new turn
+ * Execute commands
*/
- requestToStopReceived = false;
-
bool tIsNonBlockingCommand = (IRMapping[i].Flags & IR_COMMAND_FLAG_NON_BLOCKING);
if (tIsNonBlockingCommand) {
// short command here, just call
CD_INFO_PRINT(F("Run non blocking command: "));
CD_INFO_PRINTLN(tCommandName);
+#if defined(BUZZER_PIN) && defined(USE_TINY_IR_RECEIVER)
+ /*
+ * Do (non blocking) buzzer feedback before command is executed
+ */
+ if(IRMapping[i].Flags & IR_COMMAND_FLAG_BEEP) {
+ tone(BUZZER_PIN, 2200, 50);
+ }
+#endif
IRMapping[i].CommandToCall();
} else {
+ /*
+ * Blocking command here
+ */
if (aCallBlockingCommandImmediately && currentBlockingCommandCalled == COMMAND_EMPTY) {
/*
- * here we are called from main loop to execute a command
+ * Here no blocking command was running and we are called from main loop
*/
+ requestToStopReceived = false; // Do not stop the command executed now
justCalledBlockingCommand = true;
currentBlockingCommandCalled = IRReceivedData.command; // set lock for recursive calls
lastBlockingCommandCalled = IRReceivedData.command; // set history, can be evaluated by main loop
@@ -224,6 +257,15 @@ void IRCommandDispatcher::checkAndCallCommand(bool aCallBlockingCommandImmediate
CD_INFO_PRINT(F("Run blocking command: "));
CD_INFO_PRINTLN(tCommandName);
+#if defined(BUZZER_PIN) && defined(USE_TINY_IR_RECEIVER)
+ /*
+ * Do (non blocking) buzzer feedback before command is executed
+ */
+ if(IRMapping[i].Flags & IR_COMMAND_FLAG_BEEP) {
+ tone(BUZZER_PIN, 2200, 50);
+ }
+#endif
+
IRMapping[i].CommandToCall();
#if defined(TRACE)
Serial.println(F("End of blocking command"));
@@ -231,7 +273,9 @@ void IRCommandDispatcher::checkAndCallCommand(bool aCallBlockingCommandImmediate
currentBlockingCommandCalled = COMMAND_EMPTY;
} else {
/*
- * Do not run command directly, but set request to stop to true and store command for main loop to execute
+ * Called by ISR or another command still running.
+ * Do not run command directly, but set request to stop to true and store command
+ * for main loop to execute by checkAndRunSuspendedBlockingCommands()
*/
BlockingCommandToRunNext = IRReceivedData.command;
requestToStopReceived = true; // to stop running command
@@ -256,25 +300,29 @@ bool IRCommandDispatcher::checkAndRunSuspendedBlockingCommands() {
*/
if (BlockingCommandToRunNext != COMMAND_EMPTY) {
- CD_INFO_PRINT(F("Take stored command = 0x"));
+ CD_INFO_PRINT(F("Run stored command=0x"));
CD_INFO_PRINTLN(BlockingCommandToRunNext, HEX);
IRReceivedData.command = BlockingCommandToRunNext;
BlockingCommandToRunNext = COMMAND_EMPTY;
IRReceivedData.isRepeat = false;
+ requestToStopReceived = false; // Do not stop the command executed now
checkAndCallCommand(true);
return true;
}
return false;
}
+/*
+ * Not used internally
+ */
#if defined(IR_COMMAND_HAS_MORE_THAN_8_BIT)
void IRCommandDispatcher::setNextBlockingCommand(uint16_t aBlockingCommandToRunNext)
#else
void IRCommandDispatcher::setNextBlockingCommand(uint8_t aBlockingCommandToRunNext)
#endif
{
- CD_INFO_PRINT(F("Set next command to 0x"));
+ CD_INFO_PRINT(F("Set next command to run to 0x"));
CD_INFO_PRINTLN(aBlockingCommandToRunNext, HEX);
BlockingCommandToRunNext = aBlockingCommandToRunNext;
requestToStopReceived = true;
@@ -296,7 +344,6 @@ bool IRCommandDispatcher::delayAndCheckForStop(uint16_t aDelayMillis) {
}
void IRCommandDispatcher::printIRCommandString(Print *aSerial) {
- aSerial->print(F("IRCommand="));
for (uint_fast8_t i = 0; i < sizeof(IRMapping) / sizeof(struct IRToCommandMappingStruct); ++i) {
if (IRReceivedData.command == IRMapping[i].IRCode) {
aSerial->println(reinterpret_cast(IRMapping[i].CommandString));
diff --git a/examples/TinyReceiver/TinyReceiver.ino b/examples/TinyReceiver/TinyReceiver.ino
index 81604dd..cafbe9d 100644
--- a/examples/TinyReceiver/TinyReceiver.ino
+++ b/examples/TinyReceiver/TinyReceiver.ino
@@ -60,7 +60,7 @@
*/
//#define DISABLE_PARITY_CHECKS // Disable parity checks. Saves 48 bytes of program memory.
//#define USE_EXTENDED_NEC_PROTOCOL // Like NEC, but take the 16 bit address as one 16 bit value and not as 8 bit normal and 8 bit inverted value.
-//#define USE_ONKYO_PROTOCOL // Like NEC, but take both the 16 bit address and command each as one 16 bit value and not as 8 bit normal and 8 bit inverted value.
+//#define USE_ONKYO_PROTOCOL // Like NEC, but take the 16 bit address and command each as one 16 bit value and not as 8 bit normal and 8 bit inverted value.
//#define USE_FAST_PROTOCOL // Use FAST protocol (no address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command) instead of NEC / ONKYO.
//#define ENABLE_NEC2_REPEATS // Instead of sending / receiving the NEC special repeat code, send / receive the original frame for repeat.
//#define USE_CALLBACK_FOR_TINY_RECEIVER // Call the fixed function "void handleReceivedTinyIRData()" each time a frame or repeat is received.
diff --git a/src/TinyIR.h b/src/TinyIR.h
index bfcff36..9a34ce9 100644
--- a/src/TinyIR.h
+++ b/src/TinyIR.h
@@ -247,6 +247,7 @@ struct TinyIRReceiverCallbackDataStruct {
};
extern volatile TinyIRReceiverCallbackDataStruct TinyIRReceiverData;
+bool isIRReceiverAttachedForTinyReceiver();
bool initPCIInterruptForTinyReceiver();
bool enablePCIInterruptForTinyReceiver();
void disablePCIInterruptForTinyReceiver();
diff --git a/src/TinyIRReceiver.hpp b/src/TinyIRReceiver.hpp
index 19f6927..d3302aa 100644
--- a/src/TinyIRReceiver.hpp
+++ b/src/TinyIRReceiver.hpp
@@ -58,7 +58,7 @@
* - IR_FEEDBACK_LED_PIN The pin number for TinyIRReceiver feedback LED.
* - NO_LED_FEEDBACK_CODE Disables the feedback LED function. Saves 14 bytes program memory.
* - DISABLE_PARITY_CHECKS Disable parity checks. Saves 48 bytes of program memory.
- * - USE_EXTENDED_NEC_PROTOCOL Like NEC, but take the 16 bit address as one 16 bit value and not as 8 bit normal and 8 bit inverted value.
+ * - USE_EXTENDED_NEC_PROTOCOL Like NEC, but take the 16 bit address as one 16 bit value and not as 8 bit normal and 8 bit inverted value.
* - USE_ONKYO_PROTOCOL Like NEC, but take both the 16 bit address and command each as one 16 bit value and not as 8 bit normal and 8 bit inverted value.
* - USE_FAST_PROTOCOL Use FAST protocol (no address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command) instead of NEC.
* - ENABLE_NEC2_REPEATS Instead of sending / receiving the NEC special repeat code, send / receive the original frame for repeat.
@@ -78,7 +78,7 @@
//#define DISABLE_PARITY_CHECKS // Disable parity checks. Saves 48 bytes of program memory.
//#define USE_EXTENDED_NEC_PROTOCOL // Like NEC, but take the 16 bit address as one 16 bit value and not as 8 bit normal and 8 bit inverted value.
-//#define USE_ONKYO_PROTOCOL // Like NEC, but take both the 16 bit address and command each as one 16 bit value and not as 8 bit normal and 8 bit inverted value.
+//#define USE_ONKYO_PROTOCOL // Like NEC, but take the 16 bit address and command each as one 16 bit value and not as 8 bit normal and 8 bit inverted value.
//#define USE_FAST_PROTOCOL // Use FAST protocol instead of NEC / ONKYO.
//#define ENABLE_NEC2_REPEATS // Instead of sending / receiving the NEC special repeat code, send / receive the original frame for repeat.
@@ -423,6 +423,17 @@ bool isTinyReceiverIdle() {
return (TinyIRReceiverControl.IRReceiverState == IR_RECEIVER_STATE_WAITING_FOR_START_MARK);
}
+/*
+ * Checks if IR_RECEIVE_PIN is connected and high
+ * @return true, if IR Receiver is attached
+ */
+bool isIRReceiverAttachedForTinyReceiver() {
+ pinModeFast(IR_RECEIVE_PIN, OUTPUT);
+ digitalWriteFast(IR_RECEIVE_PIN, LOW); // discharge pin capacity
+ pinModeFast(IR_RECEIVE_PIN, INPUT);
+ return digitalRead(IR_RECEIVE_PIN); // use slow digitalRead here, since the pin capacity is not fully charged again if we use digitalReadFast.
+}
+
/**
* Sets IR_RECEIVE_PIN mode to INPUT, and if IR_FEEDBACK_LED_PIN is defined, sets feedback LED output mode.
* Then call enablePCIInterruptForTinyReceiver()