Con el fin de que puedas conocer algunos aspectos de iniciación en la programación con Arduino, se plantea este proyecto que pretende crear un pequeño dispositivo que permita un usuario responder a unas preguntas usando botones. El dispositivo informará si la respuesta a la pregunta en correcta o no. Además, el usuario podrá seleccionar la pregunta a la que desea responder pudiendo elegir el número de la pregunta.

Recursos para el aprendizaje de la programación con Arduino

Para que puedas profundizar en el aprendizaje de la programación con Arduino o realizar consultas sobre algún aspecto concreto, te recomiendo los siguientes enlaces:

Programación gráfica mediante bloques (Scratch)

Para iniciarte en la programación de Arduino, puedes empezar usando una aplicación de programación gráfica usando bloques (basada en Scratch), como es el caso de mBlock, así que instala esa aplicación desde su web principal: www.mblock.cc. Si lo deseas, cambia el idioma de la aplicación a Español usando el menú Language > Español. Con este tipo de aplicaciones de programación visual puedes ver las instrucciones más importantes que se pueden aplicar a una placa Arduino.

Indica a la aplicación el tipo de placa que vas a usar, desde el menú Boards. También deberás indicar el puerto por el que está conectada la placa al ordenador, para lo que debes usar el menú Conectar > Serial Port > COMx (siendo x el número de puerto USB al que esté conectada la placa).

Para simplificar el entorno de la aplicación mBlock para el uso que le vamos a dar, cambia su modo de vista usando el menú Editar > Arduino mode. Asegúrate de que también aparece marcada la opción Extensions > Arduino. Así podrás ver sólo los bloques asociados a la programación de Arduino, y en la parte derecha podrás ver el código correspondiente al código que desarrolles de manera gráfica en la parte central.

Encendido de una luz led

Empieza creando una aplicación que encienda una luz led que previamente deberás conectar a tu placa Arduino. Conecta su polo positivo a uno de los pines digitales y el polo negativo a una resistencia de 220 ohmios que a su vez se conecte a la toma de tierra. En el artículo Arduino - Blink, puedes ver un ejemplo similar de conexión, con una vista del código fuente asociado al encendido y apagado reiterado de un led conectado al pin 13.

El equivalente al ejemplo Blink realizado con mBlock sería el siguiente:

Utiliza el botón Upload to Arduino que puedes encontrar en la parte superior derecha, y tras la compilación del código, aparecerá la ventana que informa que el código se ha subido a la placa, y deberás ver el led encenciéndose y apagándose de manera indefinida.

Observa que el código generado, que aparece en la parte derecha, es muy similar al código que aparecía en el tutorial de la web de Arduino que se ha enlazado anteriormente. Tan sólo ha cambiado que el valor HIGH (alto) que se asignada desde el código, aparece como 1, y el valor LOW (bajo) se ha escrito como 0. Lo que ocurre es que tanto HIGH como LOW son constantes predefinidas de Arduino que equivalen a esos valores, por lo que se puede utilizar una manera u otra, aunque es recomendable usar las constantes para hacer más legible el código.

Repasa los comentarios y el texto introductorio que aparece en dicho tutorial Blink de la web de Arduino para comprender el funcionamiento de cada línea de código.

Encender el led al pulsar un botón

Para ir dando forma a nuestro proyecto, nos interesa ir conociendo la manera de encender un led al pulsar un botón.

Observa en el tutorial Arduino - Button, la manera de conectar un botón a la placa Arduino, y el código correspondiente para encender el led (ojo, que no aparece en el circuito, ya que usa el led integrado en la placa que es lo mismo que tener un led en el pin 13).

La programación por bloques para ese código podría ser similar al siguiente, considerando que se ha tenido que crear la variable buttonState en la sección Data&Blocks.

 

Programar 2 botones para encender un led u otro

Ahora llega el momento de que hagas pruebas por tí mismo, colocando en total 2 botones y 2 leds a la placa Arduino, y sigue realizando la programación usando la aplicación mBlock para conocer mejor las instrucciones (los siguientes apartados los realizarás usando directamente el entorno de programación de Arduino, escribiendo directamente el código).

Deberás programar los botones de manera que al pulsar uno de ellos se ilumine uno de los leds, manteniendo el otro apagado y viceversa si se pulsa el otro botón. Ten en cuenta que, a diferencia del ejemplo realizado anteriormente, cada led correspondiente deberá mantenerse encendido aunque se suelte el botón, por lo que deberás guardar en una variable un identificador del botón ha sido pulsado (por ejemplo, asignando a esa variable el valor 1 si se ha pulsado el primer botón, y 2 si se ha pulsado el segundo botón). Una vez detectado el botón que se ha pulsado, el código debe preguntar si se ha pulsado el botón 1, en cuyo caso se encenderá el led correspondiente, y a continuación se hará la misma pregunta para el botón 2 encendiendo el otro led en su caso.

Programación desde el entorno de desarrollo (IDE) de Arduino

Ahora que ya has podido tener una idea aproximada de cómo se organiza el código de Arduino y algunas de sus instrucciones, utiliza a partir de ahora el entorno de programación de Arduino para realizar nuevos cambios en el código del proyecto. 

Copia y pega el código que hasta ahora ha generado la aplicación mBlock para no empezar de cero. De ese código puedes suprimir las líneas que incluyen otras librerías (#include ....) ya que de momento sólo van a ser necesarias las instrucciones básicas de Arduino (puedes aprender más sobre librerías en Arduino - Libraries). También puedes eliminar las variables angle_radangle_deg que se declaran para facilitar la conversión de ángulos, que en este proyecto no se va a utilizar.

Utiliza el icono de subida del código a la placa, para comprobar que el entorno de programación está bien conectado a la placa, y que el código sigue funcionando correctamente.

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. 

Comprobar respuesta correcta o incorrecta

Realiza algunas modificaciones al código que se tiene hasta el momento, para que un led corresponda a una respuesta correcta y el otro a una respuesta incorrecta (sería conveniente usar una luz verde y roja para cada caso). Para ello, en una nueva variable que debes crear (puedes encontrar ayuda en Arduino - VariableDeclaration), debes almacenar el identificador (1 ó 2) del botón que corresponderá a la respuesta correcta de una hipotética pregunta. El código de la aplicación deberá modificarse, para que se encienda la luz verde si se ha pulsado el botón que se corresponde al identificador que has almacenado en la variable de la respuesta correcta, o la luz roja en caso contrario. Necesitarás conocer el funcionamiento de las sentencias if e if-else.

Comprueba que el funcionamiento del código es correcto cambiando el valor de la variable del identificador de la respuesta correcta, de manera que si vale 1 se enciende la luz verde al pulsar el botón 1, y si vale 2 se enciende la luz verde al pulsar el botón 2. La luz roja se deberá encender al pulsar el otro botón en cada caso.

Para que el código vaya quedando lo más legible posible, conviene indicar los números de los pines usando constantes, de manera que en lugar de indicar algo como digitalRead(3), se escriba por ejemplo digitalRead(PIN_BOTON1), que queda mucho más claro y fácil de modificar si se cambian las conexiones de la placa. Puedes encontrar información sobre cómo declarar constantes en Arduino - Const.

También es común utilizar comentarios dentro del código para que quede más clara la lectura de su funcionamiento por los programadores. En los ejemplos vistos anteriormente habrás podido observar muchos de esos comentarios y cómo se deben indicar. También puedes encontrar más información en Arduino - Comments. Así que introduce en tu código los comentarios oportunos para que vaya quedando claro su funcionamiento.

Utiliza la función delay (pausa) para que las luces sólo permanezcan encendidas 1 segundo tras pulsar uno de los botones (se debe enviar la señal LOW por el pin correspondiente, para apagar la luz después de la pausa). Ten en cuenta que al implementar esta funcionalidad, habrá momentos (la mayor parte del tiempo) en que no haya ninguna luz encendida, por lo que la variable que estabas utilizando para almacenar el identificador del botón que se ha pulsado deberá almacenar un valor para el caso en que no se encuentre ningún botón pulsado (por ejemplo, el valor 0).

Zumbador para avisos sonoros de respuesta correcta o incorrecta

Conecta un zumbador (buzzer) a uno de los pines digitales de la placa Arduino (la otra patilla del zumbador a la toma de tierra) y envía un sonido grave si se ha pulsado el botón incorrecto o un sonido más agudo y corto si la respuesta es correcta. Puedes encontrar información sobre cómo enviar sonido al zumbador en Arduino - Tone o buscando ejemplo en la web sobre el uso de la función tone en Arduino.

Selector de pregunta

Para hacer el proyecto más viable, puedes añadir más de una pregunta a la aplicación, con una respuesta correcta diferente para cada pregunta (si no se añaden más botones, sólo se puede elegir entre 2 posibles respuestas para cada pregunta).

Esto se podría hacer añadiendo un potenciómetro a la placa, de manera que al girarlo permita elegir un número de pregunta hasta un máximo de preguntas que se indique en el código de la aplicación.

Para que el usuario conozca el número de la pregunta sobre la que debe responder, con un servo-motor puedes hacer una especie de señalador (flecha) de manera que el adaptador que se coloque en su eje apunte a una determinada zona de un semicírculo que contenga los números de las preguntas.

Hasta ahora se estaba usando una variable que contenía el identificador de la respuesta correcta, pero ahora se debe poder almacenar más de una respuesta correcta, por lo que deberás usar arrays para poder hacerlo.

Circuito y código fuente con una posible solución (mirar sólo en caso de emergencia laughing )

#include <Servo.h>
 
const int PIN_BUTTON_A = 2;
const int PIN_BUTTON_B = 3;
const int PIN_LED_MAL = 6;
const int PIN_LED_BIEN = 7;
const int PIN_BUZZER = 8;
const int PIN_SERVO = 9;
const int PIN_POT_SELEC = 0;
 
const int NUMERO_PREGUNTAS = 4;
 
int idPreguntaSelec = -1;
 
// Identificadores de botones
const int BOTON_0 = 0;  // No hay pulsado ningún botón
const int BOTON_A = 1;
const int BOTON_B = 2;
int botonPulsado = BOTON_0;  // Id del botón que esté pulsado
 
int respuestasCorrectas[] = {
    BOTON_A, 
    BOTON_B, 
    BOTON_B, 
    BOTON_A
};
 
Servo servo;
 
void setup() {
  Serial.begin(9600);
 
  pinMode(PIN_LED_BIEN, OUTPUT);
  digitalWrite(PIN_LED_BIEN, LOW);
  pinMode(PIN_LED_MAL, OUTPUT);
  digitalWrite(PIN_LED_MAL, LOW);
 
  servo.attach(PIN_SERVO);
}
 
void loop() {
  int estadoBotonA = digitalRead(PIN_BUTTON_A);
  int estadoBotonB = digitalRead(PIN_BUTTON_B);
 
  // Considerar, inicialmente, que no se ha pulsado ningún botón
  botonPulsado = BOTON_0;
 
  if(estadoBotonA == HIGH) {
    botonPulsado = BOTON_A;
    Serial.print("Boton A pulsado\n");
  }
 
  if(estadoBotonB == HIGH) {
    botonPulsado = BOTON_B;
    Serial.print("Boton B pulsado\n");
  }
 
  if(botonPulsado != BOTON_0) {  // Se ha pulsado A o B
    // Comprobar si la respuesta es correcta
    if(respuestasCorrectas[idPreguntaSelec] == botonPulsado) {
      // Respuesta acertada
      Serial.print("Respuesta correcta\n");
      tone(PIN_BUZZER, 988, 100);
      digitalWrite(PIN_LED_BIEN, HIGH);
      delay(1000);
      digitalWrite(PIN_LED_BIEN, LOW);
    } else {
      // Respuesta incorrecta
      Serial.print("Respuesta incorrecta\n");
      tone(PIN_BUZZER, 123, 500);
      digitalWrite(PIN_LED_MAL, HIGH);
      delay(1000);
      digitalWrite(PIN_LED_MAL, LOW);
    }    
  }
 
  // Mover flecha (servo) según posición del potenciómetro
  int potSelector = analogRead(PIN_POT_SELEC);
  float rangoPorPregunta = 1024.0 / NUMERO_PREGUNTAS;
  int nuevoIdPregunta = potSelector / rangoPorPregunta;
  if(nuevoIdPregunta != idPreguntaSelec) {
    idPreguntaSelec = nuevoIdPregunta;
    Serial.print("Pregunta seleccionada: ");
    Serial.print(idPreguntaSelec);
    Serial.print("\n");
 
    int anguloServo = 180 * idPreguntaSelec / NUMERO_PREGUNTAS;
    // Ajustar ángulo para señalar al centro del sector
    anguloServo += 180 / NUMERO_PREGUNTAS * 0.5;
    servo.write(anguloServo);
    // Pequeña espera para evitar movimientos demasiado rápidos
    delay(500);
  }
 
}