Vamos a programar #29 - Inútil Apps #2 - Creando un socket bluetooth (arduino y android)

Hola a todos, el día de hoy vamos a continuar con Clock View. Ahora con todo el hardware listo y con la parte de software de arduino también listo, solo queda crear una forma de interactuar con el hardware de Clock View. Al carecer de botones solo nos queda la opción de usar el modulo Bluetooth, para comunicarnos por medio de este, podemos usar aplicaciones que ya estan disponibles en la Playstore (S2 Terminal for bluetooth por ejemplo), pero esa no es la idea del blog, en la mediada de lo posible trataremos de usar nuestro propio software para hacer las cosas.

El dia de hoy les quiero presentar Clock View para android, una aplicación que tiene cómo unica función; conectar con el modulo bluetooth HC-05 o HC-06 y cambiar los ajustes de Clock View.

La aplicacion dispone de 6 botones y una barra de "seek". Para usarla, primero debes de asegurarte de que todo el hardware está listo, despues debes de iniciar la conexion con el boton "Conectar con clock view", cuando aparace un mensaje diciendo que la conexion se llevo a cabo, puedes modificar los ajustes.


Hoy solo veremos el código, en el siguiente post, explicaré un poco acerca de cómo se realiza el intercambio de datos entre arduino y android.

El código.

El siguiente es el código en java que se encarga de hacer funcionar las cosas, los estilos y demás "layouts", irán en la descarga completa al final del post.

package com.mdev.clockview;

import android.app.Activity;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Handler;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.Calendar;

import static com.mdev.clockview.R.layout.paired_devices_list;

public class MainActivity extends Activity {

	BluetoothAdapter myBluetoothAdapter = null;
	Button BtnConnect, BtnOnOff, BtnSecondsShow, BtnSyncHour, BtnChangeFormat,BtnDisconnect;
	SeekBar SkBright;
	TextView TxtArduinoRec, TxtBrigthValue;

	String address = null;
	private ProgressDialog progress;
	BluetoothSocket btSocket = null;
	private boolean isBtConnected = false;
	private static final UUID myUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
	final int RECEIVE_MESSAGE = 1;
	private StringBuilder sb = new StringBuilder();
	private ConnectedThread mConnectedThread;
	Handler h;

	private Set<BluetoothDevice> pairedDevices;
	ListView myListView;
	ArrayAdapter BTArrayAdapter;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		//setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
		setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
		BtnConnect = (Button)findViewById(R.id.BtnConnect);
		myListView = (ListView)findViewById(R.id.BTList);
		TxtArduinoRec = (TextView)findViewById(R.id.TxtArduinoReturn);
		BtnOnOff = (Button)findViewById(R.id.BtnOn);
		BtnSecondsShow = (Button)findViewById(R.id.BtnShowS);
		BtnSyncHour = (Button)findViewById(R.id.BtnSync);
		BtnChangeFormat = (Button)findViewById(R.id.BtnFormat);
		BtnDisconnect = (Button)findViewById(R.id.BtnDisconnect);
		SkBright = (SeekBar)findViewById(R.id.SKBrillo);
		TxtBrigthValue = (TextView)findViewById(R.id.txtSeekValue);

		myBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

