Xwork's Blog

The lord is waiting to take your hand.

Mostrando las entradas con la etiqueta DIY. Mostrar todas las entradas
Mostrando las entradas con la etiqueta DIY. Mostrar todas las entradas

Inutil apps #4 - Visualizador de sonido (pt. 2.)

Hola de nuevo a todos, el día de hoy vamos a continuar con la construcción del visualizador de sonido.


En el post anterior vimos cómo hacer las conexiones en el diagrama, hoy solo veremos cómo ensamblarlo.

Para que se más vistoso decidí hacer una caja de 100 cm x 10 cm x 10 cm con madera de 0.4 cm. para eso corte 2 pedazos de 100 cm x 10cm y once pedazos de 10 cm x 10 cm para usarlos cómo divisores.



Para unir todo simplemente use clavos de 1.27 cm y pedazo de madera de 9 cm x 2 cm x 2 cm. Odio admitirlo, pero de esta parte no gusto tanto el resultado porque se así:


Y se notan más cuando la luz se enciende pero a pesar de todo decidi mantener el diseño cómo simple recordatorio de que hay que planear bien todo desde el principio.

Luego pegue los LED's usando silicón caliente, puesto que esto esta diseñado para no moverse mucho, en lugar de usar clavos o un pegamento más potente, el silicón cumple con su trabajo a la perfección y por eso también lo use en los espaciadores.


Cuando terminé de pegar los LED's use cable "rainbow" de 8 buses (que era la que tenía a la mano) + dos cables 28 AWG para hacer las conexiones hacia el circuito controlador, hay que recordar que el integrado LM3915 /14/16 va a manejar cada LED  desde su cátodo por lo que estos deben de ir por separado. Para el anodo, igual use cable 28 AWG, cada modulo LED usa 20 mA por lo que suponiendo que todos estén encendidos a su máxima capacidad, solo circularan 200mA por este cable.


Finalmente solo conecte el circuito ya hecho, todo parecía funcionar de maravilla, pero aqui vuelvo a remarcar la importancia de planear estás cosas. A pesar de que es un proyecto que llevo "haciendo" desde el 2014 (15?) cuando por pura casualidad le dije a una amiga sobre mi adicción a las luces sensibles al sonido y ella me consiguió los primero lm3915 (que todos esos murieron en nombre del progreso). Hasta ahora que en teoría ya debería de tener una idea clara de que y cómo hacerlo. Simplemente no aprendí nada ya que no tome en consideración el espacio para anclar debidamente todo el circuito y simplemente lo deje así; colgando.

Aunque es una mala practica dejar las cosas "cómo queden", por está ocasión la dejare así, ya que el resultado es igual a cómo lo imaginé hace mucho.

Y bien, por ahora es todo, no olvides revisar el post anterior donde están los diagramas.

Los leo luego.

P.D:
Lo admito.

Inútil apps #4 -Visualizador de sonido.

Desde que era pequeño, siempre tuve fascinación por las luces audioritmicas, recuerdo que mi papá tenía un Boom-Box (a grabadora) que tenia un visualizdaor de 10 niveles (un LED por nivel). El efecto que lograba era bastante llamativo y tal mosco a la luz, no podía evitar ser atraído por esta.


El día de hoy vamos a hacer un visualizador de sonido. usando uno de los integrados más comunes: el LM3915.

Para empezar revisemos el siguiente esquema:

El circuito integrado LM3915 se encarga de medir niveles de voltaje y es capaz de manejar 10 LED's.
Antes de continuar revisemos para que sirve cada una de las entradas del integrado

  1. Pin 1 - Salida de LED.
  2. Pin 2 - Tierra.
  3. Pin 3 - VCC (3 ~ 35V).
  4. Pin 4 - Divisor bajo
  5. Pin 5 - Input
  6. Pin 6 - Divisor alto.
  7. Pin 7 - Referencia de salida.
  8. Pin 8 - Ajuste de referencia.
  9. Pin 9 - Selector de modo.
  10. Pin 10 ~ Pin 18 - Salida de LED's
Para poder hacer el proyecto entonces necesitaremos los siguientes componentes:

  • U1 - Circuito integrado LM3915 (14/16).
  • D1 - Diodo 1N5819
  • R1 - Resistencia 680 Ohms
  • R2 - Resistencia 150 Ohms
  • R3 - Resistencia 10 KiloOhms
  • C1 - Capacitor electrolitico 10 micro Faradios.
  • LED1 ~ 10. Diodo LED.
Para empezar, el circuito integrado LM3915 tiene la capacidad de entregar de desde 2 mA hasta 30 mA, para poder ajustar la corriente que va a circular en cada salida, debemos de tomar en cuenta la siguiente formula:

  • 12.5/R1
En nuestro caso tenemos que la resistencia R1 tiene un valor de 680 Ohms por lo que sustituyendo tenemos:

  • 12.5/680 = 18.3 mA
Con 18.3mA tenemos la cantidad suficiente de corriente para manejar casi cualquier LED. Al establecer la corriente, no nos tendremos que preocupar por poner resistencias entre los LED's y VCC puesto que el integrado se encargara de administrar: pero hay que resaltar que si solo se va a hacer uso de un solo LED a cada salida, es recomendable bajar el voltaje al cual trabajaremos, si no lo hacemos, todo el voltaje sera disipado por el integrado y la vida útil de este se reducirá.
Si se quiere hacer uso de 12V podemos usar segementos de LED como los siguientes:


Este tipo de módulos son muy comunes, al hacer la medición cada segmento consume 20mA, para poder calcular la resistencia adecuada podemos re-acomodar la formula de tal forma que si queremos saber que resistencia debemos usar tendremos algo cómo lo que sigue:

  • 12.5/I (donde I es la corriente).
Y sustituyendo tenemos:

  • 12.5/0.02 =  625 Ohms
Ahora imaginemos que queremos usar 25 mA, solo bastará que se sustituya el nuevo valor de la corriente:

  • 12.5/0.025 = 500 Ohms.
Si se requiere una mayor corriente para manejar corrientes de mas de 30 mA, se deberá de usar algún transistor, si se le exige más corriente al integrado, este simplemente se quemará.

Para la entrada de señal, el circuito esta diseñado para ser conectado a la salida de un amplificador con niveles de 0V a 9V, para poder variar la sensibilidad, debemos de cambiar la corriente que circula por el pin8. Por ahora solo dejare esta versión, pero si necesitas saber cual es el valor de las resistencias para los voltaje, puedes leer la hoja de datos del integrado.

Para poder controlar cómo se va a mostrar los niveles, disponemos de dos modos:

  1. Modo Barra.
  2. Modo Punto.
