jueves, 26 de abril de 2012

Lecturas con la brújula I2C


Hoy os damos la noticia de que ya tenemos implementada y funcionando nuestra nueva brújula I2C CMPS03 en el robot Kigo.

Para ello, por un lado hemos tenido que realizar una serie de conexiones hardware entre la brújula y uno de los puertos de entrada del bloque NXT, tal y como podemos ver en la siguiente tabla:



 
Para realizar todas estas conexiones, hemos diseñado una placa impresa auxiliar y hemos utilizado dos conectores RJ11. Se puede encontrar el esquema eléctrico del circuito auxiliar en este enlace.

Por otro lado, hemos tenido que ampliar el software del robot con una nueva clase que se encarga de leer los registros de la brújula y convertir los valores leídos a grados sexagesimales: CompassCMPSensor.java.


La nueva estructura de clases del software del robot es:



La clase CompassCMPSensor.java consta de tres métodos:
  • public int getData(int register, byte[] buf, int len): Le pasamos como parámetro el número de registro del sensor I2C que queremos leer, un array de bytes donde queremos que se almacene la lectura y la longitud del entero que devolverá el método. Como se puede intuir al ver los parámetros de entrada, el método simplemente lee a través del bus I2C el valor del registro que se le ha indicado y guarda el byte que ha leído en un determinado array. Nos devolverá 0 si la lectura se ha realizado de forma correcta y un valor negativo en caso de error.
  • public int getSimpleDegrees(): Como ya explicamos al hablar de los distintos registros internos de la brújula CMPS03, ésta nos puede dar la posición respecto al polo Norte magnético con una resolución de grados o de décimas de grados. En el primer caso, la posición es un valor de 1 byte sin signo (registro 1), es decir, entre 0 y 255. Este valor representa un ángulo entre 0 y 360º. El método getSimpleDegrees() realiza la lectura del registro 1 de la brújula. Como Java trabaja con bytes con signo en complemento a 2, interpretará como negativos a todos los valores superiores a 128; por lo tanto, tenemos que hacer una conversión previa de byte con signo a número entero para volver a la escala de 0 a 255. Posteriormente, hacemos una segunda conversión de estos valores a grados sexagesimales, para tener una escala final de 0 a 360º:




  • public int getComplexDegreees(). Este método funciona de manera similar al que acabamos de explicar, pero con él obtenemos una resolución de décimas de grado. Lee los registros 2 y 3 de la brújula, que representan con 2 bytes valores entre 0 y 3599 (el del registro 2 es el más significativo). De nuevo hay que tener en cuenta que Java trabaja con bytes con signo en complemento a 2 y realizaremos la conversión previa a números enteros. En este caso, para obtener el valor en grados sexagesimales, simplemente tendremos que dividir entre 10 la lectura de los registros:



Uno de los problemas que nos hemos encontrado a la hora de conectar la brújula al robot es que, si la colocamos muy cerca del bloque NXT, se producen interferencias de las comunicaciones Bluetooth con la lectura de los sensores magnéticos. Por este motivo, hemos creado una estructura que permite a la brújula estar ligeramente alejada del bloque NXT para no verse afectada por sus campos electromagnéticos. 


Para completar la integración de la brújula en nuestro sistema, incorporaremos en la monitorización de sensores de la aplicación Android la lectura en tiempo real de la posición del robot respecto al polo Norte magnético.

Por último, modificaremos el algoritmo de detección de contenedores del robot (implementado en el behavior “Mapping()”). Hasta ahora medíamos el ángulo al que se encontraba un contenedor mediante el tacómetro de los motores. El problema es que, si la superficie donde trabajamos no es perfecta y las ruedas patinan, esta medida no es nada fiable, tal y como se podía ver en el vídeo del primer hito. Por este motivo, a partir de ahora mediremos este ángulo mediante la brújula. Próximamente iremos informando de las mejoras que consigamos.



domingo, 22 de abril de 2012

Hito 2: Vídeo de la aplicación Android

