Arduino

Control de un videojuego mediante Arduino y un acelerómetro MMA7455

Figura 1: Circuito de Arduino con acelerómetro MMA7455.

Por azares del destino me encargaron la lectura de datos de un acelerómetro MMA7455 con un Arduino UNO. Por fortuna, existe un proyecto utilizando este sensor y una biblioteca con todo lo necesario para entablar la comunicación por medio de I2C.

Dado que la mayoría del trabajo ya estaba hecho, decidí hacer algo un poco más divertido, como se muestra en el siguiente video:

Pero pasemos primeramente a la lectura de datos: esta tarea se realiza mediante una ligera adaptación del proyecto salida digital del acelerómetro MMA7455 para ser leído mediante Arduino Logger, como se muestra en el listado 1.

//Biblioteca para uso de I2C.
#include <Wire.h>
//Biblioteca para el acelerómetro.
#include "MMA_7455.h"

//Bandera para saber si debemos enviar datos.
bool enviar = false;
//Instancia del acelerómetro MMA7455.
MMA_7455 sensor = MMA_7455();
//Lectura de los tres ejes.
char x, y, z;

void setup() {
    Serial.begin(9600);
    sensor.initSensitivity(2);
    sensor.calibrateOffset(5, 21, -63);
}

void loop() {
    recibirDatos();
    if (enviar == true){
        x = sensor.readAxis('x');
        y = sensor.readAxis('y');
        z = sensor.readAxis('z');
        Serial.print(x, DEC);
        Serial.print("\t");
        Serial.print(y, DEC);
        Serial.print("\t");
        Serial.println(z, DEC);
    }
    delay(100);
}

//Recibimos los datos del programa de C# con el fin
//de determinar si debemos enviar o no datos.
void recibirDatos(){
    if (Serial.available() > 0){
        int recibido = Serial.read();
        // El número 49 equivale a "1", equivale a enviar.
        // El número 50 equivale a "2", equivale a pausar envío.
        if (recibido == 49){
            enviar = true;
        }else if (recibido == 50){
            enviar = false;
        }
    }
}

La calibración (línea 16) es una forma de establecer todos los valores de aceleración en cero conforme a las condiciones iniciales del sensor. Estas condiciones iniciales hacen referencia a la posición e inclinación del sensor, factores que influyen sobre las mediciones posteriores. Para mi experimento, el sensor entrega los valores de cero tras el ajuste con los valores 5, 21 y -63 (aunque con toda seguridad puedo decir que en tu caso serán diferentes).

Y fin, este proyecto de lectura de datos ha concluido… ¡pasemos a jugar!.

Arduino y Python

La ingente cantidad de proyectos disponibles en Internet me auxilio en la tarea de integración de Arduino con Pygame. Tomando como base el proyecto Pylolgraph, creé una clase que implementa la conexión con el puerto serial mediante pySerial:

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

"""
Módulo que contiene una clase para la conexión con una tarjeta Arduino UNO por
medio del puerto serial.
"""

from serial import Serial

class ArduinoUNO(Serial):
    """
    Clase para la conexión con la tarjeta Arduino UNO, derivada de la clase
    Serial para entrablar comunicación.
    """
    def __init__(self, puerto, baudios, **args):
        "Inicia la conexión con el puerto serial."
        try:
            super(ArduinoUNO, self).__init__(
                port = puerto, baudrate = baudios, **args)
        except:
            print("No se pudo establecer conexión en el puerto especificado.")
        
    def escribir(self, datos = None):
        "Envía información a la tarjeta."
        self.write(datos)
        
    def leer(self):
        "Recibe los datos enviados por la tarjeta Arduino."
        resultado = self.readline().strip()
        return resultado
        
    def desconectar(self):
        "Termina la conexión con el puerto serial."
        self.close()

Antes de integrar el acelerómetro con el juego, es necesario crear una prueba para asegurar que la lectura de datos es correcta (así como la calibración):

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

"""
Módulo de prueba de transmisión y recepción de datos con el Arduino UNO.
"""

from arduino_uno import ArduinoUNO

def prueba():
    "Prueba de lectura del puerto serial."
    puerto  = "/dev/ttyACM0"
    baudios = 9600
    arduino = ArduinoUNO(puerto, baudios)
    # Inicio del ciclo de lectura.
    print("Iniciando lectura de datos desde Arduino.")
    while True:
        print(arduino.leer())

if __name__ == "__main__":
    prueba()

El código de prueba se encarga de iniciar la comunicación con Arduino por medio del puerto serial (en caso necesario, modificar el número de puerto), y crea un ciclo infinito para la lectura de los datos del sensor. Tras comprobar que todo opera de manera correcta (se obtienen valores cercanos a cero en la mayoría de las lecturas), procedemos a integrar con Pygame.

Hora del espectáculo: Arduino y Pygame

Dado que en el videojuego solamente podemos mover la paleta del jugador en un eje (si aún no tienes el juego descarga UD Bricks v0.2), la lectura de los ejes y y z no es necesaria. Por lo tanto, modificamos un poco el código del listado 1 para adaptarnos a las nuevas necesidades:

//Biblioteca para uso de I2C.
#include <Wire.h>
//Biblioteca para el acelerómetro.
#include "MMA_7455.h"

//Instancia del acelerómetro MMA7455.
MMA_7455 sensor = MMA_7455();
//Variable para almacenar la lectura del eje X.
char x;

void setup() {
    Serial.begin(9600);
    sensor.initSensitivity(2);
    sensor.calibrateOffset(5, 21, -63);
}

void loop() {
    x = sensor.readAxis('x');
    Serial.println(x, DEC);
    delay(100);
}

Ahora, incorporaremos el control con Arduino + MAA7455 a UD Bricks v0.2. Vayamos por partes. Primero creamos el paquete arduino, con los siguientes tres archivos:

  • arduino_uno.py: el archivo que contiene la clase para la conexión por medio del puerto serial.
  • prueba.py: archivo de prueba para verificar que la conexión se realiza de manera exitosa.
  • __init__.py: archivo necesario en la carpeta de cualquier paquete.

Para integrar con UD Bricks, es necesario modificar los siguientes archivos:

  • escenas.py: es necesario crear el objeto arduino para poder utilizar el dispositivo como control.
  • objetos.py: el jugador debe aceptar el parámetro de velocidad enviado por el acelerómetro.
class EscenaJuego(Escena):
    "Clase que define la escena principal del videojuego."
    def __init__(self):
        # .
        # Código previo.
        # .
        # Conexión con Arduino UNO.
        puerto  = "COM3"
        baudios = 9600
        try:
            self.arduino = ArduinoUNO(puerto, baudios)
            self.arduino.leer()
        except:
            self.arduino = None
            print("No se pudo iniciar la conexión con Arduino.")
        
    def leer_eventos(self, eventos):
        "Atando eventos a los objetos."
        for evento in eventos:
            if evento.type == p.KEYDOWN:
                self.jugador.mover(evento.key)
        # Lectura de datos de Arduino.
        if self.arduino:
            self.jugador.moverConAcelerometro(self.arduino.leer())

En el listado 5 se muestran dos operaciones esenciales para incluir el Arduino:

  • Se define el objeto arduino al iniciar el videojuego (líneas 8 a 12). En caso de que no sea encontrado, manda un mensaje a la pantalla (líneas 13, 14 y 15).
  • Se agrega la lectura del dato del sensor a la función leer_eventos (líneas 22 a 24)
class Jugador(p.sprite.Sprite):
    def mover(self, tecla):
        "Mueve la paleta del jugador en la pantalla."
        if tecla == p.K_LEFT:
            self.speed = [-15, 0]
        elif tecla == p.K_RIGHT:
            self.speed = [15, 0]
        else:
            self.speed = [0, 0]      
        self.rect.move_ip(self.speed)
        self.verificarLimites()

    def moverConAcelerometro(self, valor):
        # Conversión del valor en cadena a un valor flotante.
        try:
            valor = -float(valor)
        except:
            valor = 0
        # Sensibilidad.
        if abs(valor) < 15:
            valor = 0
        else:
            valor = valor / 2
        # Asignación.
        self.speed = [valor, 0]
        self.rect.move_ip(self.speed)
        self.verificarLimites()

    def verificarLimites(self):
        if self.rect.left < 0:
            self.rect.left = 0
        if self.rect.right > WIDTH:
            self.rect.right = WIDTH

En el listado 6 se observan tres funciones, las cuales son las siguientes:

  1. mover, se encarga de mover la paleta del jugador en base a la lectura del teclado;
  2. verificarLimites, asegura que el jugador no abandone la pantalla (no salga por el lado izquierdo o derecho);
  3. moverConAcelerometro, recibe el dato del acelerómetro y lo convierte a flotante (líneas 14 a 18). Posteriormente, lidiamos con la sensibilidad en dos aspectos: valor mínimo considerado para el movimiento y un factor de escalamiento para un mejor control (19 a 23). Finalmente, se asignan esos valores para efectuar el movimiento de la paleta.

El código completo conforma la tercer entrega de UD Bricks, la cual pongo a su disposición en Google Code:

[button style=”black” url=”http://ud-bricks.googlecode.com/files/ud-bricks_v0.3.zip” color=”black” link=”http://ud-bricks.googlecode.com/files/ud-bricks_v0.3.zip”] Descargar UD Bricks, v0.3 [/button]

El circuito

El circuito físico se muestra en la figura 1 (se recomienda no imitar las conexiones tan desordenadas realizadas por un servidor).

Figura 1: Circuito de Arduino con acelerómetro MMA7455.

Figura 1: Circuito de Arduino con acelerómetro MMA7455.

Dado que la figura 1 no es lo suficientemente clara, en la figura 2 se muestra el esquemático del circuito.

Figura 2: Conexión del acelerómetro MMA7455 con Arduino UNO.

Figura 2: Conexión del acelerómetro MMA7455 con Arduino UNO.

Nuevos horizontes

Puesto que es un acelerómetro de tres ejes, es obvio que estamos desperdiciando mucho potencial (dos de tres ejes no son usados). Por ello, en proyectos posteriores se cubrirá esta deficiencia (con algo de suerte con proyectos más útiles y ambiciosos :3). Aunque ya cuento con algunas ideas por desarrollar, me gustaría escuchar ideas al respecto, por más simples o descabelladas que parezcan. Una nueva adquisición merece ser explotada, y qué mejor que sirviendo a un próposito, a la resolución de una necesidad. Espero sus ideas en la seción de comentarios.

You Might Also Like

9 Comentarios

  • Responder
    ROBERTO AMBROSIO
    octubre 2, 2012 at 10:38 pm

    Muy buen trabajo Carlos, te felicito, ahora debo de probarlo si se puede en el costoso LabVIEW.

  • Responder
    Antonio
    diciembre 30, 2012 at 1:01 pm

    Genial tu proyecto.
    Lo he encontrado buscando info para hacerme algo que se me a ocurrido y te puede ser util.
    Es un circuito para el coche. Cuando detecte que el freno esta pisado (luz de freno en conversor AD) y detecte una desaceleracion de mas de 0,5G’s, que encienda,con un rele, las luces de emergencia “warnings” durante unos 25 segundos.
    Algunos volvo lo llevan.

    • Responder
      Carlos Ramos
      diciembre 30, 2012 at 2:08 pm

      Gracias por tu comentario y excelente sugerencia, Antonio. He de decir que nunca me pasó por la cabeza realizar circuitos para un carro. Había pasado por mi mente algo de domótica y proyectos didácticos, pero tu sugerencia abre un nuevo abanico de ideas. Me agradaría saber más acerca de tus avances en el tema.

  • Responder
    Fabian Guerrero
    abril 15, 2015 at 10:37 am

    hola, me parecio muy interesante tu articulo, yo desde hace 2 años teno un acelerometro mma7455 y lo quise volver a usar para un proyecto de la universidad y con la libreria que usaste me dan estos valores predeterminados x=127 y=82 z=76. x y z varian bien pero y no hace nada, de casualidad me puedes explicar como calibraste el sensor?

    muchisimas gracias y un excelente video

    • Responder
      Carlos Ramos
      abril 16, 2015 at 6:18 am

      Hola, Fabián. Calibrar es una muy bonita palabra. No hice tal cosa como calibrar el sensor, es más bien como una compensación en software. En la línea 16 del listado 1 está sensor.calibrateOffset(5, 21, -63); que corresponden a los valores iniciales que me daba a mí el sensor (tras unas tantas pruebas experimentales, estimas el error). Simplemente le sumas lo que obtuviste de esas mediciones en “estado estable” y lo pones ahí para que de cero (creando la ilusión de que todo es amor y felicidad).
      Si preguntas para calibración automática, es un tema diferente. Pero la “calibración manual en software” consiste en eso: saber los valores iniciales de cada eje y sumar o restar el contrario para que de cero.
      Saludos.

  • Responder
    Richard
    septiembre 26, 2017 at 6:41 pm

    Hola muy bueno el codigo, pero quisiera saber si todo el codigo va en pyrhon oh si va en otro programa, y si podrias subir de nuevo el link de descarga me seria de ayuda gracias

  • Responder
    richard
    octubre 23, 2017 at 2:42 pm

    buenas y si en lugar de un acelerometro quisiera moverlo usando un potenciometro como seria el codigo

    • Responder
      Carlos Ramos
      octubre 25, 2017 at 4:56 pm

      Ah… pues asumo que tendrías que utilizar el convertidor analógico a digital, y en base al voltaje recibido decidir si ir en una dirección u otra ._.

    Deja tu comentario