Probando el marco digital Parrot DF3120 (parte 3 y final)

Con este artículo termino de explicar cómo explotar todas las características de este marco digital. Este artículo lo dividiré en 3 partes debido a su extensión.

La primera parte consiste en explicar cómo usar la librería SDL (Simple Directmedia Layer) para dibujar en la pantalla. Lo bueno de esta librería es que es muy sencilla de usar y te facilita bastante el dibujar gráficos en la pantalla mediante programación.

La segunda parte trata sobre usar las entradas de información del marco. Se va a leer el estado de los 3 botones, del inclinómetro y la cantidad de luz que hay en el ambiente .

La tercera parte la he reservado para las comunicaciones. Dado que el marco tiene bluetooth y una tarjeta de red configurada voy a explicar cómo intercambiar información entre el marco y otros dispositivos.

1- Librería SDL

Antes de empezar a explicar, lo mejor será que ponga un vídeo, después el código fuente, a continuación cómo compilarlo, ejecutarlo y finalmente explicar su funcionamiento.

[VÍDEO]

[CÓDIGO]

#include <stdio.h> 
#include <stdlib.h> 
#include <math.h>
#include <signal.h>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
 
volatile char salir = 0;
 
void salida(int sig)
{
	salir = 1;
}
 
int main(void) 
{ 
	SDL_Surface *izquierda;
	SDL_Surface *derecha;
	SDL_Surface *ok;
	SDL_Surface *diamante;
	SDL_Surface *sol;
	SDL_Surface *pantalla; 
 	SDL_Rect destino;
	unsigned char alpha = 0;
	float radianes;
 
	if(SDL_Init(SDL_INIT_VIDEO) != 0) 
	{ 
		printf("No se pudo iniciar SDL: %s\n",SDL_GetError()); 
		exit(1); 
	} 
 
	atexit(SDL_Quit);
 
	pantalla = SDL_SetVideoMode(320, 240, 16, 
		SDL_HWSURFACE | SDL_DOUBLEBUF); 
	if(pantalla == NULL) 
	{ 
	  	printf("No se puede inicializar el modo gráfico: %s\n",
			SDL_GetError()); 
		exit(1); 
	} 
 
	SDL_ShowCursor(SDL_DISABLE); 
 
	izquierda = IMG_Load("izquierda.png");
    	if(izquierda == NULL)
	{ 
		printf("No pude cargar gráfico: %s\n", SDL_GetError()); 
		exit(1); 
	} 
 
	derecha = IMG_Load("derecha.png");
	if(derecha == NULL)
	{ 
        	printf("No pude cargar gráfico: %s\n", SDL_GetError()); 
		exit(1); 
	}
 
	ok = IMG_Load("ok.png");
	if(ok == NULL)
	{ 
        	printf("No pude cargar gráfico: %s\n", SDL_GetError()); 
        	exit(1); 
	}
 
	diamante = IMG_Load("diamante.png");
	if (diamante == NULL)
	{ 
        	printf("No pude cargar gráfico: %s\n", SDL_GetError()); 
        	exit(1); 
    	}
 
	sol = IMG_Load("sol.gif");
	if(sol == NULL)
	{ 
		printf("No pude cargar gráfico: %s\n", SDL_GetError()); 
		exit(1); 
    	} 
 
	signal(SIGINT, salida);
 
	destino.x = 0;
	destino.y = pantalla->h / 2 - izquierda->h / 2; 
	destino.w = izquierda->w; 
	destino.h = izquierda->h; 
	SDL_BlitSurface(izquierda, NULL, pantalla, &destino); 
 
	destino.x = pantalla->w / 2 - ok->w / 2;
	SDL_BlitSurface(ok, NULL, pantalla, &destino); 
 
	destino.x = pantalla->w - derecha->w;
	SDL_BlitSurface(derecha, NULL, pantalla, &destino); 
 
	radianes = 0;
	while(!salir)
	{
		destino.x = 0;
		destino.y = pantalla->h - sol->h;
		SDL_SetAlpha(sol, SDL_SRCALPHA | SDL_RLEACCEL, alpha);
		SDL_BlitSurface(sol, NULL, pantalla, &destino); 
 
		destino.x = pantalla->w / 2 + sin(radianes) * 60 - diamante->w / 2;
		destino.y = pantalla->h / 2 + cos(radianes) * 60 - diamante->h / 2;
		SDL_BlitSurface(diamante, NULL, pantalla, &destino);
 
		SDL_Flip(pantalla);
		SDL_Delay(1);
		SDL_FillRect(pantalla, &destino, 
			SDL_MapRGB(pantalla->format, 0, 0, 0));
		destino.x = 0;
		destino.y = pantalla->h - sol->h;
		SDL_FillRect(pantalla, &destino,
			SDL_MapRGB(pantalla->format, 0, 0, 0));
		radianes += 0.02;
		alpha += 1;
	}
 
	SDL_FreeSurface(izquierda);
	SDL_FreeSurface(derecha);
	SDL_FreeSurface(ok);
	SDL_FreeSurface(diamante);
	SDL_FreeSurface(sol);
	SDL_FreeSurface(pantalla);
 
	printf("\nFin del programa\n");
 
	return 0; 
}

[COMPILACIÓN]

Para compilar el programa, copiad el código fuente y guardarlo en la carpeta minifs con el nombre pruebasdl.c, después desde ese mismo directorio ejecutad el comando:

toolchain/arm-v4t-linux-uclibcgnueabi/bin/arm-v4t-linux-uclibcgnueabi-gcc -I build-df3120/staging/usr/include -L build-df3120/staging/usr/lib -lSDL -lSDL_image -liconv -o pruebasdl pruebasdl.c

[EJECUCIÓN]

Una vez se haya creado el fichero pruebasdl, descargarlo en el marco junto con las siguientes imágenes:

tal y como explicaba en el anterior artículo y ejecutarlo. Si todo ha ido bien veréis lo mismo que en el vídeo. Para salir del programa simplemente pulsar las teclas CONTROL y C simultáneamente.

[EXPLICACIÓN]

Explicaré cómo funciona el programa. Un buen tutorial de SDL en español lo podéis descargar de aquí.

  • Se declaran los punteros SDL_Surface, que son los que contendrán las imágenes y el buffer de la pantalla.
  • La función SDL_Init se llama con el parámetro SDL_INIT_VIDEO para inicializar la librería SDL internamente.
  • La función SDL_GetError devuelve una cadena con el último error ocurrido en la librería SDL.
  • La función atexit con el puntero a la función SDL_Quit se llama para que, cuando la aplicación termine, se llame a la función SDL_Quit y libere todos los recursos usados por la librería.
  • La función SDL_SetVideoMode se llama para inicializar el buffer de la pantalla con los parámetros de anchura, altura, profundidad de color y que use la memoria de vídeo con la técnica de doble buffer (haciendo un OR de los valores SDL_HWSURFACE y SDL_DOUBLEBUF). Esta función nos devuelve la estructura SDL_Surface de ese buffer de pantalla.
  • La función SDL_ShowCursor se llama con el parámetro SDL_DISABLE para ocultar el puntero del ratón en la pantalla.
  • La función IMG_Load se llama con la ruta de una imagen como parámetro para cargar la imagen. Esta función nos devuelve la estructura SDL_Surface de la imagen.
  • La función signal se llama con el parámetro SIGINT y un puntero a una función para que se capture la pulsación de las teclas CTRL y C. Cuando se pulsen se llamará a la función salida que simplemente cambiará el valor de la variable salir. Esto se usa para poder salir del bucle principal y terminar el programa de una forma limpia.
  • La función SDL_BlitSurface se llama con una imagen, un valor nulo, el buffer de la pantalla y una estructura SDL_Rect como parámetros. Esto copia la imagen en el buffer de la pantalla en el lugar indicado por la estructura SDL_Rect.
  • La función SDL_SetAlpha se llama con una imagen, con los valores SDL_SRCALPHA y SDL_RLEACCEL y un número como parámetros. Esto modifica la transparencia de una imagen (valor 0 para transparente y valor 255 para opaca). No funciona bien con los PNG, por eso la imagen del sol es un GIF.
  • La función SDL_Flip se llama con el parámetro del buffer de pantalla para traspasar todos los pixels del buffer de pantalla a la tarjeta de vídeo y así mostrarlos.
  • La función SDL_Delay se llama con un número como parámetro para parar la ejecución del programa durante un tiempo especificado en milisegundos.
  • La función SDL_FillRect se llama con el buffer de pantalla, una estructura SDL_Rect y un color como parámetros para que dibuje en la zona determinada por la estructura SDL_Rect un rectángulo con el color especificado. En las animaciones se usa para eliminar el dibujo anterior al que se va a pintar y así dar un efecto de movimiento.
  • La función SDL_MapRGB se llama con el formato del buffer de la pantalla y 3 números como parámetros. Sirve para mapear un color RGB al formato del buffer de la pantalla y así adaptarlo a su profundidad de color.
  • La función SDL_FreeSurface se llama con una estructura SDL_Rect como parámetro para liberar todos los recursos utilizados por esta.