El modo barra consiste en que todos los niveles se encenderán si hay uno superior activo, supongamos que tenemos el LED 8 encendido, entonces los LED's del 1 al 8 estarán encendidos, aunque parezca un poco obvio, es importante mencionarlo para contrastarlo con el modo punto; en el modo punto solo el LED 8 debe de estar encendido, solo este lo estará, es decir ninguno que no sea el del nivel actual estará encendido. Si se usa el modo punto, debemos de usar una resistencia de 1 a 10KOhms entre el PIN 1 y su respectivo LED, si no se pone, cuando el nivel sea diferente al del LED 1, este presentará un ligero resplandor.

El modulo rojo debería de estar totalmente apagado, pero sin la resistencia no lo hacer (aunque pienso que quueda bien)


Y bien, por ahora es todo, en el siguiente post, veremos cómo armar el circuito y trataremos de hacer un empaque para que se vea un poco mejor.

Los leo luego.

Vamos a programar #48 - Inútil apps #3 -Velocímetro para bicicleta, sensor de efecto Hall.

Hola de nuevo a todos. El día de hoy vamos a ver una pequeña introducción a la construcción de un velocímetro.

Cómo algunos sabrán, tengo cierta afición por hacer viajes en bicicleta, pero cómo soy un hombre de números, siempre me ha gustado cuantificar lo que hago (en medida de lo posible), así que decidí llevar la cuenta de la actividad que hacía. Por fortuna, muchos de los teléfonos actuales, tienen aplicaciones que ayudan a llevar el registro (gracias a sensores y al GPS).
Hasta hace poco tenía la idea de hacer una versión propia de un programa que lleve todos esos datos, pero un par de eventos bastante desafortunados, bastaron para pensarlo dos veces. Entonces opté por comprar un velocímetro, lo encargue desde China y tenía un tiempo estimado de entrega de dos meses, pero por sismos y varias cosas, no llegaron (ya va medio año prácticamente, pero aun espero su llegada junto con la de algunos paquetes mas grandes).
Al revisar cómo es que funcionan, noté que la gran mayoría funcionan de la misma manera; se pone un sensor en la tijera que sostiene una llanta y cada vez que ésta da una vuelta, de alguna manera se notifica. En el caso del velocímetro que encargue, en el manual (de internet) decía que se debía de pegar un imán a un rayo de la llanta y el sensor ponerse fijo en la tijera, al revisar más a detalle, averigüe que se trataba de un sensor de efecto Hall.

El sensor de efecto Hall.

El sensor de efecto Hall o simplemente sensor Hall o sonda Hall (denominado según Edwin Herbert Hall) se sirve del efecto Hall para la medición de campos magnéticos o corrientes o para la determinación de la posición en la que está.
Si fluye corriente por un sensor Hall y se aproxima a un campo magnético que fluye en dirección vertical al sensor, entonces el sensor crea un voltaje saliente proporcional al producto de la fuerza del campo magnético y de la corriente. Si se conoce el valor de la corriente, entonces se puede calcular la fuerza del campo magnético; si se crea el campo magnético por medio de corriente que circula por una bobina o un conductor, entonces se puede medir el valor de la corriente en el conductor o bobina.
Si tanto la fuerza del campo magnético como la corriente son conocidos, entonces se puede usar el sensor Hall como detector de metales. Wikipedia/SensorHall
Ahora veremos cómo conectar el sensor de efecto hall, para hacerlo, necesitas lo siguiente:

  1. Sensor efecto hall.
  2. Resistencia 1KΩ  ~10KΩ. 
  3. Arduino

En mi caso, el sensor es el 44e938, cuyos pines son:


  1. Supply (4.5~24V).
  2. Ground.
  3. OutPut.
Para conectarlo, simplemente debemos de conectar la pata uno a V+, en este  caso 5V, Ground a tierra y output a un pin del arduino. Para poder sensar correctamente, debemos de poner una resistencia entre la pata 3 (output) y la pata 1 (supply), al inicio veía esquemas en los cuales siempre usaban resistencias de 10K, pero en mi caso y por el tamaño de los imanes que uso, con una resistencia de 1K fue más que suficiente. Si la resistencia no se pone, el sensor queda muy sensible (valga la redundancia) y detectara cambios tan pequeños en el flujo magnético que incluso acercar tu dedo afecta. OutPut lo conectaremos a un pin del arduino, si quieres probar el código, puedes conectarlo en el pin 3.

Con lo anterior pasemos al código.
//Pin al que coenctaremos el sensor
const int HallSensor = 3;
//LED para mostrar el cambio en caso de no disponer del monitor serie
const int LED =  13;
//Estado del sensor
int HallSensorVal = 0;
//Número de vuelta
int Lap = 0;
//Estado anterior del sensor
int OldState = 0;
//Inicializar lo necesario
void setup() {
	Serial.begin(9600);
	pinMode(LED, OUTPUT);
	pinMode(HallSensor, INPUT);
}
//Bucle princial
void loop() {
	HallSensorVal = digitalRead(HallSensor);
	if (OldState != HallSensorVal) {
		if (HallSensorVal == LOW) {
			digitalWrite(LED, HIGH);
			Lap += 1;
			Serial.println("Vuelta:");
			Serial.println(Lap);
			delay(1);
		}
		else {
		digitalWrite(LED, LOW);
		}
	}
	if (Lap > 99)
		Lap = 1;
	OldState = HallSensorVal;
}

El código anterior, lo único que hace, es verificar cual es el estado del sensor, cuando esta en modo "LOW", prende el LED que viene por default en el pin 13 y a la variable "Lap" le suma 1 (indicando una vuelta). Para probar el código anterior, necesitaras también un par de imanes, en mi caso, los extraje de un quemador de CD viejo, y los pegué en un motor (también extraído del lector de CD).
El resultado:

Con esto podemos probar que el sensor funciona de manera correcta.

Y bien, por ahora es todo, en el siguiente post, veremos cómo medir distancia y velocidad con el mínimo hardware posible. Aun no he decidido si usar las matrices para mostrar los datos o usar una pantalla LCD (cómo la del nokia) o incluso pantalla de siete segmentos. El código lo puedes copiar y pegar para usarlo, en cuanto este completo lo subire a mi dropbox.

Los leo luego.

Vamos a programar #32 - Clock view en Windows.

Hola de nuevo a todos, el día de hoy vamos a continuar con un poco más de clock view.
En los post anteriores, vimos cómo controlarlo desde una aplicación para android, pero mucha gente me pregunto si era posible hacer algo similar pero desde una computadora con Windows.
La respuesta: Clock View para Windows.
Para llevar a cabo la conexión, primeramente debemos emparejar el bluetooth de Clock view con el de la computadora que vayamos a usar.

En mi caso es "Siqueiros printer" (ignora el nombre es un arduino)

