Funcionalidades, caracterísitcas y usos
Os presento mi reloj por palabras en español que ya hice hace algún tiempo, 4-5 años. 100% cosecha propia tanto el código, como el circuito y demás, ya que la idea era aprender. El resultado es bastante profesional. Además de las palabras en intervalos de 5 minutos, usa 4 leds que se van encendiendo progresivamente para marcar los minutos exactos, cuando sean y 6minutos (o 11, 16, 21,...) se encenderá un led, para los 7, 12, 17, ... se encenderán dos leds y así. También dispone de dos botones con los que ajustar la hora (uno para la hora y otro para los minutos). Para que cambie, hay que mantener pulsado 1 segundo el botón, soltarlo y repetir, es lento, pero funciona.
Lista de materiales
- 1 Arduino nano 9-10€, el original -con el que lo hice- fueron unos 20€, desde entonces ha llovido mucho, hay muchos modelos y sitios donde comprarlos
- 1 Macetech ChronoDot + gastos de envío 21.61€ (cualquier RTC serviría, importante que lleve pila o batería para cuando se desconecte el reloj, el RTC guarde la hora)
- 1 MAX7219 chip 1.5-2€? No recuerdo el precio exacto, pero en ebay puedes comprar 10 por 14€ con envío gratis, el MAX7221 también serviría
- 104 LEDS y sus respectivas resistencias, en mi caso usé leds de 5mm 26000mcd: 11.95€ +5€ gastos de envío = 16.95
- 2 Botones, ni idea del precio, digamos que 2€ en total
- 1 Madera ligera/contrachapado/luterma/DM 800x400x5mm 5.25€
- 1 Cristal 400x400x3mm 6€
- 1 Vinilo cortado 400x400mm 25€
- 4 Enganches para el cristal 2€
- Púes, tornillos, pegamento, ... menos de 2-3€
PVP: El reloj original se vendía a 899€ (en inglés), desde entonces se han vuelto más comunes, por lo que el precio habrá bajado bastante.
Palabras
Mi idea era usar el menor número de leds posibles y que además fuese un cuadrado. Por ejemplo, con una matriz de 10x11 era muy fácil encajar todas las palabras necesarias, con una de 11x11 quedaban demasiados huecos sin usar, además de que eran más leds. Con una de 10x10, el problema era encajar todas las palabras en orden, le di bastante vueltas hasta que encontré una solución que me gustaba, que es la que se muestra en la imagen. Hay 11 leds sin usar, en los que puse letras al azar.
Para no tener que manejar 100 leds individualmente, se pueden hacer varios grupos de leds, ya que algunos se iluminarán siempre a la vez. Con esto ahorras en drivers de leds y en complicación del código. Yo hice 42 grupos. Del 1 al 37 son palabras o partes de palabras y del 38 al 42 son los 4 leds que indican los minutos. Las letras sobrantes no tienen un grupo asignado ni estan en el código, aunque si que tienen su led y su cableado.
Para encender las palabras, hay que encender los siguientes grupos de leds :
- ES: 1,2
- SON: 2,3
- LA: 4
- LAS: 4,5
- UNA: 6
- DOS: 7
- OCHO: 8,9
- ONCE: 9,10
- TRES: 11
- CUATRO: 12
- DIEZ: 13
- NUEVE: 14,15,16
- CINCO: 17
- SIETE: 18,19,20
- DOCE: 21
- SEIS: 22,23,24
- Y: 25
- MENOS: 26,27,28
- CUARTO: 29,30,31
- VEINTE: 32
- DIEZ: 33,34,35
- MEDIA: 36
- CINCO: 37
- CEINTICINCO: 15,19,23,27,30,34,37
MAX7219
Como he dicho antes, puedes usar el MAX7219 o el MAX7221, son bastante comunes en los proyectos arduino y sirven para que con pocos pines en el arduino, manejar bastantes leds. Inicialmente probé a hacer charlieplexing, pero la pérdida de luminosidad era notable. Para "encender" un grupo de leds, hay que seguir la siguiente tabla:
Por ejemplo, para encender el grupo #1 la variable a1 tiene que tener un valor de 128. Para encender el grupo #2 y el grupo #3 a la vez, la variable a1 tiene que tener un valor de 96 (= +64 +32)
Esquema de conexión
Primera vez usando Fritzing (y última), me salió una mierda, pero creo que más o menos se puede entender. El RTC pone que es un DS1307, pero yo usé el DS3132, las conexiones son las mismas. Para crear la matriz de 6*7 usé dos matrices de 5x5 (las únicas que tenía Fritzing) así que los nombres de las entradas están mal, pero se pilla la idea, mirando la documentación del MAX7219/7221 y el cuadro de arriba creo que está claro. Cada led va con su resistencia.
Código arduino
Código:
/*
Copyright 2014 Daniel Esteban - conejo@conejo.me
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <Wire.h>
// Arduino pins to connect to MAX7219
int dataIn = 2;
int load = 3;
int clock = 5;
// Buttons to change the time without need of a computer
int hourBtn = 12;
int minuteBtn = 11;
boolean hourState = 0;
boolean minState = 0;
// Default time
int hours = 0;
int minutes = 0;
int seconds = 0;
int maxInUse = 1;
int e = 0; // just a varialble
// define max7219 registers - DO NOT TOUCH unless you know what you're doing
byte max7219_reg_noop = 0x00;
byte max7219_reg_digit0 = 0x01;
byte max7219_reg_digit1 = 0x02;
byte max7219_reg_digit2 = 0x03;
byte max7219_reg_digit3 = 0x04;
byte max7219_reg_digit4 = 0x05;
byte max7219_reg_digit5 = 0x06;
byte max7219_reg_digit6 = 0x07;
byte max7219_reg_digit7 = 0x08;
byte max7219_reg_decodeMode = 0x09;
byte max7219_reg_intensity = 0x0a;
byte max7219_reg_scanLimit = 0x0b;
byte max7219_reg_shutdown = 0x0c;
byte max7219_reg_displayTest = 0x0f;
//*******************************
// AUX. FUNCTION TO HANDLE MAX7219 EASILY
// I left original comments there
//*******************************
void putByte(byte data) {
byte i = 8;
byte mask;
while(i > 0) {
mask = 0x01 << (i - 1); // get bitmask
digitalWrite( clock, LOW); // tick
if (data & mask){ // choose bit
digitalWrite(dataIn, HIGH);// write 1
}else{
digitalWrite(dataIn, LOW); // write 0
}
digitalWrite(clock, HIGH); // tock
--i; // move to lesser bit
}
}
void maxSingle( byte reg, byte col) {
//maxSingle is the "easy" function to use for a //single max7219
digitalWrite(load, LOW); // begin
putByte(reg); // specify register
putByte(col);//((data & 0x01) * 256) + data >> 1); // put data
digitalWrite(load, LOW); // and load da shit
digitalWrite(load,HIGH);
}
void maxAll (byte reg, byte col) { // initialize all MAX7219's in the system
int c = 0;
digitalWrite(load, LOW); // begin
for ( c =1; c<= maxInUse; c++) {
putByte(reg); // specify register
putByte(col);//((data & 0x01) * 256) + data >> 1); // put data
}
digitalWrite(load, LOW);
digitalWrite(load,HIGH);
}
void maxOne(byte maxNr, byte reg, byte col) {
//maxOne is for adressing different MAX7219's,
//whilele having a couple of them cascaded
int c = 0;
digitalWrite(load, LOW); // begin
for ( c = maxInUse; c > maxNr; c--) {
putByte(0); // means no operation
putByte(0); // means no operation
}
putByte(reg); // specify register
putByte(col);//((data & 0x01) * 256) + data >> 1); // put data
for ( c =maxNr-1; c >= 1; c--) {
putByte(0); // means no operation
putByte(0); // means no operation
}
digitalWrite(load, LOW); // and load da shit
digitalWrite(load,HIGH);
}
//*******************************
// END OF AUX MAX7219 FUNCTIONS
//*******************************
void setup () {
// MAX 7219
pinMode(dataIn, OUTPUT);
pinMode(clock, OUTPUT);
pinMode(load, OUTPUT);
// Buttons to change the time
pinMode(hourBtn, INPUT);
pinMode(minuteBtn, INPUT);
// connection to DS3231
Wire.begin();
// clear /EOSC bit
// Sometimes necessary to ensure that the clock
// keeps running on just battery power. Once set,
// it shouldn't need to be reset but it's a good
// idea to make sure.
Wire.beginTransmission(0x68); // address DS3231
Wire.write(0x0E); // select register
Wire.write(0b00011100); // write register bitmap, bit 7 is /EOSC
Wire.endTransmission();
//initiation of the max 7219
maxAll(max7219_reg_scanLimit, 0x07);
maxAll(max7219_reg_decodeMode, 0x00); // using an led matrix (not digits)
maxAll(max7219_reg_shutdown, 0x01); // not in shutdown mode
maxAll(max7219_reg_displayTest, 0x00); // no display test
for (e=1; e<=8; e++) { // empty registers, turn all LEDs off
maxAll(e,0);
}
maxAll(max7219_reg_intensity, 0x0f & 0x0f); // the first 0x0f is the value you can set
// range: 0x00 to 0x0f
}
// Aux. function to set the time
void set_time()
{
Wire.beginTransmission(104);
Wire.write(0);
Wire.write(decToBcd(seconds));
Wire.write(decToBcd(minutes));
Wire.write(decToBcd(hours));
Wire.endTransmission();
}
byte decToBcd(byte val)
{
return ( (val/10*16) + (val%10) );
}
// Main loop
void loop () {
// write request to read data starting at register 0
Wire.beginTransmission(0x68); // 0x68 is DS3231 device address
Wire.write(0); // start at register 0
Wire.endTransmission();
Wire.requestFrom(0x68, 3); // request three bytes (seconds, minutes, hours)
// Get time from RTC
while(Wire.available())
{
seconds = Wire.read(); // get seconds
minutes = Wire.read(); // get minutes
hours = Wire.read(); // get hours
seconds = (((seconds & 0b11110000)>>4)*10 + (seconds & 0b00001111)); // convert BCD to decimal
minutes = (((minutes & 0b11110000)>>4)*10 + (minutes & 0b00001111)); // convert BCD to decimal
hours = (((hours & 0b00110000)>>4)*10 + (hours & 0b00001111)); // convert BCD to decimal (assume 24 hour mode)
}
// Button is pressed, change hour (+1)
if(digitalRead(hourBtn)==1 && hourState==0) {
hours = hours+1;
if(hours>23) {
hours = 0;
}
set_time();
}
hourState = digitalRead(hourBtn);
// Button is pressed, change minutes (+1)
if(digitalRead(minuteBtn)==1 && minState==0) {
minutes = minutes+1;
if(minutes>59) {
minutes = 0;
}
set_time();
}
minState = digitalRead(minuteBtn);
// Reset variables
int a1 = 0;
int a2 = 0;
int a3 = 0;
int a4 = 0;
int a5 = 0;
int a6 = 0;
/*************
** MINUTES **
*************/
if(minutes>=0 && minutes<=4) {
// O'clock
} else if(minutes>=5 && minutes<=9) {
// (y)CINCO
a6 = a6+64;
} else if(minutes>=10 && minutes<=14) {
// (y)DIEZ
a5 = a5+14;
} else if(minutes>=15 && minutes<=19) {
// (y)CUARTO
a5 = a5+224;
} else if(minutes>=20 && minutes<=24) {
// (y)VEINTE
a5 = a5+16;
} else if(minutes>=25 && minutes<=29) {
// (y)VEINTICINCO
a3 = a3+136;
a4 = a4+68;
a5 = a5+68;
a6 = a6+64;
// VEINTICINCO shares some chars with a few hours, adjust the variables
if(hours==9 || hours==21) { a3 = a3-128; }
if(hours==7 || hours==19) { a3 = a3-8; }
if(hours==6 || hours==18) { a4 = a4-64; }
} else if(minutes>=30 && minutes<=34) {
// (y)MEDIA
a6 = a6+128;
} else if(minutes>=35 && minutes<=39) {
// (menos)VEINTICINCO
a3 = a3+136;
a4 = a4+64;
a5 = a5+68;
a6 = a6+64;
// VEINTICINCO shares some chars with a few hours, adjust the variables
if(hours==8 || hours==20) { a3 = a3-128; }
if(hours==6 || hours==18) { a3 = a3-8; }
if(hours==5 || hours==17) { a4 = a4-64; }
} else if(minutes>=40 && minutes<=44) {
// (menos)VEINTE
a5 = a5+16;
} else if(minutes>=45 && minutes<=49) {
// (menos)CUARTO
a5 = a5+224;
} else if(minutes>=50 && minutes<=54) {
// (menos)DIEZ
a5 = a5+14;
} else if(minutes>=55 && minutes<=59) {
// (menos)CINCO
a6 = a6+64;
}
if(minutes>=5 && minutes<=34) {
// Y
a4 = a4+16;
} else if(minutes>=35) {
// MENOS
a4 = a4+14;
}
// Move the hour if it's past 35 minutes as 7:35 is "OCHO(8) MENOS ..."
if(minutes>=35) {
hours++;
}
// SHOW none, 1,2,3 or 4 corner leds to exactly tell the time 7:17 -> SIETE Y CUARTO + 2 leds on
if((minutes%5)==1) {
a6 = a6+8;
} else if((minutes%5)==2) {
a6 = a6+8+4;
} else if((minutes%5)==3) {
a6 = a6+16+8+4;
} else if((minutes%5)==4) {
a6 = a6+32+16+8+4;
}
/*************************
** HOURS (follow table) **
*************************/
if(hours==0 || hours==12 || hours==24) {
a1 = a1+96;
a1 = a1+24;
a3 = a3+2;
} else if(hours==1 || hours==13) {
a1 = a1+192;
a1 = a1+16;
a1 = a1+4;
} else if(hours==2 || hours==14) {
a1 = a1+96;
a1 = a1+24;
a1 = a1+2;
} else if(hours==3 || hours==15) {
a1 = a1+96;
a1 = a1+24;
a2 = a2+16;
} else if(hours==4 || hours==16) {
a1 = a1+96;
a1 = a1+24;
a2 = a2+8;
} else if(hours==5 || hours==17) {
a1 = a1+96;
a1 = a1+24;
a3 = a3+32;
} else if(hours==6 || hours==18) {
a1 = a1+96;
a1 = a1+24;
a4 = a4+224;
} else if(hours==7 || hours==19) {
a1 = a1+96;
a1 = a1+24;
a3 = a3+28;
} else if(hours==8 || hours==20) {
a1 = a1+96;
a1 = a1+24;
a2 = a2+192;
} else if(hours==9 || hours==21) {
a1 = a1+96;
a1 = a1+24;
a2 = a2+2;
a3 = a3+192;
} else if(hours==10 || hours==22) {
a1 = a1+96;
a1 = a1+24;
a2 = a2+4;
} else if(hours==11 || hours==23) {
a1 = a1+96;
a1 = a1+24;
a2 = a2+96;
}
// Tell MAX7219 to print it pretty
maxSingle(1, a1);
maxSingle(2, a2);
maxSingle(3, a3);
maxSingle(4, a4);
maxSingle(5, a5);
maxSingle(6, a6);
// loop every second, as it doesn't need to be very precise
delay(1000);
// This 1second delay makes it a bit harder to update the time with the buttons, as it doesn't feel very friendly
// but you will not be changing the time very frequently, so not a big problem (for me)
}
Procedimiento
No creo que haya que explicar mucho, pero hayá va, aunque no haya muchas fotos del proceso, aqui se pueden encontrar algunas : https://www.flickr.com/photos/_conejo/se...4899548906
- Se corta la luterma en algo menos de 40x40 y se le hace un marco, la luterma estará en mitad de ese marco ya que por un lado tendrá los leds, y por el otro los cables y la electrónica (más o menos http://i.imgur.com/MSlRcFD.jpg ). Hay que poner los 4 enganches en los laterales para agarrar el cristal. También hay que hacer tres agujeros en un lateral, dos para los botones y un tercero para la alimentación usb del arduino
- Con mucho cuidado, y sirviéndose de una regla o algo recto y un poco flexible, pegamos el vinilo en el cristal sin dejar burbujas. El vinilo fue cortado en una tienda, quizás puede ser que ellos mismos te lo pegen en el cristal
- Se dibuja una cuadrícula de 10x10 en mitad de la luterma, con una púa se hacen 2 agujeros por led para meter las patillas, meter led, pegar y soltar, meter led, pegar y soltar, meter led, pegar y soltar, y así hasta 104 veces. Es útil que en la cuadrícula se escriban las palabras, a la hora de hacer los grupos y soldar juntos los leds nos vendrá bien.
- Soldamos el resto de componentes
- A disfrutar!
El diseño del vinilo no he sido capaz de encontrarlo, pero es sencillo hacerlo de nuevo.
Enlaces
El proyecto también lo podéis encontrar en github : https://github.com/conejoninja/WordClock
Album de fotos : https://www.flickr.com/photos/_conejo/se...4899548906
Licencia
El proyecto y su contenido se distribuye bajo la licencia Apache v2.0