RCT (Reloj-Calendario-Termómetro )

Autores 

  • Anthony Oswaldo Pucha 
  • Eduardo Villaverde 
  • Pablo Rodea 
  • Marina Fernández 

Objetivo 

El objetivo de nuestro grupo es crear un sistema empotrado con Arduino. El proyecto consiste en la creación de un reloj con una funcionalidad superior a lo que haría un reloj estándar, pudiendo programar múltiples alarmas con diversos niveles de prioridad, indicadores LED de estos niveles, así como la posibilidad de gestionar estas alarmas y un sensor de temperatura. 

Materiales 

CIRCUITO INTEGRADO MOD DS1307 PbF2,40 €
ZUMBADOR C. IMPRESO MINI DC 3-16V2,40 € 
CABLES PROTOBOARD M-M 652,48 € 
MODULO TEMPERATURA / HUMEDAD3,31 € 
MODULO TZT PANTALLA 1.77’’ TFT SPI7,44 € 
Caja2.99 
PRECIO TOTAL21,02 € 

Montaje y hardware 

El montaje del dispositivo sigue el siguiente esquema hecho con Virtual Breadboard:

Los actuadores y sensores de nuestro dispositivo son: 

  • Sensores: 
    • Sensor de temperatura: para tomar la temperatura ambiente. 
    • Botones: 
      • Botón 1: para el poner una alarma, cambiar la unidad de la alarma cuando la estamos creando y salir del menú que muestras las alarmas. 
      • Botón 2: para aumentar la alarma y para ir al menú que muestra las alarmas creadas. 
      • Botón 3: para decrementar la alarma y para resetear las alarmas en el menú que muestra las alarmas. 
  • Actuadores: 
    • Leds: 3 leds que brillaran cuando suene una alarma dependiendo de la prioridad de esta. 
    • Un buzzer: para crear la respuesta auditiva de la alarma. 
    • Una pantalla: para mostrar tanto el día, la hora y la temperatura como para mostrar y configurar las alarmas. 

Las conexiones necesarias para cada elemento se han hecho de la siguiente manera: 

  • El sensor de temperatura necesita una entrada analógica que en nuestro caso ha sido el pin A0. 
  • Los botones necesitan un pin de entrada cada uno que han sido el 6, 3 y 2 para los botones de poner alarma, aumentar y decrementar respectivamente. 
  • Hemos usado los pines 5,7 y 4 para los leds de prioridad baja, media y alta respectivamente además de conectarlos a tierra con una resistencia de 220Ω cada uno para controlar el paso de intensidad. 
  • Para la alarma se necesita un pin de salida para mandarle la señal de activación, nosotros hemos usado el 12. 
  • Para la pantalla se necesitan 5 conexiones para su control y el paso de datos además de la conexión con tierra y con la fuente de 5 y 3.3 V. Para el control y el paso de datos hemos usado los pines de salida 8,9,10 y 11 con una resistencia de 1KOhms para controlar el paso de intensidad y el 13, el cual tiene resistencia ya de por si para el reloj. 

Software 

Sección Declaración de variables y funciones: 

A parte de la declaración de los pines, los aspectos más importantes a destacar son lo siguiente. 

Se ha definido una clase Alarma que nos permitirá manejar las alarmas con facilidad. Esta clase tiene una serie de métodos: 

  • El constructor que inicializará los atributos a –1, que indica que aún no ha sido puesta. 
  • El método setAlarma que pondrá una alarma según los parámetros que recibe día, mes, hora, minuto y prioridad 
  • El método comprobar devuelve un valor booleano si la alarma tiene que sonar si o no. 
  • El método resetAlarma pone los atributos de la alarma a –1, es decir, se resetea. 
  • El método isSet que comprueba si una alarma ha sido puesta o no, para ello compruba si los atributos son distintos a –1  

Se ha limitado el número de alarmas que se pueden guardar a 3, porque son las que caben en pantalla. Esto se puede cambiar en la constante numAlarmas, pero se debería tener en cuenta la pantalla. 

Aunque se haya limitado el número de alarmas que se pueden guardar, al crear una alarma más, se reutiliza en el array de alarmas. Para ello, llevamos la cuenta en alarmaActual.  