Cuando ya estén emparejados, hay que revisar cual es el puerto que se asigno al bluetooth. Para eso, hay que ir a: Panel de control > Dispositivos e impresoras > "Nombre del Arduino", luego hay que hacer clic secundario y después propiedades.En la ficha "Servicios", nos mostrará cual es el puerto que se usará.

Una vez que sabemos esa información, pasaremos al código de C# que es el siguiente:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO.Ports;
using System.Windows.Forms;
using System.Configuration;

namespace ClockView
{
	public partial class FrmMain : Form
	{
		private SerialPort Port = new SerialPort();

		private void ArduinoMessage(string Message)
		{
			try
			{
				Port.Write(Message);
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}

		}

		public FrmMain()
		{
			InitializeComponent();
		}

		private void Form1_Load(object sender, EventArgs e)
		{
			string[] Ports = SerialPort.GetPortNames();
			CboPorts.Items.AddRange(Ports);
			Port.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
		}

		private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
		{
			SerialPort sp = (SerialPort)sender;
			string indata = sp.ReadExisting();
			if (indata == "Done")
			{

			}
		}
		private void BtnConnect_Click(object sender, EventArgs e)
		{
			try
			{
				if (Port.IsOpen == false)
				{
					Port.BaudRate = int.Parse(TxtSpeed.Text);
					Port.PortName = CboPorts.Text;
					Port.Parity = Parity.None;
					Port.StopBits = StopBits.One;
					Port.Parity = Parity.Even;
					Port.DataBits = 8;
					Port.Open();

				}				
			} catch (Exception ex) {
				MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		private void BtnDisconnect_Click(object sender, EventArgs e)
		{
			if (Port.IsOpen == true)
			{
				Port.Close();
			}
		}

		private void Btn1224_Click(object sender, EventArgs e)
		{
			ArduinoMessage(">SETF");
		}

		private void BtnShowSeconds_Click(object sender, EventArgs e)
		{
			ArduinoMessage(">DISS");
		}

		private void BtnLights_Click(object sender, EventArgs e)
		{
			ArduinoMessage(">SCRA");
		}

		private void trkbarBrillo_MouseUp(object sender, MouseEventArgs e)
		{
			ArduinoMessage(">SETB" + trkbarBrillo.Value.ToString());
		}

		private void BtnSync_Click(object sender, EventArgs e)
		{
			//>SETH2016122119001004
			string HourData = string.Concat(">SETH", DateTime.Now.Year, DateTime.Now.Month.ToString("D2"), DateTime.Now.Day.ToString("D2"),
				DateTime.Now.Hour.ToString("D2"), DateTime.Now.Minute.ToString("D2"), DateTime.Now.Second.ToString("D2"),
				(((int)DateTime.Now.DayOfWeek)+1).ToString("D2"));
			ArduinoMessage(HourData);

		}
	}
}

Para hacer uso del puerto serial, debemos de crear un objeto SerialPort que se incluye en el espacio de nombres System.IO.Ports.
Cuando el formulario se carga, lo primero que hará es buscar todos los puertos disponibles, pero eso no quiere decir que este conectado o en uso el periférico. Si cuando inicias el programa no puedes ver el puerto al que está conectado tu arduino, basta con que escribas el nombre en la lista. Despues solo hay que hacer clic en conectar y si todo está en orden, se podrá controlar ClockView desde la computadora con BlueTooth.
Al igual que la aplicación para android, en está aplicación se incluyen los controles que cambian las configuraciones de ClockView. Además está aplicación sirve con un cable, si conectas Clock View con un cable USB, también podrás cambiar los ajustes, pero esa no es la idea; aun si prefieres hacer uso del cable, es recomendable que desconectes el módulo HC-05/06 del arduino para que no haya ningún tipo de problemas.
Al igual que todos los otros programas, dejo el código fuente para que los descargues y lo pruebes. El Código fuente de la aplicacion para android, aun lo voy a mejorar un poco y el software para arduino también.
Por ahora es todo. Los leo luego.

Vamos a programar #30 - El código de Clock View (Android)

Hola de nuevo a todos, el día de hoy vamos a explicar el código que hace funcionar la aplicación de android Clock View.


La interfaz.

Antes que nada vamos a empezar ´por la interfaz, para el caso de Clock View esta compuesta de 2 partes. La primera son todos los botones de "acción" y parte principal de la aplicacion; la segunda solo es la lista que se encarga de mostrar todos los dispositivos que estén emparejados al dispositivo.

Para mostrar la interfaz principal, en el Layout principal, debemos de agregar el siguiente código.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/activity_main"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:paddingBottom="@dimen/activity_vertical_margin"
 android:paddingLeft="@dimen/activity_horizontal_margin"
 android:paddingRight="@dimen/activity_horizontal_margin"
 android:paddingTop="@dimen/activity_vertical_margin"
 tools:context="com.mdev.clockview.MainActivity">

 <ScrollView
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:layout_alignParentTop="true"
  android:layout_alignParentLeft="true"
  android:layout_alignParentStart="true">

  <LinearLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:orientation="vertical">

   <TextView
    android:text="La hora actual es:"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/TXTView"
    tools:ignore="HardcodedText" />

   <TextClock
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="14dp"
    android:id="@+id/TXTClock"
    android:typeface="sans"
    android:textAlignment="center"
    android:textSize="30sp"
    android:fontFamily="monospace"/>

   <Button
    android:text="Conectar con arduino."
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/BtnConnect"
    android:textAlignment="center"
    android:textAllCaps="false" />

   <Button
    android:text="Cambiar 12/24 Horas."
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/BtnFormat" />

   <Button
    android:text="No/Mostrar segundos."
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/BtnShowS"/>

   <Button
    android:text="Apagar/Encender matrices"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/BtnOn"/>

   <Button
    android:text="Sincronizar hora."
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/BtnSync"/>

   <TextView
    android:text="Nivel de brillo:"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="11dp"
    android:layout_marginStart="11dp"
    android:layout_marginTop="10dp"
    android:id="@+id/textView2" />

   <SeekBar
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="12dp"
    android:id="@+id/SKBrillo"
    android:max="15"
    android:progress="5" />

   <TextView
    android:text="Brillo: 15"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/txtSeekValue"/>

   <TextView
    android:text="Esperando a ClockView."
    android:layout_width="match_parent"
    android:id="@+id/TxtArduinoReturn"
    android:layout_height="wrap_content" />

