En este artículo vamos a recopilar la sintaxis de la programación de Arduino. Este artículo en sí mismo no es, de ningún modo, un manual de programación. La forma de aprender a programar Arduino es programando Arduino. Sigue los tutoriales numerados, realiza los montajes que se describen, analiza los sketches y haz tus propios montajes y escribe tus propios sketches. Sólo así aprenderás. Podrías leer un millón de libros, y si no practicas y adquieres experiencia, nunca sabrás programar.
Y si no es un manual de programación, ¿qué es, entonces este artículo?. La respuesta es fácil. Es un manual de referencia, donde aclarar las dudas sobre sintaxis de instrucciones y funciones, tipos de datos, estructuras de control, etc. Todo aquello que necesites consultar durante tus desarrollos. EN TODO CASO, ES COMPLEMENTARIO DE LOS TUTORIALES PRÁCTICOS. SI NO SIGUES LOS TUTORIALES, Y ANALIZAS LOS SKETCHES, ESTE MANUAL TE VA A SERVIR DE BIEN POCO.
Un buen programador, con experiencia, vocación y las ideas claras no pierde tiempo, a la hora de conocer un nuevo lenguaje o entorno de desarrollo, en aprender, de memoria, la sintaxis de las instrucciones, ni la lista de estas. En vez de eso, se dedica a comprender el lenguaje que está aprendiendo: lo que hace, lo que puede hacer, como responde a la programación… La sintaxis se consulta cuando hace falta. Solo se aprende “de memoria” cuando se te queda grabada por haber usado las instrucciones del lenguaje una vez, y otra, y otra, hasta la saciedad.
Yo llevo tres décadas programando ordenadores, y sigo recurriendo a manuales de consulta para comprobar la sintaxis exacta de tal o cual instrucción. Aquí encontrarás la referencia que necesites para tus consultas, pero ya sabes: programa, programa y sigue programando. Solo cometiendo errores se logran éxitos.
LA SINTAXIS DE ARDUINO
Vale. Después de la charla de arriba, vamos a ello. Vamos a ir desgranando, en distintos apartados, todo lo que necesitamos conocer del núcleo (también llamado kernel) del lenguaje de Arduino. En esta hoja de referencia hablaremos de todas las instrucciones, funciones, estructuras, etc. que forman parte del lenguaje Arduino, tal como viene cuando descargas el IDE. Aquellas instrucciones, métodos, etc que pertenecen a librerías externas (ya sean oficiales de Arduino o de otros desarrolladores) son objeto de estudio en los tutoriales donde se detallan en los artículos que hacen uso de tales librerías. También se detallarán en uno o más artículos de referencia relativos a la obtención y uso de cada una de esas librerías. Por supuesto, no cubriremos todas las que existen (son miles), sino sólo las más empleadas y difundidas.
En cuanto a la sintaxis del lenguaje Arduino, si has programado antes con C, C++ o, incluso, Java, verás muchas similitudes. Si no, acepta mi palabra al respecto. Hay bastante parecido.
ESTRUCTURA BÁSICA
La estructura básica de un sketch Arduino es siempre la misma:
// Zona de inclusión de librerías
// Zona de declaración de variables y constantes
// Zona de creación de objetos
// Zona de definición de funciones de usuario
void setup() {
// Instrucciones de inicialización del sketch
// Definición de los pines digitales de Arduino (entrada / salida)
// Puesta a cero de contadores
// Apertura de canales de comunicación (Serial y otros)
}
void loop() {
// Todos los proceso que lleve a cabo el sketch de forma reiterativa,
// con el fin de obtener el resultado deseado.
}
Si bien no todos los sketches cuentan con los elementos aquí descritos, sí es cierto que todos presentan la misma estructura básica.
INCLUSIÓN DE LIBRERÍAS
Las librerías externas son módulos diseñados para permitir que Arduino se comunique de forma adecuada con determinados dispositivos. En los tutoriales de esta serie tenemos varios ejemplos (servomotores, displays de cristal líquido, etc.).
Las librerías son, por lo tanto, módulos que resuelven determinadas necesidades, o aportan capacidades que el núcleo de Arduino no implementa. Están escritas e C y cualquiera que conozca este lenguaje (en su momento publicaremos una serie de temarios y tutoriales) puede leer su código y modificarlas, o crear las suyas propias.
Para incluir una librería debemos disponer del correspondiente fichero, que suele tener el nombre de la librería, con la extensión .h
. Normalmente se suele descargar comprimido en formato zip
. Algunas librerías ya las tenemos cuando instalamos el IDE de Arduino. Otras hay que obtenerlas en Internet. Se pueden localizar en la página oficial de Arduino, si son librerías oficiales del fabricante (https://www.arduino.cc/en/Reference/Libraries), o en páginas de terceros desarrolladores.
Usar una librería en nuestros sketches consta de dos pasos:
El primero es agregarla al IDE de Arduino si no viene “de fábrica”. Para ello, en nuestro IDE, pulsamos sobre el menú Programa
, y la opción Incluir librería
. Se nos abrirá un desplegable que nos muestra la lista de las librerías que nuestro IDE tiene agregadas, bien porque sean parte de la instalación, o porque las hayamos agregado en algún momento. Si la librería que queremos aparece en la lista, podemos pasar al siguiente paso. Si no, pulsamos sobre la opción Añadir librería .ZIP...
y se nos abre una ventana para que indiquemos la ubicación en nuestro ordenador del archivo de la librería que nos hayamos descargado previamente. Lo abrimos y ya queda, automáticamente, agregada a nuestro IDE. Una vez agregada, ya está disponible cuando la necesitemos. Ya podemos borrar el archivo zip que nos habíamos descargado. Este paso sólo hay que hacerlo una vez para cada librería que queramos tener disponible.
El segundo paso es incluir la librería en el sketch que la necesitemos. Para ello, el el área de edición del sketch escribimos, las principio del código, la instrucción #include
, seguida del nombre de la librería que queremos usar, incluyendo la extensión .h
y entre <
y >
. Pondremos una línea como la detallada por cada librería que necesitemos para el sketch (un mismo sketch pede incluir tantas librerías como necesitemos. Por ejemplo, supongamos que vamos a escribir un sketch que haga uso de las librerías para sevomotores y para displays LCD. Lo iniciaremos con las siguientes líneas:
#include <Servo.h>
#include <LiquidCrystal.h>
También podemos incluir las librerías pulsando sobre el menú Programa
, opción Incluir librería
y, en la lista que se despliega, pulsaremos sobre el nombre de la librería que deseamos.
Las librerías se deben incluir al principio de todo el sketch, para que estén disponibles en cualquier momento que las necesitemos.
TIPOS DE DATOS
En Arduino (cómo en cualquier lenguaje de programación) se manejan dos grandes categorías de datos: variables y constantes. Un dato en un programa siempre es un par nombre-valor. Es decir, es una etiqueta que contiene un valor. Las variables son aquellos pares nombre-valor cuyo valor (o contenido) puede cambiar durante la ejecución del sketch. Las constantes, por contra, son pares nombre-valor cuyo contenido no puede variar. Los nombres genéricos de “variables” y “constantes” son, por tanto, bastante descriptivos respecto a la naturaleza de cada dato.
- El nombre de una variable o constante debe seguir unas reglas específicas:
- Deberá empezar con una letra.
- Dentro del nombre podrá haber letras, números o guiones bajos.
- Se aconseja que sea descriptivo respecto al uso del dato, para facilitar el desarrollo y la depuración. Es decir. Es mejor llamar a una variable
contador_de_pulsaciones
quea_2321_xj
omi_variable_17
. - Los nombres de variables y constantes son sensibles a mayúsculas y minúsculas. Por lo tanto,
contador
no es el mismo nombre de variable queContador
oCONTADOR
. Algunos programadores toman como norma escribir los nombres de variables enminúsculas
o enCamelCased
(mayúscula la primera letra de cada palabra y minúsculas las demás letras); escriben con mayúsculas los nombres deCONSTANTES
, separando cadaPALABRA_DE_LA_SIGUIENTE
con guiones bajos. En todo caso, es criterio personal de cada uno. - No se puede usar una palabra reservada del lenguaje como nombre de una variable. En ese sentido lo tenemos fácil los hispano parlantes, ya que las palabras reservadas de cualquier lenguaje de programación son en inglés, o derivadas de este idioma.
En Arduino se contemplan los siguientes tipos de datos:
- DATOS NUMÉRICOS
byte
. Admite valores de 0 a 255, sin decimales.int
. Admite números de -32768 a 32767, sin decimales.unsigned int
. Admite valores de 0 a 65535.word
. Mismo rango queunsigned int
. Carece de uso práctico.long
. Admite números de -2147483648 a 2147483647, sin decimales.unsigned long
. Admite números de 0 a 4294967295float
. Admite números reales (con decimales) desde -3.4028235E38 hasta 3.4028235E38.double
. En otros lenguajes es un flotante con más rango que elfloat
, pero en Arduino es un alias de este.
Los números se representan, normalmente, en decimal. Si queremos representar un número en binario, lo precederemos de la secuencia Siguiendo el mismo principio, los números octales se preceden del carácter Los hexadecimales se preceden con la secuencia |
- DATOS DE CADENA
string
. Una cadena de texto, encerrada entre comillas dobles. Puede ser una cadena vacía (p.e. “Este soy yo” o “”).char
. Un carácter, encerrado entre comillas simples. Internamente se procesa como un número de -128 a 127, aunque, de cara al usuario, se representa como carácter.- Un dato de tipo
String
puede construirse como una matriz de datos de tipo char (ver más abajo matrices. Por ejemplochar cadena[] = {'t', 'u', 't', 'o', 'r', 'i', 'a', 'l', '\0'};
. El carácter de terminación\0
se usa para evitar algunos errores.
- VALORES LÓGICOS (BOOLEANOS)
true
ofalse
. Si a una variable booleana se le asigna un valor numérico, interpretará comofalse
el 0 y comotrue
cualquier otro valor.
- CONSTANTES DEL LENGUAJE
Existen ciertas constantes que son propias del lenguaje Arduino:
INPUT
OUTPUT
HIGH
LOW
Las variables y las constantes se declaran como podemos ver en cualquiera de los ejemplos de los tutoriales:
int const pinPot = A0;
int valorMedido;
int angulo = 0;
Todas las variables y constantes que vayamos a usar en nuestro sketck deben ser declaradas, con indicación expresa del tipo de dato que van a contener, con la palabra const
antes del nombre si se trata de constantes, y asignándoles o no un valor inicial en el momento de declararlas (si se trata de constantes, esta asignación en la declaración es obligatoria porque no se podrá cambiar su valor posteriormente). Es una buena práctica declarar juntas, en la primera sección del sketch, todas las variables y constantes que necesitemos.
CREACIÓN DE OBJETOS
clase
. Para poder implementar las funciones de una clase es necesario, como norma general, crear un conjunto de datos y funciones (que, en este contexto se llaman propiedades
y métodos
) englobados en un objeto. Un objeto es, por lo tanto, la instanciación en nuestro programa de una clase, y tiene las propiedades y métodos que se hallan definidos en dicha clase.LiquidCrystal LCD (12, 11, 5, 4, 3, 2);
LiquidCrystal
), el nombre del objeto que creamos (LCD
) y, entre paréntesis, algunos argumentos, necesarios en este ejemplo. Una función de una clase, que tiene el mismo nombre que dicha clase y se usa para instanciarla en un objeto se llama, genéricamente, constructor
.LAS FUNCIONES DE USUARIO
El programador de un sketch puede definir sus propias funciones que, por defecto no existen en el lenguaje, pero que necesitaremos para que nuestro sketch haga lo que queremos.
Una función de usuario es un fragmento de código que se carga en memoria pero no se ejecuta, sino que permanece “en el limbo” de la memoria de Arduino, hasta que es invocado. A una función podemos pasarle determinados valores, conocidos, genéricamente, como argumentos
, que pueden variar su resultado. Una función puede “hacer algo” o, según como la definamos, “devolvernos” un resultado. Podemos ver un ejemplo de funciones de usuario en el artículo 14 (la función mostrar()
es un buen ejemplo).
Las funciones de usuario se declaran con la del tipo de dato que devuelve, seguida por el nombre de la función y, entre paréntesis, los argumentos que deba recibir. Si no necesita argumentos, los paréntesis hay que ponerlos igualmente. A continuación se escriben las instrucciones que llevará a cabo la función (lo que se conoce como cuerpo de la función) entre {
y }
. El formato general obedece, por lo tanto, al siguiente esquema:
tipo function nombreDeLaFuncion (arg1, arg2, arg3, ..., argN) {
// Cuerpo de la función
}
Si la función no va a devolver ningún resultado, el tipo se declarará como void (vacío), pero no puede omitirse. Si, por el contrario, la función va a devolver un resultado, el tipo de la función corresponderá con el tipo del dato que devuelve, según la lista que hemos visto en el apartado anterior. Además, si se espera que la función devuelva un resultado, la última instrucción del cuerpo de la función deberá ser, obligatoriamente, la palabra return
, seguida del nombre de la variable que debe devolverse.
// Ejemplo de una función que "hace algo" pero sin devolver nada.
void function MiPrimeraFuncion (arg1, arg2) {
// Cuerpo de la función
}
// Ejemplo de una función que hace "algo" y devuelve un resultado de tipo int.
int MiFuncionDeEntero (arg1, arg2, arg3) {
//Cuerpo de la función
return variableTipoInt;
}
En los tutoriales de Arduino hay algunos ejemplos que incluyen funciones de usuario, para que veas como operan. Como hemos dicho que las funciones se cargan en memoria, pero no se ejecutan hasta que son invocadas, deberemos declararlas en la primera sección del sketch. Lo ideal es a continuación de la declaración de variables, constantes y objetos.
COMENTARIOS
Cuando se escribe un código (para Arduino, o en cualquier otro entorno) es una buena práctica incluir, donde sea necesario, comentarios para recordarnos que han o como operan tal o cual conjunto de líneas del sketch, o tal función, etc. Si tenemos que depurar o modificar el sketch pasado un tiempo, y este es lo suficientemente largo y complicado, tener los comentarios adecuados puede ahorrarnos muchísimas horas de trabajo.
Un comentario puede constar de una sola línea. En ese caso lo podemos preceder con dos barras inclinadas, así:
// Esto es una línea de comentario.
También puede ser un bloque de varias líneas. En ese caso lo iniciamos con /*
y lo terminamos con */
, así:
/*
Esto es un conjunto de líneas de comentarios.
La función que se define a continuación, hace esto, y aquello, y
no sé que más.
Recibe muchos datos y los procesa como puede.
Evidentemente, los comentarios no deberían tener este aspecto :)
*/
ÁMBITO DE LAS VARIABLES
Según donde declaremos nuestras variables, podremos o no acceder a ellas para cambiar su valor o leerlo. Hemos dicho que, como norma general, las variables se declaran en la primera sección del sketch. Si intentamos leer o cambiar el valor de una variable dentro de una función de usuario, no será posible: parecerá que la variable no exista. Si la necesitamos dentro de la función, deberemos pasarla como argumento (caso aparte son las funciones setup y loop, propias del sketch).
Si la declaramos dentro del cuerpo de una función, no será posible leerla o escribirla desde fuera de dicha función. Si la necesitamos fuera deberemos “sacarla” con return.
Es importante que recuerdes el ámbito donde se crea una variable (donde “vive” esa variable) porque si esperas localizarla en otro ámbito, obtendrás errores de compilación y tu sketch no funcionará.
No te preocupes ahora de si todo esto te suena un poco arcano. Los ejemplos de los tutoriales te lo aclararán. De momento, sólo quédate con la idea.
MATRICES
Una matriz es una colección de variables que se engloban bajo el mismo nombre y, dentro de ese englobamiento, se identifican mediante un índice. Se declaran con indicación expresa del tipo de datos que van a contener (lo que indica que todos los datos serán del mismo tipo. La forma general de declararlas es así:
tipo NombreDeMatriz[] = {1, 2, 3, 4, ... , n};
Como ejemplo, usamos varias en los tutoriales. Como ejemplo vemos esta línea sacada del tutorial 13, que habla de los teclados:
byte Pins_Filas[] = {13, 12, 11, 10};
Los valores se identifican, dentro de la matriz, con un índice que empieza a contar desde 0. Así pues, para localizar el primer valor (en el ejemplo, 13) lo haremos con Pins_Filas
[0]
y podemos leerlo o reasignarlo. Para cambiar el valor de ese índice, escribiríamos Pins_Filas[0
] = 26;
.
Recuerda que las matrices pueden ser como filas (una dimensión), tablas (dos dimensiones), etc. Pueden tener todas las dimensiones que nos permita gestionar la memoria de nuestro Arduino. En la práctica, yo llevo tres décadas programando y no he necesitado nunca matrices de más de tres dimensiones (y, aún estas, sólo en contadísimas ocasiones).
Una matriz de dos dimensiones se declara con dos pares de corchetes, como en
int miMatriz [][] ={{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
Los índices podemos recorrerlos mediante bucles. En el artículo de sintaxis que hablemos de estructuras de control entraremos más en detalle sobre la operativa de las matrices.
CIERRE
Con esto damos por finalizado este artículo, para no hacerlo muy pesado. En sucesivos artículos sobre sintaxis continuaremos viendo como funciona el lenguaje Arduino. Sólo una cosa más. Recuerda siempre terminar cada instrucción con un punto y coma. Es algo fácil de olvidar y causa de errores “tontos” pero de difícil localización a veces.
Pingback: LA LIBRERÍA LedControl » eldesvandejose.com
Pingback: ARD-SYN 05 – Sintaxis (V). Tipos de datos » eldesvandejose.com