Skip to content

Arduino: Navaja suiza para náufragos (I)

4 mayo, 2013

Anteriormente explicamos y probamos el proyecto Blink, el Hola mundo de Android.  Vamos a complicarlo un poco, haremos que el LED codifique la señal internacional de socorro SOS en código Morse …—…  Parece una tontería, pero fíjate en que se trata “sólo” de la primera entrada, pues esto va a dar mucho juego, sigue leyendo y ya lo comprobarás.

Para ello tendremos en cuenta las siguientes premisas: consideraremos que la duración del punto será la unidad de medida, por ejemplo de 150 ms, que la raya tiene una longitud igual a tres puntos, que entre cada señal hay una separación en silencio igual a un punto, que entre letras hay una separación en silencio igual a tres puntos y que entre palabras hay una separación de 5 puntos.   Con lo que viste en el proyecto Hola mundo y un poco de ingenio tendrías recursos suficientes para desarrollar el código.  Si quieres ver cómo lo hice yo aquí tienes el código de ejemplo (puedes descárgatelo desde Morse_v1):

/*
 Tutorial Arduino arrizen.com
 Morse v1
 */

int led = 13;
void setup() {
 pinMode(led, OUTPUT);
 Serial.begin(9600);
}
// the loop routine runs over and over again forever:
void loop() {
 Serial.print("s");
 digitalWrite(led, HIGH); delay(150); digitalWrite(led, LOW); delay(150); // punto
 digitalWrite(led, HIGH); delay(150); digitalWrite(led, LOW); delay(150); // punto
 digitalWrite(led, HIGH); delay(150); digitalWrite(led, LOW); delay(150); // punto
 delay(300); // separación entre letras = 150 x 3 (pero le restamos el retardo del símbolo anterior)
Serial.print("o");
 digitalWrite(led, HIGH); delay(450); digitalWrite(led, LOW); delay(150); // raya
 digitalWrite(led, HIGH); delay(450); digitalWrite(led, LOW); delay(150); // raya
 digitalWrite(led, HIGH); delay(450); digitalWrite(led, LOW); delay(150); // raya
 delay(300); // separación entre letras = 150 x 3 (pero le restamos el retardo del símbolo anterior)

 Serial.println("s");
 digitalWrite(led, HIGH); delay(150); digitalWrite(led, LOW); delay(150); // punto
 digitalWrite(led, HIGH); delay(150); digitalWrite(led, LOW); delay(150); // punto
 digitalWrite(led, HIGH); delay(150); digitalWrite(led, LOW); delay(150); // punto
 delay(600); // separación entre palabras = 150 x 5 (pero le restamos el retardo del símbolo anterior)
 }

Nuestro programa ahora ocupa 4.762 bytes y no es muy eficiente a juzgar por la cantidad de código repetido, pero funciona.  ¡Y sería muy útil si te quedaras tirado en una isla desierta, en medio del océano, después de un naufragio! (de ahí el título)  Imagínate, los otros supervivientes tratando de hacer señales de humo para llamar la atención de barcos y aviones de rescate, y tú conectas tu Arduino emisor de señales luminosas de “SOS” y lo dejas trabajando por ti mientras te vas a dar un bañito a cualquiera de esas playas de aguas cristalinas y arena blanca.  Tu proyecto se debería parecer a esto:

Estás en el agua, flotando tranquilamente mientras tus “vecinos” corren desesperados en círculos haciendo aspavientos con los brazos y tratando aún de hacer fuego para crear señales visuales. Entonces piensas que el código podría optimizarse, que se vería mejor añadiendo unas funciones a las que llamar para representar el punto, la raya y los dos retardos.  Bien, aquí tienes el código de la siguiente versión Morse_v2:

/*
 Tutorial Arduino arrizen.com
 Morse v2
*/
int led = 13;
void setup() {
 pinMode(led, OUTPUT);
 Serial.begin(9600);
}
void punto() {
 Serial.print(".");
 digitalWrite(led, HIGH);
 delay(150);
 digitalWrite(led, LOW);
 delay(150); // punto
}
void raya() {
 Serial.print("-");
 digitalWrite(led, HIGH);
 delay(450);
 digitalWrite(led, LOW);
 delay(150); // punto
}
void espacio_letra(){
 Serial.print(" ");
 delay(300); // 2 x punto
}
void espacio_palabra(){
 Serial.println();
 delay(600); // 4 x punto
}
void loop() {
 punto();
 punto();
 punto();
espacio_letra();

 raya();
 raya();
 raya();
espacio_letra();

 punto();
 punto();
 punto();

 espacio_palabra();
}

