Funciones en Arduino

La modularización, es una técnica usada por los programadores para hacer sus códigos más cortos, ya que consiste en reducir un gran problema complejo, en pequeños problemas más sencillos, concentrándose en la solución por separado, de cada uno de ellos.

Una función es un conjunto de instrucciones que realizan una acción determinada. Esta función tendrá un nombre y podrá ser llamada desde otras partes del código tantas veces como sea necesario. Las funciones pueden recibir datos, denominados parámetros, que pueden ser manipulados en su interior. En ocasiones, esta función, además de ejecutar todas estas instrucciones, podrá devolver un valor final que pueda ser utilizado allí donde la función ha sido llamada.

Ayudan al programador a ser mucho más organizado separando por nombres las funciones haciendo que el código sea legible.

Al utilizar funciones podemos hacer más compacto nuestro código organizándolo por bloques y llamando a las funciones con solo utilizar su nombre para correr su código interno esto nos permite que nuestro código sea más ligero y más fácil de entender.

En las funciones juegan un papel muy importe las variables, ya que como se ha dicho estas pueden ser locales o globales.

Variables Globales: Estas se crean durante toda la ejecución del programa, y son globales, ya que pueden ser llamadas, leídas, modificadas, etc; desde cualquier función. Se definen antes del main().

Variables Locales: Estas, pueden ser utilizadas únicamente en la función que hayan sido declaradas.

La sintaxis de una función es la siguiente:

Tipo_de_datos nombre_de_la_funcion(tipo y nombre de argumentos)

{

    acciones

}

 




donde:

  • Tipo_de_datos: Es el tipo de dato que devolverá esa función, que puede ser real, entera, o tipo void(es decir que no devolverá ningún valor).
  •  Nombre_de_la_funcion: Es el identificador que le damos a nuestra función, la cual debe cumplir las reglas que definimos en un principio para los identificadores.
  • Tipo y nombre de argumentos: son los parámetros que recibe la función. Los argumentos de una función no son más que variables locales que reciben un valor. Este valor se lo enviamos al hacer la llamada a la función. Pueden existir funciones que no reciban argumentos.
  • Acciones: Constituye el conjunto de acciones, de sentencias que cumplirá la función, cuando sea ejecutada.

Una función, termina con la llave de cerrar, pero antes de esta llave, debemos incluir la instrucción return, con la cual devolverá un valor específico. Es necesario recalcar que si la función no devuelve ningún valor, es decir, es tipo void, no tiene que ir la sentencia return, ya que de lo contrario, nos dará un error.

Ejemplo1: Función para realizar una medida de distancia con un sensor de ultrasonidos.

long medirDistancia()
{
long distancia;
long tiempo;
digitalWrite(TriggerPin,LOW);

delayMicroseconds(5);
digitalWrite(TriggerPin, HIGH);
delayMicroseconds(10);
digitalWrite(TriggerPin, LOW);
tiempo=pulseIn(EchoPin, HIGH);

return(distancia);
}

Explicación:

Función que devuelve el valor obtenido de la distancia de un objeto. Función que devuelve un tipo de dato entero (long).  En esta función no se pasa ningún argumento.

Llamada a la función: distanciaObjeto=medirDistancia();

Ejemplo 2: Función para controlar el giro un motor de corriente continua y el controlador L293H
 

void girarMotorDerecha()
{
  digitalWrite(E1, HIGH); //Activar el motor 1
  digitalWrite (I1, HIGH);  //Iniciar el giro
  digitalWrite (I2, LOW);
}

 

Explicación: Función que no devuelve ningún valor, función usada para hacer girar un motor de corriente continua a través de un circuito integrado L293.

 Los subprogramas se comunican con el programa principal, que es el que contiene a las funciones, mediante parámetros, que estos pueden ser: Parámetros Formales y Parámetros Actuales.


Cuando se da la comunicación los parámetros actuales son utilizados en lugar de los parámetros formales.

Paso de Parámetros a la función por valor

Los parámetros se tratan como variables locales y los valores iniciales se proporcionan copiando los valores de correspondientes argumentos.

Los parámetros formales-Locales de una función reciben como iniciales los valores de los parámetros actuales y con ellos se ejecutan las acciones descritas en el subprograma.

Ejemplo:

Función para controlar la velocidad de giro de un motor de corriente continua, mediante el circuito integrado L293.

void girarMotorDerecha(int velocidad)
{
  analogWrite(E1, velocidad); //Activar el motor 1
  digitalWrite (I1, HIGH);  //Iniciar el giro
  digitalWrite (I2, LOW);
}

Explicación: La función no devuelve ningún valor y se le pasa el valor de la velocidad.

Funciones definidas por el usuario en Arduino

Una función, como ya se ha dicho, es un bloque de código dentro del programa que se encarga de realizar una tarea determinada. Por lo tanto un  programa en Arduino debe constar de dos o más funciones, y por su puesto no puede faltar la función principal  setup() y loop().

Un viejo adagio dice: Separa y vencerás, lo cual se acopla perfectamente cuando tenemos un programa que es bastante grande; podemos separarlos en pequeños subprogramas (funciones), y concentrarnos en la solución por separados de cada uno de ellos y así resolver un gran problemas, en unos cuantos problemitas más pequeños.


Si un programa, está constituido por más de una función, las llamadas a la misma, pueden realizarse desde cualquier parte del programa, y la definición de ellas debe ser independiente unas de otras.

Por lo tanto sería un grave error el tratar de definir una función dentro de otra.

Una función puede ser llamada desde cualquier parte del programa no sólo una vez, y cuando es llamada, empieza a ejecutar las acciones que están escritas en código.

Ejemplos:

  • Funciones que no devuelven ningún valor

Ejemplos:

void setup(), void loop()

  • Funciones que devuelven un valor entero

Ejemplo:

   int leerDistancia(int pinSensor) 

  • Funciones que devuelven un valor Real

Ejemplo:

float leerLuminosidad(int pinLdr)

Ejemplo: Circuito para controlar la velocidad de giro de un motor de corriente continua en función del valor obtenido por un sensor de ultrasonidos.

 Circuito


 
Código del programa

#define E1 13 // Enable Pin para el motor 1
#define I1 12 // Control pin 1 para el motor 1
#define I2 11 // Control pin 2 para el motor 1
int velocidad=255;
const int EchoPin = 2;
const int TriggerPin =3;
long distanciaAlObjeto;
void setup()
{
  pinMode (E1, OUTPUT);
  pinMode (I1, OUTPUT);
  pinMode (I2, OUTPUT);
  pinMode (EchoPin,INPUT);
  pinMode (TriggerPin,OUTPUT);
  Serial.begin(9600);
 }

void loop()
{
  distanciaAlObjeto=medirDistancia();
  if(distanciaAlObjeto>100)
  {
   girarMotorDerecha(150);
  }
  else
  {
    girarMotorIzquierda(255);
  }
}



long medirDistancia()
{
long distancia;
long tiempo;
digitalWrite(TriggerPin,LOW); 

delayMicroseconds(5);
digitalWrite(TriggerPin, HIGH); 

delayMicroseconds(10);
digitalWrite(TriggerPin, LOW);
tiempo=pulseIn(EchoPin, HIGH);
return(distancia);
}

void girarMotorDerecha(int velocidad)
{
  analogWrite(E1, velocidad); //Activar el motor 1
  digitalWrite (I1, HIGH);  //Iniciar el giro
  digitalWrite (I2, LOW);
}
void girarMotorIzquierda(int velocidad)
{
  analogWrite(E1, velocidad); //Activar el motor 1
  digitalWrite (I1, LOW);  //Iniciar el giro
  digitalWrite (I2, HIGH);
}
void pararMotor()
{
  digitalWrite (E1, LOW); //Parar el motor 1
}
 

Mensajes de depuración a través del monitor Serie

Una de las ventajas de utilizar el entorno de programación de Arduino es poder utilizar otras herramientas más avanzadas como el monitor Serie, donde el código de nuestra aplicación puede mostrar mensajes informativos, que no aparecen directamente en ningún dispositivo de la placa Arduino. Esto se usa normalmente para depurar el código de las aplicaciones por parte de los programadores para ir conociendo los valores que van tomando las variables o por dónde va transcurriendo la ejecución del código.

Para poder usa el monitor serie, en el bloque de inicio del código de la aplicación (setup) debes habilitar la comunicación indicando la velocidad de transmisión, que en este caso es 9600:

Serial.begin(9600);

Cada vez que desees mostrar un mensaje en el monitor serie, debes efectuar una llamada a la función print de Serial, como en este ejemplo:

Serial.print("mi mensaje\n");

Como habrás podido observar, se ha añadido \n al final del mensaje. Esto lo puedes utilizar cada vez que desees introducir un salto de línea, es decir, que si no se indica \n, cada mensaje aparecerá justo detrás del anterior, por lo que se dificultaría su visibilidad.

Si en lugar de mostrar un mensaje con un texto literal, deseas mostrar el valor de una variable, deberás indicar el nombre de la variable sin usar las comillas:

Serial.print(variable);

Para ver los mensajes, abre el monitor serie después de iniciar la ejecución de la aplicación, usando el menú Herramientas > Monitor Serial.

Para probar esta funcionalidad, y puedas depurar el funcionamiento de tu código, añade un mensaje Serial cuando se pulse cada botón y sigue usándolo en los siguientes apartados para conocer si está funcionando correctamente el código que vayas desarrollando.

Ejemplo:

Mostrar la distancia medida por un sensor de ultrasonidos

Código


 
Medidas mostradas en el monitor serie
 

 
 
 
 

Variables. Conceptos y tipos

 ¿Que es una variable? Una variable es un lugar donde almacenar un datos cuyo valor no es constante sino que puede ir cambiando a lo largo de la ejecución del programa.

Antes de usarse, las variables tienen que ser declaradas definiendo su tipo y valor o bien pueden permanecer sin recibir un valor previo hasta que la instrucción se lo asigne. La mayoría de los lenguajes de programación exigen definir las variables al principio, de modo que estas primeras órdenes constituyen instrucciones de definición de datos.

La sintaxis general para definir una variable es:

tipo nombre=valor;

Por ejemplo: int contador=0;

Los nombres de variables pueden tener letras, números y el símbolo ’_’. Deben empezar por una letra (pueden empezar por ’_ ’ pero no es recomendable pues es el criterio que usan las rutinas de la biblioteca).

Pueden llevar mayúsculas y minúsculas. En C se distingue entre mayúsculas y minúsculas. La costumbre es que las variables van en minúscula y las constantes en mayúscula

Usa las mismas reglas dentro del código para el nombramiento de variables, ya sea en minúscula con palabras separadas con guiones bajos, tantos como sea necesario para mejorar la legibilidad o utilizar la convención “CapWords” (palabras que comienzan con mayúsculas), aunque generalmente la primera palabra se pone en minúsculas.

Usa un solo guión bajo como prefijo para métodos no públicos y variables de instancia.

Las palabras reservadas if, else,etc . . . no pueden usarse como nombres de variables.

Nombres para evitar: Nunca uses los caracteres ‘l’ (letra ele en minúscula), ‘O’ (letra o mayúscula), o ‘I’ (letra i mayúscula) como simples caracteres para nombres de variables, para evitar confusiones a la hora de leer el código.

Declaración de variables.

Una variable tiene un nombre, un valor y un tipo. Con la asignación, se puede cambiar el valor de la variable.

Todas las variables deben ser declaradas antes de su uso. Las declaraciones deben aparecer al principio de cada función o bloque de sentencias. Al declarar una variable se debe indicar primero el tipo de variable y luego su nombre, opcionalmente se le puede dar un valor, lo que se llama inicializar la variable.

La declaración consta de un tipo de variable y una lista de variables separadas por coma.

  • int i,j;

  • float x,pi;

  • unsigned long longitud, contador;

Las variables pueden inicializarse en la declaración

  • float pi=3.1416;

  • unsigned long contador=0;

Puede utilizarse el modificador const para indicar que la variable no puede ser cambiada en tiempo de ejecución.

  • const float e=2.7182;

La declaración de una variable sólo debe hacerse una vez en un programa, pero el valor de la variable se puede cambiar en cualquier momento usando aritmética y reasignaciones diversas.

Una variable puede ser declarada en una serie de lugares del programa y en función del lugar en donde se lleve a cabo la declaración, esto determinará en qué partes del programa se podrá hacer uso de ella, es lo que se denomina ámbito de la variable o scope.

C y C++ se dice que son lenguajes de tipado estático, es decir, la comprobación de tipificación se realiza durante la compilación, y no durante la ejecución, por lo tanto no podemos cambiar el tipo de una variable en tiempo de ejecución. Otros lenguajes, generalmente interpretados, son de tipado dinámico y una misma variable puede tomar valores de distinto tipo en distintos momentos, como PHP o python.

Ámbito de una variable

Una variable puede ser declarada al inicio del programa antes de la parte de configuración setup(), a nivel local dentro de las funciones, y, a veces, dentro de un bloque, como para los bucles del tipo if.. for.., etc. En función del lugar de declaración de la variable así se determinará el ámbito de aplicación, o la capacidad de ciertas partes de un programa para hacer uso de ella.

Una variable global es aquella que puede ser vista y utilizada por cualquier función y estamento de un programa. Esta variable se declara al comienzo del programa, antes de setup().

Recordad que al declarar una variable global, está un espacio en memoria permanente en la zona de static data y el abuso de variables globales supone un uso ineficiente de la memoria.

Una variable local es aquella que se define dentro de una función o como parte de un bucle. Sólo es visible y sólo puede utilizarse dentro de la función en la que se declaró. Por lo tanto, es posible tener dos o más variables del mismo nombre en diferentes partes del mismo programa que pueden contener valores diferentes, pero no es una práctica aconsejable porque complica la lectura de código.

El modificador de variable static, es utilizado para crear variables que solo son visibles dentro de una función, sin embargo, al contrario que las variables locales que se crean y destruyen cada vez que se llama a la función, las variables estáticas mantienen sus valores entre las llamadas a las funciones.

Constantes

En programación, una constante es un valor que no puede ser alterado/modificado durante la ejecución de un programa, únicamente puede ser leído. Una constante corresponde a una longitud fija de un área reservada en la memoria principal del ordenador, donde el programa almacena valores fijos. Por ejemplo el valor de PI = 3.1416.

El modificador const, modifica el comportamiento de una variable haciéndola “read-only”, esto significa que puede usarse como cualquier otra variable pero su valor no puede ser cambiado.

En C++ las constantes también pueden ser definidas a nivel de módulo antes de compilar, de forma que no ocupan memoria y su nombre es sustituido por el valor definido en el proceso de compilación. Estas constantes por norma se escriben con letras en mayúscula y guiones bajos separando palabras. Por ejemplo, MAX_OVERFLOW y TOTAL. Se usa la palabra clave #define para definirlas.

Más información en: https://www.arduino.cc/en/Reference/Define

A la hora de elegir un tipo de variable u otro, es importante escoger el que requiera el número menor de posiciones de memoria para cubrir nuestras necesidades; de está manera se evita sobredimensionar el programa y restar recursos de memoria. 


Tipos de datos

En programación, un tipo de dato informático o simplemente tipo es un atributo de los datos que indica al ordenador (y/o al programador) sobre la clase de datos que se va a trabajar. Esto incluye imponer restricciones en los datos, como qué valores pueden tomar y qué operaciones se pueden realizar.

Los tipos de datos comunes son: números enteros, números con signo (negativos), números de coma flotante (decimales), cadenas alfanuméricas, estados (booleano), etc.

byte

Byte almacena un valor numérico de 8 bits sin decimales. Tienen un rango entre 0 y 255. Sin signo.

int (entero)

Enteros son un tipo de datos primarios que almacenan valores numéricos de 16 bits sin decimales comprendidos en el rango 32,767 a -32,768.

long (entero largo)

El formato de variable numérica de tipo extendido “long” se refiere a números enteros (tipo 32 bits = 4 bytes) sin decimales que se encuentran dentro del rango -2147483648 a 2147483647.

float (decimales)

El formato de dato del tipo “coma flotante” o “float” se aplica a los números con decimales. Los números de coma flotante tienen una mayor resolución que los de 32 bits que ocupa con un rango comprendido 3.4028235E+38 a -3.4028235E+38.

Los números de punto flotante no son exactos, y pueden producir resultados extraños en las comparaciones. Los cálculos matemáticos de coma flotante son también mucho más lentos que los del tipo de números enteros, por lo que debe evitarse su uso si es posible. En Arduino el tipo de dato double es igual que el float.

boolean

Un booleano solo tiene dos valores true y false. Cada booleano ocupa un byte de memoria.

char (carácter)

Un char representa un carácter que ocupa 1 byte de memoria. Los caracteres simples se representan con comillas simples ‘a’ y para múltiples caracteres o strings se representan con comillas dobles “Hola!”.

Recordar que los caracteres se almacenan como números usando la codificación ASCII, lo que significa que es posible hacer operaciones aritméticas con los caracteres.

Tipos de Datos en Arduino

En Arduino podemos definir los tipos de datos tanto en variables locales como globales:

  • Void     Está reservado para la declaración de funciones.
  • Char    Se utiliza para almacenar datos de tipo texto. Ocupa un byte.
  • Byte     Se utiliza para almacenar números entre el 0 y 255. Ocupa un byte.
  • int        Se utiliza para almacenar números entre el -32768 y 32767. Ocupa dos bytes.
  • usigned int   Se utiliza para almacenar números enteros positivos. Ocupa dos bytes y permite almacenar números entre el 0  y 655535.
  • long   Se utiliza para almacenar números enteros largos, entre el -2147483648 y 2147483647. Ocupa cuatro bytes.
  • unsigned long   Se utiliza para almacenar números enteros positivos largos. Ocupa cuatro bytes y permite almacenar números entre el 0 y el 4294967296.
  • float     Se utiliza para almacenar números con decimales. Ocupa cuatro bytes.
  • String  Se utiliza para guardar cadenas de caracteres.

Asimismo, los datos pueden ser constantes, cuyo valor no cambia a lo largo del programa, o variables, cuyo contenido puede ir cambiando a lo largo de la ejecución.