Skip to content

Instantly share code, notes, and snippets.

@zr0n
Created August 10, 2025 01:49
Show Gist options
  • Save zr0n/825212a7e489349739fefc05135beb27 to your computer and use it in GitHub Desktop.
Save zr0n/825212a7e489349739fefc05135beb27 to your computer and use it in GitHub Desktop.
Como transformar controle de tv antigo em joystick
#include <Arduino.h>
#include <IRremoteESP8266.h>
#include <IRrecv.h>
#include <IRutils.h>
const uint8_t IR_PIN = 13;
IRrecv irrecv(IR_PIN, 1024, 15, true);
decode_results results;
void setup() {
Serial.begin(115200);
delay(200);
irrecv.enableIRIn();
Serial.println("Sniffer IR pronto. Aponte o controle e pressione botoes...");
}
void loop() {
if (irrecv.decode(&results)) {
Serial.println(resultToHumanReadableBasic(&results));
if (results.decode_type != decode_type_t::UNKNOWN) {
Serial.print("Codigo HEX: 0x");
Serial.println((uint32_t)results.value, HEX);
}
irrecv.resume();
}
}
#include <Arduino.h>
#include <BleGamepad.h>
#include <IRremoteESP8266.h>
#include <IRrecv.h>
#include <IRutils.h>
#ifndef GAMEPAD_HAT_CENTERED
#define GAMEPAD_HAT_CENTERED (-1)
#define GAMEPAD_HAT_UP 0
#define GAMEPAD_HAT_RIGHT 2
#define GAMEPAD_HAT_DOWN 4
#define GAMEPAD_HAT_LEFT 6
#endif
static const uint8_t IR_PIN = 13;
BleGamepadConfiguration bleCfg;
BleGamepad bleGamepad("Joystick de Pobre", "Kovil", 100);
IRrecv irrecv(IR_PIN, 1024, 15, true); // timeout 15ms
decode_results results;
struct IRToAction { uint32_t code; enum Type { HAT, BTN } type; int value; };
static const IRToAction mapTable[] = {
{ 0x02FD41BE, IRToAction::HAT, GAMEPAD_HAT_UP },
{ 0x02FDC13E, IRToAction::HAT, GAMEPAD_HAT_DOWN },
{ 0x02FD9867, IRToAction::HAT, GAMEPAD_HAT_RIGHT },
{ 0x02FDB847, IRToAction::HAT, GAMEPAD_HAT_LEFT },
{ 0x02FD40BF, IRToAction::BTN, 4 },
{ 0x02FDB847, IRToAction::BTN, 3 },
{ 0x02FD609F, IRToAction::BTN, 2 },
{ 0x02FDE817, IRToAction::BTN, 1 },
{ 0x02FD807F, IRToAction::BTN, 5 },
{ 0x02FDE01F, IRToAction::BTN, 7 },
{ 0x02FDC03F, IRToAction::BTN, 6 },
{ 0x02FD906F, IRToAction::BTN, 8 },
};
static const size_t MAP_COUNT = sizeof(mapTable)/sizeof(mapTable[0]);
struct HoldState { uint32_t code; uint32_t lastSeenMs; bool active; };
static HoldState holds[MAP_COUNT];
static const uint32_t HOLD_TIMEOUT_MS = 180;
static int currentHat = GAMEPAD_HAT_CENTERED;
static int lastIdx = -1;
static bool lastConnected = false;
static uint32_t hbLast = 0;
int findMapIndex(uint32_t code){ for(size_t i=0;i<MAP_COUNT;i++) if(mapTable[i].code==code) return (int)i; return -1; }
void sendNow(const char* tag){
if(bleGamepad.isConnected()){ bleGamepad.sendReport(); Serial.printf("[BLE] sendReport (%s)\n", tag); }
else Serial.println("[BLE] not connected, skipped sendReport");
}
void pressAction(int idx){
const auto &m = mapTable[idx];
if(m.type==IRToAction::HAT){ currentHat=m.value; bleGamepad.setHat(currentHat); Serial.printf("[BLE] HAT=%d (code=0x%08lX)\n", currentHat,(unsigned long)m.code); }
else { bleGamepad.press(m.value); Serial.printf("[BLE] PRESS BTN_%d (code=0x%08lX)\n", m.value,(unsigned long)m.code); }
holds[idx].active=true; holds[idx].lastSeenMs=millis(); sendNow("pressAction");
}
void releaseAction(int idx){
const auto &m = mapTable[idx];
if(!holds[idx].active) return;
if(m.type==IRToAction::HAT){ if(currentHat==m.value){ currentHat=GAMEPAD_HAT_CENTERED; bleGamepad.setHat(currentHat); Serial.printf("[BLE] HAT=CENTER (code=0x%08lX)\n",(unsigned long)m.code); } }
else { bleGamepad.release(m.value); Serial.printf("[BLE] RELEASE BTN_%d (code=0x%08lX)\n", m.value,(unsigned long)m.code); }
holds[idx].active=false; sendNow("releaseAction");
}
void releaseExpiredHolds(){
const uint32_t now=millis();
for(size_t i=0;i<MAP_COUNT;i++){
if(holds[i].active && (now-holds[i].lastSeenMs)>HOLD_TIMEOUT_MS){
Serial.printf("[BLE] timeout -> release idx=%d code=0x%08lX\n",(int)i,(unsigned long)mapTable[i].code);
releaseAction(i);
}
}
}
void setup(){
Serial.begin(115200);
uint32_t t0=millis(); while(!Serial && millis()-t0<2000) delay(10);
Serial.printf("\n[BOOT] start. IR_PIN=%d\n", IR_PIN);
irrecv.enableIRIn();
Serial.println("[BOOT] IR ready (NEC)");
bleCfg.setControllerType(CONTROLLER_TYPE_GAMEPAD);
bleCfg.setButtonCount(16);
bleCfg.setHatSwitchCount(1);
bleCfg.setAutoReport(false);
Serial.println("[BOOT] BLE begin...");
bleGamepad.begin(&bleCfg);
Serial.println("[BOOT] BLE ready. Pair now.");
}
void loop(){
if(millis()-hbLast>1000){ hbLast=millis(); Serial.println("[HB] alive"); }
bool c=bleGamepad.isConnected(); if(c!=lastConnected){ Serial.printf("[BLE] connected=%d\n",(int)c); lastConnected=c; }
if(irrecv.decode(&results)){
uint32_t code=(uint32_t)results.value;
bool isNEC=(results.decode_type==decode_type_t::NEC || results.decode_type==decode_type_t::NEC_LIKE);
Serial.printf("[IR] type=%d code=0x%08lX repeat=%d\n",(int)results.decode_type,(unsigned long)code,(int)results.repeat);
if(isNEC){
bool isRepeat=results.repeat || code==0xFFFFFFFF;
if(isRepeat){
if(lastIdx>=0){ Serial.printf("[IR] repeat -> reapply idx=%d code=0x%08lX\n",lastIdx,(unsigned long)mapTable[lastIdx].code); pressAction(lastIdx); }
else Serial.println("[IR] repeat with no lastIdx");
}else{
int idx=findMapIndex(code);
if(idx>=0){ lastIdx=idx; Serial.printf("[IR] mapped idx=%d\n",idx); pressAction(idx); }
else Serial.printf("[IR] unmapped 0x%08lX\n",(unsigned long)code);
}
}else{
Serial.println("[IR] non-NEC ignored");
}
irrecv.resume();
}
releaseExpiredHolds();
delay(2);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment