trabajo de fin de grado - universidad de...

93
Equation Chapter 1 Section 1 Trabajo de Fin de Grado Grado en Ingeniería de las Tecnologías de Telecomunicación Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado Autor: Daniel Sojo España Tutor: Ignacio Alvarado Aldea Dep. Ingeniería de Sistemas y Automática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2017

Upload: others

Post on 10-Mar-2020

4 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Equation Chapter 1 Section 1

Trabajo de Fin de Grado

Grado en Ingeniería de las Tecnologías de

Telecomunicación

Aplicación móvil para el control via bluetooth de un

vehículo autoequilibrado

Autor: Daniel Sojo España

Tutor: Ignacio Alvarado Aldea

Dep. Ingeniería de Sistemas y Automática

Escuela Técnica Superior de Ingeniería

Universidad de Sevilla

Sevilla, 2017

Page 2: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo
Page 3: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Proyecto Fin de Carrera

Ingeniería de Telecomunicación

Aplicación móvil para el control via bluetooth de un

vehículo autoequilibrado

Autor:

Daniel Sojo España

Tutor:

Ignacio Alvarado Aldea

Dep. Ingeniería de Sistemas y Automática

Escuela Técnica Superior de Ingeniería

Universidad de Sevilla

Sevilla, 2017

Page 4: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo
Page 5: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Trabajo de Fin de Grado: Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

Autor: Daniel Sojo España

Tutor: Ignacio Alvarado Aldea

El tribunal nombrado para juzgar el Proyecto arriba indicado, compuesto por los siguientes miembros:

Presidente:

Vocales:

Secretario:

Acuerdan otorgarle la calificación de:

Sevilla, 2017

El Secretario del Tribunal

Page 6: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo
Page 7: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

A mi familia

A mis maestros

Page 8: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo
Page 9: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Resumen

El presente proyecto de fin de grado se centra en el desarrollo de una aplicación móvil para dispositivos

Android. La finalidad de la misma es establecer conexión con un vehículo autoequilibrado de tipo péndulo

invertido controlado por una placa Arduino, previamente desarrollado por otros alumnos de la escuela, y

permitir controlar su movimiento.

El vehículo a controlar dispone de un módulo bluetooth, por lo que se hará uso de dicha tecnología para

establecer una conexión y permitir la comunicación entre el vehículo y el dispositvo móvil Android. Por

supuesto, el dispositivo móvil o smartphone ha de ser compatible con la tecnología Bluetooth.

Así mismo, a fin de cerrar la comunicación y solventar problemas previos, se reescribirá parte del código

ejecutado en la placa Arduino. Concretamente, todo lo relacionado con la gestión de la comunicación

bluetooth y la interpretación de las órdenes de dirección y movimiento.

Page 10: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo
Page 11: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Índice

Resumen 9

Índice 11

Índice de Tablas 13

Índice de Figuras 15

1 Introducción 1 1.1 Objetivo 1 1.2 Contexto 1

2 Punto de partida 3 2.1 Vehículo autoequilibrado - Mini Segway - 3

2.1.1 Hardware 3 2.1.2 Software 4

2.2 Aplicación original para dispositivos Android 4 2.2.1 Interfaz 4 2.2.2 Programación de la aplicación y consecuencias 6 2.2.3 Principales problemas 6

2.3 Memoria del proyecto original 6

3 Software 7 3.1 Android OS 7

3.1.1 Introducción 7 3.1.2 Versiones. APIs 7 3.1.3 Ciclo de vida de una aplicación 9

3.1.3.1 onCreate() 10 3.1.3.2 onStart() 10 3.1.3.3 onResume() 10 3.1.3.4 onPause() 11 3.1.3.5 onStop() 11 3.1.3.6 onDestroy() 11

3.1.4 Estructura de archivos básica 12 3.2 Arduino 13

3.2.1 Introducción 13 3.2.2 Estructura de un programa 13

4 Hardware 15 4.1 Arduino Mega 2560 15 4.2 Módulo Bluetooth HC-06 16

5 Entorno de Desarrollo 17 5.1 Android 17

5.1.1 Lenguaje de programación. Java. 17 5.1.2 Android SDK. Kit de Desarrollo Software 17

Page 12: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

5.1.3 ADB. Android Debug Bridge. 18 5.1.4 ADV. Android Virtual Devices. 18 5.1.5 Entorno de Desarrollo Integrado: Android Studio 19

5.2 Arduino 23 5.2.1 Lenguaje de programación 23 5.2.2 Entorno de Desarrollo Integrado: Arduino IDE 23

6 Control de versiones 25 6.1 Git 25 6.2 Gestor de repositorios Git: GitLab 26

7 Programación 27 7.1 Aplicación móvil para dispositivos Android 27

7.1.1 Interfaz y funcionamiento 27 7.1.2 Preliminares. Archivo manifiesto: AndroidManifest.xml 31 7.1.3 Actividades 33

7.1.3.1 SplashScreenActivity 33 7.1.3.2 MainActivity 33 7.1.3.3 Preferences 33

7.1.4 Clases Java 33 7.1.4.1 Bluetooth 34 7.1.4.2 ReceivedData 37 7.1.4.3 Message 38 7.1.4.4 Joystick 39 7.1.4.5 Accelerometer 41 7.1.4.6 Movement 43 7.1.4.7 MainActivity 44 7.1.4.8 Preferences 48 7.1.4.9 SplashScreenActivity 49 7.1.4.10 Tools 50 7.1.4.11 BluetoothDeviceListCustomAdapter 51

7.1.5 Recursos 52 7.1.5.1 Drawable 52 7.1.5.2 Layout 52 7.1.5.3 Menu 53 7.1.5.4 Mipmap 53 7.1.5.5 Values 53 7.1.5.6 Xml 53

7.2 Programa Arduino 54 7.2.1 Detalle de las nuevas rutinas 54

7.2.1.1 Respuesta a las órdenes de movimiento 54 7.2.1.2 Recepción y envío de mensajes 55

8 Conclusiones 57

9 Bibliografía 59

Apendice A: Código para Arduino 61

Apendice B: Código para Android 77

Page 13: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

ÍNDICE DE TABLAS

Tabla 2–1. Componentes principales del vehículo. 3

Tabla 3–1. Distribución relativa de versiones de Android. 8

Tabla 3–2. Versiones del S.O. Android. 8

Tabla 4–1. Detalles técnicos del Arduino Mega 2560. 15

Tabla 6–1. Comandos básicos de Git. 25

Page 14: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo
Page 15: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

ÍNDICE DE FIGURAS

Figura 2-1. Control de dirección. 5

Figura 2-2. Control de velocidad. 5

Figura 2-3. Ajustes de variables. 5

Figura 2-4. Trimado de Ángulo. 6

Figura 3-1. Paquetes según nivel de API. 7

Figura 3-2. Ciclo de vida de una Actividad en Android. 9

Figura 3-3. Estructura de archivos de una aplicación Android. 12

Figura 3-4. Arduino Mega 2560. 13

Figura 3-5. Arduino Nano. 13

Figura 4-1. Módulo Bluetooth HC-06. 16

Figura 5-1. Android SDK Manager. 18

Figura 5-2. Interfaz de Android Studio. 19

Figura 5-3. Android Studio. Barra de herramientas. 19

Figura 5-4. Android Studio. Ruta actual. 20

Figura 5-5. Android Studio. Arbol del proyecto. 20

Figura 5-6. Android Studio. Renderizado de XML. 20

Figura 5-7. Android Studio. ADVs. 21

Figura 5-8. Android Studio. Logcat. 21

Figura 5-9. Android Studio. Monitor de recursos. 22

Figura 5-10. Android Studio. Integración con Git. 22

Figura 5-11. Interfaz de Arduino IDE. 23

Figura 5-12. Interfaz de Arduino IDE. Gestor de Librerías. 24

Figura 6-1. Vistazo a un commit en GitLab. 26

Figura 7-1. Interfaz de la aplicación. Splash Screen. 27

Figura 7-2. Interfaz de la aplicación. Actividad principal – sin conexión. 27

Figura 7-3. Interfaz de la aplicación. Menú lateral – activar Bluetooth. 28

Figura 7-4. Interfaz de la aplicación. Solicitud de activación de la conectividad Bluetooth. 28

Figura 7-5. Interfaz de la aplicación. Menú lateral – dispositivos vinculados. 28

Figura 7-6. Interfaz de la aplicación. Menú lateral – conexión. 29

Figura 7-7. Interfaz de la aplicación. Actividad principal – conexión. 29

Page 16: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Figura 7-8. Interfaz de la aplicación. Joystick. 30

Figura 7-9. Interfaz de la aplicación. Acelerómetro. 30

Figura 7-10. Interfaz de la aplicación. Menú de ajustes. 30

Figura 7-11. Ejes de las medidas inerciales en Android. 42

Figura 7-12. Icono de la aplicación. 53

Page 17: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

1

1 INTRODUCCIÓN

1.1 Objetivo

El proyecto que se detalla en este documento se realiza como el correspondiente Trabajo de Fin de Grado, de

cara a la obtención del título de Graduado en Ingeniería de las Tecnologías de Telecomunicación. El mismo ha

sido llevado a cabo bajo la tutoría de Ignacio Alvarado Aldea, profesor del Departamento de Sistemas y

Automática.

El objetivo en sí es el desarrollo de una aplicación para dispositivos móviles Android capaz de establecer

conexión con un pequeño vehículo autoequilibrado de tipo péndulo invertido, permitiendo el control del

mismo. Para ello se hará uso de la tecnología bluetooth, presente tanto en el vehículo en cuestión como en casi

cualquier Smartphone de hoy día.

Establecida la conexión, la aplicación ofrecerá, en un principio, dos métodos de control.

El primero de ellos consiste en un joystick virtual, el cual permitirá dar órdenes de movimiento deslizando un

dedo sobre la pantalla del smarthpone que ejecute la aplicación, permitiendo no solo indicar la dirección del

movimiento sino también la velocidad que se desea alcanzar, según sea la separación entre el joystick virtual y

el centro del mismo.

El segundo método hará uso del acelerómetro disponible en la mayor parte de los dispositivos móviles

Android. En este caso será la inclinación aplicada al dispositivo, relativa a una referencia dada, la que

determine dirección y velocidad de movimiento.

Junto a las órdenes de control, la aplicación ofrecerá la opción de gestionar ciertos valores relativos a las

ecuaciones de control ejecutadas en el Arduino. Para ello se diseñará una pantalla adicional donde modificar

dichos valores, los cuales se detallarán en secciones posteriores de este documento.

Al mismo tiempo se actualizará el software presente en la placa Arduino para permitir el funcionamiento de la

comunicación bluetooth y el tratamiento de los datos recibidos, ya sean órdenes de movimiento o

actualizaciones en el valor de alguna variable del programa que se estaría ejecutando.

El objetivo último de este proyecto es facilitar el control del vehículo y permitir una conexión y ejecución del

programa estable, tanto por parte de la placa Arduino como del dispositivo móvil.

1.2 Contexto

A dia de hoy los dispositivos móviles igualan en potencia computacional a los equipos de escrtorio estandar de

hace unos años y además disponen de multitud de sensores embebidos de serie que les permiten interactuar

con el entorno. Todo ello unido a la popularidad de Android somo Sistema Operativo hace que podamos tener

en nuestras manos una maquina multipropósito potentísima que solo ha de ser programada adecuadamente.

Android ofrece de forma gratuita un entorno de desarrollo propio, Android Studio, así como toda la

documentación necesaria para construir cualquier aplicación desde cero o a partir de un esqueleto base. Es por

ello que los smartphones son algo más que teléfonos inteligentes, ya que con la aplicación adecuada un

terminal podría sustituir a un equipo tradicional en su función.

En este caso se busca crear el equivalente a un mando de control remoto, como el que podríamos encontrar en

un coche o vehículo radio control, con la salvedad de que podemos organizar los mandos y las órdenes

generadas a nuestro gusto, programando la propia aplicación de control.

Page 18: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Introducción

2

Page 19: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

3

2 PUNTO DE PARTIDA

El proyecto recogido en esta memoria no parte desde cero, sino que con él se pretende actualizar y continuar

con el trabajo realizado anteriormente por otros alumnos de la escuela, diferenciando dos partes: vehículo

autoequilibrado controlado por Arduino y aplicación móvil.

2.1 Vehículo autoequilibrado - Mini Segway -

En mención a sus autores originales el robot es llamado informalmente “Robot Mini Segway” o simplemente

“Mini Segway”, lo cual deriva de su similitud con los vehículos producidos por la compañía Segway, cuyo

nombre se toma hoy día a la hora de nombrar a este tipo de vehículos o robots.

2.1.1 Hardware

La estructura principal o chasis está constituida por una lámina de aluminio con una sección de madera en la

parte superior, la cual permite la sujeción de una lamina de metacrilato que hace las veces de superfice de

carga y permite suavizar los golpes gracias a unos segmentos de material acolchado en sus extremos.

En el corazón del robot se encuentra la placa microcontroladora Arduino Mega 2560, encargada de la gestión

y control del resto de componentes. Esta se encuentra acoplada a una placa de circuito impreso que facilita la

conexión con el resto de componentes, entre ellos el módulo bluetooth HC-06. En una sección posterior se

hablará detenidamente sobre estos dos componentes en concreto.

Dispone de una unidad de medidas inerciales o IMU (Inertial Measurement Unit), concretamente la

MPU6050, dos controladores de motor modelo TB6612FNG y dos motores EMG-30.

Para alimentar el sistema se optó por una batería de Polímero de Litio, Li-Po, de 1.000mAh y 11.1V de tensión

nominal, con una tasa de descarga de 25C.

En lo que respecta al hardware los cambios introducidos fueron minimos y con el objetivo de restaurar su

funcionamiento en caso de avería en alguno de los compomentes. Dichas modificaciones se detallan más

adelante.

Tabla 2–1. Componentes principales del vehículo.

Dispositivo Modelo

Placa controladora Arduino Mega 2560

Módulo bluetooth HC-06

Unidad de Medidas Inerciales (IMU) MPU6050

Controlador del motor TB6612FNG

Motores EMG-30

Batería Li-Po 11.1V 1.000mAh

Page 20: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Punto de partida

4

2.1.2 Software

En lo relativo al software, la placa microcontroladora Arduino se encontraba completamente programada,

haciendo al vehículo totalmente autosuficiente para mantener el estado de reposo y aceptar ordenes básicas de

control.

El programa emplea 4 interrupciones asociadas a cambios en los pines para realizar la lectura de nuevos

valores en la posición de las ruedas y determinar así su velocidad. Por otro lado, se utlizan dos sucesos

temporales usando los Timers 1 y 5: uno de ellos realiza el cálculo del controlador PI (Proporcional e Integral)

cada 10ms y el otro el control LQR (Linear Quadratic Regulator) cada 40ms.

2.2 Aplicación original para dispositivos Android

2.2.1 Interfaz

Cuando se inició este proyecto ya existía aplicación para dispositivos móviles Android capaz de establecer

conexión con el vehículo a través del módulo bluetooth y enviar órdenes de control básicas estipulando una

velocidad de traslación y/o rotación fijas y una orden de dirección de entre nueve posibles:

(1) – Avanzar y girar a la izquierda

(2) – Avanzar en línea recta

(3) – Avanzar y girar a la derecha

(4) – Girar a la izquierda

(5) – Reposo

(6) – Girar a la derecha

(7) – Retroceder y girara a la izquierda

(8) – Retroceder en línea recta

(9) – Retroceder y girar a la derecha

Estas órdenes de dirección estaban asociadas a un teclado numérico de 9 botones, mientras que el control de

velocidad y traslación se aplicaba con dos barras horizontales.

También daba opción a modificar varias de las variables empleadas por el programa ejecutado en la placa

microcontroladora Arduino, para lo cual se debía cumplimentar un formulario en la parte inferior de la pantalla

principal.

Page 21: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

5

Figura 2-1. Control de dirección.

Figura 2-2. Control de velocidad.

Figura 2-3. Ajustes de variables.

Page 22: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Punto de partida

6

Además de los controles indicados existía otro más y es el control del trimado de ángulo. Su finalidad era

compensar manualmente la variación del centro de gravedad, provocado, por ejemplo, al colocar una carga

descentrada sobre el segway.

Figura 2-4. Trimado de Ángulo.

Respecto a este ajuste cabe decir que desaparece en la nueva aplicación, gracias a los avances de la última

alumna de la escuela que ha trabajado con el robot.

2.2.2 Programación de la aplicación y consecuencias

La aplicación original fue desarrollada usando Scratch como lenguaje de programación y App Inventor como

entorno de desarrollo. Scratch es un lenguaje de programación visual en el cual se utilizan bloques lógicos y

funciones prediseñadas para conseguir una determinada funcionalidad. Esto hace posible programar una

aplicación sin disponer de conocimientos sobre el lenguaje de programación en cuestión, sin embargo, se

pierde gran parte del control sobre el código.

Debido a esto la aplicación del presente proyecto se hizo partiendo de cero, manteniendo la estrucutra de los

mensajes para permitir la comunicación entre dispositivo móvil y robot.

2.2.3 Principales problemas

El propio método utilizado en la programación de la aplicación provocó que esta respondiese aun único caso

de uso, totalmente lineal y secuencial, derivando normalmente en un error o cuelgue del programa si se

realizaba incorrectamente. Aun así, siguiendo estrictamente los pasos era frecuente la desconexión prematura

entre el robot y el terminal móvil, o sencillamente el cuelgue de la misma.

Además, solo se ofrecía un mecanismo de control por medio de órdenes simples de dirección y velocidad fija,

lo cual no permitía a un control preciso del sistema.

2.3 Memoria del proyecto original

