Pulsador con Arduino

En este tutorial aprenderemos a utilizar un pulsador con Arduino, con diferentes configuraciones de circuito. También veremos cómo utilizar el pulsador para varias aplicaciones, y aprovechar algunas de las capacidades de Arduino, por ejemplo las interrupciones.

Utilizaremos una placa Arduino Uno, pero este tutorial también sirve para cualquier otra placa Arduino que puedas encontrar.

Circuito del pulsador

Para armar el circuito se necesita:

  • Placa Arduino Uno
  • Breadboard
  • Algunos cables
  • Pulsador

circuito pulsador arduino

Pasos para construir el circuito

  • Asegúrate de apagar el Arduino.
  • Enchufa el pulsador en el centro de la protoboard, como en la imagen.
  • En una de las patas del pulsador, conecta un cable (negro si es posible) a un pin GND de la placa Arduino.
  • Las patas superior izquierda e inferior izquierda del botón están conectadas entre sí, y las patas superior derecha e inferior derecha están conectadas entre sí. Si has conectado el cable GND en el lado izquierdo, entonces conecta otro cable en el lado derecho, para que no estén conectados juntos. Este otro cable va a un pin digital, por ejemplo el 4.
  • Por ahora no incluirémos ninguna resistencia en el circuito. En el siguiente post hablaremos más sobre esto. Además, si quieres saber más sobre los pines de Arduino, echa un vistazo a esta guía de pines de Arduino Uno.

> Mira este vídeo como recurso adicional a esta sección del tutorial:

Código Arduino para leer el estado del pulsador

Con el siuiente código lo que conseguiremos será leer el estado del botón e imprimirlo 10 veces por segundo.

Imprimir el estado del pulsador
Aquí tienes el código para imprimir el estado del botón 10 veces por segundo.

#define BUTTON_PIN 4

void setup()
{
  Serial.begin(9600);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
}

void loop()
{
  Serial.println(digitalRead(BUTTON_PIN));
  delay(100);
}

Vamos a desglosar este código línea por línea para comprenderlo mejor.

Código para configurar el pulsador

#define PIN_BOTON 4

Primero creamos la constante #define para el pin del botón, así no necesitamos escribir «4» cada vez que queramos usar este pin en nuestro código. Esto te hará la vida más fácil, especialmente si quieres conectar el botón a otro pin más adelante. De esta manera sólo necesitas modificar una línea del código, que está en la parte superior del programa, y por lo tanto es más fácil de encontrar.

void setup() {
   Serial.begin(9600);

La función void setup() será llamada primero, sólo una vez. Lo que hacemos aquí es inicializar la comunicación Serial, para que podamos usar el Monitor Serial para imprimir los datos que obtuvimos del botón.

   pinMode(BUTTON_PIN, INPUT_PULLUP);
}

Así es como se inicializa el pulsador en el código. En el void setup(), usas la función pinMode() con 2 argumentos: primero el pin del botón – aquí BUTTON_PIN será reemplazado por «4» – y luego el modo que queremos para el pin.

Como vamos a leer datos del botón – no escribir – elegimos un modo de entrada. Y entonces todavía tenemos 2 opciones: INPUT o INPUT_PULLUP. Como no tenemos ninguna resistencia en nuestro circuito, usaremos INPUT_PULLUP para usar la resistencia pull up interna de la placa Arduino. Tenga en cuenta que en este tutorial no voy a profundizar en la explicación de INPUT_PULLUP, pero si quieres saber más, echa un vistazo a este tutorial Arduino INPUT_PULLUP.

Código para leer el estado del pulsador

void loop() {
   Serial.println(digitalRead(BUTTON_PIN));

Ahora el pin del botón está inicializado como entrada, y entramos en el bucle void(), que será llamado una y otra vez un número infinito de veces.

Para leer el estado del botón, usamos la función digitalRead(), con un argumento: el pin del botón. El resultado de esa función será HIGH o LOW. Aquí, como estamos en una configuración pull up (con INPUT_PULLUP), cuando el botón no esté pulsado leerás HIGH. Cuando el botón está presionado se leerá LOW.

Luego, ponemos el resultado de digitalRead() en la función Serial.println(), que simplemente imprimirá los datos en el Monitor Serial.

   delay(100);
}

Justo después de leer el estado del botón e imprimirlo, añadimos un delay() de 100 milisegundos, lo que significa que vamos a realizar esta acción unas 10 veces por segundo. Después de salir del bucle void(), se llama de nuevo, etc etc.

Ahora puedes probar el código compilándolo y cargándolo en tu Arduino.

Abre el Monitor Serial, y presiona/suelta el pulsador varias veces.

Verás algo como esto:

1
1
1
1
0
0
0
0
1
1
1
1