Con estos cambios el código ocupa ahora 4.474 bytes.  Tiene mejor aspecto pero aún así tiene limitaciones: si queremos cambiar la duración de la unidad básica habría que “tocar” todos los delay…  Podemos solventarlo fácilmente creando una variable que contenga el valor de duración deseado.   Tienes la solución más abajo, Morse_v3:

/*
 Tutorial Arduino arrizen.com
 Morse v3
*/
int led = 13;
int duracion = 150;
void setup() {
 pinMode(led, OUTPUT);
 Serial.begin(9600);
}
void punto() {
 Serial.print(".");
 digitalWrite(led, HIGH);
 delay(duracion);
 digitalWrite(led, LOW);
 delay(duracion); // punto
}
void raya() {
 Serial.print("-");
 digitalWrite(led, HIGH);
 delay(duracion * 3);
 digitalWrite(led, LOW);
 delay(duracion); // punto
}
void espacio_letra(){
 Serial.print(" ");
 delay(duracion * 2); // 2 x punto
}
void espacio_palabra(){
 Serial.println();
 delay(duracion * 4); // 4 x punto
}
void loop() {
 punto();
 punto();
 punto();
espacio_letra();

 raya();
 raya();
 raya();
espacio_letra();

 punto();
 punto();
 punto();

 espacio_palabra();
}

Observa cómo ahora puedes ajustar la velocidad cambiando el valor de la variable duración y además fíjate en cómo hemos calculado las duraciones respectivas en las funciones rayaespacio_letra y espacio_palabra, hemos introducido un factor de multiplicación para que sea más intuitivo.  El código generado ocupa tan sólo 4.546 bytes.  Pero, ¿no se podría mejorar aún más? (vete acostumbrándote a hacerte esa pregunta cada vez que cierres una versión)  Sí, se podría mejorar y es lo que vamos a hacer ahora.  “SOS – SOS – SOS…” se te queda un poco corto; te gustaría poder lanzar un mensaje algo más complejo, algo así como “SOS naufragio barco Arduboat.  Todos OK. Enviad ayuda. GPS: -19.054416315770293,-169.85069274902344”   Mientras tanto, tus compañeros de viaje siguen frotando unos palos para hacer fuego y señales de humo…

Vamos por partes, podrías codificar cada letra, número y signo por separado, aunque son muchas… o puedes hacer las cosas bien y hacerte un conversor de frases texto a código Morse, que tiene mejor pinta (a nivel de frikismo, eso alcanzaría un 4’7 sobre 10)

Igual que antes, tienes el código y una explicación de los cambios y si quieres puedes descargarte la versión Morse_v4 de aquí:

/*
 Tutorial Arduino arrizen.com
 Morse v4
*/
int led = 13;
int duracion = 150;
int PUNTO = 1;
int RAYA = 3;
int NULO = 0;
// Emite un punto o una raya y el silencio posterior
void emitir(byte valor) {
 if (valor != NULO) {
 digitalWrite(led, HIGH);
 delay(duracion * valor);

 digitalWrite(led, LOW);
 delay(duracion);
 }
}
void simbolo (byte caracter) {
 byte valor;
 byte indice;

 byte matrizSimbolos[] = { PUNTO, RAYA, PUNTO, RAYA, PUNTO, RAYA, // .
 RAYA, RAYA, PUNTO, PUNTO, RAYA, RAYA, // ,
 RAYA, RAYA, RAYA, PUNTO, PUNTO, PUNTO, // :
 PUNTO, PUNTO, RAYA, RAYA, PUNTO, PUNTO, // ?
 RAYA, PUNTO, PUNTO, PUNTO, PUNTO, RAYA, // -
 PUNTO, RAYA, PUNTO, PUNTO, RAYA, PUNTO}; // "

 switch (caracter) {
 case '.':
 caracter=0;
 break;

 case ',':
 caracter=1;
 break;

 case ':':
 caracter=2;
 break;

 case '?':
 caracter=3;
 break;

 case '-':
 caracter=4;
 break;

 case '"':
 caracter=5;
 break;

 default:
 return;
 }

 for (indice = 0; indice < 6; indice++) {
 valor = matrizSimbolos[(caracter * 6) + indice];
 emitir(valor);
 }
}

void numero(byte caracter) {
 byte valor;
 byte indice;
byte matrizNumeros[] = {RAYA, RAYA, RAYA, RAYA, RAYA, // 0
 PUNTO, RAYA, RAYA, RAYA, RAYA, // 1
 PUNTO, PUNTO, RAYA, RAYA, RAYA, // 2
 PUNTO, PUNTO,PUNTO, RAYA, RAYA, // 3
 PUNTO, PUNTO, PUNTO, PUNTO, RAYA, // 4
 PUNTO, PUNTO, PUNTO, PUNTO, PUNTO, // 5
 RAYA, PUNTO, PUNTO, PUNTO, PUNTO, // 6
 RAYA, RAYA, PUNTO, PUNTO, PUNTO, // 7
 RAYA, RAYA, RAYA, PUNTO, PUNTO, // 8
 RAYA, RAYA, RAYA, RAYA, PUNTO}; // 9

 caracter = caracter - '0';

 for (indice = 0; indice < 5; indice++) {
 valor = matrizNumeros[(caracter * 5) + indice];
 emitir(valor);
 }
}
void letra(char caracter) {
 byte valor;
 byte indice;
 // caracteres A, B, C, D... Z
 byte matrizLetras[] = {PUNTO, RAYA, NULO, NULO, // A
 RAYA, PUNTO, PUNTO, PUNTO, // B
 RAYA, PUNTO, RAYA, PUNTO, // C
 RAYA, PUNTO, PUNTO, NULO, // D
 RAYA, NULO, NULO, NULO, // E
 PUNTO, PUNTO, RAYA, PUNTO, // F
 RAYA, RAYA, PUNTO, NULO, // G
 PUNTO, PUNTO, PUNTO, PUNTO, // H
 PUNTO, PUNTO, NULO, NULO, // I
 PUNTO, RAYA, RAYA, RAYA, // J
 RAYA, PUNTO, RAYA, NULO, // K
 PUNTO, RAYA, PUNTO, PUNTO, // L
 RAYA, RAYA, NULO, NULO, // M
 RAYA, PUNTO, NULO, NULO, // N
 RAYA, RAYA, RAYA, NULO, // O
 PUNTO, RAYA, RAYA, PUNTO, // P
 RAYA, RAYA, PUNTO, RAYA, // Q
 PUNTO, RAYA, PUNTO, NULO, // R
 PUNTO, PUNTO, PUNTO, NULO, // S
 RAYA, NULO, NULO, NULO, // T
 PUNTO, PUNTO, RAYA, NULO, // U
 PUNTO, PUNTO, PUNTO, RAYA, // V
 PUNTO, RAYA, RAYA, NULO, // W
 RAYA, PUNTO, PUNTO, RAYA, // X
 RAYA, PUNTO, RAYA, RAYA, // Y
 RAYA, RAYA, PUNTO, PUNTO}; // Z
caracter = caracter - 'a';

 for (indice = 0; indice < 4; indice++) {
 valor = matrizLetras[(caracter * 4) + indice];
 emitir(valor);
 }
}
void frase(char *cadena){
 int indice = 0;
 char caracter;

 do {
 caracter = *(cadena + indice);
 Serial.print(caracter);

 if( (caracter >= 'A' && caracter <= 'Z' ) ||
 (caracter >= 'a' && caracter <= 'z') ) {
 caracter |= 0x20; // Convertimos a minúsculas
 letra(caracter);
 }

 else if ( caracter >= '0' && caracter <= '9' ) {
 numero(caracter);
 }

 else if (caracter == ' ') {
 delay ( duracion * 2); // separación entre palabras = duración x 5 (pero le restamos el retardo de la letra anterior)
 }

 else {
 simbolo(caracter);
 }
delay (duracion * 2); // separación entre caracteres = duración x 3 (pero le restamos el retardo del símbolo anterior)
 indice++; // apuntamos al siguiente carácter de la frase

 } while (caracter != 0);
}
void setup() {
 delay(1500);
 pinMode(led, OUTPUT);
 Serial.begin(9600);
}
void loop() {
 frase("SOS naufragio barco Arduboat. Todos OK. Enviad ayuda. GPS: -19.054416315770293,-169.85069274902344");
}

