Vamos a programar #27 - Inútil Apps #2 - Clock View ++

Hola de nuevo a todos, el día de hoy vamos a continuar con el proyecto ClockView. En entregas anteriores, implementamos las funcionalidades básicas, pero ahora vamos a extender un poco más la funcionalidad.

Si has seguido los últimos post, sabrás que todo se retraso por falta de hardware, pero hace un par de días, llegó todo lo necesario. Al inicio no sabia de que forma iba cambiar los ajustes del reloj, la primer opción era usar botones, pero para mi caso concreto, no sería una opción, ya que acabaría desmontando todo el proyecto para hacer otro, además de que todo lo que se ha armado antes es un prototipo, en un futuro cambiare el arduino uno por un arduino nano, ademas de que agregaría 5 matrices más para mostrar todos los datos, en fin, crear un reloj que se pueda colgar en la pared, cosa que no es el caso ahora porque lo tengo instalado en protoboard.

Para poder usarlo de una forma más sencilla, llegué a la conclusión de que la manera más fácil de cambiar los ajustes, es mediante bluetooth.
Para continuar con el proyecto vamos a usar los siguiente materiales en adición a los que ya teníamos:

  1. 1 Módulo bluetooth H06.
  2. 1 Módulo de tarjeta lector de tarjeta SD.


Módulo bluetooth HC06, se puede conseguir por un par de dolares.
Para poder usar todos los componentes, vamos a cambiar las conexiones que ya teníamos antes, esto para evitar complicarnos, es cierto que podemos usar más de un Modulo en el mismo pin mediante el uso de SPI (Serial Peripheral Interface), pero cómo este proyecto está diseñado para ser estático, podemos usar todos los pines disponibles.

Ademas haremos uso de la libreria SD.h (ya incluida en la instalacion del arduino IDE), para controlar la tarjeta SD, en está ya se indican cuales son los pines que se usan para el módulo SD. Entonces para poder usar el código, antes vamos a realizar las siguiente conexiones.
  • Del módulo SD:
    • CLK al pin 13.
    • MISO al pin 12.
    • MOSI al pin 11.
    • CS al pin 10.
  • Para las matrices:
    • DIN al pin 9.
    • CS al pin 8.
    • CLK al pin 7.
  • Para el módulo de reloj (DS1302):
    • CE(RST) al pin 6.
    • IO (DAT) al pin 5.
    • SCLK (CLK) al pin 4.
  • Para el módulo Bluetooth HC06:
    • TXD al pin RX (0) del arduino.
    • RXD al pin TX (1) del arduino.
Una vez que las conexiones se han realizado, vamos a ver el código que hace funcionar las cosas.
Algunas partes del código fueron tomadas de los ejemplos que se incluyen con las librerías que se usan.

//Clockview 1.0
#include <DS1302.h>
#include "LedControl.h"
#include <SD.h>
/*
 Para la conexión del modulo SD se siguen los siguientes:
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 10 - Este es el que se puede cambiar
*/
//Pin SD
int CS_PIN = 10;
//Constantes para los pines usados en la matriz
const int MaxDIn = 9;
const int MaxCS = 8;
const int MaxCLK = 7;
int MaxNDevices = 3;
bool IsConnected = false;
//Constantes para los pines usados en el reloj
const int kCePin = 6;  // RST
const int kIoPin = 5;  // Dat
const int kSclkPin = 4;  // Serial Clock
//Inicializacion de la matriz
//DIN,CLK,CS
LedControl lc = LedControl(MaxDIn, MaxCLK, MaxCS, MaxNDevices);
//Inicializacion del reloj
DS1302 rtc(kCePin, kIoPin, kSclkPin);
//Algunas variables
File Archivo;
char Texto[24];
int MatrixB = 10;
bool H24 = true;
bool HalfSecond = true;
bool IsScreenEnable = true;
bool ShowSeconds = true;
bool SDCardReady = false;
//Matriz con los "Numeros"
const unsigned char Numbers[] = {
 B111110, B100010, B111110, //0
 B100100, B111110, B100000, //1
 B111010, B101010, B101110, //2
 B100010, B101010, B111110, //3
 B001110, B001000, B111110, //4
 B101110, B101010, B111010, //5
 B111110, B101010, B111010, //6
 B000010, B000010, B111110, //7
 B111110, B101010, B111110, //8
 B001110, B001010, B111110, //9
 B111110, B001010, B001110, //A - 10
 B111110, B001100, B111110, //M - 11
 B111110, B001010, B001110, //P - 12
 B111110, B001000, B111110, //H - 13
 B111110 ,B011010, B101110, //R - 14
};
//Definimos simbolos
const unsigned char Symbols[] = {
 B0100010, B0010100, B0001000, B1111111, B0101010, B0010100, //BT 0
 B0011110, B0100011, B0101101, B0100101, B0100101, B0011110, //Clock Adjust 1
 
};
//Imprimir simbolos
void PrintSymbol(byte Index,byte SymDevice){
 for (int Y = 1; Y < 7; Y++){
  lc.setRow(SymDevice, Y, Symbols[Index * 5 + Y - 1]);
 }
}
//Funcion para escribir los numero en la matriz.
void PrintNumber(byte NumberOne, byte NumberTwo, byte Device){
 for (int X = 1; X < 8; X++){
  if (X < 4){
   lc.setRow(Device, X, Numbers[NumberOne * 3 + X - 1]);
  }
  if (X == 4){
   lc.setRow(Device, 4, 0);
  }
  if (X > 4){
   lc.setRow(Device, X, Numbers[NumberTwo * 3 + X - 5]);
  }
 }
}
//Imprimir el tiempo en las matrices y en el monitor serie
void printTime(){
 Time t = rtc.time();
 const String day = dayAsString(t.day);
 char buf[50];
 snprintf(buf, sizeof(buf), "%s %04d-%02d-%02d %02d:%02d:%02d",
  day.c_str(), t.yr, t.mon, t.date, t.hr, t.min, t.sec);
 PrintNumber(AdjustTime(t.hr, H24) / 10,AdjustTime(t.hr, H24) % 10, 0);
 PrintNumber(t.min / 10, t.min % 10, 1);
 if (ShowSeconds){
  PrintNumber(t.sec / 10, t.sec % 10, 2); 
 }else{
  if(H24)
   PrintNumber(13, 14, 2);
  if(!H24 && (t.hr-12) < 13)
   PrintNumber(10, 11, 2);
  if(!H24 && (t.hr-12) > 12)
   PrintNumber(10, 12, 2);
 }
 //PrintSymbol(0, 2);
 if (HalfSecond == true){
  digitalWrite(3, HIGH);
  HalfSecond = false;
 }else{
  digitalWrite(3, LOW);
  HalfSecond = true;
 }
 Serial.println(buf);
}
//Ajustar la hora
int AdjustTime(int Hour, bool In24Hformat){
 if(In24Hformat == true){
  return Hour;
 }
 if(In24Hformat == false && Hour > 12){
  Hour = Hour - 12;
  return Hour;
 }else{
  return Hour;
 }
}
void SaveSettings(){
 Archivo = SD.open("Settings.clv", O_WRITE | O_CREAT);
 char SaveBuf[8];
 snprintf(SaveBuf, sizeof(SaveBuf), "%s%02d",">SETB", MatrixB);
 Serial.println(SaveBuf);
 if (Archivo){
  Archivo.seek(0);
  Archivo.println(SaveBuf);
  if(H24)
   Archivo.println(">SETF");
  else
   Archivo.println("XSETF");
  if (ShowSeconds)
   Archivo.println(">DISS");
  else
   Archivo.println("XDISS");
  Archivo.flush();
  Archivo.close();
  Serial.println("Guardado");
 } else {
  Serial.println("error writing test.txt");
 }
}
//Cargar los ajustes
void LoadSettings(){
 Archivo = SD.open("Settings.clv", FILE_READ);
 int B = 0;
 byte CurRead;
 char SettBuf[22];
 if (Archivo) {
  while (Archivo.available() > 0){
   CurRead = Archivo.read();
   if (CurRead != 10 && CurRead != 13){
    SettBuf[B] = CurRead;
    B++;
    Serial.print(B);
   }else{
    CheckPetition(SettBuf, false);
    Serial.print(B);
    B = 0;
    for (int BF = 0; BF < 22; BF++)
     SettBuf[BF]=0;
   }
  }
  Archivo.close();
 }else{
  Serial.println("error opening file");
 } 
}
//Comprobar que si hay algun comando
void CheckPetition(char DATA[], bool Save){
 int i = 0;
 int j = 0;
 String Texto(DATA);
 //>SETH2016122119001004
 if (Texto.startsWith(">DISS")){
  ShowSeconds = !ShowSeconds;
  if (Save)
   SaveSettings();
  Serial.print("No/Se muestran los segundos");
  for (int CurrentDevice = 0; CurrentDevice < MaxNDevices; CurrentDevice++){
   lc.shutdown(CurrentDevice, !IsScreenEnable);
  }
  for (j = 0; j < 11; j++) {
   DATA[j] = 0;
  }
  i = 0;
 }
 if (Texto.startsWith(">SCRA")){
  IsScreenEnable = !IsScreenEnable;
  Serial.print("Las matrices se apagaron/encendieron");
  for (int CurrentDevice = 0; CurrentDevice < MaxNDevices; CurrentDevice++){
   lc.shutdown(CurrentDevice, !IsScreenEnable);
  }
  for (j = 0; j < 11; j++) {
   DATA[j] = 0;
  }
  i = 0;
 } 
 if (Texto.startsWith(">RESET")){
  SD.remove("Settings.clv");
  for (int CurrentDevice = 0; CurrentDevice < MaxNDevices; CurrentDevice++){
   lc.shutdown(CurrentDevice, !IsScreenEnable);
  }
  for (j = 0; j < 11; j++) {
   DATA[j] = 0;
  }
  i = 0;
 } 
 if (Texto.startsWith(">SETF")){
  H24 = !H24;
  if (Save)
   SaveSettings();
  Serial.print("EL reloj cambio de formato 12H-24H");
  for (j = 0; j < 11; j++) {
   DATA[j] = 0;
  }
  i = 0;
 }
 if (Texto.startsWith(">SETH")){
  AdjustTime(Texto.substring(9, 5).toInt(),Texto.substring(11,9).toInt(),Texto.substring(13, 11).toInt(),
  Texto.substring(15, 13).toInt(), Texto.substring(17, 15).toInt(), Texto.substring(19, 17).toInt(),
  Texto.substring(21, 19).toInt());
  for (j = 0; j < 11; j++) {
   DATA[j] = 0;
  }
  i = 0;
 }
 if (Texto.startsWith(">SETB")){
  MatrixB = Texto.substring(5).toInt();
  if (Save)
   SaveSettings();
  Serial.println("El brillo se cambio");
  for (int CurrentDevice = 0; CurrentDevice < MaxNDevices; CurrentDevice++){
   lc.setIntensity(CurrentDevice, MatrixB);
  }
  for (j = 0; j < 11; j++) {
   DATA[j] = 0;
  }
  i = 0;
 }
 else {
  for (j = 0; j < 11; j++) {
   DATA[j] = 0;
  }
  i = 0;
 }
}
//Convertir los dias
String dayAsString(const Time::Day day){
 switch (day){
  case Time::kSunday: return "DOM";
  case Time::kMonday: return "LUN";
  case Time::kTuesday: return "MAR";
  case Time::kWednesday: return "MIE";
  case Time::kThursday: return "JUE";
  case Time::kFriday: return "VIE";
  case Time::kSaturday: return "SAB";
 }
 return "(unknown day)";
}
//Ajustar la hora
void AdjustTime(int Year,int Month, int Day, int Hour, int Minute, int Second, int DayOfWeek){
 Time::Day CurrentDay;
 switch (DayOfWeek){
  case 1:
   CurrentDay = Time::kSunday;
   break;
  case 2:
   CurrentDay = Time::kMonday;
   break;
  case 3:
   CurrentDay = Time::kTuesday;
   break;
  case 4:
   CurrentDay = Time::kWednesday;
   break;
  case 5:
   CurrentDay = Time::kThursday;
   break;
  case 6:
   CurrentDay = Time::kFriday;
   break;
  case 7:
   CurrentDay = Time::kSaturday;
   break;
 }
 //Esta parte se usa para actualizar la hora.
 rtc.writeProtect(false);
 rtc.halt(false);
 Time t(Year, Month, Day, Hour, Minute, Second, CurrentDay);
 rtc.time(t); 
}
//Inicializar la tarjeta SD para usarla
void InitializeSD(){
 pinMode(CS_PIN, OUTPUT);
 if (SD.begin()){
  SDCardReady = true;
  Serial.println("La tarjeta SD esta lista");
 }else{
  SDCardReady = false;
  Serial.println("La tarjeta SD no esta lista");
  return;
 }
}
//Setup
void setup(){
 Serial.begin(9600);
 for (int CurrentDevice = 0; CurrentDevice < MaxNDevices; CurrentDevice++){
  lc.shutdown(CurrentDevice, false);
  lc.setIntensity(CurrentDevice, MatrixB);
  lc.clearDisplay(CurrentDevice);
 }
 InitializeSD();
 pinMode(3, OUTPUT);
 if(SDCardReady){
  LoadSettings();
 }else{
  Serial.println("La tarjeta no está lista");
 }
}
//Loop
void loop(){
 int i = 0;
 printTime();
 delay(500);
 if (Serial.available()) {
  while (Serial.available() > 0) {
   Texto[i] = Serial.read();
   i++;
  }
  Texto[i] = '\0';
 }
 CheckPetition(Texto, true);
}
Al código que teníamos la primera vez lo hemos modificado, las partes que se agregaron fueron las que se encargan de modificar y guardar los ajustes. la funcion "LoadSettings" se encarga de buscar un archivo llamado "Settings.clv" en la tarjeta SD y lo lee, para procesar lo que se lee, va a usar la misma función que se usaba cuando se enviaba un comando por el monitor serie, es decir para que podamos modificar los ajustes desde la tarjeta de memoria, debemos de guardar los comandos; por ejemplo: >SETB15, si lo guardamos en la tarjeta de memoria, al momento de que se carga, la función buscara por la frase ">SETB", cualquier linea que empiece por esa frase, tratará de ser procesada y hay que recordar que solo si después de ">SETB" hay un numero válido entre 1 y 15, solo entonces el valor se pasará para que sea la intensidad del brillo.

Hay que recordar que ya hemos definido algunos comandos entonces si queremos omitir o no cambiar su valor, basta con cambiar una letra del inicio. basado en lo estricto que es cada parámetro, entonces para cambiar al formato de 24 horas, hay que recordar que usamos una variable del tipo boolean, al inicio del código se declara y tiene un valor de verdadero (true), para no hacer ningun tipo de cambio, debemos de hacer que el comando que se guarde en la tarjeta SD no tenga la forma correcta. La manera correcta de hacerlo, seria eliminar la linea que lo contiene, pero en su lugar, decidí simplemente escribir "XSETF", al hacer esto, el reloj permanecerá en formato de 24 horas, esto debido a que el código indica eso, para hacer que al momento de encender el reloj este este en formato de 12 horas, debemos de guardar ">SETF" debido a que cada vez que introducimos ese comando, al  momento de ser procesado, simplemente se invierte su valor, por lo que si queremos que este sea falso (false) basta que lo guardemos de esa manera.

Para la funcion "SaveSettings" simplemente sobre-escribimos el texto, para el brillo, siempre tendrá la forma: >SETBnn, donde nn es un número entre 1 y 15, luego >SETF, solo si se quiere que cada vez que el reloj encienda, este tenga el formato de 12 horas, y finalmente >DISS que al igual que >SETF solo se debe de escribir así, cuando NO se quiera mostrar los segundos, si se quiere el valor por default, basta con poner "XDISS". El código lo puedes bajar de mi dropbox.

Bien, por ahora es todo, en el siguiente Post veremos cómo controlar ClockView desde nuestro teléfono móvil. Además me enorgullece decir que el primer reto de XWork ha sido completado, en los proximos dias mostrare al ganado y además, lanzaré el segundo reto.

Los leo luego.

2 comentarios

  1. Todo bien mas ahora siempre que se pone el reloj en formato de 12 horas muestra pm ��

    ResponderBorrar