Cada 100 milisegundos tendrás una nueva línea, con 0 o 1.

Como escribí anteriormente, el valor que obtienes de digitalRead() es HIGH o LOW, pero cuando lo envías a través de Serial, esto va a ser convertido a 1 (HIGH) o 0 (LOW).

Así, cuando el botón no está presionado verás impreso «1», y cuando el botón está presionado, verás «0».

Mejora: Imprimir un mensaje más explícito

Este primer código está muy bien como ejemplo mínimo para ver los datos procedentes del pulsador. Pero en una aplicación real, puede que quieras hacer cosas más interesantes que simplemente imprimir el estado del botón. Por ejemplo, podrías leer el estado del botón, guardarlo dentro de una variable, y luego dependiendo del valor, hacer una acción específica.

Usemos el siguiente código Arduino.

#define BUTTON_PIN 4

void setup()
{
  Serial.begin(9600);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
}

void loop()
{
  byte buttonState = digitalRead(BUTTON_PIN);
  
  if (buttonState == LOW) {
      Serial.println("Botón Pulsado");
  }
  else {
      Serial.println("El botón no está pulsado");
  }
  delay(100);
}

La fase de configuración es la misma. Y como puedes ver en el bucle(), ahora guardamos el valor que obtuvimos de digitalRead() en una variable, que llamamos buttonState. El tipo de datos para esta variable es byte, que es lo que necesitamos para los valores HIGH y LOW.

Luego, con una simple estructura if, comprobamos si el estado es LOW, lo que significa que, en nuestra configuración con la resistencia pull up interna de Arduino, el botón está pulsado. Cuando detectamos esto, podemos decidir hacer la acción que queramos. Y si la condición es falsa, lo que significa que el estado es ALTO (porque sólo hay 2 estados posibles), sabemos que el botón no está pulsado.

Ahora puedes ejecutar este código, y ver impreso «Botón no pulsado» cada 100 milisegundos, y cuando pulses sobre el pulsador, «Botón pulsado».

El botón no está pulsado
El botón no está pulsado
El botón no está pulsado
El botón no está pulsado
Botón pulsado
Botón pulsado
Botón pulsado
El botón no está pulsado
El botón no está pulsado

Esta es una buena mejora al código anterior, que nos da más contexto y permite que el código crezca con más funcionalidades.

Usando resistencias pull up/down

Por ahora no hemos utilizado ninguna resistencia en nuestro circuito – en realidad sí, pero se trata de la resistencia interna de Arduino.

Básicamente, cuando conectas un pulsador a un pin digital, el valor que lee el Arduino está entre 0V y 5V. Si el valor está cerca de 0V, obtendrás LOW en tu código, y si está cerca de 5V, obtendrás el valor HIGH.

Si no pones ninguna resistencia, el valor puede estar «flotando» entre 0V y 5V, por lo que te dará resultados aleatorios y extraños. Añadiendo una resistencia, puedes forzar que el estado por defecto sea LOW o HIGH. Y cuando pulses el botón el estado se convertirá en el opuesto.

Así que tenemos 3 opciones aquí:

  1. Sin resistencia, pero usando el modo INPUT_PULLUP para activar la resistencia pull-up interna (forzará el voltaje de lectura por defecto a 5V – HIGH).
  2. Añadir una resistencia pull-up (valor por defecto: HIGH).
  3. Añada una resistencia pull-down (valor por defecto: LOW).

Para más detalles sobre resistencias pull up/down, lea este tutorial INPUT_PULLUP.

Por lo tanto, ya hemos utilizado la opción 1, ahora vamos a ver cómo trabajar con las opciones 2 y 3.

¿Y por qué deberíamos considerar las opciones 2 y 3, si podemos construir el circuito sin ninguna resistencia? Bueno, porque la resistencia pull up interna es bastante débil comparada con la que vamos a añadir manualmente en el circuito. Esto podría dar lugar a resultados poco fiables, si tienes cables más largos, por ejemplo.

Pulsador Arduino con resistencia pull up externa

Para este circuito necesitarás una resistencia de 10k Ohm, y todos los demás componentes que hemos utilizado anteriormente.

circuito pulsador arduino con resistencia pull up

  • Antes de modificar el circuito asegúrate de apagar el Arduino, para evitar cualquier riesgo para tu hardware.
  • Para el cable de tierra (negro) y el cable de datos (azul), es lo mismo que antes. Asegúrese de que están en diferentes lados del botón – por ejemplo GND en el lado izquierdo, y el pin digital en el lado derecho.
  • Vas a añadir la resistencia de 10kOhm al mismo lado que la conexión del pin digital – en este ejemplo en el lado derecho. La otra pata de la resistencia va a una línea diferente en la protoboard, y desde esta pata, se añade un cable (rojo) a la patilla de 5V del Arduino. Esta es la resistencia pull up, que se asegurará de que el voltaje por defecto que se lee es de 5V, de ahí HIGH en el código.

El código fuente para este caso es el siguiente:

#define BUTTON_PIN 4

void setup()
{
  Serial.begin(9600);
  pinMode(BUTTON_PIN, INPUT);
}

void loop()
{
  byte buttonState = digitalRead(BUTTON_PIN);
  
  if (buttonState == LOW) {
      Serial.println("Botón Pulsado");
  }
  else {
      Serial.println("El botón no está pulsado");
  }
  delay(100);
}

Como puedes ver, el código es el mismo, sólo hemos modificado el modo en la función pinMode(). En vez de usar INPUT_PULLUP, que pondrá el pin como INPUT+ y activará la resistencia pull up interna, simplemente usamos INPUT.

Pulsador Arduino con resistencia pull down externa

Para este circuito también utilizaremos una resistencia de 10k Ohm.

circuito pulsador arduino con resistencia pull down

El principio es el mismo, pero ten en cuenta que aquí las cosas son un poco diferentes:

  • Una vez más, asegúrese de apagar el Arduino antes de hacer nada.
  • Vas a añadir la resistencia de 10k Ohm en el lado conectado a tierra. Así, a partir de una pierna del botón, usted tiene la resistencia, y luego un cable (negro) conectado a GND.
  • El cable conectado al pin digital debe estar ahora en el mismo lado que la resistencia – en este ejemplo, ambas conexiones GND y pin digital están en el lado izquierdo (y no en lados opuestos).
    Añade un cable (rojo) entre una pata del botón – desde el lado opuesto de GND, así que aquí en el lado derecho – y 5V.

Este es el código fuente:

#define BUTTON_PIN 4

void setup()
{
  Serial.begin(9600);
  pinMode(BUTTON_PIN, INPUT);
}

void loop()
{
  byte buttonState = digitalRead(BUTTON_PIN);
  
  if (buttonState == HIGH) {
      Serial.println("Button is pressed");
  }
  else {
      Serial.println("Button is not pressed");
  }
  delay(100);
}

Como puedes ver, también usamos el modo INPUT en la función pinMode().

Pero ahora, cuando leemos el estado del pulsador, LOW significa que el pulsador no está pulsado. Así que tenemos que cambiar la forma en que analizamos los datos en nuestro código. Eso es lo que puedes ver en la línea 13: si el estado es HIGH, ahora significa que el botón ha sido pulsado.

Detectando un cambio de estado

Genial, ahora puedes leer el estado del botón, y hacer algo diferente en tu código, si el botón está pulsado o no.

¿Pero qué pasa si quieres detectar no el estado en sí, sino un cambio de estado? Por ejemplo, cuando el estado del botón pasa de «no pulsado» a «pulsado» (press), o lo contrario (release). Dependiendo de tu aplicación eso puede ser super útil.

Y para eso podemos usar 2 métodos: interrupciones y polling.

Para esos 2 métodos te voy a dar una introducción justa aquí – código que te hará empezar con una comprensión básica – pero si quieres saber más, he puesto algunos enlaces a otros tutoriales en profundidad en la sección de conclusiones de este tutorial.

Usar interrupciones con el pulsador

Con las interrupciones básicamente puedes crear una función que va a ser llamada cuando ocurra un cierto cambio en un pin digital. La llamada de esta función «interrumpirá» el flujo normal del programa.

Sólo ciertos pines digitales se pueden utilizar para las interrupciones. En Arduino Uno, puedes usar los pines 2 y 3. Como el botón está actualmente conectado al pin 4, tenemos que modificar el circuito.

Aquí está el circuito con la resistencia pull-down externa, pero esta vez el cable de datos está conectado al pin digital 3.

circuito pulsador arduino con resistencia pull down conectada a pin 3 para interrupciones
Ahora, el código para detectar cuando se suelta el botón:

#define BUTTON_PIN 3

volatile byte buttonReleased = false;

void buttonReleasedInterrupt() {
  buttonReleased = true;
}

void setup() {
  Serial.begin(9600);
  pinMode(BUTTON_PIN, INPUT);
  attachInterrupt(digitalPinToInterrupt(BUTTON_PIN),
                  buttonReleasedInterrupt,
                  FALLING);
}

void loop() {
  if (buttonReleased) {
    buttonReleased = false;
    // realizar una acción, por ejemplo imprimir en Serial
    Serial.println("Botón Pulsado");
  }
}

La forma de inicializar el botón es la misma que antes.

Luego creamos una nueva función llamada buttonReleasedInterrupt(). Esta función será llamada cuando se dispare la interrupción – con modo FALLING, lo que significa que el estado del botón pasa de HIGH a LOW.

Entonces, cuando el botón es liberado (el estado va de HIGH a LOW), la función es llamada. Establecemos un valor booleano (marcado como «volátil», porque lo modificamos dentro de una interrupción) a true, y en el bucle void(), comprobamos ese booleano. Si es verdadero, entonces sabemos que el botón ha sido liberado. Volvemos a poner el booleano a false y hacemos cualquier acción que queramos, por ejemplo podemos imprimir «Botón liberado» en el Monitor Serial.

Nota importante: no uses Serial directamente dentro de una función de interrupción.

Si quieres una explicación más detallada, también he escrito un tutorial completo sobre las interrupciones de Arduino, que también te dará todo lo que debes/no debes hacer para usar las interrupciones correctamente.

Detectar cambio de estado con polling

Probemos otra forma de comprobar si el botón ha sido liberado, sondeando continuamente el estado.

#define BUTTON_PIN 3

byte lastButtonState = LOW;

void setup() {
  Serial.begin(9600);
  pinMode(BUTTON_PIN, INPUT);
}

void loop() {
  byte buttonState = digitalRead(BUTTON_PIN);
  if (buttonState != lastButtonState) {
    lastButtonState = buttonState;
    if (buttonState == LOW) {
      // realizar una acción, por ejemplo imprimir en Serial
      Serial.println("Botón Pulsado");
    }
  }
}

De nuevo, inicializamos el botón de la misma manera que lo hicimos antes.

También mantenemos una variable global llamada lastButtonState, para poder comparar el estado del botón actual que leemos con el anterior.

En el bucle void(), comprobamos constantemente el estado del botón. Si el estado es diferente al anterior, significa que algo ha pasado: o acabamos de pulsar el botón, o acabamos de soltarlo. En ambos casos guardamos el estado actual como el último estado.

Y luego comprobamos si el estado es LOW. En caso afirmativo -en nuestra configuración de resistencia pull down- significa que hemos soltado el botón. Si estuvieras usando la configuración pull up, tendrías que hacer lo contrario: comprobar si el estado es HIGH.

Rebote del botón

El problema con el código anterior es que cuando lo ejecutas, en algunas ocasiones puedes ver múltiples «Botón liberado» cuando sólo sueltas el botón una vez.

Esto se debe a que el botón está «rebotando» físicamente cuando lo tocas (pasando de BAJO a ALTO y de ALTO a BAJO muchas veces en un intervalo corto), igual que cuando dejas caer una pelota al suelo. La pelota no llega al suelo y se queda allí, sino que rebota unas cuantas veces antes de estabilizarse. Esto es similar para el pulsador.

Afortunadamente podemos resolver esto en nuestro código.

#define BUTTON_PIN 3

byte lastButtonState = LOW;

unsigned long debounceDuration = 50; // millis
unsigned long lastTimeButtonStateChanged = 0;

void setup() {
  Serial.begin(9600);
  pinMode(BUTTON_PIN, INPUT);
}

void loop() {
  if (millis() - lastTimeButtonStateChanged > debounceDuration) {
      byte buttonState = digitalRead(BUTTON_PIN);
      if (buttonState != lastButtonState) {
      lastTimeButtonStateChanged = millis();
      lastButtonState = buttonState;
      if (buttonState == LOW) {
        // realizar una acción, por ejemplo imprimir en Serial
        Serial.println("Botón Presionado");
      }
    }
  }
}

Lo que hacemos aquí es básicamente ignorar cualquier resultado del pulsador durante un tiempo determinado después de que el estado haya cambiado. De esta manera, podemos evitar leer el estado cuando el botón está físicamente rebotando.

Para ello, antes de leer el estado del botón, comprobamos si la duración desde la última vez que el estado ha cambiado es mayor que el debounceDuration que hemos establecido (en nuestro código 50 milisegundos). En caso afirmativo, leemos el estado del botón. Y entonces, si encontramos que el estado es diferente del último, reseteamos la variable lastTimeButtonStateChanged al tiempo actual con millis(). Al hacer esto, tendremos que esperar de nuevo la próxima vez – al menos 50 milisegundos.

Conclusión de esta unidad del curso: Pulsador Arduino

En este tutorial sobre botónes pulsadores con Arduino aprendimos a:

  • Crear correctamente un circuito con un pulsador conectado a tu placa Arduino,
  • Leer el estado del botón,
  • Usar este estado o el cambio de estado del botón para varios casos de uso.
  • Para avanzar más con el tema, te sugiero que consultes el tutorial sobre cómo encender y apagar un LED con un pulsador. Verás cómo incluir el pulsador en varias aplicaciones sencillas usando LEDs. Además obtendrás una explicación mucho más completa sobre el mecanismo de debounce.

Otros artículos relacionados de nuestro Curso Arduino que pueden interesarte: