Categories
Arduino Embedded projects. Mods.

IR remote controll of a RPI KODI player with a twist.

Like many others I Am using a Raspberry PI 3B+ with LibreElec (KODI) as a media player for the TV. My setup used a RF remote with USB dongle to control the PRI and it worked great. What wasn’t so great was the fact that combined with the TV and the IPTV box there were a total of three remote controls, which was pretty annoying for us. The IPTV box remote has a programmable TV/AV button, but the TV can’t switch the signal sources with a single button. It is done via a menu, so the TV remote had to stay for the sole purpose of switching the signal source between the RPI and the IPTV box. One day I thought, “There’s got to be a better way“.

SOLUTION: Make a device that receives IR signals, and converts them to a USB keyboard hits and switches one HDMI output between two HDMI inputs. It also has to repeat the IR signals through an IR LED to control the IPTV box only when it is selected and do not repeat the IR signals if the Raspberry Pi with LibreElec(KODI) is selected.

IR to USB states.

After a bit of thinking I rejected my initial idea of using a PIC PIC18F14K50 micro controller (It has USB capabilities and I have used one in the past to make a USB keyboard and still have some in my parts box) and decided to go the Arduino way. Arduino Pro Micro it is.

Arduino Pro Micro

It is based on ATmega32u4 and has USB capabilities instead of using FTDI or any other UART convertors. This means that a USB HID can be implemented with it.

For more info visit: https://learn.sparkfun.com/tutorials/pro-micro–fio-v3-hookup-guide/all , where you can find information about the board, the pinout, how to add the board to your Arduino IDE and examples how to use it as a keyboard or a mouse.

KEYBOARD: I decided to use the HID-project library from NicoHood because it has greater capabilities and is capable of sending multimedia buttons like Volume Up/Down etc.

IR REMOTE RECEIVER: The library I decided to use is he IRREMOTE library by Ken Shirriff The link contains information on IR receivers, transmitters and protocols, a tutorial how to install and use the library together with examples for receiving and sending IR commands. I am using only the receiving part of the library.

IR REMOTE REPEATER: To prevent the IPTV receiver from doing unknown actions while the KODI player is selected, the IR signal should reach the IPTV receiver only then TV mode is selected and the HDMI signal from the IPTV receiver is routed to the TV by the HDMI switch. To achieve this the Arduino, if TV mode is selected, repeats all signals without any processing and sends them through an IR LED. It is mounted in front of the IPTV IR receiver in a manner that won’t allow ambient IR signals to reach it.

HDMI SOURCE SWITCH: So far by using the Arduino as an IR receiver and a USB HID keyboard I can get rid of one of the remote controls. But two are still too many :). In order to eliminate the need for the original TV remote used only to switch the signal sources I decided to use an HDMI switch, which I had to hack to make in controllable by an Arduino. Bellow is shown the switch which I used. It is 3 to 1 switch but I am using only two of the inputs. There is one button to select an input and 3 LEDs which indicate the active input. Any other model controlled by a button and having LEDs for feed back should do as well.

HDMI switch
HDMI switch

To make it usable for my purpose I need a way to connect the HDMI switch to the Arduino to emulate a button press to change the input selection and read the status of the LEDs as a feed back, so I can check if the desired input is actually selected. All that was needed, was to add a connector and wire the ground, one end of the tactile button, which pulls a pin to ground when pressed, and the two outputs of the chip which control the LEDs. One thing to keep in mind though, the HDMI switch operates at 3.3V and my Arduino Pro micro is a 5V version. For the outputs from the switch to the Arduino the levels are fine and no need to do anything special, but for the output of the Arduino which emulates a HDMI switch button press, an open collector circuit is needed.

Wired HDMI switch.

Glue the connector to the PCB, cut an opening in the plastic case for the connector and the HDMI switch modification is done. Now it has an interface through which it is easily controlled by a microcontroller. One thing to keep in mind is that connector wires between the HDMI switch and the Arduino should be kept short since 3.3V signals are transmitted. Mine are about 15cm.

ARDUINO WIRING DIAGRAM: The transistor switch for the HDMI switch control is mandatory if the Arduino and the HDMI switch are not working with the same supply voltage. This is the case here. The Arduino Pro Micro is a 5V device and the HDMI switch is a 3.3V one. The pull down resistors ,R2 and R3, on the inputs are there to keep the inputs from floating in case the cable to the HDMI switch is detached. The 100nF capacitor C2 is to be mounted as close as possible to the IR receiver. Especially if the receiver is attached by a cable to the Arduino as in my build.

Schematics

ARDUINO SKETCH: You can find the sketch on my GIThub project https://github.com/deelbg/IR_remote_to_USB or copy from bellow.

