Linux Droids Blog

Experiencias y Proyectors robóticos de Sphinx

Comunicaciones USB entre Pinguino y PC 25 noviembre, 2013

Filed under: Pinguino,Programación — Sphinx @ 15:30
Tags: , ,

ProgramaciónLa idea es familiarizarnos con el interfaz USB, las comunicaciones que podemos desarrollar entre un dispositivo y el PC, y poner en práctica lo que sabemos de lenguaje C. Todo ello utilizando la librería libusb (1.0), que es la que nos permitirá programar el PC para habilitar este tipo de comunicaciones entre el PC y Pinguino. De este modo, realizaremos algunos programas simples para comunicarnos con Pinguino, que más adelante nos servirán como base para propósitos más ambiciosos.

Conceptos Generales

Algunos conceptos que tenemos que tener claro en una transferencia USB son por ejemplo:

  • Host: Dispositivo maestro que inicia la comunicación (Generalmente un PC/computador).
  • Puntos terminales (Endpoints): Es una localidad especifica dentro del dispositivo. El Endpoint es un buffer que almacena múltiples bytes, típicamente es un bloque de la memoria de datos o un registro dentro del microcontrolador. Todos lo dispositivos deben soportar el punto terminal 0. Este punto terminal es el que recibe todo el control y la peticiones de estado durante la enumeración cuando el dispositivo esta sobre el bus.
  • Tuberías (Pipes): Es un enlace virtual entre el host (el PC) y el dispositivo USB, este enlace configura los parámetros asociados con el ancho de banda, que tipo de transferencia se va a utilizar (Control, Bulk, Isócrona o Interrupt), dirección del flujo de datos y el máximo y/o mínimo tamaño de los paquetes/buffers. Cada enlace está caracterizado por su banda de paso (Token), su tipo de servicio, el número de punto terminal (End Point) y el tamaño de los paquetes. Estos enlaces se definen y crean durante la inicialización del USB . Siempre existe un enlace virtual 0 que permite tener acceso a la información de configuración del periférico USB (estado, control e información). La norma USB define 2 tipos de enlaces virtuales (pipe):stream y message:
  • Stream Pipes: se trata de un flujo sin formato USB definido, esto significa que se puede enviar cualquier tipo de dato. Este tipo de pipe soporta las transferencias bulk, isocronas, e interrupt. Además tanto el host como el dispositivo USB pueden controlar.
  • Message Pipes: este tipo de enlace virtual si tiene un formato USB definido y solo puede soportar la transferencia Control.

Por dar algún otro detalle de los modos de transferencia que pueden existir entre un host y un dispositivo USB que se han mencionado antes los definimos un poco más aquí:

  • Control: En el tipo control los parámetros se utilizan para permitir el acceso a diferentes partes del dispositivo USB. Se utiliza para configurar el dispositivo, obtener información sobre el dispositivo, el envío de comandos al dispositivo, o recuperar los informes sobre la situación sobre el dispositivo. Los datos enviados son generalmente de pequeño tamaño. Cada dispositivo USB tiene un tipo de transferencia de control denominado “endpoint 0 “que es utilizado por el núcleo para configurar el dispositivo USB cuando es insertado. Estas transferencias están garantizadas a través del protocolo USB que siempre va a disponer de suficiente ancho de banda.
  • Interrupción: Los endpoints de interrupción transfieren pequeñas cantidades de datos a una velocidad estable cada vez que el computador pide datos al dispositivo USB. Este tipo es el el principal método de comunicación para teclados USB y ratones. También son comúnmente usados para enviar datos a los dispositivos USB para controlar el dispositivo, pero no se utiliza generalmente para la transferencia de grandes cantidades de datos. Estas transferencias están garantizadas por el protocolo USB para que siempre tenga suficiente ancho de banda reservado.
  • Masivo (bulk): Este tipo transfiere grandes cantidades de datos. Estos datos son normalmente mucho más numerosos que los transmitidos por la transmisión por interrupción. Este tipo es usado normalmente por los dispositivos que necesitan transferir cualquier cantidad de información sin pérdida de datos. Estos tipos son comunes en las impresoras, almacenamiento y dispositivos de red.
  • Isócrona: modo utilizado para la transmisión de audio o video comprimido. Este tipo de transmisión funciona en tiempo real. Este es el modo de mayor prioridad. La transmisión de la voz es un ejemplo de esta aplicación.

