Thursday, March 23, 2017

IR to IP Bridge with Arduino

Looks like I've been away for a while...Well it's worth it, cause I searched high and low for something to accomplish this and came up empty.

I recently got rid of my Harmony remotes (which had seen 10+ years of faithful service) in exchange for a URC control system. It always felt like the Harmony was getting in my way in an attempt to be helpful (like Clippy, that obnoxious little paperclip MS put on us). In contrast, URC is very straight forward for someone with a little programming knowledge. I love them!

One this they allow is for amazing customization and total house control. Once I got the basics of the IR and base stations figured out, I started to fully integrate the home controls. HDMI matrix in the equipment closet, each of the four main level televisions connected to it with each room's remote able to talk to each room's RF base station.

Then it hit me! The only thing I need to really be inside the "home of tomorrow" is remote-based control of my lights. Already having a robust Insteon system powered by the incomparable ISY-994i, I finally used the ISY's IR receiving.

Loading the 40 pre-built IR codes into the URC remote's CCP software was a breeze. Then making a program on the ISY to look for those IR signals and turn on or off the appropriate lights was simple enough. I already was using tabletop controllers and Insteon's wireless keypads to do this, but now I have one remote to do it all. You could easily have it dim the lights when you press play and raise them when you pause for that cool theater effect.

Once this was working, I somehow caught wind that Roku (my set top box of choice) allows for a lot of fine grained IP control. You can remotely query which apps are installed, which one is currently running, send remote key presses, or (and this is where I salivated) make it launch a particular program.

One thing the Roku's do not have is a super full featured remote. The included remotes are simple and have few buttons. They do have a couple (depends on model) direct app access buttons (Netflix, Amazon, etc.) but they have yet to come out with a remote that allows quick access to the apps that I use most often (Plex, or more accurately Plex RARFlix, and PS Vue). Roku's network control guide gives you the framework to launch whichever app you desire, but only through an http POST request. URC's "Complete Control" series, which is what I use as a DIY guy, do not have IP control. Their higher end, and fully locked down, "Total Control" do, but that wouldn't be DIY and wouldn't be fun.

Luckily for me, the ISY has a networking module from which you can send HTTP POST and GET requests. Combine that with an IR trigger and voila, I've got direct access to my favorite Roku apps. However, for some reason the ISY wasn't liking to learn new remote codes for me. So I was limited to the 40 in-built codes. This quickly was dwindled once I started to play.

What I really needed at this point is an IR-to-IP device. My Google-fu skills are tip top, but I wasn't able to find anything, other than possibly an irTrans device. Those were fairly expensive for what I'd want and would have to come from the EU.

So what I present here is an Arduino powered IR receiving, IP command sending device. It'll take whichever IR command you want to use and, upon receiving it, send an HTTP command over the network.

The hardware is simple. Arduino of your choice (I initially used an UNO clone, but moved to a Mega for more memory), an ethernet shield, and an IR receiver.

Anyone who's worked with Arduino should be able to understand the code. IR receiver needs to have the data pin plugged in to pin 8 on the Arduino in my example.

Here's the code:

----------Code Starts Here--------------

#include <SPI.h>
#include <Ethernet.h>
#include <IRremote.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //MAC Address of the Arduino
char rokutheater[] = "192.168.1.254"; //IP Address of the target device
EthernetClient client;
IPAddress ip(192, 168, 1, 250); //IP Address of the Arduino
//IPAddress gateway(192, 168, 1, 1); //Uncomment if you need Internet access
//IPAddress subnet(255, 255, 0, 0);
int RECV_PIN = 8; //Pin the IR Receiver is on

#define TheaterPlex 2397 //IR code when the desired IR remote button is pressed
#define TheaterPlex2 349
#define TheaterPSVue 2372
#define TheaterPSVue2 324
#define TheaterHome 52275

IRrecv irrecv(RECV_PIN);
decode_results results;

void setup()
{
  Ethernet.begin(mac, ip);
  Serial.begin(9600);
  Serial.println("Serial Output Begun");
  Serial.print("My IP address: ");
  ip = Ethernet.localIP();
  for (byte thisByte = 0; thisByte < 4; thisByte++) {
    // print the value of each byte of the IP address:
    Serial.print(ip[thisByte], DEC);
    Serial.print(".");
  }
  irrecv.enableIRIn();
}

