EL RELOJ DIGITAL GRANDE

EL RELOJ DIGITAL GRANDE

El objetivo de este proyecto fue el de poder contar con un reloj digital que fuera extremadamente fácil de ver, por lo que sus dígitos debían ser lo más grandes que fuera posible, y con luz propia. Además, era necesario que el mismo brindase siempre la hora exacta.

La razón de esos requerimientos fue que este reloj sirviese como reemplazo de un gadget del sistema Windows de la computadora principal del departamento, el cual presentaba en la pantalla principal del sistema, un recuadro rectangular en el que se mostraba la hora actual con dígitos grandes y de color llamativo, de manera que se pudiera saber rápidamente la hora actual sin necesidad de desviar la vista de la pantalla. Además, el reloj del gadget estaba siempre en hora, debido a la conexión con Internet, que le permitía sincronizarse con los servidores de NTP (Network Time Protocol) de la red. Este gadget cumplía muy bien su función, con la salvedad de que, a veces, el espacio de pantalla que ocupaba se superponía con algún elemento necesario en los programas que se estaban utilizando. Normalmente esto no debía suceder, ya que mediante un utilitario específico, toda una franja vertical de la pantalla principal quedaba reservada para gadgets, impidiendo que los programas normales la ocupen. Pero al utilizar Photoshop CC, en particular, este podía extenderse sobre esa zona, quedando a veces algún control necesario para la edición, oculto e inaccesible debajo del reloj.

Como se buscaba una solución de hardware, el primer paso fue elegir cómo mostrar la hora, por lo que se buscaron dígitos con luz propia (LED) del mayor tamañio posible. Al momento de comprar los elementos, se conseguían displays de 7 segmentos de una y de dos pulgadas de alto. Desafortunadamente, de los de dos pulgadas, no quedaban más que tres unidades, y el proyecto requería de cuatro de ellas: dos para la hora y dos para los minutos.

Se optó entonces por utilizar cuatro dígitos de LEDs rojos, de una pulgada de alto, como el que se muestra en la siguiente imagen:

Los cuatro dígitos fueron montados en una plaqueta perforada, en dos grupos de dos, separados levemente por dos LEDs pequeños que cumplen la función de mostrar los dos puntos separadores de las horas respecto de los minutos. En esta plaqueta, se montaron también 8 resistencias de 330 ohms, una por cada uno de los 7 segmentos y otra para el punto de los displays. En cuanto a las conexiones, se juntaron los contactos de cada segmento (y los de los puntos) en una misma conexión para los cuatro dígitos, interponiendo una de las resistencias para limitar la corriente y evitar que se quemen. Si bien en este proyecto no se utilizan los puntos, se incluyeron sus conexiones en la plaqueta por si en un futuro fuese necesario destinarla a otro proyecto.

En definitiva, la plaqueta formó un módulo con cuatro dígitos LED, cuyos segmentos contaban con un único contacto común a los cuatro dígitos, protegidos mediante las resistencias.

Se asignó además, un contacto para cada una de las terminales de cátodo común de los dígitos, y dos contactos para la alimentación de los leds que forman los dos puntos separadores.
En definitiva, la unidad es interconectable con el circuito de control que se elija utilizar mediante un cable plano de 8 hilos para las señales de los segmentos, uno de 4 hilos para alimentar a cada dígito en particular, y otro de 2 hilos para alimentar los dos puntos.

Si bien, todos los segmentos de igual posición en los cuatro dígitos utilizan la misma conexión, utilizando una técnica de multiplexado, que consiste en proporcionar la tensión de alimentación a un dígito por vez, a la vez que se envían las señales a los segmentos que deben mostrarse en el mismo para que forme el número deseado. De esta manera, se debe encender cada dígito en forma secuencial y con una velocidad tal que parezcan estar encendidos todos a la vez en forma continua.

Como unidad de control para ese módulo, la opción más natural fue la de una placa Arduino.

Dada la necesidad de que la hora se sincronice con la Internet, era necesario también un módulo de conexión a redes, ya sea mediante WiFi, cable Ethernet, o telefonía celular.

