Cómo se escribe un programa
Ya vimos en Arduino que los programas se llaman sketches, que se escriben en el IDE y que todo sketch tiene dos funciones obligatorias: setup(), que se ejecuta una sola vez, y loop(), que se repite para siempre. Ese es el esqueleto vacío de cualquier programa:
void setup() {
// se ejecuta una sola vez, al encender la placa
}
void loop() {
// se repite sin parar, mientras haya energía
}
Todo lo que se agregue va adentro de alguna de esas dos llaves. Además, hay tres detalles de forma que conviene aprender desde el primer día, porque son los que producen la mayoría de los errores:
- Cada instrucción termina en punto y coma (
;). Es el equivalente al punto final de una oración. - Las llaves agrupan: todo lo que está entre
{y}es una misma lista de instrucciones. - Arduino distingue mayúsculas de minúsculas.
digitalWritees una instrucción;digitalwriteno existe y el programa no compila.
setup se escribe Setup, el programa no arranca y el error que aparece no lo dice con esas palabras.
Encender un LED
El primer objetivo es simple: que una luz se prenda. Para eso hacen falta dos instrucciones, una en cada función:
void setup() {
pinMode(13, OUTPUT); // el pin 13 va a ser una salida
}
void loop() {
digitalWrite(13, HIGH); // le mando 5 V al pin 13
}
pinMode(pin, OUTPUT)avisa que ese pin va a entregar energía en lugar de recibirla. Si no se declara, el pin queda desorientado y el LED no responde.digitalWrite(pin, HIGH)pone el pin en HIGH, que son los 5 V: el LED se enciende. Con LOW el pin baja a 0 V y el LED se apaga.
En el pin 13 de la placa hay un LED chiquito soldado, marcado con la letra L. Eso permite probar el primer programa sin conectar nada: si el LED L se enciende al cargar el programa, el cable, el puerto y la placa funcionan.
Hacerlo titilar
Si el LED queda encendido, el programa no se ve funcionar: se ve un LED encendido. Para que haga algo hay que apagarlo y encenderlo, y para que ese cambio se note hay que esperar entre una cosa y la otra. Eso lo hace delay():
int ledPin = 13;
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
digitalWrite(ledPin, HIGH); // LED encendido
delay(1000); // espera 1000 milisegundos = 1 segundo
digitalWrite(ledPin, LOW); // LED apagado
delay(1000); // espera otro segundo
}
Ese programa, que titila un LED una vez por segundo, es el "Hola mundo" de Arduino. El IDE lo trae escrito y listo para cargar: está en Archivo → Ejemplos → 01.Basics → Blink. Cargarlo y ver el LED titilar es la prueba de que todo el circuito de trabajo funciona.
loop(), la última línea se conecta otra vez con la primera y el titileo no se detiene.Con delay() se cambia el tiempo: delay(100) es una décima de segundo (un titileo rapidísimo), delay(5000) son cinco segundos. Ojo con una cosa: mientras el programa está esperando, no hace nada más. Si en el mismo programa hay que leer un botón, una espera larga hace que la placa reaccione tarde.
Guardar valores: las variables
En los ejemplos anteriores el pin está escrito como el número 13. Si un programa usa el pin 13 en seis lugares y hay que cambiarlo al 9, hay que corregir seis líneas y es fácil olvidarse una. Una variable guarda ese valor en un lugar con un nombre:
const int ledPin = 9; // el nombre del pin queda guardado acá
void setup() {
pinMode(ledPin, OUTPUT); // en lugar de pinMode(9, OUTPUT)
}
La línea se lee así: const int dice que es un número entero que no va a cambiar; ledPin es el nombre que se le da; y = 9 es el valor que guarda. const no es obligatorio, pero si el valor no cambia, avisa al que lee el programa de que ese número está fijo a propósito.
Hay varios tipos, y el tipo se elige según lo que se va a guardar:
| Tipo | Guarda | Ejemplo |
|---|---|---|
int |
Números enteros, sin decimales. | int veces = 5; |
float |
Números con decimales. | float temperatura = 23.5; |
bool |
Solamente dos valores: true o false. |
bool encendido = true; |
String |
Texto, entre comillas dobles. | String nombre = "Aula 3"; |
Los nombres de las variables se eligen, con dos reglas: sin espacios y sin acentos ni ñ. Se acostumbra a escribirlas con la primera palabra en minúscula y las siguientes con mayúscula inicial: ledPin, valorSensor, pinDelBoton.
Leer un pulsador
Hasta acá el programa solamente escribe: manda energía a un LED. Para que reaccione a lo que pasa afuera, hay que leer un pin. Un pulsador (un botón) se conecta entre el pin y masa, y se lee con digitalRead(), que devuelve HIGH o LOW según cómo esté:
const int botonPin = 2;
const int ledPin = 13;
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(botonPin, INPUT_PULLUP); // entrada, con resistencia interna
}
void loop() {
if (digitalRead(botonPin) == LOW) { // ¿el botón está apretado?
digitalWrite(ledPin, HIGH);
} else {
digitalWrite(ledPin, LOW);
}
}
INPUT_PULLUPactiva una resistencia que la placa ya tiene adentro. Sin ella, un pin de entrada queda "al aire" y toma valores al azar: el LED prende y apaga solo, sin que nadie toque nada. Es el error más desconcertante de los primeros circuitos.- Con la resistencia activada, la lógica queda al revés: el pin está en
HIGHen reposo y baja aLOWcuando se aprieta el botón. Por eso el programa pregunta porLOW. if ... elsees la decisión que ya se vio en pseudocódigo: si la condición se cumple se ejecuta el primer bloque, y si no, el segundo. Está explicada en Decisión (IF).
Si la lógica invertida resulta confusa, se puede escribir primero el botón con su resistencia externa, como se ve en Componentes: en ese caso, apretar el botón da HIGH y el programa se lee al derecho.
Valores intermedios: analogWrite()
digitalWrite() solamente sabe de dos estados: 5 V o 0 V. No hay punto medio. Pero un LED puede lucir con menos brillo, y un motor girar más despacio. Eso se consigue con los pines marcados con el símbolo ~, llamados PWM, y con la instrucción analogWrite():
analogWrite(9, 255); // máximo: el LED brilla al 100 %
analogWrite(9, 128); // mitad: el LED se ve a media luz
analogWrite(9, 0); // mínimo: el LED apagado
El valor va de 0 a 255, y ahí está el truco: el pin no entrega 2,5 V, sino que se prende y se apaga muy rápido, cientos de veces por segundo. Si está encendido la mitad del tiempo, el LED (y el ojo) reciben la mitad de la energía y se ve a media luz.
analogWrite() no entrega tensiones intermedias: enciende y apaga muy rápido. Lo que cambia es cuánto tiempo de cada ciclo queda encendido. Con 0 el pin nunca enciende; con 255, nunca se apaga.Como el brillo es un número que va subiendo, se puede hacer que el LED se encienda y se apague de a poco, con el ciclo for que ya se vio en Ciclo exacto (FOR):
void loop() {
for (int brillo = 0; brillo <= 255; brillo = brillo + 5) {
analogWrite(9, brillo); // sube de 5 en 5: el LED se ilumina de a poco
delay(20);
}
for (int brillo = 255; brillo >= 0; brillo = brillo - 5) {
analogWrite(9, brillo); // ahora baja: el LED se apaga de a poco
delay(20);
}
}
Leer un sensor: analogRead()
Los pines analógicos (A0 a A5) hacen lo contrario que los PWM: en lugar de entregar un valor, lo miden. analogRead() lee la tensión que llega al pin —entre 0 y 5 V— y la devuelve como un número entre 0 y 1023.
int valor = analogRead(A0); // 0 cuando hay 0 V, 1023 cuando hay 5 V
float voltios = valor * 5.0 / 1023; // el mismo dato, convertido a voltios
La segunda línea es una regla de tres, y explica por qué el número es 1023 y no 1000: la placa mide la tensión en 1024 escalones (10 bits), y con dos cables se puede averiguar la tensión real que llega al pin.
Un potenciómetro es el sensor más fácil para empezar, porque no mide nada del mundo: es una perilla que entrega una tensión que se puede girar a mano, de 0 a 5 V. Combinando la lectura con analogWrite() sale el programa clásico de la perilla que regula el brillo de un LED:
void setup() {
pinMode(9, OUTPUT);
}
void loop() {
int perilla = analogRead(A0); // 0 a 1023
int brillo = map(perilla, 0, 1023, 0, 255); // lo llevo al rango del PWM
analogWrite(9, brillo);
}
map() hace el trabajo sucio: toma un número que está en un rango y lo convierte a otro rango respetando la proporción. Sin él habría que dividir y multiplicar a mano, y el resultado se vería igual.
El monitor serie: ver qué pasa por dentro
Cuando algo no funciona, no se puede adivinar qué valor está leyendo el programa. El monitor serie muestra esos valores en la computadora, y es la herramienta de diagnóstico más útil que tiene Arduino:
void setup() {
Serial.begin(9600); // se prepara la comunicación serie
}
void loop() {
int valor = analogRead(A0);
Serial.print("Valor leido: "); // escribe el texto
Serial.println(valor); // escribe el número y salta de línea
delay(500);
}
Serial.begin(9600)una sola vez, ensetup(). El 9600 es la velocidad de la comunicación, en baudios.Serial.print()escribe sin saltar de línea ySerial.println()escribe y salta. Se combinan para que el dato salga ordenado.- Para ver el resultado hay que abrir el monitor serie con el ícono de la lupa, arriba a la derecha del IDE, y elegir la misma velocidad que dice el programa. Si dice 9600 en el código y el monitor está en 115200, aparecen caracteres raros en lugar de números.
Errores comunes al cargar el programa
Casi todos los problemas de las primeras clases son estos, y casi ninguno tiene que ver con el código:
| Qué pasa | Por qué | Qué hacer |
|---|---|---|
| Al cargar dice que no encuentra la placa o el puerto. | El IDE no sabe a qué placa le está hablando. | En Herramientas, elegir la placa (Arduino Uno) y el puerto. En Linux el puerto se llama /dev/ttyACM0 o /dev/ttyUSB0; en Windows, COM3 o similar. |
El error menciona avrdude y dice que el programador no responde. |
Se eligió un puerto que no es el de la placa. | Desconectar la placa, mirar qué puerto desaparece de la lista y volver a conectarla: ese es el puerto correcto. |
| El LED L no titila mientras carga. | El programa no está subiendo a la placa. | Revisar placa y puerto, y probar con otro cable USB. |
| Cambié el cable y sigue sin cargar. | Hay cables que solamente cargan: no llevan los cables de datos. | Usar el cable que vino con la placa, o uno que se sepa que transmite datos. |
Error del tipo "expected ';' before ...". |
Falta un punto y coma. | Mirar la línea anterior a la que indica el IDE: el error casi siempre está ahí. |
| Error del tipo "no fue declarado" con una instrucción conocida. | Está escrita con mayúsculas equivocadas. | Comparar con la instrucción correcta: digitalWrite, no digitalwrite. |
| Error de llave al final del programa. | Falta cerrar una }. |
Cerrar cada llave al mismo tiempo que se abre, y ordenar el código con la sangría del IDE (Herramientas → Formatear). |
| El programa carga bien, pero el LED no hace nada. | El pin escrito en el programa no es donde está conectado el LED. | Comparar el número del pin en el código con el del cable en la placa. |
Un programa completo, comentado
El siguiente programa junta todo lo de esta página: un LED, un pulsador y el monitor serie. Se lee de arriba hacia abajo, y las líneas que empiezan con // son comentarios que el programa ignora.
const int botonPin = 2; // el botón está conectado en el pin 2
const int ledPin = 13; // el LED en el pin 13
int veces = 0; // cuántas veces se apretó el botón
void setup() {
pinMode(ledPin, OUTPUT); // el LED entrega energía
pinMode(botonPin, INPUT_PULLUP); // el botón recibe, con resistencia interna
Serial.begin(9600); // arranca la comunicación serie
Serial.println("Listo: apretá el botón");
}
void loop() {
if (digitalRead(botonPin) == LOW) { // si el botón está apretado
digitalWrite(ledPin, HIGH); // prendo el LED
veces = veces + 1; // sumo uno al contador
Serial.print("Se apretó ");
Serial.print(veces);
Serial.println(" vez/veces");
delay(300); // espera para no contar el mismo apretón muchas veces
} else {
digitalWrite(ledPin, LOW); // si no, lo apago
}
}
Cosas para mirar en ese programa, porque están en casi todos los que se van a escribir de acá en adelante:
- Las variables van arriba, antes de
setup(), para que las dos funciones puedan usarlas. veces = veces + 1es la operación de contador que se vio en Operaciones: se lee el valor que tenía la variable y se guarda el mismo valor aumentado en uno.- El
delay(300)no está para que el LED se vea mejor, sino porque una persona mantiene el botón apretado un rato: sin esa espera, el contador sumaría decenas de veces por segundo. - El programa avisa por el monitor serie lo que está pasando. Un programa que habla ahorra muchísimo tiempo de prueba.