   <Button
    android:text="Desconectar"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/BtnDisconnect" />
  </LinearLayout>
 </ScrollView>
</RelativeLayout>

Tras implementar el código, la aplicacion lucira algo similar a la siguiente imagen:



Cómo verás, solo consta de 6 botones, una barra de seek y las etiquetas para mostrar texto.
La funcion de cada uno, la veremos en el código Java, pero antes, vamos a terminar con la interfaz. La lista que contiene los dispositivos emparejados, es un layout que solo contiene un list view y su código es el siguiente;

<LinearLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:id="@+id/bt_list"
 android:orientation="vertical"
 android:layout_width="match_parent"
 android:layout_height="match_parent">

 <ListView
  android:id="@+id/BTList"
  android:layout_width="match_parent"
  android:layout_height="200dp"
  android:headerDividersEnabled="true"
  android:footerDividersEnabled="false">
 </ListView>

</LinearLayout>

Con esto completamos la parte de la interfaz, además en el código completo he agregado el diseño para cuando la orientacion del dispositivo es vertical y para cuando es horizontal, en lo personal preferí forzar a que la aplicacion siempre este en horizontal, pero eres libre de cambiarla a tu gusto.

El código en java.

El código en Java, lo he modificado un poco desde la última vez, corregi algunos errores, uno de ellos era: cuando se intentaba hacer la conexion y esta fallaba, al hacer click en cualquier botón, este intentaba mandar algo a traves del socket que supestemente debio de haber sido creado, al no estar disponible, la aplicion "crasheaba" y salia; el error se debia que a pesar de no haber conexion, la condicion que se debia de comprobar era verdadera debido a que se verificaba si el bluetooth estaba activado; la condicion se podia cumplir, pero eso significaria necesariamente que el socket ya se hubiera creado. Para solucionarlo, simplemente use un flag boleano que solo se cambiaba cuando se abria o cerraba la conexion, ahora si no hay conexion, simplemente no se intenta enviar nada.

Ademas coerregi algunos detalles y bugs minimos. El código actualizado es el siguiente:

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("Brillo: " + 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(BuildHour(true));
    }
   }
  });

  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 BuildHour(boolean ForArduino){
  //>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 Fecha = null;
  if (ForArduino == true){
   Fecha = String.format(">SETH%1$04d%2$02d%3$02d%4$02d%5$02d%6$02d%7$02d",
     Anio, Mes + 1, Dia, Horas, Minutos,Segundos, DiaSema);
   msg(Fecha);
  }else {
   Fecha = String.format("%1$04d/%2$02d/%3$02d - %4$02d:%5$02d:%6$02d",
     Anio, Mes + 1, Dia, Horas, Minutos,Segundos);
  }
  return Fecha;
 }

 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(info);

   } 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 ("+ address +")...", "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();
  }
 }
}

Ahora con el código actualizado vamos a ver que hace cada parte del el.

Clock view está compuesto por tres clases, la primera de ellas es la que se encarga de mostrar todos los componente de la interfaz principal (crear lo eventos para los botones, inflar las cosas necesarias, etc), la segunda es ConnectedThread.

La clase ConnectedThread, es la que se va a encarga de enviar y recibir los datos de y hacia la aplicación.
En ella hay dos métodos: run() y write(). En el método run es donde vamos a leer todos los datos que vienen desde el arduino. Lo pasaremos cómo mensaje y despues el manejado h, se encargará de re-direccionar el contenido y mostrarlo en un textview.
El método write(), servirá para enviar datos hacia el arduino, está es la función que se debe de usar para enviar los comandos, por ejemplo, para enviar el comando para mostrar los segundos, debemos de llamar a la función con el parámetro ">DISS" write(">DISS").
La tercera clase es la que se encarga de crear la conexion y mostrar un dialogo de progreso/espera, cuando la conexión se realiza, deja un socket bluetooth listo para usarse.

Cómo verás, la aplicación es realmente sencilla, pero aun queda mucho por agregar a ambas partes de Clock View, por ahora el código fuente completo lo reservo para cuando ya tenga todas la funciones listas, pero con lo anterior, ya puedes crear tu propia versión.

La parte del código para arduino se actualizará, pero ese en cuanto lo cambie, también actualizaré el vinculo en dropbox.

Por ahora es todo, pero a esto aun le resta bastante para que sea un reloj funcional (más funcional).
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 #26 - Inútil Apps #2 - Clock View

Hola de nuevo a todos, el día de hoy vamos a continuar con el código de Clock View, la vez anterior solo implementamos las parte mas "criticas" de el, y  ya podría considerarse cómo un reloj funcional, pero vamos a agregar unas cuantas funciones para hacerlo más funcional.

Para empezar echemos un vistazo al nuevo código.
//Clockview 1.0
#include <DS1302.h>
#include "LedControl.h"
//Constantes para los pines usados en la matriz
const int MaxDIn = 12;
const int MaxCS = 11;
const int MaxCLK = 10;
int MaxNDevices = 3;
bool IsConnected = false;
//Constantes para los pines usados en el reloj
const int kCePin = 7;  // RST
const int kIoPin = 8;  // Dat
const int kSclkPin = 9;  // 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
char Texto[24];
int MatrixB = 4;
bool H24 = true;
bool HalfSecond = true;
bool IsScreenEnable = true;
bool ShowSeconds = true;
//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
 B111110, B001100, B111110, //M
 B111110, B001010, B001110, //P
 B111110, B001000, B111110, //H
 B111110 ,B011010, B101110, //R
};
//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(13, HIGH);
  HalfSecond = false;
 }else{
  digitalWrite(13, LOW);
  HalfSecond = true;
 }
 Serial.println(buf);
}
//Ajustar la hora
int AdjustTime(int Hour, bool In24Hformat){
 if(In24Hformat == true){
  return Hour;
 }
 else{
  Hour = Hour - 12;
  return Hour;
 }
}
void CheckPetition(char DATA[]){
 int i = 0;
 int j = 0;
 String Texto(DATA);
 //>SETH2016121221001007
 //Bright = Texto.substring(4).toInt();
 //MatrixB
 if (Texto.startsWith(">DISS")){
  ShowSeconds = !ShowSeconds;
  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(">SETF")){
  H24 = !H24;
  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());
  Serial.print("La hora se ajusto.");
  for (j = 0; j < 11; j++) {
   DATA[j] = 0;
  }
  i = 0;
 }
 if (Texto.startsWith(">SETB")){
  MatrixB = Texto.substring(5).toInt();
  Serial.print("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); 
}
//Setup
void setup(){
 for (int CurrentDevice = 0; CurrentDevice < MaxNDevices; CurrentDevice++){
  lc.shutdown(CurrentDevice, false);
  lc.setIntensity(CurrentDevice, MatrixB);
  lc.clearDisplay(CurrentDevice);
 }
 pinMode(13, OUTPUT);
 Serial.begin(9600);
}
//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);
}

Analicemos un poco el código, para empezar, he creado algunas variables que sirven para controlar algunos de los aspectos del reloj. Podemos elegir entre mostrar la hora en formato de 12 o de 24 horas, decidir si se quiere mostrar o no los segundos y ademas, ahora podemos apagar las matrices, útil para cuando llega la hora de dormir.
En la matriz que almacena los datos de los números, ademas de agregar las letras "a", "m" y "p", se agregaron las letras "h" y "r", estas se usan cuando la hora esta en formato de 24 horas y ademas la opción de "no mostrar los segundos" está activa
La opción de 24 horas y la opción de "No mostrar los segundos" activos.

La opción de 24 horas no está activa y la opción de no mostrar los segundos tampoco.
Para poder mostrar algunas opciones, se han agregado algunos símbolos, pero por ahora no se usan, decidí ponerlos en una matriz a parte para que se puedan modificar sin alterar los numero, ademas estos serán un poco mas "grandes" que los números, lo que hace necesario crear una función aparte para imprimirlos en las matrices.

//Imprimir simbolos
void PrintSymbol(byte Index,byte SymDevice){
 for (int Y = 1; Y < 7; Y++){
  lc.setRow(SymDevice, Y, Symbols[Index * 5 + Y - 1]);
 }
}

Cómo notarás, la mayor parte del código es similar a la función "PrintNumber", solo que en este caso, lee completamente de 5 en 5 y no de 3 en tres como en la función para mostrar los números. Recibe cómo parámetros, el indice en donde se encuentra la primera fila a desplegar y ademas, también recibe en que numero de matriz se va a mostrar el resultado.

La función "PrintTime" se ha modificado para que la hora se muestre de acuerdo a las opciones que el usuario haya pedido, en los casos anteriores, para mostrar la información de un formato u otro, solo basta con que se le mande un comando para que se hagan los cambios pertinentes. Toda la parte de los comandos lo veremos un poquito más adelante.
//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(13, HIGH);
  HalfSecond = false;
 }else{
  digitalWrite(13, LOW);
  HalfSecond = true;
 }
 Serial.println(buf);
}
Los cambios que se hicieron, son prácticamente los que indican el despliegue de la información, los nuevos "if" solo controlan que es lo que se debe de mostrar.

Cómo mencione en hace un poco, se han añadido algunos comandos que se usaran para modificar las propiedades del reloj sin la necesidad de re-compilar el código cada vez, actualmente los comandos disponibles son los siguientes:
  1. >DISS (Display Sesconds) - Intercambia entre mostrar o no los segundos.
  2. >SCRA (Screen Active) - Apaga o enciende las matrices.
  3. >SETF (Set Format) - Intercambia entre el formato de 12/24 horas.
  4. >SETHYYYYMMDDHHmmSSdd (Set Hour) - Cambia la hora del reloj, donde YYYY es el año, MM son los meses, DD son los días, HH son las horas en formato de 24 horas, mm son los minutos, SS los segundos y dd es el dia de la semana del 1 al 7 en donde el día 1 es domingo.
  5. >SETBxx (Set Brightness) - Cambia el brillo de las matrices, en donde xx es un valor que este entre el rango de 1 a 15, donde 1 es lo menos brillantes y 15 es el máximo.
Los primeros comandos al ser booleanos, solo pueden ser "cierto" o "falso", por lo que cuando se envía el comando, lo único que se hace es invertir su valor, después se en el monitor serial, se despliega información indicando que ese parámetro se ha cambiado.
Algo importante por mencionar es que todos los comados se enviaran por la interfaz serial, por lo que se puede usar el monitor serial del IDE de arduino u otros métodos que veremos un poco después.


Cuando se quiere ajustar la hora, podemos usar el comando ">SETH", si por ejemplo queremos establecer la siguiente hora:20:30:45 Miércoles 28 de diciembre del 2016, entonces el comando deberá de tener el siguiente aspecto. ">SETH2016122820304504". Si la hora que se quiere ingresar tiene un formato incorrecto, el reloj mostrará las 00:00:00 del 1/1/2000 domingo. si eso llegará a pasar, se corrige cuando se ingresa un fecha válida.

Para el caso del brillo, cualquier valor fuera de los limites validos (1-15) hará que las matrices tengan el brillo más bajo.
Por ultimo, se ha agregado una función que prende y apaga el LED que está conectado al PIN 13, esto con la finalidad de que se use como indicador, la mayoría de los relojes digitales, tiene un par de puntos entre cada par de dígitos que prenden y apagan cada segundo, si se quiere mostrar algo similar, bastara con conectar LED al pin 13.
Este reloj está lejos de ser terminado por completo, pero por ahora es todo, si en el transcurso de la semana llegan los otros componentes que quiero agregar, la semana que viene, veremos cómo agregar un medio para que todas las configuraciones que se cambien, queden guardadas y así si por alguna razón se quiere desconectar todo el proyecto, no se tendrán que hacer cambios a los ajustes cada vez.
Ademas de que agregaremos una solución "móvil" para que el reloj se pueda controlar a distancia.
Y bien, por ahora es todo, el código completo actualizado, cómo siempre, lo puedes descargar de mi dropbox.
Por ahora es todo. Los leo luego.

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

Hola a todos, el día de hoy vamos a realizar otro pequeño proyecto con arduino, es muy simple en realidad, pero creo que sirve para reforzar lo básico, además, si cómo yo, eres fanático de los LED's de seguro este proyecto te encantará.


Cuando hice el proyecto para mostrar la información de ITunes en las matrices LED, usaba una librería que se encargaba de desplegar las letras, los números y además, también proporcionaba algunos efectos básicos: scroll a la derecha, scroll a la izquierda, etc.

Para está ocasión, vamos a hacer algo un poco más sencillo, porque me parece un desperdicio de recursos utilizar toda la librería que use en el proyecto antes dicho para mostrar la hora.

Para los componentes de hoy vamos a requerir:
  1. Un arduino Uno/Mega/Nano.
  2. Un protoboard.
  3. Dos o más matrices led con driver Max7219.
  4. Modulo de reloj en tiempo real con DS1302.
  5. Liberia "LedControl.h"
  6. Liberia "DS1302.h"
  7. Algunos cables jumper para hacer las conexiones.
  8. Un par de manos (o en su defecto una).

Las conexiones.

Lo primero que debemos de hacer las conexiones entre los componentes. Yo en mi caso voy a usar 3 matrices LED, justo ahora compre más por internet, pero como van a tardar mas de 15 días en llegar, solo usaré las que tengo disponibles.
Las conexiones que voy a poner en este post lo más seguro es que cambien un poco en futuras actualizaciones del proyecto, por lo que si quieres usar pines diferentes en el arduino, eres libre de hacerlo.


Para empezar, vamos a conectar las matrices de izquierda a derecha, es decir si miramos de frente, la primer matriz estará al lado izquierdo, y la ultima estará al lado derecho.
Las conexiones de la matriz que se conecta al arduino irán de las siguiente manera:
  1. VCC. Se conecta a +5 voltios.
  2. GND. Se conecta a tierra.
  3. DIN. Lo conectaremos al pin 12 del arduino.
  4. CS. Lo conectaremos al pin 11 del arduino.
  5. CLK. Lo conectaremos al pin 10 del arduino.
Para la siguiente matriz solo conectaremos las salidas iguales con iguales, es decir:
  1. VCC. Se conecta a VCC en la entrada de la siguiente matriz.
  2. GND. Se conecta a tierra.
  3. DIN. Lo conectaremos a DIN en la entrada de la siguiente matriz.
  4. CS. Lo conectaremos a CS en la entrada de la siguiente matriz.
  5. CLK. Lo conectaremos CLK en la entrada de la siguiente matriz.
Las otras se conectaran de la misma forma, hay que recordar que, cómo máximo se pueden usar ocho por cada objeto "LedControl".

Para el modulo de reloj, las conexiones se harán de la siguiente manera:
  1. VCC. Se conecta a +5 voltios.
  2. GND. Se conecta a tierra.
  3. CLK (Serial clock). Lo conectaremos al pin 9 del arduino.
  4. DAT (IO). Lo conectaremos al pin 8 del arduino.
  5. RST (Chip enable). Lo conectaremos al pin 7 del arduino.
Una vez que se realizaron las conexiones anteriores, la parte del hardware está lista, ahora vamos a ver el código que hace funcionar las cosas..

El código en C.

Primero vamos a ver el código fuente completo y después explicare cada una de las partes que lo compone.

//Clockview 1.0
#include <DS1302.h>
#include "LedControl.h"
//Constantes para los pines usados en la matriz
const int MaxDIn = 12;
const int MaxCS = 11;
const int MaxCLK = 10;
int MaxNDevices = 3;
//Constantes para los pines usados en el reloj
const int kCePin = 7;  // RST
const int kIoPin = 8;  // Dat
const int kSclkPin = 9;  // 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
int MatrixB = 1;
//Matriz con los "Numeros"
const unsigned char Numbers[] = {
 B11111, B10001, B11111, //0
 B10010, B11111, B10000, //1
 B11101, B10101, B10111, //2
 B10001, B10101, B11111, //3
 B00111, B00100, B11111, //4
 B10111, B10101, B11101, //5
 B11111, B10101, B11101, //6
 B00001, B00001, B11111, //7
 B11111, B10101, B11111, //8
 B00111, B00101, B11111, //9
 B11111, B00101, B00111, //A
 B11111, B01100, B11111, //M
 B11111, B00101, B00111, //P
};
//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();
 char buf[10];
 snprintf(buf, sizeof(buf), "%02d:%02d:%02d", t.hr, t.min, t.sec);
 PrintNumber(t.hr / 10, t.hr % 10, 0);
 PrintNumber(t.min / 10, t.min % 10, 1);
 PrintNumber(t.sec / 10, t.sec % 10, 2);
 Serial.println(buf);
}
//Convertir los dias
String dayAsString(const Time::Day day) {
 switch (day){
  case Time::kSunday: return "Sunday";
  case Time::kMonday: return "Monday";
  case Time::kTuesday: return "Tuesday";
  case Time::kWednesday: return "Wednesday";
  case Time::kThursday: return "Thursday";
  case Time::kFriday: return "Friday";
  case Time::kSaturday: return "Saturday";
 }
 return "(unknown day)";
}
//Setup
void setup(){
 for (int CurrentDevice = 0; CurrentDevice < MaxNDevices; CurrentDevice++)
 {
  lc.shutdown(CurrentDevice, false);
  lc.setIntensity(CurrentDevice, MatrixB);
  lc.clearDisplay(CurrentDevice);
 }
 Serial.begin(9600);
 //Esta parte se usa para actualizar la hora.
 //rtc.writeProtect(false);
 //rtc.halt(false);
 //Time t(2016, 12, 7, 23, 57, 00, Time::kWednesday);
 //rtc.time(t);
}
//Loop
void loop() {
 printTime();
 delay(1000);
}

Lo primero que hacemos es incluir las librerías "LedControl.h" y "DS1302.h", creamos constantes para los pines que usara la primer matriz, para el reloj y después creamos los objetos "lc" y "rtc" para la matriz y para el reloj respectivamente.
El objeto lc se le asigna un objeto "LedControl"; el cual recibe cuatro parámetros: los números de pines a usar, el primer parámetro debe de ser el pin que se usará para conectará a "DIN" en la primer matriz, el segundo conectará a "CLK", el tercero conectará a "CS" y finalmente el cuarto, sirve para indicar cuantas matrices se usarán con ese objeto.
Para el reloj, crearemos un objeto "DS1302" y para "inicializarlo", le pasaremos como parámetros, los pines que se usarán. El primer parámetro es el pin que conecta a "RST", el segundo parámetro es el pin que conecta a "DAT" y finalmente el tercero es el pin que conecta "CLK".
Una vez que tenemos lo objetos listos, creamos unas variables, la primera es "MatrixB" que las usaremos para almacenar el brillo que tendrán las matrices, por ahora la única forma de cambiarlo, es editando el valor que le asignamos, pero un poco mas adelante, seremos capaces de cambiar el brillo de forma externa.

Después viene una matriz que contiene los datos que vamos a usar para crear los "números" que después veremos en las matrices, pero antes de eso hay que saber como es que se controlan los LED´s de las matrices.

LedControl y Matrices LED.

Para este proyecto estamos usando la librería LEdControl.h, está nos ofrece varias maneras de controlar los LED's de la matriz, las funciones disponibles son: setLed, setRow y setColum.
La función setLed tiene una forma similar a la siguiente:
void setLed(int addr, int row, int col, boolean state);
El primer parámetro indica en que dirección se encuentra la matriz que queremos usar (de cero a siete), es decir, si tenemos 4 matrices conectadas en "serie", y queremos prender el primer led de la tercera matriz, tendremos que poner un 3 en el primer parametro.
El segundo parámetro indica en que columna está el LED que queremos encender (mejor dicho controlar).
El tercero indica en que columna está el LED que queremos controlar.
El último parametro indica el estado en que vamos a poner el LED, cuando se pone "True", el LED se enciende y cuando se pone "False", el LED se apaga.
void setRow(int addr, int row, byte value);
Está funcion recibe 3 parametros, el primero es el mismo que el de la funcion anterior, el segundo indica que fila es la que se va controlar, despues, el ultimo parametro nos indica que LED´s son los que se van a prender.Para este último, hay una forma relativamente sencilla de cómo establecer los LED´s que deben de prender, tomando por ejemplo el numero 2 y el número 5 que queremos mostrar,
Como lucirá el numero 25
Antes que nada, es importante tomar en cuenta donde se ubica el origen, en mi caso (y supongo que para los que tienen un kit similar al que uso yo), el origen se encuentra en la esquina inferior izquierda, viendo la matriz como en la primer imagen, por lo que las columnas están en el lado izquierdo de la matriz y las filas en el lado inferior, entonces los LED's se controlaran de abajo hacia arriba y de izquierda a derecha si usamos la funcion "setRow". una vez que ya has determinado donde empieza la matriz, se deben de hacer los cambios pertinentes.
Para asignar el ultimo parámetro, miremos la imagen anterior, en ella se puede ver claramente que cuando un led está encendido tiene el valor de 1 y cuando está apagado es 0 y cada digito esta compuesto por 3 llamadas a la funcion "setRow", para hacer de forma rapida el dos, podriamos llamar a la función "setRow" y como ultimo parámetro el  valor "B00011101" en la primera llamada, en la segunda, el ultimo parametro "B00010101" y en la tercera llamada a la función usar cómo ultimo valor "B00010111".
Cómo los 0´s que están al lado izquierdo no son significativos, podemos omitirlos y simplemente poner "B11101" para la primer llamada a la funcion "setRow", si te fijas en el código, veras que hay algunos valores que conservan los ceros a la izquierda, esto solo es con fines "estéticos" y para que todos los valores estén alineados.

Mostrando dígitos en las matrices.

Para mostrar los dígitos en las matrices, he creado una función que se encarga de eso. La función "PrintNumber", recibe tres parámetros, el primero es el primer numero que queremos mostrar (al lado izquierdo), el segundo es el numero que queremos mostrar y finalmente,se debe de indicar en que numero de matriz vamos a despegarlos.
Todos los datos de los numero están pensados de tal forma que se puedan poner dos dígitos por cada matriz, entonces cada una nos da un rango de 00 a 99, pero para mostrar algo cómo la hora. los segundos o los minutos funciona. Para que la función "funcione", debemos de alinear los datos que componen a cada numero, si te fijas bien leemos de tres en tres y debido al acomodo que tiene la matriz "Numbers", cuando le pedimos el número 0, leerá los valores Numbers[0], Numbers[1] y Numbers[2].
La formula que dexcribe cómo se obtienen los valores del primer dígito es la siguiente:
Indice = NumeroDado * 3 +ValorDelIterador - 1
 Y para el segundo:
Indice = NumeroDado * 3 +ValorDelIterador - 5
Y con eso, podemos obtener los valores del 0 al 9 y a partir de ahi, componer cualquier numero. Por eso es importante el orden en que está cada parte de la matriz (arreglo desde este momento para evitar confusiones.).

Completando el resto del código.

La función "PrintTime" se encargará de obtener la hora y de llamar a la función "PrintNumber" para desplegar la información en la matriz LED, debido a que hay que pasar los dígitos por separado, no es posible usar "12" cómo parámetro para la función "PrintNumber", entonces lo que hacemos es dividir, primero entre 10 y para el segundo dígito, solo tomamos el residuo de la división, por lo que si tenemos el numero 21, al hacer la división entre 10, obtenemos 2; este lo pasamos cómo primer parámetro y luego obtenemos el residuo que es 1 y lo asignamos como valor del segundo parámetro, ademas de que la hora la mostramos en el monitor serie, eso es opcional pero para fines de debug funciona.
Después viene la parte de las funciones propias del arduino, "setup()" y "loop()", En la parte de setup, es donde vamos a inicializar los componentes (prácticamente aquí todo lo que se necesita ejecutar una sola vez), ademas hay unas lineas que están comentadas, estas solo sirven para ajustar la hora del reloj, si es la primera vez que vas a usar el modulo de reloj, o si cambias la batería o hiciste cualquier cosa y quieres establecer la fecha y hora, esas lineas las debes de des-comentar y editar solo la que contiene la fecha y hora, cuando hayas cargado el proyecto al arduino, si modificas alguna parte del código y ya no quieres actualizar la hora, debes de comentar de nuevo esas lineas para evitar que la hora se cambie de nueva cuenta.
Finalmente en "loop()" ponemos el código que se va a ejecutar de manera cíclica, en este caso, solo es la función que muestra la hora y un retraso de 1000ms que se usa para que la hora solo se actualice cada segundo.

Y bien, por ahora es todo, pero en el próximo post vamos a agregar un poco mas de hardware para que el uso sea mas sencillo. Cómo siempre, el código lo puedes bajar de mi dropbox para que lo revises.
Por ahora es todo. Los leo luego.

Vamos a programar #24 - Haciendo un instalador en NSIS #3 - Datos útiles.

EL día de hoy vamos a continuar con la creación de instalador en NSIS. En post anteriores, ya hemos visto alguna cosas útiles para incluirlas en el instalador. Para continuar  y hacer un instalador lo más presentable posible, veremos algunas cosas más que ayudaran a crear un buen instalador.


Tip #2 - Apéndice de todos los valores posibles para el des-instalador.

En el post anterior vimos que hay valores que agregandolos al registro de windows, mostraran información adicional, incluirlas o no ya es cuestión de cada quien, pero yo prefiero agregar al menos las mas primordiales, esto puede ayudar a que el usuario en determinado momento opte o no por quitar nuestra aplicación.

En la siguiente tabla se muestran todas las entradas posibles y que función cumplen, además de se muestra que tipo de entrada es.

Propiedad Informacion Tipo de entrada
AuthorizedCDFPrefix La direccion URL para actualizar la aplicacion REG_SZ
Comments Comentarios adicionales para mostrarse REG_SZ
DisplayIconEl icono que se mostrara para representar a la aplicacion. REG_SZ
DisplayName El nombre del programa a mostrarse REG_SZ
DisplayVersion La version instalada del programa REG_SZ
EstimatedSize Tamaño estimado del programa  (en KB) DWORD
HelpLink Direccion URL para el soporte REG_SZ
HelpTelephone Numero telefonico para ofrecer soporte REG_SZ
InstallDate Fecha de instalacion REG_SZ
InstallLocation Ubicacion del programa instalado REG_SZ
InstallSourceUbicacion de la fuente de instalacion REG_SZ
Language Idioma del programa REG_SZ
ModifyPath La ruta del programa de re-instalacion, reparacion REG_SZ
NoModifyNO muestra la opcion de "Modificar". DWORD
NoRepairNO muestra la opcion de "Reparar"DWORD
Publisher Nombre del publicador REG_SZ
Readme La ubicacion del archivo "LEAME" REG_SZ
UninstallString La ruta del des-instalador REG_SZ
URLInfoAboutLa direccion URL con informacion del programa REG_SZ
URLUpdateInfo La direccion URL con informacion de las actualizaciones REG_SZ
Version La version del programa (X.xx.xx) REG_SZ
VersionMajor La version Major del programa (x.XX.xx) REG_SZ
VersionMinor La version Minor del programa (x.xx.XX) REG_SZ