Se optó entonces por una placa Arduino MEGA 2560 R3, con un Shield de conexión a Ethernet, simplemente porque estaba disponible en casa al momento de comenzar el proyecto, pero existen varias posibles configuraciones alternativas, algunas incluso, de menor costo.

La concreción del proyecto fue entonces sencilla, se acoplaron la placa Arduino y el módulo Ethernet, y se conectó el módulo de los dígitos mediante los cables planos, asignando una salida digital del Arduino para cada segmento, una para cada terminal de cátodo común de los dígitos, y se conectaron los dos cables de alimentación de los LEDs de los dos puntos separadores, directamente a las terminales de masa y 5 voltios de la plaqueta.

Se montó el módulo con los dígitos en una caja plástica de frente desmontable, reemplazando el de plástico original por un trozo de cartón grueso negro, de igual forma y tamaño, con un calado que permite ver sólo los dígitos.



Entre el frente de cartón y los dígitos, se colocó un rectángulo de acrílico de color rojo oscuro, transparente, para mejorar el contraste de la luz emitida por el módulo.

La plaqueta Arduino con el módulo de conexión a Ethernet acoplado fueron adheridos al interior del gabinete mediante una tira de velcro, y los cables de alimentación y de conexión a la red, fueron adheridos al gabinete utilizando una pistola de resina plástica, de manera que soporten tironéos moderados sin que estos puedan afectar a los circuitos.

Finalmente se cargó en el Arduino, un sketch con la programación necesaria para la configuración inicial del sistema y un lazo cerrado en el que constantemente se consulta la hora en los servidores NTP y se la muestra en los dígitos.
  
#include "Arduino.h"
#include <SPI.h>                  //
#include <Ethernet.h>             // PARA LA CONEXION A LA RED E INTERNET
#include <EthernetUdp.h>          // PROTOCOLO UDP EN LA RED E INTERNET
#include <Dns.h>                  // PARA TRABAJAR CON DNS

// VARIABLES PARA LA CONEXION EN RED
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

// INICIALIZACION DE UDP
unsigned int localPort = 80;
IPAddress timeServer(193, 92, 150, 3);
const int NTP_PACKET_SIZE= 48;
byte packetBuffer[NTP_PACKET_SIZE];
const char* host="nsath.forthnet.gr";
EthernetUDP Udp;
DNSClient Dns;
IPAddress rem_add;

int horas;
int minutos;

// ***********************************************
// ** SETUP                                     **
// ***********************************************
void setup() {
  pinMode(22,OUTPUT);   // Display 1 - común
  pinMode(24,OUTPUT);   // Display 2 - común
  pinMode(26,OUTPUT);   // Display 3 - común
  pinMode(28,OUTPUT);   // Display 4 - común
  pinMode(30,OUTPUT);   // Dígito -
  pinMode(32,OUTPUT);   // Dígito -
  pinMode(34,OUTPUT);   // Dígito -
  pinMode(36,OUTPUT);   // Dígito -
  pinMode(38,OUTPUT);   // Dígito -
  pinMode(40,OUTPUT);   // Dígito -
  pinMode(42,OUTPUT);   // Dígito -
  pinMode(44,OUTPUT);   // Dígito -
  digitalWrite(22,HIGH);
  digitalWrite(24,LOW);
  digitalWrite(26,LOW);
  digitalWrite(28,LOW);
  digitalWrite(30,HIGH);  // Inferior derecho
  digitalWrite(32,HIGH);  // Punto
  digitalWrite(34,HIGH);  // Inferior izquierdo
  digitalWrite(36,HIGH);  // Abajo y al centro
  digitalWrite(38,HIGH);  // Al centro
  digitalWrite(40,HIGH);  // Superior izquierdo
  digitalWrite(42,HIGH);  // Superior izquierdo
  digitalWrite(44,HIGH);  // Superior
  Serial.begin(9600);
  if (Ethernet.begin(mac) == 0) {
    Serial.println("ERROR EN DHCP");
  }
  Udp.begin(localPort);
  Dns.begin(Ethernet.dnsServerIP());
  horas = 0;
  minutos = 0;
}

// **************************
// ** RUTINAS DE APOYO     **
// **************************

// ACTIVAR DIGITO
void activarDigito(int digito, int valor) {
  switch (digito) {
    case 1:
      digitalWrite(22,HIGH);
      digitalWrite(24,LOW);
      digitalWrite(26,LOW);
      digitalWrite(28,LOW);
      break;
    case 2:
      digitalWrite(22,LOW);
      digitalWrite(24,HIGH);
      digitalWrite(26,LOW);
      digitalWrite(28,LOW);
      break;
    case 3:
      digitalWrite(22,LOW);
      digitalWrite(24,LOW);
      digitalWrite(26,HIGH);
      digitalWrite(28,LOW);
      break;
    case 4:
      digitalWrite(22,LOW);
      digitalWrite(24,LOW);
      digitalWrite(26,LOW);
      digitalWrite(28,HIGH);
      break;
  }
  switch (valor) {
    case 0:
      digitalWrite(40,LOW);   // Superior izquierdo
      digitalWrite(44,LOW);   // Superior
      digitalWrite(42,LOW);   // Superior derecho
      digitalWrite(38,HIGH);  // Al centro
      digitalWrite(30,LOW);   // Inferior derecho
      digitalWrite(36,LOW);   // Abajo y al centro
      digitalWrite(34,LOW);   // Inferior izquierdo
      digitalWrite(32,HIGH);   // Punto
      break;
    case 1:
      digitalWrite(40,HIGH);  // Superior izquierdo
      digitalWrite(44,HIGH);  // Superior
      digitalWrite(42,LOW);   // Superior derecho
      digitalWrite(38,HIGH);   // Al centro
      digitalWrite(30,LOW);   // Inferior derecho
      digitalWrite(36,HIGH);   // Abajo y al centro
      digitalWrite(34,HIGH);  // Inferior izquierdo
      digitalWrite(32,HIGH);  // Punto
      break;
    case 2:
      digitalWrite(40,HIGH);   // Superior izquierdo
      digitalWrite(44,LOW);   // Superior
      digitalWrite(42,LOW);   // Superior derecho
      digitalWrite(38,LOW);   // Al centro
      digitalWrite(30,HIGH);   // Inferior derecho
      digitalWrite(36,LOW);   // Abajo y al centro
      digitalWrite(34,LOW);  // Inferior izquierdo
      digitalWrite(32,HIGH);  // Punto
      break;
    case 3:
      digitalWrite(40,HIGH);   // Superior izquierdo
      digitalWrite(44,LOW);   // Superior
      digitalWrite(42,LOW);   // Superior derecho
      digitalWrite(38,LOW);   // Al centro
      digitalWrite(30,LOW);   // Inferior derecho
      digitalWrite(36,LOW);   // Abajo y al centro
      digitalWrite(34,HIGH);  // Inferior izquierdo
      digitalWrite(32,HIGH);  // Punto
      break;
    case 4:
      digitalWrite(40,LOW);   // Superior izquierdo
      digitalWrite(44,HIGH);   // Superior
      digitalWrite(42,LOW);   // Superior derecho
      digitalWrite(38,LOW);   // Al centro
      digitalWrite(30,LOW);   // Inferior derecho
      digitalWrite(36,HIGH);   // Abajo y al centro
      digitalWrite(34,HIGH);  // Inferior izquierdo
      digitalWrite(32,HIGH);  // Punto
      break;
    case 5:
      digitalWrite(40,LOW);   // Superior izquierdo
      digitalWrite(44,LOW);   // Superior
      digitalWrite(42,HIGH);   // Superior derecho
      digitalWrite(38,LOW);   // Al centro
      digitalWrite(30,LOW);   // Inferior derecho
      digitalWrite(36,LOW);   // Abajo y al centro
      digitalWrite(34,HIGH);  // Inferior izquierdo
      digitalWrite(32,HIGH);  // Punto
      break;
    case 6:
      digitalWrite(40,LOW);   // Superior izquierdo
      digitalWrite(44,LOW);   // Superior
      digitalWrite(42,HIGH);   // Superior derecho
      digitalWrite(38,LOW);   // Al centro
      digitalWrite(30,LOW);   // Inferior derecho
      digitalWrite(36,LOW);   // Abajo y al centro
      digitalWrite(34,LOW);  // Inferior izquierdo
      digitalWrite(32,HIGH);  // Punto
      break;
    case 7:
      digitalWrite(40,HIGH);   // Superior izquierdo
      digitalWrite(44,LOW);   // Superior
      digitalWrite(42,LOW);   // Superior derecho
      digitalWrite(38,HIGH);   // Al centro
      digitalWrite(30,LOW);   // Inferior derecho
      digitalWrite(36,HIGH);   // Abajo y al centro
      digitalWrite(34,HIGH);  // Inferior izquierdo
      digitalWrite(32,HIGH);  // Punto
      break;
    case 8:
      digitalWrite(40,LOW);   // Superior izquierdo
      digitalWrite(44,LOW);   // Superior
      digitalWrite(42,LOW);   // Superior derecho
      digitalWrite(38,LOW);   // Al centro
      digitalWrite(30,LOW);   // Inferior derecho
      digitalWrite(36,LOW);   // Abajo y al centro
      digitalWrite(34,LOW);  // Inferior izquierdo
      digitalWrite(32,HIGH);  // Punto
      break;
    case 9:
      digitalWrite(40,LOW);   // Superior izquierdo
      digitalWrite(44,LOW);   // Superior
      digitalWrite(42,LOW);   // Superior derecho
      digitalWrite(38,LOW);   // Al centro
      digitalWrite(30,LOW);   // Inferior derecho
      digitalWrite(36,LOW);   // Abajo y al centro
      digitalWrite(34,HIGH);  // Inferior izquierdo
      digitalWrite(32,HIGH);  // Punto
      break;
    case 10:
      digitalWrite(40,HIGH);   // Superior izquierdo
      digitalWrite(44,HIGH);   // Superior
      digitalWrite(42,HIGH);   // Superior derecho
      digitalWrite(38,HIGH);   // Al centro
      digitalWrite(30,HIGH);   // Inferior derecho
      digitalWrite(36,HIGH);   // Abajo y al centro
      digitalWrite(34,HIGH);  // Inferior izquierdo
      digitalWrite(32,LOW); // Punto
      break;
  }
}

  void mostrarHora(int horas, int minutos) {
    activarDigito(1,(int) horas/10);
    delay(1);
    activarDigito(2, horas%10);
    delay(1);
    activarDigito(3,(int) minutos/10);
    delay(1);
    activarDigito(4, minutos%10);
    delay(1);
  }

// RESETEA POR SOFTWARE AL ARDUINO
void (* resetFunc)(void) = 0;

// SEND AN NTP REQUEST TO THE TIME SERVER AT THE GIVEN ADDRESS
unsigned long sendNTPpacket(IPAddress& address) {
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  packetBuffer[0] = 0b11100011;
  packetBuffer[1] = 0;
  packetBuffer[2] = 6;
  packetBuffer[3] = 0xEC;
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;
  Udp.beginPacket(address, 123);
  Udp.write(packetBuffer,NTP_PACKET_SIZE);
  Udp.endPacket();
}




// ***********************************************
// ** LOOP                                      **
// ***********************************************
void loop() {
  if(Dns.getHostByName(host, rem_add) == 1 ){
    Serial.println("DNS resolve...");
    Serial.print(host);
    Serial.print("  = ");
    Serial.println(rem_add);
    sendNTPpacket(rem_add);
  } else {
    Serial.print("DNS fail...");
    Serial.print("time.nist.gov = ");
    Serial.println(timeServer);
    sendNTPpacket(timeServer);
  }
  for (int iii=1; iii<5000; iii++) {
    mostrarHora(horas,minutos);
    delay(1);
  }
  if ( Udp.parsePacket() ) {
    Udp.read(packetBuffer,NTP_PACKET_SIZE);	 // read the packet into the buffer
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    const unsigned long seventyYears = 2208988800UL;
    unsigned long epoch = secsSince1900 - seventyYears - 3*60*60;
    Serial.println(epoch);
    horas = ((epoch % 86400L) / 3600);
    minutos = ((epoch % 3600) / 60);
  }
  for (int iii=1; iii<5000; iii++) {
    mostrarHora(horas,minutos);
    delay(1);
  }
}