Junto al hardware y software original también fue facilitada la memoria del proyecto original, en la cual se

detalla desde un punto de vista mucho más técnico el funcionamiento del robot. Dicha memoria se puede

encontrar en la bibliografía de este proyecto.

Page 23: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

7

3 SOFTWARE

Como se anticipó en secciones anteriores la aplicación móvil pretende ser ejecutada en un Smartphone

Android y establecerá conexión con un módulo bluetooth controlado por una placa Arduino. Por ello, el punto

de partida pasa por el propio Sistema Operativo y la gestión de una aplicación en el caso de Android, así como

la estrucutra de un programa en Arduino.

3.1 Android OS

3.1.1 Introducción

Android es un Sistema Operativo desarrollado por Google1. Está basado en el núcleo Linux y originalmente

fue diseñado para dispositivos móviles con pantall tactil como smarthpones o tabletas, aunque terminó

diversificándose en todo tipo de dispositivos electrónicos como relojes inteligentes, televisores, equipos

multimedia o vehículos.

El código fuente de Android es distribuido por Google bajo una licencia de código libre. Este carácter “libre”

es precisamente una de las grandes bazas de Android y es lo que ha fomentado el desarrollo de aplicaciones

tanto por parte de empresas como desarrolladores independientes.

3.1.2 Versiones. APIs

La primera versión de Android fue lanzada en septiembre de 2008 con Android 1.0 como nombre, a la cual

seguiría Android 1.1. A modo anecdótico, a todas las versions sucesivas se les ha asignado un nombre en

clave, en orden alfabético, haciendo referencia a algún tipo de dulce o postre.

El interés en estas versiones reside en el salto sucesivo en el nivel de API2. Con cada nuevo nivel se habilitan

nuevas librerías y paquetes para uso del desarrollador en la comunicación con el Sistema Operativo.

Estos paquetes segmentados por nivel de API se pueden ver y analizar de una forma muy cómoda e intuitiva

en developer.android.com

Figura 3-1. Paquetes según nivel de API.

Por ello, uno de los primeros parámetros a definir a la hora de desarrollar una aplicación para Android es el

nivel de API mínimo necesario para que esta pueda funcionar. Cuando mayor sea el nivel fijado, mayor será el

abanico de posibilidades a la hora de interactuar con el Sistema. Sin embargo, desde que se lanza una nueva

1 El desarrollo inicial fue llevado a cabo por Android Inc, empresa que sería adquirida por Google en 2005. 2 Application Programming Inteface o Intefaz de Programación de Aplicaciones.

Page 24: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Software

8

versión hasta que una parte importante de la población llega a hacer uso de la misma transcurre bastante

tiempo, por lo que ha de seleccionarse un nivel intermedio que de acceso a la mayor cantidad de población

posible. A nivel informativo se adjunta la distribución de versiones más actual ofrecida por Google.

Tabla 3–1. Distribución relativa de versiones de Android3.

Versión Nombre en clave API Distribución Compatibilidad

2.3.3 – 2.3.7 Gingerbread 10 0.70 % -

4.0.3 – 4.0.4 Ice Cream Sandwich 15 0.70 % 99.30 %

4.1.X

4.2.X

4.3

Jelly Bean 16

17

18

2.70 %

3.80 %

1.10 %

98.60 %

95.90 %

92.10 %

4.4 KitKat 19 16.00 % 91.00 %

5.0

5.1

Lollipop 21

22

7.40 %

21.80 %

75.00 %

67.60 %

6.0 Marshmallow 23 32.30 % 45.80 %

7.0

7.1

Nougat 24

25

12.30 %

1.20 %

13.50 %

1.20 %

De ella se deduce que, actualmente, fijar el nivel de API en 15 permitiría que nuestra aplicación puediese ser

utilizada prácticamente por todos los dispositivos móviles con Android en activo.

En lo que respecta a la aplicación objeto de esta memoria el nivel de API se fijó en 13 a fin de dar soporte al

smartphone que en un principio se utilizó para las pruebas.

Tabla 3–2. Versiones del S.O. Android.

Nombre en clave Versión Nivel API

Android 1.0 1.0 1

Android 1.1 1.1 2

Cupcake 1.5 3

Donut 1.6 4

Eclair 2.0 – 2.1 5 – 7

Froyo 2.2 – 2.2.3 8

Gingerbread 2.3 – 2.3.7 9 – 10

Honeycomb 3.0 – 3.2.6 11 – 13

Ice Cream Sandwich 4.0 – 4.0.5 14 – 15

Jelly Bean 4.1 – 4.3.1 16 – 18

KitKat 4.4 – 4.4.4 19 – 20

Lollipop 5.0 – 5.1.1 21 – 22

Marshmallow 6.0 – 6.1 23

Nougat 7.0 – 7.1.2 24 – 25

Oreo 8.0 26

3 Datos recopilados por Google durante un período de 7 días hasta 8/8/2017. No se muestran versiones con una distribución inferior al 0,1%.

Page 25: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

9

9 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

3.1.3 Ciclo de vida de una aplicación

Las aplicaciones en Android están constituidas por una o varias actividades. Estas pueden atravesar diferentes

estados a lo largo de su ciclo de vida conforme el usuario interactua con ellas. Por este motivo, la clase

Actividad proporciona una serie de callback4 que indican cuando se produce un cambio o transición de estado.

Haciendo uso de estos callbacks el programador puede definir el comportamiento de sus actividades ante las

acciones del usuario, por ejemplo, reaccionando de una forma determinada si el usuario sale y vuelve a entrar a

la aplicación o si esta pasa a un segundo plano por algún otro motivo. Además, una buena implementación de

los mismos ayuda a gestionar correctamente los recursos del sistema y recuperar el estado de la aplicación

cuando se produce una rotación de pantalla o esta regresa desde un segundo plano.

Figura 3-2. Ciclo de vida de una Actividad en Android.

4 Función que se usa como argumento de otra función principal. La función principal ejecutará la función pasada como parámetro cuando se produzca un determinado evento. Esto permite mantener una capa de abstracción.

Page 26: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Software

10

3.1.3.1 onCreate()

El único de los siete callbacks o métodos de obligatoria implementación y el que será lanzado en cuanto el

sistema cree la actividad. En él se ha de situar aquel código de inicialización de la aplicación que solo se

pretende ejecutar una vez al inicio de la misma. Además, es aquí cuando se recibe los datos que fueron

guardados del estado anterior de la actividad, por ejemplo, tras producirse una rotación de pantalla.

@Override

public void onCreate(Bundle savedInstanceState) {

// Llamar a la super-clase para completar la creación de la actividad

super.onCreate(savedInstanceState);

// Recuperar datos guardados (si los hubiese) de un estado anterior

if (savedInstanceState != null) {

// ToDo

}

// Establecer el layout de la interfaz para la actividad

setContentView(R.layout.main_activity);

// ToDo

}

3.1.3.2 onStart()

El sistema llama a este callback cuando la aplicación entra en el estado “Iniciada”. Define el punto intermedio

entre la carga de datos de la actividad y la representación en pantalla de la interfaz de usuario.

@Override

public void onStart() {

// Llamar al método de la super clase

super.onStart();

// ToDo

}

3.1.3.3 onResume()

Llamado cuando la actividad entra en el estado “Reanudada” y pasa a un primer plano. En este estado la

actividad es controlable por el usuario y permanecerá en él mientras no suceda algún evento que la destrulla o

lleve a un segundo plano.

@Override

public void onResume() {

// Llamar al método de la super clase

super.onResume();

// ToDo

}

Page 27: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

11

11 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

3.1.3.4 onPause()

El sistema llamará a esta función como primer indicio de que el usuario está abandonado la aplicación. Un

buen uso de este callback es detener procesos que no debiesen continuar ejecutándose en segundo plano.

@Override

public void onPause() {

// Llamar a la super-clase

super.onPause();

// ToDo

}

3.1.3.5 onStop()

Su llamada implica que la actividad deja de estar visible para el usuario. La duración de este evento es superior

al anterior onPause() y por ello es aquí donde se debe realizar cualquier operación de salvaguardado de datos.

Como buena práctica, dado que la aplicación dejará de ser utilizada (al menos temporalmente) deben liberarse

todos los recursos posibles.

@Override

public void onStop() {

// Llamar a la super-clase

super.onStop();

// ToDo

}

3.1.3.6 onDestroy()

Esta llamada se realizará justo antes de que la actividad sea destruida, esto es, deje de estar en memoria. Este

evento se produce bien porque el usuario la ha abandonado completamente o porque el sistema ha decidido

eliminarla de la memoria para recuperar recursos.

@Override

public void onDestroy () {

// Llamar a la super-clase

super.onDestroy();

// ToDo

}

Page 28: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Software

12

3.1.4 Estructura de archivos básica

El código que da forma a una apliación Android se distribuye en un conjunto de directorios dentro del

proyecto. En el nivel más superior distiguimos:

manifest – contiene el archivo AndroidManifest.xml que define a la aplicación. En él encontraremos

los detalles de la aplicación, sus permisos, las actividades que la componen y la interacción entre ellas.

java – le sigue un conjunto de directorios que dan nombre el paquete de nuestra aplicación y que

contienen los archivos de código fuente Java.

res – contienen todos los recursos de nuestra aplicación: vistas de las actividades, iconos de la

aplicación (según resolución de pantalla), imágenes en general, cadenas de texto, valores numéricos

(dimensiones), definiciones de colores, figuras geométricas, etc. Según el tipo son clasificados en sub-

directorios. Además, aquí se puede distinguir entre distintos niveles de API, densidad de píxeles o

lenguajes de interfaz según estos archivos sean distribuidos y nombrados.

Figura 3-3. Estructura de archivos de una aplicación Android.

Page 29: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

13

13 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

3.2 Arduino

3.2.1 Introducción

Arduino es una plataforma electrónica y software de código abierto. Las placas micrcontroladoras Arduino

disponen de múltiples interfaces de entrada y/o salida, capaces de leer valores analógicos y/o digitales, realizar

cálculos con los mismos y generar una salida o respuesta determinada. Su carácter open-source y su bajo

precio han hecho que constituyan una opción ideal a la hora de realizar prototipos electrónicos de bajo coste.

Acutalmente existen en el mercado diferentes modelos que varian en tamaño, recursos, poder computacional y

número de conexiones disponibles. Así mismo, se distribuyen innumerables shields o placas de expansión con

distintos componentes que añaden funcionalidades no disponibles de serie como conectividad bluetooth,

sensores de infrarrojos o ultrasonidos, etc.

Figura 3-4. Arduino Mega 2560.

Figura 3-5.

Arduino Nano.

3.2.2 Estructura de un programa

Los programas en Arduino son escritos en un lenguaje similar a C o C++ y se componen de dos bloques

principales:

Setup() – Bloque que será ejecutado una única vez al proveer de alimentación a la placa o tras

provocar un reseteo de la misma. En esta sección se inicializan variables y registros.

Loop() – Bloque que se ejecuta de forma contínua e infinita. Aquí estará el programa en sí.

void setup () {

// Codigo que se ejecutará una única vez tras iniciarse la placa

}

void loop () {

// Codigo que será ejecutado en un bucle infinito

}

Page 30: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Software

14

Page 31: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

15

4 HARDWARE

El robot que se desea controlar está formado por varios componentes interconectados a través de una placa de

circuito impreso que a su vez permite a la placa Arduino tomar control sobre ellos.

En lo referente a este proyecto, y en lo que concierne al hardware, son dos los elementos de importancia. La

placa microcontroladora Arduino Mega 2560 y el módulo bluetooth HC-06 conectado a la misma a través de

su puerto serie.

4.1 Arduino Mega 2560

El Arduino Mega 2560, así como el resto de modelos comerciales de Arduino, viene pre-programado con un

bootloader5 que permite al usuario cargar su programa en la placa sin necesidad de hardware adicional,

simplemente conectadolo via USB con un ordenador.

Tabla 4–1. Detalles técnicos del Arduino Mega 2560.

Concepto Valor

Microcontrolador ATmega2560

Voltaje de funcionamiento 5V

Voltaje de entrada (recomendado) 7-12 V

Voltaje de entrada (límite) 6-20 V

Pines digitales de entrada/salida 54 (15 permiten salida PWM)

Pines analógicos de entrada 16

Corriente contínua por pin 20 mA

Corriente contínua para el pin de 3.3V 50 mA

Memoria flash 256 KB (8 KB para el bootloader)

SRAM 8 KB

EEPROM 4 KB

Frecuencia de reloj 16 MHz

5 Programa o gestor de arranque. Pequeña pieza de software encargada de preparar todo lo necesario antes de inciar el sistema operativo o programa principal.

Page 32: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Hardware

16

4.2 Módulo Bluetooth HC-06

Se trata de un dispositivo que se comunica con la placa Arduino a través de su puerto serie, con una velocidad

de trasferencia varaible de entre 1.200 y 1.382.400 baudios6, y permite la recepción y transmisión de datos a

través de Bluetooth.

Con el uso de los demoninados comandos AT se pueden cargar valores en su memoria interna para modificar

su funcionamiento. De este modo se consigue que actue como servidor a la escucha de conexiones entrantes,

respondiendo de forma autónoma y retransmitiendo los datos recibidos via Bluetooth a través del puerto serie

mencionado.

Figura 4-1. Módulo Bluetooth HC-06.

6 Número de símbolos por segundo. Cada síbolo puede constituir uno o más bits.

Page 33: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

17

5 ENTORNO DE DESARROLLO

La aplicación móvil que se pretende crear está diseñada para ser instalada y ejecutada en un smartphone con

Sistema Operativo Android, pero a su vez ha de ser capaz de establecer comunicación con un programa

ejecutado en una placa Arduino. Es por ello que este proyecto debe moverse a lo largo de dos entornos

diferentes, cada uno propio de la máquina donde se pretende ejecutar.

5.1 Android

5.1.1 Lenguaje de programación. Java.

Las aplicaciones diseñadas para Android usan Java como lenguaje de programación.

Java se caracteriza por ser de propósito general, concurrente, basado en clases y orientado a objetos. Su idea

original era permitir a los programadores escribir sus programas una única vez, haciendo que dicho programa

pudiese ser ejecuado en cualquier máquina, esto es, crear aplicaciones independientes de la plataforma de

ejecución. Un programa en Java compilado puede ser ejecutado en cualquier plataforma que soporte Java,

independientemente del hardware, usando una máquina virtual de Java.

En Java es primordial el concepto de clase. Tanto código funcional como variables y programas

independientes son encapsulados en lo que se denomina clases. Dentro de las clases podemos encontrar pues

variables y funciones, solo que a estas variables se las llama atributos y a las funciones métodos de la clase.

Tanto clases como variables y métodos pueden ser públicos, protegidos o privados. Esto se consigue

añadiendo una palabra reservada y afecta a su visibilidad desde el punto de vista del código.

En una aplicación Android nos encontramos pues con múltiples documentos o archivos con extensión .java,

contenedores del código de la aplicación, cada uno con el nombre de la clase definida en su interior.

5.1.2 Android SDK. Kit de Desarrollo Software

Para poder desarollar aplicaciones Android se precisan de herramientas software. Todas ellas son facilitadas

por Google y se pueden encontrar en el Kit de Desarrollo Software de Android, abreviado en inglés como

Android SDK.

En el caso de Windows, desde su panel de control se pueden instalar y desinstalar los paqueres que se

precisen, así como actualizar su versión conforme una nueva sea publicada. Dichas actualizaciones están

ligadas directamente al desarrollo de Android como Sistema Operativo.

Page 34: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Entorno de Desarrollo

18

Figura 5-1. Android SDK Manager.

5.1.3 ADB. Android Debug Bridge.

El conector o puente para depuración de Android, abreviado como ADB, es una colección de herramientas que

permiten al programador establecer conexión con un dispositivo Android y ejecutar comandos hacia o desde

él. Con ello se puede obtener información de los dispositivos conectados, transferir archivos, modificar

variables del sistema, instalar aplicaciones, ejecutar comandos dentro del propio sistema operativo Android o,

más importante desde el punto de vista del desarrollo de aplicaciones, depurarlas.

Normalmente, el uso de ADB se realiza por medio del terminal, invocando al programa junto a la función a

ejecutar. Por ejemplo, el comando:

adb devices

Devolvería un listado con los dispositivos Android conectados al ordenador (físicos o emulados) junto a su ID

único. Dicho ID se utiliza en otros comandos para especificar el dispositivo afectado usando el prefijo -s.

adb –s [ID] shell

adb –s [ID] install ruta_al_apk

5.1.4 ADV. Android Virtual Devices.

Si no se dispone de un terminal móvil donde probar y depurar una aplicación, siempre se puede emular. Con

las herramientas software anteriormente mencionadas podemos crear un dispositivo móvil virtual en el que

instalar la aplicación en desarrollo como si de un terminal físico se tratase, eso si, con ciertas limitaciones en lo

que a sensores se refiere.

Page 35: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

19

19 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

5.1.5 Entorno de Desarrollo Integrado: Android Studio

A fin de agrupar todo lo anterior y facilitar la programación de aplicaciones para Android, Google ofrece un

entorno de desarrollo específicamente diseñado para tal funcionalidad: Android Studio.

Figura 5-2. Interfaz de Android Studio.

En la figura anterior podemos ver de forma general la interfaz de Android Studio. En la parte superior

encontramos los típicos menús con todas las opciones del IDE, agrupadas según utilidad. Bajo ellos tenemos

una barra de herramientas con las opciones más habituales.

Figura 5-3. Android Studio. Barra de herramientas.

Estas herramientas se dividen en nueve grupos:

Cargar un nuevo archivo o guardar/actualizar el actual.

Deshacer/rehacer

Cortar, copiar o pegar