La variable i es un valor que empieza en el 0 y termina en el 4, según las veces que se pulse el botón 1. Puede tener 5 valores, que indica en qué parámetro de la alarma nos encontramos, es decir, que el botón 1 sirve para moverse entre los parámetros de la alarma a poner. En orden: 0=hora, 1=minutos, 2=día, 3=mes, 4=prioridad. 

La declaración de las variables para el estado de los botones 1 y 2, buttonState1 y buttonState2, deben ser volatile, que nos permitirá cambiar los estados cuando ocurra una interrupción accionada al pulsar cualquiera de los dos botones. 

Sección Setup: 

A parte de inicializar lo que se va a mostrar por pantalla, establecer la fecha del reloj (simulado con una librería), y establecer el modo de los pines; lo más importante a destacar es lo siguiente. 

Para asociar una interrupción con los botones 1 y 2, hay que usar los pines digitales 2 y 3 porque son los pines digitales que permiten las interrupciones en Arduino UNO. Esta asociación se realiza con la siguiente instrucción: 

  • attachInterrupt(digitalPinToInterrupt(PIN), metodo, modo) 

Recibe el pin de los botones, un método que se ejecutará cuando ocurra la interrupción, y el modo en el que se activará la interrupción. 

Por ejemplo, en el caso del botón 1: 

  • La función digitalPinToInterupt recibe la constante buttonPin1, que es el pin 2 
  • El método que se ejecutará cuando ocurra la interrupción es toggleBtn1, que cambia el valor de buttonState1 a HIGH 
  • El modo es CHANGE, es decir, cuando el pin de los botones cambie de valor, la interrupción se activará, y esto ocurre cuando pulsamos el botón. 

Sección Loop: 

La razón de utilizar interrupciones es porque es más sencillo que tener que pulsar los botones justo cuando se está ejecutando la lectura de los botones. De esta forma, da igual el momento que cuando pulsemos un botón el estado cambiará y entraremos en el menú correspondiente. 

En la pantalla hay tres menús: 

  1. Menú ‘inicial’ donde se muestra fecha, hora y temperatura. Se mostrará siempre cuando buttonState1 y buttonState2 son LOW 
  1. Menú ‘poner alarma’ donde se muestra los parámetros que necesita una alarma: día, mes, horas, minutos, nivel de prioridad. Entraremos cuando buttonState1 es HIGH 
  1. Menú ‘alarmas’ donde se muestra el listado de alarmas creadas y no creadas. Entraremos cuando buttonState2 es HIGH 

La primera sentencia IF coincide con la entrada al menú de ‘poner alarma’.  

  1. Imprimiremos por pantalla los valores iniciales de los parámetros a poner en la alarma. 
  1. Entramos en un bucle del cuál saldremos cuando i == 4, es decir, cuando hayamos pasado por todos los parámetros. Mientras tanto, leemos el botón 1 que irá aumentado la variable i
  1. En el mismo bucle esperamos a la activación de uno de los botones 2 y 3. Cuando uno de los dos es accionado, entramos en un bucle donde aumentaremos o disminuiremos, según buttonState2 y buttonState3, el parámetro indicado por la variable i.
    •  Cabe destacar que cuando se encuentra en el parámetro de prioridad, según el nivel de prioridad, se le enciende un LED al usuario.  
  1. Cualquier cambio en los parámetros es mostrado por pantalla. 

La segunda sentencia IF coincide con la entrada al menú de ‘alarmas’. 

  1. Imprimimos por pantalla el título ‘ALARMAS’. 
  1. Entramos en un bucle que recorrerá todas las alarmas guardadas. Creamos un String alarmaStr que tendrá un formato según si la alarma está puesta o no, para ello, empleamos el método isSet
    • El bucle sirve para imprimir por pantalla cada alarma del array. 
  1. En otro bucle esperamos las acciones de los botones 1 y 3. Con buttonPin3 hacemos un reset de todas las alarmas y salimos, y con buttonPin1 salimos sin hacer un reset de las alarmas. 

Cuando no se entra en ninguna de estas dos sentencias, nos encontramos en el menú ‘inicial’, que simplemente muestra por pantalla fecha, hora y temperatura. 

Además, en cada iteración del loop, se comprueba si alguna de las alarmas tiene que sonar. 

Para ello, entramos en un bucle que recorre todas nuestras alarmas, y comprobamos los atributos de cada una de ellas con los datos actuales. Si coinciden, se activa buzzerPin de forma intermitente. Además, el LED correspondiente a la prioridad asignada a cada alarma lucirá. 

class Alarma {
  public: int dia;
  public: int mes;
  public: int hora;
  public: int minuto;
  public: int prioridad;

  public:
    Alarma () {
      dia = -1;
      mes = -1;
      hora = -1;
      minuto = -1;
      prioridad = -1;
    }
    void setAlarma (int d, int me, int h, int mi, int p) {
      dia = d;
      mes = me;
      hora = h;
      minuto = mi;
      prioridad = p;
    }

    boolean comprobar (int d, int m, int hr, int mn) {
      return d == dia && m == mes && hr == hora && mn == minuto;
    }

    int getDia() {
      return dia;
    }

    int getMes() {
      return mes;
    }

    int getHora() {
      return hora;
    }

    int getMinuto() {
      return minuto;
    }

    int getPrioridad() {
      return prioridad;
    }

    void resetAlarma () {
      dia = -1;
      mes = -1;
      hora = -1;
      minuto = -1;
      prioridad = -1;
    }

    boolean isSet () {
      return dia != -1 && mes != -1 && hora != -1 && minuto != -1 && prioridad != -1;
    }
};


#include <TimeLib.h>

// Sensor Temp
#include <DHT.h>
#include <DHT_U.h>

#define DHTPIN A0
#define DHTTYPE DHT11

DHT dht(DHTPIN, DHTTYPE);

float tempC;

// Pantalla
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SPI.h>

#define TFT_CS    10
#define TFT_RST   8
#define TFT_DC    9

#define TFT_SCLK 13
#define TFT_MOSI 11

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS,  TFT_DC, TFT_RST);
//Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);

#define COLOR1 ST7735_WHITE
#define COLOR2 ST7735_BLACK

// Pines
const int ledPin1 = 5; // Baja
const int ledPin2 = 7; // Media
const int ledPin3 = 4; // Alta

const int buzzerPin = 12;

const int buttonPin1 = 2;
const int buttonPin2 = 3;
const int buttonPin3 = 6;

// Variables
const int numAlarmas = 3;
int alarmaActual = -1;
Alarma alarmas[numAlarmas];

volatile int buttonState1 = 0;
volatile int buttonState2 = 0;
int buttonState3 = 0;

int i = 0;

String diaSemana[7] = { "LUNES", "MARTES", "MIERCOLES", "JUEVES", "VIERNES", "SABADO", "DOMINGO" };


// Variables alarma
int hr = 0;
int mn = 0;
int d;
int m;
int p = 0; // 0: Baja, 1: Media, 2: Alta

void imprimir(int textSize, int x, int y, String cadena) {
  tft.setTextSize(textSize);
  tft.setCursor(x, y);
  tft.setTextColor(COLOR1, COLOR2);
  tft.print(cadena);
}

String formatDigit(int digit) {
  String resultado = String(digit);
  if (digit < 10) {
    resultado = "0" + resultado;
  }
  return resultado;
}

void toggleBtn1 () {
  buttonState1 = HIGH;
}
void toggleBtn2 () {
  buttonState2 = HIGH;
}

void setup(void) {
  Serial.begin(9600);
  tft.initR(INITR_BLACKTAB);
  tft.setRotation(1);
  tft.fillScreen(COLOR2);
  tft.drawRect(0, 0, 160, 128, COLOR1);
  setTime(01, 00, 0, 4, 5, 2021); //hr, min, sec, day, month, year
  d = day();
  m = month();
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(ledPin3, OUTPUT);
  pinMode(buttonPin1, INPUT);
  attachInterrupt(digitalPinToInterrupt(buttonPin1), toggleBtn1, CHANGE);
  pinMode(buttonPin2, INPUT);
  attachInterrupt(digitalPinToInterrupt(buttonPin2), toggleBtn2, CHANGE);
  pinMode(buttonPin3, INPUT);
  pinMode(buzzerPin, INPUT);

  dht.begin();
}

void loop() {
  if (buttonState1 == HIGH) {
    alarmaActual = (alarmaActual + 1) % numAlarmas;

    ///
    tft.fillScreen(COLOR2);
    tft.drawRect(0, 0, 160, 128, COLOR1);
    String fecha = formatDigit(d) +  "/" + formatDigit(m) + "/" + String(year());
    imprimir(1, 12, 4, fecha);
    String hora = formatDigit(hr) +  ":" + formatDigit(mn);
    imprimir(3, 12, 4 + 24, hora);
    String prioridad = "Nivel de Prioridad:" + String(p);
    imprimir(1, 12, 105, prioridad);
    ///
    Serial.println("SET ALARM MODE");
    while (true) {
      if (digitalRead(buttonPin1)) {
        delay(250);

        if (i == 4) {
          tft.fillScreen(COLOR2);
          tft.drawRect(0, 0, 160, 128, COLOR1);
          i = 0;
          alarmas[alarmaActual].setAlarma(d, m, hr, mn, p);
          Serial.println("EXIT");
          hr = 0;
          mn = 0;
          d = day();
          m = month();
          p = 0;
          digitalWrite(ledPin1, LOW);
          digitalWrite(ledPin2, LOW);
          digitalWrite(ledPin3, LOW);
          break;
        } else {
          i++;
          if (i == 4) {
            digitalWrite(ledPin1, HIGH);
          }
        }
      }
      buttonState2 = digitalRead(buttonPin2);
      buttonState3 = digitalRead(buttonPin3);
      while (buttonState2 == HIGH || buttonState3 == HIGH) {
        delay(250);

        if (i == 0) {
          Serial.print("EDIT HOUR: ");
          if (buttonState2) {
            hr = ((hr + 1) % 24);
          }
          if (buttonState3) {
            if (hr == 0) {
              hr = 24;
            }
            hr = ((hr - 1) % 24);
          }
          Serial.println(hr);

        } else if (i == 1) {
          Serial.print("EDIT MINUTES: ");
          if (buttonState2) {
            mn = ((mn + 1) % 60);
          }
          if (buttonState3) {
            if (mn == 0) {
              mn = 60;
            }
            mn = ((mn - 1) % 60);
          }
          Serial.println(mn);

        } else if (i == 2) {
          Serial.print("EDIT DAY: ");
          if (buttonState2) {
            d = ((d + 1) % 32);
            if (d == 0) {
              d = 1;
            }
          }
          if (buttonState3) {
            if (d == 1) {
              d = 32;
            }
            d = ((d - 1) % 32);
          }
          Serial.println(d);
        } else if (i == 3) {
          Serial.print("EDIT MONTH: ");
          if (buttonState2) {
            m = ((m + 1) % 13);
            if (m == 0) {
              m = 1;
            }
          }
          if (buttonState3) {
            if (m == 1) {
              m = 13;
            }
            m = ((m - 1) % 13);
          }
          Serial.println(m);
        } else {
          Serial.print("EDIT PRIORITY: ");

          if (buttonState2) {
            p = ((p + 1) % 3);
          }
          if (buttonState3) {
            if (p == 0) {
              p = 3;
            }
            p = ((p - 1) % 3);
          }
          if (p == 0) {
            digitalWrite(ledPin1, HIGH);
            digitalWrite(ledPin2, LOW);
            digitalWrite(ledPin3, LOW);
          } else if (p == 1) {
            digitalWrite(ledPin2, HIGH);
            digitalWrite(ledPin1, LOW);
            digitalWrite(ledPin3, LOW);
          } else {
            digitalWrite(ledPin3, HIGH);
            digitalWrite(ledPin2, LOW);
            digitalWrite(ledPin1, LOW);
          }
          Serial.println(p);
        }
        buttonState2 = LOW;
        buttonState3 = LOW;
        ///
        String fecha = formatDigit(d) +  "/" + formatDigit(m) + "/" + String(year());
        imprimir(1, 12, 4, fecha);
        String hora = formatDigit(hr) +  ":" + formatDigit(mn);
        imprimir(3, 12, 4 + 24, hora);
        String prioridad = "Nivel de Prioridad:" + String(p);
        imprimir(1, 12, 105, prioridad);
        delay(100);
        ///
      }
    }
    buttonState1 = LOW;
  }
  if (buttonState2 == HIGH) {
    Serial.print("ALARMAS");
    tft.fillScreen(COLOR2);
    tft.drawRect(0, 0, 160, 128, COLOR1);
    imprimir(1, 12, 4, "Alarmas");
    int y = 28;
    for (int i = 0; i < numAlarmas; i++) {
      Alarma alarma = alarmas[i];
      String alarmaStr;
      if (alarma.isSet()) {
        String fecha = formatDigit(alarma.getDia()) + "/" + formatDigit(alarma.getMes());
        String hora = formatDigit(alarma.getHora()) + ":" + formatDigit(alarma.getMinuto());
        alarmaStr = fecha + " " + hora + " " + String(alarma.getPrioridad());
      } else {
        alarmaStr = "**NO HAY ALARMA**";
      }
      imprimir(1, 12, y, alarmaStr);
      y = y + 30;
    }
    imprimir(1, 12, 108, "Btn1:Exit - Btn 3:Reset");
    while (true) {
      if (digitalRead(buttonPin3)) {
        tft.fillScreen(COLOR2);
        tft.drawRect(0, 0, 160, 128, COLOR1);
        for (int i = 0; i < numAlarmas; i++) {
          alarmas[i].resetAlarma();
        }
        Serial.print("RESET ALL");
        break;
      }
      if (digitalRead(buttonPin1)) {
        tft.fillScreen(COLOR2);
        tft.drawRect(0, 0, 160, 128, COLOR1);
        Serial.print("EXIT");
        delay(150);
        buttonState1 = LOW;
        break;
      }
    }
    buttonState2 = LOW;
  }
  ///
  String fecha = diaSemana[weekday()] + " " + formatDigit(day()) +  "/" + formatDigit(month()) + "/" + String(year());
  imprimir(1, 12, 4, fecha);

  String hora = formatDigit(hour()) +  ":" + formatDigit(minute());
  imprimir(3, 12, 4 + 24, hora);

  tempC = dht.readTemperature();
  String temperatura = "Temp:" + String(tempC) + " C";
  imprimir(2, 12, 105, temperatura);
  ///

  // ALARMA - DURA 10 SECS
  for (int i = 0; i < numAlarmas; i++) {
    if (alarmas[i].comprobar(day(), month(), hour(), minute()) && second() <= 10) {
      tone(buzzerPin, 1000);
      delay(250);
      noTone(buzzerPin);
      int prioridad = alarmas[i].getPrioridad();
      if (prioridad == 0) {
        digitalWrite(ledPin1, HIGH);
        delay(250);
        digitalWrite(ledPin1, LOW);
      } else if (prioridad == 1) {
        digitalWrite(ledPin2, HIGH);
        delay(250);
        digitalWrite(ledPin2, LOW);
      } else {
        digitalWrite(ledPin3, HIGH);
        delay(250);
        digitalWrite(ledPin3, LOW);
      }
    }
  }
  delay(500);
}

Vídeo

Dificultades y Soluciones 

En el proceso de realización del proyecto surgieron múltiples problemas, uno de ellos ocurrió con el reloj integrado.

En primer lugar, el circuito integrado requería un oscilador y una pila, lo que aumentaba el número de pines a utilizar. Como no teníamos suficientes pines digitales después de conectar pantalla, botones y LEDs, lo descartamos y usamos una librería. 

Otro problema que nos encontramos y que sigue (en parte) ocurriendo es la mala calidad de la pantalla, la cual parpadea de forma intermitente y todos los colores que mostraba los mostraba degradados en un lado de la pantalla.  

Después de hablar con el profesor, nos comentó que el tema de contraste igual podría ser solucionado con el uso de otra librería. 

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 *