Para más información sobre como trabaja la comunicación USB podeis dirigiros a la página de especificación de USB, o buscar en internet por información sobre las comunicaciones USB. De momento nosotros nos podemos apañar para empezar a trabajar con Pinguino con los datos hasta aquí expuestos.

PCyPinguino_peq

Trabajando con Pinguino

Para trabajar con Pinguino nos centraremos en el modo de transferencia BULK. Necesitamos conocer entonces básicamente qué configuración es la que necesita el dispositivo, qué interface tiene definido, y cuales son los Endpoints que vamos a utilizar para enviarle datos (Endpoint OUT) y para recibir datos de él (Endpoint IN). Para conocer estos datos nos podemos ir al código del bootloader que hayamos cargado en Pinguino. Yo he utilizado la última versión a día de hoy, es decir la 4.13. De modo, que si miramos el fichero picUSB.c, nos encontramos con los siguientes datos:

  • Configuración = 1
  • Interface = 0
  • Endpoint_OUT = 0x01
  • Endpoint_IN = 0x82

Además, para identificar nuestro dispositivo Pinguino de la lista de dispositivos conectados a nuestro Host (PC), necesitamos anotar también estos 2 datos addicionales:

  • Vendor ID (el de microchip): 0x04D8
  • Product ID : 0xFEAA (*)

(*) Este es el Product ID que os podreis encontrar cuando utiliceis un Pinguino 18F2550 o 18F4550. También podeis verlos si una vez cargado nuestro programa de comunicaciones USB en Pinguino haceis:

lsusb

Bien, a partir de aqui, vamos a crear un programa muy simple para que Pinguino haga una tarea muy simple: Recibir lo que se le envíe desde el PC, y a continuación enviarlo de vuelta al PC. Esta es la parte más sencilla, y queda resumida en el siguiente programa. Como podeis ver nos limitamos a utilizar las funciones:

  • BULK.available: que nos permite saber si hay algún dato disponible en el bus.
  • BULK.read: que nos permite guardar el contenido enviado en una cadena.
  • BULK.write: que nos permite enviar una cadena por el bus USB hacia el host.

La tarea que debe realizar Pinguino para el propósito que hemos definido queda descrita en el siguiente diagrama de flujo:

ProgramaBasicoPinguino

Y queda autoexplicado en el siguiente código:

/*-----------------------------------------------------
Author: Jesús Carmona Esteban
Date: 20 de Nov de 2013
Description: Comunicación con el PC utilizando las
funciones BULK.read y BULK.write.
El PIC tiene cargado el bootloader v4.x
-------------------------------------------------------*/
u8 receivedbyte;
char buffer[64];

void setup()
{
    pinMode(USERLED, OUTPUT);
}

void loop()
{
    receivedbyte=0;

    // Esperamos a que existan datos disponibles en USB
    if(BULK.available()) {
        receivedbyte = BULK.read(buffer);
    }

    // C string must be null-terminated
        buffer[receivedbyte] = 0;

    // Si se recibió algo...
    if (receivedbyte > 0) {
    //Cambiamos de estado el LED de usuario,
        toggle(USERLED);
        // ...y reenviamos la cadena de vuelta al host.
        BULK.write(buffer,receivedbyte);
    }
}

Carga este programa en Pinguino desde el IDE, y pasamos a programar el PC para que se comunique con él. Lo primero, debemos instalarnos la librería libusb en caso de que no la tuviesemos. En UBUNTU:

sudo apt-get install libusb-dev

Para trabajar con libusb hace falta que tengamos claros varias cosas:

  • Qué pasos debemos realizar para iniciar la comunicación con nuestro dispositivo.
  • Qué tipo de comunicación vamos a establecer con nuestro dispositivo: asíncrona o síncrona.

Pasos a realizar para iniciar la comunicación

    1. Inicializar la librería.
    2. Listar los dispositivos USB conectados a nuestra máquina, y buscar nuestro dispositivo especifico (Pinguino).
    3. Abrir nuestro dispositivo.
    4. Comprobar que la configuración de Pinguino activa es la correcta, y si no seleccionarla.
    5. Reclamar el interfaz.

¡¡¡Ya estamos listos para comunicarnos con el dispositivo !!!

Qué tipo de comunicación establecer con nuestro dispositivo

La librería nos ofrece 2 formas de hacerlo. El modo síncrono y el modo asíncrono. La elección de dicho modo va a depender más del tipo de aplicación que estemos desarrollando. En la página de la librería se describen estos 2 modos, pero básicamente podemos decir que trabajan así:

  • Modo síncrono: Este modo es el más sencillo. Con una sola llamada hacemos la transferencia de nuestros datos desde nuestra aplicación. Para esta llamada tan solo tenemos que utilizar la función de la librería libusb_bulk_transfer() . El inconveniente de este modo, es que nuestra aplicación se puede quedar dormida dentro de la llamada libusb_bulk_transfer() hasta que la transacción I/O haya sido completada. Esto puede pasar en el caso de que nuestro dispositivo tarde mucho en realizar el trabajo por su parte por cualquier razón.
  • Modo asíncrono: Este modo es más complejo, pero más potente. En lugar de proporcionar funciones que bloquean nuestro programa hasta que la I/O ha sido completada, el modo asíncrono dispone de funciones no bloqueantes las cuales inician una transferencia y retornan inmediatamente. Tu aplicación le pasa un puntero a una función diseñada por ti, para procesar el retorno a esta función no bloqueante, a la cual libusb llamará una vez que la transacción haya sido completada. Sin embargo, esta flexibilidad no está exenta de alguna que otra complicación más al diseñar tu programa. Pero no voy a entrar en esos detalles aquí y ahora, si no que en todo caso los abordaremos en posteriores articulos. De momento, si quieres ampliar los conocimientos puedes consultar la documentación sobre I/O de la librería.

Nosotros en este primer ejemplo empezaremos por lo sencillo. Utilizaremos un modo de comunicación síncrono, y la llamada libusb_bulk_transfer() . Lo haremos basicamente, porque nuestro programa no tendrá otra cosa que hacer que enviar una cadena de texto a Pinguino y esperar a que la devuelva. Y por otro lado Pinguino ya está programado con el programa que hemos expuesto más arriba para responder a esta interacción, es decir, escuchar lo que le vamos a enviar y devolverlo instantaneamente.

De modo que el diagrama de flujo de nuestro programa en C para nuestro PC es bien sencillo:

ProgramaBasicoPC

El diagrama anterior está muy a groso modo. El programa en C permite ver algún que otro detalle adicional. El codigo para el programa en el PC es el siguiente:

// ---------------------------------------------------------------------------------
// Autor: Jesús Carmona Esteban
// Fecha: 20/11/2013
// Versión: 1.0
// Desccripción:
// El siguiente programa muestra como utilizar las funciones de libusb
// para comunicarse con Pinguino desde C.
// Para completar la comunicación es necesario haber cargado en el otro extremo,
// en Pinguino, el programa adjunto para comunicarse con el PC a través de este.
// ---------------------------------------------------------------------------------
#include <stdio>
#include 	<libusb-1.0/libusb.h>
//Compilar con...
//gcc -o usb_conversacion usb_conversacion.c -I/usr/local -L/usr/local -lusb-1.0
#define MICROCHIP_VENDOR_ID 0x04D8
#define PINGUINO_PRODUCT_ID 0xFEAA
#define PINGUINO_CONFIGURATION 1
#define PINGUINO_INTERFACE 0
#define BULK_EP_OUT 0x01
#define BULK_EP_IN 0x81

//Función para comprobar nuestro VID & PID
int is_usbdevblockPinguino( libusb_device *dev ) {
    struct libusb_device_descriptor desc;
    int r = libusb_get_device_descriptor( dev, &desc );
    if( desc.idVendor == MICROCHIP_VENDOR_ID && desc.idProduct == PINGUINO_PRODUCT_ID ){
        return 1;
    }
    return 0;
}

int main( int argc, char **argv) {

    // Definiendo variables y buscando dispositivos USB:
    // -------------------------------------------------
    libusb_device **list;
    libusb_device *found = NULL;
    libusb_context *ctx = NULL;
    int attached = 0;
    libusb_init(&ctx);
    libusb_set_debug(ctx,3);
    ssize_t cnt = libusb_get_device_list(ctx, &list);
    ssize_t i = 0;
    int err = 0;
    if (cnt < 0){
        printf( "Ningun dispositivo usb encontrado.\n" );
        error();
    }
    // Localizando el Pinguino
    // -----------------------
    for(i = 0; i < cnt; i++){
        libusb_device *device = list[i];
        if( is_usbdevblockPinguino(device) ){
            found = device;
            break;
        }
    }

    // Si lo encontramos, lo abrimos:
    if(found){
        printf( "Encontrado usb-dev-block Pinguino !\n" );
        libusb_device_handle *handle;
        err = libusb_open(found, &handle);
        if (err){
            printf("Imposible abrir dispositivo usb Pinguino\n"); error();
        }
        if ( libusb_kernel_driver_active(handle,0) ){
            printf("Dispositivo ocupado...desconectándolo para utilizarlo.\n");
            libusb_detach_kernel_driver(handle,0);
            attached = 1;
        }else printf("Dispositivo ya desvinculado del kernel. Lo podemos utilizar.\n");

        // Comprobamos la configuración activa y
        // ...si no es la que necesitamos, la cambiamos.
        int config;
        err = libusb_get_configuration( handle , &config);

        if (!err){ printf("Configuración activa: %x\n",config);
            if (config != PINGUINO_CONFIGURATION){
                err = libusb_set_configuration( handle , PINGUINO_CONFIGURATION);
                if (err){
                    printf( "Fallo al intentar configurar: " );
                    switch( err ){
                        case LIBUSB_ERROR_NOT_FOUND: printf( "no encontrado\n" ); break;
                        case LIBUSB_ERROR_BUSY: printf( "ocupado\n" ); break;
                        case LIBUSB_ERROR_NO_DEVICE: printf( "no hay dispositivo\n" ); break;
                        default: printf( "otro\n" ); break;
                    }
                    error();
                }
            }
        } else printf("Error intentando obtener la configuración.\n");

        // Reclamamos el interfaz (Claim interface):
        err = libusb_claim_interface( handle, PINGUINO_INTERFACE );
        if (err){ printf( "Fallo al reclamar el interfaz: " );
            switch( err ){
                case LIBUSB_ERROR_NOT_FOUND: printf( "no encontrado\n" ); break;
                case LIBUSB_ERROR_BUSY: printf( "ocupado\n" ); break;
                case LIBUSB_ERROR_NO_DEVICE: printf( "no hay dispositivo\n" ); break;
                default: printf( "otro\n" ); break;
            }
            error();
        }

        // Enviamos un string a Pinguino
        char data[15]="Hola Pinguino!"; //La cadena tiene que terminar en 0x00 (null).
        int actual_length;
        int r = libusb_bulk_transfer(handle, BULK_EP_OUT, data, sizeof(data), &actual_length, 1000);
        if (r == 0 && actual_length == sizeof(data)) {
            printf("Cadena enviada: %s\n",data);
            printf("longitud transmitida=%d.\n",actual_length);
            printf("longitud cadena origen=%d.\n",sizeof(data));
        } else { error(); }

        // Recibimos los datos de vuelta de Pinguino
        char inputPacketBuffer[65];
        printf("Listos para recibir...\n");
        r = libusb_bulk_transfer(handle, BULK_EP_IN, inputPacketBuffer,65, &actual_length, 1000);

        if (r == 0) {
            printf("Lo hemos recibido.\n");
            printf("longitud recibida=%d.\n",actual_length);
            inputPacketBuffer[actual_length]=0; //terminamos la cadena con un null.
            printf("cadena recibida: %s\n",inputPacketBuffer);
        } else { error(); }

        if( attached == 1 ){
            libusb_attach_kernel_driver( handle, 0 );
        }

        libusb_close( handle );
    }

    //Liberamos y cerramos el dispositivo y el contexto:
    libusb_free_device_list(list, 1);
    libusb_exit( ctx );
}

Lo guardamos con un editor de texto. Yo lo llamé usb_conversacion.c . Y lo compilamos con la siguiente llamada:

gcc -o usb_conversacion usb_conversacion.c -I/usr/local -L/usr/local -lusb-1.0

Finalmente lo ejecutamos desde la linea de comandos, teniendo Pinguino ya programado y conectado por USB con lo que hemos descrito anteriormente:

sudo ./usb_conversacion

y el resultado en pantalla deberá ser similar a este:

Captura de pantalla de 2013-11-21 13:06:00

