Dispensador de pastillas diario

Introducción

En un principio, nos pareció muy abrumador la selección de un tema para este proyecto. Al ser completamente libre, originalmente pensamos en realizar alguna especie de juego o juguete, pero finalmente decidimos guiarnos por una característica: la utilidad. Independientemente de lo que decidiésemos construir, queríamos que tuviera una aplicación en el mundo real: ¿seríamos capaces de realizar algo que de verdad fuera aplicable, y no sólo un proyecto más para aprobar la asignatura?

Con esto en mente, acabamos decidiendo crear un dispensador de pastillas automático. Combina una alarma visual y una sonora con un reloj programable que al llegar la hora seleccionada dispense las pastillas que el usuario requiera cada día. Existen multitud de aplicaciones para dispositivos móviles que ejercen una función similar como recordatorios, pero son pocas las personas de edad avanzada que son capaces de utilizar estos dispositivos inteligentes, por lo que nos pareció acertado tomar un enfoque más centrado en el hardware. Así, nuestro proyecto resultaría más sencillo, — queríamos mantener una imagen de simplicidad y comodidad, para que todo resultara intuitivo y fácil de usar independientemente de la experiencia previa del usuario con este tipo de aparatos. 

Durante los siguientes puntos explicaremos también la organización del proyecto así como el material utilizado, el código y los componentes hardware para terminar con los casos de uso y las posibles mejoras.

Material

MaterialCoste
1 x Arduino UNOIncluido en el material de la URJC
2 x Protoboard Incluido en el material de la URJC
1 x LCD 16×2Incluido en el material de la URJC
1 x led azulIncluido en el material de la URJC
1 x buzzerIncluido en el material de la URJC
2 x pulsadoresIncluido en el material de la URJC
2 x res 100kIncluido en el material de la URJC
1 x res 220Incluido en el material de la URJC
1 x Servomotor rotación continua 15 €

Software: código

//Librerias
#include <TimerOne.h>
#include <LiquidCrystal.h>
//pines LCD
int v0 = 3;
int rs = 4;
int e = 5;
int d4 = 6;
int d5 = 7;
int d6 = 8;
int d7 = 9;
//LCD
LiquidCrystal lcd (rs, e, d4, d5, d6, d7);
//Servomotor
int servo = 11;
float pausa;
/*** melodias alarma ***/
long DO = 523.25, FA = 698.46;
long alarma[] = {DO, FA, DO, FA, DO, FA, DO, FA, DO, FA};
long alarma_vacio[] = {DO, DO, DO, DO, DO, DO, DO, DO, DO, DO};
long d = 1000;
long duracion_alarma[] = {d, d, d, d, d, d, d, d, d, d};
/*** fin melodias alarma ***/
//pines pulsadores, buzzer y led
const int pulsador_modo = 12;
const int pulsador_incrementar = 13;
const int buzzer = 2;
const int led_azul = 10;
/*** variables y constantes ***/
int contador_dias = 0;
//RELOJ
int horas = 0;
int minutos = 0;
volatile int segundos = 0;
volatile boolean actualizar = true;
//ALARMA
int horas_alarma = 0;
int minutos_alarma = 0;
boolean alarma_on = false;
char texto[10];
int modo = 0;
const int n_modos = 6;
/*
    modos posibles:
    0: modo normal
    1: cambiar hora reloj
    2: cambiar minutos reloj
    3: cambiar hora alarma
    4: cambiar minutos alarma
    5: cambiar ON/OFF alarma
*/
/*** fin variables y constantes ***/
void setup() {
  pinMode(servo, OUTPUT);
  analogWrite(v0, 10);
  lcd.begin(16, 2);
  lcd.clear();
  pinMode(pulsador_modo, INPUT);
  pinMode(pulsador_incrementar, INPUT);
  pinMode(buzzer, OUTPUT);
  pinMode(led_azul, OUTPUT);
  Timer1.initialize(1000000);
  Timer1.attachInterrupt(controladorTiempo);
}
void loop() {
  if (digitalRead(pulsador_modo) == HIGH) {
    modo++;
    modo = modo % n_modos;
    fijarCursorModo();
    while (digitalRead(pulsador_modo) == HIGH);//evitar rebotes
  }
  switch (modo) {
    //RELOJ
    case 1:
      if (digitalRead(pulsador_incrementar) == HIGH) {
        incrementarHoras();
        while (digitalRead(pulsador_incrementar) == HIGH);
      }
      break;
    case 2:
      if (digitalRead(pulsador_incrementar) == HIGH) {
        incrementarMinutos();
        while (digitalRead(pulsador_incrementar) == HIGH);
      }
      break;
    //ALARMA
    case 3:
      if (digitalRead(pulsador_incrementar) == HIGH) {
        incrementarHorasAlarma();
        while (digitalRead(pulsador_incrementar) == HIGH);
      }
      break;
    case 4:
      if (digitalRead(pulsador_incrementar) == HIGH) {
        incrementarMinutosAlarma();
        while (digitalRead(pulsador_incrementar) == HIGH);
      }
      break;
    case 5:
      if (digitalRead(pulsador_incrementar) == HIGH) {
        alarma_on = !alarma_on;
        actualizar = true;
        while (digitalRead(pulsador_incrementar) == HIGH);
      }
      break;
  }
  if (actualizar == true) { //actualizar hace que el reloj se actualice cada segundo
    actualizarReloj();
    lcd.clear();
    lcd.noBlink();
    //RELOJ
    lcd.setCursor(0, 0);
    sprintf(texto, "%02d:%02d:%02d", horas, minutos, segundos);
    lcd.print(texto);
    //ALARMA
    lcd.setCursor(0, 1);
    sprintf(texto, "%02d:%02d", horas_alarma, minutos_alarma);
    lcd.print(texto);
    //alarma encendida
    if (alarma_on == true) {
      if (segundos == 0 && horas == horas_alarma && minutos == minutos_alarma && contador_dias != 7) {
        contador_dias += 1;
        girarServo(45);
        for (int nota_actual = 0; nota_actual < 10; nota_actual++) {
          digitalWrite(led_azul, HIGH);
          tone(buzzer, alarma[nota_actual]);
          delay(duracion_alarma[nota_actual]);
          noTone(buzzer);
          digitalWrite(led_azul, LOW);
        }
      } else if (segundos == 0 && horas == horas_alarma && minutos == minutos_alarma && contador_dias == 7) {
        //han transcurrido 7 dias, alarma diferente para indicar que el dispensador esta vacio
        contador_dias = 0;
        girarServo(45);
        for (int nota_actual = 0; nota_actual < 10; nota_actual++) {
          tone(buzzer, alarma_vacio[nota_actual]);
          delay(duracion_alarma[nota_actual]);
          noTone(buzzer);
        }
        for (int n = 0; n < 10; n++) {
          digitalWrite(led_azul, HIGH);
          delay(1000);
          digitalWrite(led_azul, LOW);
          delay(1000);
        }
      }
      lcd.print(" ON");
    } else {
      lcd.print(" OFF");
    }
    actualizar = false; //esta línea permite que sea controladorTiempo() quien actualice el reloj cada segundo
    fijarCursorModo();
  }
}
/*** metodos ***/
void controladorTiempo() {
  segundos++;
  actualizar = true;
}
void actualizarReloj() {
  minutos += segundos / 60;
  segundos = segundos % 60;

  horas += minutos / 60;
  minutos = minutos % 60;

  horas = horas % 24;
}
void incrementarHoras() {
  horas ++;
  horas = horas % 24;
  actualizar = true;
}
void incrementarHorasAlarma() {
  horas_alarma++;
  horas_alarma = horas_alarma % 24;
  actualizar = true;
}
void incrementarMinutosAlarma() {
  minutos_alarma ++;
  minutos_alarma = minutos_alarma % 60;
  actualizar = true;
}
void incrementarMinutos() {
  minutos ++;
  minutos = minutos % 60;
  actualizar = true;
}
void fijarCursorModo() {
  switch (modo) {
    case 0:
      lcd.noBlink();
      break;
    case 1:
      lcd.setCursor(0, 0); //situa el cursor en las horas del reloj
      lcd.blink();
      break;
    case 2:
      lcd.setCursor(3, 0); //situa el cursor en los minutos del reloj
      lcd.blink();
      break;
    case 3:
      lcd.setCursor(0, 1); //situa el cursor en las horas de la alarma
      lcd.blink();
      break;
    case 4:
      lcd.setCursor(3, 1); //situa el cursor en los minutos de la alarma
      lcd.blink();
      break;
    case 5:
      lcd.setCursor(6, 1); //situa el cursor en ON/OFF de la alarma
      lcd.blink();
      break;
  }
}
void girarServo(int x) {
  for (int hz = 1; hz <= 50; hz++) {
    pausa = (45 * 2000 / 180) + 500;
    digitalWrite(servo, HIGH);
    delayMicroseconds(pausa);
    digitalWrite(servo, LOW);
    delayMicroseconds(22000 - pausa);
  }
  delay(1000);
}
/*** fin metodos ***/

Hardware: construcción e implementación

En este apartado se presentan dos imágenes creadas con la herramienta Tinkercad correspondientes al montaje de nuestro proyecto, tanto su vista esquemática como la vista del propio circuito y la solución materializada.

Imagen 1. Vista esquemática

Imagen 2. Vista de circuito

Imagen 3. Construcción hardware final

Problemas y soluciones

El mayor problema que hemos tenido a la hora de realizar el proyecto ha sido el uso del servomotor. Nos costó entender su funcionamiento ya que hay diversos modelos en el mercado y el nuestro, al ser un motor de 360º de rotación continua, había menos documentación y ejemplos disponibles que pudiéramos utilizar como apoyo… Aunque finalmente no nos sirvió de mucho todo lo aprendido sobre este componente.

Debido a que nuestro kit entregado por la universidad no contenía un servomotor, tuvimos que comprar uno por nuestra parte. Desgraciadamente, esto significó que tuvimos que esperar a que nos fuese enviado. Por esto, realizamos la totalidad del código y gran parte de la construcción sin haber recibido el servo.

Una vez lo recibimos, el servo no giraba. Estuvimos días intentando entender cuál era el problema, pensábamos que podía ser por la alimentación, que fuese insuficiente, pero fue fácil comprobar con un voltímetro que la tensión era la adecuada. Luego nuestras sospechas se trasladaron al servomotor, lo que también fue cuestión de minutos darnos cuenta que el aparato funcionaba correctamente: ejecutamos con éxito un código de prueba muy sencillo que consistía en mover el servomotor durante un segundo y mantenerlo parado durante otro segundo (ver imagen 4) sucesivas veces. 

Finalmente llegamos a la conclusión que debía haber algún error en el código, a pesar de que lógicamente tenía sentido. El primer problema encontrado fue que nos equivocamos en la posición del pin del servo, pues este requiere la utilización de un pin PWM. Sin embargo, esto no fue suficiente: el servo seguía sin girar. 

Después de muchas pruebas y tener que buscar documentación sobre problemas similares, encontramos algo interesante: había un solapamiento entre las librerías Servo.h y TimerOne.h, puesto que la librería del servomotor usa el mismo timer que utilizamos en nuestro proyecto para el control del reloj, lo que impedía su correcto funcionamiento. Cambiamos entonces las librerías, probando primero con otros timers (concretamente Timer.h y MsTimer2.h), con lo que no hubo suerte puesto que aparecieron otros problemas (por ejemplo, cuando sonaba la alarma, ese tiempo no se acumulaba en el reloj y cuando terminaba de sonar los segundos empezaban en “00”).

Al final sustituímos la librería del servomotor por ServoTimer2.h, la cual no provoca conflictos con TimerOne.h. Sin embargo, hay que aclarar que nuestro servomotor al ser de rotación continua no funcionaba muy bien con esta librería, pero con unos cambios en la parte del código del servo conseguimos apañarlo. De esta forma conseguimos solucionar finalmente el problema… o eso creíamos. 

Cuando comenzamos a probar nuestro dispensador, a la hora de sonar la segunda alarma (que se correspondería con el segundo día) el servomotor no giraba. Volvimos a cambiar las librerías, probando con esta y con aquella, una y otra vez hasta que, después de una búsqueda desesperada para dar con la solución, apareció un vídeo donde se explicaba cómo se puede manejar un servomotor sin incluir librerías en el código: lo ajustamos a nuestro proyecto y al fin funcionó como debía, aunque con algunos desajustes (pues en el vídeo se empleaba un servo de 180º y el nuestro, al ser de 360º, no gira de igual manera) pero mínimos e inapreciables.Cabe mencionar también un pequeño contratiempo que tuvimos con el LCD: en el setup del código tenemos una línea en la que le pasamos al pin v0 del display el valor del contraste (estos valores de intensidad van de 0 a 255), pues bien, al principio le pasamos un 50 y vimos que era demasiado alto para nuestro LCD ya que aparecían rayas horizontales que impedían ver correctamente lo mostrado en la pantalla. Fuimos disminuyendo el valor hasta que llegamos al 10, con el que se distinguían perfectamente todos los dígitos.

Imagen 4. Código de prueba – Servomotor

Casos de uso

En cuanto a los casos de uso tenemos:

  • Visualizar la hora
  • Ajustar la hora del reloj (horas y minutos) y la hora de la alarma (horas y minutos)
  • Encender y apagar alarma (encenderla incluye que suene una melodía determinada y se emita una luz cuando las horas y minutos del reloj coincidan con la alarma) y apagar
  • Recoger pastilla

Imagen 5. Casos de uso

Las horas y minutos del reloj y de la alarma se ajustan mediante dos pulsadores: uno para fijar el cursor en horas o minutos del reloj, horas o minutos de la alarma o en la parte “ON/OFF” para activar o desactivar la alarma respectivamente; el otro pulsador incrementa las horas o minutos del reloj o de la alarma y enciende o apaga la alarma (cambia de ON a OFF y viceversa).

Por el lado del servomotor tenemos que, mientras no sea el séptimo día (esto significaría que no quedan más pastillas en el dispensador), la alarma sonará con dos frecuencias intermitentes (similar a la sintonía de una ambulancia) y el led permanecerá encendido mientras dure la melodía cada vez que caiga una pastilla.

Cuando se alcance el séptimo día, la alarma suena de forma uniforme (para diferenciarla de la sintonía normal) y el led se enciende y se apaga intermitentemente con intervalos de 1 segundo después de que suene la alarma. De esta forma, el usuario sería consciente de la necesidad de rellenar de nuevo el dispensador para la semana siguiente.

Posibles mejoras

Nuestro proyecto tiene un gran abanico de posibles mejoras que se podrían implementar:

  • La alarma podría estar continuamente sonando hasta que se accionase un pulsador que parase la alarma e hiciese caer la pastilla correspondiente.
  • Las horas y minutos solo pueden cambiarse incrementando su valor, por lo que un botón para decrementar dicho valor hubiese hecho menos tedioso el cambio de hora pues, si el usuario se equivoca y pulsa tan solo una vez de más el botón, únicamente podría llegar al número previo pulsando ese mismo botón tantas veces como sean necesarias, y serían, seguro, más de 20 veces.
  • Solo contamos con un mismo led para ambas alarmas (intentamos distinguirlas emitiendo luz mediante intervalos distintos), por lo que un led de distinto color (por ejemplo rojo) para indicar que el dispensador está vacío podría ser una buena opción para diferenciar aún más claramente las dos alarmas.
  • Una melodía distinta para cada alarma podría ser una posible mejora de cara a vender el producto, pues dependiendo de la música emitida podría hacerlo más o menos atractivo de cara al consumidor.

Las tres primeras posibles mejoras del listado no fueron efectuadas debido a que no había suficientes pines en nuestro Arduino UNO, aunque teníamos soluciones como utilizar uno de los dos pulsadores que ya tenía nuestro circuito para implementar el primer punto expuesto, comprar un arduino Leonardo o un módulo I2C para disminuir el número de pines necesarios para el LCD, etc, pero respecto a esto último no vimos la necesidad de gastar más dinero al ser nuestro proyecto ya funcional.

Video Memoria

Material adicional

También te podría gustar...

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *