PlayStation3 (PS3) bluetooth remote and LIRC?
#1
Question 
Hello,
I have been following the progress of XBMC Linux since day one, and I have to say, you are all doing great!

I am not a cpp programmer by any means, so I will help where I can. At the moment, I am attempting to get the PS3 BD Remote to work with XBMC. (This is a bluetooth remote) Although a lot of the work I am doing is not with XBMC directly, I would like to post my progress, and hopefully there are others interested and could give some input.

So far, I have been able to pair the remote, decode all the button presses, get battery life info, and map the buttons to keyboard button presses (which is how I control XBMC), using a program called xte, which interfaces with the xtest library. Everything I have written is written in python.

The problems I have run in to that is XBMC related is mostly with the Virtual Keyboard in XBMC. The keyboard doesn't have any commands to move the cursor-type thing around the letters on the virtual keyboard (i assume, because you don't need a virtual keyboard, if you have a physical one).

To solve this problem, there are a number of things that can be done:
-Fix the virtual keyboard to work with keyboard buttons
-Make a driver to interface the sony remote with LIRC.
-Make a driver to treat the remote as a joystick


The first option is probably the quickest and easiest to do, and will solve the problem just fine, but the second option is probably the most complete solution, with the third being "if all else fails".

Another problem I am having, is I have to pair the remote every time the script is run. I don't know enough about bluetooth to make it remember the connection so I don't have to pair it every time.

The following two posts will include everything I know about the remote commands that are sent, as well as a sample python script that I use to map button presses to keyboard presses.

If you are interested in helping with this project, I will gladly accept any code or even simple guidance on how to progress with this.

Thanks,

Brandon
Reply
#2
The commands received by the remote look like this:
a1 01 00 00 00 03 ff ff ff ff ff 01 02

The following is an explanation of what I know so far about each number:

The 1st octet always stays the same at A1. It's purpose is unknown.

The 2nd octet always stays the same at 01. It's purpose is unknown.

The 3rd 4th and 5th octets are for multiple button presses with the controller keys. When multiple buttons are pressed, it's values are added together to form the key combination.

The 6th octet is the actual button that is being pressed. if the result is FF, then it is a special command (for key releases and multiple button presses)

the 7th - 11th octets always stay the same at FF. It's purpose is unknown.

the 12th octet is used when the 6th octet is FF. If 6th is FF, and 12th is 00, then all the keys are released. If the 6th is FF and 12th is 01, then it is a multi key combination. If 6th is NOT FF, then 12th is always 01.

The 13th octet is the current battery life of the remote. 5 is full, 0 is empty.


Key Maps (6th octet)
--------

ff --Key Released
16 --EJECT
64 --AUDIO
65 --ANGLE
63 --SUBTITLE
0f --CLEAR
28 --TIME
00 --1
01 --2
02 --3
03 --4
04 --5
05 --6
06 --7
07 --8
08 --9
09 --0
81 --RED
82 --GREEN
80 --BLUE
83 --YELLOW
70 --DISPLAY
1a --TOP MENU
40 --POP UP/MENU
0e --RETURN
5c --OPTIONS/TRIANGLE
5d --BACK/CIRCLE
5e --X
5f --VIEW/SQUARE
54 --UP
55 --RIGHT
56 --DOWN
57 --LEFT
0b --ENTER
5a --L1
58 --L2
51 --L3
5b --R1
59 --R2
52 --R3
43 --PLAYSTATION
50 --SELECT
53 --START
33 -- <-SCAN
34 -- SCAN->
30 --PREV
31 --NEXT
60 -- <-SLOW/STEP
61 -- SLOW/STEP->
32 --PLAY
38 --STOP
39 --PAUSE




Keys that support multiple key combinations
--------------------------------------------

UP
RIGHT
DOWN
LEFT

SELECT
L3
R3
START

OPTIONS/TRIANGLE
BACK/CIRCLE
X
VIEW/SQUARE

L2
R2
L1
R1
Reply
#3
This is the script I have written so far:

Code:
import bluetooth
import os

loop_forever = True
while loop_forever is True:

        target_name = "BD Remote Control"
        target_address = None
        remote = bluetooth.BluetoothSocket(bluetooth.L2CAP)


        target_connected = False
        while target_connected is False:
                print "Searching for BD Remote Control"
                print "(Press Start + Enter on remote to make discoverable)"
                nearby_devices = bluetooth.discover_devices()

                for bdaddr in nearby_devices:
                        if target_name == bluetooth.lookup_name( bdaddr ):
                                target_address = bdaddr
                                break

                if target_address is not None:
                        print "Found BD Remote Control with address ", target_address
                        print "Attempting to pair with remote"

                        try:
                                remote.connect((target_address,19))
                                target_connected = True
                                print "Remote Paired.\a"
                        except:
                                print "ERROR - Could Not Connect. Trying again..."

                else:
                        print "Could not find BD Remote Control. Trying again..."


        done = False
        while not done:
                datalen = 0
                try:
                        data = remote.recv(1024)
                        datalen = len(data)
                except:
                        done = True

                if datalen == 13:
                        keycode = data.encode("hex")[10:12]

                        if keycode == "ff":
                                print "Key Released or Multiple keys pressed"

                        elif keycode == "16": #EJECT
                                print "Eject"
                                os.system("xte 'key s'")

                        elif keycode == "64": #AUDIO
                                print "Audio"
                                os.system("xte 'key w'")

                        elif keycode == "65": #ANGLE
                                print "Angle"
                                os.system("xte 'key z'")

                        elif keycode == "63": #SUBTITLE
                                print "Subtitle"
                                os.system("xte 'key n'")

                        elif keycode == "0f": #CLEAR
                                print "Clear"
                                os.system("xte 'key d'")

                        elif keycode == "28": #TIME
                                print "Time"
                                os.system("xte 'key t'")

                        elif keycode == "00": #1
                                print "1"
                                os.system("xte 'key 1'")

                        elif keycode == "01": #2
                                print "2"
                                os.system("xte 'key 2'")

                        elif keycode == "02": #3
                                print "3"
                                os.system("xte 'key 3'")

                        elif keycode == "03": #4
                                print "4"
                                os.system("xte 'key 4'")

                        elif keycode == "04": #5
                                print "5"
                                os.system("xte 'key 5'")

                        elif keycode == "05": #6
                                print "6"
                                os.system("xte 'key 6'")

                        elif keycode == "06": #7
                                print "7"
                                os.system("xte 'key 7'")

                        elif keycode == "07": #8
                                print "8"
                                os.system("xte 'key 8'")

                        elif keycode == "08": #9
                                print "9"
                                os.system("xte 'key 9'")

                        elif keycode == "09": #0
                                print "0"
                                os.system("xte 'key 0'")

                        elif keycode == "81": #RED
                                print "Red"

                        elif keycode == "82": #GREEN
                                print "Green"

                        elif keycode == "80": #BLUE
                                print "Blue"

                        elif keycode == "83": #YELLOW
                                print "Yellow"

                        elif keycode == "70": #DISPLAY
                                print "Display"
                                os.system("xte 'key i'")

                        elif keycode == "1a": #TOP MENU
                                print "Top Menu"

                        elif keycode == "40": #POP UP/MENU
                                print "Pop Up/Menu"

                        elif keycode == "0e": #RETURN
                                print "Return"
                                os.system("xte 'key BackSpace'")

                        elif keycode == "5c": #OPTIONS/TRIANGLE
                                print "Options/Triangle"
                                os.system("xte 'key s'")

                        elif keycode == "5d": #BACK/CIRCLE
                                print "Back/Circle"
                                os.system("xte 'key Escape'")

                        elif keycode == "5e": #X
                                print "X"
                                os.system("xte 'key x'")

                        elif keycode == "5f": #VIEW/SQUARE
                                print "View/Square"
                                os.system("xte 'key v'")

                        elif keycode == "54": #UP
                                print "Up"
                                os.system("xte 'key Up'")

                        elif keycode == "55": #RIGHT
                                print "Right"
                                os.system("xte 'key Right'")

                        elif keycode == "56": #DOWN
                                print "Down"
                                os.system("xte 'key Down'")

                        elif keycode == "57": #LEFT
                                print "Left"
                                os.system("xte 'key Left'")

                        elif keycode == "0b": #ENTER
                                print "Enter"
                                os.system("xte 'key Return'")

                        elif keycode == "5a": #L1
                                print "L1"
                                os.system("xte 'key +'")

                        elif keycode == "58": #L2
                                print "L2"
                                os.system("xte 'key -'")

                        elif keycode == "51": #L3
                                print "L3"

                        elif keycode == "5b": #R1
                                print "R1"
                                os.system("xte 'key Page_Up'")

                        elif keycode == "59": #R2
                                print "R2"
                                os.system("xte 'key Page_Down'")

                        elif keycode == "52": #R3
                                print "R3"

                        elif keycode == "43": #PLAYSTATION
                                print "Playstation"

                        elif keycode == "50": #SELECT
                                print "Select"
                                os.system("xte 'key ['")

                        elif keycode == "53": #START
                                print "Start"
                                os.system("xte 'key]'")

                        elif keycode == "33": #<-SCAN
                                print "<-Scan"
                                os.system("xte 'key r'")

                        elif keycode == "34": #  SCAN->
                                print "Scan->"
                                os.system("xte 'key f'")

                        elif keycode == "30": #PREV
                                print "Prev"
                                os.system("xte 'key ,'")

                        elif keycode == "31": #NEXT
                                print "Next"
                                os.system("xte 'key .'")

                        elif keycode == "60": #<-SLOW/STEP
                                print "<-Slow/Step"

                        elif keycode == "61": #  SLOW/STEP->
                                print "Slow/Step->"

                        elif keycode == "32": #PLAY
                                print "Play"
                                os.system("xte 'key p'")

                        elif keycode == "38": #STOP
                                print "Stop"
                                os.system("xte 'key x'")

                        elif keycode == "39": #PAUSE
                                print "Pause"
                                os.system("xte 'key Space'")

                        else:
                                print "Unknown or garbage data"
                else:
                        print "Unknown or garbage data"

        print "Disconnected."
        try:
                remote.close()
        except:
                print "Can not close. Oh well."

print "Program Ended."
Reply
#4
I would say that option two is the best. It would be a more robust solution against changes in XBMC and it would benefit the entire lirc comunity. Unfortunally I'm not a programmer so for this I'm no good
Reply
#5
I agree.

Looking through the LIRC source code, there is a daemon for a Sony Ericsson cellphone, which communicates via bluetooth. This could probably be modified to produce the results desired. I need to take a closer look at this.

-Brandon
Reply
#6
Question 
Any updates on this?

That Sony PlayStation 3 Remote Control would make a really cheap RF (Radio Frequency) remote if someone could get it working with LIRC.

...and the LIRC website does state that it works with "Bluetooth mobile phones" so maybe it would be possible to do what brandonj suggest and modify that to get the Sony PlayStation 3 Remote Control working via bluetooth, as it would be much nicer to have it working nativly than to have to go through a python or other script. By the way, some guys did manage to get it working via a python like brandonj suggested by making as virtual keyboard interface:
http://www.youtube.com/watch?v=0C8QN3Hux4g

Now back top discussion about a native implementation; I wonder which bluetooth profile(s) the remote uses, there are a few possibilites;

AVRCP (Audio/Video Remote Control Profile)
This profile is designed to provide a standard interface to control TVs, Hi-fi equipment, etc. to allow a single remote control (or other device) to control all of the A/V equipment to which a user has access. It may be used in concert with A2DP or HID (see bellow). It has the possibility for vendor-dependent extensions. Additionally, with the version 1.3 release of the specification, there is now capability to transmit information on the status of the music source, including information on the track itself (artist, track name, etc).
http://en.wikipedia.org/wiki/Bluetooth_profile

SPP (Serial Port Profile)
This profile is based on the ETSI TS 07.10 specification and uses the RFCOMM protocol. It emulates a serial cable to provide a simply implemented wireless replacement for existing RS-232 based serial communications applications, including familiar control signals. It provides the basis for DUN, FAX, HSP and AVRCP profiles.
http://en.wikipedia.org/wiki/Bluetooth_profile

HID (Human Interface Device Profile)
Provides support for devices such as mice, joysticks, keyboards, as well as sometimes providing support for simple buttons and indicators on other types of devices. It is designed to provide a low latency link, with low power requirements. Bluetooth HID is a lightweight wrapper of the Human Interface Device protocol defined for USB. The use of the HID protocol simplifies host implementation (ex: support by Operating Systems) by enabling the re-use of some of the existing support for USB HID to also support Bluetooth HID. Popular devices that feature support for this profile include: Logitech diNovo Media Desktop 2.0, Microsoft Optical Desktop Elite for Bluetooth. PlayStation 3 controllers and Wii Remotes also use BT HID.
http://en.wikipedia.org/wiki/Bluetooth_profile
Always read the XBMC online-manual, FAQ and search the forum before posting.
Do not e-mail XBMC-Team members directly asking for support. Read/follow the forum rules.
For troubleshooting and bug reporting please make sure you read this first.
Reply
#7
There have been no major updates with me on the PS3 remote, although I have been using it exclusively in XBMC-Linux using python scripts. I will post what I'm using in the next post - it's an "out-of-the-box" solution, so you don't have to modify XBMC at all to use it. You do have to pair it every time the script is run though. I don't know how to get around this. I am pretty new to bluetooth.

Unfortunately, the virtual keyboard does not play nicely when using the real keyboard (the script I modified just sends keyboard events via uinput), so adding sources and stuff like that requires a real keyboard to type everything out.

I think that LIRC would be the best way to go, just to have a common interface. I don't have enough coding experience to write one myself, so I'm pretty much depending on someone else to implement this. I am willing to help in any way I can, however.

The PS3 remote uses HID to communicate, as far as I have been able to tell - when you pair it in windows, it shows up as a HID device. If you look at my previous posts, you can see that it just sends a single line of hex to show the button states, battery life, etc.
Reply
#8
This shell script came from the azureus wiki to pretty much make any console a daemon. I modified it to call the python script that maps button presses to keyboard events

ps3remote.py
Code:
#! /bin/sh

#The user that will run Azureus
BD_USER=root

#Name of the screen-session
NAME=remotecontrol_screen

#executable files in the following paths that are perhaps needed by the script
PATH=/bin:/usr/bin:/sbin:/usr/sbin

#your path to the azureus directory, where Azureus2.jar is located
DIR=/usr/bin

#Description
DESC="Remote Control screen daemon"

case "$1" in
start)
   if [[ `su $BD_USER -c "screen -ls |grep $NAME"` ]]
      then
      echo "Remote Daemon is already running!"
   else
      echo "Starting $DESC: $NAME"
      su $BD_USER -c "cd $DIR; screen -dmS $NAME python cakemote.py"
   fi
   ;;
stop)
   if [[ `su $BD_USER -c "screen -ls |grep $NAME"` ]]
      then
      echo -n "Stopping $DESC: $NAME"
      su $BD_USER -c "screen -X quit"
      echo " ... done."
   else
      echo "Coulnd't find a running $DESC"
   fi
   ;;
restart)
   if [[ `su $BD_USER -c "screen -ls |grep $NAME"` ]]
       then
      echo -n "Stopping $DESC: $NAME"
      su $BD_USER -c "screen -X quit"
      echo " ... done."
   else
      echo "Coulnd't find a running $DESC"
   fi
   echo "Starting $DESC: $NAME"
      su $BD_USER -c "cd $DIR; screen -dmS $NAME python cakemote.py"
   echo " ... done."
   ;;
status)
   if [[ `su $BD_USER -c "screen -ls |grep $NAME"` ]]
      then
      echo "Remote Daemon is RUNNING"
   else
      echo "Remote Daemon is DOWN"
   fi
   ;;
*)
   echo "Usage: $0 {start|stop|status|restart}"
   exit 1
   ;;
esac

exit 0
Reply
#9
The following is the script I am currently using to interface with the PS3 remote:

cakemote.py modified for my needs:

http://cl1p.net/cakemote/
Reply
#10
Well from what I can tell this remote uses the L2CAP protocol and if that script takes care of ALL the buttons on the remote? this script shouldn't be all that hard to "port" to C/C++ and implement natively into XBMC.

Here's some read
http://people.csail.mit.edu/albert/bluez...ap-and-udp

Compare (C code)
http://people.csail.mit.edu/albert/bluez.../x559.html
to (Python code)
http://people.csail.mit.edu/albert/bluez.../x264.html

I'd even suggest looking at my Wiiremote implementation as a reference on what needs to be changed in order for this to work
http://sourceforge.net/tracker/index.php...tid=581840
Granted, my code doesn't use many bluez calls as the wiiremote have it's lib and my code is emulation mouse behavior but this should be possible depending on how well the python calls you've created works?
Reply
#11
Smile 
brandonj, thanks for your script. I've updated it to use the new event server that is now part of XBMC and committed it to SVN. It works quite well, here are some shots of it:

Image

Image

Image
Always read the XBMC online-manual, FAQ and search the forum before posting.
Please read and follow the forum rules.
For troubleshooting and bug reporting, please make sure you read this first.


Image
Reply
#12
d4rk: very impressive! I have been trying to accomplish this using the libcwiid code that topfs wrote to implement the wiimote, but was having a terrible time doing so (i blame my terrible C++ skills). You made it so I don't have to finish it Smile.

Your implementation looks great! I am glad that some of my script was useful for you. Keep it up, and if I come up with any enhancements, I will definately let you know.

-Brandon
Reply
#13
this might be a little offtopic but... I've seen some remote for PS3 that are given with USB IR adapter. I've never purchased one but was thinking of using it to replace the PS3 remote with a standard logitech harmony remote.

Off course I was waiting to see some progress with XBMC on PS3 before looking better at the remote replacement Smile

So big question, from your topic I guess you are using Linux XBMC on PS3 Smile
would you provide any feedback about it? how it works? performances with DIVx playback without hw acceleration support? have you tried divx playback at lower res (720, 480, etc)?
Reply
#14
stepir Wrote:So big question, from your topic I guess you are using Linux XBMC on PS3 Smile

Actually, it is XBMC on a PC. I simply use the PS3 remote with a bluetooth adapter, and wrote a python script to map button presses from the remote to keyboard presses, which in turn was modified by d4rk to run on XBMC's event server (which is awesome, BTW).

So, no, it does not run on the PS3. But they day it does (and runs well) will also be the day I buy a PS3 Smile

-Brandon
Reply
#15
stepir Wrote:So big question, from your topic I guess you are using Linux XBMC on PS3 Smile
No, you have totally missunderstod, there is no XBMC for PS3. This topic is only about using the PS3 bluetooth remote with XBMC for Linux on a normal x86-computer (not the PS3), with a bluetooth dongle on the x86-computer.

If you would like to discuss (the non-existing) XBMC for PS3 please see this other topic-thread => http://forum.xbmc.org/showthread.php?tid=21849
Always read the XBMC online-manual, FAQ and search the forum before posting.
Do not e-mail XBMC-Team members directly asking for support. Read/follow the forum rules.
For troubleshooting and bug reporting please make sure you read this first.
Reply

Logout Mark Read Team Forum Stats Members Help
PlayStation3 (PS3) bluetooth remote and LIRC?0