Con todo esto hemos logrado dibujar en la pantalla imágenes png y gif, además de animar dos de ellas en un bucle (una haciendo círculos gracias a las funciones trigonométricas de seno y coseno y la otra a fundirse mediante transparencia) a la espera de que el usuario pulse la combinación de teclas CTRL+C para salir del programa.

2- Entradas de información

Al igual que antes pongo un vídeo, después el código fuente, a continuación cómo compilarlo, ejecutarlo y finalmente explicar su funcionamiento.

[VÍDEO]

[CÓDIGO]

#include <stdio.h> 
#include <stdlib.h> 
#include <math.h>
#include <signal.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
 
#define BASE_PUERTOS 0x56000000
#define BASE_ADC 0x58000000
#define PUERTO_F_CONFIGURACION 0x50
#define PUERTO_G_CONFIGURACION 0x60
#define PUERTO_F_DATOS PUERTO_F_CONFIGURACION + 4
#define PUERTO_G_DATOS PUERTO_G_CONFIGURACION + 4
#define ADC_0 0x0C
#define BOTON_I  3
#define BOTON_C  4
#define BOTON_D  2
#define INCLINOMETRO_1  9
#define INCLINOMETRO_2  10
#define CONFIGURA_F 0xFC0F
#define CONFIGURA_G 0xFFC3FFFF
#define CONFIGURA_ADC 0x7FC2
 
typedef struct
{
 char boton_izquierdo;
 char boton_central;
 char boton_derecho;
 char inclinacion;
 unsigned char luminosidad;
}
ENTRADAS;
 
 
volatile char salir = 0;
static volatile void *memoria_puertos;
static volatile void *memoria_adc;
 
void salida(int sig)
{
	salir = 1;
}
 
void inicializa_entradas()
{
	int fd = open("/dev/mem", O_RDWR);
 
        if (fd < 0)
	{
		perror("/dev/mem");
		exit(1);
	}
	memoria_puertos = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, BASE_PUERTOS);
	if (memoria_puertos == MAP_FAILED) 
	{
		perror("mmap puertos");
		exit(1);
	}
 
	memoria_adc = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, BASE_ADC);
	if (memoria_adc == MAP_FAILED) 
	{
		perror("mmap adc");
		exit(1);
	}
 
	uint32_t configuracion = *(uint32_t *) (memoria_puertos + PUERTO_F_CONFIGURACION);
	configuracion &= CONFIGURA_F;
	*(uint32_t *) (memoria_puertos + PUERTO_F_CONFIGURACION) = configuracion;
 
	configuracion = *(uint32_t *) (memoria_puertos + PUERTO_G_CONFIGURACION);
	configuracion &= CONFIGURA_G;
	*(uint32_t *) (memoria_puertos + PUERTO_G_CONFIGURACION) = configuracion;
 
	*(uint32_t *) (memoria_adc) = CONFIGURA_ADC;
	configuracion = *(uint32_t *) (memoria_adc + ADC_0);
 
}
 
void lee_entradas(ENTRADAS *entradas)
{
	uint32_t datos = *(uint32_t *) (memoria_puertos + PUERTO_F_DATOS);
	entradas->boton_derecho = (datos & (int)pow(2, BOTON_D)) >> BOTON_D;
	entradas->boton_izquierdo = (datos & (int)pow(2, BOTON_I)) >> BOTON_I;
	entradas->boton_central = (datos & (int)pow(2, BOTON_C)) >> BOTON_C;
	datos = *(uint32_t *) (memoria_puertos + PUERTO_G_DATOS);
	entradas->inclinacion = ((datos & (int)pow(2, INCLINOMETRO_2)) + (datos & (int)pow(2, INCLINOMETRO_1))) >> INCLINOMETRO_1;
	datos = *(uint32_t *) memoria_adc;
	if(datos & 0x8000)
	{
		datos = *(uint32_t *) (memoria_adc + ADC_0);
		entradas->luminosidad = (datos & 0x3FF) * 255 / 1023;
	}
}
 
int main(void) 
{ 
	SDL_Surface *izquierda;
	SDL_Surface *derecha;
	SDL_Surface *ok;
	SDL_Surface *diamante;
	SDL_Surface *sol;
	SDL_Surface *pantalla;
 	SDL_Rect destino;
	float radianes = 0;
	float destino_radianes;
	float suma_radianes;
	ENTRADAS entradas;
 
	inicializa_entradas();
 
	if(SDL_Init(SDL_INIT_VIDEO) != 0) 
	{ 
		printf("No se pudo iniciar SDL: %s\n",SDL_GetError()); 
		exit(1); 
	} 
 
	atexit(SDL_Quit);
 
	pantalla = SDL_SetVideoMode(320, 240, 16, 
		SDL_HWSURFACE | SDL_DOUBLEBUF); 
	if(pantalla == NULL) 
	{ 
	  	printf("No se puede inicializar el modo gráfico: %s\n",
			SDL_GetError()); 
		exit(1); 
	} 
 
	SDL_ShowCursor(SDL_DISABLE); 
 
	izquierda = IMG_Load("izquierda.png");
    	if(izquierda == NULL)
	{ 
		printf("No pude cargar gráfico: %s\n", SDL_GetError()); 
		exit(1); 
	} 
 
	derecha = IMG_Load("derecha.png");
	if(derecha == NULL)
	{ 
        	printf("No pude cargar gráfico: %s\n", SDL_GetError()); 
		exit(1); 
	}
 
	ok = IMG_Load("ok.png");
	if(ok == NULL)
	{ 
        	printf("No pude cargar gráfico: %s\n", SDL_GetError()); 
        	exit(1); 
	}
 
	diamante = IMG_Load("diamante.png");
	if (diamante == NULL)
	{ 
        	printf("No pude cargar gráfico: %s\n", SDL_GetError()); 
        	exit(1); 
    	}
 
	sol = IMG_Load("sol.gif");
	if(sol == NULL)
	{ 
		printf("No pude cargar gráfico: %s\n", SDL_GetError()); 
		exit(1); 
    	} 
 
	signal(SIGINT, salida);
 
	while(!salir)
	{
		lee_entradas(&entradas);
 
		// dibujamos el boton izquierdo
		destino.x = 0;
		destino.y = pantalla->h / 2 - izquierda->h / 2; 
		destino.w = izquierda->w; 
		destino.h = izquierda->h;
		if(!entradas.boton_izquierdo)
		{
			SDL_BlitSurface(izquierda, NULL, pantalla, &destino);
		}
		else
		{
			SDL_FillRect(pantalla, &destino, SDL_MapRGB(pantalla->format, 0, 0, 0));
		}
		// dibujamos el boton central
		destino.x = pantalla->w / 2 - ok->w / 2;
		if(!entradas.boton_central)
		{
			SDL_BlitSurface(ok, NULL, pantalla, &destino);
		}
		else
		{
			SDL_FillRect(pantalla, &destino, SDL_MapRGB(pantalla->format, 0, 0, 0));
		}
		// dibujamos el boton derecho
		destino.x = pantalla->w - derecha->w;
		if(!entradas.boton_derecho)
		{
			SDL_BlitSurface(derecha, NULL, pantalla, &destino);
		}
		else
		{
			SDL_FillRect(pantalla, &destino, SDL_MapRGB(pantalla->format, 0, 0, 0));
		}
		// dibujamos el sol de luminosidad
		destino.x = 0;
		destino.y = pantalla->h - sol->h;
		SDL_SetAlpha(sol, SDL_SRCALPHA | SDL_RLEACCEL, entradas.luminosidad);
		SDL_BlitSurface(sol, NULL, pantalla, &destino); 
		// dibujamos el diamante de inclinación
		switch(entradas.inclinacion)
		{
			case 1: // izquierda
				destino_radianes = 3 * M_PI / 2;
				suma_radianes = -0.10;
				break;
			case 2: // derecha
				destino_radianes = M_PI_2;
				suma_radianes = 0.10;
				break;
			case 3: // abajo
				if(radianes > M_PI)
				{
					destino_radianes = 2 * M_PI;
					suma_radianes = 0.10;
				}
				else
				{
					destino_radianes = 0;
					suma_radianes = -0.10;
				}
				break;
 
		}
		if((radianes > (destino_radianes + 0.10)) || (radianes < (destino_radianes - 0.10)))
		{
			radianes += suma_radianes;
			if(radianes < 0)
			{
				radianes = 2 * M_PI;
			}
			else if(radianes > (2 * M_PI))
			{
				radianes = 0;
			}
		}
 
		destino.x = pantalla->w / 2 + sin(radianes) * diamante->w - diamante->w / 2;
		destino.y = pantalla->h / 2 + cos(radianes) * diamante->h - diamante->h / 2;
		SDL_BlitSurface(diamante, NULL, pantalla, &destino);
 
		SDL_Flip(pantalla);
		SDL_Delay(1);
		SDL_FillRect(pantalla, &destino, 
			SDL_MapRGB(pantalla->format, 0, 0, 0));
		destino.x = 0;
		destino.y = pantalla->h - sol->h;
		SDL_FillRect(pantalla, &destino,
			SDL_MapRGB(pantalla->format, 0, 0, 0));
	}
 
	SDL_FreeSurface(izquierda);
	SDL_FreeSurface(derecha);
	SDL_FreeSurface(ok);
	SDL_FreeSurface(diamante);
	SDL_FreeSurface(sol);
	SDL_FreeSurface(pantalla);
 
	printf("\nFin del programa\n");
 
	return 0; 
}

[COMPILACIÓN]

Hay que hacer lo mismo que en la COMPILACIÓN del apartado Librería SDL. Sólo que esta vez el fichero de salida se llama pruebaentradas y el del código fuente pruebaentradas.c.

[EJECUCIÓN]

Hay que hacer lo mismo que en la EJECUCIÓN del apartado Librería SDL, incluidas las imágenes si no las tenéis ya. Sólo que esta vez el fichero se llama pruebaentradas.

[EXPLICACIÓN]

En esta ocasión he aprovechado el código del anterior apartado y le he modificado para que responda a eventos. Ahora cada vez que se pulse un botón saldrá en la pantalla el icono correspondiente, se mostrará hacia que lado del marco está la gravedad y cuanta luz llega al sensor trasero.

Me centraré en explicar cómo he recuperado la información de las entradas de las que dispone el marco (botones, inclinómetro y fotoresistencia).

Si os acordáis en el primer artículo explicaba cómo acceder a la memoria del vídeo para escribir los píxeles de la imagen directamente en ella. Aquí lo que vamos a hacer es abrir el fichero /dev/mem ya que desde él podemos acceder a los registros del microprocesador que contienen la información de las entradas y poder configurarlas. Si queréis saber a fondo cómo funciona el microprocesador s3c2412 que gobierna el marco os podéis bajar su datasheet. Las entradas de los botones se pueden leer desde los pines 2, 3 y 4 del puerto F y las del inclinómetro desde los pines 9 y 10 del puerto G, en ambos casos son GPIO y los leeremos mediante polling. El valor de la fotoresistencia se puede leer del pin ADC0 y como indica el nombre es un ADC que leeremos igualmente mediante polling.

En la función inicializa_entradas inicializaremos los registros:

  • Con la función open abrimos el fichero /dev/mem como lectura y escritura.
  • Con la función mmap abrimos las posición de memoria 0×56000000 para poder acceder desde el espacio de usuario a los registros de configuración y de datos de los puertos GPIO y la posición de memoria 0×58000000 para leer los canales ADC. Estos números se pueden encontrar en las páginas 68 y 69 del datasheet que he puesto antes.
  • Para leer los puertos F y G debemos configurarlos antes. Para esto debemos acceder a las direcciones de memoria de configuración del puerto F (GPFCON 0×56000050) y del puerto G (GPGCON 0×56000060) e indicar qué bits serán de lectura. En el puerto F serán los bits 2, 3 y 4 por lo que el valor de configuración para este registro (según las página 275 del datasheet) será xxxxxx000000xxxx, o lo que es lo mismo, para dejar el resto de bits a su valor original y sólo cambiar los 6 bits de los botones hay que hacer una operación AND del valor 0xFC0F. En el puerto G serán los bits 9 y 10 por lo que el valor de configuración para este registro (según la página 276 del datasheet) será xxxxxxxxxx0000xxxxxxxxxxxxxxxxxx, o lo que es lo mismo, para dejar el resto de bits a su valor original y sólo cambiar los 4 bits del inclinómetro hay que hacer una operación AND del valor 0xFFC3FFFF.
  • Para leer continuamente el valor del canal 0 del ADC donde se encuentra conectada la fotoresistencia debemos configurar el registro ADCCON situado en la dirección de memoria 0×58000000.  Lo inicializaremos asignándole el valor 0x7FC2 (según la página 420 del datasheet). Finalmente leemos el valor del ADC para activarlo.

En la función lee_entradas leeremos el valor de las entradas y los guardaremos en una estructura llamada ENTRADAS:

  • Accedemos a las direcciones de memoria de datos del puerto F (GPFDAT 0×56000054) y G (GPGDAT 0×56000064), extraemos uno por uno los valores de los pines con una operación AND, desplazamos esos valores mediante shifting hasta dejarlos en el primer bit  y los vamos guardando en la variable correspondiente de la estructura. Los botones cuando están libres tienen un valor de 1, pero cuando están pulsados tienen un valor de 0. El botón izquierdo corresponde al pin 3, el botón central corresponde al pin 4 y el botón derecho al pin 2. El inclinómetro tiene un valor de 3 cuando el marco reposa sobre su base , un valor de 2 si reposa sobre el lado derecho y un valor de 1 si reposa sobre el lado izquierdo. No detecta cuando el marco reposa por el lado contrario a la base (que sería un valor de 0).
  • Para el ADC0 accedemos a su dirección de memoria de configuración (ADCCON 0×58000000), leemos su valor y comprobamos que el último bit está a 1 para saber si podemos leer el resultado de la conversión o todavía la está haciendo, si está a 1 leemos la dirección de memoria de datos (ADCDAT0 0x5800000C), nos quedamos con los primeros 10 bits y hacemos una conversión a 8 bits para que se pueda usar directamente en la función  SDL_SetAlpha.

Con todo esto hemos modificado el programa del apartado Librería SDL para que ahora sólo represente las imágenes dependiendo del estado de las distintas entradas que componen el marco digital.

3- Comunicaciones

Como en los casos anteriores pongo un vídeo, después el código fuente, a continuación cómo compilarlo, ejecutarlo y finalmente explicar su funcionamiento.

[VÍDEO]

[CÓDIGO]

#include <stdio.h> 
#include <stdlib.h> 
#include <math.h>
#include <signal.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
 
volatile char salir = 0;
 
void salida(int sig)
{
	salir = 1;
}
 
void carga_imagen(char *cadena)
{
	int conexion, recibidos, reales;
	char cabecera = 0;
	char *puntero;
	char buffer[1024];
	struct addrinfo parametros, *servidores, *coincidencia;
	int rv;
	FILE *fichero;
 
	if((fichero = fopen("texto.png", "wb")) == NULL)
	{
		perror("fichero texto.png");
		return;
	}
 
    	memset(&parametros, 0, sizeof parametros);
    	parametros.ai_family = AF_UNSPEC;
    	parametros.ai_socktype = SOCK_STREAM;
 
	if ((rv = getaddrinfo("sistemasorp.es", "80", &parametros, &servidores)) != 0)
	{
	        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
	        return;
	}
 
	for(coincidencia = servidores; coincidencia != NULL; coincidencia = coincidencia->ai_next)
	{
		if ((conexion = socket(coincidencia->ai_family, coincidencia->ai_socktype,
                coincidencia->ai_protocol)) == -1)
		{
			perror("socket");
			continue;
		}
 
		if (connect(conexion, coincidencia->ai_addr, coincidencia->ai_addrlen) == -1)
		{
		    close(conexion);
		    perror("connect");
		    continue;
		}
 
	        break;
	}
 
	if (coincidencia == NULL)
	{
	        fprintf(stderr, "No se puedo conectar\n");
    	}
 
	freeaddrinfo(servidores);
 
	sprintf(buffer,"GET /blog/imagen.php?cadena=%s HTTP/1.0\r\nHost:www.sistemasorp.es\r\n\r\n", cadena);
	send(conexion, buffer, strlen(buffer), 0);
	while ((recibidos = recv(conexion, buffer, sizeof(buffer), 0)) > 0) 
	{
		puntero = buffer;
		if(!cabecera)
		{
			puntero = strstr(buffer, "\r\n\r\n");
			puntero += 4;
			reales = recibidos - (puntero - buffer);
			recibidos = reales;
			cabecera = 1;
		}
		fwrite(puntero, 1, recibidos, fichero);
	}
 
 
	close(conexion);
	fclose(fichero);
}
 
