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]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
#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:
1 |
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]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
#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 0x56000000 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 0x58000000 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 0x56000050) y del puerto G (GPGCON 0x56000060) 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 0x58000000. 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 0x56000054) y G (GPGDAT 0x56000064), 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 0x58000000), 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]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
#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(¶metros, 0, sizeof parametros); parametros.ai_family = AF_UNSPEC; parametros.ai_socktype = SOCK_STREAM; if ((rv = getaddrinfo("sistemasorp.es", "80", ¶metros, &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)
1 2 3 |
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
1 |
route add default gw 172.16.61.2 |
En el marco dejar visible a todos el bluetooth y permitir conexiones al marco
1 |
hciconfig hci0 piscan |
En el marco mostrar la MAC ADDRESS del bluetooth (para saber a donde se debe conectar el ordenador por rfcomm)
1 |
hciconfig hci0 |
En el marco dejar en modo de escucha para rfcomm
1 |
rfcomm listen /dev/rfcomm0 |
En el ordenador conectarse al marco mediante rfcomm
1 |
sudo rfcomm connect /dev/rfcomm0 <MAC ADDRESS> |
En el ordenador descargarse el programa screen (si no se tenía antes)
1 |
sudo apt-get install screen |
En el ordenador conectarse al dispositivo rfcomm0 con el programa screen
1 |
sudo screen /dev/rfcomm0 |
En el marco ejecutar el programa
1 |
./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 https://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.
Unbelievable, thank you so much for this articles.
Thanks to you for reading and your comments.
Can you please tell me how can I resize picture when I load it so it fits the screen?
One picture of your own, the icons or the png from the web?
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.
SDL can’t scale images, but you can add this function to do that: http://www.sdltutorials.com/sdl-scale-surface
Impresionante tutorial, muchas gracias por tu tiempo y esfuerzo.
Gracias, siempre espero que sirva de algo lo que publico 😉
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.
I found a slideshow aplication based on SDL here: http://en.sourceforge.jp/projects/sfnet_sshow/releases/
How can I integrate the slideshow-0.7.0.tar.gz file to the minifs build for the parrot?
I’m afraid that you cannot use that software. The minifs build only includes SDL library, but not SDL_ttf.
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.
Anyone tried pairing a Wiimote?
I get Thread Creation Error when I run wminput…
Wiimote is detected ok.
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…
Tendrías que recompilar el SDLvncviewer poniendo SDL_ShowCursor(SDL_DISABLE) inmediatamente después de SDL_SetVideoMode.
la modificacion que me as dado y tambien arrancar el vncserver con ‘-nocursor’==> y ya no tengo esos cursores!
Muchas gracias!