Para agregar una entrada del tipo REG_SZ o DWORD usando NSIS usaremos las siguientes instrucciones:


WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VEncoder" "HelpLink" "http://xworkforall.blogspot.com/2016/07/el-video-correcto-2.html"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VEncoder" "NoModify" 1

La instrucción WriteRegStr es las que se usará cuando se quieran escribir valores REG_SZ (normalmente cadenas de texto) y la instrucción WriteRegDWORD, escribirá los valores DWORD (números por lo general).


Ahora supongamos que queremos agregar un comentario al instalador que usamos cómo ejemplo (antes de continuar, si no has visto el código fuente del instalador de VEncoder 2 sugiero que lo descargues y le eches un vistazo). Para agregar los comentarios, necesitamos escribir un valor en el registro, retomado el código anterior , solo hay que usar la función que escribe un valor REG_SZ y escribir un valor llamado "Comments" y su contenido será el texto que queremos mostrar.

WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VEncoder" "HelpLink" "http://xworkforall.blogspot.com/2016/07/el-video-correcto-2.html"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VEncoder" "NoModify" 1
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VEncoder" "Comments" "Eres libre se usarlo y modificarloa tu gusto, pero no reclamarlo cómo tuyo"

Tras re-compilar el instalador y ejecutarlo, al buscar la información en el registro lucirá cómo se ve en la siguiente imagen:

Tip #3 - Asociando una extensión a nuestro programa.

Otra parte de nuestro instalador, es la posibilidad de que ciertos tipos de archivo se abran con nuestro programa. NSIS ayuda a hacer esta tarea de forma sencilla.


Retomando el ejemplo que se incluye en la descarga, al compilarlo y ejecutarlo, verás que todos los archivos con extensión VEPX, cambian y en lugar de aparecer el icono de archivo desconocido, aparece uno como el de la imagen anterior. Si hacemos doble clic en el archivo, se abrirá un cuadro de dialogo preguntándonos que programa queremos usar para abrir ese tipo de archivos.
Normalmente, queremos que cuando el usuario haga clic en ellos, se abra nuestro programa y a partir de ahi, trabajaremos con este archivo.
Para poder abrir directamente el archivo con nuestro programa, debemos de crear entradas en el registro de Windows.

HKEY_CLASSES_ROOT
   MyProgram.exe
      shell
         open
            command
               (Default) = C:\MyDir\MyProgram.exe /a "%1"
         print
            command
               (Default) = C:\MyDir\MyProgram.exe /a /p "%1"
         printto
            command
               (Default) = C:\MyDir\MyProgram.exe /a /p "%1" "%2"

El código anterior muestra cómo se deben de crear las entradas en el registro de windows.
Para eso vamos a agregar el siguiente código en NSIS:

WriteRegStr HKCR ".VEPX" "" "VEncoder.VEPX"
WriteRegStr HKCR "VEncoder.VEPX" "" "Archivo de perfil para Vencoder"
WriteRegStr HKCR "Vencoder.VEPX\DefaultIcon" "" "$INSTDIR\VEPX.ico" 
WriteRegStr HKCR "VEncoder.VEPX\shell\Open\" "" "Cargar en VEncoder 2"
WriteRegStr HKCR "VEncoder.VEPX\shell\Open\command" "" "$INSTDIR\Vencoder 2.exe"


Lo que hace el código anterior es lo siguiente (hay que recordar que todas las claves se crean en HKey Classes Root, HKCR):

  1. Creamos la entrada a la extensión ".VEPX", y en el valor predeterminado, escribimos que toda la información referente a esta extensión, estará en la clave "VEncoder.VEPX".
  2. Luego creamos la clave "Vencoder.VEPX" y al valor por default le asignamos el texto que describe nuestro archivo, en este caso usamos "Archivo de perfil para VEncoder"
  3. Luego asignamos el icono que queremos usar para representar a nuestros archivos. Debemos de crear una clave llamada "DefaultIcon" y al valor por default, le asignamos la ruta en donde se encuentra nuestro icono, archivo dll o exe que lo contenga; si se usa un archivo dll o exe, hay que indicar el indice (wscript.exe 4 por ejemplo).
  4. Para agregar items en el menú contextual de Windows, dentro de la clave "VEncoder.VEPX" y en general para cualquier programa que hayamos hecho, hay quie agregar una clave llamada "Shell". Para que la opción de abrir con nuestro programa sea la primera, debemos de agregar una sub-clave llamada "Open" y al valor por default, agregar el texto que queremos mostrar en el menú contextual.
  5. Para que la entrada anterior funcione, debemos de asignar que "cosa" debe de pasar cuando hagamos clic en el. Para el caso  concreto de VEncoder, solo abriremos la aplicación, por lo que creamos una nueva sub-clave llamada "command" y en el valor debemos de asignar el programa junto con los parámetros para su ejecución, retomando la estructura anterior al código, ahí se muestran como se deben de pasar los argumentos y el nombre de archivo, sera representado por "%1". Si VEncoder aceptara parámetros (cosa que no es así por el momento), la instrucción debería de quedar similar a la siguiente: '$INSTDIR\Vencoder 2.exe "%1"', usaríamos primero las comillas simples y después las comillas dobles.
El resultado del código anterior se verá cómo este:


Para agregar entradas adicionales, solo debemos de repetir los últimos dos paso, solo cambiando el nombre de la clave:
WriteRegStr HKCR ".VEPX" "" "VEncoder.VEPX"
WriteRegStr HKCR "VEncoder.VEPX" "" "Archivo de perfil para Vencoder"
WriteRegStr HKCR "Vencoder.VEPX\DefaultIcon" "" "$INSTDIR\VEPX.ico" 
WriteRegStr HKCR "VEncoder.VEPX\shell\Open\" "" "Cargar en VEncoder 2"
WriteRegStr HKCR "VEncoder.VEPX\shell\Open\command" "" "$INSTDIR\Vencoder 2.exe"
WriteRegStr HKCR "VEncoder.VEPX\shell\Editar\" "" "Esta es una prueba"
WriteRegStr HKCR "VEncoder.VEPX\shell\Editar\command" "" "$INSTDIR\Vencoder 2.exe"

Y el resultado será como el siguiente:

Igual que en los post anteriores, el código competo lo puedes descargar de aqui.
Y bien, por ahora es todo, dudas o comentarios, puedes ponerlos en la parte de abajo.

Los leo luego.