int main(void) 
{
	char caracter;
	char cadena[100];
	char *puntero = cadena;
	FILE *fichero;
	fd_set lista;
	struct timeval tv;
	SDL_Surface *texto;
	SDL_Surface *pantalla; 
 	SDL_Rect destino;
 
	if(SDL_Init(SDL_INIT_VIDEO) != 0) 
	{ 
		printf("No se pudo iniciar SDL: %s\n",SDL_GetError()); 
		exit(1); 
	} 
 
	atexit(SDL_Quit);
 
	pantalla = SDL_SetVideoMode(320, 240, 16, 
		SDL_HWSURFACE | SDL_DOUBLEBUF); 
	if(pantalla == NULL) 
	{ 
	  	printf("No se puede inicializar el modo gráfico: %s\n",
			SDL_GetError()); 
		exit(1); 
	} 
 
	SDL_ShowCursor(SDL_DISABLE); 
 
	fichero = fopen("/dev/rfcomm0", "r");
 
	if(fichero == NULL)
	{
		perror("NO se pudo abrir /dev/rfcomm0");
		exit(1); 
	}
 
	signal(SIGINT, salida);
 
	destino.x = 0;
	destino.y = 0; 
	destino.w = pantalla->w; 
	destino.h = pantalla->h; 
	SDL_FillRect(pantalla, &destino, 
		SDL_MapRGB(pantalla->format, 255, 255, 255));
	SDL_Flip(pantalla);
	destino.x = 0;
	destino.y = pantalla->h / 2 - 15; 
	destino.w = pantalla->w; 
	destino.h = 30;
 
	memset(cadena, 0, sizeof(cadena));
 
	while(!salir)
	{
 
		fread(&caracter, 1, 1, fichero);
		putchar(caracter);
		if(caracter == ' ')
		{
			caracter = '+';
		}
 
		if(caracter == '\n')
		{
			salir = 1;
		}
		else
		{
			*puntero++ = caracter;
			*puntero = '\0';
			carga_imagen(cadena);
		}
		texto = IMG_Load("texto.png");
		if (texto != NULL)
		{
			SDL_BlitSurface(texto, NULL, pantalla, &destino);
			SDL_Flip(pantalla);
	    	}
	}
 
	fclose(fichero);
	SDL_FreeSurface(pantalla);
 
	printf("\nFin del programa\n");
 
	return 0; 
}

[COMPILACIÓN]

Hay que hacer lo mismo que en la COMPILACIÓN del apartado Librería SDL. Sólo que esta vez el fichero de salida se llama pruebacomunicaciones y el del código fuente pruebacomunicaciones.c.

[EJECUCIÓN]

En el ordenador debéis tener bluetooth ya sea incorporado o mediante un usb y conexión a internet.

En el ordenador activar el ruteo de paquetes tcp/ip para la interfaz de red del marco (ejecutarlo todo como usuario root, en ubuntu con sudo -s)

iptables --table nat --append POSTROUTING --out-interface eth0 -j MASQUERADE
iptables --append FORWARD --in-interface usb0 -j ACCEPT
echo 1 > /proc/sys/net/ipv4/ip_forward

En el marco activar la ruta por defecto de los paquetes tcp/ip

route add default gw 172.16.61.2

En el marco dejar visible a todos el bluetooth y permitir conexiones al marco

hciconfig hci0 piscan

En el marco mostrar la MAC ADDRESS del bluetooth (para saber a donde se debe conectar el ordenador por rfcomm)

hciconfig hci0

En el marco dejar en modo de escucha para rfcomm

rfcomm listen /dev/rfcomm0

En el ordenador conectarse al marco mediante rfcomm

sudo rfcomm connect /dev/rfcomm0 <MAC ADDRESS>

En el ordenador descargarse el programa screen (si no se tenía antes)

sudo apt-get install screen

En el ordenador conectarse al dispositivo rfcomm0 con el programa screen

sudo screen /dev/rfcomm0

En el marco ejecutar el programa

./pruebacomunicaciones

En el ordenador desde el programa screen escribir caracteres. ENTER para hacer salir el programa del marco.

Anotaciones

Tendreis que abrir una consola nueva tanto en el ordenador como en el marco después de ejecutar el comando rfcomm puesto que se queda permanentemente conectado hasta que se pulse CTRL + C.

Para salir del programa screen simplemente pulsad CTRL y A a la vez y después de soltarlos pulsad la tecla K.

[EXPLICACIÓN]

Por un lado se pretende conectar mediante bluetooth al marco usando las utilidades BlueZ mediante RFCOMM. Así conseguimos que desde nuestro ordenador podamos enviarle datos al marco.

Por otro lado pretendemos conectarnos a una página de Internet para recuperar una imagen y mostrarla en el marco.

La unión de ambas cosas es lo que habéis visto en el vídeo. Mientras escribo en el programa screen caracteres, estos se envían al marco mediante bluetooth. Despueś el marco los va concatenando y va llamando a una página web que he hecho a tal efecto para que vaya generando la cadena de texto en una imagen PNG. Esta imagen se la descarga el marco y posteriormente la muestra.

Gracias a las utilidades de BlueZ la conexión bluetooth ya se establece mediante comandos y nosotros nos tenemos que dedicar simplemente a abrir el fichero /dev/rfcomm0 y desde este escribir o leer con las funciones básicas que proporciona C para manejo de ficheros.

Las conexiones a Internet se consiguen mediante programación de sockets y esto nos da mucho juego para poder pedir información (descargar una imagen, llamar a un servicio web, etc.) y luego mostrarla en nuestro marco. En el ejemplo cuando se llama a la función carga_imagen hacemos una conexión http a http://www.sistemasorp.es/blog/imagen.php con la cadena pasada como parámetro y guardamos en el fichero texto.png la imagen PNG que se ha generado. No voy a explicar qué hace cada función de los sockets, pero si os recomiendo leer el mejor manual que hay en internet para saber cómo programarlos: Beej’s Guide to Network Programming.

Y aquí acaban esta serie de artículos esperando que os haya servido de ayuda para que podáis empezar a juguetear con el marco y sacarle muchas utilidades. Me gustaría que si hicieseis algo con el lo pusierais en los comentarios, además de vuestras dudas o lo que sea.

1º artículo

2º artículo

Licencia Creative Commons
Probando el marco digital Parrot DF3120 (parte 3 y final) por SISTEMAS O.R.P, a excepción del contenido de terceros y de que se indique lo contrario, se encuentra bajo una Licencia Creative Commons Attribution 4.0 International Licencia.

16 pensamientos en “Probando el marco digital Parrot DF3120 (parte 3 y final)

  1. mistery

    I changed your code to display only one jpg, and this jpg is bigger then screen, so I woud need to change it to 320 x 240 to fit the screen. Yes my own pic.I want to display it just like the original parrot program displays it.

    Responder
  2. doragasu

    Impresionante, estos 3 tutoriales cubren absolutamente todo lo necesario para sacarle partido al marco, de una manera sencilla y con ejemplos fantásticos.

    Totalmente imprescindible.

    Responder
  3. Cristo Bolaños

    Bestial, muchas gracias por la amplia documentación y el paso a paso.
    Voy a usar este frame como pantalla para un proyecto que tengo con arduino, y me viene de perlas :) toda la potencia de un linux en una pantalla pequeña, el tamaño justo para visualizar datos.

    Responder
  4. richard

    Anyone tried pairing a Wiimote?

    I get Thread Creation Error when I run wminput…

    Wiimote is detected ok.

    Responder
  5. tempest69

    Hola, yego un poco tarde sobre el tema, pero …
    Quando arranca el parrot y uso el SDLvncviewer tengo dos cursor de raton en la pantalla, uno arriba a la izquierda y otro al medio (este es el del vnc).
    No se como suprimir el primero cursor de raton, una idea?
    De momento estoy usando el parrot como vncviewer y en mi vncserver estoy usando el lcd4linux con el driver ‘X11′… y esos cursores de raton quedan muy feos…

    Responder
  6. tempest69

    la modificacion que me as dado y tambien arrancar el vncserver con ‘-nocursor’==> y ya no tengo esos cursores!
    Muchas gracias!

    Responder

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>