Documentos de Académico
Documentos de Profesional
Documentos de Cultura
2020 - 2021
Dedicatoria
1
Agradecimientos
Quiero agradecer a mi tutora Jenifer, que, a pesar de estar ocupada a más no poder, siempre ha
sacado tiempo para ayudarme con este proyecto. A mi padre, mi madre y a mi hermano, por el
apoyo que me han dado durante toda mi estancia en la Universidad.
2
Resumen
Internet of Things (IoT) o Internet de las Cosas representa para muchos el futuro tecnológico
a seguir por nuestra sociedad. Este concepto busca relacionar los objetos cotidianos o entidades
fı́sicas con la red digital o Internet. Esto permite construir un flujo constante de intercambio
de información entre sensores fı́sicos y la red, y, en función de las medidas recibidas, envı́ar una
serie de acciones que han de ser realizadas por unos actuadores.
Una buena forma de potenciar el desarrollo de IoT de cara al futuro, es promover su u-
so entre los estudiantes, despertando su interés para aprender a usar estas tecnologı́as. Para
este propósito, Arduino es una herramienta perfecta. Es un dispositivo IoT cuyos sensores y
actuadores son totalmente adaptables para construir sistemas de diversa complejidad.
Aunque Arduino es un sistema intuitivo, destinado para usuarios de todos los niveles, en
muchas ocasiones, puede existir una gran barrera de entrada para aquellos usuarios que no
posean conocimientos de electrónica o programación, necesarios para construir cualquier sistema
y la lógica que trata la información.
Para eliminar esa barrera, se ha desarrollado Arduino Modelling Tool, facilitando a los nuevos
usuarios la posibilidad de crear sus propios sistemas IoT, despertando ası́ interés en jóvenes
estudiantes e ingenieros en tecnologı́as IoT. Esto garantizará la continuidad en los avances para
esta corriente tecnológica, cuyos beneficios ya son visibles hoy dı́a en lo que se conoce como smart
cities o en dispositivos que utilizamos de forma cotidiana como los wearable devices.
Arduino Modelling Tool es una herramienta diseñada para construir sistemas Arduino a través
de una interfaz sencilla e intuitiva, compuesta por una barra de herramientas desde la que se
arrastran y sueltan los distintos elementos en un tapiz, formando ası́ un modelo que representa
Arduino y sus elementos.
Para construir esta herramienta, se ha seguido un desarrollo basado en Model Driven Devel-
opment (MDD) y concretamente un Modelado Especı́fico de Dominio, que permite al usuario
inexperto crear sus propios sistemas Arduino sin tener que preocuparse de errores en el código.
Además, gracias a su flexibilidad, los usuarios más avanzados podrán construir sistemas más
complejos de forma más rápida o crear sus prototipos antes de diseñarlos fı́sicamente. Esto se
consigue a través de la generación automática de código, integrado en la herramienta Arduino
Modelling Tool, capaz de generar el código que da soporte al modelo creado por el usuario de
forma instantánea y libre de errores.
Arduino Modelling Tool está basada en un Modelo Especı́fico de Dominio y su Lenguaje, el
cual es fácilmente ampliable y personalizable según las necesidades del dominio IoT. Gracias a
esto, la herramienta posee un gran potencial para expandirse a otros dispositivos, ya sean de la
familia Arduino o simplemente se encuentren dentro del ámbito IoT.
Por lo tanto, Arduino Modelling Tool se presenta como una solución que facilite la expansión
de las tecnologı́as IoT, acercándolas a usuarios poco experimentado o facilitando el prototipado
y diseño de sistemas complejos a usuarios expertos.
3
Abstract
Internet of Things (IoT) means the technological future to be followed by our society. This
concept tries to link everyday objects or physical entities with the digital network or Internet.
IoT establishes a constant information flow between sensors and actuators through the com-
munication network. Sensors collect measures, i.e. data, which are processed by controllers;
controllers determine a response, depending on the received measures and ask actuators the
response’s execution.
To encourage IoT development for the future, it is important to promote its use among
students, creating interest to learn these technologies. Arduino is a perfect candidate to deal
with this purpose. Arduino is an IoT device which allows to build different systems varying their
complexity due to the wide amount of compatible sensors and actuators it provides in order to
make multiple configurations.
However, although Arduino is an easy learning tool in general, sometimes, user without
electronics or programming knowledge, have problems to build a complete system with its pro-
gramming logic.
Arduino Modelling Tool has been developed with the objective of eliminating this barrier,
making the creation of IoT systems easier for new users. As a result, it may increase the interest
in students or future engineers in IoT technologies. This will ensure further progress for this
technological approach, which benefits are already evidenced in areas such as smart cities or IoT
use every day, such as wearables devices.
Arduino Modelling Tool has been designed to build Arduino systems through a simple and
intuitive graphical user interface that allows modelling Arduino systems by drawing and dropping
the Arduino components from a toolbar.
To build this tool, a Model Driven Development (MDD) approach has been followed, and
specifically using domain models. The model abstraction of MDD allows inexperienced users to
create their own Arduino systems without being worry about coding bugs. In addition, thanks
to its flexibility, advanced users are able to build and prototyping their complex systems more
efficiently. These advantages are achieved thanks to the automatic code generation templates,
integrated within the Arduino Modelling Tool, witch generates the code from the user model
created by the user, instantly and free of bugs.
Arduino Modelling Tool is based on a Domain Specific Model and its corresponding Language,
which are easily extensible and customisable according to the IoT domain needs. As a result,
this tool has a great potential to be extended to other devices and services, whether they belong
to the Arduino family or simply fall within the IoT scope.
As a conclusion, Arduino Modelling Tool is a solution to expend the IoT technologies, bringing
them closer to inexperienced users or facilitating the prototyping and design of complex systems
for expert users.
4
Índice
Listado de Figuras 6
1 Introducción 10
1.1 Motivación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.2 Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.3 Estructura PFG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2 Conceptos previos 12
2.1 Internet of Things (IoT) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.2 Arduino . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.2.1 ¿Qué es Arduino? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.2.2 Arduino UNO: Componentes básicos . . . . . . . . . . . . . . . . . . . . . 13
2.2.3 Arduino IDE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.2.4 Construyendo un primer proyecto . . . . . . . . . . . . . . . . . . . . . . . 19
2.3 MDD y DSL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.3.1 Modelos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.3.2 Model Driven Development (MDD) . . . . . . . . . . . . . . . . . . . . . . 23
2.3.3 Lenguaje Especı́fico de Dominio (Domain-Specific Languages DSL) . . . . 24
6 Conclusiones 104
6.1 Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
6.2 Trabajos futuros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
6.3 Impactos sociales y ambientales . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
Bibliografı́a 106
5
Listado de Figuras
6
3.14 Elementos del lenguaje: Condition . . . . . . . . . . . . . . . . . . . . . . . . . . 42
3.15 Domain Enumeration: Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
3.16 Relaciones del lenguaje: LoopHasSentence . . . . . . . . . . . . . . . . . . . . . . 43
3.17 Elementos del lenguaje: SimpleSentence . . . . . . . . . . . . . . . . . . . . . . . 44
3.18 Elementos del lenguaje: ManualSentence . . . . . . . . . . . . . . . . . . . . . . . 44
3.19 Elementos del lenguaje: IOSentence . . . . . . . . . . . . . . . . . . . . . . . . . 45
3.20 Elementos del lenguaje: Delay . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
3.21 DSL-Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
3.22 DSL-Editor : Compartimentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
3.23 Toolbox Icon: If . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.24 Toolbox Icon: While . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.25 Toolbox Icon: For . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.26 Toolbox Icon: Condition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.27 User Toolbox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
3.28 Main: ArduinoDevice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
3.29 Main: Measure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
3.30 Main: Value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
3.31 Main: Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.32 Inputs: Switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.33 Inputs: Potentiometer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.34 Inputs: LightSensor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.35 Inputs: TemperatureSensor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.36 Inputs: LightSensor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.37 Inputs: IRSensor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.38 Outputs: LED . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
3.39 Outputs: DCMotor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
3.40 Outputs: Terminal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
3.41 Outputs: Display . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
3.42 Logical: For . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
3.43 Logical: While . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
3.44 Logical: If . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
3.45 Logical: Condition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
3.46 Logical: IOSentence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
3.47 Logical: ManualSentence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
3.48 Logical : Diagrama lógico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
3.49 Connectors: Controller-Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
3.50 Connectors: Controller-Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
3.51 Outputs - Image Shape . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
3.52 Shape Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
3.53 Shape examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
3.54 Connector Mapping: ArduinoDeviceHasOutputs . . . . . . . . . . . . . . . . . . . 55
3.55 CnctOutputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.56 Parent Element Path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.57 Custom Parent Element Path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
7
4.11 Manual de Usuario: Crear Controller . . . . . . . . . . . . . . . . . . . . . . . . . 63
4.12 Manual de Usuario: Redimensionar Controller . . . . . . . . . . . . . . . . . . . 63
4.13 Manual de Usuario: crear ManualSentence . . . . . . . . . . . . . . . . . . . . . . 64
4.14 Manual de Usuario: CnctSentence . . . . . . . . . . . . . . . . . . . . . . . . . . 64
4.15 Manual de Usuario: Ordenar Sentencias . . . . . . . . . . . . . . . . . . . . . . . 64
4.16 Manual de Usuario: Controller-Input/Output . . . . . . . . . . . . . . . . . . . . 65
4.17 Manual de Usuario: IOSentence . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.18 Manual de Usuario: Crear Bucles . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
4.19 Manual de Usuario: Propiedades for . . . . . . . . . . . . . . . . . . . . . . . . . 66
4.20 Manual de Usuario: Crear Condition . . . . . . . . . . . . . . . . . . . . . . . . . 67
4.21 Manual de Usuario: Propiedades Condition . . . . . . . . . . . . . . . . . . . . . 67
4.22 Manual de Usuario: Ejemplo while . . . . . . . . . . . . . . . . . . . . . . . . . . 68
4.23 Flujo de Trabajo: New Item... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
4.24 Flujo de Trabajo: Creando fichero Arduino . . . . . . . . . . . . . . . . . . . . . 80
4.25 Flujo de Trabajo: Fichero Arduino en Solution Explorer . . . . . . . . . . . . . . 80
4.26 Flujo de Trabajo: Ejemplo Modelo . . . . . . . . . . . . . . . . . . . . . . . . . . 81
4.27 Flujo de Trabajo: Plantilla1.ino . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
4.28 Flujo de Trabajo: Copiar Código en Arduino IDE . . . . . . . . . . . . . . . . . . 82
8
Código
9
Capı́tulo 1
Introducción
1.1 Motivación
En 2021, Internet ya se ha implantado completamente en nuestra sociedad en la mayorı́a de
lugares del planeta. Por tanto, ¿cuál es el siguiente paso? Para muchos, la respuesta es Internet
of Things (IoT) o Internet de las Cosas, una visión de futuro que busca conectar entidades
digitales con objetos fı́sicos.[1]
Esta aspiración que hace unos años parecı́a muy lejana, ya es el presente. Aunque puede
resultar a primera vista un concepto muy abstracto, en muchos casos está presente en la vida
cotidiana, en lo que se concoce como smart devices o dispositivos inteligentes. Estos disposi-
tivos son cada vez más accesibles y forman parte del dı́a a dı́a, como, por ejemplo, los relojes
inteligentes.
Desgraciadamente, un evento reciente ha mostrado el verdadero potencial de este tipo de
tecnologı́as, la pandemia del virus COVID-19 está siendo combatida en muchas ocasiones a
través de dispositivos inteligentes. Por ejemplo, ambulancias habilitadas con tecnologı́a IoT,
enviando en tiempo real el estado del paciente al hospital, que se puede preparar para recibirle
en las mejores condiciones. Pacientes que pueden ser atendidos desde sus casas ya que cuentan
con equipos que son capaces de monitorizar las constantes vitales y mandarlas través de la red
hasta sus médicos. Esta aplicación en el ámbito sanitario es denominada como Internet of Health
Thingso por su término más común e-Health. E-Health es un claro ejemplo de la importancia y
capacidad que pueden alcanzar las tecnologı́as IoT en un futuro próximo. [2]
Para poder continuar la expansión de este concepto tecnológico es necesario formar e incen-
tivar su uso y conocimiento desde edades tempranas hasta los futuros estudiantes de ingenierı́a
para que recojan el legado de lo que se ha conseguido hasta ahora y que aporten nuevas ideas que
ayuden a mejorar el estado actual de las tecnologı́as IoT. Una buena forma de despertar interés
a jóvenes y acercar este mundo IoT es a través de Arduino [3]. Un proyecto que promueve el
uso de IoT a todos los niveles y ámbitos, desde el nivel escolar hasta las empresas más punteras
en robótica o Inteligencia Artificial.[4]
Aunque Arduino es accesible desde edades tempranas, en numerosas ocasiones puede exis-
tir una gran barrera de entrada debido a que los usuarios no poseen conocimientos previos de
electrónica y, sobre todo, de programación, necesarios para crear cualquier programa en este dis-
positivo. Este proyecto nace de la necesidad de romper esa barrera y crear una herramienta capaz
de facilitar el comienzo a aquellos nuevos usuarios interesados en el aprendizaje de las tecnologı́as
IoT, que pueden ser desde jóvenes estudiantes hasta ingenieros que no poseen conocimientos de
programación y les gustarı́a crear sus propios proyectos con Arduino.
1.2 Objetivos
El objetivo de este proyecto es crear una herramienta que tenga la capacidad de potenciar
e incentivar el uso de dispositivos IoT, haciendo uso de una interfaz intuitiva y sencilla que
permita facilitar el uso y aprendizaje del sistema Arduino, tanto a usuarios sin unos conocimientos
previos sobre programación o electrónica como a aquellos usuarios más avanzados, para los cuales
supondrá una notable agilización el construir programas más complejos.
Los objetivos especı́ficos de este Trabajo de Fin de Grado son los siguientes:
10
Crear una herramienta gráfica de modelado que proporcione soporte dicho lenguaje, a
través de la cual el usuario pueda elegir distintas configuraciones para Arduino.
Crear un compilador del Modelado Especı́fico de Dominio, capaz de leer dicha configuración
introducida por el usuario y generar el código de soporte necesario de Arduino.
Diseñar un conjunto de pruebas que validan la completitud del lenguaje, construyendo
diferentes configuraciones de Arduino y generando diferentes programas.
Validar el modelo y la herramienta mediante la ejecución de las pruebas diseñadas.
11
Capı́tulo 2
Conceptos previos
2.2 Arduino
2.2.1 ¿Qué es Arduino?
Arduino es una plataforma open-source utilizada para la construcción de proyectos de electrónica.
Su entorno se compone de dos partes fundamentales: una placa de circuito programable y un
IDE Integrated Development Environment que se utiliza para cargar el código de un programa.
[7]
Esta placa, no deja de ser un sencillo ordenador en el que se puede cargar código y ejecutar
diversos programas. El propósito principal de Arduino no era otro que ofrecer a jóvenes estudi-
antes sin conocimientos previos en electrónica ni programación una herramienta sencilla pero con
mucho potencial con la cual iniciarse en este ámbito. Sin embargo, con el paso del tiempo y el
crecimiento de la comunidad Arduino, ha ampliado los horizontes del dispositivo hasta incluirse
en proyectos de IoT (Internet of Things), impresoras 3D o en wearable technologies, entre otros.
Existen multitud de modelos de microcontroladores Arduino, en este TFG se va a utilizar
la versión más básica, Arduino UNO, la cual es capaz de leer diversas entradas (inputs), como
la luz en un sensor o la pulsación en un botón y devolver unas salidas (outputs), por ejemplo,
encendiendo un LED o un motor.
12
2.2.2 Arduino UNO: Componentes básicos
A continuación se explican los componentes básicos de los que se compone este kit de Arduino
(ver Figura 2.1).
13
Figure 2.3: Protoboard Breadboard 400 points
Entradas: también denominadas Inputs son los elementos encargados de recopilar infor-
mación del entorno. Existen numerosos tipos de estos elementos, dependiendo del tipo de
variable que monitoricen. A continuación, se detallan los utilizados en este PFG:
– Sensor de temperatura: Cambia su voltaje de salida en función de la temperatura del
entorno. En términos de código, producirá una salida HIGH o LOW en función de la
temperatura detectada en el entorno (ver Figura 2.4).
14
– Pulsador: componente que permite el paso de corriente cuando es pulsado y lo impide
cuando no se encuentra pulsado. (ver Figura 2.7)
Sensor de infrarrojos: gracias a su rayo de infrarrojos permite detectar cuándo está cerca
de algún otro objeto, encendiendo ası́ su LED integrado y emitiendo una señal HIGH o
LOW. La distancia de detección se puede ajustar gracias a su potenciómetro (ver Figura
2.8).
Salidas: también denominadas Outputs, son aquellos elementos actuadores que producen
un cambio en el entorno. El principal uso de este tipo de elemento será realizar una
determinada acción en función de la información recopilada por las entradas, siguiendo
unas reglas que haya determinado el usuario.
– Diodos emisores de luz (LEDs): un tipo de diodo que se ilumina cuando la electricidad
pasa por él. En este kit se incluyen diversos diodos LED de distintos colores (ver Figura
2.10).
15
Figure 2.10: Diodo LED Rojo
16
Figure 2.13: LCD Alfanumérico JHD659
17
de herramientas permite verificar el código, subir los programas y crear, abrir o guardar los
sketches (ver Figura 2.15).
Será en el editor de texto donde se deberá escribir el código de cualquier sketch (ver Figura
2.16). Al crear cualquier sketch, Arduino IDE proporciona la plantilla que incluye los dos métodos
principales.
void setup(): Este método se ejecuta una vez cuando se inicia o reinicia la placa.
18
2.2.4 Construyendo un primer proyecto
Todo sketch de un Arduino estará compuesto por unas entradas y unas salidas, pudiendo
ser estas del entorno o fı́sicas, es decir, que involucran un componente fı́sico por el que recibir
información en caso de tratarse de una entrada, o en caso contrario, de dar información si
hablamos de una salida. Por tanto, cuando se quiera construir un nuevo sketch, se debe tener claro
cómo se quiere tratar la información proporcionada por los diferentes inputs determinando una
serie de reglas y condiciones para ası́ conseguir obtener la salida deseada. Este comportamiento
lo representa el esquema de la Figura 2.17.
Una vez comprendido esto, es hora de construir el primer sketch, para ello Arduino IDE
proporciona unos sencillos ejemplos ya codificados, a los que se puede acceder desde File/Ex-
amples. Aquı́ se encuentran divididos por categorı́as, para este ejemplo se accede a la categorı́a
Basics/Blink (ver Figura 2.18).
19
Figure 2.18: Interfaz Arduino IDE: Examples
Todos estos ejemplos cuentan con un comentario al inicio en el que se explica el funcionamiento
del sketch y otros detalles. Por ejemplo, el sketch Blink de los ejemplos básicos (ver Figura 2.18),
incluye un comentario donde se especifica que el código consiste en que el LED integrado en la
placa Arduino parpadee (ver Código 2.1).
20
1 /*
2 Blink
3
4 Turns an LED on for one second, then off for one second, repeatedly.
5
6 Most Arduinos have an on-board LED you can control. On the UNO, MEGA and ZERO
7 it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN is set to
8 the correct LED pin independent of which board is used.
9 If you want to know what pin the on-board LED is connected to on your Arduino
10 model, check the Technical Specs of your board at:
11 https://www.arduino.cc/en/Main/Products
12
13 modified 8 May 2014
14 by Scott Fitzgerald
15 modified 2 Sep 2016
16 by Arturo Guadalupi
17 modified 8 Sep 2016
18 by Colby Newman
19
20 This example code is in the public domain.
21
22 http://www.arduino.cc/en/Tutorial/Blink
23 */
24
25 // the setup function runs once when you press reset or power the board
26 void setup() {
27 // initialize digital pin LED_BUILTIN as an output.
28 pinMode(LED_BUILTIN, OUTPUT);
29 }
30
31 // the loop function runs over and over again forever
32 void loop() {
33 digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
34 delay(1000); // wait for a second
35 digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
36 delay(1000); // wait for a second
37 }
En el caso en el que también se quiera que parpadee el LED que se instalará en nuestra
protoboard, es necesario declarar nuestro LED de la siguiente manera: se declara led al principio
del código y se iguala al pin al que se enchufa en la placa. Cuando se llama a los métodos
pinMode y digitalWrite hay que pasar por parámetro el nuevo led en lugar del integrado (ver
Código 2.2).
Ahora hay que montar los componentes en nuestra placa, en el enlace que aparece en el
fragmento 2.1, lı́nea 22, se pueden encontrar los componentes necesarios (ver Figura 2.19).
21
Figure 2.19: Sketch Blink: Hardware
En primer lugar, es necesario conectar el cable de corriente al pin número 13 y realizar las
conexiones necesarias a la toma de tierra (GND) y a la placa protoboard, como se muestra en la
Figura 2.20.
Después, se enchufa la placa Arduino con el cable USB al ordenador, verificar el código por si
existe algún fallo y, en caso de que todo funcione correctamente, cargarlo en la placa. Se puede
observar como el LED se enciende y se apaga con un segundo de diferencia (ver Figura 2.21). [9]
22
Figure 2.21: Sketch Blink : LED encendido
23
Su principal ventaja y el objetivo por el que fue creado es incrementar la productividad a
la hora de desarrollar un sistema. Se busca reducir el tiempo de desarrollo y a la vez los
costes, generando el código a partir de los modelos.
Mantenibilidad y portabilidad: con el uso de modelos se busca obtener una arquitectura
sostenible, en la que se puedan hacer cambios rápido y de manera consistente. En caso de
que necesitemos migrarnos de tecnologı́a, los modelos son independientes de los detalles de
la implementación, por ello es más fácil realizar los cambios para efectuar la migración.
Adaptabilidad: realizar cambios en la función de un componente resulta más rápido y
directo gracias a que la inversión en automatización ya se ha realizado previamente.
MDD cuenta con dos enfoques de adopción: MDA (Model Driven Architecture) y Software
Factories. MDA se asocia con un enfoque dirigido por modelos en el que el código es generado
automáticamente desde modelos abstractos, y que emplea un lenguaje estandarizado (creado por
OMG en 2001) para describir dichos modelos y las transformaciones entre ellos [12]. Su objetivo
principal es que en caso de producirse un cambio o avance tecnológico, este no afecte gravemente
a la arquitectura del sistema [11].
Sin embargo, para este PFG se ha basado en el enfoque Software Factories. Impulsado por
Microsoft, esta corriente busca la reutilización de componentes software para generar un código
de calidad. Soportado por la tecnologı́a DSL Tools utilizada en este proyecto, la cual permite
crear un Lenguaje Especı́fico de Dominio, utilizado para generar código de forma totalmente
adaptable a las necesidades del usuario [11].
24
proporciona unas plantillas sobre las que construir un lenguaje DSL, para ello tiene definido un
tipo de proyecto especı́fico (ver Figura 2.22).
La interfaz de Visual Studio se divide en tres partes bien diferenciadas, que son, de izquierda
a derecha en la Figura 2.23, la Toolbox, el Diagrama de Clases y el DSL Explorer.
25
Figure 2.24: Visual Studio: Diagrama de Clases
En la parte derecha de la Figura 2.23 se encuentra el DSL Explorer, desde el que se puede
acceder de forma rápida a los elementos del lenguaje. Destacar el apartado Editor que, aunque
se explicará más adelante, permite personalizar la caja de herramientas que utilizará el usuario
final para crear su modelo (ver Figura 2.25).
Al hacer botón derecho sobre Editor se pueden añadir tantos compartimentos como se nece-
siten (ver Figura 2.26).
26
Figure 2.26: DSL-Editor: Add New Toolbox Tab
Dentro de cada compartimento se pueden crear dos tipos de objetos, elementos y conectores.
Los elementos son las distintas clases que se han definido en el modelo, mientras que los conectores
son capaces de relacionar aquellas clases que tengan una relación referencial (ver Figura 2.27).
Además, en la pestaña de Propiedades, se puede personalizar el nombre del compartimento que
visualizará el usuario.
En las Propiedades de cada objeto que se ha creado, ya sea conector o elemento, se deben
definir distintos parámetros: la clase del modelo a la que hace referencia, el nombre que visualizará
el usuario al usar la herramienta, o el icono que representará dicho objeto en la Toolbox (ver
Figura 2.28). El resultado final de la Toolbox que verá el usuario se presentará en la siguiente
sección (ver Figura 3.27).
27
Figure 2.28: DSL-Editor: Element Properties
Por último, explicar la parte izquierda de la Figura 2.23, donde se encuentra la Toolbox desde
la que se pueden arrastrar sus elementos hasta el diagrama (ver Figura 2.29).
Los principales elementos que se han utilizado para este proyecto se encuentran explicados a
continuación:
Domain Class (Clase de Dominio): se trata del elemento básico de nuestro lenguaje. Se
utilizará para definir los conceptos que queremos modelar. Por ejemplo, si un lenguaje
está orientado a una biblioteca multimedia, una clase de dominio podrı́a ser ”Canción”
o ”Foto”. Además podemos añadir diversas propiedades para completar la definición de
28
nuestra clase, pudiendo ser estas del tipo String, Int o DateTime entre otros, de carácter
público o privado (Figura 2.30). [15]
Los elementos explicados hasta ahora se construyen sobre la parte izquierda del diagrama
Classes and Relationships (ver Figura 2.24). Sin embargo, los tres elementos explicados a conti-
nuación tienen una naturaleza diferente, se construyen sobre la parte derecha, Diagram Elements
(ver Figura 2.24) y necesitan ser asociados a una clase o una relación.
Image Shape (Forma de Imagen): asociado a una clase, este elemento permite asignar la
imagen que representará a una clase sobre el tapiz. Un elemento similar es Geometry Shape,
que en lugar de una imagen, asociará una forma geométrica. En ambos tipos se pueden
definir Decoradores o Decorators, que permiten mostrar propiedades de la clase a la que
están asociados, por ejemplo, para mostrar un nombre (Figura 2.33).
29
Figure 2.33: Componentes DSL: Image and Geometry Shape
Connector (Conector): permite personalizar la apariencia que tendrá el conector que une
dos clases en una relación sobre el tapiz. Para el caso de las relaciones referenciales, es
obligatorio asignar un conector, sin embargo, para las relaciones embedding, es opcional
(Figura 2.34).
Como se ha explicado, para permitir posteriormente definir una metáfora gráfica, se hará
uso de dos elementos, Image Shape y Geometry Shape. La primera nos permite una asociar una
imagen determinada a la clase elegida, la segunda es algo más limitada, ya que únicamente nos
permite asociar una determinada forma geométrica, a continuación, se explica cómo hacerlo.
En las propiedades de una Image Shape, en el apartado de recursos se debe importar la imagen
que debe representar la clase en cuestión, además se puede cambiar el tamaño de la imagen con
las propiedades Initial Height e Initial Weight (Figura 2.35). En el caso de una forma geométrica
no será necesario seleccionar la imagen, sino la forma geométrica y colores deseados (Figura 2.36).
Ambas formas permiten agregar decoradores o decorators, los cuales permiten mostrar pro-
piedades de la clase asociada a la shape, ya sea en forma de texto o de icono (ver Figura 2.37).
30
Figure 2.37: Geometry Shape: Add Decorator
Todas las formas que representan entradas o salidas en el modelo cuentan con un Text Decora-
tor que añade un cuadro de texto con el nombre que ha designado el usuario a dicho componente.
Esto es posible ya que las clases abstractas Inputs y Outputs (ver Figuras 3.2 y 3.8) cuentan con
una propiedad Name, heredada por las clases hijas y que ha sido enlazada a dicho Text Decorator
de las Image Shapes asociadas a cada entrada y salida.
Para conseguir este enlace entre el decorador y la propiedad en cuestión de la clase es nece-
sario relacionarlo manualmente. Accediendo a las propiedades de la relación entre la clase y la
forma asociada, en la pestaña Decorator Maps, se selecciona el Decorator a enlazar, mientras que
en el desplegable Display Property aparecerán todas las propiedades (heradadas o propias) que
posea la clase y se deberá elegir la propiedad a enlazar con el decorador.
31
Capı́tulo 3
ArduinoDevice: esta clase representa el dispositivo Arduino al que irán unidos todos
los componentes, tanto fı́sicos como lógicos. Es por esto que son sumamente relevantes
las relaciones que se han definido en el lenguaje para esta clase, siendo las siguientes (ver
Figura 3.1):
32
Figure 3.1: Elementos del lenguaje: ArduinoDevice
Inputs: esta clase de dominio representa los distintos Inputs que se emplearán en un
sketch. El marco discontinuo de la clase indica que se trata de una clase abstracta, es
decir, no se podrán instanciar objetos de esta clase, sino que se deberán crear objetos de
alguna de las clases hijas de esta, que heredarán las DomainProperties definidas en la clase
padre, Inputs (ver Figura 3.2).
33
Figure 3.2: Elementos del lenguaje: Inputs
La clase Inputs tiene dos clases hijas, ManualInput y Sensor, esta división se ha realizado
para diferenciar los dos tipos de entradas que existen. Aquellas controladas por el usuario
serán las entradas manuales, mientras que las que reciban información del entorno serán los
sensores. Además, esto facilitará la generación de código, como se explicará más adelante.
– ManualInput: esta clase (ver Figura 3.3) contiene la herencia con las clases Switch
y Potentiometer, las cuales representan un interruptor (ver Figura 2.7) y un poten-
ciómetro (Figura 2.9), respectivamente.
34
– Sensor : esta clase (ver Figura 3.4) contiene la herencia de las clases LightSensor (sen-
sor de luz, ver Figura 2.6), TemperatureSensor (sensor de temperatura, ver Figura
2.4), IRSensor (sensor de infrarrojos, ver Figura 2.8) y TiltSensor (sensor de incli-
nación, ver Figura 2.5).
Measure: esta clase permite almacenar las medidas recopiladas por nuestros Inputs (ver
Figura 3.5). A través de las propiedades definidas se podrán establecer un nombre para
esta variable y su tipo.
– InputHasMeasure: esta relación permite asociar una medida al Input que la re-
copila. Debido a que una Measure no tiene sentido sin la existencia de un Input y
viceversa, la cardinalidad es de 1 a 1 en ambos sentidos.
35
Figure 3.5: Relaciones del lenguaje: InputHasMeasure
Value: esta clase permite traducir la medida almacenada en Measure a un valor que el
usuario pueda interpretar (ver Figura 3.6).
– MeasureHasValue: esta relación permite asociar una medida a un valor, para ası́
poder traducirlo. Una medida podrá tener o no un valor asociado, por tanto la
cardinalidad será de 0 a 1, sin embargo un valor ha de tener una y solo una medida
asociada, cardinalidad 1 a 1.
Como se puede observar en la figura 3.6, las propiedades DataType y Type son de tipos
distintos a los habituales. Se han creado dos Enumeraciones de dominio, DataType
permite al usuario definir el tipo de variable de Measure, mientras Values representa un
tipo de medida común, por ejemplo Celsius, útil para el sensor de temperatura. Ambos
enumeradores son fácilmente ampliables en caso de ser necesario.
36
(b) Values
(a) DataType
Outputs: a partir de esta clase de dominio crean las salidas necesarias. Al igual que la
clase Inputs, es abstracta, por lo que se deberán crear las salidas instanciando alguna de
las clases hijas (Figura 3.8).
De igual manera que para las entradas, se ha realizado esta herencia ya que las clases
hijas de EnvironmentalOutputs poseen una propiedad en común, el PinNumber al que se
conectan a la placa, a diferencia de la salida Terminal.
37
Figure 3.9: Elementos del lenguaje: EnvironmentalOutputs
38
Figure 3.11: Elementos del lenguaje: Controller
Sentence: una sentencia es el elemento básico que se empleará para establecer el tra-
tamiento de la información que se recopila desde los Inputs. Permitirá construir la lógica
de un programa, creando condiciones, repeticiones, operaciones, etc. En definitiva, esta
clase ofrece la posibilidad de crear un simple lenguaje de programación que le permitirá
abstraerse de la sintaxis concreta del lenguaje Arduino y de la compilación del mismo, ya
que el código asociado se generará automáticamente, libre de errores.
La relación referencial de una sentencia a otra nace de la necesidad de establecer un orden
entre las mismas, ya que, como en cualquier lenguaje de programación, es sumamente
importante el orden de ejecución de las lı́neas de código. Por tanto, esta relación representa
dicho orden entre sentencias, el usuario podrá enlazar a través de un conector las distintas
sentencias para establecer dicha ordenación (ver Figura 3.12).
La clase Sentence será abstracta, por lo que se tendrá que hacer uso de sus clases hijas,
explicadas a continuación.
39
Figure 3.12: Elementos del lenguaje: Sentence
Loop: esta clase es una de las hijas de Sentence, la cual también es abstracta, por lo que
no es posible instanciarla directamente, sino que se ha de hacer uso de sus clases hijas: If,
While y For, las cuales representan las repeticiones básicas de la programación. Destacar
que If se ha incluı́do como hija de la clase Loop ya que comparte propiedades con el resto
de bucles, y es que también posee una condición que se ha de cumplir para que se ejecuten
las sentencias que contiene en su interior, solo que, a diferencia de While y For, estas
sentencias se ejecutarán únicamente una vez.
Como propiedades de dominio adicionales, la clase For presenta cómo se actualiza la variable
que controla el número de repeticiones del bucle (Increment) y el valor de inicio de dicho
número (Initialization), para que el usuario pueda elegir el comportamiento de dicho bucle
(ver Figura 3.13).
40
Figure 3.13: Elementos del lenguaje: Loop
Condition: esta clase permite construir las condiciones de los bucles. Una condición se
compone de dos operandos y un operador, resultando en las tres propiedades definidas para
esta clase (ver Figura 3.14).
41
Figure 3.14: Elementos del lenguaje: Condition
Todo bucle incluye sentencias dentro de él. Para ello se debe definir en cada clase hija de
Loop la siguiente relación embebida LoopHasSentence (donde Loop corresponderá al bucle
hijo en cuestión), la cual nos permitirá ”arrastrar y soltar” sentencias de cualquier tipo
dentro de un bucle. De forma que, un bucle puede tener de 0 a n sentencias, y esta puede
pertenecer a 0 o 1 bucle (ver Figura 3.16).
42
Figure 3.16: Relaciones del lenguaje: LoopHasSentence
SimpleSentence: esta clase permite representar cualquier tipo de sentencia que no sea
compleja, es decir, que no incluya sentencias dentro, como las hijas de la clase Loop (ver
Figura 3.17). Es abstracta, por lo que no se podrá instanciar directamente, sino que se
hará uso de alguna de sus clases hijas, las cuales se explicarán a continuación.
43
Figure 3.17: Elementos del lenguaje: SimpleSentence
IOSentence: esta clase permite inyectar el código correspondiente a los principales mé-
todos de lectura y escritura que utilizan nuestras distintas entradas y salidas, los más
comunes son digitalRead(), digitalWrite(), analogRead() y analogWrite().
Sin embargo, en otras ocasiones será necesario hacer uso de otros métodos, por esta razón
será de importancia saber el tipo de Input u Output del que se está haciendo uso, de ahı́
44
la necesidad de definir dos relaciones referenciales, una para las entradas y otra para las
salidas.
Estas relaciones permitirán enlazar este tipo de sentencia con el componente del cual se
quiera crear una sentencia de lectura o escritura e inyectar el código correspondiente al
método que utiliza dicho componente, abstrayendo al usuario de la sentencia concreta que
se debe utilizar en cada caso (ver Figura 3.19).
45
Figure 3.21: DSL-Editor
La Toolbox estará compuesta por distintos Toolbox Tabs, que agruparán elementos de natu-
raleza similar (ver Figura 3.22).
A continuación, se explican los distintos compartimentos y los elementos que contienen cada
uno:
Connectors: los conectores se emplean para relacionar distintos elementos del modelo.
Se han definido tres tipos de conectores en nuestro lenguaje: CnctOutputSentence para
46
relacionar sentencias de entrada/salida con una salida (ver Figura 3.19, relación IOSen-
tenceReferencesOutputs), CnctIntputSentence para relacionar sentencias de entrada/salida
con una entrada (ver Figura 3.19, relación IOSentenceReferencesOutputs) y CnctSentence
para establecer el orden entre las distintas sentencias (ver Figura 3.12, relación SentenceRef-
erencesTargetSentence)
Inputs: en este apartado se encuentran las distintas entradas que se pueden utilizar en un
sketch. Los elementos incluı́dos en este compartimento hacen referencia a las clases hijas
de ManualInput (ver Figura 3.3) y Sensor (ver Figura 3.4).
Logical: este compartimento comprende aquellos elementos que se emplearán para con-
struir el apartado lógico del programa, es decir, las distintas clases hijas de la clase Sentence
(ver Figuras 3.12, 3.13 y 3.17).
Main: en el apartado Main o Principal se encuentran elementos imprescindibles para la
creación de cualquier sketch. ArduinoDevice sirve para instanciar la clase ArduinoDevice
(ver Figura 3.1), Controller instancia la clase Controller (ver Figura 3.11), mientras que
Measure y Value hacen referencia a las clases Measure y Value del modelo (ver Figura 3.6).
Outputs: en este apartado se encuentran las salidas que componen un sketch. Este Tab
hace referencia a las clases hijas de EnvironmentalOutputs (ver Figura 3.9) y a la clase
Terminal (ver Figura 3.10).
Por último, se presenta el resultado final de la Barra de Herramientas, tal y como la verı́a el
usuario (véase Figura 3.27). Comentar que los iconos empleados en el Toolbox son las mismas
imágenes que representarán a los elementos ya una vez creados sobre el tapiz. Excepcionalmente,
para representar en el Toolbox las clases If, While, For y Condition, se ha empleado un icono
personalizado para cada una, en lugar de la forma geométrica coloreada que se muestra una vez
se crean en el tapiz estas clases.
Figure 3.25: Toolbox Icon: For Figure 3.26: Toolbox Icon: Condition
47
Figure 3.27: User Toolbox
48
3.2 Notación Gráfica
La notación o metáfora gráfica asignada a cada elemento del lenguaje definido mediante el modelo
y la barra de herramientas, es fundamental para componer una interfaz de usuario intuitiva y
visualmente atractiva.
Como se ha explicado en el punto anterior, se ha definido una Toolbox o Caja de herramientas,
la cual esta compuesta por los elementos que se podrán utilizar para construir un sketch. Para
facilitar la comprensión al usuario, la Toolbox está dividida en distintos compartimentos, que
agrupan elementos con caracterı́sticas similares. Estos compartimentos han sido explicados en
la Figura 3.22, por tanto, ahora se agruparán de igual forma para explicar su metáfora gráfica.
Main
Para representar el dispositivo Arduino UNO, se ha decidido emplear una imagen real del
mismo, sobre el que se arrastran y sueltan el resto de elementos (ver Figura 3.28).
Una cinta de medir representará el elemento Measure (Figura 3.29), mientras que Value
será una gráfica sostenida por unas manos (Figura 3.30).
Controller es un cerebro con forma de chip con conexiones en la Toolbox (ver Figura 3.31),
mientras que sobre el tapiz será un rectángulo azul sobre el que dejar caer las distintas
sentencias con el icono mostrado en la esquina superior derecha.
49
Figure 3.31: Main: Controller
Inputs
Para representar un Switch se utiliza un dibujo de un interruptor (Figura 3.32), siendo el
caso de Potentiometer el dibujo de un potenciómetro (Figura 3.33).
Un nivel es la metáfora gráfica del sensor de inclinación (Figura 3.36) y la Figura 3.37
representa al sensor de infrarrojos.
Outputs
50
El dibujo de un diodo LED y un motor eléctrico, son las metáforas gráficas de LED y
DCMotor, respectivamente (ver Figuras 3.38 y 3.39).
El dibujo de un monitor representa un Terminal (ver Figura 3.40), mientras que una
pantalla LCD es la salida Display (ver Figura 3.41).
Logical
A continuación, se muestran los distintos bucles (Figuras 3.42, 3.43 y 3.44) y la metáfora
de la condición (Figura 3.45).
Las figuras 3.46 y 3.47 representan las metáforas gráficas de la sentencia de entrada/salida
y sentencia manual respectivamente.
51
Figure 3.46: Logical: IOSentence Figure 3.47: Logical: ManualSentence
Un ejemplo más real de cómo se ven representados los distintos elementos lógicos podrı́a
ser el siguiente (Figura 3.48):
Connectors
Estos dos conectores (ver Figuras 3.49 y 3.50 ) representan la relación referencial entre
IOSentence y una entrada o salida (ver figura 3.19). El tercer y último conector se empleará
para establecer el orden entre las distintas sentencias de un controlador. Un ejemplo de
este conector se puede observar en el ejemplo realizado en esta misma sección (ver figura
3.48), la punta en flecha indica la sentencia siguiente, mientras que el extremo opuesto
indica la sentencia previa.
52
Figure 3.49: Connectors: Controller-Input
Gracias a este ”mapping”, es posible establecer una relación entre las propiedades definidas
en una clase y los decoradores de una Shape. Esto tiene como objetivo, por ejemplo en el caso
de la propiedad Name de las clases Inputs y Outputs, que se muestre el nombre especificado por
el usuario debajo de la imagen correspondiente a dicha Shape.
53
En la Figura 3.52 se muestra cómo realizar lo explicado anteriormente para la relación entre
Terminal, la cual cuenta con dos propiedades (Name y Message, ver Figura 3.8) y su Shape
asociada, ISTerminal.
Se puede observar en la imagen 3.53 cómo se asocian las imágenes explicadas en la metáfora
a las distintas salidas y se muestran los nombres gracias al resultado de este proceso.
En la figura 3.53 además de aparecer las formas asociadas a la clase ArduinoDevice y a los
distintos Outputs, aparece otro elemento, las lı́neas entre ambas. Para conseguir mostrar estas
lı́neas es necesario crear un Connector o conector, que irá asociado a la relación existente entre
las salidas y el dispositivo Arduino en este caso. En la Figura 3.55 se muestra el conector definido
para esta relación, mientras que en la Figura 3.54 se muestra cómo se ha enlazado dicho conector
a la relación entre ArduinoDevice y Outputs.
54
Figure 3.55: CnctOutputs
Figure 3.54: Connector Mapping: ArduinoDe-
viceHasOutputs
Un claro ejemplo de estos contenedores se puede observar en la figura 3.48, en el que el con-
trolador almacena todas las sentencias y los distintos bucles guardan su respectiva condición y
las distintas sentencias que se ejecutan en su interior, sin embargo, para poder lograr esto es
necesario realizar unos ajustes a cómo funciona un lenguaje DSL por defecto.
Una limitación que impone DSL es que una Clase de Dominio puede establecer una relación
”embedding” una única vez, ya sea de forma directa o transitiva. Continuando con nuestro ejem-
plo de contenedor, esto quiere decir que un elemento, solo puede ser contenido por un contenedor
a la vez. Observando de nuevo la figura 3.48 se puede apreciar que las sentencias contenidas en
los bucles if y while están a su vez contenidas en el controlador. Esto genera ambigüedad en el
lenguaje, ya que no sabe concretar quién es el contenedor de las sentencias, impidiendo ası́ su
generación.
Para solventar este problema es necesario indicar manualmente al lenguaje quién es el con-
tenedor padre del elemento, esto se hace a través de la propiedad Parent Element Path, la cual
se puede definir a través del enlace entre una Domain Class y su representación en el diagrama,
ya sea ImageShape o GeometryShape.
55
Figure 3.57: Custom Parent Element Path
La figura 3.56 muestra cómo está definida esta propiedad en la clase Buzzer, la cual no presenta
este problema ya todas las entradas y salidas de nuestro modelo únicamente son embebidas por
el propio dispositivo Arduino y por tanto, no es necesario especificar manualmente su contenedor
padre.
Por el contrario, la figura 3.57 muestra la definición de esta propiedad para la clase Manu-
alSentence, la cual, como se ha explicado previamente presenta este incoveniente de ambigüedad.
Se puede observar que está marcada la casilla ”Has custom parent element” y no se especifica la
ruta de dicho padre, significando esto que le vamos a indicar manualmente cuál es su padre.
Para establecer esta propiedad manualmente se ha creado un fichero de clase auxiliar, Fix-
UpDiagram, la cual contiene un método para cada clase del lenguaje sobre la que queremos
personalizar dicha propiedad.
Como se puede observar en el extracto de código 3.1 obtenido de dicha clase, se declara la
clase FixUpDiagram y uno de sus métodos, lı́nea 3. Este método es el correspondiente a la clase
ManualSentence y por ello recibe una instancia de dicha clase por parámetro. Será invocado cada
vez que se vaya a crear una sentencia manual, para ası́ saber sobre qué clase se está creando la
relación ”embedding” en tiempo de ejecución. Concretamente, lo que el código hace es comprobar
las distintas relaciones de esta sentencia, explicadas en la figura 3.16 y 3.11. devolviendo aquella
que no sea nula, es decir, aquella sobre la que se haya ”soltado” la sentencia.
56
En la clase FixUpDiagram existe un método propio para cada clase hija de Sentence, recibien-
do por parámetro una instancia de dichas clases hijas. La clase Condition también tiene definido
un método propio aunque en este caso se devuelve directamente la clase hija de Loop sobre el que
se está creando la relación. Esto permite la creación de bucles anidados sin ambigüedad sobre
qué condición pertenece a cada bucle.
57
Capı́tulo 4
El primer elemento que se debe crear en cualquier diagrama será el dispositivo Arduino, ya
que sin él, no se pueden instanciar el resto de elementos. Para crear cualquier elemento desde la
barra de herramientas será necesario hacer clic izquierdo en dicho elemento y arrastarlo hasta el
tapiz. En la Figura 4.2 se puede ver el resultado de esta acción. El zoom del tapiz es totalmente
regulable (Mantenga pulsada tecla CTRL + Rueda del ratón). Además todos los elementos son
reposicionables dentro del tapiz, haciendo clic izquierdo sobre el mismo y arrastrándolo hasta la
posición desesada.
58
Figure 4.2: Manual de Usuario: Crear elemento
El siguiente paso será incluir las distintas entradas para el sketch, estas se encuentran en el
compartimento Inputs en la barra de herramientas. Para ello seleccionamos la entrada deseada
y la arrastramos dentro del dispositivo Arduino creado previamente.
59
Figure 4.5: Manual de Usuario: Crear Inputs
Todos los elementos del diagrama deben ser ”soltados” en una zona habilitada para ello, estas
han sido definidas gracias a las relaciones de tipo ”embedding” explicadas en la sección 3.1. En
caso de intentar soltar un elemento en una zona incorrecta, la herramienta nos imposibilitará
la acción, cambiando el icono del cursor a un sı́mbolo de ”prohibido” al pasar por una zona
incorrecta.
De igual forma que con el dispositivo Arduino, se pueden modificar las propiedades de las
entradas (véase Figura 4.6), indicando el nombre (opcional) y el número del pin al que será
enchufado (obligatorio).
Es posible añadir tantas entradas como queramos y en caso de querer eliminar alguna, se po-
drá hacer de dos maneras: haciendo uso de la opción Delete (véase Figura 4.4) o seleccionando
el elemento a eliminar y pulsando la tecla ”Delete” o ”Supr” en el teclado.
Cada vez que se cree una entrada, es necesario asociar una Measure, para ası́ poder almacenar
el valor recopilado. Para ello, desde el apartado principal de la Toolbox se arrastra una Measure
y se deja caer sobre una entrada, ya que es el único sitio en el que la herramienta permite crearla.
De esta forma se crea la medida y un conector que relaciona la misma con la entrada a la que
está asociada (ver Figura 4.7).
60
Figure 4.7: Manual de Usuario: Crear Measure
En las propiedades de Measure se puede modificar su nombre y su Data Type o tipo de datos,
que en la mayorı́a de ocasiones se deberá seleccionar Int, para recopilar valores numéricos (ver
Figura 4.8).
En caso de necesitar traducir el valor leı́do por la entrada, al usar un sensor de temperatura
por ejemplo, será necesario asociar un Value a la medida previamente creada. En las propiedades
de Value se puede seleccionar a qué tipo de escala se desea traducir la medida recopilada (ver
Figura 4.9).
61
Figure 4.9: Manual de Usuario: Editar Value
Una vez incluı́das las entradas necesarias, se debe seguir el mismo procedimiento para las
salidas, sin embargo, para estas no es necesario asociar ninguna Measure o Value. En la figura
3.53 se puede observar un ejemplo de la creación de varias salidas. Destacar el caso del Display,
para el que se deberá especificar en sus propiedades distintos parámetros tales como el número
de filas y columnas que dispone o el mensaje a mostrar (ver Figura 4.10).
El último paso será crear la lógica del programa, es decir, las reglas y condiciones por las
que pasará la información recogida por las entradas para ası́ producir unas u otras respuestas a
través de las salidas.
El elemento que almacenará todas las sentencias y bucles será el controlador, por ello será el
primer elemento a crear. Para ello se seguirán los mismos pasos que para las entradas y salidas, se
arrastrará un Controller desde la sección Main de la Toolbox y se dejará caer sobre el dispositivo
Arduino. (ver Figura 4.11)
62
Figure 4.11: Manual de Usuario: Crear Controller
63
Figure 4.13: Manual de Usuario: crear ManualSentence
Una parte de suma importancia a la hora de crear sentencias para definir la lógica del pro-
grama es establecer el orden de ejecución entre las mismas. Para ello, se deberá usar el conector
entre sentencias, CnctSentence, generado a partir de la relación SentenceReferencesTargetSen-
tence (ver Figura 3.17). Este conector se encuentra en la barra de herramienta, en la sección
Connectors (Figura 4.14).
Para hacer uso de este conector, se deberá seleccionar, haciendo clic izquierdo en la Toolbox
sobre él. Una vez seleccionado, se sitúa el cursor sobre la sentencia origen, haciendo clic sobre ella
y arrastrando el cursor hasta la sentencia destino. De esta forma se crea un enlace entre ambas,
en el que la flecha al final indica la sentencia destino, es decir, la sentencia que se ejecutará
a continuación de la que se encuentra en el extremo opuesto (ver Figura 4.15). Este conector
permite establecer el orden entre cualquier tipo de sentencia, ya sean sentencias simples o bucles
(ver Figura 3.48).
Una sentencia de entrada/salida (IOSentence) se comporta de igual forma que una sentencia
manual, sin embargo esta no tiene una propiedad Text para definir la sentencia, sino que se
deberá enlazar a una entrada o salida para que cobre significado. Para hacer esto se hará uso de
otros dos conectores: Controller-Input y Controller-Output (véase Figura 4.16).
64
Figure 4.16: Manual de Usuario: Controller-Input/Output
El uso de estos conectores es muy similar al conector entre sentencias. Una vez seleccionado el
conector, se debe situar el cursor sobre una sentencia IOSentence, hacer clic izquierdo y arrastrar
hasta un Input u Output, en función del tipo de conector seleccionado (Figura 4.17). No se podrá
usar el conector de entradas para enlazar una IOSentence con una salida ni viceversa. Tampoco
se podrá enlazar una IOSentence a más de una entrada o salida.
Los bucles comparten caracterı́sticas tanto con las sentencias como con un controlador, ya
que se podrán crear sentencias en su interior y además se podrá establecer el orden de ejecución
de los bucles respecto a otras sentencias. Para crear cualquier bucle lo seleccionamos desde la
barra de herramientas y lo soltamos sobre un controlador. Además se puede modificar su tamaño
de igual forma que un controlador, para poder almacenar sentencias en él (Figura 4.18).
65
Figure 4.18: Manual de Usuario: Crear Bucles
Los bucles for tienen la peculiaridad de que poseen dos propiedades personalizables por el
usuario: la inicialización y el incremento (ver Figura 4.19). Estas propiedades permiten definir
el valor de inicio de la variable que controla el bucle y el incremento que sufre dicha variable
cada vez que se ejecuta el bucle.
Estas dos propiedades junto a una condición de parada completarı́an la definición de un bucle
for. Para establecer dicha condición que indica la finalización de la ejecución del bucle se debe
hacer uso de la clase Condition, la cual se puede crear una vez seleccionada en la barra de tareas
y arrastrando hasta soltarla sobre un bucle (Figura 4.20).
66
Figure 4.20: Manual de Usuario: Crear Condition
En las propiedades de una condición se pueden elegir los dos operandos que van a ser com-
parados y el operador que los compara, siendo Operand1 el operando a la izquierda del operador
y Operand2 el situado a la derecha (ver Figura 4.21).
Definiendo las propiedades tal y como se muestra en la Figura 4.21, quedarı́a una condición
de parada del bucle tal que i ¡ 10, que junto a las propiedades del bucle for definidas en la Figura
4.19, resultarı́a un bucle que comienza a ”contar” desde 0, incrementando dicha variable en 1
cada vez que se ejecuta el bucle mientras que dicha variable sea menor que 10, es decir, hasta 9.
En resumen, un bucle que ejecuta las sentencias que hay en su interior 10 veces.
Este comportamiento se podrı́a conseguir de igual forma haciendo uso de un bucle while.
Para ello, se define una sentencia manual que crea la variable ”contador” y la inicializa al valor
0. A continuación, se crea un bucle while con la misma condición que se ha explicado para el
67
bucle for previo (ver Figura 4.21), solo que esta vez en la propiedad Operand1 debemos indicar
la variable ”contador”. Importante establecer el orden correcto entre la sentencia manual y el
bucle, ya que es necesario definir primero la variable contador antes de utilizarla en el bucle.
Por último, creamos otra sentencia manual dentro del bucle, que será la que modifique el
valor de la variable contador para evitar que se produzca un bucle infinito. De nuevo, el resultado
es un bucle que ejecuta lo que hay en su interior 10 veces, en la figura 4.22 se puede observar un
posible ejemplo de ello.
68
4.2 Patrón de Generación automática de código para Ar-
duino
El siguiente objetivo será generar un fichero con extensión .ino para ası́ cargarlo en un dispositivo
Arudino. Dicho fichero se deberá crear a partir de los elementos que se han modelado con la
herramienta en el diagrama, de tal forma que contenga el código adecuado para dar soporte a los
componentes conectados a la placa y se trate la información con las reglas definidas en la parte
lógica del sketch.
Para ello, es necesario implementar un patrón de generación de código gracias a la facilidad de
las plantillas .tt que ofrece Visual Studio. Estas plantillas .tt, que leen el diagrama creado y
producen el fichero con la extensión que indicada y el código correspondiente. La extensión del
archivo de salida se ha determinado gracias a la siguiente lı́nea de código, indicada al principio
del fichero que permite su configuración (ver Código 4.1). El campo requires indica el diagrama
sobre el que se debe basar la plantilla para generar el código.
Como se explicó en la sección de 2.2.3, todo sketch de Arduino se compone de dos métodos:
void setup() y void loop(). Antes de estos podremos inicializar, en caso de que sea necesario,
algunos compoonentes o variables, además de incluir algunas librerı́as externas, necesarias para
el correcto funcionamiento de algunos componentes. Es por esto mismo que se ha dividio la
plantilla de generación de código en tres secciones, cada una correspondiente a las tres partes de
las que consta un sketch de Arduino.
1 if(this.ArduinoDeviceModel != null)
2 {
3
4 if (this.ArduinoDeviceModel.ArduinoDevice != null)
5 {
6 if(this.ArduinoDeviceModel.ArduinoDevice.Inputs != null)
7 {
8 foreach(Inputs input in this.ArduinoDeviceModel.ArduinoDevice.Inputs)
9 {
10 newInput(input);
11 newMeasure(input.Measure);
12 }
13 }
14 if(this.ArduinoDeviceModel.ArduinoDevice.Outputs != null)
15 {
16 foreach(Outputs output in this.ArduinoDeviceModel.ArduinoDevice.Outputs)
17 {
18 newOutput(output);
19 }
20 }
21
22 }
23 }
La inyección de código se produce a través del método WriteLine(), por tanto el concepto
principal de la generación de código será recorrer el modelo que se ha creado e inyectar el código
correspondiente. La primera de inicialización se muestra en el fragmento 4.2 comprobamos la
existencia de un ArduinoDevice y por cada Input y Output incluido se invocan los métodos
69
correspondientes para crear dichos elementos. Estos métodos son newInput() para las entradas
y newOutput() para las salidas.
1 <#+
2 private void newInput(Inputs input)//Method to create an input
3 {
4 if(input is ManualInput || input is Sensor)
5 {
6 WriteLine("int "+ input.Name + " = " + input.PinNumber + ";");
7 }
8 }
9 #>
El método newInput() (Código 4.3) recibe por parámetro un Input, del que se leen sus
propiedades para declarar dicho elemento e indicar el pin al que lo enchufaremos fı́sicamente
en la placa Arduino. Además necesitaremos inyectar el códgio correspondiente a la Measure
asociada al Input para poder almacenar el valor recogido. Para ello, en función de la opción que
se haya seleccionado en las propiedades de dicha medida se creará un tipo de variable u otro (ver
Código 4.4).
1 <#+
2 private void newMeasure(Measure measure) //Method to create a measure
3 {
4 switch(measure.DataType.ToString())
5 {
6 case "Int":
7 WriteLine("int " + measure.Name + " = 0;");
8 break;
9 case "String":
10 WriteLine("String " + measure.Name + " = null;");
11 break;
12 case "Bool":
13 WriteLine("bool " + measure.Name + " = false;");
14 break;
15 case "Double":
16 WriteLine("double " + measure.Name + " = 0;");
17 break;
18 }
19
20 }
21 #>
70
1 private void newOutput(Outputs output){ //Method to create an output
2 if(output is EnvironmentalOutputs)
3 {
4 if(output is LED || output is DCMotor || output is Buzzer)
5 {
6 EnvironmentalOutputs envO = (EnvironmentalOutputs) output;
7 WriteLine("int "+ envO.Name + " = " + envO.PinNumber + ";");
8 }
9 else if(output is Display)
10 {
11 WriteLine("#include <LiquidCrystal.h>");
12 WriteLine("LiquidCrystal " + output.Name + "(12, 11, 5, 4, 3, 2); //Default pins (can
,→ be changed)");
13 }
14 }
15 }
El método newOutput() es bastante similar a newInput(), comprueba el tipo de input del que
se trata e inyecta el código correspondiente. Se ha de resaltar que para el uso de un Display será
necesario importar la librerı́a que se indica en el código.
Con esta primera parte de inyección de cógido y sus métodos auxiliares ya se genera el código
previo necesario. Lo siguiente será crear el método void setup().
1 <#
2 //Generating the void setup() method
3
4 if(this.ArduinoDeviceModel != null)
5 {
6 WriteLine("void setup(){");
7 WriteLine("");
8 WriteLine("Serial.begin(9600);");
9 if (this.ArduinoDeviceModel.ArduinoDevice != null)
10 {
11 foreach(Inputs input in this.ArduinoDeviceModel.ArduinoDevice.Inputs)
12 {
13 setUpInput(input);
14 }
15 foreach(Outputs output in this.ArduinoDeviceModel.ArduinoDevice.Outputs)
16 {
17 setUpOutput(output);
18 }
19
20 }
21 WriteLine("");
22 WriteLine("}");
23 }
24
25 #>
71
1 private void setUpInput(Inputs input){ //Method to set up an input
2 if(input is ManualInput)
3 {
4 WriteLine(" pinMode("+ input.Name + ", INPUT); //Optional");
5 }
6 else if(input is Sensor)
7 {
8 if(input is IRSensor)
9 {
10 WriteLine(" pinMode("+ input.Name + ", INPUT);");
11 }
12 }
13 }
El método pinMode(pin, mode) recibe por parámetro el pin al que se ha enchufado el com-
ponente a la placa o en su defecto, el nombre asignado en el código previo y el modo en el que
se va a inicializar. Como se trata de una entrada, en este caso es INPUT, aunque en muchas
ocasiones se puede omitir.
Se hace lo mismo con las salidas, solo que esta vez se usa el modo OUTPUT en el método
pinMode(). Se ha de tener en cuenta que un Display tiene un tratamiento distinto ya que es
necesario indicar el número de filas y columnas del que se dispone. Tras esto solo queda generar
el código del método void loop().
72
1 <#
2 //Generating the void loop() method
3
4 if(this.ArduinoDeviceModel != null)
5 {
6 WriteLine("void loop()");
7 WriteLine("{");
8 if (this.ArduinoDeviceModel.ArduinoDevice != null)
9 {
10 if(this.ArduinoDeviceModel.ArduinoDevice != null)
11 {
12 foreach(Controller controller in this.ArduinoDeviceModel.ArduinoDevice.Controller)
13 {
14 newController(controller);
15 }
16 }
17
18 }
19 WriteLine("");
20 WriteLine("}");
21 }
22
23 #>
Para generar el método void loop() se buscan los controladores que se hayan creado en el
diagrama y por cada uno de ellos se llamará al método newController() (ver Código 4.10).
Este método (Fragmento de Código 4.10) crea las sentencias asignadas al controlador que recibe
por parámetro gracias al método newSentence, pudiendo generar ası́ la lógica del sketch. En
caso de que existan sentencias en el controlador (Código 4.10, lı́nea 3), se buscará la primera
sentencia según el orden que haya definido el usuario y se pasará dicha sentencia por parámetro
a newSentence() (Código 4.10, lı́nea 9).
El método para crear las sentencias es bastante sencillo, se realiza un ”casting” dependiendo
del tipo de sentencia que se trate y se invoca el método correspondiente para la creación de la
sentencia hija ya que la clase es abstracta (ver Código 4.11).
73
1 <#+
2 private void newSentence(Sentence sentence){ //Method to create a simple sentence
3 if(sentence is SimpleSentence)
4 {
5 SimpleSentence ss = (SimpleSentence) sentence;
6 newSimpleSentence(ss);
7
8 }
9 else if(sentence is Loop)
10 {
11 Loop loop = (Loop) sentence;
12 newLoopSentence(loop);
13 }
14 }
15 #>
El método newSimpleSentence() recibe por parámetro una sentencia de tipo simple, la cual
es abstracta, por lo que hay que comprobar de qué tipo de sentencia hija se trata y de nuevo,
realizar un ”casting” (ver Código 4.12). En caso de tratarse de una sentencia manual, el usuario
habrá definido en el parámetro Text el código a inyectar, por lo que se recupera esa información
y la generamos. Si se trata de un Delay, se inyecta el propio método delay(int duration) y se lee
la duración que el usuario haya indicado.
Por último queda la sentencia IOSentence, que al ser algo más compleja invoca a su propio
método de generación. En cualquiera de los casos, hay que comprobar la existencia de posibles
sentencias que haya a continuación de estas, de ahı́ la necesidad de la relación referencial de
Sentence a otra Sentence explicada previamente. En caso de existir sentencias siguientes, se
invoca el método de creación de sentencias para repetir todo este proceso hasta que no haya
sentencias siguientes.
74
Para generar una sentencia de lectura o escritura se hace uso del método newIOSentence(), el
cual recibe una sentencia de este tipo por parámetro. Esta sentencia tiene referenciado una única
entrada o salida, nunca ambas o ninguna, en cuyo caso se muestra un mensaje para indicárselo
al usuario. En caso de haber enlazado una salida, se invoca el método loopOutput, si es una
entrada se invocan los métodos correspondientes para tratar la información de una entrada.
Para almacenar en la medida el valor leı́do por la entrada, se utiliza el método setMeasure() y
en caso de que sea necesario traducir dicho valor, se llama al método setValue(), recibiendo por
parámetro dicha Measure.
Tanto el método loopInput() como loopOutput() (ver Código 4.14 y 4.15, respectivamente), son
invocados en newIOSentence(), ver4.13, lı́neas 10 y 15. Ambos métodos son bastantes similares,
se recibe por parámetro el elemento sobre el que se ha de crear la sentencia para leer o escribir
información. dependiendo del componente en concreto será necesario inyectar un método u
otro, por tanto, estos métodos se basan en comprobar qué tipo de componente se ha pasado
por parámetro y generar la sentencia adecuada. Por ejemplo, para recopilar información desde
cualquier tipo de sensor, será necesario el método analogRead(pin), recibiendo por parámetro el
nombre o pin al que está enchufado dicha entrada (ver Código 4.14, lı́nea 17). [16]
1 <#
2 private void loopInput(Inputs input) //Method to loop an input
3 {
4 if(input is ManualInput)
5 {
6 if(input is Switch)
7 {
8 WriteLine(" digitalRead("+ input.Name + ");");
9 }
10 else if(input is Potentiometer)
11 {
12 WriteLine(" analogRead("+ input.Name + ");");
13 }
14
15 }else if(input is Sensor)
16 {
17 WriteLine(" analogRead("+ input.Name + ");");
18 }
19 }
20 #>
75
1 <#
2 private void loopOutput(Outputs output)
3 {
4 if(output is Terminal)
5 {
6 Terminal terminal = (Terminal)output;
7 WriteLine(" Serial.print("+ terminal.Message + ");");
8 }
9 else if(output is EnvironmentalOutputs)
10 {
11 if(output is LED)
12 {
13 WriteLine(" digitalWrite("+ output.Name +", HIGH); //use LOW to turn off the LED");
14 }
15 if(output is Buzzer)
16 {
17 Buzzer buzzer = (Buzzer)output;
18 if(buzzer.Duration != 0)
19 {
20 WriteLine(" tone(" + buzzer.Name + ", " + buzzer.Frequency + ", " + buzzer.Duration +
,→ "); //duration parameter is optional and noTone(buzzer.Name) can be used instead");
21 }
22 else
23 {
24 WriteLine(" tone(" + buzzer.Name + ", " + buzzer.Frequency + "); //use
,→ noTone(buzzer.Name) to turn the buzzer off");
25 }
26 }
27 if(output is DCMotor)
28 {
29 DCMotor dcMotor = (DCMotor)output;
30 WriteLine(" analogWrite(" + dcMotor.Name + ", " + dcMotor.Speed + "); //Speed must be
,→ between 0 and 255");
31 }
32 if(output is Display)
33 {
34 Display display = (Display)output;
35 WriteLine(" " + display.Name + ".clear();");
36 WriteLine(" " + display.Name + ".setCursor(0,0);");
37 WriteLine(" " + display.Name + ".print(" + display.Message + ");");
38 }
39 }
40 }
41 #>
Una de las partes más importantes en cualquier lenguaje de programación son los bucles y las
condiciones. Para generarlos, se hace uso del método newLoopSentence, que recibe por parámetro
una sentencia de tipo loop. Como se ha explicado previamente, la clase Loop es abstracta y es
padre de las clases While, For e If, por tanto lo primero que hace este método es comprobar de
qué tipo es el parámetro que se le ha pasado para ası́ tratarlo de una forma u otra.
En cualquiera de los casos se genera la condición de parada de dicho bucle haciendo uso del
método newCondition(). Al igual que en el método newController() (véase Fragmento de código
4.10), hay que buscar la primera sentencia siguiendo el orden que ha definido el usuario. Destacar
también que para el bucle for se registran las propiedas definidas por el usuario para inyectar el
código correspondiente a su inicialización e incremento.
76
1 private void newLoopSentence(Loop loop)//Method to create a loop
2 {
3 if(loop is If){
4 Write(" if (");
5 newCondition(loop);
6 WriteLine(")");
7 WriteLine(" {");
8 If ifLoop = (If) loop;
9 if(ifLoop.Sentence.Count != 0)//checking for sentences inside the loop
10 {
11 foreach(Sentence sentence in ifLoop.Sentence)//looking for the first sentence
12 {
13 if(sentence.SourceSentence == null)
14 {
15 newSentence(sentence);
16 }
17 }
18 }
19 WriteLine(" }");
20 if(ifLoop.TargetSentence != null)//checking for sentences after the loop
21 {
22 newSentence(ifLoop.TargetSentence);
23 }
24 }else if(loop is For){
25 For forLoop = (For)loop;
26 Write(" for (" + forLoop.Initialization + "; ");
27 newCondition(forLoop);
28 WriteLine(" ; " + forLoop.Increment + ")");
29 WriteLine(" {");
30 if(forLoop.Sentence.Count != 0)//checking for sentences inside the loop
31 {
32 foreach(Sentence sentence in forLoop.Sentence)//looking for the first sentence
33 {
34 if(sentence.SourceSentence == null)
35 {
36 newSentence(sentence);
37 }
38 }
39 }
40 WriteLine(" }");
41 if(forLoop.TargetSentence != null)//checking for sentences after the loop
42 {
43 newSentence(forLoop.TargetSentence);
44 }
45 } else if(loop is While)
46 {
47 //...
48 }
49 }
77
1 private void newOperator(Condition condition)
2 {
3 switch(condition.Operator.ToString()){
4 case "And":
5 Write("&&");
6 break;
7 case "Equals":
8 Write("==");
9 break;
10 case "Greater":
11 Write(">");
12 break;
13 case "GreaterOrEquals":
14 Write(">=");
15 break;
16 case "Lesser":
17 Write("<");
18 break;
19 case "LesserOrEquals":
20 Write("<=");
21 break;
22 case "NotEquals":
23 Write("!=");
24 break;
25 case "Or":
26 Write("||");
27 break;
28 }
29 }
De esta forma, quedan explicados los métodos que contiene la plantilla de generación de
código empleada para generar todo el código que da soporte al diagrama creado por el usuario
en la interfaz gráfica.
78
4.3 Desplegando código Arduino con Arduino Modelling
Tool
En esta sección se va a explicar el flujo de trabajo o los distintos pasos a seguir para crear un
modelo concreto en Arduino Modelling Tool hasta desplegar el código generado automáticamente
por la herramienta (sketch) en el dispositivo Arduino.
Una vez ejecutada la herramienta, para crear un nuevo modelo desde el que generar código,
hay que dirigirse a parte derecha, Solution Explorer. Se debe hace clic derecho sobre el apartado
Debugging - Add - New Item... (ver Figura 4.23).
Se abrirá esta ventana en la que se puede crear cualquier tipo de elemento. Para crear un
tapiz sobre el que crear modelo, se debe elegir un fichero tipo Arduino, se puede buscar utilizando
el buscador de la esquina superior derecha. En la parte inferior se puede modificar el nombre del
fichero que se ofrece por defecto a uno representativo del modelo que se va a crear. Por último,
se debe pulsar el botón Add para terminar este proceso (ver Figura 4.24).
79
Figure 4.24: Flujo de Trabajo: Creando fichero Arduino
Una vez hecho esto, se habrá creado un modelo sobre el que modelar los elementos del sistema
Arduino que se desea diseñar. Este archivo se puede visualizar en el Solution Explorer (ver Figura
4.25)
Para abrir este archivo, basta con hacer doble clic sobre él en el explorador de soluciones.
Se abrirá un tapiz con la interfaz gráfica para construir un modelo. A modo ilustrativo, se ha
creado un modelo de ejemplo (ver Figura 4.26) sobre el que se generará el código.
80
Figure 4.26: Flujo de Trabajo: Ejemplo Modelo
Una vez creado el modelo, hay que abrir la plantilla de generación de código y modificar el
campo que indica el fichero Arduino sobre el que se debe generar el código (ver Código 4.1). Para
este caso, hay que indicar NewSketch.Arduino, que es el fichero sobre el que se está trabajando.
En el Código 4.19 se puede observar el resultado de esta modificación. Esto se debe de hacer
únicamente si se ha creado un nuevo modelo, la herramienta proporciona un modelo en blanco
por defecto. En caso de utilizar este modelo inicial, no es necesario modificar este parámetro en
ningún momento.
81
Figure 4.27: Flujo de Trabajo: Plantilla1.ino
Por último, se debe copiar el código generado en el IDE Arduino (ver Figura 4.28) para poder,
en última instancia, subir el código al dispositivo Arduino conectado al ordenador. Se deberá
reproducir fı́sicamente la configuración realizada en el modelo de Arduino Modelling Tool para
comprobar que el código y el diseño se comportan correctamente.
82
Capı́tulo 5
En este capı́tulo se lleva a cabo el objetivo del trabajo fin de carrera de Diseñar un conjunto
de pruebas que validan la completitud del lenguaje, construyendo diferentes configuraciones de
Arduino y generando diferentes programas.
Para ello, se han diseñado un total cuatro pruebas de modelado y generación en las que se varı́a
su complejidad y se emplean diferentes tipos de sensores, actuadores, ası́ cómo la lógica asociada,
utilizando diferentes estructuras de control de programación. En esta baterı́a de pruebas es
posible observar el flujo de información recopilado por las diferentes entradas y cómo afecta,
siguiendo la lógica del código, a las diferentes salidas empleadas.
83
Figure 5.2: Blink : Propiedades LED
Además, hay que establecer las reglas por las que se rigen las salidas definidas, crear la lógica
del Sketch. Para este primer programa, la lógica será sencilla, únicamente se necesita incluir un
retraso entre cada encendido y apagado del LED. El orden necesario es el siguiente: encender
LED, esperar un segundo, apagar LED, esperar un segundo y vuelta a empezar. Por tanto serán
necesarias dos sentencias de entrada/salida y dos sentencias de Delay, como se muestra en la
Figura 5.3.
84
Figure 5.4: Blink : Ordenar sentencias
Por último, se enlazan las sentencias de entrada/salida con el LED, utilizando el conector
desde controlador a salida y se establece la duración del retraso en milisegundos (Figura 5.5).
85
1 int led = 13;
2
3 void setup() {
4
5 pinMode(led, OUTPUT);
6 }
7
8 void loop() {
9 digitalWrite(led, HIGH);
10 delay(1000);
11 digitalWrite(led, LOW);
12 delay(1000);
13 }
En la figura 5.18 se muestra la disposición fı́sica de los elementos empleados. Se debe observar
cómo el led está conectado al pin número 13, tal como se ha indicado en el código.
El resultado del montaje del circuito, ası́ como una muestra de su ejecución se puede observar
en las figuras 2.20 y 2.21. El código utilizado para esa demostración (ver Código 2.2) es el mismo
que se ha generado en esta prueba (ver Código 5.1).
86
5.2 Sketch: Imprimir Matriz Temperaturas
En la siguiente prueba se muestra cómo se puede crear un programa en el que se recopila la
información de cualquier input, en este caso será un sensor de temperatura y se muestran por
pantallas dichos valores, formando una matriz. Además, se mostrará la posibilidad que ofrece la
herramienta de crear bucles anidados necesarios para crear la forma de matriz.
La metodologı́a a seguir será la misma que en el ejemplo anterior, primero, se identifican
las entradas, que únicamente será un sensor de temperatura, con su correspondiente Measure y
Value, para poder tratar los datos (Figura 5.7).
Como la salida se imprimirá por pantalla, es necesario crear una salida Terminal, que muestre
el valor almacenado en temperature (Figura 5.8).
Queda definir la lógica del programa, la cual consiste básicamente en dos bucles for anidados
que serán capaces de generar la matriz. Se ha decidido que la matriz sea de 10x10, por tanto,
los bucles se repetirán diez veces. En la Figura 5.9 se puede observar cómo se ha definido el
primer bucle y su condición, siendo igual para el bucle anidado salvo que para este se utilizará
otra variable.
87
Figure 5.9: Sketch Matriz Temperaturas: Propiedades bucle y condición
En la Figura 5.10 se presenta el resultado final del controlador del programa. El orden de las
secuencias es el siguiente: primero se ejecuta el bucle for que representa las filas de la matriz,
imprimiendo por pantalla un salto de lı́nea cada vez. Tras esto, se accede al bucle anidado, que
se encarga de imprimir las columnas de la matriz. Cada vez que se ejecute este bucle, el sensor
de temperatura enlazado a la primera IOSentence, recupera la temperatura actual y la traduce
a Celsius. A continuación, la siguiente sentencia enlazada a la salida Terminal, imprime por
pantalla el valor almacenado en temperature. Por último, se imprime un espacio y se espera un
minuto antes de continuar con la ejecución del bucle.
88
Figure 5.11: Sketch Matriz Temperaturas: Vista General
El código generado a partir del modelo realizado se puede observar en el Fragmento 5.2.
89
5.3 Sketch: Sensor de Proximidad
En este sketch, se construirá un sensor de proximidad. Para ello, se utilizará un sensor de
infrarrojos como el de la Figura 2.8. Esta será la única entrada utilizada, mientras que la salidas
serán un zumbador (ver Figura 2.12) y dos leds de distintos colores (ver Figura 2.10).
La lógica propuesta para este sketch es la siguiente: cuando el sensor de infrarrojos detecte
un objeto cercano, deberá avisar al usuario de ello. Para esto, se activará el zumbador, emitiendo
un pitido intermitente y se encenderá un led de color rojo. Mientras que no haya ningún objeto
cercano, se mantendrá encendido un led de color verde.
De nuevo, se construyen primero las entradas en Arduino Modelling Tool. Se creará una
entrada IRSensor y se asocia una Measure en la cual se almacena el valor leı́do. Se personalizan
las propiedades de estos elementos, asociando un nombre a cada una e indicando el pin en el que
se enchufará fı́sicamente el sensor, que en este caso será el pin número 3. El resultado de este
proceso se puede observar en la Figura 5.12.
90
Figure 5.14: Sketch Sensor de Proximidad: Crear Salidas
Para crear la lógica explicada, se necesitarán dos condiciones que ejecuten sentencias difer-
entes en función de la información recibida por el sensor. Cuando el sensor detecta un objeto,
emite una señal igual al valor 0, entonces el Buzzer debe emitir un pitido, se debe encender el
led rojo y apagar el led verde. Cuando la Measure del sensor sea igual a 1, significa que no hay
ningún objeto cerca y por tanto, se debe encender el led verde y apagar el rojo.
Se empieza por crear la estructura general de la lógica, para ello se hará uso de un Controller
que almacenará todas las sentencias. Se necesita una sentencia IOSentence enlazada al IRSensor
para leer la información y guardarla en la Measure. Posteriormente, se llegará a los bucles If con
sus respectivas condiciones. Por último, se añade un Delay para evitar la ejecución continua del
programa (ver Figura 5.15).
91
Figure 5.15: Sketch Sensor de Proximidad: Lógica general
Figure 5.16: Sketch Sensor de Proximidad: Condición 1 Figure 5.17: Sketch Sensor de Proximidad: Condición 2
Si se cumple la primera condición, significa que ha detectado algún objeto cercano, por tanto,
se debe activar tanto el zumbador como el led de color rojo y apagar el verde. Por tanto, se
necesitarán tres sentencias de entrada/salida, cada una enlazada a una salida. Cuando se cumpla
la segunda condición, se debe apagar el led de color rojo y encender verde. Por ello, se necesitarán
únicamente dos sentencias IOSentence enlazadas a estas salidas.
El resultado de crear estas sentencias y enlazarlas a sus respectivas salidas y con ello, el
aspecto final del diagrama, se puede observar en la Figura 5.18.
92
Figure 5.18: Sketch Sensor de Proximidad: Diagrama General
El archivo .ino generado contiene el código mostrado en Código 5.3 del cual hay que realizar
únicamente dos modificaciones. Estas modificaciones serán en las lı́neas 23 y 28, para apagar el
led correspondiente según el valor leı́do por el sensor. Esto se consigue pasando como segundo
parámetro el valor LOW en lugar de HIGH. Se puede observar el resultado final del código en
Código 5.4.
93
1 int irSensor = 3;
2 int sensorMeasure = 0;
3 int ledVerde = 6;
4 int ledRojo = 5;
5 int buzzer = 10;
6
7 void setup()
8 {
9 Serial.begin(9600);
10 pinMode(irSensor, INPUT);
11 pinMode(ledVerde, OUTPUT);
12 pinMode(ledRojo, OUTPUT);
13
14 }
15
16 void loop()
17 {
18 sensorMeasure = digitalRead(irSensor);
19 if (sensorMeasure == 0)
20 {
21 tone(buzzer, 800, 500); //duration parameter is optional and noTone(buzzer.Name) can be used
,→ instead
22 digitalWrite(ledRojo, HIGH); //use LOW to turn off the LED
23 digitalWrite(ledVerde, HIGH); //use LOW to turn off the LED
24 }
25 if (sensorMeasure == 1)
26 {
27 digitalWrite(ledVerde, HIGH); //use LOW to turn off the LED
28 digitalWrite(ledRojo, HIGH); //use LOW to turn off the LED
29 }
30 delay(1000); // Delay duration in milliseconds
31
32 }
1 int irSensor = 3;
2 int sensorMeasure = 0;
3 int ledVerde = 6;
4 int ledRojo = 5;
5 int buzzer = 10;
6
7 void setup()
8 {
9 Serial.begin(9600);
10 pinMode(irSensor, INPUT);
11 pinMode(ledVerde, OUTPUT);
12 pinMode(ledRojo, OUTPUT);
13
14 }
15
16 void loop()
17 {
18 sensorMeasure = digitalRead(irSensor);
19 if (sensorMeasure == 0)
20 {
21 tone(buzzer, 800, 500);
22 digitalWrite(ledRojo, HIGH);
23 digitalWrite(ledVerde, LOW);
24 }
25 if (sensorMeasure == 1)
26 {
27 digitalWrite(ledVerde, HIGH);
28 digitalWrite(ledRojo, LOW);
29 }
30 delay(1000);
31
32 }
94
Por último, en la Figura 5.19 se muestra el circuito montado y las conexiones realizadas.
Destacar que las entradas y salidas están conectadas a los pines especificados en la propiedad
PinNumber, lo cual se refleja en el código generado (ver Código 5.4).
95
5.4 Sketch: Mostrar nivel de luminosidad en un Display
LCD
El objetivo de este sketch es mostrar la capacidad de la herramienta para construir un sistema
en el que la información del entorno recibida por las entradas afecte de forma directa a unos
actuadores visibles, a través de un código con distintas condiciones que se cumplirán en función
de las entradas. En este caso, se construirá un sistema en el que la entrada es un sensor de
luminosidad, que recopilará información sobre los niveles de luz del entorno. La salida en la que
se mostrarán los datos es un Display LCD, en el que aparecerá un mensaje u otro dependiendo
de la luz detectada.
Para conseguir esto, se construyen primero las entradas. En este caso se necesita únicamente
un sensor de luminosidad (ver Figura 2.6). En la Figura 5.20 se muestra el resultado de crear la
entrada y asociar una Measure, lo cual es necesario para cualquier entrada.
El siguiente paso es construir las salidas. Como el objetivo es que en función de las medidas
recopiladas por el sensor, aparezca un mensaje u otro en un display, se crea un Display LCD
desde la toolbox (ver Figura 5.21).
96
Figure 5.21: Sketch Luminosidad en Display: Crear Salidas
97
desde el sensor de luz. Como la sentencia a ejecutar dentro de cada condición está directamente
relacionada con la salida, será necesario hacer uso de las sentencias tipo IOSentence, creando
una en cada condición. Todas estas sentencias se deben unir a sus entradas o salidas utilizando
el conector correspondiente (ver Figura 5.23).
98
Figure 5.24: Sketch Luminosidad en Display: Ordenar sentencias
De forma opcional, se puede mostar en el Display el valor exacto leı́do. Para ello se deben
añadir sentencias manuales (aunque también se podrı́a hacer uso una sentencia IOSentence),
siendo las siguientes (ver Figura 5.26):
99
Sentencia 1: display.setCursor(0,1); para situar el cursor en la segunda fila del Display.
Antes de generar el código se debe definir el mensaje a mostrar por las sentencias de salidas
de las cuatro condiciones. Esto se debe hacer a través de las propiedades de Display. Se indicará
un mensaje general que luego se debe personalizar una vez generado el código (ver Figura 5.27).
A continuación se muestra el código generado (ver Código 5.5), del que se debe modificar el
mensaje mostrado en cada condición, siguiendo el rango de valores definido anteriomente. El
resultado final se puede observar en Código 5.6.
100
1 int lightPin = A0;
2 int luminosidad = 0;
3 #include <LiquidCrystal_I2C.h>
4 LiquidCrystal_I2C display(0x27,20,4); //Default pins (can be changed)
5
6 void setup()
7 {
8 Serial.begin(9600);
9 display.init();
10 display.backlight();
11
12 }
13
14 void loop()
15 {
16 luminosidad = analogRead(lightPin);
17 if (luminosidad < 250)
18 {
19 display.clear();
20 display.setCursor(0,0);
21 display.print("NIVEL: " );
22 }
23 if (luminosidad > 250)
24 {
25 display.clear();
26 display.setCursor(0,0);
27 display.print("NIVEL: " );
28 }
29 if (luminosidad > 500)
30 {
31 display.clear();
32 display.setCursor(0,0);
33 display.print("NIVEL: " );
34 }
35 if (luminosidad > 750)
36 {
37 display.clear();
38 display.setCursor(0,0);
39 display.print("NIVEL: " );
40 }
41 display.setCursor(0,1);
42 display.print("Valor: ");
43 display.print(luminosidad);
44 delay(2000); // Delay duration in milliseconds
45
46 }
101
1 int lightPin = A0;
2 int luminosidad = 0;
3 #include <LiquidCrystal_I2C.h>
4 LiquidCrystal_I2C display(0x27,20,4); //Default pins (can be changed)
5
6 void setup()
7 {
8 Serial.begin(9600);
9 display.init();
10 display.backlight();
11
12 }
13
14 void loop()
15 {
16 luminosidad = analogRead(lightPin);
17 if (luminosidad < 250)
18 {
19 display.clear();
20 display.setCursor(0,0);
21 display.print("NIVEL: Oscuro" );
22 }
23 if (luminosidad > 250)
24 {
25 display.clear();
26 display.setCursor(0,0);
27 display.print("NIVEL: Normal" );
28 }
29 if (luminosidad > 500)
30 {
31 display.clear();
32 display.setCursor(0,0);
33 display.print("NIVEL: Claro" );
34 }
35 if (luminosidad > 750)
36 {
37 display.clear();
38 display.setCursor(0,0);
39 display.print("NIVEL: MuyClaro" );
40 }
41 display.setCursor(0,1);
42 display.print("Valor: ");
43 display.print(luminosidad);
44 delay(2000); // Delay duration in milliseconds
45
46 }
En la Figura 5.28 se muestra en detalle el circuito construido para este sketch. Destacar que
el sensor de luminosidad debe ir conectado al pin A0 de la parte ANALOG IN del dispositivo
Arduino, tal y como se ha indicado en su inicialización en el Código 5.6.
102
Figure 5.28: Sketch Luminosidad en Display: Circuito
103
Capı́tulo 6
Conclusiones
6.1 Conclusiones
En este Proyecto de Fin de Grado se ha explicado todo el proceso de construcción de una
herramienta de modelado, Arduino Modelling Tool, la cual permite crear diversas configuraciones
para una placa Arduino UNO, a través de una interfaz intuitiva y sencilla. Además, permite
generar de forma automática el código Arduino necesario que aporta la lógica y soporte para el
correcto funcionamiento de los componentes seleccionados por el usuario.
La principal motivación de este proyecto se ha visto alcanzada al ofrecer a aquellos usuar-
ios que no poseen grandes conocimientos previos sobre programación, una herramienta que les
permita diseñar sus propios proyectos Arduino. Eliminando esa barrera, se fomenta que jóvenes
estudiantes conozcan el mundo IoT, propulsando el futuro de estas tecnologı́as.
Como Arduino Modelling Tool ha sido desarrollada siguiendo los principios definidos por
MDD, tanto su evolución como mantenimiento resultarán tareas sencillas y de bajo coste. Por
ejemplo, es fácilmente adaptable a otro modelo de placa Arduino, ya que las entradas y salidas
son ampliables y sustituibles.
En cuanto a la generación automática de código, los ficheros .ino se encuentran libre de
errores, al eliminar la posibilidad de introducir errores humanos, lo cual supone una gran ventaja
para los nuevos usuarios. Sin embargo, los usuarios más avanzados también pueden beneficiarse
de esta funcionalidad, ya que pueden empezar sus proyectos desde una base generada y seguir
programando a partir de ella, aumentando ası́ su productividad.
A nivel personal, para poder realizar este proyecto y enseñar los conceptos básicos sobre
Arduino, primero los he tenido que aprender yo. Este proceso de aprendizaje ha supuesto el
diseño y programación manual de distintos sketches Arduino a través de los cuales he adquirido
los conocimientos necesarios para más tarde ”enseñárselos” a la herramienta y poder obtener la
plantilla de generación de código. Gracias a la directora de este proyecto, he conocido más a
fondo el mundo IoT y el potencial que este alberga. También he profundizado mis conocimientos
en MDD y la creación de Lenguajes Especı́ficos de Dominio, que, aunque se estudian durante la
carrera, son tecnologı́as y enfoques en constante evolución y que ofrecen multitud de posibilidades
a los desarrolladores.
104
Creación de modelos completos construidos. Integrar en la herramienta una serie de ejemp-
los de modelos sencillos ya construidos, por ejemplo, Sketch: Blink. De esta forma, aquellos
usuarios sin experiencia o nuevos en la herramienta puedan aprender a través de estos e-
jemplos, teniendo la oportunidad de personalizar pequeñas variables a modo de iniciación
en el entorno.
Otro objetivo futuro serı́a la distribución de la herramienta. A partir del proyecto de Visual
Studio original, se puede crear una plantilla del mismo como una extensión de Visual Studio,
que al ser instalada en otros equipos, permite crear proyectos a otros usuarios, es decir, utilizar
la herramienta de modelado [17] [18]. Además de esto, habrı́a que crear un manual de usuario
con los requisitos previos y pasos a seguir sobre la instalación de la herramienta.
105
Bibliografı́a
106
[19] O. de las Naciones Unidas, “Desarrollo sostenible – united nations sustainable
development sites,” accessed: 14-05-2021. [Online]. Available: https://www.un.org/
sustainabledevelopment/es/
[20] T. A. Team, “Arduino education,” accessed: 14-05-2021. [Online]. Available:
https://www.arduino.cc/education
[21] F. Endesa, “Smart city: Las ciudades inteligentes en la actualidad,” accessed: 14-05-2021.
[Online]. Available: https://www.fundacionendesa.org/es/recursos/a201908-smart-city
107