This is my first post.

I have started a project with a rpi2 that currently allows me to bluetooth my rpi2 to a bluetooth speaker. On that bluetooth speaker there are three buttons. One that plays and stops the music and the other two that changes the tracks back and forth, but NOT currently. My ultimate goal is to "map" these buttons to correspond with the key presses that allow mpg123 (command-line mp3 player) to move tracks, back and forward.

When you run mpg123 with the command-line option "-C" you can press a key like "d" (on the keyboard) to move track forward. The key "f" moves the track back.

Now, this rpi2 has no physical keyboard hooked to it. I do this through an ssh session. That could be a problem, I guess? I've been messing around with some python code that I've found, that allows me to view these button "events" when pressed. Here is the code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

Reading Events


from subprocess import call, Popen, PIPE
from evdev import eventio, uinput, InputEvent, InputDevice, categorize, ecodes

# Get the device
dev = InputDevice('/dev/input/event0')

# Start event loop
for event in dev.read_loop():
# On Bluetooth Device Event 
if event.code == ecodes.KEY_NEXTSONG:
   # If the pressed key is 'KEY_NEXTSONG'           
   var1 = (categorize(event))
   if var1.keystate == 1:    
      print("Next Track ->")

elif event.code == ecodes.KEY_PREVIOUSSONG:
   # If the pressed key is 'KEY_PREVIOUSSONG'       
   var2 = (categorize(event))
   if var2.keystate == 1:       
      print("<- Previous Track")

elif event.code == ecodes.KEY_PLAYCD:
   # Stops script once 'KEY_PLAYCD' is pressed
      print("Stopping Script")

I get this output:

Next Track ->
<- Previous Track
Stopping Script

I can print the variables by removing the print "string" and replacing it with variable itself:


and I get:

key event at 1465481383.228349, 163 (KEY_NEXTSONG), down

I would like to do more than just print these events. What I would like to know, is it possible (I'm sure it is) to "convert" these key events into keyboard strokes (like "d" and "f") and then inject these into "mpg123" so I can move the tracks "back" and "forward"? Remember, there is no physical keyboard connected to the rpi2.

I'm not a seasoned Python programmer, but I would appreciate someone whom might have done something like this before, whom might could set me on the right path.

I'm thinking I would need to run mpg123 in the script. I have done that before:

import subprocess
import evdev

from evdev import InputDevice, categorize, ecodes
dev = InputDevice('/dev/input/event0')

from subprocess import call
call(["mpg123", "-C", "--list", "/home/pi/bluemusic/playlist.txt" --random", "--buffer", "1024"])

Any help would be much appreciated.


5 Answers 5


Mpg123 has a remote interface (documented here) which is started using the -R --fifo [path-to-fifo] options:

mpg123 -R --fifo '/tmp/fifo'

After starting mpg123 like this, you can pipe instructions to the fifo path using Python like so:

import pipes

t = pipes.Template()
# Command to load and play 1.mp3 (NEED TO USE FULL PATH)
t.append('echo l /home/pi/Music/1.mp3', '--')
# Send command to fifo
f = t.open('/tmp/fifo', 'w')

There isn't, unfortunately, a pre-made skip forward/back option for this interface, but it's not particularly difficult to bang together something workable. Mpg123 requires you to use a collection index to select which track in the playlist to play. Here I've created a playlist file (/home/pi/tmp/playlist) with 3 entries (1.mp3, 2.mp3, 3.mp3). Using the loadlist <entry> <url> command I'm telling it to load and play the track at the corresponding playlist position:

import pipes

# Index of currently playing track
current = 0

t = pipes.Template()
# Command to load playlist and begin playback at current + 1
t.append('echo loadlist ' + str(current + 1) + ' /home/pi/tmp/playlist', '--')
f = t.open('/tmp/fifo', 'w')

This approach leaves you with some additional responsibilities (implement random shuffle, listen for/respond to track ending signals, etc.), but I think it offers more options for fine grained control over a running mpg123 instance than is afforded by the -C remote option. There's a full list of available commands on the documentation page.


For simulating keypresses, I've personally tried python-uinput. Link should contain everything you need (installation, usage, examples).

Basically, it creates a virtual keyboard that you can virtually press.


You've handled getting everything you need from /dev/input events into your python script. The following, demonstrates how I would get everything into mpg123's stdin while emulating a terminal:

from subprocess import Popen,PIPE
from time import sleep

c = Popen("socat stdin EXEC:\"mpg123 -C a.mp3\",pty", stdin=PIPE, shell=True)

while True:
        print("sending a request")

The important parts are:

  • Use Popen directly since you can't wait for mpg123 to finish
  • Use socat to emulate a pty
  • Use [Popen's result].stdin.write('[char]') at the place of the prints in your loop on input.

I'm using the following method to bind the buttons to change tracks in mpg123 (only works when mpg123 is ran from "console", on a headless raspberry pi Zero W with apt-get conspy and screen attached)

login: pi

sudo loadkeys <<EOF
keycode 200 = bracketleft
keycode 201 = bracketright
keycode 165 = bracketright
keycode 163 = bracketleft

screen -S conspy.mpg123
mpg123 -Z -k 0 -@ /home/pi/.mp3z
<ctrl + A,D> to detach

The buttons only work to swap tracks however they do not go "previous" it just skips to another random mp3 from the -Z option passed. Keep in mind you'll have to reload the loadkey script after boot to rebind them. Thanks to [email protected] for the help!


Here is the solution, this is what worked!

from subprocess import Popen, PIPE
from evdev import InputDevice, categorize, ecodes
import os

dev = InputDevice('/dev/input/event0')

# Thanks for the help with this code
masterfd, slavefd = os.openpty()
pid = os.fork()
if pid == 0:
  os.dup2(slavefd, 0)
  os.execlp('mpg123', 'mpg123', '-C', '--list', '/media/flash/music /.playlist.txt', '--random', '-q','--smooth')

def getKey():
    for event in dev.read_loop():
        if event.type == ecodes.EV_KEY:
           c = categorize(event)
           if c.keystate == c.key_down:
               yield c.keycode

keygenerator = getKey()
while True:
    c = next(keygenerator)
    if c == 'KEY_NEXTSONG': os.write(masterfd, ecodes.KEY[32]) 
    if c == 'KEY_PREVIOUSSONG': os.write(masterfd, ecodes.KEY[33])

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.