Buscar o buscar y reemplazar

Navegación adelante o atrás

Construcción y/o ejecución de la aplicación en desarrollo; ya sea en un dispositivo físico o virtual, en

modo normal o depuración. Reinicio o detención de la ejecución.

Actualizar el repositorio local y/o remoto así como ver diferencias entre versiones y revertir cambios.

Ajustes generales y del proyecto.

Sincronización del proyecto con los arhivos Gradle7. Gestión de ADV y SDK. Monitor Android.

7 Sistema de código abierto para la construcción automática de software, en este caso la aplicación Android.

Page 36: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Entorno de Desarrollo

20

Inmediatamente bajo esta barra encontramos una sucesión de directorios con la ruta del fichero actualmente

abierto y en edición.

Figura 5-4. Android Studio. Ruta actual.

Continuando por la izquierda nos encontramos con el proyecto en sí. Android Studio no ofrece varias visiones

del mismo: una visión de su árbol de archivos o la estructura lógica del mismo con clases, métodos y variables.

También encontramos aquí caputras de ejecuciones que hayan sido grabadas para su posterior análisis.

Figura 5-5. Android Studio. Arbol del proyecto.

Aunque la aplicación se programa íntegramente en Java, es preciso usar otro meta-lenguaje llamado XML

para diseñar las vistas. Este IDE ofrece una visión adicional para los archivos XML y no es otra que el

renderizado de los mismos, pudiendo apreciarse en tiempo real cómo afecta la modificación de unas líneas a la

vista que pretenden definir. No solo eso, sino que además se puede aplicar el renderizado a distintos niveles de

API, pudiendo jugar con la representación de cada componente en pantalla según la versión de Android final.

Figura 5-6. Android Studio. Renderizado de XML.

Page 37: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

21

21 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

Desde el propio IDE se puede gestionar tanto las librerías del SDK como los dispositivos virtuales o ADV. El

programador puede crear su smartphone o tablet virtual personalizada, escogiendo desde resolución de

pantalla y densidad de píxeles de la misma, hasta la versión de Android ejecutada, tipo de CPU, cantidad de

memoria RAM, etc. Desde el panel de gestión de dispositivos virtuales Android pueden lanzarse estas

máquinas virtuales para posteriormente instalar en ellas la aplicación en desarrollo.

Figura 5-7. Android Studio. ADVs.

Por supuesto, desde Android Studio se pueden leer y registrar todos los logs lanzados por nuestra propia

aplicación en ejecución, el sistema Android o cualquier otra aplicación. Para diferenciar entre ellos basta con

mirar el nombre del paquete que originó el mensaje o bien aplicar un filtro. En la siguiente figura se están

filtrando estos logs buscando la cadena “MyApp.” en ellos.

Figura 5-8. Android Studio. Logcat.

Page 38: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Entorno de Desarrollo

22

Sin embargo, el monitor de Android no se limita solo a logs, también registra el efecto de la aplicación en el

sistema: porcentaje de uso de la CPU, RAM ocupada, uso de red y porcentaje de uso de la GPU.

Figura 5-9. Android Studio. Monitor de recursos.

Para terminar, como se adelantó anteriormente, también tenemos total compatibilidad con el sistema de control

de versiones Git sobre el que se hablará en una sección posterior. Desde su panel podemos ver los archivos con

modificaciones pendientes de registrar, así como dichas modificaciones del código fuente y el histórico de

commits realizados en las distintas ramas.

Figura 5-10. Android Studio. Integración con Git.

Page 39: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

23

23 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

5.2 Arduino

5.2.1 Lenguaje de programación

Los programas para Arduino son escritos en un lenguaje de programación propio pero muy similar a C/C++.

En líneas generales, los programas están formados por una estructura definida, un conjunto de constantes y

variables y una colección de funciones.

En lo que respecta a la estructura se distiguen dos bloques fundamentales indicados anteriormente: setup() y

loop(). Las estructuras de control por su parte, son las mismsa que podríamos encontrar en C/C++: if, if-else,

while, do-while, etc.

Las funciones pueden ser definidas por el programador a gusto propio, pero cabe mencionar que existen

multitud de ellas particulares de una placa Arduino y que guardan estrecha relación con la definición de pines

de entrada/salida y el trabajo con los mismos.

5.2.2 Entorno de Desarrollo Integrado: Arduino IDE

Arduino como empresa ofrece un Entrono de Desarrollo propio, tanto web como local. En este proyecto nos

centraremos en la aplicación de escritorio.

Este IDE ofrece un editor de texto donde escribir el código del programa, un área para mensajes informativos,

una consola o terminal y una barra de herramientas con botones para las funciones más comunes.

Figura 5-11. Interfaz de Arduino IDE.

Tomando la figura anterior como referencia y comenzando desde arriba tendríamos:

Menús – Todas las opciones del programa agrupadas en menús. Entre ellas: cargar proyectos,

modificar el formato del editor, verificar/compilar el código, incluir librerías, cambiar la placa de

desarrollo Arduino y/o procesador, etc.

Barra de herramientas – Aquí encontramos cinco operaciones básicas. De izquierda a derecha:

verificar/compilar, subir el programa, nuevo archivo, abrir archivo, guardar archivo.

Paneles – Cada archivo que forma parte del proyecto.

Page 40: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Entorno de Desarrollo

24

Editor de texto – Campo donde ver y editar el código fuente.

Mensajes de estado – Aquí se muestran mensajes referentes al estado de la verificación/compilación,

como errores porducidos o uso de recursos en caso de éxito, así como el estado de la subida del

programa a la placa arduino.

Sin salir del IDE podemos gestionar de forma muy simple las librerías utilizadas, añadiendo o eliminándolas

usando un sencillo buscador. Sin embargo, cabe mencionar que esta biblioteca de librerías no contiene ni

mucho menos todas las disponibles. En la mayoría de los casos se precisan de librerías creadas por

desarrolladores independientes y para ello ha de ser buscada y descargada de forma manual, para

posteriormente incorporada al IDE usando su herramienta para la importación de librerías.

Figura 5-12. Interfaz de Arduino IDE. Gestor de Librerías.

Para terminar, una vez desarrollado y compilado el programa no hará falta más que conectar la placa Arduino

al ordenador donde se esté ejecutando el IDE. Este se comunicará con la placa en cuestión y subirá a ella el

código correspondiente cuando se le ordene.

Page 41: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

25

6 CONTROL DE VERSIONES

En cualquier proyecto software es necesario mantener una copia de resplado o cierta redundancia de todo el

trabajo desarrollado, pues al fin y al cabo un ordenador, así como sus medios de almacenamiento, son

electrónica que podría fallar en cualquier momento por algún motivo.

Cuando se trabaja con multitud de archivos de código fuente, además de dicho respaldo, se requiere de cierta

lógica o control sobre las copias salvaguardadas. Llegados a este punto se hace indispensable utilizar algún

software de control de versiones, pues realizar dicho trabajo manualmente sería demasiado tedioso.

6.1 Git

Git es el software de control de versiones por excelencia en la actualidad y el seleccionado para este proyecto.

Esta herramienta soluciona el problema que supone guardar cada versión desarrollada, mantiendo toda su

estrucutra y permitiendo restaurarla en cualquier momento, sin perder ningún dato anterior o posterior.

A los proyectos o directorios sobre los cuales actua Git se los llama repositorios y para actuar sobre ellos

disponemos de un amplio abanico de comandos.

A cada modificación o versión del proyecto guardada se la llama commit. Estas versiones se agrupan a lo largo

de una o varias ramas creadas por el desarrollador, las cuales constituyen líneas independientes de desarrollo.

Tabla 6–1. Comandos básicos de Git.

Comando Argumentos básicos Resultado

git init Ruta al directorio Inicializa un nuevo repositorio en el directorio

indicado.

git status - Muestra el estado de la rama actual como

cambios pendientes de commit.

git add Ruta al archivo/s o

directorio/s

Añade los archivos indicados para un commit

posterior o activa el seguimiento de los mismos

si estaban siendo ignorados.

git commit Nombre para el commit Genera una nueva versión o commit con los

cambios anterioremnte indicados usando add.

git push -

Actualiza el repositorio remoto por defecto

subiendo los cambios realizados por el

programador.

git pull - Actualiza el respositorio local trayendo nuevos

cambios del servidor por defecto.

Page 42: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Control de versiones

26

También cabe mencionar la utilidad de varios archivos/directorios que son generados cuando se crea un

repositorio local:

.git – Directorio oculto donde se almacenan los datos de todas las versiones o commit realizados en el

proyecto

.gitignore – Documento con las rutas que debe ignorar el software a la hora de hacer seguimiento de

los cambios en los archivos. Personalizar el mismo según el proyecto realizado y lenguaje usado

permite hacer seguimiento únicamente de los archivos que lo precisen, omitiendo todo aquello que,

aun estando en el mismo directorio raíz, no sea necesario.

Aun con toda la ayuda que nos ofrece Git, sigue tratándose de una copia local en el disco duro de la máquina

de trabajo, la cual podría fallar.

6.2 Gestor de repositorios Git: GitLab

GitLab es un gestor de repositorios basado en web, con funcionalidades muy similares al más conocido

GitHub.

A diferencia de este, GitLab ofrece a sus usuarios la posibilidad de generar infitios repositorios privados de

forma totalmente gratuita. Además, al ser de código abierto, cualquiera puede instalarse dicho gestor en un

servidor propio, funcionando de forma totalmente indendiente.

Desde su interfaz web podemos gestionar todos nuestros proyectos (repositorios) y realizar multitud de

funciones: modificar su visibilidad, añadir nuevos desarrolladores con distintos permisos, analizar de forma

visual el detalle de cada commit o rama del proyecto, crear y mantener una wiki del proyecto con información

detallada del mismo, etc.

Figura 6-1. Vistazo a un commit en GitLab.

Page 43: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

27

7 PROGRAMACIÓN

En esta sección se hablará de todo lo relativo a la programación propiamente dicha de la aplicación que da

origen a esta memoria. Comenzaremos con la aplicación Android, hablando de su interfaz y funcionalidad,

estructura de archivos, clases juto a su finalidad y recursos utilizados. Seguidamente se hablará del programa

desarrollado para ser ejecutado en la placa Arduino, con lo cual se cerraría la comunicación Bluetooth entre

dispositivo móvil y robot.

7.1 Aplicación móvil para dispositivos Android

7.1.1 Interfaz y funcionamiento

Nada más pulsar el icono de la aplicación se lanzará una primera pantalla, típicamente llamada Splash Screen,

que no es más que una primera introducción mostrada mientras se finaliza la carga de la interfaz de la

actividad principal.

Figura 7-1. Interfaz de la aplicación.

Splash Screen.

Figura 7-2. Interfaz de la aplicación.

Actividad principal – sin conexión.

En la Figura de la derecha se puede apreciar la interfaz ofrecida al usuario. Se trata de algo muy simple: tres

items informativos en la parte superior (estado de la conexión, dirección de movimiento y tiempo de bucle8 del

robot), dos opciones de control en la barra intermedia y una zona inferior vacía, destinada principalmente a

8 Tiempo que tarda la placa Arduino en completar el programa dentro de la estrcutra loop(). Es uno de los varios parámetros devueltos por el robot via Bluetooth.

Page 44: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Programación

28

contener el joystick virtual o bien dar detalles del acelerómetro.

En el lateral izquierdo nos encontramos con un menú cuyo objetivo es dirigir al usuario en el proceso de

conexión con el vehículo mediante Bluetooth.

Figura 7-3. Interfaz de la aplicación.

Menú lateral – activar Bluetooth.

Figura 7-4. Interfaz de la aplicación.

Solicitud de activación de la conectividad

Bluetooth.

Si la conectividad Bluetooth está desactivada en el terminal móvil, la aplicación ofrecerá la opción de habilitar

dicha conectividad. Nótese que pulsar en el botón “Activar” no hará que se active la conexión, sino que la

aplicación lanzará una petición (intent) al Sistema Android para realizar dicho proceso; una petición que

tendremos que responder nosotros como usuarios.

Si por el contrario ya teníamos activada dicha conectividad o aceptamos su activación, en este menú se

mostrará un listado con los dipositivos Bluetooth actualmente enlazados o vinculados.

Figura 7-5. Interfaz de la aplicación. Menú lateral – dispositivos vinculados.

Page 45: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

29

29 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

Para iniciar la conexión basta con pulsar sobre uno de los dispositivos, identificándolo mediante su nombre o

MAC.

Si el dispositivo en cuestión no hubiese sido vinculado previamente, bajo esta lista estará la opción de ir a los

ajustes del Sistema Android y desde ahí vincular el dispositivo remoto.

Una vez establecida la conexión, nuevamente, se actualizará el menú que pasará a mostar los datos de la

conexión actual junto a la opción de finalizarla.

Figura 7-6. Interfaz de la aplicación. Menú lateral – conexión.

Durante este proceso el estado de la conexión Bluetooth será mostrado y actualizado igualmente en la pantalla

principal, terminando con el estado “conectado” al final del proceso.

Con la conexión establecida solo resta seleccionar un método de control, joystick o acelerómetro, y proceder a

dirigir el vehículo.

Figura 7-7. Interfaz de la aplicación. Actividad principal – conexión.

Page 46: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Programación

30

Figura 7-8. Interfaz de la aplicación.

Joystick.

Figura 7-9. Interfaz de la aplicación.

Acelerómetro.

Por último, pulsando en el icono de la llave inglesa situado en la parte superior derecha se lanzaría la pantalla

de ajustes de la aplicación.

Estos ajustes modifican varaibles de la ley de control implementada en la placa Arduino así como el UUID9

Bluetooth y la frecuencia de muestreo del acelerómetro.

Figura 7-10. Interfaz de la aplicación. Menú de ajustes.

9 Universally Unique Identifier (UUID)

Page 47: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

31

31 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

7.1.2 Preliminares. Archivo manifiesto: AndroidManifest.xml

El archivo AndroidManifest.xml podría ser un buen punto de partida para realizar un primer acercamiento a la

aplicación. Aquí se define, por así decirlo, el esqueleto de la aplicación: los permisos solicitados, su nombre,

su icono, el tema aplicado y lo más importante las actividades que la componen y sus características.

En mención al autor original del vehículo autoequilibrado se mantuvo el nombre de la aplicación como “Mini

Segway”, pasando el paquete a llamarse “com.daniel.minisegway”.

Dado que la aplicación hará uso de la conectividad Bluetooth del dispositivo Android se ha de marcar tal

permiso como necesario. En este caso se precisan dos permisos relativos a Bluetooth:

<uses-permission android:name="android.permission.BLUETOOTH" />

<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

El primer permiso se necesita para poder establecer conexión con un dispositivo previamente emparejado. El

segundo posibilita la búsqueda de dispositivos y el emparejamiento con ellos.

Volviendo a la aplicación, su icono, nombre y tema se asignan como sigue:

<application

android:allowBackup="true"

android:icon="@mipmap/ic_launcher"

android:label="@string/app_name"

android:supportsRtl="true"

android:theme="@style/MyAppTheme">

...

</application>

Cabe destacar que se están utilizando referencias a recursos en esas líneas. Por ejemplo, el texto

@mipmap/ic_launcher se refiere a un archivo múltiple, dependiente de la resolución del dispositivo,

localizado en el directorio /res/mipmap. Otro ejemplo sería el texto @string/app_name, el cual se refiere a un

ítem dentro del archivo /res/values/strings.xml el cual se corresponde con el nombre de la aplicación en este

caso.

Page 48: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Programación

32

Dentro de la etiqueta application se listan las actividades que forman la aplicación, en este caso, tres

actividades:

<activity

android:name="com.daniel.minisegway.SplashScreenActivity"

android:screenOrientation="portrait"

android:theme="@style/MyAppTheme.SplashTheme">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

<activity

android:name="com.daniel.minisegway.MainActivity"

android:label="@string/app_name"

android:screenOrientation="portrait"

android:theme="@style/MyAppTheme.NoActionBar">

<intent-filter>

<action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />

</intent-filter>

</activity>

<activity

android:name="com.daniel.minisegway.Preferences"

android:label="@string/app_name"

android:screenOrientation="portrait">

<!--android:parentActivityName=".MainActivity" >-->

<intent-filter>

<action android:name="sekth.droid.Preferences.AppPreferenceActivity" />

<category android:name="android.intent.category.DEFAULT" />

</intent-filter>

</activity>

En cada una de las actividades se especifica:

Su “nombre”, o más concretamente la clase que define a la actividad.

Una etiqueta asociada a la actividad, usualmente mostrada en la barra superior como título.

La orientación de la pantalla, que en este caso se fuerza a ser vertical.

El tema aplicado.

Los intent10, o tipos de estos, a los que responde la actividad.

Sobre los intents, la primera de las actividades se declara como principal o inicial, y es a la que se tendrá

acceso desde el Launcher del dispositivo móvil (es decir, será la que se ejecute tras pulsar en el icono de la

aplicación). La segunda será receptora de cambios en la conectividad bluetooth del terminal. Y finalmente la

tercera se declara como actividad relativa a una sección de ajustes.

10 Descripción abstracta de una operación a realizarse. El ejemplo más simple es la solicitud de lanzar una actividad.

Page 49: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

33

33 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

7.1.3 Actividades

Como se adelataba, la aplicación consta de tres actividades: SplashScreenActivity, MainActivity y Preferences.

7.1.3.1 SplashScreenActivity

Esta es la actividad lanzada tras pulsar el icono de la aplicación. Su función es muy sencilla: mostrar una

pantalla simple y atractiva mientras se espera a la carga de la actividad principal.

No se lleva a cabo ningún tipo de proceso, tan solo se lanza un intent con el objetivo de iniciar la actividad

