Control TV via IR with Arduino & AppleScript Part 2

[Part 1 Learn your IR codes] [Part 2 Control Arduino using AppleScript] [Part 3 Examples and Downloads]

Part 2

This next part, for me at least, came with a bit of luck. I found a forum somewhere (which for the life of me I can’t re-find) which pointed out a very important part of the IR code which the IRrecord sketch we used last time didn’t show. As we know, the serial output of the IRrecord sketch showed us something like this:

20DF906F

but if you look at my IR codes, they all have a “0x” at the beginning. For my TV at least, that part is very important (I haven’t used this with any other TV, I presume it is just as important). So bearing that in mind, we’ll add that to the beginning of all of our codes. You can always remove it, or change it if it doesn’t work for you. Now we can create a new sketch to run on the Arduino. We don’t need the IR Receiver, or even the status LED anymore (but you can leave it if you want it).  The IR library we are using (by Ken Shirriff) is very particular about which pin you may use, in my case it is Pin 3. If you’re not using an Uno, then you can look in the IRremoteInt.h file in the IRremote library folder (stored wherever you put it). You’ll see something like this:

// Arduino Duemilanove, Diecimila, LilyPad, Mini, Fio, etc
#else
//#define IR_USE_TIMER1 // tx = pin 9
#define IR_USE_TIMER2 // tx = pin 3
#endif

When you know which pin you have to use (probably 3), then go ahead and set up your Arduino and breadboard like this:

IRPINSETUP (Yes, it does look like the IR pin is connected to Pin 4, but it isn’t.)

So here we have Ground on the Arduino connected to Ground on the power rail of the breadboard.

Arduino Pin 3 connected directly to the LONG leg of the IR LED (the Anode (positive)).

Ground from the power rail to the SHORT leg of the LED (the Cathode (negative)).

That’s as much as you need. You can connect an LED to Arduino Pin 13 if you wish.

Now that the hardware is set up, we can look at the code. The code that I have used originally began life as a method for raising and lowering a flag to alert a user to a new email (by Aaron Eiche) Now, while we have no servos to worry about, we do need to get commands to the Arduino using AppleScript. To do that, we use Tod Kurt’s Arduino-Serial package. Download and install it.

With that downloaded and installed, we take a step back and think about the Arduino again. If we are going to give the Arduino commands using AppleScript, the Arduino needs to know what to do when it receives those commands.  Open up a new Sketch and paste in the following:

#include <IRremote.h>

IRsend irsend;
int incomingByte = 0; // for incoming serial data

void setup()
{
 Serial.begin(9600);
}

Next lets create some subroutines. First we’ll create one for Muting the TV. Copy the command you have for Mute from your spreadsheet and add it into the following code:

void sendMUTE() {
 irsend.sendNEC(INSERT YOUR MUTE COMMAND HERE, 32);
 delay(4*100);
}

For instance, my Mute routine looks like this:

void sendMUTE() {
 irsend.sendNEC(0x20df906f, 32);
 delay(4*100);
}

Notice the line

irsend.sendNEC

this is determined from your serial out put. (I perhaps should have mentioned this before, my bad). If you’re using LG, then you can use NEC. If you didn’t notice that during your serial session, then you have the following options:

sendNEC
sendSony
sendRC5
sendRC6
sendDISH
sendSharp
sendPanasonic
sendJVC

There is also a sendRAW option, but that requires extra variables. You can find more information on the options above, in the IRremote.h file in the IRremote library folder (stored wherever you put it). Now you have one routine set up, test it by adding it to your main loop, like so:

void loop() {
sendMUTE();
delay(5000);
}

Verify and upload. (Make sure to take the insulating tape off of the TV!) If it’s all gone well (and you’re not too far away from your TV) then that sketch will Mute your TV, then unmute it 5 seconds later. If it doesn’t, make sure you have the right prefixing digits (e.g. “0x”) by doing some Googling, and that you have the right sendNEC, sendSony, etc. AND that you did take the tape off the TV and that you’re not too far away. (Also make sure your LED is connected to the correct Pin and that it is indeed an IR LED.)

Assuming that it has worked, I shall continue. Before we make any more routines, we need to test the AppleScript side of things. To do that, change your main loop to look like this:

void loop() {
 if (Serial.available() > 0) {
 // read the incoming byte:
 incomingByte = Serial.read();
 Serial.println(incomingByte);
 if(incomingByte == 97){
 sendMUTE();
 }else if(incomingByte == 48){
 }
}

(It may be that I have missed a curly bracket off the end of that.) Verify and upload. This time, the Arduino will run, but won’t run the code until it gets a command from our computer. Now let’s set that up. Open up AppleScript on your computer and paste the following:

do shell script "/Library/Scripts/arduino-serial-master/arduino-serial -b 9600 -p /dev/tty.usbmodem621 -s a"

There are two important steps here:

1. “/Library/Scripts/arduino-serial-master/arduino-serial” is the path to the particular file “arduino-serial” on my computer. You need to change this to whatever the the path to that file is on your computer. You can do this by finding the “arduino-serial” file on your computer, and dragging it into the AppleScript window. This will give you the full file path.

2. “/dev/tty.usbmodem621” needs to be set to the serial port of your Arduino. You can find this in the Arduino IDE by selecting Tools > Serial Port > whichever one has a tick mark next to it is the one you’re using. Copy that out exactly as I have done.

Once you’ve made those changes, run the script by hitting CMD+R (or the green play button at the top of the script window).

That will send the message “a” to your Arduino. Your Arduino will interpret that as “97” and will activate the sendMUTE(); routine, muting your TV.

Run the script again to unmute your TV.

All working? Excellent.

Now let’s jump pack to the Arduino and add some more functionality. If you’re using an LG TV, and my Mute code has already worked for you, then you can go ahead and copy and paste the following code. If not, then you’ll have to do this bit manually. But it’s pretty simple.

void sendMUTE() {
 irsend.sendNEC(0x20df906f, 32);
 delay(4*100);
}

void sendVOLUP() {
 irsend.sendNEC(0x20df40bf, 32);
 delay(3*100);
 irsend.sendNEC(0x20df40bf, 32);
}

void sendVOLDWN() {
 irsend.sendNEC(0x20dfc03f, 32);
 delay(3*100);
 irsend.sendNEC(0x20dfc03f, 32);
}

void sendAUP() {
 irsend.sendNEC(0x20df01fd, 32);
 delay(5*100);
}

void sendADWN() {
 irsend.sendNEC(0x20df827d, 32);
 delay(5*100);
}

void sendALFT() {
 irsend.sendNEC(0x20dfe01f, 32);
 delay(5*100);
}

void sendARGT() {
 irsend.sendNEC(0x20df609f, 32);
 delay(5*100);
}

void sendSELECT() {
 irsend.sendNEC(0x20df22dd, 32);
 delay(5*100);
}

void sendMENU() {
 irsend.sendNEC(0x20dfc23d, 32);
 delay(5*100);
}

void sendRETURN() {
 irsend.sendNEC(0x20df14eb, 32);
 delay(5*100);
}

void sendEXIT() {
 irsend.sendNEC(0x20dfda25, 32);
 delay(5*100);
}

void sendONOFF() {
 irsend.sendNEC(0x20df10ef, 32);
 delay(10*100);
}

void send1() {
 irsend.sendNEC(0x20df8877, 32);
 delay(10*100);
}

void send2() {
 irsend.sendNEC(0x20df48b7, 32);
 delay(10*100);
}

void send3() {
 irsend.sendNEC(0x20dfc837, 32);
 delay(10*100);
}

void send4() {
 irsend.sendNEC(0x20df28d7, 32);
 delay(10*100);
}

void send5() {
 irsend.sendNEC(0x20dfa857, 32);
 delay(10*100);
}

void send6() {
 irsend.sendNEC(0x20df6897, 32);
 delay(10*100);
}

void send7() {
 irsend.sendNEC(0x20dfe817, 32);
 delay(10*100);
}

void send8() {
 irsend.sendNEC(0x20df18e7, 32);
 delay(10*100);
}

void send9() {
 irsend.sendNEC(0x20df9867, 32);
 delay(10*100);
}

void send0() {
 irsend.sendNEC(0x20df08f7, 32);
 delay(10*100);
}

Notice that the Volume UP and Down routines send their commands twice. This is because when you press the volume up (or down) button on my TV just once, it doesn’t actually affect the volume, it simply shows the volume bar. The volume can only be turned up while the volume bar is on screen, hence they send their commands twice.

In order to use all those in the main loop we need to add them to it. If you did the last step manually, but you kept your routine names the same as mine, then you can simply copy and paste the next section, if not, well, it’s back to manual labour for you!

void loop() {

 if (Serial.available() > 0) {
 // read the incoming byte:
 incomingByte = Serial.read();
 Serial.println(incomingByte);
 if(incomingByte == 97){
 sendMUTE();

 }else if(incomingByte == 48){

 }else if(incomingByte == 98){
 sendVOLUP();
 }else if(incomingByte == 99){
 sendVOLDWN();
 }else if(incomingByte == 100){
 sendAUP();
 }else if(incomingByte == 101){
 sendADWN();
 }else if(incomingByte == 102){
 sendALFT();
 }else if(incomingByte == 103){
 sendARGT();
 }else if(incomingByte == 104){
 sendSELECT();
 }else if(incomingByte == 105){
 sendMENU();
 }else if(incomingByte == 106){
 sendRETURN();
 }else if(incomingByte == 107){
 sendEXIT();
 }else if(incomingByte == 108){
 sendONOFF();
 }else if(incomingByte == 109){
 send1();
 }else if(incomingByte == 110){
 send2();
 }else if(incomingByte == 111){
 send3();
 }else if(incomingByte == 112){
 send4();
 }else if(incomingByte == 113){
 send5();
 }else if(incomingByte == 114){
 send6();
 }else if(incomingByte == 115){
 send7();
 }else if(incomingByte == 116){
 send8();
 }else if(incomingByte == 117){
 send9();
 }else if(incomingByte == 118){
 send0();
 }

 }

 }

The next step is to add these commands to our AppleScript, we could simply do this:

do shell script "/Library/Scripts/arduino-serial-master/arduino-serial -b 9600 -p /dev/tty.usbmodem621 -s a"

do shell script "/Library/Scripts/arduino-serial-master/arduino-serial -b 9600 -p /dev/tty.usbmodem621 -s b"

do shell script "/Library/Scripts/arduino-serial-master/arduino-serial -b 9600 -p /dev/tty.usbmodem621 -s c"

But that would be very inefficient. If ever the path to the arduino-serial file changes, or the Arduino serial port changes, we will have to go in and manually change every entry (and also it would run them one after the other).  Instead, we’ll do this:  

on signalTV(command)

   set sc to "/Library/Scripts/arduino-serial-master/arduino-serial -b 9600 -p /dev/tty.usbmodem621 -s "

   set commandlist to {"sendMUTE", "sendVOLUP", "sendVOLDWN", "sendAUP", "sendADWN", "sendALFT", "sendARGT", "sendSELECT", "sendMENU", "sendRETURN", "sendEXIT", "sendONOFF", "send1", "send2", "send3", "send4", "send5", "send6", "send7", "send8", "send9", "send0", "sendSLEEP", "sendGET2WAKE", "sendWAKE2SLEEP", "sendCANCELSLEEP"}

   set txlist to {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}

   set x to my getListPosition(command, commandlist)
   set command to item x of txlist

   do shell script sc & command

end signalTV

on getListPosition(this_item, this_list)

   repeat with i from 1 to the count of this_list
      if item i of this_list is this_item then
      return i
   end repeat
   return 0

end getListPosition

This way, if the file path or the serial port ever change, we only have to change one line of code. If you compile and run that, it won’t do anything, so to get it to do something, add something like:  

my signalTV("sendMUTE")

I chose to make that script it’s own separate script that I can call from other scripts. To do that you simply remove any additions you made and save it in a useful place. I chose the “/Library/Scripts” folder (but you will need to change the permissions for that to work), so you could stick it in your documents folder.

To call the script from another script, simply add:

set commander to load script "PATH TO SCRIPT YOU JUST MADE.scpt"

Then, to use it’s subroutines in that new script you would add something like this:

commander's signalTV("sendMUTE")

Below is an example of a script that uses an external script like the one we just created:


set commander to load script "/Library/Scripts/ArduinoIRCommands.scpt"

commander's signalTV("sendONOFF")

That script runs when I put my computer to sleep.

 

That’s all for today. In Part 3 we will look at a specific and lengthy practical example that translates a user’s input into IR commands, sets a user defined TV wake time, TV sleep time, computer shutdown time and boot up time.

[Part 1 Learn your IR codes] [Part 2 Control Arduino using AppleScript] [Part 3 Examples and Downloads]

Author: Dan

Share This Post On