miércoles, 14 de mayo de 2008

Robot Xilófono - Parte 4: Programación e interfaz.

Bueno, con este post termino ya el tutorial del robot. Este en vez de fotografías llevará código, así que me temo que para quien no le guste la programación, pues será un post un rato pesado.

Antes de ponerme a explicar lo que yo hice, voy a explicar qué opciones tenemos para controlar el robot.

Lo único que necesita el robot es que le mandemos números por el puerto paralelo, si tenemos un programa que ya hace eso, pues ya nos sirve. Ahora bien, como es un robot musical, es importante cuándo mandar los valores. Por ejemplo: Es posible que haya algún modo de mandarle valores mediante algún tipo de impresora, de modo que simplemente tendríamos que darle a imprimir un documento con los valores, y el propio sistema operativo se encargaría de enviar los datos al robot. Pero ¿Cuánto tardaría en empezar? ¿Y si mete el trabajo en cola para imprimirlo un poco más tarde? ¿Manda todos los datos igualmente espaciados en el tiempo? ¿Cuánto tiempo pasa entre un dato y otro?
Hay demasiadas cosas que no controlamos, y que sería interesante controlar.

Lo mejor en este caso es conseguir acceso directo al puerto paralelo, y meter los datos directamente cuando nosotros queramos.

¿Cómo se consigue acceso al puerto paralelo?
En Linux, hay que ejecutar el programa como superusuario, y usar sus funciones para pedir permisos de acceso.
En Windows no existen esas funciones, así que hay que buscar librerías que suplan esa carencia, y den acceso al puerto de la impresora.

Aparte de las consideraciones del sistema operativo, tenemos que tener en cuenta el lenguaje que vamos a utilizar. Algunos lenguajes tienen funciones del tipo outb(valor, puerto). Si conseguimos usarlas, es un modo directo y efectivo de mandar datos al puerto paralelo. Otros lenguajes tienen librerías para acceder al puerto paralelo. Buscando información me encontré con pyParallel, que es una librería para acceder al puerto paralelo desde Python.
Dependiendo de qué lenguaje querais usar, tendreis que buscar librerías y similares.

En mi caso me decanté por lo conocido y sencillo, que fue lenguaje C en sistema Linux. La limitación de esta elección fue que el programa debe ejecutarse como superUsuario, pero por el resto, da acceso directo al puerto paralelo, y el C es un lenguaje con el que estoy muy familiarizado.

Así que lo primero es explicar cómo accedemos al puerto paralelo.
Jairo me pasó este archivo:
/***************************************************************************
* parport.c
*
* Wed Dec 27 03:22:16 2006
* Copyright 2006 Jairo Chapela Martínez
* jairochapela@gmail.com
****************************************************************************/

/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <stdio.h>
#include <unistd.h> /* needed for ioperm() */
#include <sys/ioctl.h>
#include <sys/io.h>
#include <fcntl.h>
#include <linux/parport.h> /* for outb() and inb() */
#include <linux/ppdev.h>

#define BASE_PORT 0x378

int fd;
const char* parport_path = "/dev/parport0";

int init_parport (void)
{
fd = open (parport_path, O_RDWR);
if (fd < 0)
{
printf ("Sorry, you were not able to gain access to the ports\n");
exit (1);
}

return 0;
}


void parport_write (unsigned char byte)
{
int value = byte;

if(ioperm(BASE_PORT, 3, 1))
{
perror("ioperm");
exit(1);
}

outb(value, BASE_PORT);
}

Podemos compilar el fichero c y hacer una librería, podemos incluirlo como si de un fichero .h se tratase, o podemos copiar las funciones y pegarlas en nuestro código. init_parport() inicializa el puerto y parport_write(byte) manda un byte al puerto paralelo.

Con estas dos funciones ya podemos comunicarnos perfectamente con nuestro robot. Cómo programe cada uno su interfaz ya es otra cosa.

Mi interfaz tenía esta pinta.


Como era para el día de la ciencia en la calle, quería que fuese una cosa que llamase la atención, y que fuese fácil de utilizar. Opté por el OpenGL por que ya lo había usado antes, y las cosas en 3D quedan muy bonitasy resultonas.

La interfaz funciona de la siguiente manera:
Al pulsar en las teclas de abajo, se envía directamente el dato al puerto paralelo, y se toca la nota al momento. De ese modo, es como un teclado, al pulsar una tecla, suena la nota.
En la partitura se pueden poner notas, y al darle a play (El botón con la flecha azul) se tocan todas seguidas.
Las flechas rojas son para cambiar de página, para poder hacer canciones un poco largas, ya que en esa partitura solo caben 24 notas. La página actual se puede ver encima de la partitura, hay un total de 10 páginas.
Hay unos botones de siguiente y anterior (botones con la flecha y un cuadro azules), que sirven para cambiar de canción entre las que hay guardadas en disco.
El botón de stop (el del cuadrado azul) para de reproducir la canción en curso.
Por último, el botón de grabar (círculo rojo) guarda en disco la partitura actual.

Un detalle que ya tenía pensado, pero que consideré imprescindible una vez que me dijeron que nos iban a poner una pantalla táctil, es el de las animaciones. Por un lado, está bien tener algún tipo de animación continua de fondo. En mi caso, el toroide de la esquina superior derecha.
Su única función es que se vea que está funcionando, un poco a modo de los salvapantallas, es algo que se mueve, y por lo tanto el ordenador está haciendo algo. También sirve para tener una referencia de la fluidez de las animaciones, y saber si se nos ha colgado el programa.
Por otro lado, los botones y "entes pulsables" deberían tener todos algún tipo de animación, y más en una pantalla táctil. En general la gente espera de los ordenadores respuestas inmediatas. Si pulsas un botón, tiene que pasar algo, y si el programa es lento, la partitura tiene silencios al principio, o el xilófono tarda un poco en dar la primera nota, la gente se impacienta, así que tienes que dejar bien claro que sí que han pulsado el botón. Lo normal es animar el botón. La gente pulsa el botón, ve que el botón se hunde, y sabe que lo ha pulsado. En una interfaz con un ratón o similar, tenemos el propio botón del ratón para convencernos, pero de una pantalla táctil no solemos fiarnos, por que no hay mecanismo físico tangible, así que tenemos que hacer un mecanismo software tangible.

Lo siguiente que queda ya es programar en C usando las librerías de OpenGL. En este caso utilicé también las librerías GLU y GLUT, que facilitan muchísimo el uso de ventanas, ratón, teclado, timer, etc. Y que además son portables entre plataformas, con lo que el mismo código OpenGL se puede compilar en Windows y en Linux sin mucho problema.

En principio no voy a comentar aquí cómo se utiliza el OpenGL, por que para eso hay tutoriales, cursillos, ejemplos, etc. Pero si alguien está muy interesado, puedo pasarle el código, o comentarle algunos truquillos, o darle alguna página de referencia.

Y bueno, aquí se acaba el tutorial del robot que toca el xilófono.... ya tengo ideas para los siguientes experimentos, ahora solo necesito tiempo libre, y una excusa para fabricarlos ^__^

Pd. Perdón por el código sin indentar ni nada, pero no encuentro un modo sencillo de ponerlo con sus tabulaciones, espacios y demás... tendré que investigar al respecto.