Projects‎ > ‎Bits and Pieces‎ > ‎

LG TV Hacks

The hacked OSD says "Arrrrr!"
(greetings Hack a Day readers!)

Long story short, I recently wound up with a LG TV as my display monitor -- a LD450 series to be more specific.  There were a few things that, despite going to a large display seemed novel and exciting, were a downgrade from my previous display.  Mainly, the VGA fuzziness was much more pronounced and I needed to use HDMI inputs instead -- and also the display doesn't automatically sleep or wake when the input signal goes inactive/active.  

Usually I'd just buck it up and shrug my shoulders, but I noticed an RS-232 port on the back and curiosity got the better of me.  That and I don't have the remote for it (bought the display model from the store front at a nice discount).

Some background: LG used Linux and other FOSS when developing their TV's firmware.  Because of this, the majority of this firmware source code can be downloaded from their website: http://www.lg.com/global/support/opensource/opensource.jsp.  LG also documents some of the RS232 port functionality in the owner's manual: You can send remote control codes directly from your PC.  Actually, you can do even better.  You can set specific values for things and also READ the values as well.

If you have a Logitech Harmony remote, or some older cell phones that can send IR, there are some hacks available to help you get access to the hidden service menus of the TV.  There are a couple of reasons you should do this immediately: 1) You can enable support for USB hubs and multiple USB devices from said hubs and 2) Most importantly, you can enable DivX movie playback by flipping a switch -- quite literally.  This is all documented very well elsewhere, but it's worth knowing about.

For my purposes, I had two problems that I felt were very necessary to solve.

1) I want to adjust the brightness of the display so that I don't get blinded at night or have to squint in the middle of the day
2) I regularly flip between two computers, and each is on a different input.  I really don't like having to fumble with a remote since I already have a KVM that switches the keyboard/mouse when I double-press scroll-lock.  (One practical solution is to get a KVM that supports HDMI and be done with it.  Assume I'm too cheap to go that route.)

Some fun reading:

For starters, I wrote a bash script that does all the communication with the TV.  First, it looks for a serial port that responds like the LG TV should.  At 9600 baud, if the TV receives "KA 01 FF", that means "TV with ID 1 please return your value for setting KA (power)", and if the TV is on then returns 01.  That handles the autodetection of the port.  The detection can slow things down a bit, so it might be desired to tweak the script to use a hard-coded value to simplify things.

Since the TV communication is in the form of "command id value" and the ID of the TV never changes, I hard-coded the ID of 1 in the script.

tv.sh:
#!/bin/bash
function execCommandRaw {
   echo $2 > $1 && sleep 0.07s && read -dx -t 1 response < $1
   echo $response
   return `echo $response | grep -q OK`
}

function execCommand {
   result=$(execCommandRaw "$1" "$2")
   response=$?
   echo $result | cut -c8-
   return $response
}

function testPort {
   if [ -e $1 ]
   then
      stty -F $1 ispeed 9600 ospeed 9600 -echo
      if [ "$(execCommandRaw $1 'ka 1 ff')" = "a 01 OK01" ]
      then
         return 0
      fi
   fi
   return 1
}

function findPort {
   for f in /dev/ttyUSB*; do
      testPort "$f"
      if [ $? = 0 ]
      then
         echo $f
         return 0
      fi
   done
   for f in /dev/ttyS*; do
      testPort $f
      if [ $? = 0 ]
      then
         echo $f
         return 0
      fi
   done
   return 1
}

portName=$(findPort)
if [ $? = 1 ]; then
   echo "Could not find TV"
   exit 1
fi

if [ "$1" == "-s" ]; then
  current=$(execCommand $portName "$2 01 ff")
  shopt -s nocasematch
  # The regex is meant to work for zero-padded values
  # Combined with the option set above, it's a case-insensitive match
  if [[ "$current" =~ ^0?$3$ ]]; then
  exit 0
fi
execCommand $portName "$2 01 $3"
if [ $? = 1 ]; then 
echo error
exit 1
  fi
else
  execCommand "$portName" "$1 01 $2"
  if [ $? = 1 ]; then 
  echo error
exit 1
  fi
fi

I removed the autodetection from my local copy, replacing the findPort function with:
function findPort {
  for f in /dev/ttyUSB*; do
      stty -F $f ispeed 9600 ospeed 9600 -echo
  echo $f
  return 0
   done
  return 1
}
What this does is find the first match for /dev/ttyUSB* and return it, setting the baud rate settings at the same time -- and doesn't bother to determine if the TV sends a valid response.  Usually my serial port (a usb dongle) is at /dev/ttyUSB0, but occasionally when unplugged and replugged in it changes to ttyUSB1.  This corrects for that situation nicely.  

Examples of using this:
Turn on TV from standby mode: ./tv.sh ka 01 (outputs 01 -- need to wait 20 sec for this to finish)
Set the aspect ratio to "Just Scan": ./tv.sh kc 09 (outputs 09)

If you look at the back of the LG owner's manual, it has a list of all the "supported" commands.  There are several others I've found that were not documented, but I'll save that for another writeup.

If you pass the first parameter as "-s" followed by command and value, the script will first poll the current value from the TV and only set the new value if it is different.  If you set anything on the TV regardless of the current value, the TV will throw a bit on-screen OSD stating the value was set.  If you are doing something periodically like adjusting the backlight, this can get rather annoying.  The -s option helps avoid extra OSD distractions.

Once you're talking to the TV, there are a number of interesting things you can do next.  Check the sub-topics on the left for other things I've done using this script such as adjusting the backlight automatically and sending commands to the TV when the screensaver goes active/inactive.