Skip to content

Instantly share code, notes, and snippets.

@tarnak
Forked from blacklight/IRclone.ino
Created August 16, 2021 22:05
Show Gist options
  • Save tarnak/d95d2ce8b0d5419efa234e55d9d28c50 to your computer and use it in GitHub Desktop.
Save tarnak/d95d2ce8b0d5419efa234e55d9d28c50 to your computer and use it in GitHub Desktop.
#include <IRremote.h>
#define NO_IR 0
#define NO_BTN -1
#define IR_REPEAT 0xFFFFFFFF
#define IR_SEND_MODE 1
#define IR_STORE_MODE 2
#define BUFSIZE 128
typedef struct {
int button;
bool pressed;
uint64_t pressedTime;
} ButtonState;
typedef enum {
NONE = 0,
BUTTON_PRESSED = 1,
IR_RECV = 2
} State;
typedef enum {
RED = 0,
GREEN = 1,
BLUE = 2
} Color;
//// Constants
// Time constants
const float longPressDelay = 2.0;
// Buttons
const int buttonPins[] = {8, 9};
const int nButtons = (int) (sizeof(buttonPins) / sizeof(int));
// IR interface
const int irRecvPin = 7;
// LED interface
const int ledPins[] = {5, 6, 10}; // RGB
const uint8_t nColors = sizeof(ledPins) / sizeof(ledPins[0]);
const uint8_t modeColors[][nColors] = {
{0, 0, 0}, // NONE
{0, 255, 0}, // BUTTON_PRESSED: LED green
{255, 0, 120}, // IR_RECV: LED purple
};
//// Variables
ButtonState buttonStates[nButtons];
decode_results irButtonData[nButtons];
unsigned int irBuffer[BUFSIZE];
IRsend irsend;
IRrecv irrecv(irRecvPin);
void setup() {
Serial.begin(9600);
irrecv.enableIRIn();
irrecv.blink13(true);
for (int i=0; i < nButtons; i++) {
pinMode(buttonPins[i], INPUT_PULLUP);
decode_results irData;
irData.value = NO_IR;
ButtonState buttonState = {
.button = i,
.pressed = false,
.pressedTime = 0
};
memset(irButtonData, 0, nButtons * sizeof(decode_results));
memcpy(&(buttonStates[i]), &buttonState, sizeof(ButtonState));
}
for (int i=0; i < nColors; i++) {
pinMode(ledPins[i], OUTPUT);
analogWrite(ledPins[i], 0);
}
}
float getButtonDelay(uint64_t pressedTime) {
return (((uint64_t) (millis() - pressedTime)) / 1000.0);
}
void printButtonState(ButtonState* state) {
Serial.print("{\"button\":");
Serial.print(state->button);
if (state->pressedTime) {
Serial.print(", \"delay\":");
Serial.print(getButtonDelay(state->pressedTime));
}
Serial.print(", \"pressed\":");
Serial.print(state->pressed);
Serial.println("}");
}
void printIrState(uint32_t irState) {
Serial.print("{\"ir\":");
Serial.print(irState, HEX);
Serial.println("}");
}
void printNewIrButtonData(int button) {
Serial.print("{\"action\":\"store_new_data\", \"button\":");
Serial.print(button);
Serial.print(", \"ir\":\"");
Serial.print(irButtonData[button].value, HEX);
Serial.println("\"}");
}
ButtonState getPressedButton() {
ButtonState noBtn = {
.button = NO_BTN,
.pressed = false,
.pressedTime = 0
};
for (int i=0; i < nButtons; i++) {
int state = digitalRead(buttonPins[i]);
if (state == HIGH) {
buttonStates[i].pressed = true;
if (buttonStates[i].pressedTime == 0) {
printButtonState(&buttonStates[i]);
buttonStates[i].pressedTime = millis();
}
return buttonStates[i];
} else {
buttonStates[i].pressed = false;
if (buttonStates[i].pressedTime != 0) {
printButtonState(&buttonStates[i]);
buttonStates[i].pressedTime = 0;
}
}
}
return noBtn;
}
uint32_t getIrValue(decode_results* irResults) {
uint32_t value = NO_IR;
if (irrecv.decode(irResults)) {
if (irResults->value && irResults->value != REPEAT) {
value = irResults->value;
printIrState(value);
}
irrecv.resume();
}
return value;
}
void storeNewIrButtonData(int button, decode_results* irData) {
memcpy(&(irButtonData[button]), irData, sizeof(decode_results));
printNewIrButtonData(button);
}
void sendIrCommand(int button) {
decode_results irData = irButtonData[button];
if (irData.value == NO_IR)
return;
switch (irData.decode_type) {
case NEC:
irsend.sendNEC(irData.value, 32);
break;
case SONY:
irsend.sendSony(irData.value, 12);
break;
case PANASONIC:
irsend.sendPanasonic(irData.address, irData.value);
break;
case RC5:
irsend.sendRC5(irData.value, 12);
break;
case RC6:
irsend.sendRC6(irData.value, 20);
break;
case JVC:
irsend.sendJVC(irData.value, 8, false);
break;
case SAMSUNG:
irsend.sendSAMSUNG(irData.value, 20);
break;
case SHARP:
irsend.sendSharp(irData.address, irData.value);
break;
case DENON:
irsend.sendDenon(irData.value, 15);
break;
case LG:
irsend.sendLG(irData.value, 32);
break;
default:
Serial.println("Unknown IR protocol");
return;
}
irrecv.enableIRIn();
Serial.print("{\"action\":\"send_ir_data\", \"ir\":");
Serial.print(irData.value, HEX);
Serial.println("}");
delay(100);
}
void ledNotifyState(State state) {
uint8_t ledColor[nColors] = {0};
if (state & BUTTON_PRESSED)
for (int i=0; i < nColors; i++)
ledColor[i] |= modeColors[BUTTON_PRESSED][i];
if (state & IR_RECV)
for (int i=0; i < nColors; i++)
ledColor[i] |= modeColors[IR_RECV][i];
for (int i=0; i < nColors; i++)
analogWrite(ledPins[i], ledColor[i]);
}
void loop(){
State state = NONE;
decode_results irResults;
ButtonState button = getPressedButton();
uint32_t irValue = getIrValue(&irResults);
if (button.button != NO_BTN) {
state |= BUTTON_PRESSED;
}
if (irValue != NO_IR) {
state |= IR_RECV;
if (button.button != NO_BTN) {
storeNewIrButtonData(button.button, &irResults);
}
} else {
// Press a button with a stored command for more than 2 seconds and hold the key to clone
// on the other remote to overwrite the existing command
if (button.button != NO_BTN && getButtonDelay(button.pressedTime) < longPressDelay) {
sendIrCommand(button.button);
}
}
ledNotifyState(state);
delay(10);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment