sábado, 28 de septiembre de 2013

Raspberry Pi ejecutando MAME controlado con un WiiMote

El día de hoy, después de muchos meses sin actualizar el blog, quiero compartirles este proyecto que puede ser bastante atractivo en mas de un sentido, pues si eres gamer retro o tienes curiosidad en entrar en el mundo del control inalámbrico e interactivo esto te puede dar algunas ideas.


Los requerimientos


Lo primero que tenemos que saber es que el control WiiMote se comunica de manera inalámbrica usando Bluetooth, lo cual nos lleva a tener como requisito un dispositivo Bluetooth USB compatible.

El segundo requerimiento es obviamente el emulador MAME, la version que se ejecuto al grabar este video fue instalada siguiendo las instrucciones de este sitio http://blog.sheasilverman.com/2012/06/raspberry-pi-and-mame-part-2/ , una vez instalado el emulador. necesitaremos algunas ROMS para jugar, en este enlace pueden descargar algunas de estas ROMS que son 100% legales http://www.mamedev.org/roms/ .

El siguiente requisito es cargar en memoria el módulo de kernel uinput con el siguiente comando:

sudo modprobe uinput

tras la ejecución del comando, si no están firmados en el sistema como root, les pedirá su contraseña, y una vez introducida, no se obtendrá mas salida. Para confirmar que el módulo esta cargado, ejecutamos el siguiente comando:


lsmod

este nos listará los módulos que esten cargados en memoria, y su salida será algo similar a esto:

Module                  Size  Used by
uinput                  6599  0 
snd_bcm2835            16432  0 
snd_pcm                77728  1 snd_bcm2835
snd_seq                53482  0 
snd_timer              20110  2 snd_pcm,snd_seq
snd_seq_device          6462  1 snd_seq
snd                    58744  5 snd_bcm2835,snd_timer,snd_pcm,snd_seq,snd_seq_device
snd_page_alloc          5169  1 snd_pcm
leds_gpio               2243  0 
led_class               3570  1 leds_gpio

MAME funciona por default con el teclado, es decir, ciertas teclas se usan para control de dirección y las diferentes acciones que se requieran en el juego, por lo que necesitaremos crear un "traductor" que reciba la información del WiiMote a traves del dispositivo Bluetooth y envie al emulador el evento de teclado adecuado de acuerdo a la acción.

El Código


De nuevo recurriremos a Python para este objetivo, la libreria cwiid es la que tiene toda la lógica para la comunicación con el WiiMote, y la libreria uinput es la que nos permitirá generar los eventos de teclado  para que MAME los use como entrada.

Antes de iniciar con el código, quiero darle credito a Brian Hensley por su script wiimotest.py y a Chris Swan por su script rpi-gpio-jstk.py en los cuales me basé para crear este código. 

#-------------------------------------------------------------------------------
# Name:        Wii Mame
# Purpose:     Script that connects a Wiimote via Bluetooth using cwiid to read
#              inputs and send keystrokes using uinput
#
# Author:      Ismael Martinez based on scripts
#                   wiimotest.py by Brian Hensley and
#                   rpi-gpio-jstk.py by Chris Swan 
#
# Created:     01/10/2012
# Copyright:   OpenSource
#-------------------------------------------------------------------------------
#!/usr/bin/env python

import cwiid
import time
import sys
import uinput

def main():
events = (uinput.KEY_LEFT, uinput.KEY_RIGHT, uinput.KEY_UP, uinput.KEY_DOWN, uinput.KEY_1, uinput.KEY_5, uinput.KEY_LEFTCTRL, uinput.KEY_ESC, uinput.KEY_ENTER)

device = uinput.Device(events)

# Bools to keep track of movement
btn_a = False
btn_b = False
btn_one = False
btn_two = False
btn_pls = False
btn_min = False
btn_up  = False
btn_dwn = False
btn_lft = False
btn_rgt = False
debug = False

        attempt = 0
max_attempts = 3
        while attempt < max_attempts:
            attempt = attempt + 1
            print 'Press button 1 + 2 on your Wii Remote...'
            time.sleep(1)
            try:
                wm=cwiid.Wiimote()
                break
            except:
                if attempt <= max_attempts:
                    print 'Press button 1 + 2 on your Wii Remote... attempt #', attempt,' of ',max_attempts
                else:
                    print 'No Wii Remote found after ', max_attempts, ' attempts. Finishing execution.'
                    sys.exit(0)
        
print 'Wii Remote connected...'
print '\nPress [A]+[B] buttons to disconnect the Wii and end the application'
        time.sleep(1)

        wm.rpt_mode = cwiid.RPT_BTN

        while True:
            bitmask = bin(wm.state['buttons'])[2:].rjust(13, '0')
            #print bitmask

            if bitmask[-1] == "1": #wm.state['buttons'] == 1:
if debug:
print ' [2] pressed \n'
if (not btn_two) :
   btn_two = True
   device.emit(uinput.KEY_LEFTCTRL, 1)
   else:
if btn_two:
   btn_two = False
   device.emit(uinput.KEY_LEFTCTRL, 0)
            if bitmask[-2] == "1": #wm.state['buttons'] == 2:
if debug:
print ' [1] pressed \n'
if (not btn_one) :
   btn_one = True
   device.emit(uinput.KEY_LEFTCTRL, 1)
   else:
if btn_one:
   btn_one = False
   device.emit(uinput.KEY_LEFTCTRL, 0)
            if bitmask[-3] == "1": #wm.state['buttons'] == 4:
if debug:
print ' [B] button pressed \n'
if (not btn_b) :
   btn_b = True
   device.emit(uinput.KEY_1, 1)
   else:
if btn_b:
   btn_b = False
   device.emit(uinput.KEY_1, 0)
   if bitmask[-4] == "1": #wm.state['buttons'] == 8:
if debug:
print ' [A] button pressed \n'
if (not btn_a) :
   btn_a = True
   device.emit(uinput.KEY_5, 1)
   else:
if btn_a:
   btn_a = False
   device.emit(uinput.KEY_5, 0)
   if bitmask[-5] == "1": #wm.state['buttons'] == 16:
if debug:
print ' [-] button pressed \n'
if (not btn_min) :
   btn_min = True
   device.emit(uinput.KEY_LEFTCTRL, 1)
   else:
if btn_min:
   btn_min = False
   device.emit(uinput.KEY_LEFTCTRL, 0)
            if bitmask[-8] == "1": #wm.state['buttons'] == 128:
if debug:
print ' [Home] button pressed \n'
device.emit(uinput.KEY_ESC, 1)
time.sleep(0.25)
device.emit(uinput.KEY_ESC, 0)
time.sleep(0.25)
device.emit(uinput.KEY_DOWN, 1)
time.sleep(0.25)
device.emit(uinput.KEY_DOWN, 0)
time.sleep(0.25)
device.emit(uinput.KEY_ENTER, 1)
time.sleep(0.25)
device.emit(uinput.KEY_ENTER, 0)
time.sleep(0.25)
            if bitmask[-9] == "1": #wm.state['buttons'] == 256:
if debug:
print ' [Down] button pressed \n'
if (not btn_dwn) :
   btn_dwn = True
   device.emit(uinput.KEY_DOWN, 1)
   else:
if btn_dwn:
   btn_dwn = False
   device.emit(uinput.KEY_DOWN, 0)
   if bitmask[-10] == "1": #wm.state['buttons'] == 512:
if debug:
print ' [Up] button pressed \n'
if (not btn_up) :
   btn_up = True
   device.emit(uinput.KEY_UP, 1)
   else:
if btn_up:
   btn_up = False
   device.emit(uinput.KEY_UP, 0)
            if bitmask[-11] == "1": #wm.state['buttons'] == 1024:
if debug:
print ' [Right] button pressed \n'
if (not btn_rgt) :
   btn_rgt = True
   device.emit(uinput.KEY_RIGHT, 1)
   else:
if btn_rgt:
   btn_rgt = False
   device.emit(uinput.KEY_RIGHT, 0)
            if bitmask[-12] == "1": #wm.state['buttons'] == 2048:
if debug:
print ' [Left] button pressed \n'
if (not btn_lft) :
   btn_lft = True
   device.emit(uinput.KEY_LEFT, 1)
   else:
if btn_lft:
   btn_lft = False
   device.emit(uinput.KEY_LEFT, 0)
   if bitmask[-13] == "1": #wm.state['buttons'] == 4096:
if debug:
print ' [+] button pressed \n'
if (not btn_pls) :
   btn_pls = True
   device.emit(uinput.KEY_LEFTCTRL, 1)
   else:
if btn_pls:
   btn_pls = False
   device.emit(uinput.KEY_LEFTCTRL, 0)

   if wm.state['buttons'] > 0 :
if debug:
  print ' <-'
if wm.state['buttons'] == 12 :
   print 'closing Bluetooth connection. Good Bye!'
   exit(wm)

            time.sleep(.02) # Poll every 20ms (otherwise CPU load gets too high)


if __name__ == '__main__':
    main()

El resultado y algunos detalles acerca del orden de ejecución en el siguiente video.


Gracias por seguirme, Saludos!

No hay comentarios.:

Publicar un comentario