//Librerias
#include "LedControl.h"
#include <FontLEDClock.h> //libreria de tipografias
#include <Wire.h> // Libreria para DS1307
#include "RTClib.h" // Libreria para DS1307
#include <Button.h> // Libreria de botones
unsigned long ct = 0;
unsigned long pt = 0;
// Configurar la matriz LED
// el pin 11 está conectado a DataIn en la pantalla
// el pin 13 está conectado a CLK en la pantalla
// el pin 10 está conectado a CS en la pantalla
LedControl lc = LedControl(11, 13, 10, 4); // configura los pines 11, 13 y
10 y luego establece 4 pantallas (el máximo es 8 pantallas)
// Variables globales
byte intensity = 5; // Intensidad/brillo predeterminado
(0-15)
byte clock_mode = 1; // Modo de reloj predeterminado.
Predeterminado = 0 (basic_mode)
bool random_mode = 0; // Define el modo aleatorio: cambia
el tipo de visualización cada pocas horas. Predeterminado = 0 (desactivado)
byte old_mode = clock_mode; // Almacena el modo de reloj
anterior, para que si vamos a la fecha o lo que sea, sepamos a qué modo
volver después.
bool ampm = 1; // Define el formato de hora: 12 o
24 horas. 0 = 24 horas. 1 = 12 horas
byte change_mode_time = 0; // Almacena la hora en la que el
modo de reloj cambiará si está en modo aleatorio.
unsigned long delaytime = 500; // Siempre esperamos un poco entre
actualizaciones de la pantalla
int rtc[7]; // Almacena la salida del reloj en
tiempo real
char days[7][4] = {
"Dom", "Lun", "Mar", "Mie", "Jue", "Vie", "Sab"
}; // arreglo de días - utilizado en los modos slide, basic_mode y jumble
(El DS1307 devuelve valores del 1 al 7 para el día de la semana)
char daysfull[7][9] = {
"Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado"
};
char suffix[4][3] = {
"de", "de", "de", "de"
}; // sufijo de fecha - utilizado en los modos slide, basic_mode y jumble
// definir constantes
#define NUM_DISPLAY_MODES 3 // Número de modos de visualización
(contando cero como el primer modo)
#define NUM_SETTINGS_MODES 4 // Número de modos de configuración
= 6 (contando cero como el primer modo)
#define SLIDE_DELAY 20 // El tiempo en milisegundos para
el efecto de deslizamiento por carácter en el modo de deslizamiento. Aumenta
este valor para un efecto más lento
#define cls clear_display // Limpiar pantalla
RTC_DS1307 ds1307; // Crear objeto RTC
Button buttonA = Button(2, BUTTON_PULLUP); // Configurar botón A
(usando la biblioteca de botones)
Button buttonB = Button(3, BUTTON_PULLUP); // Configurar botón B
(usando la biblioteca de botones)
void setup() {
digitalWrite(2, HIGH); // activar resistencia pull-up para
el botón en el pin 2
digitalWrite(3, HIGH); // activar resistencia pull-up para
el botón en el pin 3
digitalWrite(4, HIGH); // activar resistencia pull-up para
el botón en el pin 4
[Link](9600); //Inicio del serial
// inicializar los 4 paneles de la matriz
// ya hemos establecido el número de dispositivos cuando creamos el
LedControl
int devices = [Link]();
// tenemos que inicializar todos los dispositivos en un bucle
for (int address = 0; address < devices; address++) {
/* El MAX72XX está en modo de ahorro de energía al inicio */
[Link](address, false);
/* Establecer el brillo a un valor medio */
[Link](address, intensity);
/* y borrar la pantalla */
[Link](address);
}
//Configurar DS1307 RTC
#ifdef AVR
[Link]();
#else
[Link](); // Los pines I2C del Shield se conectan al bus I2C
alternativo en Arduino
#endif
[Link](); // Iniciar el reloj RTC
if (! [Link]()) {
[Link]("¡El RTC no está funcionando!");
[Link](DateTime(__DATE__, __TIME__)); // establece el RTC a la
fecha y hora en que se compiló este sketch
}
// Mostrar la versión del software y el mensaje de bienvenida
printver();
// Habilitar el LED rojo
digitalWrite(13, HIGH);
}
void loop() {
// ejecutar el reloj con el modo establecido por clock_mode - el valor
predeterminado se establece en la parte superior del código.
switch (clock_mode){
case 0:
basic_mode();
break;
case 1:
small_mode();
break;
case 2:
slide();
break;
case 3:
word_clock();
break;
case 4:
setup_menu();
break;
}
}
//graficar un punto en la pantalla
void plot (byte x, byte y, byte val) {
//seleccionar la matriz dependiendo de la coordenada x
byte address;
if (x >= 0 && x <= 7) {
address = 3;
}
if (x >= 8 && x <= 15) {
address = 2;
x = x - 8;
}
if (x >= 16 && x <= 23) {
address = 1;
x = x - 16;
}
if (x >= 24 && x <= 31) {
address = 0;
x = x - 24;
}
if (val == 1) {
[Link](address, y, x, true);
} else {
[Link](address, y, x, false);
}
}
//Limpiar la pantalla
void clear_display() {
for (byte address = 0; address < 4; address++) {
[Link](address);
}
}
//desvanecer la pantalla
void fade_down() {
//desvanecer desde la intensidad global hasta 1
for (byte i = intensity; i > 0; i--) {
for (byte address = 0; address < 4; address++) {
[Link](address, i);
}
delay(30); //cambia esto para ajustar la velocidad de desvanecimiento
}
}
clear_display(); // Limpiar completamente la pantalla (apagada)
// Restablecer la intensidad al valor global
for (byte address = 0; address < 4; address++) {
[Link](address, intensity);
}
}
// Encender prueba de LED y mostrar número de versión del software
void printver() {
byte i = 0;
char hello_message[9] = " ¡Hola! ";
// probar todos los LEDs.
for (byte x = 0; x <= 31; x++) {
for (byte y = 0; y <= 7; y++) {
plot(x, y, 1);
}
}
delay(500);
fade_down();
while (hello_message[i]) {
puttinychar((i * 4), 1, hello_message[i]);
delay(35);
i++;
}
delay(700);
fade_down();
}
// Copia un glifo de carácter de 3x5 desde la estructura de datos myfont a
la memoria de visualización, con su esquina superior izquierda en las
coordenadas dadas
// Esto no está optimizado y simplemente utiliza plot() para dibujar cada
punto.
void puttinychar(byte x, byte y, char c)
{
byte dots;
if (c >= 'A' && c <= 'Z' || (c >= 'a' && c <= 'z') ) {
c &= 0x1F; // Mapear A-Z a 1-26
}
else if (c >= '0' && c <= '9') {
c = (c - '0') + 32;
}
else if (c == ' ') {
c = 0; // espacio
}
else if (c == '.') {
c = 27; // punto
}
else if (c == ':') {
c = 28; // dos puntos
}
else if (c == '\'') {
c = 29; // comilla simple
}
else if (c == '!') {
c = 30; // signo de exclamación
}
else if (c == '?') {
c = 31; // signo de interrogación
}
for (byte col = 0; col < 3; col++) {
dots = pgm_read_byte_near(&mytinyfont[c][col]);
for (char row = 0; row < 5; row++) {
if (dots & (16 >> row))
plot(x + col, y + row, 1);
else
plot(x + col, y + row, 0);
}
}
}
void putnormalchar(byte x, byte y, char c)
{
byte dots;
// if (c >= 'A' && c <= 'Z' || (c >= 'a' && c <= 'z') ) {
// c &= 0x1F; // A-Z maps to 1-26
// }
if (c >= 'A' && c <= 'Z' ) {
c &= 0x1F; // A-Z se mapea a 1-26
}
else if (c >= 'a' && c <= 'z') {
c = (c - 'a') + 41; // A-Z se mapea a 41-67
}
else if (c >= '0' && c <= '9') {
c = (c - '0') + 31;
}
else if (c == ' ') {
c = 0; // espacio
}
else if (c == '.') {
c = 27; // punto
}
else if (c == '\'') {
c = 28; // comilla simple
}
else if (c == ':') {
c = 29; // flecha del selector de modo de reloj
}
else if (c == '>') {
c = 30; // flecha del selector de modo de reloj
}
else if (c >= -80 && c <= -67) {
c *= -1;
}
for (char col = 0; col < 5; col++) {
dots = pgm_read_byte_near(&myfont[c][col]);
for (char row = 0; row < 7; row++) {
// verificar que las coordenadas estén en la pantalla antes de
intentar trazar
//if ((x >= 0) && (x <= 31) && (y >= 0) && (y <= 7)){
if (dots & (64 >> row)) { // solo 7 filas.
plot(x + col, y + row, 1);
} else {
plot(x + col, y + row, 0);
}
//}
}
}
}
//small_mode
//mostrar la hora en caracteres pequeños de 3x5 con visualización de
segundos
void small_mode() {
char textchar[8]; // los 16 caracteres en la pantalla
byte mins = 100; // minutos
byte secs = rtc[0]; // segundos
byte old_secs = secs; // almacena el valor anterior de segundos - desde la
última vez que los segundos se actualizaron en la pantalla - se utiliza para
verificar si los segundos han cambiado
cls();
// ejecutar el bucle principal del reloj mientras run_mode devuelva true
while (run_mode()) {
get_time();
// verificar si se presiona el botón A
if ([Link]()) {
switch_mode();
return;
}
// verificar si se presiona el botón B
if ([Link]()) {
display_date();
return;
}
// si los segundos han cambiado, actualizarlos en la pantalla
secs = rtc[0];
if (secs != old_secs) {
// segundos
char buffer[3];
itoa(secs, buffer, 10);
// corregir - ya que si el número tiene un cero inicial, por ejemplo,
"03" segundos, itoa lo convierte en caracteres con espacio "3 ".
if (secs < 10) {
buffer[1] = buffer[0];
buffer[0] = '0';
}
puttinychar( 20, 1, ':'); // dos puntos de los segundos
puttinychar( 24, 1, buffer[0]); // segundos
puttinychar( 28, 1, buffer[1]); // segundos
old_secs = secs;
}
// si cambia el minuto, cambiar la hora
if (mins != rtc[1]) {
// restablecer estos valores para la comparación la próxima vez
mins = rtc[1];
byte hours = rtc[2];
if (hours > 12) {
hours = hours - ampm * 12;
}
if (hours < 1) {
hours = hours + ampm * 12;
}
//byte dow = rtc[3]; // el DS1307 devuelve valores del 0 al 6 donde 0
= domingo.
//byte date = rtc[4];
// establecer caracteres
char buffer[3];
itoa(hours, buffer, 10);
// corregir - ya que si el número tiene un cero inicial, por ejemplo,
"03" horas, itoa lo convierte en caracteres con espacio "3 ".
if (hours < 10) {
buffer[1] = buffer[0];
// si estamos en modo de 12 horas, dejar en blanco el cero inicial.
if (ampm) {
buffer[0] = ' ';
}
else {
buffer[0] = '0';
}
}
// establecer caracteres de horas
textchar[0] = buffer[0];
textchar[1] = buffer[1];
textchar[2] = ':';
itoa (mins, buffer, 10);
if (mins < 10) {
buffer[1] = buffer[0];
buffer[0] = '0';
}
// establecer caracteres de minutos
textchar[3] = buffer[0];
textchar[4] = buffer[1];
// hacer los segundos
textchar[5] = ':';
buffer[3];
secs = rtc[0];
itoa(secs, buffer, 10);
// corregir - ya que si el número tiene un cero inicial, por ejemplo,
"03" segundos, itoa lo convierte en caracteres con espacio "3 ".
if (secs < 10) {
buffer[1] = buffer[0];
buffer[0] = '0';
}
// establecer segundos
textchar[6] = buffer[0];
textchar[7] = buffer[1];
byte x = 0;
byte y = 0;
// imprimir cada carácter
for (byte x = 0; x < 6 ; x++) {
puttinychar( x * 4, 1, textchar[x]);
}
}
delay(50);
ct = millis();
if(ct-pt>20000)
{
pt = ct;
display_date();
return;
}
}
fade_down();
}
// basic_mode()
// muestra la hora en caracteres de 5x7
void basic_mode()
{
cls();
char buffer[3]; // para la conversión de int a char para convertir los
valores rtc en chars que podemos imprimir en la pantalla
byte offset = 0; // utilizado para desplazar la posición x de los dígitos
y centrar la pantalla cuando estamos en modo de 12 horas y el reloj muestra
solo 3 dígitos. p. ej., 3:21
byte x, y; // utilizado para dibujar un cuadro transparente sobre
el "1" de la izquierda de la pantalla cuando pasamos de 12:59 -> 1:00 am en
el modo de 12 horas.
// hacer la conversión de 12/24 horas si ampm está configurado en 1
byte hours = rtc[2];
if (hours > 12) {
hours = hours - ampm * 12;
}
if (hours < 1) {
hours = hours + ampm * 12;
}
// hacer la conversión de offset
if (ampm && hours < 10) {
//offset = 2;
}
// establecer el siguiente minuto en el que mostraremos la fecha
//set_next_date();
// inicialmente establecer mins en el valor 100 - para que nunca sea igual
a rtc[1] en el primer bucle del reloj, lo que significa que dibujamos la
pantalla del reloj cuando ingresamos a la función
byte secs = 100;
byte mins = 100;
int count = 0;
// ejecutar el bucle principal del reloj siempre que run_mode devuelva
true
while (run_mode()) {
// Obtener la hora del chip del reloj
get_time();
// Verificar si se presionó el botón A
if ([Link]()) {
switch_mode();
return;
}
// Verificar si se presionó el botón B
if ([Link]()) {
display_date();
return;
}
// Verificar si es hora de mostrar automáticamente la fecha
//check_show_date();
// Dibujar el parpadeo de los dos puntos si los segundos han cambiado.
if (secs != rtc[0]) {
// actualizar secs con el nuevo valor
secs = rtc[0];
// dibujar :
plot (15, 2, 1); // punto superior
plot (15, 5, 1); // punto inferior
count = 400;
}
// si count se ha agotado, apagar el :
if (count == 0) {
plot (15, 2, 0); // punto superior
plot (15, 5, 0); // punto inferior
}
else {
count--;
}
// volver a dibujar la pantalla si se presiona el botón o si mins !=
rtc[1], es decir, si el tiempo ha cambiado desde lo que teníamos almacenado
en mins (también se activa al ingresar por primera vez a la función cuando
mins es 100)
if (mins != rtc[1]) {
// actualizar mins y hours con los nuevos valores
mins = rtc[1];
hours = rtc[2];
// ajustar hours si ampm está configurado en modo de 12 horas
if (hours > 12) {
hours = hours - ampm * 12;
}
if (hours < 1) {
hours = hours + ampm * 12;
}
itoa(hours, buffer, 10);
// si hours < 10, el número, por ejemplo, "3" horas, itoa convierte
esto en caracteres con espacio "3 ", lo cual no queremos
if (hours < 10) {
buffer[1] = buffer[0];
buffer[0] = '0';
}
//imprimir horas
//si estamos en modo de 12 horas y las horas son menores a 10,
entonces no imprimir el cero inicial y establecer el desplazamiento para
centrar la pantalla con 3 dígitos.
if (ampm && hours < 10) {
//offset = 2;
//si la hora es 1:00 am, limpiar toda la pantalla ya que el
desplazamiento cambia en este momento y necesitamos borrar el antiguo 12:59
if ((hours == 1 && mins == 0) ) {
cls();
}
}
else {
//else no offset and print hours tens digit
offset = 0;
//si la hora es 10:00 am, limpiar toda la pantalla ya que el
desplazamiento cambia en este momento y necesitamos borrar el antiguo 9:59
if (hours == 10 && mins == 0) {
cls();
}
//putnormalchar(1, 0, buffer[0]);
putnormalchar(2, 0, buffer[0]);
}
//print hours ones digit
putnormalchar(8 - offset, 0, buffer[1]);
// imprimir minutos
// agregar cero inicial si los minutos son menores a 10
itoa(mins, buffer, 10);
if (mins < 10) {
buffer[1] = buffer[0];
buffer[0] = '0';
}
// imprimir las decenas y unidades de los minutos
putnormalchar(18 - offset, 0, buffer[0]);
putnormalchar(24 - offset, 0, buffer[1]);
}
ct = millis();
if(ct-pt>20000)
{
pt = ct;
display_date();
return;
}
}
fade_down();
//como basic_mode pero con efecto de deslizamiento
void slide() {
byte digits_old[4] = {99, 99, 99, 99}; // valores antiguos que almacenamos
para el tiempo. Se establece en algo que nunca coincidirá con el tiempo
inicialmente para que todos los dígitos se dibujen cuando comienza el modo
byte digits_new[4]; // nuevos dígitos que se deslizarán para revelar
byte digits_x_pos[4] = {25, 19, 7, 1}; // posición x para dibujar cada
dígito
char old_char[2]; // se utiliza cuando usamos itoa para transponer el
dígito actual (tipo byte) en un char para pasarlo a la función de animación
char new_char[2]; // se utiliza cuando usamos itoa para transponer el
nuevo dígito (tipo byte) en un char para pasarlo a la función de animación
// old_chars: almacena los 5 caracteres de día y el sufijo de fecha en la
pantalla. p. ej., "lun" y "ro". Los pasamos a la animación de deslizamiento
como el carácter actual cuando se actualizan estos caracteres.
// Inicialmente los establecemos en A, que se utiliza cuando el reloj
entra en el modo y no se almacenan caracteres antiguos.
// char old_chars[6] = "AAAAA";
//graficar los dos puntos del reloj en la pantalla
cls();
putnormalchar( 13, 0, ':');
byte old_secs = rtc[0]; //almacenar los segundos en old_secs. Comparamos
los segundos y los viejos segundos. Cuando son diferentes, volvemos a
dibujar la pantalla
//ejecutar el bucle principal del reloj siempre que run_mode devuelva true
while (run_mode()) {
get_time();
// verificar si se presiona el botón A
if ([Link]()) {
switch_mode();
return;
}
// verificar si se presiona el botón B
if ([Link]()) {
display_date();
return;
}
// si los segundos han cambiado, actualizar la pantalla
if (rtc[0] != old_secs) {
old_secs = rtc[0];
// realizar la conversión de 12/24 horas si ampm está configurado en 1
byte hours = rtc[2];
if (hours > 12) {
hours = hours - ampm * 12;
}
if (hours < 1) {
hours = hours + ampm * 12;
}
// separar la fecha y la hora en dígitos individuales y almacenarlos
en el array digits_new
// rtc[0] = seconds // posición del array y
dígito almacenado
// digits_new[0] = (rtc[0] % 10); // 0 - unidades de
segundos
// digits_new[1] = ((rtc[0] / 10) % 10); // 1 - decenas de segundos
// rtc[1] = minutes
digits_new[0] = (rtc[1] % 10); // 2 - unidades de minutos
digits_new[1] = ((rtc[1] / 10) % 10); // 3 - decenas de minutos
// rtc[2] = hours
digits_new[2] = (hours % 10); // 4 - unidades de horas
digits_new[3] = ((hours / 10) % 10); // 5 - decenas de horas
// rtc[4] = date
// digits_new[6] = (rtc[4] % 10); // 6 - unidades de fecha
// digits_new[7] = ((rtc[4] / 10) % 10); // 7 - decenas de fecha
// dibujar pantalla inicial de todos los caracteres. Después de
esto, solo dibujamos los cambios.
// comparar dígitos 0 a 3 (minutos y horas)
for (byte i = 0; i <= 3; i++) {
// verificar si el dígito ha cambiado...
if (digits_old[i] != digits_new[i]) {
// ejecutar secuencia de animación de 9 pasos para cada uno de
ellos
for (byte seq = 0; seq <= 8 ; seq++) {
// convertir el dígito a cadena
itoa(digits_old[i], old_char, 10);
itoa(digits_new[i], new_char, 10);
// si está configurado en modo de 12 horas y estamos en el
dígito 2 (modo de decenas de horas), entonces verifique si es un cero. Si lo
es, en lugar de eso, déjelo en blanco para que obtengamos 2.00 pm en lugar
de 02.00 pm
if (ampm && i == 3) {
if (digits_new[3] == 0) {
new_char[0] = ' ';
}
if (digits_old[3] == 0) {
old_char[0] = ' ';
}
}
// dibujar el marco de animación para cada dígito
slideanim(digits_x_pos[i], 0, seq, old_char[0], new_char[0]);
delay(SLIDE_DELAY);
}
}
}
// guardar el array de dígitos en el array de dígitos antiguos para
comparar en la siguiente iteración
for (byte i = 0; i <= 3; i++) {
digits_old[i] = digits_new[i];
}
}//secs/oldsecs
ct = millis();
if(ct-pt>20000)
{
pt = ct;
display_date();
return;
}
}//while loop
fade_down();
}
// llamado por slide
// esto dibuja la animación de un carácter deslizándose y el otro
deslizándose. Hay 8 pasos en la animación, llamamos a la función para
dibujar uno de los pasos de 0 a 7
// las entradas son el carácter x e y, la secuencia de fotogramas de
animación (0-7) y los caracteres actuales y nuevos que se están dibujando.
void slideanim(byte x, byte y, byte sequence, char current_c, char new_c) {
// Para deslizar un carácter y mostrar otro, necesitamos 9 pasos o
fotogramas en secuencia...
// seq# 0123456 <-filas de la pantalla
// | |||||||
// seq0 0123456 INICIO - todas las filas de la pantalla 0-6 muestran las
filas 0-6 del carácter actual
// seq1 012345 el carácter actual se mueve una fila hacia abajo en la
pantalla. Solo vemos las filas 0-5. Están en las posiciones de la pantalla
1-6. Hay una fila en blanco insertada en la parte superior
// seq2 6 01234 el carácter actual se mueve 2 filas hacia abajo. Ahora
solo vemos las filas 0-4 en las filas 2-6 de la pantalla. La fila 1 de la
pantalla está en blanco. La fila 0 muestra la fila 6 del nuevo carácter
// seq3 56 0123
// seq4 456 012 mitad del carácter antiguo / mitad del carácter nuevo
// seq5 3456 01
// seq6 23456 0
// seq7 123456
// seq8 0123456 FIN - todas las filas muestran el nuevo carácter
//a partir de lo anterior, podemos ver...
//currentchar va de 0 a 6, luego de 0 a 5, luego de 0 a 4 hasta 0. La
posición inicial de Y aumenta en 1 fila cada vez.
//new char va de 6, luego de 5 a 6, luego de 4 a 6, luego de 3 a 6. La
posición inicial de Y aumenta en 1 fila cada vez.
//si el número de secuencia es menor que 7, necesitamos dibujar el
carácter actual
if (sequence < 7) {
byte dots;
// if (current_c >= 'A' && || (current_c >= 'a' && current_c <= 'z') )
{
// current_c &= 0x1F; // A-Z maps to 1-26
// }
if (current_c >= 'A' && current_c <= 'Z' ) {
current_c &= 0x1F; // Mapea A-Z a 1-26
}
else if (current_c >= 'a' && current_c <= 'z') {
current_c = (current_c - 'a') + 41; // Mapea a-z a 41-67
}
else if (current_c >= '0' && current_c <= '9') {
current_c = (current_c - '0') + 31;
}
else if (current_c == ' ') {
current_c = 0; // espacio
}
else if (current_c == '.') {
current_c = 27; // punto
}
else if (current_c == '\'') {
current_c = 28; // comilla simple
}
else if (current_c == ':') {
current_c = 29; // dos puntos
}
else if (current_c == '>') {
current_c = 30; // flecha de selector de modo de reloj
}
byte curr_char_row_max = 7 - sequence; // el número máximo de filas a
dibujar es 6 - número de secuencia
byte start_y = sequence; // posición y para comenzar, es igual al número
de secuencia. Incrementamos esto en cada bucle
// dibujar cada fila hasta el máximo de filas (calculado a partir del
número de secuencia)
for (byte curr_char_row = 0; curr_char_row <= curr_char_row_max;
curr_char_row++) {
for (byte col = 0; col < 5; col++) {
dots = pgm_read_byte_near(&myfont[current_c][col]);
if (dots & (64 >> curr_char_row))
plot(x + col, y + start_y, 1); // encender LED
else
plot(x + col, y + start_y, 0); // apagar LED
}
start_y++; // agregar uno a y para dibujar la siguiente fila más abajo
}
}
// dibujar una línea en blanco entre los caracteres si la secuencia está
entre 1 y 8. Si no hacemos esto, quedan restos de la última posición de los
caracteres actuales en la pantalla
if (sequence >= 1 && sequence <= 8) {
for (byte col = 0; col < 5; col++) {
plot(x + col, y + (sequence - 1), 0); // la posición y para dibujar la
línea es equivalente al número de secuencia - 1
}
}
// si la secuencia es mayor o igual a 2, también necesitamos comenzar a
dibujar el nuevo carácter
if (sequence >= 2) {
// determinar el carácter
byte dots;
// if (new_c >= 'A' && new_c <= 'Z' || (new_c >= 'a' && new_c <= 'z') )
{
// new_c &= 0x1F; // A-Z se mapea a 1-26
// }
if (new_c >= 'A' && new_c <= 'Z' ) {
new_c &= 0x1F; // A-Z se mapea a 1-26
}
else if (new_c >= 'a' && new_c <= 'z') {
new_c = (new_c - 'a') + 41; // a-z se mapea a 41-67
}
else if (new_c >= '0' && new_c <= '9') {
new_c = (new_c - '0') + 31;
}
else if (new_c == ' ') {
new_c = 0; // espacio
}
else if (new_c == '.') {
new_c = 27; // punto
}
else if (new_c == '\'') {
new_c = 28; // comilla simple
}
else if (new_c == ':') {
new_c = 29; // flecha de selector de modo de reloj
}
else if (new_c == '>') {
new_c = 30; // flecha de selector de modo de reloj
}
byte newcharrowmin = 6 - (sequence - 2); // número mínimo de fila para
dibujar el nuevo carácter - esto genera una salida de 6 a 0 cuando se
alimentan números de secuencia de 2 a 8. Esta es la fila mínima para dibujar
el nuevo carácter
byte start_y = 0; // posición y para comenzar, es igual al número de
secuencia. Incrementamos esto en cada bucle
// dibujar cada fila desde la fila mínima (calculada a partir del número
de secuencia) hasta 6
for (byte newcharrow = newcharrowmin; newcharrow <= 6; newcharrow++) {
for (byte col = 0; col < 5; col++) {
dots = pgm_read_byte_near(&myfont[new_c][col]);
if (dots & (64 >> newcharrow))
plot(x + col, y + start_y, 1); // encender LED
else
plot(x + col, y + start_y, 0); // apagar LED
}
start_y++; // agregar uno a y para dibujar la siguiente fila más abajo
}
}
}
//imprimir un reloj usando palabras en lugar de números
void word_clock() {
cls();
char numbers[19][10] = {
"one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
"ten",
"eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen",
"seventeen", "eighteen", "nineteen"
};
char numberstens[5][7] = {
"ten", "twenty", "thirty", "forty", "fifty"
};
//potencialmente 3 líneas para mostrar
char str_a[8];
char str_b[8];
char str_c[8];
//byte hours_y, mins_y; //horas y minutos y posiciones para las líneas de
horas y minutos
byte hours = rtc[2];
if (hours > 12) {
hours = hours - ampm * 12;
}
if (hours < 1) {
hours = hours + ampm * 12;
}
get_time(); //obtener la hora del chip del reloj
byte old_mins = 100; //almacenar los minutos en old_mins. Comparamos los
minutos y los viejos minutos y cuando son diferentes volvemos a dibujar la
pantalla. Inicialmente establecemos esto en 100 para que se dibuje la
pantalla cuando comienza el modo.
byte mins;
//ejecutar el bucle principal del reloj siempre que run_mode devuelva
verdadero
while (run_mode()) {
//verificar si se presiona el botón
if ([Link]()) {
switch_mode();
return;
}
if ([Link]()) {
display_date();
}
get_time(); //obtener la hora del chip del reloj
mins = rtc[1]; //obtener los minutos
//si los minutos son diferentes de los viejos minutos - volver a dibujar
la pantalla
if (mins != old_mins) {
//actualizar old_mins con el valor actual de los minutos
old_mins = mins;
//restablecer estos valores para la comparación la próxima vez
mins = rtc[1];
hours = rtc[2];
//convertir las horas en formato de 12 horas
if (hours > 12) {
hours = hours - 12;
}
if (hours == 0) {
hours = 12;
}
// dividir el valor de los minutos en dos dígitos separados
int minsdigit = rtc[1] % 10;
byte minsdigitten = (rtc[1] / 10) % 10;
// si los minutos son menores o iguales a 10, entonces la primera
línea debe decir "minsdigit PAST" y la segunda línea dice horas
if (mins < 10) {
strcpy (str_a, numbers[minsdigit - 1]);
strcpy (str_b, "PAST");
strcpy (str_c, numbers[hours - 1]);
}
// si los minutos son iguales a 10, no se puede usar minsdigit como
arriba, por lo que es un caso especial para imprimir "10 pasado" /n hora.
if (mins == 10) {
strcpy (str_a, numbers[9]);
strcpy (str_b, " PAST");
strcpy (str_c, numbers[hours - 1]);
}
// si el tiempo no está en punto - es decir, ambos dígitos de los
minutos no son cero,
// entonces la primera línea dice "horas" y las 2da y 3ra líneas dicen
"minstens" "mins" por ejemplo "tres /n veinte /n uno"
else if (minsdigitten != 0 && minsdigit != 0 ) {
strcpy (str_a, numbers[hours - 1]);
// si los minutos están en los teens, usa los teens del arreglo
numbers para la 2da línea, por ejemplo "quince"
// if (mins >= 11 && mins <= 19) {
if (mins <= 19) {
strcpy (str_b, numbers[mins - 1]);
}
else {
strcpy (str_b, numberstens[minsdigitten - 1]);
strcpy (str_c, numbers[minsdigit - 1]);
}
}
// si el dígito de los minutos es cero, no lo imprima. leer "horas"
"minstens" por ejemplo "tres /n veinte"
else if (minsdigitten != 0 && minsdigit == 0 ) {
strcpy (str_a, numbers[hours - 1]);
strcpy (str_b, numberstens[minsdigitten - 1]);
strcpy (str_c, "");
}
// si ambos minutos son cero, es decir, está en punto, la línea
superior dice "horas" y la línea inferior dice "en punto"
else if (minsdigitten == 0 && minsdigit == 0 ) {
strcpy (str_a, numbers[hours - 1]);
strcpy (str_b, "EN PUNTO");
strcpy (str_c, "");
}
}//fin de calcular la hora
// ejecutar en un bucle
// imprimir línea a "doce"
byte len = 0;
while (str_a[len]) {
len++;
}; // obtener longitud del mensaje
byte offset_top = (31 - ((len - 1) * 4)) / 2; //
// dibujar línea de horas
byte i = 0;
while (str_a[i]) {
puttinychar((i * 4) + offset_top, 1, str_a[i]);
i++;
}
// mantener la pantalla pero verificar si se presiona un botón
int counter = 1000;
while (counter > 0){
// verificar si se presiona un botón
if ([Link]()) {
switch_mode();
return;
}
if ([Link]()) {
display_date();
}
delay(1);
counter--;
}
fade_down();
//imprimir línea b
len = 0;
while (str_b[len]) {
len++;
}; //obtener longitud del mensaje
offset_top = (31 - ((len - 1) * 4)) / 2;
i = 0;
while (str_b[i]) {
puttinychar((i * 4) + offset_top, 1, str_b[i]);
i++;
}
// mantener la pantalla pero verificar si se presiona un botón
counter = 1000;
while (counter > 0){
if ([Link]()) {
switch_mode();
return;
}
if ([Link]()) {
display_date();
}
delay(1);
counter--;
}
fade_down();
//imprimir línea c si existe.
len = 0;
while (str_c[len]) {
len++;
}; //obtener longitud del mensaje
offset_top = (31 - ((len - 1) * 4)) / 2;
i = 0;
while (str_c[i]) {
puttinychar((i * 4) + offset_top, 1, str_c[i]);
i++;
}
counter = 1000;
while (counter > 0){
// verificar si se presiona un botón
if ([Link]()) {
switch_mode();
return;
}
if ([Link]()) {
display_date();
}
delay(1);
counter--;
}
fade_down();
// mantener la pantalla en blanco pero verificar si se presiona un botón
antes de comenzar de nuevo.
counter = 1000;
while (counter > 0){
// verificar si se presiona un botón
if ([Link]()) {
switch_mode();
return;
}
if ([Link]()) {
display_date();
}
delay(1);
counter--;
}
}
fade_down();
}
//display_date - imprime el día de la semana, la fecha y el mes con un
efecto de cursor parpadeante
void display_date()
{
cls();
//lee la fecha del DS1307
byte dow = rtc[3]; // día de la semana 0 = domingo
byte date = rtc[4];
byte month = rtc[5] - 1;
int year = rtc[6];
//arreglo de nombres de mes para imprimir en la pantalla. Algunos están
acortados ya que solo tenemos 8 caracteres de ancho para jugar
char monthnames[12][8] = {
"Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio",
"Agosto", "Septiemb", "Octubre", "Noviembr", "Diciembr"
};
//imprimir el nombre del día
//obtener la longitud del texto en píxeles, de esta manera podemos
centrarlo en la pantalla dividiendo los píxeles restantes por 2 y usándolos
como desplazamiento
byte len = 0;
while(daysfull[dow][len]) {
len++;
};
byte offset = (31 - ((len-1)*4)) / 2; //nuestro desplazamiento para
centrar el texto
//imprimir el nombre
int i = 0;
while(daysfull[dow][i])
{
puttinychar((i*4) + offset , 1, daysfull[dow][i]);
i++;
}
delay(1000);
fade_down();
cls();
// imprimir los números de la fecha
char buffer[3];
itoa(date,buffer,10);
offset = 10; // desplazamiento para centrar el texto si son 3 caracteres,
por ejemplo, 3rd
// primero determinar el sufijo de 2 letras de la fecha, por ejemplo, st,
nd, rd, th, etc.
// char suffix[4][3]={"st", "nd", "rd", "th" }; se define en la parte
superior del código
byte s = 3;
if(date == 1 || date == 21 || date == 31) {
s = 0;
}
else if (date == 2 || date == 22) {
s = 1;
}
else if (date == 3 || date == 23) {
s = 2;
}
//imprimir el primer número de la fecha
puttinychar(-4+offset, 1, buffer[0]);
//si la fecha es menor a 10, solo tenemos 1 dígito, por lo que
establecemos las posiciones del sufijo más cerca de 1 carácter
byte suffixposx = 4;
//si la fecha es mayor a 9, entonces imprimimos el segundo número y
establecemos la posición del sufijo a 1 carácter más lejos
if (date > 9){
suffixposx = 8;
puttinychar(0+offset, 1, buffer[1]);
offset = 8; //desplazamiento para centrar el texto si son 4 caracteres
}
//imprimir los 2 caracteres del sufijo
puttinychar(suffixposx+offset, 1, suffix[s][0]);
puttinychar(suffixposx+4+offset, 1, suffix[s][1]);
delay(1000);
fade_down();
//imprimir el nombre del mes
//obtener la longitud del texto en píxeles, de esta manera podemos
centrarlo en la pantalla dividiendo los píxeles restantes por 2 y usándolos
como desplazamiento
len = 0;
while(monthnames[month][len]) {
len++;
};
offset = (31 - ((len-1)*4)) / 2; //nuestro desplazamiento para centrar el
texto
i = 0;
while(monthnames[month][i])
{
puttinychar((i*4) +offset, 1, monthnames[month][i]);
i++;
}
delay(1000);
fade_down();
char bufferx[4];
itoa(year, bufferx, 10);
offset = 10; // desplazamiento para centrar el texto si son 3 caracteres
puttinychar(-2+offset, 1, bufferx[0]);
puttinychar(2+offset, 1, bufferx[1]);
puttinychar(6+offset, 1, bufferx[2]);
puttinychar(10+offset, 1, bufferx[3]);
delay(1000);
fade_down();
//
}
// Mostrar menú para cambiar el modo de reloj
void switch_mode() {
// Recordar el modo en el que estamos. Usamos este valor si entramos en el
modo de configuración, para poder volver al modo anterior (6) desde el modo
de configuración.
old_mode = clock_mode;
char* modes[] = {
"Basic", "Small", "Slide", "Words", "Setup"
};
byte next_clock_mode;
byte firstrun = 1;
// bucle esperando el botón (tiempo de espera después de 35 iteraciones
para volver al modo X)
for (int count = 0; count < 35 ; count++) {
// si el usuario presiona el botón, cambia el modo de reloj
if ([Link]() || firstrun == 1) {
count = 0;
cls();
if (firstrun == 0) {
clock_mode++;
}
if (clock_mode > NUM_DISPLAY_MODES + 1 ) {
clock_mode = 0;
}
// imprimir flecha y nombre actual del modo de reloj en la línea uno y
imprimir el nombre del próximo modo de reloj en la línea dos
char str_top[9];
//strcpy (str_top, "-");
strcpy (str_top, modes[clock_mode]);
next_clock_mode = clock_mode + 1;
if (next_clock_mode > NUM_DISPLAY_MODES + 1 ) {
next_clock_mode = 0;
}
byte i = 0;
while (str_top[i]) {
putnormalchar(i * 6, 0, str_top[i]);
i++;
}
firstrun = 0;
}
delay(50);
}
}
// Ejecutar el bucle principal del reloj mientras run_mode devuelva true
byte run_mode() {
// Si el modo aleatorio está activado... verificar la hora en la que
cambiamos de modo.
if (random_mode) {
// Si el valor de la hora en change_mode_time es igual a la hora actual,
entonces devolver false, es decir, salir del modo.
if (change_mode_time == rtc[2]) {
// Establecer el siguiente modo de reloj aleatorio y la hora para
cambiarlo
set_next_random();
// Salir del modo actual.
return 0;
}
}
}
return 1;
}
// Establecer la próxima hora en la que el modo del reloj cambiará cuando el
modo aleatorio esté activado
void set_next_random() {
// Establecer la próxima hora en la que cambiará el modo del reloj: hora
actual más 1-4 horas
get_time();
change_mode_time = rtc[2] + random (1, 5);
// Si change_mode_time es mayor a 23, establecerlo entre la 1 y las 3 de
la mañana
if (change_mode_time > 23) {
change_mode_time = random (1, 4);
}
// Establecer el nuevo modo del reloj
clock_mode = random(0, NUM_DISPLAY_MODES + 1); // elegir un nuevo modo de
reloj aleatorio
}
// Mostrar menú para cambiar la configuración del reloj
void setup_menu() {
char* set_modes[] = {
"Rndom", "24 Hr","Set", "Brght", "Exit"};
if (ampm == 0) {
set_modes[1] = ("12 Hr");
}
byte setting_mode = 0;
byte next_setting_mode;
byte firstrun = 1;
// bucle esperando el botón (tiempo de espera después de 35 iteraciones
para volver al modo X)
for(int count=0; count < 35 ; count++) {
// si el usuario presiona el botón, cambia el modo del reloj
if([Link]() || firstrun == 1){
count = 0;
cls();
if (firstrun == 0) {
setting_mode++;
}
if (setting_mode > NUM_SETTINGS_MODES) {
setting_mode = 0;
}
// imprimir flechas y nombre actual del modo de reloj en la línea uno
y imprimir el nombre del siguiente modo de reloj en la línea dos
char str_top[9];
strcpy (str_top, set_modes[setting_mode]);
next_setting_mode = setting_mode + 1;
if (next_setting_mode > NUM_SETTINGS_MODES) {
next_setting_mode = 0;
}
byte i = 0;
while(str_top[i]) {
putnormalchar(i*6, 0, str_top[i]);
i++;
}
firstrun = 0;
}
delay(50);
}
//seleccionar el modo
switch(setting_mode){
case 0:
set_random();
break;
case 1:
set_ampm();
break;
case 2:
set_time();
break;
case 3:
set_intensity();
break;
case 4:
//salir del menu
break;
}
// cambiar el reloj del modo 6 (configuración) al que estaba antes
clock_mode=old_mode;
}
//alternar el modo aleatorio: elegir un modo de reloj diferente cada pocas
horas
void set_random(){
cls();
char text_a[9] = "Off";
char text_b[9] = "On";
byte i = 0;
// Si el modo aleatorio está activado, desactívalo
if (random_mode){
// Desactivar el modo aleatorio
random_mode = 0;
// Mostrar un mensaje en la pantalla
while(text_a[i]) {
putnormalchar((i*6), 0, text_a[i]);
i++;
}
} else {
// Activar el modo aleatorio
random_mode = 1;
// Establecer la próxima hora en la que cambiará el modo del reloj
set_next_random();
// Mostrar un mensaje en la pantalla
while(text_b[i]) {
putnormalchar((i*6), 0, text_b[i]);
i++;
}
}
delay(1500); // dejar el mensaje durante un segundo aproximadamente
}
// Establecer reloj de 12 o 24 horas
void set_ampm() {
// Modo de reloj AM/PM o de 24 horas - invertir el bit (convierte 0 en 1,
o 1 en 0 para el modo ampm)
ampm = (ampm ^ 1);
cls();
}
//cambiar la intensidad de la pantalla
void set_intensity() {
cls();
byte i = 0;
char text[7] = "Bright";
while(text[i]) {
puttinychar((i*4)+4, 0, text[i]);
i++;
}
// Esperar entrada del botón
while (![Link]()) {
levelbar (0,6,(intensity*2)+2,2); // Mostrar el nivel de intensidad
como una barra
while ([Link]()) {
if(intensity == 15) {
intensity = 0;
cls ();
}
else {
intensity++;
}
// Imprimir el nuevo valor
i = 0;
while(text[i]) {
puttinychar((i*4)+4, 0, text[i]);
i++;
}
// mostrar el nivel de intensidad como una barra
levelbar (0,6,(intensity*2)+2,2);
// cambiar la configuración de brillo en las pantallas
for (byte address = 0; address < 4; address++) {
[Link](address, intensity);
}
delay(150);
}
}
}
// mostrar una barra horizontal en la pantalla en la posición xposr por ypos
con altura y ancho de xbar, ybar
void levelbar (byte xpos, byte ypos, byte xbar, byte ybar) {
for (byte x = 0; x < xbar; x++) {
for (byte y = 0; y <= ybar; y++) {
plot(x+xpos, y+ypos, 1);
}
}
}
//rutina para establecer la hora y la fecha
void set_time() {
cls();
// Llenar las configuraciones con los valores actuales del reloj leídos
del reloj
get_time();
byte set_min = rtc[1];
byte set_hr = rtc[2];
byte set_date = rtc[4];
byte set_mnth = rtc[5];
int set_yr = rtc[6];
// Función de configuración - pasamos: qué mensaje 'set' mostrar en la
parte superior, valor actual, valor de reinicio y límite de desbordamiento.
set_date = set_value(2, set_date, 1, 31);
set_mnth = set_value(3, set_mnth, 1, 12);
set_yr = set_value(4, set_yr, 2013, 2099);
set_hr = set_value(1, set_hr, 0, 23);
set_min = set_value(0, set_min, 0, 59);
[Link](DateTime(set_yr, set_mnth, set_date, set_hr, set_min));
cls();
}
// Se utiliza para establecer los valores de minutos, horas, día, mes y año.
Pasar
// mensaje = qué mensaje 'set' imprimir,
// valor actual = valor actual de la propiedad que estamos configurando
// valor de reinicio = a qué valor reiniciar si se produce un
desbordamiento. Por ejemplo, los minutos pasan de 60 a 0, los meses de 12 a
1
// límite de desbordamiento = cuando el valor se desborda
int set_value(byte message, int current_value, int reset_value, int
rollover_limit){
cls();
char messages[6][17] = {
"Set Mins", "Set Hour", "Set Day", "Set Mnth", "Set Year"};
//Imprimir "establecer xyz" en la línea superior
byte i = 0;
while(messages[message][i])
{
puttinychar(i*4 , 1, messages[message][i]);
i++;
}
delay(2000);
cls();
// imprimir dígitos en la línea inferior
char buffer[5] = " ";
itoa(current_value,buffer,10);
puttinychar(0 , 1, buffer[0]);
puttinychar(4 , 1, buffer[1]);
puttinychar(8 , 1, buffer[2]);
puttinychar(12, 1, buffer[3]);
delay(300);
// esperar entrada del botón
while (![Link]()) {
while ([Link]()){
if(current_value < rollover_limit) {
current_value++;
}
else {
current_value = reset_value;
}
//imprimir el nuevo valor
itoa(current_value, buffer ,10);
puttinychar(0 , 1, buffer[0]);
puttinychar(4 , 1, buffer[1]);
puttinychar(8 , 1, buffer[2]);
puttinychar(12, 1, buffer[3]);
delay(150);
}
}
return current_value;
}
void get_time()
{
//obtener la hora
DateTime now = [Link]();
//guardar la hora en un arreglo
rtc[6] = [Link]();
rtc[5] = [Link]();
rtc[4] = [Link]();
rtc[3] = [Link](); // devuelve 0-6 donde 0 = Domingo
rtc[2] = [Link]();
rtc[1] = [Link]();
rtc[0] = [Link]();
}