Espero que esto os pueda servir de base para comenzar a hacer programas con Pinguino y el PC comunicandose entre sí.

En posteriores articulos daré más detalles de por qué quiero utilizar esta comunicación.

Anuncios
 

13 Responses to “Comunicaciones USB entre Pinguino y PC”

  1. Hola Sphinx!
    En primer lugar felicitarte por tu blog y los posts relacionados con el proyecto Pinguino, en especial éste que acabo de leer.

    Me pareció más que interesante la rutina en C y su interacción con libusb. Muy bueno!

    Te quería hacer una pregunta al respecto, ya que en la Wiki del proyecto no están documentadas estas funciones: Qué diferencia existe entre las funciones USB.* y BULK.* ? Si utilizo USB.* no estoy haciendo transferencias del tipo Bulk ?

    Desde ya, muchísimas gracias de antemano. Te comento que utilizando de base la rutina .pde y un script en Python y PyUSB puedo ahora controlar la placa Pinguino 2550. Pronto publicaré los sources esperando que le sean de utilidad a alguien más.

    Me gusta

    • Sphinx Says:

      ¡Hola Victor!
      ¡Muchas gracias por tus palabras! Encantado de que lo que hace uno sirva para los demás 😉 Por cierto, estupendo que tú también te animes a hacerlo.

      Interesante tu pregunta al respecto de USB.* y BULK.* No te sabía responder a bote pronto, así que me he ido directamente a mirar el código del proyecto y he visto que las funciones USB.* y BULK.* llaman exactamente a las mismas librerías y funciones. Es decir, BULK.send es exactamente lo mismo que USB.send. Esto lo he encontrado en un fichero donde se mapean las funciones que escribimos en el IDE, con las que están escritas en las librerías de C del proyecto. Este fichero que menciono es h: svn/ide/x.4/p8/pdl/usb.pdl. Ahí podemos ver esto:

      BULK.read BULK_read#include
      BULK.write BULK_write#include
      BULK.printf BULK_printf#include
      BULK.available BULK_available#include
      BULK.gets BULKgets#include
      BULK.puts BULKputs#include

      USB.read BULK_read#include
      USB.write BULK_write#include
      USB.send BULK_write#include
      USB.printf BULK_printf#include
      USB.available BULK_available#include
      USB.gets BULKgets#include
      USB.puts BULKputs#include

      Lo mismo, como ves.
      Así que las puedes usar indistintamente. Como creas que tu código queda más claro.

      Saludos,
      Sphinx.

      PD: Por cierto, lo mismo ya lo habías visto. Pero te dejo este enlace a la wiki del proyecto pinguino que habla de “interfacing Python-Pinguino” : http://wiki.pinguino.cc/index.php/Interfacing_with_Python

      Me gusta

  2. fandres Says:

    Buen día, Gracias por la info me ah sacado de algunas dudas. si enviara desde la PC diferentes caracteres como 1, 2, 3 … y en el pinguino compararlos, como tendría que compararlos? > if (receivedbyte == ’1′) toggle(USERLED); o if (receivedbyte == 49) , eh intentado de diferentes maneras y aun no logro que este compare.
    PDA: en el código del PC ” #include ” le falto ,h no? >> #include

    Me gusta

    • Sphinx Says:

      Hola fandres,
      en mi programa Pinguino que ves en el artículo, la variable receivedbyte no almacena ningún carácter recibido. Lo que almacena es el número total de caracteres recibidos:

      receivedbyte = BULK.read(buffer);

      Ciertamente esto no está documentado en la wiki de pinguino, y voy a intentar solventarlo cuanto antes. Pero, créeme ;), Lo que devuelve la función es el número de caracteres recibidos.

      Entonces, una vez que compruebes que has recibido algo, es decir if (receivedbyte >0)…. utiliza la variable buffer, e indexa el caracter que quieres comparar. Si por ejemplo le estas enviando un solo número(o carácter) como me indicas en tu mensaje (el 1 ó el 2, ó el 3….), entonces haz esto:

      if (buffer[0] == “1”) toggle (USERLED);

      Efectivamente, si en lugar de poner el “1” entre comillas, le indicas el número del código ASCII, la comparación también es válida.

      Espero que te sirva.
      Un saludo,
      Sphinx.

      Me gusta

  3. Igorer88 Says:

    Buenas! Que bueno que lei los comentarios, las librerias USB y BULK son las mismas, gracias por responder eso! Ahora…. Sabrás la diferencia entre las librerias CDC y Serial? y entre USB (BULK) y CDC?

    Me gusta

  4. igorer88 Says:

    Hola! Me aclaraste la duda con la respuesta al primer comentario que tambien la tenia. Ahora… Sabrás la diferencia entre USB (BULK) y CDC? y la diferencia entre CDC y Serial? Gracias de antemano!

    Me gusta

  5. […] Si queremos que Pinguino, sea placa subordinada, no la principal en nuestro diseño (robot, proyecto, lo que sea…). Es decir que sólo se comporte como un servo controller, tendremos que crear un modo de comunicarnos con ella para pasarle los datos que Pinguino debe aplicar a los servos. Para esto, necesitamos crear un mini protocolo de comunicaciones para que un “host” se entienda con Pinguino4Robots. Esto lo abordaré en otra entrada de blog, posteriormente. La idea es utilizar el propio puerto USB de Pinguino4Robots como puerto de comunicaciones y utilizar lo ya desarrollado en mi anterior entrada comunicaciones USB entre Pinguino y PC. […]

    Me gusta

  6. Hola, como estás te felicito por tu blog. Te escribo porque no estoy pudiendo compilar el programa en C desde una netbook con Huayra 3.0 (Debian Jessie), salta este error:
    usb_conversacion.c:11:17: fatal error: stdio: No existe el fichero o el directorio
    #include
    ^
    compilation terminated.

    ¿Alguna sugerencia?. Gracias, disculpe. Felicitaciones otra vez. Salud!

    Me gusta

    • Sphinx Says:

      Hola Mateo, gracias por tus palabras.
      No he trabajado con esa distro de Linux. Vengo haciendolo habitualmente con Ubuntu. Pero creo que para este caso es lo mismo. No estoy al 100% seguro, pero sospecho que te faltan por instalar los paquetes básicos de programación.
      Si este es el caso, te paso este link. build-essential es un conjunto de librerías y ejecutables que te permitirán programar y compilar en C, C++ . Mira aquí: https://packages.debian.org/es/sid/build-essential
      Dime si se resolvió.
      Saludos!

      Me gusta

      • Angel Says:

        Buenas tardes, amigo he intentado lo siguiente;

        en vez de esto

        // Si se recibió algo…
        if (receivedbyte > 0) {
        //Cambiamos de estado el LED de usuario,
        toggle(USERLED);

        Realice esto

        if (buffer[0] == “1”)
        digitalWrite(7, HIGH);

        y tambien

        if (buffer == “1”)
        digitalWrite(7, HIGH);

        y de ninguna de las dos formas me funciona, lo que quiero es activar un led una vez tecleado una tecla correspondiente.. osea a pulsar el numero uno (1) este verifique si es pulsado y active el led respectivo, que estaría conectado a una pin del pinguino.

        agradesco tu ayuda.

        Me gusta

      • Sphinx Says:

        Hola Angel,

        Lo primero una pregunta:
        Imagino que en tu programa conservas la última linea del código que he puesto en el artículo, de modo que lo que le envías a Pinguino te lo devuelve al ordenador y lo imprimes por pantalla, ¿verdad? Mi pregunta es: ¿Qué es lo que te devuelve Pinguino?

        Ahora un par de ideas que se me ocurren:

        1) utiliza comillas simples en la linea
        Buffer[0] == ‘1’
        Yo puse comillas dobles, pero no sé si eso será un problema. Puedes intentar ese cambio.

        2) Puedes hacer un bucle con ‘for’ y recorrer todo el buffer para encontrar un solo carácter que sea ‘1’. A lo mejor no cae en la posición 0 de buffer, si no en la 1, 2, 3…. De todos modos esto podrás verlo en lo que se imprime de vuelta por la pantalla de tu ordenador.

        3) Puedes intentar utilizar el carácter ASCII del número ‘1’ que es 31 si no me equivoco. Prueba
        buffer[0]==0x31
        Creo que no me equivoco en la notación.

        Prueba estas ideas.

        De todos modos, volveré a meter el programa en Pinguino, y publicaré los resultados en este hilo de conversación.

        Saludos,
        Sphinx

        Me gusta


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