Hoy os presentamos el vídeo tanto con la explicación como con la demostración de cómo funciona nuestra aplicación para dispositivos Android y su interacción con el robot Kigo.


Aunque la parte de reconocimiento de gestos posiblemente sufra algunas modificaciones, en el vídeo sí que se puede ver en qué se basa su funcionamiento a día de hoy.


jueves, 19 de abril de 2012

El acelerómetro de un dispositivo Android

El próximo paso que vamos a dar en nuestro proyecto es la implementación del reconocimiento de gestos mediante el acelerómetro de un dispositivo Android.

Un acelerómetro es un sensor que mide la aceleración relativa, tomando como referencia la de caída libre (es decir, en la Tierra la referencia será g = 9,81 m/s^2). Por lo tanto, la medida que nos dará un acelerómetro será:

Esto quiere decir que, si el móvil se encuentra en reposo sobre la mesa, la medida del acelerómetro será A= 9,81 m/s^2. Sin embargo, si sufre una caída libre, obtendremos como medida A=0.


En los dispositivos Android, el sistema de coordenadas es relativo a la pantalla:

  • Eje x: Es horizontal y apunta a la derecha.
  • Eje y: Es vertical y apunta hacia arriba.
  • Eje z: Es la normal saliente a la pantalla del dispositivo.

Las librerías de Android nos proporcionan varias clases e interfaces que nos permiten trabajar con sus sensores (entre ellos el acelerómetro) y que nosotros usaremos:


Clase Sensor

Como su nombre indica, esta clase representa un sensor.

A través de distintas constantes de tipo int determinamos con qué sensor estamos trabajando: de temperatura, de presión, de humedad relativa, de luz, etc. El acelerómetro lleva asociada la constante TYPE_ACCELEROMETER, que toma el valor 1.

Los métodos que incluye nos permiten conocer el rango máximo del sensor, su resolución, la versión del módulo o la potencia que consume.


Clase SensorEvent

Esta clase representa un evento relacionado con un sensor y contiene información sobre él: de qué tipo es el sensor que ha generado el evento, su precisión, el instante temporal en que se ha producido el evento (en nanosegundos) y un array de valores que depende del sensor que se esté monitorizando.

En el caso del acelerómetro, el array de valores está formado por los siguientes floats:

  • values[0]: aceleración en el eje x
  • values[1]: aceleración en el eje y
  • values[2]: aceleración en el eje z
Todos estos valores son relativos a la aceleración de la gravedad.


Interfaz SensorEventListener

Al implementar esta interfaz, hay que definir dos métodos:

  • public abstract void onSensorChanged(SensorEvent event): se llama a este método cuando los valores de un sensor han cambiado. Por ejemplo, podemos usar este método para almacenar los nuevos valores que ha leído el sensor.

  • public abstract void onAccuracyChanged(Sensor sensor, int accuracy): es llamado si la precisión de un sensor cambia.

Clase SensorManager

Esta clase nos permite acceder a los sensores de los dispositivos. De todos los métodos que presenta, los dos más importantes son:

  • public boolean registerListener (SensorEventListener listener, Sensor sensor, int rate): Asocia un SensorEventListener a un determinado sensor, de forma que podemos detectar si los valores del sensor cambian y leer los nuevos valores. Tenemos que especificar una velocidad a la que los eventos son detectados (por ejemplo, cada cuánto tiempo queremos comprobar las coordenadas de la aceleración del acelerómetro si se está produciendo movimiento). El valor de este parámetro de entrada puede ser: SENSOR_DELAY_NORMAL (recomendado para cambios en la orientación de la pantalla), SENSOR_DELAY_UI (recomendado para la interfaz de usuario), SENSOR_DELAY_GAME (recomendado para juegos), o SENSOR_DELAY_FASTEST (se obtienen las lecturas lo más rápido posible) o el retardo deseado entre eventos en microsegundos. El método devuelve true si el sensor está habilitado.

  • public void unregisterListener (SensorEventListener listener): Elimina la asociación entre el listener y los sensores.

Con todas estas herramientas disponibles, podremos asociar gestos realizados con el móvil con unas determinadas coordenadas de la aceleración sufrida por el acelerómetro durante el gesto. Así almacenaremos en memoria una serie de gestos patrón que irán asociados a comandos del robot (avanzar, retroceder, rotar, parar…). Al realizar un nuevo gesto, compararemos sus coordenadas con las de todos los patrones y calcularemos cuál de ellos se encuentra a la menor distancia. De esta forma, decidiremos cuál es el comando que hay que enviar por Bluetooth al robot Kigo.

martes, 17 de abril de 2012

Cambios en la aplicación Android

Tras realizar pruebas con la aplicación para Android y el robot Kigo, hemos introducido algunos cambios en la última versión que presentamos de la interfaz gráfica.

En primer lugar, nos dimos cuenta de que al utilizar el control remoto y deslizar el dedo por la pantalla de izquierda a derecha o viceversa, el móvil podía interpretar que queríamos cambiar de página. Para evitar esta discrepancia, hemos añadido un checkbox “Habilitar”, que tendremos que marcar antes de empezar con el control remoto. De esta manera, nos mantendremos en la página de “control remoto” aunque deslicemos el dedo a un lado de la pantalla.

Otro cambio que hemos hecho es la forma de indicar la lectura del sensor de color en la interfaz. Inicialmente, el círculo del color leído aparecía con una sombra alrededor, pero en ocasiones no era apenas perceptible. Por este motivo, hemos dejado los colores no seleccionados con un tono de menor saturación. Así se distingue claramente qué color está leyendo el sensor, pues hay un mayor contraste entre los círculos.

Además, hemos conseguido que en las pestañas de selección de página aparezca una barra azul debajo del título de la página en la que nos encontramos.

Un cambio que no es visible para el usuario pero modifica la estructura del software de la aplicación es que hemos eliminado la actividad “ControlActivity.java”, pues hemos incluido todas sus funciones en la actividad principal “NXJAndroid.java” para tener un código más compacto.

Todas estas modificaciones se podrán ver ya en el vídeo de demostración que aquí publicaremos próximamente.

domingo, 15 de abril de 2012

Interfaz de la aplicación Android

En la última semana nos hemos dedicado a mejorar tanto el aspecto como las funcionalidades que ofrece la aplicación al usuario. En esta entrada presentamos el aspecto que tiene la interfaz gráfica a día de hoy.

Como ya comentamos en la entrada anterior, la aplicación está formada por tres páginas y se puede pasar de una a otra mediante unas pestañas que hay en la parte superior o simplemente deslizando el dedo hacia un lado de la pantalla, gracias a la clase MyAdapter.java (que implementa la interfaz TitleProvider y extiende la clase PagerAdapter).

A continuación mostramos cada una de estas tres páginas:


COMANDOS

En ella aparecen una serie de botones que nos permiten enviar comandos al robot.

Inicialmente, el único que está habilitado (en color azul celeste) es el de “Conectar”, para establecer la conexión Bluetooth entre el dispositivo Android y el robot Lego Mindstorms.

El resto de botones se habilitan una vez que la conexión ya ha sido establecida:

  • Avanzar: hace que el robot avance o retroceda a la velocidad establecida en el cuadro de texto adyacente (debe ser un valor entre -128 y 127, pues se representa en 8 bits con signo en complemento a 2).
  • Rotar: el robot gira el número de grados indicados en el cuadro de texto.
  • Mapear: es el botón que inicia el modo autónomo de detección de contenedores. Al accionar este botón, el robot empieza a buscar los contenedores que hay a su alrededor.
  • Procesar: inicia el modo autónomo de clasificador de residuos (anteriormente el robot habrá tenido que detectar contenedores para que se realice la clasificación de manera correcta).
  • Disparar: lanza una pelota del cargador del robot.
  • Parar: detiene el movimiento del robot.



SENSORES

Esta es la página en la que se puede llevar a cabo la monitorización de los sensores y otra información acerca del estado del robot.

En primer lugar, encontramos unos círculos de colores que nos indican de qué color son la siguiente pelota que se va a disparar y el contenedor que está detectando el robot (sólo en caso de que tenga un contenedor delante). Aunque en la captura de pantalla que aquí tenemos no se puede apreciar, pues no marca ninguna lectura de los sensores, cuando el robot está en funcionamiento el círculo del color indicado por el sensor de color aparece resaltado.

A continuación encontramos el valor de la distancia (entre 0 y 255) a la que el sensor de ultrasonidos detecta que hay un objeto (sólo en caso de que lo haya; si no hay ningún objeto cerca, marcará 255). La barra también indica de forma cualitativa cuál es esta distancia, estando vacía para una distancia de 0 y completa para 255.

De la misma manera, se presenta el nivel de batería que le queda al robot en ese momento, tanto de forma cualitativa en una barra como en porcentaje exacto.

Por último, podemos ver cuántas pelotas le quedan al robot en el cargador.


CONTROL

En esta página se puede colar de forma remota el movimiento del robot. Deslizando el dedo por la imagen “joystick”, se puede ordenar al robot avanzar (si se desliza por el primer y el segundo cuadrante de la circunferencia) o retroceder (por el tercer o cuarto cuadrante). Cuanto mayor sea la distancia del punto donde se apoya el dedo al centro del círculo, con mayor velocidad se moverá el robot. Además, girará con un ángulo similar al establecido al deslizar el dedo.

También tenemos un botón para disparar, que nos permite hacer una clasificación de residuos de forma manual.

Por último, encontramos el botón de parada, para finalizar el control remoto del robot y dejarlo en reposo.



En los próximos días publicaremos un vídeo de demostración de cómo interactúa un usuario con la aplicación y el robot Kigo.

Software de la aplicación Android

Una vez conocidos los fundamentos básicos de la programación para Android, en esta entrada explicaremos qué clases Java encontramos detrás de la aplicación y cuál es la función de cada una de ellas, así como los aspectos de mayor interés.

Todavía no mostraremos el aspecto de la aplicación en el blog, pues hemos partido de una interfaz con botones muy sencilla que sólo utilizamos para hacer pruebas del software del robot y una primera monitorización básica de los sensores. Ya estamos trabajando en la mejora del aspecto de la interfaz y próximamente la presentaremos.

En el siguiente diagrama tenemos las cuatro clases de la aplicación Android:


MyAdapter.java

Nuestra aplicación estará formada por al menos 3 pantallas distintas: la relacionada con la monitorización de sensores, la de comandos que se pueden enviar al robot y la del control remoto. Esta clase es el Adapter que vamos a utilizar para acceder a nuestra colección de Views, es decir, nos permite hacer el cambio entre una pantalla y otra.

La clase MyAdapter.java implementa la interfaz TitleProvider y extiende la clase PagerAdapter. De esta manera, el cambio entre las pantallas se realizará de forma muy sencilla: a través de unas pestañas en la parte superior de la interfaz (tal y como se puede ver en la imagen) o simplemente deslizando el dedo hacia un lado de la pantalla.

El constructor de la clase toma como argumentos el contexto y un ArrayList de Linear Layouts (los títulos de las distintas páginas o pantallas que se pueden mostrar).

Los métodos más importantes que se implementan de PagerAdapter y TitleProvider en esta clase son:

  • getTitle(): es el único método de TitleProvider, al que se le pasa como parámetro un entero que indica la posición de una página y devuelve el título de esa página.
  • getCount(): devuelve el número de páginas que hay.
  • instantiateItem(): crea las Views de la página correspondiente a una determinada posición
  • destroyItem(): destruye Views que han sido creadas anteriormente.


Robot.java

Esta clase es el hilo que se encarga de leer el stream de datos de entrada, para poder llevar a cabo la monitorización de los sensores.

Mientras el dispositivo móvil está conectado por Bluetooth al robot, recibe de él 3 bytes: el primero de ellos es la distancia a la que el sensor de ultrasonidos detecta un objeto, el segundo es el color de la siguiente pelota a disparar y el tercero el color del contenedor que tiene delante (en caso de que lo haya).

El método run() del hilo lee estos 3 bytes y los almacena en las variables “distance”, “ballColor” y “containerColor”. Posteriormente llama a distintos métodos de la actividad principal (pasando estas variables como parámetro) para que se muestre la información al usuario en la pantalla del dispositivo Android.



NXJAndroidActivity.java

Esta es la actividad principal de nuestro programa Android, encargada de mostrar la interfaz de usuario y saber cómo actuar frente a los eventos generados por el usuario.

Las variables que encontramos dentro de esta Activity son:
  • Todos los botones que aparecen en la interfaz: connectBtn, avanzarBtn, rotarBtn, parartBtn, disparartBtn, mapearBtn, procesarBtn.
  • Un array de botones “buttons”, en el que se almacenarán todos los correspondientes a los comandos.
  • Un ArrayList de Linear Layoutslayouts”.
  • Una barra de progreso “pg” para mostrar la medida del sensor de distancia.
  • Un TitlePageIndicator titleIndicator”, que permite al Adapter saber qué página mostrar.

Los métodos más importantes dentro de esta actividad son los siguientes:
  • public void onCreate(Bundle savedInstanceState): Llama al método setContentView() para dibujar por pantalla, configurando la aplicación para que se muestre en la pantalla entera (mediante la constante FLAG_FULLSCREEN de LayoutParams) y sin título en la parte superior (requestWindowFeature(Window.FEATURE_NO_TITLE)). Inicializa el ArrayList de LinearLayouts, con los layouts correspondientes a la página de comandos, de sensores y de control remoto. Crea un nuevo objeto de la clase MyAdapter para que se encargue de crear las Views correspondientes.

  • private void initComponents(): Inicializa todos los botones de la aplicación y determina cómo deben responder los Listeners cuando se hace click sobre cada botón. En el caso de los botones asociados a comandos, se mandará por el stream de datos de salida el byte con los 3 bits de comandos que el robot debe interpretar.

  • public void setBallColor(int color) y public void setContainerColor(int color): Ambos métodos hacen que se activen los indicadores que muestran de qué color es la pelota o el contenedor respectivamente que los sensores de color están detectando. Estos métodos son llamados desde el hilo Robot.java, que pasa como parámetro un entero asociado al color correspondiente (0 para el rojo, 1 para el verde, 3 para el amarillo y 4 para el azul).

  • public void setDistanceSensor(int distance): Hace que la barra de progreso muestre la lectura del sensor de ultrasonidos, dando un valor entre 0 y 255. También se muestra como texto por la pantalla. Como ocurría con los dos métodos anteriores, el hilo Robot.java llama a este método pasando como parámetro el entero correspondiente a la distancia.
Como podemos ver, tanto la página de “comandos” como la de “sensores” utilizan esta actividad, pero no la de “control” (remoto).



ControlActivity.java

Esta es la actividad correspondiente al control remoto del robot (asociada a la página de “control”). En ella podemos encontrar 3 métodos:
  • public void onCreate(Bundle savedInstanceState): llama al método setContentView() para que se muestre por pantalla el layout de “control”. Crea un objeto del tipo DisplayMetrics, que nos permitirá obtener información sobre el tamaño y otras propiedades de la pantalla. Además, invoca a los métodos initComponents() y addListener() de esta actividad.

  • private void initComponents(): Inicializa los botones de “disparar” y “parar”, así como la imagen sobre la que se podrá deslizar el dedo para indicar la dirección en que se debe mover el robot (denominada “joystick” a partir de ahora).

  • private void addListener(): Este método determina cómo deben responder los Listeners a las interacciones del usuario. Al accionar el botón de disparar, se envía al robot el comando correspondiente (los bits de comando han de ser 011 para dar el control al behavior Shoot(), por lo que el byte enviado es 0x60). Al pulsar el botón de parar, se finaliza el control remoto poniendo a 0 la velocidad de ambos motores (Byte de comando = 0x20 para que se accione el behavior MotorSpeed() y bytes de velocidad 0x00 para parar los dos motores). Al tocar la imagen “joystick”, se calcula la velocidad y la dirección a la que se debe mover el robot y se le envía también por el stream de salida (el primer byte indicará el comando, el segundo la velocidad del motor derecho y el tercero la del motor izquierdo).
En esta actividad, la cuestión más interesante es el cálculo que se realiza dentro del método addListener() para determinar a qué velocidad y en qué sentido deben girar los motores del robot en función de dónde apoya su dedo el usuario en la imagen “joystick”:
  • Sacamos las coordenadas del centro de la imagen cenX y cenY, teniendo en cuenta que el origen del sistema de referencia se encuentra en la esquina superior izquierda de la pantalla del móvil.
  • Obtenemos también las coordenadas x e y del punto donde se ha producido el evento de tocar la pantalla.
  • Establecemos como el origen de coordenadas de nuestro nuevo sistema de referencia el centro de la imagen. Las nuevas coordenadas del punto donde se había producido el evento serán longX= x – cenX y longY= cenY – y.
  • Expresamos este punto en coordenadas polares, obteniendo el módulo dist (proporcional al valor absoluto de la velocidad) y el ángulo arg (indica la dirección en la que se debe mover el robot; el robot avanzará si pertenece al primer y el segundo cuadrante y en otro caso retrocederá).
  • Cuando estemos en el primer y el cuarto cuadrante, el motor A (derecho) debe rotar más despacio que el C (izquierdo), para poder girar hacia la derecha. Por este motivo, la velocidad del motor A irá multiplicada por el seno del ángulo ang.
  • En cambio, en el segundo y el tercer cuadrante es el motor C el que debe girar más despacio para girar hacia la izquierda. En esta ocasión, la velocidad del motor C se multiplica por el seno del ángulo ang.

Aunque posteriormente iremos añadiendo alguna clase más en la aplicación Android para el reconocimiento de gestos, estas 4 son las clases fundamentales de la misma.

sábado, 14 de abril de 2012

Conceptos básicos de Android


Para posibilitar el control del robot de manera inalámbrica, estamos desarrollando una aplicación para dispositivos Android.

Antes de pasar a explicar con detalle el software que hay detrás de la aplicación, nos gustaría presentar algunos conceptos básicos de programación Android para que posteriormente se pueda comprender mejor el funcionamiento de las clases de nuestra aplicación.

ACTIVIDADES

Activity es la clase principal de Android, encargada de mostrar la interfaz de usuario y permitir la interacción, pues responde a los eventos generados por los usuarios (al pulsar un botón, por ejemplo).

Cuando la actividad se inicia, se invoca el método onCreate(), al que se le pasa como parámetro un objeto de la clase Bundle (contiene el estado anterior de la actividad en caso de que haya sido suspendida). Dentro de él, se llama al método encargado de dibujar por pantalla: el Activity.setContentView(), al que se le pasa como parámetro un objeto View (del que hablaremos posteriormente).

Las aplicaciones normalmente tienen fijada una actividad como punto de entrada y, después, pueden tener una actividad distinta para cada pantalla que muestran. Según se van ejecutando, las actividades se van almacenando en una pila y se extraen de ella cuando las finalizamos con el método finish() o se pulsa la tecla de “atrás” en el dispositivo Android.

ANDROID MANIFEST

Todos los proyectos Android tienen un fichero AndroidManifest.xml, en el que se detallan las características principales que el sistema debe conocer antes de ejecutar la aplicación.

Algunas de las funciones que tiene este fichero son:
  • Nombrar el paquete Java para la aplicación
  • Describir los componentes de la aplicación, como las Actividades
  • Declarar los permisos que otros han de tener para interactuar con la aplicación
  • Listar librerías que utilizará la aplicación