void loop()
{
if (irrecv.decode(&results)) {
        unsigned int value = results.value;
        switch(value) { //Branch off to the correct subroutine based on the received code
       
          //Theater
           case TheaterPlex:
             TheaterRoomPlex();
              break;
          
           case TheaterPSVue:
             TheaterRoomPSVue();
              break;
          
           case TheaterPlex2:
             TheaterRoomPlex();
              break;
          
           case TheaterPSVue2:
             TheaterRoomPSVue();
              break;
          
           case TheaterHome:
             TheaterRoomHome();
             break;
        }
        Serial.println(value); // Sends the received IR button code to the serial monitor, helpful to find new keys or otherwise debug
        irrecv.resume(); // Ready to receive the next command/value
      }
}

//-------------------THEATER-------------------------
void TheaterRoomPlex(){

    client.stop();

    if (client.connect(rokutheater, 8060)) {
    Serial.println("connecting...");
    client.println("POST /launch/13535 HTTP/1.1");
//    client.println("Host: www.arduino.cc");
//    client.println("User-Agent: arduino-ethernet");
    client.println("Connection: close");
    client.println();
    Serial.print("Plex on Theater Roku"); 
    delay(500);
 
  } else {
    Serial.println("connection failed");
  }
}

void TheaterRoomPSVue(){

    client.stop();

    if (client.connect(rokutheater, 8060)) {
    Serial.println("connecting...");
    client.println("POST /launch/93374 HTTP/1.1");
//    client.println("Host: www.arduino.cc");
//    client.println("User-Agent: arduino-ethernet");
    client.println("Connection: close");
    client.println();
    Serial.print("PS Vue on Theater Roku"); 
    delay(500);
 
  } else {
    Serial.println("connection failed");
  }
}

void TheaterRoomHome(){

    client.stop();

    if (client.connect(rokutheater, 8060)) {
    Serial.println("connecting...");
    client.println("POST /keypress/Home HTTP/1.1");
//    client.println("Host: www.arduino.cc");
//    client.println("User-Agent: arduino-ethernet");
    client.println("Connection: close");
    client.println();
    Serial.print("Going Home on Theater Roku"); 
    delay(500);
 
  } else {
    Serial.println("connection failed");
  }
 
}

----------End Code------------

You'll need both the Arduino Ethernet library (found here) and shirriff's IR Remote library .(found here)

Any IR Receiver will work, but I used the YwRobot IR Receiver breakout board out of convenience like this one. I also used a generic Arduino Ethernet shield. I'd imagine with a little tweaking, you could use an ESP8266 or a wifi shield too. That'll probably be my next step. 

I also used a generic Arduino UNO R3 at first, which the above code compiles to use 56% of program storage and 44% of ram. Using a Mega2560 clone, it compiles to use about 7% storage and 11% ram. So if you're only going to use it for a few commands an UNO is capable, but I only wanted to build the device once, so I went with Mega.

Now, to just find someone who is good at programming and making websites. Ideally, I wouldn't have to hook it to a computer to program it. Would be nice to use the SD card functionality to serve up a web page that would display received IR codes and allow you to create new commands to send when a code is received. I would think that this would be fairly easy, but beyond my basic skills. I guess this leaves me plenty of room to continue to tinker.

Meanwhile, I've now been using it, shoehorned into a plastic project box, for a week to test its function. It has operated flawlessly thus far. Even by my standards I'm impressive sometimes!

UPDATE 07/01/2018

For some reason this past week this system quit working on me. For some reason, the Rokus suddenly stopped liking the formatting of the HTTP POST messages in my code.

After some troubleshooting, I stumbled across the solution. Remove (or comment out) these two lines in each section of your code:
    client.println("Host: www.arduino.cc");
    client.println("User-Agent: arduino-ethernet");

I've updated the code above to reflect these changes. Not sure why this change was necessary. So if you used my code, you need to update the Arduino a bit.

At least it's an easy/quick change.

Search This Blog