The sketch uses the HID-project and IR-remote libraries as mentioned above. The array g_ir_to_kbd_map[] is a map between the codes read from the IR receiver and the HID keyboard buttons to be sent to the USB. To get the IR codes from your remote control uncomment the line “#define IR_SCAN_MODE”, this will trace the codes through the Arduino serial connection. Default KODI keyboard controls can be found here https://kodi.wiki/view/Keyboard_controls.

The sketch will send only single HID keyboard hits, i.e. even if you hold the IR remote button this will translate in only one key press on the USB.

Place the IR code for the button with which you want to switch between the two HDMI signals in line “#define IR_TV_AV 0xE8A24030ul”. With this the configuration is done.

Now when the default TV mode is selected the IR signal is retransmitted through the IR LED and no USB HID key strokes are sent. The HDMI switch send the In1 signal to the output. The IR repeater is based on a 38kHz 50% infill PWM generated by a hardware timer and switches in on and of via pin interrupt.

When AUX mode is selected, the IR signals are not transferred to the IR LED and USB HID key strokes are generated based on the lookup table. The HDMI switch sends In2 to the output.

HDMI switch is controlled by pulsing the open collector output and checking if the desired input is in high level. This is repeated until the selected position on the HDMI switch is confirmed by the feedback inputs.

#include "HID-Project.h"
#include <IRremote.h>

// Configuration:
//#define IR_SCAN_MODE        // Enable to trace the IR button codes on Serial.
#define IR_RECEIVER_PIN 8   // The pin number on which the IR receiver's output is connected.

#define IR_REPEATER_IN_PIN 7   // The pin number on which the IR Repeater input - IR receiver is connected.
#define IR_REPEATER_OUT_PIN 9  // The pin number on which the IR Repeater output - a LED is connected. - Warning - do not change!!!!

#define HDMI_SWITCH_OUT_PIN 10             // HDMI switch controll output pin.
#define HDMI_SWITCH_CHANEL_TV_PIN 14       // HDMI switch feedback for TV channel pin.
#define HDMI_SWITCH_CHANEL_AUX_PIN 16      // HDMI switch feedback for AUX channel.
#define HDMI_SWITCH_OUT_PULSE_TIME_MS 150  // HDMI switch controll pulse lenght.
#define HDMI_SWITCH_OUT_WAIT_TIME_MS 500   // HDMI switch controll period between switch attempts.

#define IR_NO_CODE     0x00000000ul
#define IR_REPEAT_CODE 0xFFFFFFFFul

typedef struct {
  uint32_t ir_code;
  KeyboardKeycode  kbd_key;
} ir_to_kbd_t;  

// IR buttons codes to keyboard keys Mapping:
#define IR_TV_AV 0xE8A24030ul

ir_to_kbd_t g_ir_to_kbd_map[] =  {
  /* IR CODE - KBD KEY */
  {0x8877A956, KEY_VOLUME_MUTE}, /*IR_MUTE*/
  {0x887753AC, KEY_UP_ARROW},    /*IR_UP*/
  {0x88774BB4, KEY_DOWN_ARROW},  /*IR_DOWN*/
  {0x88779966, KEY_LEFT_ARROW},  /*IR_LEFT*/
  {0x8877837C, KEY_RIGHT_ARROW}, /*IR_RIGHT*/
  {0x887709F6, KEY_BACKSPACE},   /*IR_BACK*/
  {0x8877738C, KEY_ENTER},       /*IR_OK*/
  {0x8877B946, KEY_M},           /*IR_MENU*/
  {0x887719E6, KEY_VOLUME_UP},   /*IR_NEXT*/
  {0x8877F30C, KEY_VOLUME_DOWN}, /*IR_PREVIOUS*/
  {0x88776B94, KEY_F},           /*IR_FF*/
  {0x88775BA4, KEY_R},           /*IR_RWND*/
  {0x8877C33C, KEY_SPACE},       /*IR_PLAY_PAUSE*/
  {0x88778B74, KEY_T},           /*IR_LANG*/
  {0x8877BB44, KEY_C},           /*IR_SEARCH*/
  {IR_NO_CODE, KEY_ESC}          /*IR_NONE*/
};

#define IR_TO_KBD_MAP_SIZE (sizeof(g_ir_to_kbd_map)/sizeof(ir_to_kbd_t))

typedef enum 
{
  TV = 0,
  AUX
} tv_mode_t;

tv_mode_t g_tv_mode = TV;

IRrecv irrecv(IR_RECEIVER_PIN);
decode_results g_ir_result;


void hdmi_switch_out_pulse(void);
void ir_repeater_init(void);
void ir_repeater_pwm_on(void);
void ir_repeater_pwm_off(void);
void ir_repeater_isr(void);


void setup()
{
#ifdef IR_SCAN_MODE
  SerialUSB.begin(9600);
  delay(3000);
  SerialUSB.println("Test IR remote:");
#endif
  Keyboard.begin();       //Init keyboard emulation
  Keyboard.releaseAll();
  irrecv.enableIRIn();    // Start the receiver
  ir_repeater_init();

  pinMode(HDMI_SWITCH_OUT_PIN, OUTPUT);
  digitalWrite(HDMI_SWITCH_OUT_PIN, LOW);
  pinMode(HDMI_SWITCH_CHANEL_TV_PIN, INPUT);
  pinMode(HDMI_SWITCH_CHANEL_AUX_PIN, INPUT);
}

void loop() {
  //Wait for an IR code
  if (irrecv.decode(&g_ir_result)) 
  {
#ifdef IR_SCAN_MODE
    SerialUSB.print("PROTOCOL: ");
    SerialUSB.print(g_ir_result.decode_type);
    SerialUSB.print("  VALUE: 0x");
    SerialUSB.println(g_ir_result.value, HEX);        
#endif

    if (IR_TV_AV == g_ir_result.value)
    {
      if (TV == g_tv_mode)
      {
        g_tv_mode = AUX;
      }
      else
      {        
        g_tv_mode = TV;
      }
    }

    
    if (NEC == g_ir_result.decode_type)
    {
      if (AUX == g_tv_mode)
      {          
        if (IR_REPEAT_CODE != g_ir_result.value) //If first press.
        {
          // Search for the mapped KBD KEY
          for (uint8_t index = 0u; index < IR_TO_KBD_MAP_SIZE; index++)
          {
            if (g_ir_to_kbd_map[index].ir_code == g_ir_result.value)
            {
              //Send the KBD KEY              
          Keyboard.press(g_ir_to_kbd_map[index].kbd_key);
              Keyboard.releaseAll();
            }
          }
        }
      }
    }
    
    irrecv.resume(); // Wait for another IR code
  }

  if (TV == g_tv_mode)
  {
    while(LOW == digitalRead(HDMI_SWITCH_CHANEL_TV_PIN))
    {
      hdmi_switch_out_pulse();
    }    
  }
  else
  {    
    while(LOW == digitalRead(HDMI_SWITCH_CHANEL_AUX_PIN))
    {
      hdmi_switch_out_pulse();
    }
  }
  
  delay(100);
}


void hdmi_switch_out_pulse(void)
{
  digitalWrite(HDMI_SWITCH_OUT_PIN, HIGH);
  delay(HDMI_SWITCH_OUT_PULSE_TIME_MS);
  digitalWrite(HDMI_SWITCH_OUT_PIN, LOW);
  delay(HDMI_SWITCH_OUT_WAIT_TIME_MS);
}

void ir_repeater_init(void)
{ 
  pinMode(IR_REPEATER_OUT_PIN, OUTPUT);
  pinMode(IR_REPEATER_IN_PIN, INPUT);
  attachInterrupt(digitalPinToInterrupt(IR_REPEATER_IN_PIN), ir_repeater_isr, CHANGE);   
}

void ir_repeater_pwm_on(void)
{
  pinMode(IR_REPEATER_OUT_PIN, OUTPUT);
  // Enable PWM outputs for OC1A on digital pins
  TCCR1A = _BV(COM1A1) | _BV(WGM11);                
  // Set fast PWM and no prescaler on timer 1
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10);  
  // Set the PWM frequency to 38kHz = (16MHz / (420))
  ICR1 = 420;                                       
  // Set duty-cycle to 50% on D9
  OCR1A = 210;                                      
}

void ir_repeater_pwm_off(void)
{
  // Enable PWM outputs for OC1A on digital pins
  TCCR1A = 0;  
  // Disable fast PWM and no prescaler on timer 1   
  TCCR1B = 0;
  // Set the PWM frequency to 38kHz = (16MHz / (420))
  ICR1 = 420;  
  // Set duty-cycle to 0% on D9   
  OCR1A = 0;      
  digitalWrite(IR_REPEATER_OUT_PIN, LOW);
}

void ir_repeater_isr(void)
{
  static bool b_pwm_on = false;
  
  if (HIGH == digitalRead(IR_REPEATER_IN_PIN))
  {    
    b_pwm_on = false;
    ir_repeater_pwm_off();
  }
  else
  {
    if (TV == g_tv_mode)
    {
      if (false == b_pwm_on)
      {
        ir_repeater_pwm_on();        
        b_pwm_on = true;
      }
    }    
  }
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s