Ahora el resultado tiene mejor pinta, aunque no deja de ser una luz parpadeante, pero iremos mejorándolo.  He subido un vídeo de demostración aplicando una duración para el punto de sólo 100 ms para que vaya más rápido, que bastante larga es ya la trasmisión del mensaje.

El código compilado tiene un tamaño de 5.580 bytes.   Es mucho más complejo de entender, si no tienes conocimientos previos de programación pero trataré de explicarlo con algo de detalle, empezando por el final.  Lo que viene ahora es una sobredosis de información así que si nunca antes has programado se te podría indigestar.   La función setup introduce un retardo de un segundo y medio y establece la patilla 13 como salida (todo esto ya lo habíamos visto).  Después el bucle infinito loop llama a la función frase pasándole una cadena de texto delimitada entre comillas dobles.

La función frase recibe un puntero a una cadena de texto (char*), si nunca has oído hablar de punteros piensa que a esta función se le está diciendo dónde está la cadena de texto.  Recuerda que las funciones pueden recibir parámetros (valores) y devolver también un resultado cuando finalizan (además de todo lo que hagan dentro de su código); pues en este caso no se le está pasando una cadena de texto sino una referencia, una dirección a esa cadena de texto.  Dentro de frase hay dos variables locales que sirven para almacenar valores: indice que es igual a cero y caracter que no está inicializada.  Se les llama locales porque son declaradas dentro de la función a diferencia de la variable led, que vimos en el primer ejemplo y que está declarada fuera de las funciones.  A la variable led se puede acceder desde cualquier parte del programa pero a las locales sólo desde el bloque en el que fueron definidas, por eso verás que he creado otras variables con estos mismos nombres en otras funciones, al ser locales se pueden solapar los nombres sin problemas.  Después hay un bucle do – while, fíjate en la cantidad de llaves que hay en el código, cada par de llaves engloba un bloque de código que es influido por una sentencia, una función, un condición, etcétera.  En este caso, el bloque do-while  lo componen esas dos sentencias y todo lo que hay entre medio y quiere decir algo así como: “haz {todo esto} mientras que se cumpla la condición indicada al final”  En este caso ejecutará todo el bloque mientras que la variable caracter sea diferente de cero (!= significa “es diferente de”).  Bien, primer problema, la variable caracter se ha definido más arriba localmente pero no tiene ningún valor, a diferencia de indice, que sí se le ha asignado el valor cero, ¿eso está bien?  Sí, lo está, pero ten mucho cuidado con estas cosas porque es una fuente de problemas y comportamientos extraños, usar una variable sin haberla inicializado da problemas.  en nuestro caso lo primero que hará la sentencia  do-while es precisamente asignarle un valor a caracter, que será igual a *(cadena + indice);  Fíjate que todas las sentencias acaban en un punto y coma (;) es un requisito del lenguaje.  Bien esto significa que en caracter, que es una variable local de tipo char, le va a meter “el contenido de lo que hay en la dirección apuntada por” (*) cadena más indice.  Y cadena es el nombre del argumento que recibe la función.  Es decir si cadena está apuntando al texto “SOS naufragio barco Arduboat. Todos OK. Enviad ayuda. GPS: -19.054416315770293,-169.85069274902344”, lo que le pasará a caracter es la dirección de la primera letra de la cadena (la primera ‘S’).  Si quisiéramos haber cogido la segunda letra, entonces indice debería valer 1 (el primer valor de una matriz de datos, y una cadena lo es, empieza siempre con el índice cero).  Después hacemos unas comprobaciones, para eso valen las sentencias if-else if else.  Si el contenido de caracter es “mayor o igual que” (>=) la letra A, y además (&&) es “menor o igual que” (<=) la letra ‘Z’, o bien si (fíjate en los paréntesis) el contenido de caracter es “mayor o igual que” (>=) la letra a, y además (&&) es “menor o igual que” (<=) la letra ‘z’, entonces haz (bloque de llaves {}) caracter |= 0x20 (esto es muy rebuscado pero lo uso para convertir cualquier mayúscula a minúscula) y luego haz letra(caracter), es decir, llama a la función letra, pasándole el valor de caracter como argumento.  Seguimos con las comparaciones: o si (else if) el contenido de caracter es “mayor o igual” (>=) que el número 0 y “menor o igual que” (<=)  el número 9 entonces llama a la función numero pasándole el valor de caracter como argumento.  Si el contenido de caracter es igual a un espacio en blanco (ojo porque en las comprobaciones se usa un doble signo de igual ==) entonces haz un retardo (delay) de duracion x 2.  Si no se ha cumplido ninguna de las anteriores condiciones (else) entonces llama a la función simbolo pasándole el valor de caracter como argumento.  Después, independientemente de lo que haya sucedido antes, ejecuta un retardo de duracion x 2 e  incrementa el valor que haya en indice (indice++); después de esto indice valdrá 1, en la siguiente pasada 2, luego 3, etc. apuntando cada vez a una letra diferente de la cadena.  Un detalle importante, cuando hacemos una llamada a otra función (y aquí llamamos a letranumerosimbolo y la función predefinida delay) hacemos que la ejecución se desvíe pero tan pronto finalice el código de la función llamada, la ejecución volverá de nuevo a la función anterior, es decir: al hacer un delay(100)  por ejemplo, lo que hacemos es que la ejecución salte a un bloque de código que hace un retardo de 100 ms, pero cuando finalice devolverá el control a nuestra función original.  Ahora veamos qué sucede dentro de esas tres funciones letranumero y simbolo. La función letra no devuelve nada pero recibe un valor de nombre caracter y de tipo char.  Como ves hemos reutilizado el nombre caracter.  En realidad no se trata de la misma variable que definimos en loop, esto de ahora se llama parámetro y afecta, igual que las variables locales, sólo al bloque de código en el que está declarado, es decir, esta variable caracter sólo se verá desde esta función.  En letra definimos otras tres variables locales valor, indice y matrizLetras.  Al igual que sucede con caracterindice comparte el nombre de una variable ya usada en loop, pero al ser local en realidad no tiene nada que ver con la otra.  bien, matrizLetras tiene una peculiaridad, es una matriz o array (lo indican los corchetes []), esto quiere decir que no almacenará un valor sino un conjunto de valores.  Si te fijas verás ordenados en grupos de cuatro, las definiciones en código Morse de las principales letras del abecedario; se han definido con los valores RAYA, PUNTO y NULO que son nombres de variables globales declaradas e inicializadas al comienzo del programa (éstas sí que afectan a todas las funciones)  Podríamos haber puesto todos los valores PUNTO, RAYA y NULO seguidos, tal y como están separados por comas, pero poniendo cada valor en una línea diferente el código se entiende mejor.   Veamos, la letra ‘A’ se codifica en morse como “.-“, de ahí que hayamos puesto PUNTO, RAYA (los dos valores NULO del final sirven para hacer que cada carácter tenga siempre un mínimo de cuatro símbolos, pues una letra en código Morse tendrá un máximo de cuatro símbolos); así sucede con el resto de letras, cada una tiene su codificación correspondiente.  la siguiente línea de código le resta a la variable caracter el valor de la letra ‘a’ (recordemos que contiene el valor pasado desde loop, y que para esta llamada le pasábamos siempre una letra convertida a minúscula)  Con esta resta convertiremos una letra de la ‘a’ a la ‘z’ en un valor numérico del 0 al 25, es decir en un índice (ya te digo que esto se comprende mejor con un buen bagaje de conocimientos de programación).  Lo siguiente es un bucle ‘for‘; se trata de una sentencia a la que se le pasan cuatro  valores: una asignación que se ejecuta sólo una vez, una condición de control, un incremento y un cuerpo de ejecución.  Primero se realiza la asignación indice = 0 (esta asignación no volverá a producirse durante el bucle for) , después se verifica la condición “siempre y cuando indice sea menor que cuatro” (indice < 4) y se ejecuta el cuerpo de ejecución (las dos líneas de código entre las llaves).  Por último se realiza el incremento de la variable indice (indice++) .  Resumiendo, indice inicialmente vale cero, se comprueba que sea menor que 4 y si es así ejecuta las instrucciones entre llaves para después incrementar indice. El cuerpo del bucle asigna a la variable local valor el contenido que hay en la posición (caracter x 4 ) + indice de la variable  local matrizLetras.  Por último llama a la función emitir pasándole como argumento el contenido de la variable valor.  Dicho así suena más complicado de lo que es, pero básicamente coge el valor de la letra que le hemos pasado, por ejemplo la letra ‘c’, le resta la posición de la primera letra de referencia , la ‘a’, convirtiendo de este modo el carácter ‘c’ en un número 2 (recuerda que la primera letra es la 0, la segunda la 1, etc.)  y después cogerá los cuatro primeros valores de la posición 2 x 4, es decir, (2×4)+ 0, (2×4) +1, (2×4) + 2 y (2×4) + 3 y se los irá enviando a la función emitir, que como habrás supuesto se encarga de emitir el valor a través del LED.

La función numero es idéntica pero cambiando dos cosas, la tabla de valores (aquí se llama matrizNumeros y tiene un tamaño de cinco elementos por cada número) y por supuesto el recorrido del bucle for, que en lugar de cuatro deberá sacar los cinco valores asociados a la entrada seleccionada.

La función simbolo es algo diferente, tiene igualmente una tabla pero en lugar de un bucle for contiene una sentencia switch…case.  Es fácil de entender, viene a ser como una condición if.  Se le pasa al switch un valor y éste lo compara con diferentes posibilidades (case)  En nuestro caso hacemos switch(caracter) y los “cases” son  por ejemplo case ‘.’:  caracter = 0;  break; Es decir, si caracter es igual a ‘.’ entonces asigna a caracter el valor 0 y finaliza las comparaciones (es importante la presencia del break pues de lo contrario seguiría ejecutando el resto del código del switch independientemente de los otros cases)  Después hace como las otras dos funciones, direcciona en la tabla el valor correspondiente, lee sus seis elementos (los símbolos en Morse tienen seis unidades de información) y los envía a la función emitir.

Sólo nos queda ver qué hace la función emitir, que tiene como parámetro de entrada la variable valor (recordemos que también es local)  En su cuerpo simplemente comprueba que valor sea diferente de NULO y si lo es pone en nivel alto la patilla indicada por led, hace un retardo de duracion x valor milisegundos, pone a nivel bajo la patilla led y hace otro retardo, esta vez de duracion milisegundos.   Resumiendo, si recibe NULO no hace nada, si recibe PUNTO enciende el LED durante un pulso y si recibe RAYA lo enciende durante tres pulsos.

Es mucha teoría si no tienes conocimientos previos de programación, pero si has visto algo de C habrás entendido el código sin problemas.  Puedes encontrar infinidad de tutoriales de programación en Internet y toda la documentación del lenguaje, funciones internas, etc, la tienes en la página oficial de Arduino (aquí)

Por supuesto que nuestro transceptor Morse aún no está acabado, le faltan cosas (¡conmigo siempre le faltará algo!)  En próximas entradas te enseñaré cómo adaptar un teclado y un display para que puedas introducir tus propios mensajes sin tener que recompilar el código cada vez.  El mensaje que estamos emitiendo lleva las coordenadas de la isla desierta en la que te encuentras ¿no habrás pensado calcularlas a mano, o mirando las estrellas, verdad…? Además, hay otro friki en la isla de enfrente que se ha montado un aparato como el tuyo y estaría genial decodificar qué está diciendo para poder hablar entre vosotros.  Todo esto y mucho más en próximas entradas.  Vamos a convertir este esperpento en la navaja suiza de la supervivencia.  Una friki-herramienta con la que soñaría el mismísimo Bear Grylls; algo que a MacGyver le haría enrojecer de envidia.  Y lo mejor de todo, aprendiendo a programar con Arduino, verás que es sencillo.

 

Recuerda que nos mudamos. nuestra nueva dirección es:

http://arrizen.com

Anuncios

From → Arduino

Dejar un comentario

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: