Calificación:
  • 0 voto(s) - 0 Media
  • 1
  • 2
  • 3
  • 4
  • 5
Proyecto: Reloj por palabras en español
#1
resim


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€
Precio total: entre 90 y 100€
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.


resim

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:

resim

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


[Imagen: CLOCK.png]

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

  1.  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
  2. 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
  3.  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.
  4. Soldamos el resto de componentes
  5. A disfrutar!
Quizás faltaría una mejor explicación a la hora de hacer la caja del reloj, pero creo que lo importante y lo que podría dar problemas es la parte de la electrónica que es la que mejor explicada está.
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
Citar
#2
Gracias por compartirlo ICEM4N.

Dan ganas de hacerse uno, sin duda. Los había visto pero como dices, muy caros.
Citar
#3
Felicidades ICEM4N, es la primera vez que veo uno así, y me has dejado sin palabras.

Me gusta mucho.
Citar
#4
Gracias a los dos, el próximo proyecto intentaré explicarlo más detalladamente
Citar
#5
Hace tiempo que conozco el reloj, pero como bien dices, todos en inglés.

Buena currada!
Citar
#6
Me encanta tu proyecto. Vendes?
Citar
#7
Proyecto chulisimo !!

¿Has pensado usar leds indexables? Te ahorraria bastante electronica y se encenderian con codigo de arduino.
Citar
#8
(27-12-2016, 09:57 PM)May escribió: Me encanta tu proyecto. Vendes?


No lo había pensado la verdad, lleva bastantes horas de trabajo, pero lo peor, es que creo que no quedaría "profesional" del todo

(01-01-2017, 05:04 PM)daniez escribió: Proyecto chulisimo !!

¿Has pensado usar leds indexables? Te ahorraria bastante electronica y se encenderian con codigo de arduino.

Es una buena idea, aunque creo que sería algo más cara. En el fondo, lo que haces con los MAX72XX es montarte tu propio led indexable
Citar