Este es el tutorial hecho por el compañero Saul para lograr dicho objetivo:
LINK: Como hacer un hack a un dispositivo EEG con Arduino
Preparación
Primero que nada hay que instalar la IDE de Arduino, aqui un minitutorial hecho por mi
LINK: Programando con Arduino
Después hay que instalar la librería Brain, los pasos son sencillos:
- 1. Descargar la libreria de aquí: Arduino Brain Library
- 2. Descomprimimos y la agregamos a la carpeta libraries en la carpeta de Arduino, en mi caso el path es:
Y ya dentro:
Así se ve el sketch, si se fijan en la función loop comente la línea Serial.println(brain.readErrors()); , esto es para que no nos regrese ninguna otra información que no sean los datos que nos interesan del MindReader.
Ahora simplemente lo compilamos y lo subimos a nuestro Arduino conectado al MindReader, con el botón UPLOAD

Yo usé Python para el juego, entonces, para escuchar los datos enviados por Arduino debemos instalar pySerial, los pasos son ultra sencillos:
- Descargar libreria: http://pypi.python.org/pypi/pyserial
- Descomprimir el archivo descargado
- Entrar a la carpeta y ejecutar el comando: python setup.py install
- TUTORIAL: http://www.arduino.cc/playground/Interfacing/Python
El juego
El juego lo llame Magic Warriors!.
El objetivo del juego es sencillo, se trata de algo parecido a hacer Genkidamas. Según tu nivel de concentración es la cantidad de energía que se agrega a la Genkidama, asi pues, de todos los datos que me da el MindReader solo me quedo con dos que son atención y meditación; dichos valores se suman y se saca un promedio, el promedio es la energía que se agrega cada segundo a la Genkidama. Básicamente son sumas, del valor anterior mas el nuevo valor durante un tiempo determinado que son 5 segundos.
Por el lado de la CPU, es una simple sumatoria que se realiza mediante números random, que, según la dificultad, son obtenidos dentro de determinado rango. Entre mayor es el nivel el rango es más ajustad y se mueve hacia valores más altos.
El ganador se selecciona comparando quién acumulo la mayor cantidad de energía, una comparación entre tu sumatoria final y la del CPU.
Código relevante
El juego fue desarrollado utilizando pygame, la temática es simple, y como ven el cálculo que realiza es super sencillo, entonces, veamos algunas partes relevantes del código desarrollado.
Escuchando al Arduino
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ArduinoEEG: | |
#=========================================================== | |
# El constructor de la clase, toma el puerto donde esta | |
# conectado el Arduino. | |
# USB es el puerto serial, es dinamico, por alguna razón | |
# cambia, hay que estarlo verificando en laa IDE de | |
# Arduino. | |
# CONNECTION almacena el estado de la conexion. | |
# EEG_VALUE es un diccionario que almacenara los datos | |
# enviados por Arduino. | |
#=========================================================== | |
def __init__(self, port): | |
self.port = port | |
self.usb = '/dev/ttyACM0' | |
self.connection = '' | |
self.eeg_value = dict() | |
#================ def connect(self) ======================== | |
# Este metodo comienza la conexion al Arduino. | |
# El intento de conexion esta dentro de un bloque try... except | |
# para mostrar el mensaje correspondiente. | |
# 'Ready' en caso de exito, 'Error' en caso de fracasar. | |
# Ademas, regresa un 0 en caso de exito y un 1 en caso de fracasar | |
#=========================================================== | |
def connect(self): | |
print '>> Attempting to connect to Arduino EEG' | |
try: | |
self.connection = serial.Serial(self.usb, self.port) | |
print '>> ARDUINO EEG CONNECTION => Ready' | |
return 0 | |
#self.connection.baudrate = 19200 | |
except: | |
print '>> ARDUINO EEG CONNECTION => Error' | |
return 1 | |
#================== def read(self) ========================= | |
# Este metodo lee la cadena de datos enviada desde Arduino. | |
# Se crea una cadena vacia 'temp' | |
# Si hay conexion, leemos el dato y lo separamos (recordemos | |
# que es una cadena con formato CSV) | |
# A veces no recibimos una cadena completa, mientras su | |
# longitud sea muy pequeña, seguimos leyendo hasta obtener | |
# una completa. | |
# Imprimimos temo, que en este punto debido a la funcion | |
# 'split' ya es una lista de elementos. Regresamos la lista. | |
#=========================================================== | |
def read(self): | |
print '>> Reading values from Arduino EEG...' | |
temp = '' | |
if(self.connection != ''): | |
temp = self.connection.readline() | |
temp = temp.split(',') | |
while(len(temp) < 10): | |
temp = self.connection.readline() | |
temp = temp.split(',') | |
print temp | |
return temp | |
else: | |
return 0 | |
#=================== def get(self) ========================= | |
# Este metodo obtiene los datos de la cadena de datos leida | |
# desde Arduino. | |
# Los valores sera una lista de elementos, retornada por el | |
# metodo 'read()'. | |
# Creamos 2 elementos en el diccionario self.eeg_value | |
# correspondientes a la atencion y meditacion, que son los | |
# primeros 2 elementos de la lista, los demas no nos importan | |
# para efectos del juego. | |
# que es una cadena con formato CSV) | |
# Imprimimos el diccionario para verificar y regresamos | |
# ambos valores. | |
#=========================================================== | |
def get(self): | |
print '>> Getting values from Arduino EEG' | |
values = self.read() | |
self.eeg_value['attention'] = int(values[1]) | |
self.eeg_value['meditation'] = int(values[2]) | |
print self.eeg_value | |
return [self.eeg_value['meditation'], self.eeg_value['meditation']] | |
#================ def randValues(self) ===================== | |
# Este metodo es usado para efectos de testing, emula el | |
# metodo de leer-obtener valores de Arduino. | |
# Se crea una lista de elementos temporal. | |
# Creamos 2 elementos en el diccionario self.eeg_value | |
# Generamos los 10 valores que comunmente el Arduino nos | |
# retornaria y los agregamos a la lista. | |
# Creamos 2 elementos en el diccionario self.eeg_value | |
# correspondientes a la atencion y meditacion, que son los | |
# primeros 2 elementos de la lista. | |
# Imprimimos el diccionario para verificar y regresamos | |
# ambos valores. | |
#=========================================================== | |
def randValues(self): | |
print '>> Generating Random Values...' | |
temp = list() | |
for i in range(10): | |
temp.append(random.randint(0,100)) | |
self.eeg_value['attention'] = temp[1] | |
self.eeg_value['meditation'] = temp[2] | |
print self.eeg_value | |
return [temp[2], temp[1]] |
Jugador y Enemigo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Player: | |
#================================================== | |
# El constructor. | |
# Recibe el objeto Arduino, el cual ya debio haber | |
# sido construido y debe tener una conexion | |
# establecida. | |
# MAGIC es el nivel de magia inicial, es cero. | |
#================================================== | |
def __init__(self, arduino): | |
print '>> Creating new Player' | |
self.magic = 0 | |
self.arduino = arduino | |
#============= def recharge(self) ================= | |
# Funcion que recarga la energia del jugador. | |
# Con la funcion 'get' del objeto Arduino obtenemos | |
# los datos. | |
# La magia es igual a la sumatoria de la magia | |
# anterior mas el promedio de los valores obtenidos | |
# desde el Arduino [ (atencion+meditacion)/2 ] | |
# Imprimimos la magia como informacion. | |
#================================================== | |
def recharge(self): | |
print 'Player is recharging magic' | |
magicUp = self.arduino.get() | |
self.magic = self.magic + (sum(magicUp)/2) | |
print 'Player\' magic => ' + str(self.magic) | |
#=========== def randRecharge(self) =============== | |
# Funcion que recarga la energia del jugador con | |
# valores random. | |
# Generamos un numero random. | |
# La magia es igual a la sumatoria de la magia | |
# anterior mas el valor random | |
# Imprimimos la magia como informacion. | |
#================================================== | |
def randRecharge(self): | |
print 'Player is recharging power' | |
magicUp = random.randint(0, 100) | |
self.magic = self.magic + magicUp | |
print 'Player\' magic => ' + str(self.magic) | |
class Enemy: | |
#================================================== | |
# El constructor. | |
# Recibe el nivel del CPU | |
# MAGIC es el nivel de magia inicial, es cero. | |
# Se definen los limites dentro de los cuales seran | |
# calculados los valores de magia. | |
#================================================== | |
def __init__(self, level): | |
print '>> Creating new Enemy' | |
self.magic = 0 | |
self.level = level | |
defineLimits(self) | |
#============= def defineLimits(self) ============= | |
# Define los limites dentro de los cuales seran | |
# calculados los valores de magia. | |
#================================================== | |
def defineLimits(self): | |
if(self.level == 1): | |
self.limitA = 10 | |
self.limitB = 60 | |
if(self.level == 2): | |
self.limitA = 20 | |
self.limitB = 70 | |
if(self.level == 3): | |
self.limitA = 30 | |
self.limitB = 80 | |
if(self.level == 4): | |
self.limitA = 40 | |
self.limitB = 90 | |
if(self.level == 5): | |
self.limitA = 50 | |
self.limitB = 100 | |
#============= def recharge(self) ================= | |
# Funcion que recarga la energia del enemigo con | |
# valores random. | |
# Generamos un numero random dentro de los limites | |
# establecidos. | |
# La magia es igual a la sumatoria de la magia | |
# anterior mas el valor random | |
# Imprimimos la magia como informacion. | |
#================================================== | |
def recharge(self): | |
print 'Enemy is recharging magic' | |
magicUp = random.randint(self.limitA, self.limitB) | |
self.magic = self.magic + magicUp | |
print 'Enemy\' magic => ' + str(self.magic) |
La Batalla!!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Battle: | |
#============================================= | |
# El constructor | |
#============================================= | |
def __init__(self): | |
pass | |
#=========== def build(self, level) =========== | |
# Recibe el nivel del enemigo. | |
# Prepara un objeto Arduino. | |
# Verifica si hay conexion. | |
# Establece un nivel de batalla. | |
# Si hay conexion, entonces creamos al jugador | |
# y al enemigo. | |
# Si no hay conexion, regresamos un cero para | |
# avisar. | |
#============================================= | |
def build(self, level): | |
arduino = ArduinoEEG(9600) | |
isConnected = arduino.connect() | |
self.level = level | |
if(isConnected == 0): | |
self.player = Player(arduino) | |
self.enemy = Enemy(self.level) | |
else: | |
self.level = 0 | |
#============= def start(self) ============== | |
# Solo imprime mensajes de aviso y la magia | |
# inicial de los participantes. | |
# La batalla comienza al presionar la tecla | |
# 'F'. | |
#============================================ | |
def start(self): | |
print '>> Starting battle...' | |
print '\nLEVEL => ' + str(self.level) | |
print 'Focus your magic ...' | |
print 'And press F when you are focused' | |
print '\nPlayer Magic = ' + str(self.player.magic) | |
print 'Enemy Magic = ' + str(self.enemy.magic) | |
#============= def fight(self) ============== | |
# Se encarga de llamar a los metodos de | |
# de recarga de energia de cada uno de los | |
# participantes, durante 5 segundos. | |
# Compara para seleccionar el ganador. | |
#============================================ | |
def fight(self): | |
for i in range(10): | |
self.player.recharge() | |
#self.player.randRecharge() | |
self.enemy.recharge() | |
time.sleep(0.5) | |
winner = self.compare() | |
#============ def compare(self) ============= | |
# Compara la magia final de los participantes | |
# para seleccionar el ganador. | |
# Imprime el ganador en pantalla. | |
#============================================ | |
def compare(self): | |
if(self.player.magic >= self.enemy.magic): | |
print 'Player wins :)' | |
return 1 | |
else: | |
print 'Player loses :(' | |
return 0 |
Lo demas ya son funciones propias de python para interconectar clases, enviar variables y bueno, los métodos obligados de PyGame para la asignación de teclas y demás. Por ultimo, les dejo las librerías que utilice por si a alguien le interesa.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import time, csv, sys, random, threading | |
import serial | |
import pygame | |
from pygame.locals import * |
Espero que les guste, lo que falta es obviamente lo gráfico en en 6 horas fue imposible de terminar, adicional quise agregar algo de interactividad con OpenCV, NADA DE TECLAS!!, quería hacer que el juego se controlara con movimientos en la webcam, hacer un movimiento con el brazo para avanzar entre pantallas y levantar los brazos como si realmente estuvieras haciendo la Genkidama para recargar energía, pero bueno, eso era mas complejo, y ademas quería imprimir los valores del EEG en todo momento pero igual, por falta de tiempo no pude instalar las dependencias y demás.
Sin embargo, me divertí mucho haciéndolo y deseo terminarlo un día de estos, comprar mi EEG y todo el rollo porque realmente estuvo genial.
Bueno, esa fue mi idea en la competencia, espero les haya gustado :)
15 para el lab de integrados
ResponderEliminar