		if (myBluetoothAdapter == null) {
			msg("El bluetooth no es soportado por este dispositivo");
		}
		else {
			msg("Esperando la conexion");
		}
		SkBright.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener(){

			@Override
			public void onProgressChanged(SeekBar seekBar, int progress,
										  boolean fromUser) {
				TxtBrigthValue.setText(String.valueOf(progress));
				if (isBtConnected)
				{
					String result = String.format(">SETB%1$02d", progress);
					mConnectedThread.write(result);
				}
			}

			@Override
			public void onStartTrackingTouch(SeekBar seekBar) {
			}

			@Override
			public void onStopTrackingTouch(SeekBar seekBar) {

			}
		});
		BtnConnect.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View view) {
				if (!myBluetoothAdapter.isEnabled()) {
					Intent turnOnIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
					startActivityForResult(turnOnIntent, 1);
				}else{
					showBTDialog();
				}
			}
		});
		BtnOnOff.setOnClickListener(new View.OnClickListener()
		{
			@Override
			public void onClick(View view){
				if (isBtConnected)
				{
					mConnectedThread.write(">SCRA");
				}
			}
		});

		BtnSecondsShow.setOnClickListener(new View.OnClickListener()
		{
			@Override
			public void onClick(View view){
				if (isBtConnected)
				{
					mConnectedThread.write(">DISS");
				}
			}
		});

		BtnSyncHour.setOnClickListener(new View.OnClickListener()
		{
			@Override
			public void onClick(View view){
				if (isBtConnected)
				{
					mConnectedThread.write(BuildHourToArduino());
				}
			}
		});

		BtnChangeFormat.setOnClickListener(new View.OnClickListener()
		{
			@Override
			public void onClick(View view){
				if (isBtConnected)
				{
					mConnectedThread.write(">SETF");
				}
			}
		});
		
		BtnDisconnect.setOnClickListener(new View.OnClickListener()
		{
			@Override
			public void onClick(View view){
				if (isBtConnected)
				{
					Disconnect();
				}
			}
		});
		//encargado de procesar los mensajes.
		h = new Handler() {
			public void handleMessage(android.os.Message msg) {
				switch (msg.what)
				{
					case RECEIVE_MESSAGE:
						byte[] readBuf = (byte[]) msg.obj;
						String strIncom = new String(readBuf, 0, msg.arg1);
						sb.append(strIncom);
						int endOfLineIndex = sb.indexOf("\r\n");
						if (endOfLineIndex > 0)
						{
							String sbprint = sb.substring(0, endOfLineIndex);
							sb.delete(0, sb.length());
							TxtArduinoRec.setText("Data from Arduino: " + sbprint);
						}
						break;
				}
			};
		};

	}
	private void msg(String s)
	{
		Toast.makeText(getApplicationContext(),s,Toast.LENGTH_LONG).show();
	}
	private String BuildHourToArduino(){
		//>SETH2016122119001004
		//>SETHAAAAMMDDHHMMSSWW
		Calendar c = Calendar.getInstance();
		int DiaSemana = c.get(Calendar.DAY_OF_WEEK);
		int Anio = c.get(Calendar.YEAR);
		int Mes = c.get(Calendar.MONTH);
		int Dia = c.get(Calendar.DAY_OF_MONTH);
		int Horas = c.get(Calendar.HOUR_OF_DAY);
		int Minutos = c.get(Calendar.MINUTE);
		int Segundos = c.get(Calendar.SECOND);

		String DateForArduino = String.format(">SETH%1$04d%2$02d%3$02d%4$02d%5$02d%6$02d%7$02d",
				Anio, Mes + 1, Dia, Horas, Minutos,Segundos, DiaSemana);
		msg(DateForArduino);

		return DateForArduino;
	}

	private void Disconnect()
	{
		if (btSocket!=null)
		{
			try
			{
				btSocket.close();
				isBtConnected = false;
			}
			catch (IOException e)
			{ msg("Error");}
		}
	}

	private AdapterView.OnItemClickListener myListClickListener = new AdapterView.OnItemClickListener()
	{
		public void onItemClick (AdapterView<?> av, View v, int arg2, long arg3) {
			if (myBluetoothAdapter.isEnabled()) {
				String info = ((TextView) v).getText().toString();
				address = info.substring(info.length() - 17);
				msg(address);

			} else {
				msg("El Bluetooth debe de estar encendido.");
			}
		}
	};


	public void showBTDialog() {
		final AlertDialog.Builder popDialog = new AlertDialog.Builder(this);
		final LayoutInflater inflater = (LayoutInflater) this.getSystemService(LAYOUT_INFLATER_SERVICE);
		final View Viewlayout = inflater.inflate(paired_devices_list, (ViewGroup) findViewById(R.id.bt_list));

		popDialog.setTitle("Dispositivos Bluetooth:");
		popDialog.setView(Viewlayout);

		myListView = (ListView) Viewlayout.findViewById(R.id.BTList);
		BTArrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
		myListView.setAdapter(BTArrayAdapter);
		pairedDevices = myBluetoothAdapter.getBondedDevices();

		for(BluetoothDevice device : pairedDevices)
			BTArrayAdapter.add(device.getName()+ "\n" + device.getAddress());

		myListView.setOnItemClickListener(myListClickListener);
		// Button OK
		popDialog.setPositiveButton("Conectar",
				new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int which) {
						if (address != null)
						{
							new ConnectBT().execute();
						}
						dialog.dismiss();
					}
				});
		popDialog.create();
		popDialog.show();

	}
	private class ConnectedThread extends Thread {
		private final InputStream mmInStream;
		private final OutputStream mmOutStream;

		public ConnectedThread(BluetoothSocket socket) {
			InputStream tmpIn = null;
			OutputStream tmpOut = null;
			try {
				tmpIn = socket.getInputStream();
				tmpOut = socket.getOutputStream();
			} catch (IOException e) { }

			mmInStream = tmpIn;
			mmOutStream = tmpOut;
		}

		public void run() {
			byte[] buffer = new byte[256];
			int bytes;
			while (true) {
				try {
					bytes = mmInStream.read(buffer);
					h.obtainMessage(RECEIVE_MESSAGE, bytes, -1, buffer).sendToTarget();
				} catch (IOException e) {
					break;
				}
			}
		}

		public void write(String message) {
			byte[] msgBuffer = message.getBytes();
			try {
				mmOutStream.write(msgBuffer);
			} catch (IOException e) {
				msg("Error al enviar: " + e.getMessage());

			}
		}
	}



	private class ConnectBT extends AsyncTask<Void, Void, Void>
	{
		private boolean ConnectSuccess = true;

		@Override
		protected void onPreExecute()
		{
			progress = ProgressDialog.show(MainActivity.this, "Conectando...", "Espera...!!!");
		}

		@Override
		protected Void doInBackground(Void... devices)
		{
			try
			{
				if (btSocket == null || !isBtConnected)
				{
					myBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
					BluetoothAdapter.getDefaultAdapter().cancelDiscovery();
					BluetoothDevice dispositivo = myBluetoothAdapter.getRemoteDevice(address);
					btSocket = createBluetoothSocket(dispositivo);
					btSocket.connect();
				}
			}
			catch (IOException e)
			{
				ConnectSuccess = false;
			}
			return null;
		}

		private BluetoothSocket createBluetoothSocket(BluetoothDevice device) throws IOException {
			if(Build.VERSION.SDK_INT >= 10){
				try {
					final Method  m = device.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", new Class[] { UUID.class });
					return (BluetoothSocket) m.invoke(device, myUUID);
				} catch (Exception e) {
					msg("No se pudo crear la conexion");
				}
			}
			return  device.createRfcommSocketToServiceRecord(myUUID);
		}

		@Override
		protected void onPostExecute(Void result)
		{
			super.onPostExecute(result);

			if (!ConnectSuccess)
			{
				msg("La conexion falló, intentelo de nuevo");
				isBtConnected = false;
			}
			else
			{
				msg("Conectado.");
				isBtConnected = true;
				mConnectedThread = new ConnectedThread(btSocket);
				mConnectedThread.start();
			}
			progress.dismiss();
		}
	}
}

Cómo todo el código requiere una explicacion detallada (o si no quieres meterte en programacion), por ahora solo dejo el APK ya compilado,
Los leo luego.

Vamos a programar #28 - Inútil Apps #2 - Configurando el bluetooth

El dia de hoy continuaremos con la parte del software para Clock View.
En la entrega anterior prometí agregar una forma para modificar todos los ajustes que pueda llevar Clock View, al carecer de botones, solo hay un par de formas para cambiarlos; una de ellas es mediante un cable USB e introducir el comando adecuado (solo para recordar, es: >SETHXXXXXXXXX), lo cual resultará en algo tedioso o in-practico si no disponemos de una computadora portátil o si la computadora de escritorio está muy lejos del reloj.
La segunda solución y más practica, consiste en agregar un modulo bluetooth, en el post anterior vimos que podemos hacer uso de esa función con simplemente agregar un modulo cuyo precio es de un par de dolares.

Los modelos más comunes son los HC-06 y HC-05, al comprarlos y emparejarlos por primera vez, verás que tienen un nombre por default, pero que pasa si queremos mostrar un nombre más apropiado para nuestro arduino (ClockView por ejemplo).
En este caso el dispositivo se llama Siqueiros Printer.



Para realizar esta operacion vamos a usar comandos AT, para el modulo HC-06 podemos usar los siguientes:
  • Prueba de funcionamiento:
    • Comando AT: AT
    • Respuesta: OK
  • Configurar el Baudrate:
    • Enviar: AT+BAUD<Numero>
    • El parámetro número es un carácter hexadecimal de ‘1’ a ‘c’ que corresponden a los siguientes Baud Rates:
      • 1=1200
      • 2=2400
      • 3=4800
      • 4=9600
      • 5=19200
      • 6=38400
      • 7=57600
      • 8=115200
      • 9=230400
      • A=460800
      • B=921600
      • C=1382400
  • Respuesta: OK<baudrate>
  • Configurar el Nombre de dispositivo Bluetooth:
    • Comando: AT+NAME<Nombre>
    • Respuesta: OKsetname
  • Configurar el código PIN de emparejamiento:
    • Comando: AT+PIN<pin de 4 dígitos>
    • Recibe: OK<pin de 4 dígitos>
  • Obtener la versión del firmware:
    • Enviar: AT+VERSION
    • REspuesta: VERSION
En el caso del modulo HC-05, basta con enviar el comando seguido de un <ENTER>, pero para el caso del módulo HC-06, solo tendremos un segundo para enviar el comando, todo lo que se envie en ese periodo sera tratado como tal, por lo que si no es lo suficientemente rápido, el comando no será aceptado. para simplificar las cosas, vamos a usar el siguiente código que se encargará de enviar los comandos.
#include <SoftwareSerial.h>
/*CONEXIONES:
 5V - VCC
 GND - GND
 PIN 2 - TX
 PIN 3 - RX
 */
//Crea conexion al BlueToothModuletooth - PIN 2 a TX y PIN 3 a RX
SoftwareSerial BlueToothModule(2, 3);
//Nombre de 20 caracteres maximo
char NOMBRE[21] = "ClockView"; 
// 1=1200 , 2=2400, 3=4800, 4=9600, 5=19200, 6=38400, 7=57600, 8=115200
char BPS = '4';
//PIN O CLAVE de 4 caracteres numericos
char PASS[5] = "0000";

void setup()
{
// inicialmente la comunicacion serial a 9600 Baudios (velocidad de fabrica
 BlueToothModule.begin(9600);
 pinMode(13,OUTPUT);
 delay(4000);
// Inicializa comando AT
 BlueToothModule.print("AT");
 delay(1000);
// Configura el nuevo nombre
  BlueToothModule.print("AT+NAME"); 
 BlueToothModule.print(NOMBRE);
 delay(1000);
// Configura la nueva velocidad  
 BlueToothModule.print("AT+BAUD");
 BlueToothModule.print(BPS); 
 delay(1000);
// Configura el nuevo PIN
 BlueToothModule.print("AT+PIN");
 BlueToothModule.print(PASS); 
 delay(1000);
}
 
void loop()
{
 //Nada
}

Para ver una lista completa de todos lo comandos disponibles, puedes ver el este documento. Para usar el código anterior, debemos de conectar el pin TX del bluetooth al pin 2 del arduino, el pin RX al pin 3 y antes de cargar el código, debemos de editar el nombre, la velocidad de transferencia (aunque recomindo dejarla en 9600 para usarse en Clock View) y la contraseña. Si no quieres complicarte, podemos usar los valores tal y como vienen en el código, solo debes recordar que el nombre del dispositivo será "ClockView" y la contraseña será 0000.
Y bien, ahora que tenemos listo todo, solo nos queda por hacer la parte de android para poder comunicarnos con el arduino mediante el telefono.

Al igual que siempre, el código lo puedes descargar de mi dropbox.

Por ahora es todo, los leo luego.

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.

Actualizarse o morir.

Hola a todos, el día de hoy les voy a platicar una anécdota (si, el hardware que compré aun no ha llegado, entonces ClockView tendrá que esperar un poco más).

Bueno, cómo pocos o muchos sabrán, yo generalmente me dedico a crear software, la mayoría de lo que produzco está destinado a usarse en una computadora, pero en tiempos relativamente recientes, el uso de software para dispositivos móviles, ha crecido mucho, por lo tanto me ha obligado a desarrollar para esas plataformas.

Para aplicar las soluciones cómo es debido, es necesario tener un dispositivo en el cual probar la aplicación; pero antes de seguir, si, se que existe un emulador en el entorno de android, pero realmente se puede saber si tu aplicación funciona cuando las ejecutas en un dispositivo de verdad. Entonces, que dispositivo debía de elegir. En realidad yo no soy fanático de tener una máquina super-potente, tener un teléfono con un procesador de 8 núcleos a 2.1 GHz y 4 GB de RAM... y luego, en mi caso particular (y de la mayor parte de la gente que me rodea), nunca utilizaríamos esa potencia de manera cotidiana, la mayoría de la gete juega al "Candy Crush", usa "Twitter", "Facebook" y aplicaciones que en realidad no son más que clientes, porque todo el trabajo sucio que llegara a realizarse, se hace en los servidores (en el caso concreto de las redes sociales y para candy crush, realmente no es necesario tanto para poder ejecutarlo).

Si se necesita hacer algo realmente complejo o que requiera un poder de procesamiento grande, usualmente se recurre a la PC.

Para realizar muchas de las pruebas del código que programaba, usaba un teléfono relativamente viejo.
Funciona y funciona muy bien.
Con ese telefono hice una gran cantidad de aplicaciones y en el se podian ejecutar sin problemas; pero eso fue hasta hace poco.

Unos de los principales problemas que tuve, fue que muchas de las aplicaciones que se encuentran en la ultima versión, ya no eran compatibles con el sistema operativo que el teléfono posee, en el caso del que tengo, posee android 2.1 eclair, y muchas de las aplicaciones (navegadores, reproductores de música) ya no son compatibles con esa versión. Yo cuando programo, siempre trato de poner la mayor "cantidad de compatibilidad", por lo que cuando hacia una aplicación que tiene cómo destino la versión 2.1 de android, resultaba 99% compatible con la versión 5.0. Incluso la aplicación (cuyo codigo está aqui), la probé en el teléfono con android 2.1 y también corrió perfectamente en una tableta con  android 5.0.

Recientemente Android Studio, sacó una nueva actualización, al instalarla me llevé una pequeña sorpresa.

En efecto, ya no tiene soporte para la versión 2.1 y bueno cómo dicen por ahí: "Actualizarse o morir".

Y bien por ahora ya no queda más remedio que adaptarme, pero a raíz de esto, ya no queda más que dejar  descansar al teléfono y echarle toda la carga a lo nuevo. En lo personal, se me hace un desperdicio, hay mucho Hardware por ahí que tiene especificaciones realmente buenas, pero que el único limitante que poseen es el sistema operativo. Además hay que recordar que su uso primordial es el de un telefoneo, pero cosas cómo está solo impulsan a comprar más.
Yo seguiré usando mi teléfono, para mi funciona y además le tengo aprecio por cómo lo recibí, si llego a cambiarlo, solo será porque este deje de funcionar.

Por ahora es todo, en cuanto reciba el hardware, haré la siguiente parte de ClockView.

Hasta pronto.

P.D: Feliz 2017.