principal. Completado dicho objetivo la actividad SplashScreenActivity es destruida.

7.1.3.2 MainActivity

Esta es la actividad principal, con la que el usuario podrá interactuar, gestionar la conexión Bluetooth y tomar

control del robot.

Es en esta actividad donde se instancian todas las clases utilizadas y se permite la interacción entre ellas.

7.1.3.3 Preferences

Por último encontramos la actividad que controla el menú de opciones. Se trata de una actividad simple, que

sigue las guias ofrecidas por Google a la hora de crear un listado de ajustes u opciones en una aplicación.

7.1.4 Clases Java

Dado que en Java toda funcionalidad se encuentra encapsulada en una clase, incluidas las actividades, el

objetivo de esta sección será listar y detallar las once clases que conforman la aplicación, comenzando con una

breve descripción y prosiguiendo con el detalle de sus atributos y métodos:

Bluetooth – Clase contenedora de todas las operaciones relativas a la conexión bluetooth.

ReceivedData – Clase contenedora y constructora del mensaje recibido por la aplicación via Bluetooth

desde el robot.

Message – Clase contenedora y constructora del mensaje enviado por la aplicación via Bluetooth

hacia el robot.

Joystick – Clase encargada de la gestión del método de control que usa un Joystick virtual.

Accelerometer – Clase encargada de la gestión del método de control que usa el acelerómetro del

dispositivo Android.

Movement – Clase donde se definen los aspectos relativos al movimiento del robot.

MainActivity – Clase correspondiente a la actividad principal de la aplicación.

Preferences – Clase correspondiente a la actividad que gestiona la sección de ajustes o preferencias de

la aplicación.

SplashScreenActivity – Clase correspondiente a una actividad auxiliar usada para lanzar la actividad

principal

Tools – Clase con un cojunto de herramientas de propósito general

BluetoothDeviceListCustomAdapter – Clase utilizada como adaptador a la hora de rellenar el ListView

que muestra los dipositivos bluetooth disponibles.

Page 50: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Programación

34

7.1.4.1 Bluetooth

La clase Bluetooth es la encargada de efectuar todas las operaciones referentes a la conectividad Bluetooth y es

también la clase receptora de los eventos relativos a dicha conectividad. Por ello, lo primero a destacar es que

esta clase hereda de la clase BroadcastReceiver.

public class Bluetooth extends BroadcastReceiver {

...

}

Continuando con los atributos de la clase y como en todas las demás clases, lo primero en definirse es el ID

asignado a los logs generados por la clase, algo muy útil a la hora de depurar la aplicación. En este caso:

private static final String LOG_ID = "MyApp.Bluetooth";

Tanto en esta como en otras clases que van a interactuar con la actividad principal se almacena el contexto de

la actividad principal (de la actividad MainActivity).

private final Context mainContext;

El resto de atributos son propios de la clase:

private BluetoothAdapter bluetoothAdapter;

private BluetoothSocket socket;

// Bandera para recordar el estado del socket (socket.isConnected() requiere API 14)

private Boolean socketIsConnected = false;

// Registros sobre la conexión existente

private String connectedDeviceName;

private String connectedDeviceMac;

// Valores para los Intent

protected static final String EVENT_GENERATED = BuildConfig.APPLICATION_ID +

".bluetooth.EVENT_GENERATED";

protected static final String EXTRA_EVENT = BuildConfig.APPLICATION_ID +

".bluetooth.EXTRA_EVENT";

// El UUID del modulo bluetooth de Arduino 00001101-0000-1000-8000-00805F9B34FB

private UUID BLUETOOTH_UUID;

// Nombre identificador para la conexión bluetooth

private static final String BLUETOOTH_APP_NAME = "MiApp";

// Codificación utilizada para traducir bytes a String

private static final String BYTE_CODIFICATION = "UTF-8";

La instancia de BluetoothAdapter permite interactuar con el estado de la conexión Bluetooth mientras que la

instancia de BluetoothSocket permite realizar la conexión y trasmitir/recibir datos.

Como se puede ver, por limitar el nivel de API a 13 se ha de gestionar manualmente el estado del socket

usando el flag socketIsConnected de la clase. A partir del nivel de API 14 se puede hacer uso del método

isConnected de la instancia de BluetoothSocket.

Otro detalle interesante son las cadenas de texto que van a definir la etiqueta asociada a los eventos (intents)

generados por esta clase. Estos textos se construyen a partir del nombre del paquete más un texto propio.

El UUID se precisa a la hora de obtener el socket de conexión con el dispositivo remoto. En este caso es una

cadena de texto fija dependiente del módulo Bluetooth conectado a la placa Arduino, aunque como se pudo

ver esta cadena puede ser modifiada desde los ajustes de la aplicación.

Page 51: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

35

35 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

Como se adelantaba, esta clase generará eventos y dichos eventos se corresponden con los siguientes valores:

// Posibles estados del bluetooth

protected static final int ERROR = 0;

protected static final int NOT_SUPPORTED = 1;

protected static final int DISCOVERING = 2;

protected static final int ENABLED = 3;

protected static final int DISABLED = 4;

protected static final int CONNECTED = 5;

// Códigos de estado para la conexión

protected static final int CONNECTION_ATTEMPT_START = 10;

protected static final int CONNECTION_ATTEMPT_END = 11;

protected static final int SERVER_LISTENING_ATTEMPT_START = 12;

protected static final int SERVER_LISTENING_ATTEMPT_END = 13;

protected static final int CONNECTION_ERROR = 14;

protected static final int ERROR_CLOSING_SOCKET = 15;

protected static final int ERROR_CLOSING_SERVER_SOCKET = 16;

protected static final int CONNECTION_LOST = 17;

Todos ellos son valores estáticos y finales, accesibles a nivel de clase, sin precisar una instancia de la misma.

Cada uno define bien un estado de la conectividad Bluetooth o un estado de la conexión.

Los métodos de esta clase son más de los estrictamente necesarios. Esto es así porque originalmente las

pruebas de conectividad y transmisión de datos fueron realizadas conectando dos dispositivos móviles

Android entre si, de ahí la necesidad, por ejemplo, de generar un servidor de escucha Bluetooth.

public Bluetooth (Context context)

@Override

public void onReceive (Context context, Intent intent)

public void initialize ()

public void setUUID (String uuid)

public void connect (String mac, String name)

public void startServer ()

public void send (String data)

public void receive ()

public void closeSocket ()

public Set<BluetoothDevice> getBondedDevices ()

public int getStatus()

public String getConnectedDeviceName ()

public String getConnectedDeviceMac ()

public void enable (Activity activity)

public void enable ()

public void discard ()

private void sendBroadcastEvent (int state)

private void onDataReceived (String data)

El primer método, cuyo nombre es el mismo que la clase, es el constructor y su función aquí no es más que

inicializar el contexto de la actividad principal y obtener el bluetoothAdapter.

Los eventos relativos al cambio de estado en la conectividad Bluetooth son generados por el Sistema Android

y son recibidos y gestionados por el método onReceive. Por su parte, cada vez que se produce un cambio de

los registrados en los atributos anteriormente citados se ejecuta sendBroadcastEvent. Esto hace que se lance un

intent con información del cambio de estado, que como se verá más adelante es capturado por la actividad

principal para actualizar la interfaz de usuario. En cualquier caso, siempre se puede utilizar el método getStatus

para obtener el estado actual.

Recordando que estamos usando Java, los métodos enable son un claro ejemplo de otra característica de este

lenguaje no citada anteriormente: la sobrecarga de métodos. Si se les pasa un parámetro se solicitará, de parte

de la actividad indicada como parámetro, la activación de la conectividad bluetooth. Si no se indica parámetro,

se forzará la activación de la conectividad sin dar posibilidad al usuario de mediar en la decisión.

Page 52: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Programación

36

Para obtener un listado de los dispositivos bluetooth vinculados al terminal se dispone del método

getBondedDevices, que como se puede apreciar devuelve un conjunto o set de instancias BluetoothDevice.

El método connect es el encargado de iniciar el establecimiento de una conexión, usando una de las tareas

asíncronas que veremos más adelante.

Establecida una conexión, se pueden utilizar los métodos send y receive para enviar y recibir datos en forma

de cadenas de texto (nuevamente, aquí se usan tareas asíncronas), así como los métodos

getConnectedDeviceName y getConnectedDeviceMac para obtener el nombre y MAC del dispositivo. Cada

vez que se reciban datos via Bluetooth se pasarán al método privado onDataReceived.

El cierre de la conexión se hace mediante closeSocket, donde se capturará usando un bloque try/catch si la

operación es correcta. Sin embargo, si se desea no solo cerrar la conexión sino deshabilitar la instancia de la

clase, se dispone del método discard.

Además de estos métodos la clase Bluetooth posee cuatro clases privadas encargadas de las tareas asíncronas:

Connect, Server y Receive que heredan de AsyncTask y Send que hereda de Thread.

private class Connect extends AsyncTask<String, Integer, String> {

@Override

protected void onPreExecute() { ... }

@Override

protected String doInBackground(String... params) { ... }

@Override

protected void onProgressUpdate(Integer... info) { ... }

@Override

protected void onPostExecute(String result) { ... }

}

private class Server extends AsyncTask<String, Integer, String> {

...

}

private class Send extends Thread {

...

}

private class Receive extends AsyncTask<String, String, String>

...

}

Todas estas tareas deben ser asíncronas puesto que su duración es indefinida y no deben bloquear al hilo

principal.

Las tareas (clases) que heredan de AsyncTask implementan los métodos onPreExecute, doInBackground,

onProgressUpdate y onPostExecute, mientras que la tarea que heredan de Thread solo precisa del método run.

Page 53: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

37

37 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

7.1.4.2 ReceivedData

El objetivo de esta clase es analizar los datos recibidos via Bluetooth y distribuir los valores obtenidos en las

variables correspondientes. Para ello se definen los siguientes atributos:

private static final String LOG_ID = "MyApp.ReceivedData";

/* ----- Parámetros enviados por el Arduino ----- */

private float Kp;

private float Ki;

private float K1;

private float K2;

private float K3;

private float c;

private float loop; // Tiempo bucle principal de Arduino (ms)

private int dir;

private float state; // Estado del sistema

private float xg;

private float xt;

// Almacena las cadenas recibidas para construir la cadena útil

private String dataBuffer = "";

// Tamaño máximo del buffer para datos recibidos

private final static int DATA_BUFFER_MAX_SIZE = 400;

// Separadores de parámetro y cadena utilizados en la RECEPCIÓN bluetooth

private final static String PARAMETER_SEPARATOR = ",";

private final static String CHAIN_SEPARATOR = "^";

private final static int NUMBER_OF_PARAMS_PER_CHAIN = 11;

// Expresión regular para filtrar las cadenas catalogadas como útiles

private final static String REGEX_FILTER = "[0-9,.^-]+";

Aquí se pueden apreciar las variables donde se almacenarán los parámetros extraidos de las cadenas de texto

recibidas. Además, en dichas cadenas se espera encontrar once parámetros separados por comas entre sí, con el

carácter “^” como fin/inicio de cadena.

Para poder analizar las cadenas de texto recibidas, estas se almacenan provisionalmente en dataBuffer, una

cadena de texto de tamaño fijo predefinido a 400 caracteres. Esto se hará siempre y cuando pasen por la

expresión regular usada como filtro, en este caso aceptando únicamente números o los símbolos “.”, “,”, “^” o

“-”.

Para esas operaciones se disponen los siguientes métodos:

public void decrypt (String data)

public float getLoopTime ()

public float getState ()

Con getLoopTime y getState se permite obtener los valores de los atributos loop y state respectivamente,

mientras que el método decrypt es el encargado de tratar las cadenas de texto recibidas como se ha indicado

anteriormente, obteniendo los valores útiles de las variables.

Page 54: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Programación

38

7.1.4.3 Message

Mientras que en la clase anterior se gestionaban los mensajes recibidos, en esta se generan los mensajes

(cadenas de texto) que serán enviados como ordenes a la placa Arduino.

En los atributos de la clase volvemos a encontrarnos con un esquema similar.

private static final String LOG_ID = "MyApp.Message";

// Contexto de la actividad principal

private Context mainContext;

/* ----- Parámetros que componen el mensaje ----- */

private float xg; // Velocidad de giro

private float xt; // Velocidad de traslación

private float Kp; // Constante del motor (1)

private float Ki; // Constante del motor (2)

private float K1; // Constante LQR (1)

private float K2; // Constante LQR (2)

private float K3; // Constante LQR (3)

private float c; // Constante global del LQR

private int dir; // Variable para la dirección (1-9)

// Separadores de parámetro y cadena utilizados en la TRANSMISIÓN bluetooth

private final static String PARAMETER_SEPARATOR = ",";

private final static String START_OF_COMMAND = "<";

private final static String END_OF_COMMAND = ">";

En este caso tenemos una colección de variables que serán utilizadas en las órdenes de control y un conjunto

de parámetros que definen la estructura de los mensajes de control. Así, dichos mensajes estarán compuestos

por nueve parámetros separados por comas y distribuidos en una cadena de texto que comienza con el carácter

“<” y termina con el carácter “>”.

Los parámetros enviados serán: velocidad de rotación (xg), velocidad de traslación (xt), constantes del motor

(Kp y Ki), constantes del LQR (K1, K2, K3 y c) y dirección de movimiento (dir).

Entre sus métodos:

public Message (Context context)

public void send ()

public void sendStopOrder ()

public void setMovement (float xg, float xt, int dir)

public void setLQR (float Kp, float Ki, float K1, float K2, float K3, float c)

Obviando el constructor, que simplemente inicializa los atributos, nos encontramos con dos métodos para

enviar mensajes y dos métodos para modificar los valores de los atributos.

El método send hace uso de la instancia de la clase Bluetooth localizada en la actividad principal para enviar el

mensaje. Esto se puede hacer gracias a que dicha instancia se cataloga como static y por tanto se puede

acceder a ella a nivel de Clase.

Los métodos setMovement y setLQR alteran los valores de los atributos del mensaje, de forma que si hay

cambio alguno se ejecuta inmediatamente el método send, enviando los nuevos valores al robot.

El método sendStopOrder por su parte, hace uso del método setMovement para establecer el reposo como

estado objetivo y si este es distinto del estado previo dicha orden será enviada via Bluetooth.

Page 55: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

39

39 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

7.1.4.4 Joystick

El Joystick es uno de los dos métodos de control disponibles. Este se entiende como un espacio de la pantalla

reservado para realizar movimientos o desplazamientos con un dedo u objeto capaz de interactuar con la

pantalla. Dado que esta clase necesita recibir las acciones realizadas sobre la pantalla táctil del dispositivo, es

necesario implementar la interfaz OnTouchListener.

public class Joystick implements OnTouchListener {

...

}

En cuanto a sus atributos de clase:

private static final String LOG_ID = "MyApp.Joystick";

// Contexto de la actividad principal

private Context mainContext;

// Layout donde se representará el joystick

private ViewGroup joystickLayout;

// Objetos a representar

private Stick stick;

private Border border;

// Tinte a emplear

private Paint stickPaint;

private Paint borderPaint;

// Posición central del joystick en el layout

private float joystickX;

private float joystickY;

// Posición del "stick" relativa al centro del joystick (coordenadas polares)

private float stickLength;

private double stickAngle;

// Medidas del joystick

private int stickRadius;

private int borderRadius;

// Longitud del "stick" a partir de la cual se descarta el estado de reposo

private int restBreakPoint;

Como se decía, se precisa un espacio reservado para este joystick virtual. Dicho espacio se entenderá como

una instancia de la clase ViewGroup, clase base para layouts y contenedores de vistas.

Se definen también los dos elementos que formarán el joystick: el puntero o stick y el borde o círculo que

limita el movimiento del puntero. Cada uno es en sí una instancia de la clase Stick y Border respectivamente.

La información sobre el color y estilo usados en su representación es contenida en instancias de la clase Paint.

El resto de parámetros se corresponden con posiciones relativas a la vista y a su centro, usadas para el cálculo

de la dirección solicitada, así como velocidad de traslación y/o rotación.

El último de los atributo, restBreakPoint, define la separación mínima que ha de alcanzarse respecto al centro

del joystick para considerarse que se sale del estado de reposo.

Page 56: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Programación

40

Esta clase dispone de dos constructores. El primero está diseñado para indicar expresamente todos los

parámetros del joystick: contexto de la actividad principal, vista donde se situará, radio del puntero, radio de la

circunferencia límite y la separación mínima para salir del reposo. El segundo constructor calcula las

dimensiones anteriores a partir de las dimensinoes de la vista y es el que finalmente se ha utilizado para tener

un joystick de tamaño proporcional a la pantalla donde se representa.

public Joystick(Context context, ViewGroup joystickLayout, int stickRadius, int

borderRadius, int restBreakPoint)

public Joystick(Context context, ViewGroup joystickLayout)

Continuando con sus métodos tenemos:

private void initialize()

public void disable ()

public boolean onTouch (View view, MotionEvent event)

private void analyzeMeasures()

private void draw (View view)

Los dos primeros a destacar son initialize y disable. El primero se encarga de crear las instancias de Stick y

Border acordes a los parámetros previamente definidos e iniciar la escucha de eventos en la pantalla táctil, que

serán recibidos por esta misma clase. El segundo destruye las vistas que hayan podido generarse y deshabilita

la escucha.

Cada vez que se recibe un evento se ejecuta onTouch; método que implementa el definido en la interfaz

OnTouchListener. Aquí se distingue entre el primer contacto con la pantalla, un desplazamiento sobre esta o

una perdida de contacto, dibujando el joystick en su posición, desplazándolo o eliminándolo respectivamente.

Para dibujar estos elementos se utiliza el método draw, pasándole como parámetro el ítem a dibujar (stick o

borde).

Cada vez que se recibe un evento relativo al desplazamiento sobre la pantalla se lanza el método

analyzeMeasures. Es aquí donde se analiza el desplazamiento relativo del stick o puntero respecto al punto

central inicial donde se dibujó el joystick cuando se pulsó por primera vez en la pantalla.

Por su parte, las clases Stick y Border heredan de la clase View, ya que al fin y al cabo son ítems que se

pretende dibujar en la pantalla. Para ello implementan dos métodos: onDraw y setPosition. El primero dibuja

un círculo con cierto radio y color y el segundo ajusta el centro de dicho círculo.

private class Stick extends View {

private float x;

private float y;

private Stick(Context context) { ... }

public void onDraw (Canvas canvas) { ... }

public void setPosition (float x, float y) { ... }

}

private class Border extends View {

private float x;

private float y;

private Border(Context context) { ... }

public void onDraw (Canvas canvas) { ... }

public void setPosition (float x, float y) { ... }

}

Page 57: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

41

41 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

7.1.4.5 Accelerometer

El segundo método de control consiste en hacer uso del acelerómetro disponible en el terminal Android para

calcular una dirección de movimiento, así como una velocidad de traslación y/o rotación. Por ello,

nuevamente, necesitamos implementar una interfaz, en este caso SensorEventListener.

La implementación de esta interfaz nos da las herramientas (métodos) necesarios para recibir valores de los

distintos sensores disponibles en la plataforma de ejecución. En este caso únicamente nos centraremos en

valores proporcionados por el acelerómetro.

public class Accelerometer implements SensorEventListener {

...

}

Los atributos de esta clase son los que siguen:

private static final String LOG_ID = "MyApp.Accelerometer";

// Contexto de la actividad principal

private final Context mainContext;

// Layout donde representar gráficos del acelerómetro

private ViewGroup accelerometerLayout;

// TextView donde mostrar el refresco de los datos del acelerómetro

private TextView sensorDelay;

private SharedPreferences sharedPreferences;

// Gestor del sensor

protected SensorManager sensorManager;

protected Sensor sensor;

// Flag para determinar si se ha realizado una calibración previa

private boolean calibrated = false;

// Datos obtenidos del acelerómetro

private float gravity_x;

private float gravity_y;

private float gravity_z;

// Posición de reposo introducida por el usuario

private float gravity_x_anchor;

private float gravity_y_anchor;

private float gravity_z_anchor;

// Almacena el tiempo en ms en que se ha recibido respuesta del sensor

private long timeStamp;

// Incremento necesario en los valores de gravedad para determinar el cambio en la

dirección

private static final float sensitivity = 1.5f;

// Valor de la gravedad (m/s^2)

private static final float gravity = 9.81f;

// Constante de tiempo para el filtro paso de baja (milisegundos)

private static final int lowPassFilterTimeConstant = 500000;

Al igual que sucedía con el joystick, necesitamos un layout o vista donde situar el control por acelerómetro,

aunque en este caso dicha vista tendrá un uso diferente al anteriormente visto (no vamos a dibujar nada).

Aquí aparece por primera vez la clase SharedPreferences, que se emplea para acceder a los ajustes de la

aplicación. Como vimos en los detalles de la interfaz, la aplicación tiene una pantalla de ajustes y entre estos

ajustes está la tasa de refresco del acelerómetro; un valor que será utilizado en esta clase.

Page 58: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Programación

42

Las medidas inerciales dadas por Android se distribuyen en tres ejes:

Eje X – Paralelo a la pantalla y perpendicular a los laterales.

Eje Y – Paralelo a la pantalla y a los laterales del dispositivo.

Eje Z – Perpendicular a la pantalla del dispositivo.

Figura 7-11. Ejes de las medidas inerciales en Android.

Por ello tendremos tres variables que almacenarán los valores instantáneos y otras tres para almacenar los

valores del punto de referencia. Por defecto se parte de que no se ha “calibrado” el acelerómetro, es decir, no

se tiene una referencia a partir de la cual calcular una diferencia. Esta “calibración” se realiza ejecutando el

método setReference, el cual almacena las medidas actuales como punto de referencia.

Cabe mencionar que, en general, las medidas obtenidas tienden a ser muy dispares. Por ello se usará un filtro

paso de baja a la hora de actualizar los valores finales de la medición.

public Accelerometer (Context context, ViewGroup accelerometerLayout)

private void initialize ()

public void disable ()

@Override

public void onAccuracyChanged (Sensor sensor, int accuracy)

@Override

public void onSensorChanged (SensorEvent event)

private void analyzeMeasures ()

public void setReference()

Como sucedía con el joystick se tiene un método, initialize, para iniciar la escucha de eventos y otro método,

disable, para deshabilitarla.

Gracias a implementar la interfaz SensorEventListener podemos usar los métodos onAccuracyChanged y

onSensorChanged, los cuales son lanzados cuando se modifica la precisión del sensor y cuando se tiene una

nueva medida, respectivamente.

También volvemos a tener el método analyzeMeasures que cumple una función similar al situado en la clase

Joystick; en este caso, traducir las medidas inerciales junto al punto de referencia en una dirección y velocidad

de traslación y/o rotación.

Page 59: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

43

43 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

7.1.4.6 Movement

El objetivo de las clases Joystick y Accelerometer es proporcionar un mecanismo de control y, en definitiva,

proporcionar una dirección de movimiento y una velocidad relativa de traslación y rotación. Para unificar estas

medidas se tiene la clase Movement.

private static final String LOG_ID = "MyApp.Movement";

// Valor mínimo que pueden tomar la velocidad de traslación o de rotación,

respectivamente

protected static final int MIN_TRANSLATION_SPEED = 0;

protected static final int MIN_TURN_SPEED = 0;

// Valor máximo que pueden tomar la velocidad de traslación o de rotación,

respectivamente

protected static final int MAX_TRANSLATION_SPEED = 4;

protected static final int MAX_TURN_SPEED = 4;

// Posibilidades de movimiento aceptadas por Arduino

protected static final int FORWARD_PLUS_LEFT = 1;

protected static final int FORWARD = 2;

protected static final int FORWARD_PLUS_RIGHT = 3;

protected static final int TURN_LEFT = 4;

protected static final int STILL = 5;

protected static final int TURN_RIGHT = 6;

protected static final int BACKWARD_PLUS_LEFT = 7;

protected static final int BACKWARD = 8;

protected static final int BACKWARD_PLUS_RIGHT = 9;

Aquí se definen las velocidades mínimas y máximas de traslación y rotación, junto a las nueve posibles

direcciones originalmente establecidas. Como se verá más adelante, dichas direcciones pasarán a ser

redudantes y serán ignoradas por el robot tras actualizarse su programa.

Los dos métodos que ofrece permiten traducir una velocidad relativa entre 0.0 y 1.0 a una velocidad útil de

cara a las órdenes, entre el mínimo y el máximo posible.

public static float getTranslationSpeed (float relativeTranslationSpeed)

public static float getTurnSpeed (float relativeTurnSpeed)

Page 60: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Programación

44

7.1.4.7 MainActivity

La clase MainActivity compone la actividad principal de la aplicación. Dado que hablamos de una aplicación,

esta clase debe heredar de alguna de las clases propias de una actividad, en este caso, de AppCompatActivity.

Se selecciona tal clase para heredar de ella porque es la clase base para actividades con características

incorporadas en su action bar, siendo esta la sección superior de la aplicación – la barra que contiene el título

de la aplicación y otras herramientas como en este caso el acceso a las opciones.

public class MainActivity extends AppCompatActivity {

...

}

Además de los atributos vistos anteriormente, esta clase, una vez instanciada, contiene las instancias de todos

los elementos de la interfaz de usuario con los que el usuario puede interactuar: vistas o layouts y widgets. Esto

es así debido a que en Android solo el hilo principal de la aplicación puede realizar modificaciones sobre la

UI11. El resto de clases, o instancias de las mismas, utilizan el contexto principal para interactuar con dichos

elementos si así fuese necesario.

private static final String LOG_ID = "MyApp.MainActivity";

private SharedPreferences sharedPreferences;

// Receptor de anuncios enviados a difusión

private BroadcastReceiver localBroadcastReceiver;

// Ventana lateral

private DrawerLayout drawer;

// Vistas dinámicas

private ViewAnimator bluetoothViewAnimator;

private ViewAnimator controlViewAnimator;

// Vistas de los ViewAnimator

private final static int BLUETOOTH_VIEW_ANIMATOR_DISABLED = 0;

private final static int BLUETOOTH_VIEW_ANIMATOR_ENABLED = 1;

private final static int BLUETOOTH_VIEW_ANIMATOR_CONNECTED = 2;

private final static int CONTROL_VIEW_ANIMATOR_JOYSTICK = 0;

private final static int CONTROL_VIEW_ANIMATOR_ACCELEROMETER = 1;

private final static int CONTROL_VIEW_ANIMATOR_NO_CONTROL_METHOD = 2;

// Máximo de dispositivos a listar

private static final int MAX_BLUETOOTH_DEVICES_TO_LIST = 3;

// Layouts

private RelativeLayout joystickLayout;

private RelativeLayout accelerometerLayout;

// Widgets

private ListView bondedDevicesList;

private ProgressBar progressBar;

private TextView connectedDeviceName;

private TextView connectedDeviceMac;

private TextView bluetoothStatus;

protected static TextView direction;

protected static TextView arduinoLoop;

private RadioGroup controlTypeSelector;

11 User Interface o Interfaz de Usuario.

Page 61: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

45

45 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

Esta actividad usa dos ViewAnimator en su interfaz para condensar la información en la pantalla. Estos no son

más que clases que permite animar la transición entre las vistas que contiene. Uno de ellos se emplea en la

pantalla principal, para alternar la entre control via joystick y control via acelerómetro. El otro se utiliza en el

menú lateral izquierdo y alterna su contenido desde la solicitud de activación de la conectividad Bluetooth,

hasta la visualización del estado de la conexión, pasando por el listado de dispositivos emparejados.

Para la gestión de todo el sistema se emplean cinco objetos, instancias de las clases vistas hasta el momento:

// Objetos para la gestión del sistema

protected static Bluetooth bluetooth;

protected static Message message;

protected static ReceivedData receivedData;

protected Accelerometer accelerometer;

protected Joystick joystick;

Cabe destacar que bluetooth, message y receivedData son de tipo static puesto que solo se instancian una

única vez al cargarse la aplicación y de este modo se permite su acceso desde otras clases.

@Override

public void onCreate(Bundle savedInstanceState) { ... }

El punto crítico de esta clase es la creación de la actividad. En este método, onCreate, se llevan a cabo los

siguientes procesos en el siguiente orden:

1. Se establece el layout de la actividad.

2. Se fuerza a mantener activa la pantalla.

3. Se obtiene el objeto sharedPreferences necesario para interactuar con los valores dados a los ajustes

de la aplicación.

4. Se asigna como ActioBar la barra de herramientas con el botón para acceder a los ajustes.

5. Se configura el menú lateral izquierdo para que actúe como tal.

6. Se configuran los dos viewAnimator.

7. Se obtiene la referencia a los layouts del joystick y acelerómetro, para posteriormente instanciar en

ellos dichos “widget”. Se asignan también todos los widgets de la vista a su atributo correspondiente

en la clase para facilitar su acceso más adelante.

8. Usando el atributo Build.VERSION.SDK_INT se comprueba si el nivel de API del Sistema Operativo

destino es inferior a 21; en dicho caso se fuerza a que el texto de los botones pase a minúsculas. Esto

se requiere nuevamente porque hasta ese nivel de API el texto de los botones se muestra en

mayúsculas por defecto.

9. Se crean las instancias de las clases Bluetooth, Message y ReceivedData y se inicializa el objeto

bluetooth.

10. Se inicializa el listado de dispositivos bluetooth vacío y se asocia un listener ante las pulsaciones en

sus ítems, el cual lanzará el método connect de la clase Bluetooth, usando los datos del dispositivo

Bluetooth sobre el que se pulsó.

11. Se asocia un listener ante la pulsación sobre el estado de la conexión Bluetooth. Esto abrirá el menú

lateral para proseguir con la conexión o ver su estado detallado.

12. Se asocia un listener ante la pulsación sobre alguno de los botones para el cambio de método de

control.

13. Finalmente, se crea y registra un receptor de mensajes a difusión locales, localBroadcastReceiver,

para recibir cambios en el estado de la conectividad bluetooth. Estos cambios serán lanzados por la

instancia generada de la clase Bluetooth. Ante dichos cambios se ejecutará el método

onBluetoothEventReceived indicando el código del evento.

Page 62: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Programación

46

Si continuamos con los métodos propios de la actividad nos encontramos con onResume y onDestroy.

@Override

protected void onResume()

@Override

protected void onDestroy()

Tras crearse la aplicación, o bien regresar de un segundo plano, se ejecuta el método onResume. Aquí se fuerza

siempre a obtener el estado de la conexión Bluetooth para poder actualizar la interfaz de usuario según

corresponda. Además, se actualizan todos los valores que encontramos en la pantalla de ajustes.

Al destruirse la aplicación, o más bien justo antes de ser destruida, se desactiva completamente la clase y se

deja de estar a la escucha de eventos procedientes de ella.

Los métodos asociados a los botones que encontramos en el menú lateral son: buttonBluetoothSettings,

buttonEnableBluetooth y buttonCloseConnection. Cada uno hace lo que se puede deducir: lanzar los ajustes

del Sistema Android relativos a la conectividad Bluetooth, solicitar al sistema la activación de la conectividad

Bluetooth y cerrar la conexión, respectivamente.

public void buttonBluetoothSettings(View view)

public void buttonEnableBluetooth(View view)

public void buttonCloseConnection(View view)

La actualización del listado de dispositivos, la selección de un método de control y la desactivación del mismo

se realiza usando los métodos: updateBondedDevicesList, updateControlLayout y disableControls.

Aquí se ha de incidir en que deshabilitar los métodos de control, disableControls, no solo impide al usuario

controlar el robot sino que le envía la orden de volver al estado de reposo.

private void updateControlLayout()

private void disableControls()

public void updateBondedDevicesList()

Como se mencionó, la interfaz va actualizandose conforme se reciben ciertos eventos o sucesos desde el objeto

bluetooth. El método onBluetoothEvenReceived analiza el evento y actúa frente a él; así por ejemplo cuando se

inicia la conexión se activa una barra de progreso y cuando finaliza se oculta además de mostrarse un mensaje

de éxito o error.

Por otro lado la aplicación también responde a otro tipo de eventos: cambios en el foco de la interfaz principal.

El foco se pierde no solo tras enviar la aplicación a un segundo plano, también al abrir el menú lateral o

desplegar la barra de estado del propio Sistema Android; en definitiva, cualquier suceso que “oculte” la

interfaz principal.

Siempre que se pierde el foco se deshabilitan los métodos de control. Esto se hace para evitar enviar órdenes

de control erróneas al robot mientras, por ejemplo, desplegamos la barra de notificaciones de nuestro terminal

para ver un mensaje o accedemos a los ajustes de la aplicación.

private void onBluetoothEventReceived(int event)

public void onWindowFocusChanged(boolean hasFocus)

Page 63: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

47

47 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

Tras estos nos encontramos con tres métodos creados usando la guía de Android y cuyo objetivo es generar y

responder ante eventos en el menú de opciones. Un menú que en este caso está formado únicamente por un

ítem del cual solo se ve su icono: la llave inglesa en la parte superior derecha de la interfaz.

@Override

public boolean onCreateOptionsMenu(Menu menu)

@Override

public boolean onOptionsItemSelected(MenuItem item)

Por último queda una pequeña funcionalidad para la cual se sobreescribe el método onBackPressed, de modo

que su acción por defecto (salir de la aplicación) no se ejecute si tenemos abierto el menú lateral. En dicho

caso, en lugar de salir de la aplicación se cerrará tal menú.

@Override

public void onBackPressed()

Page 64: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Programación

48

7.1.4.8 Preferences

Esta clase forma la tercera actividad que encontramos en la aplicación y es la encargada de administrar la

pantalla de opciones ofrecida por esta.

Para ello lo primero es heredar de la clase PreferenceActivity, que es la clase base para una actividad que

pretende mostrar una colección de opciones al usuario

Adicionalmente es necesario implementar la interfaz OnSharedPreferenceChangeListener, a fin de actuar

inmediatamente cada vez que un ajuste es modificado por el usuario.

public class Preferences extends PreferenceActivity implements

OnSharedPreferenceChangeListener {

...

}

Esta vez solo tenemos un atributo, la mencionada etiqueta para los logs de la clase:

private static final String LOG_ID = "MyApp.Preferences.java";

Dando un vistazo a los métodos nos encontramos:

@Override

public void onCreate(Bundle savedInstanceState)

// Listener llamado ante la modificación de algún parámetro de ajuste

public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String

key)

private void initSummary(Preference p)

private void updatePrefSummary(Preference p)

protected void setValue (SharedPreferences sharedPreferences, String key, String

value)

@Override

protected void onResume()

@Override

protected void onPause()

Como se trata de una actividad, se ha de implementar obligatoriamente el método onCreate. En él se establece

el layout de la actividad y se inicializa el listado de preferencias con sus valores almacenados usando el

método initSummary.

Cada vez que se altera el valor de un ajuste se lanzará el método onSharedPreferenceChanged, el cual

determina el ajuste que ha sido editado y si no es nulo actualiza el valor usando updatePrefSummary.

Los métodos onResume y onPause son utilizados para habilitar y deshabilitar la escucha de cambios en los

ajustes, evitando el uso de recursos del sistema si la actividad de ajustes no es interactiva de cara al usuario.

Page 65: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

49

49 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

7.1.4.9 SplashScreenActivity

La clase SplashScreenActivity define la primera de las actividades de la aplicación y con ello la más simple. Su

código se resume como sigue:

public class SplashScreenActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

startActivity(new Intent(this, MainActivity.class));

finish();

}

}

Su única función es ser ejecutada e inmediatamente lanzar la actividad principal, destruyéndose a sí misma en

el proceso.

Esto, que en un principio no tiene sentido, se hace simplemente para mostrar al usuario la “vista” asociada a

esta actividad; una vista que está vacía pero gracias al tema aplicado hace que se muestre inmediatamente una

imagen de fondo formada por un bitmap personalizado.

Page 66: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Programación

50

7.1.4.10 Tools

El objetivo inicial de esta clase era contener todas aquellas funciones auxiliares que puediesen ser necesarias

pero sin tener un propósito específico que las ligase a alguna de las clases anteriores. Al final solo úna función

llegó a cumplir dichos requisitos de generalidad:

public static boolean setListViewHeight(ListView listView, int maxItemsToShow)

Su utilidad se basa en, dado una instancia de la clase ListView, modificar el alto de su vista asociada para

mostrar exactamente la cantidad de ítems indicada.

El único uso que se hace de esta función es a la hora de listar los dispositivos bluetooth vinculados con el

terminal. De este modo en el listado solo se verá completamente la información de maxItemsToShow

dispositivos (tres en el programa actual), necesitando hacer scroll para visualizar el resto.

Page 67: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

51

51 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

7.1.4.11 BluetoothDeviceListCustomAdapter

Cuando se listan los dispositivos Bluetooth vinculados con el terminal móvil se hace usando una instancia de

ListView. Un ListVIew define un grupo de vistas que muestra una lista de elementos desplazables, donde los

elementos de la lista son insertados con un Adapter que toma a su vez el contenido de una determinada fuente.

Esta clase define el Adapter personalizado usado para rellenar la lista de dispositivos Bluetooth. Por ello la

clase hereda de BaseAdapter, que como se puede deducir es la clase base o común a la hora de crear un

Adapter.

public class BluetoothDeviceListCustomAdapter extends BaseAdapter {

...

}

Sus atributos son el ya mencionado ID de logs, el conjunto de dispositivos bluetooth a introducir en la lista y el

layout o vista final generada.

private static final String LOG_ID = "MyApp.BLCustomAdapter";

private ArrayList<BluetoothDevice> bondedDevices;

// inflater para crear un view a partir de xml

private static LayoutInflater inflater = null;

En cuanto a sus métodos tenemos:

public BluetoothDeviceListCustomAdapter(Context context, Set<BluetoothDevice>

bondedDevices)

@Override

public int getCount()

@Override

public Object getItem (int position)

@Override

public long getItemId (int position

@Override

public View getView(final int position, View convertView, ViewGroup parent)

El constructor de la clase toma el listado de dispositivos Bluetooth dado y obtiene una instancia de

LayoutInflater usando el contexto recibido como parámetro.

El resto de métodos sobreescriben los heredados de BaseAdapter a fin de dotar a la clase de la funcionalidad

propia de un Adapter. De ellos el interesante es getView, pues es el que toma la vista (archivo xml) que define

un ítem de la lista e introduce en ella el nombre del dispositivo y su MAC, devolviendo la vista generada para

su inserción en el ListView.

El intermediario entre los datos a introducir y la vista final es la denominada clase Holder. Esta no es más que

un contenedor con dos TextView o vistas de texto que serán enlazadas a los correspondientes TextView de la

vista definida en xml para un ítem de la lista.

// Clase para almacenar los datos relativos a una entrada en el ListView

public class Holder {

TextView name;

TextView mac;

}

Page 68: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Programación

52

7.1.5 Recursos

En el directorio res (resources o recursos) del proyecto encontramos los archivos XML que definen las vistas

de la aplicación, así como cualquier otro recurso incorporado a esta más allá del propio código Java: imágenes,

figuras geométricas, estilos, colores, cadenas de texto, valores numéricos (dimensiones), etc. Dentro de este

directorio encontramos un conjunto de sub-directorios que clasifican el contenido:

drawable – Aquí se situan los gráficos usados por la aplicación que son independientes de la densidad

de píxeles.

layout – Contiene los archivos xml que definen las vistas de la aplicación.

menu – Archivos xml que definen menús.

mipmap – Contiene el/los icono/s de la aplicación.

values – Contiene archivos xml que definen distintos tipos de variables: cadenas de texto,

dimensiones, estilos, etc. Encontramos archivos tales como: strings.xml, dimens.xml, styles.xml,

colors.xml, themes.xml, etc.

xml – Archivos xml varios.

7.1.5.1 Drawable

En este directorio nos encontramos con los siguientes archivos:

rounded_button.xml – Define los estilos aplicados a los botones de la aplicación

rounded_button_only_bottom.xml – Define el mismo estilo anterior pero con solo la parte inferior

redondeada.

rounded_button_only_top.xml – Igual que el anterior pero en este caso solo la parte superior está

redondeada

splash_screen_background.xml – Fondo aplicado a la actividad SpashScreenActivity.

ui_background.xml – Define los estilos usados en cada bloque de la aplicación; da un aspecto de

tarjetas a los bloques con esquinas redondeadas y sombra bajo ellos.

segway.png – Imagen en formato png del vehículo autoequilibrado.

7.1.5.2 Layout

Las vistas usadas en esta aplicación son las que siguen:

bluetooth_list_view_device_info.xml – Define la representación en pantalla de cada ítem en el listado

de dispositivos bluetooth.

main_activity_content.xml – Vista principal de la aplicación. Está compuesta por tres bloques: datos

de la conexión y orden de movimiento, botones para seleccionar la opción de control y espacio

reservado para el joystick o información del acelerómetro.

main_activity_app_bar.xml – La vista anterior (main_activity_content.xml) más la barra superior de la

aplicación.

header.xml – Vista de la cabecera (imagen y título) situada en el menú lateral izquierdo.

main_activity_nav.xml – Vista anterior (header.xml) más el resto del menú lateral izquierdo.

main_activity.xml – Vista conjunta de la interfaz principal (main_activity_app_bar.xml) más el menú

lateral izquierdo completo (main_activity_nav.xml). Este es el layout utilizado por la actividad

MainActivity.

plus_less_button.xml – Vista auxiliar utilizada orginalmente para diseñar un widget personalizado, el

cual consta de dos botones que incremente/decrementan un valor mostrado entre ellos.

Page 69: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

53

53 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

7.1.5.3 Menu

Aquí solo encontraremos un archivo, settings.xml, pues esta aplicación solo consta de un menú. Dicho menú es

simplemente el icono de la llave inglesa que encontramos en la parte superior derecha en la interfaz principal

de la aplicación.

7.1.5.4 Mipmap

Aquí está el icono de la aplicación en distintas resoluciones, cargándose uno u otro dependiendo de la densidad

de píxeles de la pantalla del dispositivo donde sea instalada.

Figura 7-12. Icono de la aplicación.

7.1.5.5 Values

Contiene los archivos:

colors.xml – Como se puede deducir del nombre aquí se definen los colores utilizados en la

aplicación.

dimens.xml – Aquí se especifican dimensiones estándar para los ítems o widgets usados en las vistas,

así como tamaños de letra.

strings.xml – Este documento contiene todas las cadenas de texto usadas por la aplicación, ya sea en la

propia interfaz o en mensajes emergentes o Toasts12.

styles.xml – Aquí se definen los temas usados por la aplicación. Cada tema se puede componer de un

gran abanico de opciones que sobreescribirán a los estilos por defecto de, por ejemplo, botones u otros

widgets, colores, etc.

7.1.5.6 Xml

Por último en este directorio encontraremos el archivo preferences.xml. En él se definen los parámetros que se

listarán en la pantalla de ajustes, indicando su tipo y valor por defecto entre otros aspectos. Estos a su vez están

clasificados, en este caso, en dos secciones como se pudo ver en el vistazo a la interfaz.

12 Pequeño mensaje emergente que aparece normalmente en la parte inferior de la pantalla.

Page 70: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Programación

54

7.2 Programa Arduino

El programa estaba formado por tres archivos principales de código fuente:

PI_modificado.ino – Archivo principal del proyecto.

Transf_bluetooth.ino – Define dos funciones: RecepcionBT() y EnvioBT(). Usadas para recibir y

enviar datos a través del puerto serie conectado al módulo Bluetooth.

Mando_Motor.ino – Define la función Mando_Motor(). Esta función se encarga de establecer las

referencias para los motores y controlar con ello la dirección y velocidad de movimiento/rotación del

robot.

Originalmente, la segunda parte de este proyecto solo consistía en modificar el programa ejecutado en la placa

arudino, a fin de que fuese posible la comunicación entre este y la aplicación móvil. Para ello tan solo se

realizaron unas sencillas modificaciones en las funciones RecepcionBT y EnvioBT variando el número de

parámetros que se esperaba recibir y el número de parámetros que se debía enviar.

Sin embargo, futuras decisiones hicieron que se llevase a cabo tres modificaciones:

1. Primero se rediseñó el código fuente del archivo Mando_Motor.ino, a fin de eliminar la dirección

como parámetro de control, usando únicamente la referencia de velocidad de traslación y rotación,

que seguirían siendo de tipo flotante pero ahora con signo (para distinguir entre adelante/atrás e

izquierda/derecha). Esto dio como resultado un nuevo fichero, control_motor.ino, junto a una nueva

función: updateEngineReferences().

2. Seguidamente se crearon funciones intermedias para manipular los valores recibidos via Bluetooth y

aplicar un filtro de paso de baja a las referencias de velocidad recibidas via Bluetooth. Todas ellas se

encuentran en asignacion_variables.ino.

3. Por último se reescribó el archivo Transf_bluetooth.ino, sustituyendo las funciones bloqueantes

Serial.parseInt() y Serial.parseFloat() por otras personalizadas que buscarían realizar la misma

función sin detener potencialmente la ejecución del programa. Este nuevo archivo se llamó

bluetooth.ino.

7.2.1 Detalle de las nuevas rutinas

7.2.1.1 Respuesta a las órdenes de movimiento

Tras eliminar la dirección como parámetro y pasar a tener referencias de velocidad de traslación y rotación con

signo, basta con utilizar dicho signo para inferir la dirección del movimiento o el sentido de giro. En este caso

se utilizan valores positivos en la velocidad de traslación, xt, para iniciar un movimiento hacia delante y

valores positivos de la velocidad de traslación, xg, para iniciar una rotación a la izquierda. Signos opuestos

implican direcciones opuestas.

void updateEngineReferences () { dtheta_r = 0.76*PI*xt; u_mi = -u_m - 10.0*xg; u_md = -u_m + 10.0*xg; if (u_mi>=0) { digitalWrite(PinIn1I, HIGH); digitalWrite(PinIn2I, LOW); } else { digitalWrite(PinIn1I, LOW); digitalWrite(PinIn2I, HIGH); } if (u_md>=0) { digitalWrite(PinIn1D, HIGH); digitalWrite(PinIn2D, LOW);

Page 71: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

55

55 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

} else { digitalWrite(PinIn1D, LOW); digitalWrite(PinIn2D, HIGH); } PWMI = 14 + abs(u_mi); PWMD = 14 + abs(u_md); if (PWMI > 255) { PWMI = 255; } if (PWMD > 255) { PWMD = 255; } if (abs(kalAngleY) > 30) { PWMI = 0; PWMD = 0; ivr = 0; } analogWrite(PinPWMI, PWMI); analogWrite(PinPWMD, PWMD); }

7.2.1.2 Recepción y envío de mensajes

En el nuevo archivo bluetooth.ino se siguen manteniendo tanto la función original para el envio de mensajes

como la función para la recepción de mensajes que usa llamadas bloqueantes.

Esta última sin embargo se acompaña ahora de un conjunto adicional de funciones que hacen uso de un buffer,

un array de caracteres, intero para extraer de él los datos útiles recibidos. Entre ellas:

updateBuffer() – Toma byte a byte todos los datos ofrecidos por el módulo Bluetooth y los guarda en

en el array de caracteres que hace las veces de buffer, sobreescribiendo los datos iniciales del mismo

al llenar dicho array (funciona como una “lista” circular).

processBuffer() – Analiza los datos (caracteres) almacenados en el array y busca en ellos un conjunto

que se corresponda con el modelo que se ha seguido: carácter de inicio de cadena (carácter “<”) más

una serie de valores (enteros o flotantes con o sin signo) seguidos de un separador de valor (coma) si

hubiese otro valor tras él y finalmente el cierre de cadena (carácter “>”). Si encuentra dicha cadena

toma los valores presentes en ella y los asigna a cada una de las variables de la sentencia de control.

Page 72: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Programación

56

Page 73: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

57

8 CONCLUSIONES

Este proyecto parte, en cieta medida, de una de las propuestas de mejora del proyecto original que completó el

desarrollo del vehículo autoequilibrado. En ella se mencionaba una futura aplicación, que hiciese uso de los

sensores incorporados hoy día en la mayoría de los smarthphones y diese la posibilidad de controlar el robot

con ellos.

Con esa idea, junto al afán de aprender nuevas tecnologías, nace este proyecto. A dicho método de control se

le sumaría posteriormente el joystick virtual. Para finalizar, se llevaron a cabo las modificaciones mencionadas

en el programa Arduino, a fin de corregir algunos problemas previos y simplificar parte del mismo.

Con ello sin embargo, no se cierra el ciclo. Aun quedan muchas líneas de progreso abiertas, varias de ellas

propuestas para este mismo proyecto pero que no fueron posibles abarcar.

Entre estas propuestas, la principal consistiría en pasar a ejecutar la ecuación relativa a la ley de control en el

dispositivo Android. No cabe decir que estaríamos comparando el poder computacional de un

microprocesador que funciona a 16MHz, como es el caso de la placa Arduino, con otro de varios núcleos y a

frecuencias que superan el gigahercio. Además, sumado a este incremento en la capacidad de realiar

operaciones, se abriría una nueva puerta al estudio de los mecanismos de control, ya que entrarían en partido

los retrasos producidos por la comunicación bluetooth así como las pérdidas de datos inherentes a ella.

Page 74: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Conclusiones

58

Page 75: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

59

9 BIBLIOGRAFÍA

Se ha recurrido principalmente a dos documentos a la hora de solventar dudas y tomar un punto de partida:

o Vehículo autoequilibrado de tipo péndulo invertido de bajo coste. Antonio J. Croche Navarro (2014).

o Apuntes de la asignatura Diseño de aplicaciones móviles.

El resto de información necesaria para la elaboración del presente proyecto fue obtenida de diferentes recursos

online:

o https://developer.android.com/reference/packages.html

o https://developer.android.com/about/dashboards/index.html?hl=es-419

o https://developer.android.com/guide/components/activities/activity-lifecycle.html

o https://developer.android.com/reference/android/Manifest.permission.html

o https://www.arduino.cc/en/Guide/Environment

o https://www.arduino.cc/en/Main/arduinoBoardMega2560/

o https://www.arduino.cc/en/Reference/HomePage

o https://en.wikipedia.org/wiki/Java_(programming_language)

o https://gitlab.com

o https://developer.android.com/guide/topics/ui/menus.html

o https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html

o https://www.bignerdranch.com/blog/splash-screens-the-right-way

o http://iconhandbook.co.uk/reference/chart/android

Page 76: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Bibliografía

60

Page 77: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

61

APENDICE A: CÓDIGO PARA ARDUINO

Archivo: PI_modificado.ino

1. #include <I2C.h> // Esta librería permite comunicar con dispositivos I2C TWI. http://arduino.cc/es/Reference/Wire#.U0WDz6KeZdg

2. #include "Kalman.h" 3. 4. #define RESTRICT_PITCH // Si esta linea se comenta, el giro alrededor del eje X se restrin

ge a ±90º, mientras que el giro alrededor de Y se restringe a ±180º. Si esta linea no se comenta, el que queda restringido a ±90º es el eje Y

5. 6. 7. Kalman kalmanX; 8. Kalman kalmanY; //Aquí está creando los objetos kalmanX y KalmanY de tipo Kalman(librería)

. Más adelante si quiero usar una función dentro de Kalman.h la usaré particularizada al objeto haciendo: kalmanX.funcion()o kalmanY.funcion()

9. 10. /* IMU Data */ 11. double accX, accY, accZ; //componentes de la acelareción 12. double gyroX, gyroY, gyroZ; //componentes de giro 13. int16_t tempRaw; //Aquí se almacenará la temperatura en crudo (no en ºC) 14. 15. 16. double gyroXangle, gyroYangle; // Angle calculate using the gyro only 17. double compAngleX, compAngleY; // Calculated angle using a complementary filter 18. double kalAngleX, kalAngleY; // Calculated angle using a Kalman filter 19. 20. uint32_t timer; 21. uint8_t IMUData[14]; // Buffer for I2C data Creación del vector IMUData reservando 14 in

dices (para la comunicación de la IMU) 22. 23. /* PARA LOS ENCODERS, VELOCIDAD Y ACELERACIÓN */ 24. 25. //Motor Izquierdo 26. #define encoder0PinA 18 27. #define encoder0PinB 19 28. volatile float encoder0Pos = 0; 29. unsigned long time1; 30. volatile float ivr; 31. float pos0; 32. float vel; 33. float acel; 34. float vel0; 35. 36. //Motor Derecho 37. #define encoder1PinA 2 38. #define encoder1PinB 3 39. volatile float encoder1Pos = 0; 40. unsigned long time2; 41. float pos0d; 42. 43. 44. 45. ////////////////////////// 46. 47. /* PARA EL MOTOR */ 48. 49. #define Pinpot1 A2

Page 78: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Apendice A: Código para Arduino

62

50. #define Pinpot2 A1 51. #define Pinpot3 A0 52. #define PinPWMD 7 //Lo he cambiado del 8 al 7 para poder subir la frecuencia PWM tal y co

mo dice la pag. web que encontré. 53. #define PinPWMI 9 54. #define PinIn1I 8 55. #define PinIn2I 10 56. #define PinIn1D 5 57. #define PinIn2D 6 58. 59. 60. float PWMD; 61. float PWMI; 62. 63. ///////////////////////// 64. 65. /* PARA CALCULAR EL CONTROL PID*/ 66. 67. float u; 68. float phi_r,dphi_r,dtheta_r; 69. float phi, dphi; 70. float Ahora; 71. float Cambiot, Ultimot; 72. float Kp,Ki; 73. float r,e; 74. float Ahora1, Cambiot1, Ultimot1; 75. float ITerm; 76. float u_m; 77. float cuenta1; 78. 79. 80. /* PARA MANIOBRAS*/ 81. 82. float u_mi, u_md; 83. float tiempoDir; 84. float Estado; 85. 86. /* PARA EL FILTRO DE LA VELOCIDAD*/ 87. 88. float wf, wf_1, wf_2, wf0; 89. float vel_1, vel_2, acelf; 90. float veli, veld; 91. 92. 93. /* PARA LAS VAR DE LA COMUNICACIÓN BLUETOOTH*/ 94. float K1, K2, K3, c, K4; //variables para las constantes del LQR 95. int Dir; //variable para la dirección (de 1 a 9) 96. float xg, xt; //velocidad de giro y de traslación 97. int trim; 98. int cuenta; 99. 100. float K1_r, K2_r, K3_r, c_r; //variables para las constantes en proceso de recepción de

l LQR 101. int Dir_r; //variable para la dirección (de 1 a 9) 102. float xg_r, xt_r; 103. float trim_r; 104. 105. float Kp_r, Ki_r; //para la recepcion constantes control motor 106. 107. float tiempo0; //Para el cálculo de tiempo de bucle de programa. 108. float bucle; 109. 110. float bluetoothInterval = 100; // Cada cuantos ms se han de ejecutar la funciones

de adquisición de datos 111. float bluetoothIntervalTimer = 0; // Inicio a 0 del contador 112. 113. /* PARA EL TRIMADO*/

Page 79: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

63

63 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

114. float trim_estado, adicion, trimado; 115. float kalAngleY_trimado; 116. 117. ///////////////////////// 118. 119. 120. void setup() { 121. Serial.begin(115200); 122. 123. //configuración Timer 1 y 5 124. noInterrupts(); //desactiva todas las interrupciones para inicializar la in

terrupción temporal 125. TCCR5A = 0; //inicializamos los registros de control 126. TCCR5B = 0; 127. TCNT5 = 0; 128. 129. OCR5A = 9999; // valor CTC que se guarda en el registro OCR1A 130. TCCR5B |= (1 << WGM52); // modo output compare 131. TCCR5B |= (1 << CS50); // preescalador de 64 132. TCCR5B |= (1 << CS51); 133. TIMSK5 |= (1 << OCIE5A); // activa la interrupción por comparación temporal 134. 135. TCCR1A = 0; //inicializamos los registros de control 136. TCCR1B = 0; 137. TCNT1 = 0; 138. 139. OCR1A = 19999; // valor CTC que se guarda en el registro OCR3A 140. TCCR1B |= (1 << WGM12); // modo output compare 141. TCCR1B |= (1 << CS11); // preescalador de 8 142. TIMSK1 |= (1 << OCIE1A); // activa la interrupción por comparación temporal 143. 144. interrupts(); // activa todas las interrupciones 145. 146. 147. I2c.begin(); 148. //TWBR = ((F_CPU / 400000L) - 16) / 2; // Poner frecuencia I2C a 400kHz TWBR –

TWI Bit Rate Register - porque en el Datasheet de la MPU6050 dicen que la máx. velocidad del bus es 400kHz.

149. I2c.setSpeed(1); //Poner frecuencia a 400kHz 150. I2c.pullup(1); //activamos la resistencia de pull-up 151. I2c.timeOut(40); //habilita el recibir los errores por el envío fallido y no se queda

colgado ya que te dice el error y sigue funcionando 152. 153. IMUData[0] = 7; // >>Config tasa muestreo. Registro 0x19hex=25dec (Nuestro

giroscopo funciona a 8kHz, ver pag.12-13 Mapa Registro pdf) 154. IMUData[1] = 0x00; // >>Sincronización y Filtrado. Registro 0x1Ahex=26dec (ver pag

.13 Mapa Registro pdf) 155. IMUData[2] = 0x00; // >>Configuración Giróscopos. Registro 0x1Bhex=27dec Lo pone

a máxima sensibilidad/minimo rango y deshabilita el autotest(ver pag.14 Mapa Registro pdf)

156. IMUData[3] = 0x00; // >>Configuración Acelerómetros. Registro 0x1Chex=28dec Lo pone a máxima sensibilidad/minimo rango y deshabilita el autotest (ver pag.15-16 Mapa Registro pdf)

157. while (IMUWrite(0x19, IMUData, 4, false)); // >>Mete los anteriores 4 valores en cada posición del registro y es un while porque devuelve un 0 cuando es con éxito y si no lo reintenta (ver archivo i2C.ino)

158. while (IMUWrite(0x6B, 0x01, true)); // >>Deshabilita modo standby. Usa como señal de reloj el giroscopo del eje X

159. // Esto lo mete en el Registro 0x69hex=107dec (ver pag.41 Mapa Registro pdf)

160. 161. while (IMURead(0x75, IMUData, 1)); //Lee los registros 0x75h

ex=117dec; y los mete en el vector IMUData. Como el registro 0x75 es el último, el resto de valores del vector quedará con los valores que tenía antes.

162. if (IMUData[0] != 0x68) { // Read "WHO_AM_I" register //Este registro "Quién Soy?" (ver pag. 46) devuelve el valor 0x68 para decir cuál es la dirección del dispositivo (aunque realmente sabemos que la dirección será sólo 0x68 cuando el pin AD0 esté LOW y 0x69 cuando el pin AD0 esté HIGH. Esto aparece en el datasheet)

Page 80: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Apendice A: Código para Arduino

64

163. Serial.print(F("Error reading sensor")); 164. while (1); 165. } 166. 167. delay(100); // Espera a que se estabilice el sensor 168. 169. /* Seleccionar kalman and gyro starting angle */ 170. while (IMURead(0x3B, IMUData, 6)); //Lectura de los Registros de Ace

lerómetros:0x3Bhex=59dec y 0x3Chex=60dec; 0x3Dhex=61dec y 0x3Ehex=62dec; 0x3Fhex=63dec y 0x40hex=64dec; y los mete en el vector IMUDataver pag.30 Mapa Registro pdf)

171. accX = (IMUData[0] << 8) | IMUData[1]; //(IMUData[0] << 8)=esto es un desplazamiento de 8 bits a la izquierda de forma que el IMUData[0]queda en las posiciones más significativas y a la derecha quedan ceros.

172. accY = (IMUData[2] << 8) | IMUData[3]; // Con la operación | se suman bit a bit las posiciones menos significatvas con el valor IMUData[1]. Así se obtiene un número

173. accZ = (IMUData[4] << 8) | IMUData[5]; // de 16 bit a partir de dos números de 8 bit (porque los registros sólo pueden guardar 8 bits)

174. 175. 176. #ifdef RESTRICT_PITCH 177. double roll = atan2(accY, accZ) * RAD_TO_DEG; 178. double pitch = atan(-accX / sqrt(accY * accY + accZ * accZ)) * RAD_TO_DEG; 179. #else 180. double roll = atan(accY / sqrt(accX * accX + accZ * accZ)) * RAD_TO_DEG; 181. double pitch = atan2(-accX, accZ) * RAD_TO_DEG; 182. #endif 183. 184. kalmanX.setAngle(roll); // Seleccionar ángulo de inicio 185. kalmanY.setAngle(pitch); 186. gyroXangle = roll; 187. gyroYangle = pitch; 188. compAngleX = roll; 189. compAngleY = pitch; 190. 191. timer = micros(); 192. 193. /////// CÓDIGO PARA LA POSICION, VELOCIDAD Y ACELERACIÓN ////// 194. 195. //Motor izquierdo 196. pinMode(encoder0PinA, INPUT); 197. digitalWrite(encoder0PinA, HIGH); // activar resistencias pullup 198. pinMode(encoder0PinB, INPUT); 199. digitalWrite(encoder0PinB, HIGH); // activar resistencias pullup 200. 201. //Recordar que son encoders de cuadratura, hay dos sensores por cada motor colocados

a 90º uno del otro 202. attachInterrupt(5, doEncoderA0, CHANGE); // Encoder A en la interrupción 5 (pin 18) 203. attachInterrupt(4, doEncoderB0, CHANGE); // Encoder B en la interrupción 4 (pin 19)

204. 205. 206. //Motor derecho 207. pinMode(encoder1PinA, INPUT); 208. digitalWrite(encoder1PinA, HIGH); // activar resistencias pullup 209. pinMode(encoder1PinB, INPUT); 210. digitalWrite(encoder1PinB, HIGH); // activar resistencias pullup 211. 212. //Recordar que son encoders de cuadratura, hay dos sensores por cada motor colocados

a 90º uno del otro 213. attachInterrupt(0, doEncoderA1, CHANGE); // Encoder A en la interrupción 0 (pin 2) 214. attachInterrupt(1, doEncoderB1, CHANGE); // Encoder B en la interrupción 1 (pin 3) 215. 216. time1=millis(); // Inicializamos tiempo y posicion para el calculo de velocidad 217. time2=millis(); // Inicializamos tiempo y posicion para el calculo de velocidad 218. 219.

Page 81: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

65

65 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

220. /////////// MOTOR ///////////// 221. //Para subir la frecuencia del PWM pin 7 del Mega 2560 de 489Hz a 31333Hz 222. TCCR4B = TCCR4B & 0b000 | 0x01; //timer 4 lo que va después del palo es el valor de f

recuencia al que se le cambia 223. //Para subir la frecuencia del PWM pin 9 del Mega 2560 de 489Hz a 31333Hz 224. TCCR2B = TCCR2B & 0b000 | 0x01; //timer 2 225. 226. pinMode(PinPWMD, OUTPUT); 227. pinMode(PinPWMI, OUTPUT); 228. pinMode(PinIn1I, OUTPUT); 229. pinMode(PinIn2I, OUTPUT); 230. pinMode(PinIn1D, OUTPUT); 231. pinMode(PinIn2D, OUTPUT); 232. 233. ///CONTROLADOR//// 234. phi_r=0; dphi_r=0;dtheta_r=0; 235. u=0; 236. Kp=0.2;//0.2;//0.35;//0.25;//0.25;//0.22;//0.3;//0.25;//0.21; //0.2;//Kp=0.1; 237. Ki=0.1;//0.1;//0.07;//0.06;//0.06;//0.08;//0.07;//0.06;//0.08;//0.09; //0.1;//Ki=0.4;

238. K1=-332.8746; K2=-52.7835; K3=-7; c=2.0; Dir=5; xg=0.0; xt=0.0; K4=-

0.3; ivr=0; //cuenta1=0; 239. Kp_r = 0; 240. Ki_r = 0; 241. K1_r = 0; 242. K2_r = 0; 243. K3_r = 0; 244. c_r = 0; 245. Dir_r = Dir; 246. xg_r = xg; 247. xt_r = xt; 248. /////////////////////////////// 249. 250. ///FILTRO VELOCIDAD//// 251. wf_2=1.0; 252. wf_1=1.0; 253. wf=1.0; 254. vel_2=1.0; 255. vel_1=1.0; 256. wf0=1.0; 257. 258. // Inicializar el buffer para bluetooth 259. initializeBuffer(); 260. 261. } 262. 263. 264. void loop() { 265. 266. if ( (millis() - bluetoothIntervalTimer) >= bluetoothInterval) { 267. // Enviar reporte via bluetooth 268. sendData(); 269. // Actualizar el buffer con los posibles nuevos datos recibidos via bluetooth 270. updateBuffer(); 271. // Procesar los bytes recibidos y extraer de ellos los valores pertinentes 272. processBuffer(); 273. // Aplicar los datos recibidos a las variables globales (aquí se aplica el filtro)

274. applyData(); 275. // Resetear el inicio de intervalo 276. bluetoothIntervalTimer = millis(); 277. } 278. 279. 280. 281. /////CÓDIGO PARA LA IMU//// 282. 283. /* Update all the values */

Page 82: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Apendice A: Código para Arduino

66

284. while (IMURead(0x3B, IMUData, 14)); 285. accX = ((IMUData[0] << 8) | IMUData[1]) -

857.08;//883.8 Su offset:+ 2000.0; //Los valores de accX vienen de los registros 0x3B y 0x3C por eso los combina y le suma un offset (2000 que no sé como ha calculado). Esto puede verse en esta página donde hacen lo mismo: http://www.botched.co.uk/pic-tutorials/mpu6050-setup-data-aquisition/

286. accY = ((IMUData[2] << 8) | IMUData[3]) - 114.5; //Su offset:+ 3319.84; //Para el resto es analogo a lo explicado para el de arriba

287. accZ = ((IMUData[4] << 8) | IMUData[5]) + 134.75; //Su offset:+ 664.48; 288. tempRaw = (IMUData[6] << 8) | IMUData[7]; 289. gyroX = (IMUData[8] << 8) | IMUData[9]; 290. gyroY = (IMUData[10] << 8) | IMUData[11]; 291. gyroZ = (IMUData[12] << 8) | IMUData[13]; 292. 293. double dt = (double)(micros() - timer) / 1000000; // Calcular el delta t 294. timer = micros(); 295. 296. 297. #ifdef RESTRICT_PITCH 298. double roll = atan2(accY, accZ) * RAD_TO_DEG; 299. double pitch = atan(-accX / sqrt(accY * accY + accZ * accZ)) * RAD_TO_DEG; 300. #else 301. double roll = atan(accY / sqrt(accX * accX + accZ * accZ)) * RAD_TO_DEG; 302. double pitch = atan2(-accX, accZ) * RAD_TO_DEG; 303. #endif 304. 305. double gyroXrate = gyroX / 131.0; // Convierte a deg/s 306. double gyroYrate = gyroY / 131.0; // Convierte a deg/s 307. 308. #ifdef RESTRICT_PITCH 309. // Solución al problema de transición cuando el ángulo del acelerometro salta entre

-180 y 180 grados 310. if ((roll < -90 && kalAngleX > 90) || (roll > 90 && kalAngleX < -90)) { 311. kalmanX.setAngle(roll); 312. compAngleX = roll; 313. kalAngleX = roll; 314. gyroXangle = roll; 315. } else 316. kalAngleX = kalmanX.getAngle(roll, gyroXrate, dt); // Cálculo del ángulo con el F

iltro Kalman 317. 318. if (abs(kalAngleX) > 90) 319. gyroYrate = -

gyroYrate; // Invierte la velocidad angular, de forma que cumple la lectura restringida del acelerometro

320. kalAngleY = kalmanY.getAngle(pitch, gyroYrate, dt); 321. #else 322. // Solución al problema de transición cuando el ángulo del acelerometro salta entre

-180 y 180 grados 323. if ((pitch < -90 && kalAngleY > 90) || (pitch > 90 && kalAngleY < -90)) { 324. kalmanY.setAngle(pitch); 325. compAngleY = pitch; 326. kalAngleY = pitch; 327. gyroYangle = pitch; 328. } else 329. kalAngleY = kalmanY.getAngle(pitch, gyroYrate, dt); // Cálculo del ángulo con el

Filtro Kalman 330. 331. if (abs(kalAngleY) > 90) 332. gyroXrate = -

gyroXrate; // Invierte la velocidad angular, de forma que cumple la lectura restringida del acelerometro

333. kalAngleX = kalmanX.getAngle(roll, gyroXrate, dt); // Cálculo del ángulo con el Filtro Kalman

334. #endif 335.

Page 83: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

67

67 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

336. gyroXangle += gyroXrate * dt; // Calcula el ángulo obtenido del giroscopo sin ningun

filtro 337. gyroYangle += gyroYrate * dt; 338. 339. compAngleX = 0.93 * (compAngleX + gyroXrate * dt) + 0.07 * roll; // Cálculo del ángul

o usando filtro complementario 340. compAngleY = 0.93 * (compAngleY + gyroYrate * dt) + 0.07 * pitch; 341. 342. // Resetear el ángulo del giroscopo si ha sufrido gran deriva. 343. if (gyroXangle < -180 || gyroXangle > 180) 344. gyroXangle = kalAngleX; 345. if (gyroYangle < -180 || gyroYangle > 180) 346. gyroYangle = kalAngleY; 347. 348. 349. ///////// CÓDIGO PARA POSICIÓN, VELOCIDAD Y ACELERACIÓN //////// 350. 351. if ((millis()-

time1)>=10){ //Al reducir esto de 20 a 10 parece que mejora un poco el comportamiento, aunque no lo suficiente, sigue respondiendo muy lento.

352. veli=((encoder0Pos-pos0)*1000.0/(millis()-time1))*(PI/180.0); 353. pos0=encoder0Pos; 354. veld=((encoder1Pos-pos0d)*1000.0/(millis()-time1))*(PI/180.0); 355. pos0d=encoder1Pos; 356. // veld=veli; 357. // if (Estado==10 || Estado==30 || Estado==40 || Estado==60 || Estado==70 || Estado=

=90) { 358. // veld=-veli; 359. // } 360. 361. vel=(veli+veld)/2.0; 362. 363. // wf=1.8430*wf_1 - 0.8545*wf_2 + 0.0029*vel + 0.0057*vel_1 + 0.0029*vel_2; 364. //Voy a cambiar ahora el filtro de Wn=0.0354 a Wn=0.0784, para ver si responde más

rápido 365. wf=1.6544*wf_1 - 0.7059*wf_2 + 0.0129*vel + 0.0257*vel_1 + 0.0129*vel_2; 366. 367. wf_2=wf_1; 368. wf_1=wf; 369. vel_2=vel_1; 370. vel_1=vel; 371. 372. acel=(vel-vel0)*1000.0/(millis()-time1); 373. 374. acelf=(wf-wf0)*1000.0/(millis()-time1); 375. 376. wf0=wf; 377. vel0=vel; 378. time1=millis(); 379. 380. } 381. 382. 383. ////////////////////////////////////////////////////////////////// 384. 385. ///////// CÓDIGO PARA CONTROLADOR //////// 386. 387. 388. /*Ahora=millis(); 389. Cambiot=Ahora-Ultimot;*/ 390. 391. // Trimado(); 392. 393. /*if (Cambiot>=40){ 394. phi=kalAngleY_trimado*(PI/180); 395. dphi=gyroYrate*(PI/180); 396. ivr=(encoder0Pos+encoder1Pos)/2.0*(PI/180.0); 397. u=K1*(phi_r-phi) + K2*(dphi_r-dphi) + K3*(dtheta_r-wf) + K4*ivr;

Page 84: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Apendice A: Código para Arduino

68

398. r=-u*c; //Voy a tocar esto a ver si responde más rápido. 399. Ultimot=Ahora; 400. }*/ 401. /*if (cuenta1>=500){ 402. dtheta_r=2;}/*quitar lo de mando_motor añadir cuenta1 en isr, cambiar dthet

a incial*/ 403. dphi=gyroYrate*(PI/180); 404. 405. // Actualizar las referencias de los motores 406. updateEngineReferences(); 407. 408. 409. 410. //////////////////////////////////////////////////////////////// 411. 412. /*SECCIÓN PARA EL ENVÍO*/ 413. 414. // Tiempo transcurrido en el bucle 415. bucle=micros()-tiempo0; 416. 417. // Calcular cuanto tiempo de menos ha trascurrido. Este bucle debe durar ~10ms 418. long forcedDelay = 11000 - bucle; 419. if (forcedDelay > 0) { 420. delayMicroseconds(forcedDelay); 421. } 422. 423. // Recalcular el tiempo de bucle y resetear el contador. 424. bucle=micros()-tiempo0; 425. tiempo0=micros(); 426. 427. } 428. 429. 430. 431. 432. ISR(TIMER5_COMPA_vect) 433. { 434. //cuenta1=cuenta1+1; 435. phi=kalAngleY*(PI/180); 436. //phi=kalAngleY_trimado*(PI/180); 437. /*dphi=gyroYrate*(PI/180);*/ 438. 439. ivr=ivr+dtheta_r-wf; 440. u=K1*(phi_r-phi) + K2*(dphi_r-dphi) + K3*(dtheta_r-wf) + K4*ivr; 441. //u=0.1; 442. r=-u*c; //Voy a tocar esto a ver si responde más rápido. 443. 444. //if (cuenta1>=300) r=-0.3; 445. } 446. ISR(TIMER1_COMPA_vect) // rutina de interrupción con Output Compare 447. { 448. e=r-acelf; 449. 450. ITerm += Ki*e; 451. if((abs(ITerm))>255.0){ //antiwindup 452. if (ITerm>0) ITerm=255.0; 453. else ITerm=-255.0;} 454. 455. u_m=Kp*e+ITerm; 456. } 457. 458. ///// A CONTINUACIÓN VIENEN LAS FUNCIONES DE LAS INTERRUPCIONES PARA LOS ENCODERS /////

// 459. 460. //INTERRUPCIONES ENCODERS MOTOR IZQUIERDO 461. void doEncoderA0() {

Page 85: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

69

69 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

462. if (digitalRead(encoder0PinA) == HIGH) { // busca un cambio de bajo-a-

alto en el canal A 463. if (digitalRead(encoder0PinB) == LOW) { // comprueba el canal B para saber en qué

sentido gira 464. encoder0Pos++;} // Horario 465. else {encoder0Pos--;} // Antihorario 466. } 467. 468. else{ // en otro caso sería un cambio de alto-a-bajo en canal A 469. if (digitalRead(encoder0PinB) == HIGH) { // comprueba el canal B para saber en qué

sentido gira 470. encoder0Pos++;} // Horario 471. else {encoder0Pos--;} // Antihorario 472. } 473. } 474. 475. void doEncoderB0(){ 476. if (digitalRead(encoder0PinB) == HIGH) { //busca un cambio de bajo-a-

alto en el canal B 477. if (digitalRead(encoder0PinA) == HIGH) { // comprueba el canal A para saber en qué

sentido gira 478. encoder0Pos++;} // Horario 479. else {encoder0Pos--;} // Antihorario 480. } 481. 482. else { // en otro caso sería un cambio de alto-a-bajo en canal B 483. if (digitalRead(encoder0PinA) == LOW) { // comprueba el canal A para saber en qué

sentido gira 484. encoder0Pos++;} // Horario 485. else {encoder0Pos--;} // Antihorario 486. } 487. } 488. 489. //INTERRUPCIONES ENCODERS MOTOR DERECHO 490. void doEncoderA1(){ 491. if (digitalRead(encoder1PinA) == HIGH) { // busca un cambio de bajo-a-

alto en el canal A 492. if (digitalRead(encoder1PinB) == LOW) { // comprueba el canal B para saber en qué

sentido gira 493. encoder1Pos--;} // Horario 494. else {encoder1Pos++;} // Antihorario 495. } 496. 497. else{ // en otro caso sería un cambio de alto-a-bajo en canal A 498. if (digitalRead(encoder1PinB) == HIGH) { // comprueba el canal B para saber en qué

sentido gira 499. encoder1Pos--;} // Horario 500. else {encoder1Pos++;} // Antihorario 501. } 502. } 503. 504. void doEncoderB1(){ 505. if (digitalRead(encoder1PinB) == HIGH) { //busca un cambio de bajo-a-

alto en el canal B 506. if (digitalRead(encoder1PinA) == HIGH) { // comprueba el canal A para saber en qué

sentido gira 507. encoder1Pos--;} // Horario 508. else {encoder1Pos++;} // Antihorario 509. } 510. 511. else { // en otro caso sería un cambio de alto-a-bajo en canal B 512. if (digitalRead(encoder1PinA) == LOW) { // comprueba el canal A para saber en qué

sentido gira 513. encoder1Pos--;} // Horario 514. else {encoder1Pos++;} // Antihorario 515. } 516. }

Page 86: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Apendice A: Código para Arduino

70

Archivo: control_motor.ino

1. void updateEngineReferences () { 2. 3. dtheta_r = 0.76*PI*xt; 4. u_mi = -u_m - 10.0*xg; 5. u_md = -u_m + 10.0*xg; 6. 7. if (u_mi>=0) { 8. digitalWrite(PinIn1I, HIGH); 9. digitalWrite(PinIn2I, LOW); 10. } else { 11. digitalWrite(PinIn1I, LOW); 12. digitalWrite(PinIn2I, HIGH); 13. } 14. 15. if (u_md>=0) { 16. digitalWrite(PinIn1D, HIGH); 17. digitalWrite(PinIn2D, LOW); 18. } else { 19. digitalWrite(PinIn1D, LOW); 20. digitalWrite(PinIn2D, HIGH); 21. } 22. 23. PWMI = 14 + abs(u_mi); 24. PWMD = 14 + abs(u_md); 25. 26. if (PWMI > 255) { PWMI = 255; } 27. if (PWMD > 255) { PWMD = 255; } 28. 29. if (abs(kalAngleY) > 30) { 30. PWMI = 0; 31. PWMD = 0; 32. ivr = 0; 33. } 34. 35. analogWrite(PinPWMI, PWMI); 36. analogWrite(PinPWMD, PWMD); 37. }

Page 87: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

71

71 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

Archivo: asignacion_variables.ino

1. // Valor aplicado al filtro 2. float alpha = 0.75; 3. 4. 5. 6. void applyData () { 7. setKs(Kp_r, Ki_r, K1_r, K2_r, K3_r); 8. setC(c_r); 9. setDir(Dir_r); 10. setTranslationSpeed(xt_r); 11. setTurnSpeed(xg_r); 12. } 13. 14. 15. void setKs (float kp_r, float ki_r, float k1_r, float k2_r, float k3_r) { 16. // Si son cero no se actualiza el valor 17. if (kp_r != 0) { Kp = kp_r; } 18. if (ki_r != 0) { Ki = ki_r; } 19. if (k1_r != 0) { K1 = k1_r; } 20. if (k2_r != 0) { K2 = k2_r; } 21. if (k3_r != 0) { K3 = k3_r; } 22. } 23. 24. 25. void setC (float c_r) { 26. // Si es cero no se actualiza el valor 27. if (c_r != 0) { c = c_r; } 28. } 29. 30. 31. void setDir (int dir_r) { 32. // Si es cero no se actualiza el valor 33. if (dir_r != 0) { 34. Dir = dir_r; 35. if (Dir == 1 || Dir == 3 || Dir == 7 || Dir == 9) { 36. tiempoDir = millis(); 37. } 38. } 39. } 40. 41. 42. void setTranslationSpeed (float xt_r) { 43. xt = alpha * xt + (1 - alpha) * xt_r; 44. } 45. 46. 47. void setTurnSpeed (float xg_r) { 48. xg = alpha * xg + (1 - alpha) * xg_r; 49. }

Page 88: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Apendice A: Código para Arduino

72

Archivo: bluetooth.ino

1. #define SOC '<' 2. #define EOC '>' 3. #define PS ',' 4. #define EMPTY '*' 5. #define BUFFER_LENGTH 100 6. #define PARAM_LENGHT 10 7. #define MAX_INTEGERS 1 8. #define MAX_FLOATS 8 9. 10. #define INTEGERS_EXPECTED 1 11. #define FLOATS_EXPECTED 8 12. 13. 14. // Buffer para los datos recibidos via bluetooth e índice del último dato insertado 15. char buffer[BUFFER_LENGTH]; 16. int bufferNextIndex = 0; 17. 18. // Flag para indicar la recepción del final de comando (EOC) y su índice en el buffer 19. boolean eocFound = false; 20. int eocIndex = 0; 21. 22. // Contienen los valores recibidos, según sean enteros o flotantes 23. int integers[MAX_INTEGERS]; 24. int integersNextIndex = 0; 25. int integersCount = 0; 26. float floats[MAX_FLOATS]; 27. int floatsNextIndex = 0; 28. int floatsCount = 0; 29. 30. 31. 32. void initializeBuffer () { 33. // Inicializar el buffer 34. for (int i=0 ; i < BUFFER_LENGTH ; i++) { buffer[i] = EMPTY; } 35. } 36. 37. int getNextBufferIndex (int index) { 38. return ++index < BUFFER_LENGTH ? index : 0; 39. } 40. 41. int getPrevBufferIndex (int index) { 42. return --index >= 0 ? index : BUFFER_LENGTH - 1; 43. } 44. 45. int addToBuffer (char character) { 46. int index = bufferNextIndex; 47. buffer[index] = character; 48. bufferNextIndex = getNextBufferIndex(index); 49. return index; 50. } 51. 52. void addInteger (int integer) { 53. if (integersNextIndex < MAX_INTEGERS) { 54. integers[integersNextIndex++] = integer; 55. } 56. integersCount++; 57. } 58. 59. void addFloat (float floatValue) { 60. if (floatsNextIndex < MAX_FLOATS) { 61. floats[floatsNextIndex++] = floatValue; 62. } 63. floatsCount++; 64. }

Page 89: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

73

73 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

65. 66. 67. void updateBuffer () { 68. // Almacenar todos los Bytes disponibles 69. while (Serial.available() > 0) { 70. // Byte recibido 71. char character = (char) Serial.read(); 72. // Insertarlo en el buffer 73. int lastItemIndex = addToBuffer(character); 74. // Marcar el flag de comado recibido 75. if ( character == EOC ) { 76. eocIndex = lastItemIndex; 77. eocFound = true; 78. } 79. } 80. } 81. 82. 83. void processBuffer () { 84. // Comprobar que se haya encontrado el fin de comando (EOC). En otro caso abortar ope

raciones 85. if (eocFound) { 86. // Flags 87. boolean stopProcess = false; 88. boolean isInteger; 89. // Índice relativo al buffer 90. int index = eocIndex; 91. // Datos del parámetro reconstruido 92. char paramData[PARAM_LENGHT+1]; // +1 (final de cadena '\0') 93. int paramDataIndex; 94. // Inicializar a 0 y añadir final de cadena. El índice comienza desde el final 95. for (int i=0 ; i<PARAM_LENGHT ; i++) { paramData[i] = '0'; } paramData[sizeof(param

Data)-1] = '\0'; 96. paramDataIndex = PARAM_LENGHT-1; 97. isInteger = true; // Por defecto, es un entero 98. 99. // Recorrer los datos recibidos, partiendo del final de comando (EOC) 100. while (!stopProcess) { 101. // Indice del dato y el dato en sí a tratar (tomado del buffer) 102. index = getPrevBufferIndex(index); 103. char character = buffer[index]; 104. // Checkear el tipo de dato 105. if (character != PS && character != SOC && character != EOC && character !

= EMPTY) { 106. // Datos de un parámetro 107. if (character == '-') { 108. // Mover el simbolo "-" al inicio de la cadena 109. paramData[0] = character; 110. } else { 111. if (paramDataIndex > 0) { 112. paramData[paramDataIndex--] = character; 113. } else { 114. // Fallo al recopilar datos de un parámetro. Se ha excedido el tamañ

o máximo de parámetro 115. // Serial.println("paramData[] full"); 116. stopProcess = true; 117. } 118. } 119. // Marcar el flag de flotante, si corresponde 120. if (character == '.') { isInteger = false; } 121. } else { 122. // Se trata de un separador de parámetro (PS), inicio de comando (SOC) o

caracter vacío (EMPTY) 123. if (character == PS || character == SOC) { 124. // Discrimiar el tipo de dato y almacenarlo 125. if (isInteger) { 126. addInteger(atoi(paramData)); 127. } else {

Page 90: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Apendice A: Código para Arduino

74

128. addFloat(atof(paramData)); 129. } 130. // Operar según el discriminador 131. if (character == SOC) { 132. // Fin de parámetro y comando 133. stopProcess = true; 134. } else if (character == PS) { 135. // Fin de parámetro. Reinicializar 136. for (int i=0 ; i<PARAM_LENGHT ; i++) { paramData[i] = '0'; } 137. paramDataIndex = PARAM_LENGHT-1; 138. isInteger = true; 139. } 140. } else { 141. // Fallo en la estructura de la cadena de comando 142. // Serial.println("Unexpected parameter end"); 143. stopProcess = true; 144. } 145. } 146. } 147. 148. // Procesado finalizado. Resetear el flag. 149. eocFound = false; 150. 151. // Comprobar que se hayan recibido (exáctamente) los parámetros esperados 152. if (integersCount == INTEGERS_EXPECTED && floatsCount == FLOATS_EXPECTED) {

153. // Tomar los enteros 154. Dir_r = integers[0]; 155. // Tomar los flotantes 156. c_r = floats[0]; 157. K3_r = floats[1]; 158. K2_r = floats[2]; 159. K1_r = floats[3]; 160. Ki_r = floats[4]; 161. Kp_r = floats[5]; 162. xt_r = floats[6]; 163. xg_r = floats[7]; 164. } 165. // En cualquier caso, se descartan los valores almacenados 166. integersCount = integersNextIndex = 0; 167. floatsCount = floatsNextIndex = 0; 168. } 169. } 170. 171. 172. void sendData () { 173. Serial.print("^"); 174. Serial.print(Kp); Serial.print(","); 175. Serial.print(Ki); Serial.print(","); 176. Serial.print(K1); Serial.print(","); 177. Serial.print(K2); Serial.print(","); 178. Serial.print(K3); Serial.print(","); 179. Serial.print(c); Serial.print(","); 180. Serial.print(bucle); Serial.print(","); 181. Serial.print(Dir); Serial.print(","); 182. Serial.print(Estado); Serial.print(","); 183. Serial.print(xg); Serial.print(","); 184. Serial.print(xt); 185. 186. delay(1); 187. } 188. 189. 190. 191. void getBluetoothData () { 192. while (Serial.available() > 0) { 193. xg_r = Serial.parseFloat();

Page 91: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

75

75 Aplicación móvil para el control via bluetooth de un vehículo autoequilibrado

194. xt_r = Serial.parseFloat(); 195. Kp_r = Serial.parseFloat(); 196. Ki_r = Serial.parseFloat(); 197. K1_r = Serial.parseFloat(); 198. K2_r = Serial.parseFloat(); 199. K3_r = Serial.parseFloat(); 200. c_r = Serial.parseFloat(); 201. Dir_r = Serial.parseInt(); 202. } 203. }

Page 92: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

Apendice A: Código para Arduino

76

Page 93: Trabajo de Fin de Grado - Universidad de Sevillabibing.us.es/proyectos/abreproy/91391/fichero/memoria.pdf · Resumen El presente proyecto de fin de grado se centra en el desarrollo

77

APENDICE B: CÓDIGO PARA ANDROID

Dada su extensión, el código relativo a la aplicación móvil se adjunta en el CD que contiene todos los datos de

este proyecto.