From 8b9e036889a264d6cc7ea04fa91fcdb9cdd48910 Mon Sep 17 00:00:00 2001 From: Armin Date: Tue, 13 Feb 2024 15:46:48 +0100 Subject: [PATCH] Convenience function isIRReceiverAttachedForTinyReceiver() and 3.party --- Doxyfile | 2 +- README.md | 8 +- changelog.md | 4 + examples/AllProtocolsOnLCD/ADCUtils.h | 14 ++- examples/AllProtocolsOnLCD/ADCUtils.hpp | 103 ++++++++++++++++-- .../IRDispatcherDemo/IRCommandDispatcher.h | 28 +++-- .../IRDispatcherDemo/IRCommandDispatcher.hpp | 77 ++++++++++--- examples/TinyReceiver/TinyReceiver.ino | 2 +- src/TinyIR.h | 1 + src/TinyIRReceiver.hpp | 15 ++- 10 files changed, 215 insertions(+), 39 deletions(-) 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()