@ShermanP & @kmin and others.
So I believe I have a working solution! First of all, the IRremote library is able to both decode and send at least 18 protocols and variations without using excessive memory. But more important, I only need to store about 6 bytes to send a remote code to any of those protocols! I only need to store a number representing the protocol, along with an address and a command. This means I no longer have to worry about storing or compacting raw timing codes. I can easilly create an array of structures containing the data I wish to save for each button, sized to the number of buttons I want for my remote. If I want 20 buttons, the array of structures would only take up 128 bytes of EEPROM! So I'll be able to make one remote to control many devices with different protocols, though I have only tested/verified about 5 so far.
I'm going to include a sketch here to demonstrate the operation for anyone else looking for a similar solution. It is just a "work in progress" sketch, so there are unnecessary comments and prints. I'm not saving data in eeprom yet (doing so is trivia). But I think its better I post something that works rather than simply abandon the thread. For reference, I'm using a basic NANO (ATMega328p) and the IRremote library version 4.4.1. This sketch assumes you are already familiar with the use of an IR LED, and IR sensor. You will also need a momentary pushbutton, and a per board. For reference, you can use the " Arduino Hookup" diagram in the very fine article at "(IR Remotes Revisited - 2023 | DroneBot Workshop)". Just be aware the PIN numbers will likely be different! For my NANO project:
- pin 2 is the IR receive pin (always*)
- pin 3 is the IR Send pin (always*)
- pin 5 is used as a digital input for my one single "send" button (the other buttons in the referenced diagram are not in my demo)
- pin 6 is used as an output for a "USER" indicator LED (always*).
NOTE: "*always" means that the header the pins used are defined in one of the IRRemote "pinDefinitionsAndMore.h" header file, based on your board. The NANO pins will be different than what you see in the referenced diagram (which uses the UNO board). Some may be changeable, but I believe the IR Receive pin can not be changed.
Make sure to use the serial output from the sketch so you can verify what is happening. Operation is as follows:
- The sketch continually reads data from your IR sensor. No protocols are defined, so all 18 (and a few variations) can be decoded.
- Point any remote at the IR sensor and send a command . Use something you can easilly very with a nearby device. If the protocol is known, it is displayed as a name and reference number, along with an address and command. These are saved in some local variables, which I will later place in a structure as explained earlier.
- A button is defined in the sketch for sending. If you press the button, the receiver is temporarily disabled, and the protocol along with its address and command are transmitted through your IR LED.
- If you hold the button, it will keep sending the command, with a millisecond delay I specified in DELAY_BETWEEN_REPEAT
- when you release the button, the sketch will re-start the receiver, but the stored codes will not be erased until a new IR message is received.
Again, this is just a proof of concept sketch. I believe it verifies that a universal remote can be built within the confines of a basic NANO board. This sketch has some waste, but so far only uses 15826 bytes (51%) of program storage space and 764 bytes (37%) of dynamic memory. So there is plenty of room to add code for more buttons and EEPROM storage. More important, the amount of capture data needed to resend any remote command a small enough to store several dozen operations.
Sketch (work in progress). Hope it helps someone
/*
* my irRemoteTest. based on SimpleSender.cpp from IRremote library
* Using NANO ATMega328p
*
* Captures command from an IR remote, and keeps only the protocol number (an enum),
* address, and command. That is all I need to pass to this library function
* to send the same command...
* IrSender.write(aProtocol, aAddress, aCommand, sRepeats);
* With only these few args needed, I can easilly save a large number of commands to
* associate with buttons on the remote I will build.
*/
#include <Arduino.h>
/*
* Specify which protocol(s) should be used for decoding.
* If no protocol is defined, all protocols (except Bang&Olufsen) are active.
* This must be done before the #include <IRremote.hpp>
* // NOTE: I had no trouble including ALL protocols!
*/
//#define DECODE_DENON // Includes Sharp
//#define DECODE_JVC
//#define DECODE_KASEIKYO
//#define DECODE_PANASONIC // alias for DECODE_KASEIKYO
//#define DECODE_LG
//#define DECODE_NEC // Includes Apple and Onkyo. To enable all protocols , just comment/disable this line.
//#define DECODE_SAMSUNG
//#define DECODE_SONY
//#define DECODE_RC5
//#define DECODE_RC6
//#define DECODE_BOSEWAVE
//#define DECODE_LEGO_PF
//#define DECODE_MAGIQUEST
//#define DECODE_WHYNTER
//#define DECODE_FAST
//#define DECODE_DISTANCE_WIDTH // Universal decoder for pulse distance width protocols
//#define DECODE_HASH // special decoder for all protocols
//#define DECODE_BEO // This protocol must always be enabled manually, i.e. it is NOT enabled if no protocol is defined. It prevents decoding of SONY!
//#define DEBUG // Activate this for lots of lovely debug output from the decoders.
//#define RAW_BUFFER_LENGTH 750 // For air condition remotes it requires 750. Default is 200.
/*************************
* to associate protocol number with proper send method, use enums in IRprotocol.h
*/
// This include defines the actual pin number for pins like IR_RECEIVE_PIN, IR_SEND_PIN for many different boards and architectures
#include "PinDefinitionsAndMore.h"
#include <IRremote.hpp> // include the library
#define SEND_BUTTON_PIN 5 // APPLICATION_PIN (should be 5) to send my captured command
#define DEFAULT_REPEATS 0 // I find it best to control repeats myself
#define DELAY_BETWEEN_REPEAT 250
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
pinMode(ALTERNATIVE_IR_FEEDBACK_LED_PIN, OUTPUT);
pinMode(SEND_BUTTON_PIN, INPUT_PULLUP);
Serial.begin(115200);
while (!Serial); // Wait for Serial to become available. Is optimized away for some cores.
// show which program is running on my Arduino
Serial.println(F("START " __FILE__ " from " __DATE__ "\r\nUsing library version " VERSION_IRREMOTE));
Serial.print(F("Send IR signals at pin "));
Serial.println(IR_SEND_PIN);
// Start the receiver and if not 3 parameters specified, take
// LED_BUILTIN pin from the internal boards definition as default feedback LED
IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);
Serial.print(F("Ready to receive IR signals of protocols: "));
printActiveIRProtocols(&Serial);
Serial.println(F("at pin " STR(IR_RECEIVE_PIN)));
IrSender.begin(); // Start with IR_SEND_PIN -which is defined in PinDefinitionsAndMore.h
// - as send pin and enable feedback LED at default feedback LED pin
Serial.print(F("Ready to send IR signals at pin " STR(IR_SEND_PIN) " on press of button at pin "));
Serial.println(SEND_BUTTON_PIN);
//disableLEDFeedback(); // Disable feedback LED at default feedback LED pin
}
// minimum data to store to resend a captured IR remote operation
uint16_t aAddress = 0; // 8 or 16 bit address (Sony & Denon have 5 bit adr )
uint16_t aCommand = 0; // 8 bit command
decode_type_t aProtocol = UNKNOWN; // just a typed enum referencing protocol
uint8_t sRepeats =DEFAULT_REPEATS; // I always use zero()) and manage repeats myself
bool sendState =false;
// Handy call I created to display the captured IR data
void showIRData() {
Serial.print(F("\n protocol="));
Serial.print(aProtocol);
Serial.print(" (");
Serial.print(getProtocolString(aProtocol)); // translate protocol number to text name!
Serial.print(F(") address=0x"));
Serial.print(aAddress, HEX);
Serial.print(F(" command=0x"));
Serial.print(aCommand, HEX);
Serial.print(F(", repeats="));
Serial.print(sRepeats);
Serial.println();
Serial.flush();
}
// I use this to clear captured values before storing new ones
void resetIRvalues() {
Serial.println(F("\nIR data clear"));
aAddress=aCommand=sRepeats=0; aProtocol=UNKNOWN;
}
//////////////// main loop /////////////////////
void loop() {
// if send not active, and we receive data, decode it.
if (sendState == false ) if (IrReceiver.decode()) {
resetIRvalues(); // my private copy of key vars
aProtocol = IrReceiver.decodedIRData.protocol;
if (IrReceiver.decodedIRData.protocol == UNKNOWN) {
Serial.println(F("\nReceived noise or an unknown (or not yet enabled) protocol"));
// We have an unknown protocol here, print extended info
IrReceiver.printIRResultRawFormatted(&Serial, true);
IrReceiver.resume(); // Do it here, to preserve raw data for printing with printIRResultRawFormatted()
} else {
// aProtocol = IrReceiver.decodedIRData.protocol;
aCommand = IrReceiver.decodedIRData.command;
aAddress = IrReceiver.decodedIRData.address;
sRepeats = DEFAULT_REPEATS; // or NO_REPEATS or SEND_REPEAT_COMMAND
IrReceiver.resume(); // Early enable receiving of the next IR frame
IrReceiver.printIRResultShort(&Serial);
IrReceiver.printIRSendUsage(&Serial);
}
Serial.print(F("\n********************\nReceived: \n"));
showIRData();
// some delay greater than 5 ms (RECORD_GAP_MICROS), otherwise the receiver
// may see remote repeats as one long signal
delay(1000);
}
// If button pressed, and we have data, wait for release, then send it
sendState = !digitalRead(SEND_BUTTON_PIN);
if (sendState ) {
if (aAddress ==0 || aCommand == 0 || aProtocol == UNKNOWN) {
Serial.println(F("\nNo valid IR data to send"));
}
if (aAddress !=0 && aCommand !=0) { // send IR msg received
Serial.println();
Serial.print(F("\nSend now:"));
showIRData();
Serial.flush();
IrReceiver.stop(); // stop receiving disableIRIn();
// from IRSend.hpp... IrSender.write() will switch/case using the protocol
// number to find and use the corresponding send call (sendNec(), sendPanasonic() etc.)
while (digitalRead(SEND_BUTTON_PIN) == LOW) {
digitalWrite (ALTERNATIVE_IR_FEEDBACK_LED_PIN, HIGH);
IrSender.write(aProtocol, aAddress, aCommand, sRepeats);
digitalWrite (ALTERNATIVE_IR_FEEDBACK_LED_PIN, LOW);
delay (DELAY_BETWEEN_REPEAT);
}
// resume receiving when I stop sending
Serial.println(F("Button released -> start receiving"));
IrReceiver.start(); //begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);
}
sendState=false;
}
}