VIEWS

Todos los componentes de una interfaz de usuario de Android descienden de la clase View: una estructura de datos cuyas propiedades contienen los datos de la capa y la información específica del área de la pantalla. Los objetos de esta clase se agrupan formando un árbol con el que se pueden definir interfaces muy complejas.

Como ya hemos visto, al inicializarse una Activity se llama al método Activity.setContentView(), que se encarga de recorrer el árbol partiendo del objeto View raíz, descendiendo por sus nodos para presentar toda la interfaz. Para dibujar cada elemento de una vista, se invoca al método draw(). Éste llama primero al método measure(), que define el tamaño de cada objeto View, y luego al método layout(), que posiciona el objeto dentro de la vista.

Para que Android sepa dibujar correctamente los objetos, tenemos que pasarle algunos datos de la clase LayoutParams, que puede tomar los siguientes valores:
  • Un número
  • La constante FILL_PARENT, que indica que la vista debe intentar ser tan grande como su padre, quitando el padding.
  • La constante WRAP_CONTENT, para que intente ser lo suficientemente grande para mostrar su contenido, más el padding.

CONTEXT

Context es una interfaz para la información global de la aplicación. Gracias a este contexto, un objeto que acaba de ser creado puede saber qué ha pasado en la aplicación hasta ese momento. Además, nos permite acceder a recursos como el lanzamiento de actividades.

Se puede acceder al contexto de varias formas: en general, llamando a los métodos getContext() o getApplicationContext(); si estamos dentro de una Activity, como implementan esta interfaz, simplemente tenemos que hacer referencia a ella misma con (this) o NombreActivity.this.

LAYOUT

Un Layout nos permite posicionar cada objeto gráfico en un lugar de la pantalla determinado, obteniendo así el diseño de la estructura deseada para nuestra interfaz.

Los Layouts son de tipo ViewGroup, una subclase de View cuya función es contener y controlar una lista de Views.

Existen muchos tipos de Layouts para Android: Frame Layout, Linear Layout, Relative Layout, Table Layout, Tab LayoutNosotros utilizaremos el Linear Layout, que va colocando a sus hijos unos detrás de otros, comenzando por la esquina superior izquierda de la pantalla.



LISTENER

Para conseguir la interacción del usuario y la aplicación, es necesario responder a los eventos causados por el usuario. Esta función la cumplen los Listeners, que son llamados cada vez que se produce un evento.

Algunos de los más comunes son:
  • setOnClickListener(): responde cada vez que se pulsa sobre la vista a la que lo aplicamos.
  • setOnKeyListener(): para eventos de teclado.
  • setOnItemClickListener(): responde al seleccionar un elemento de una lista.

ADAPTER

Mediante el uso de Adapters definimos una forma común de acceder a colecciones de datos: es el responsable de crear la vista que será mostrada para cada elemento en la colección de datos.

Por ejemplo, si cada elemento de una lista estuviera formado a su vez por una imagen y varias etiquetas, el responsable de generar y establecer el contenido de todos estos "sub-elementos" a partir de los datos del propio adaptador.


Esperamos que, con estas nociones básicas, cualquier lector de nuestro blog pueda entender la estructura del software de nuestra aplicación Android. En la próxima entrada comenzaremos a hablar sobre ella.

miércoles, 11 de abril de 2012

Integración de Brújula I2C

Durante la Semana Santa hemos estado evaluando la integración de un nuevo sensor I2C como complemento al hardware de nuestro sistema, para mejorar sus funcionalidades y poder tener una mayor precisión a la hora de clasificar residuos de forma autónoma.

Finalmente hemos decidido implementar una brújula a través de la interfaz I2C. El sensor elegido es la brújula digital CMPS03, en la que la posición respecto al polo Norte magnético es traducida por un módulo de sensores magnéticos a una información de ángulo en grados (con una precisión es de 3-4 grados y una resolución de décimas de grado).

Este sensor está específicamente diseñado como sistema de navegación para robots. Utiliza dos sensores magnéticos KMZ51 de Phillips, sensibles al campo magnético de la Tierra, colocados en ángulo de 90 grados.

La lectura del sensor se puede realizar a través de dos interfaces distintas: el PWM (Pulse Width Modulation) y el bus I2C, que facilita la comunicación con una amplia gama de microcontroladores. Como ya hemos comentado, nosotros lo haremos mediante el bus I2C.


CONEXIONADO

De los 9 pines que encontramos en el CMPS03, los que nosotros utilizaremos son:

  • Pin 1 (+5 V): entrada de alimentación
  • Pin 2 (SCL): señal de reloj para la comunicación síncrona I2C
  • Pin 3 (SDA): señal de datos de la comunicación I2C
  • Pin 6 (Calibración): para calibrar el sensor hay que conectar este pin a masa momentáneamente apuntando a cada uno de los cuatro puntos cardinales
  • Pin 7 (50/60 Hz): Mediante esta entrada se activa un filtro intern
    o que permite anular los campos magnéticos generados por la red eléctrica, que pueden provocar una desviación de unos 1,5º. Si se sincroniza con la frecuencia de la red, se puede reducir esta desviación a 0,2º. Se puede seleccionar entre un filtro a 50 Hz (nivel bajo) o a 60 Hz (nivel alto)
  • Pin 9 (GND)

En cuanto al resto de pines, mientras que el 5 y el 8 no se conectan, el 4 se usa únicamente como salida PWM.

Las señales SCL y SDA irán conectadas a los pines DIGIA0 y DIGIA1 de algún puerto de entrada. Es importante recordar que ambas líneas deben estar polarizadas a nivel alto en reposo; para ello, es necesario usar resistencias de pull-up. En las especificaciones del hardware de Lego se recomienda usar resistencias de unos 82 KΩ.


COMUNICACIÓN I2C

El interfaz I2C permite conectar este módulo a un bus I2C como dispositivo esclavo, para ser gobernado por un microcontrolador que actúe como maestro. El protocolo que se sigue es el siguiente:

  1. Tras el bit de inicio, el maestro manda la dirección del módulo en modo escritura. Esta tiene como valor fijo 0xC0 (en modo lectura sería 0xC1).
  2. Se envía el número de registro al que se quiere acceder.
  3. De nuevo se genera un bit de inicio y esta vez el maestro manda la dirección del módulo en modo lectura (0xC1).
  4. El módulo CMPS03 empieza a transmitir de forma secuencial el contenido de los registros que se han pedido (en los registros de 2 bytes, se transmite primero el de mayor peso).
  5. La comunicación acaba cuando el maestro genera el bit de parada.
Aunque el módulo dispone de 16 registros internos, algunos de ellos no se utilizan. A continuación detallamos las funciones de todos ellos:
  • 0: Se lee el número de la revisión del firmware interno del módulo.
  • 1: La posición actual de la brújula se lee en un único byte (un valor entre 0 y 255) en el que se representa un rango de 0 a 360º.
  • 2 y 3: La posición actual de la brújula se lee en dos bytes (16 bits, estando los más significativos en el registro 2) que pueden tomar valores de 0 a 3599 (se representa de 0 a 359,9º).
  • 4 y 5: Test interno (valor de 16 bits que refleja el estado del sensor 1)
  • 6 y 7: Test interno (valor de 16 bits que refleja el estado del sensor 2)
  • 8 y 9: Test interno (valor 1 de calibración de 16 bits)
  • 10 y 11: Test interno (valor 2 de calibración de 16 bits)
  • 12, 13 y 14: Sin usar, devuelven 0
  • 15: Registro para la calibración de la brújula, escribiendo 255 en él al apuntar a cada uno de los cuatro puntos cardinales.

En las siguientes semanas realizaremos las conexiones de este nuevo sensor al bloque NXT, calibraremos la brújula, intentaremos leer de sus registros e incorporaremos en la aplicación Android la monitorización de la posición del robot respecto al polo Norte magnético.