Xwork's Blog

The lord is waiting to take your hand.

Vamos a programar #77 - VEncoder 2.0.1

Hola de nuevo a todos, el día de hoy vamos a ver un poco mas sobre VEncoder en su versión 2.0.1.

Hace algún tiempo que tenía ganas de actualizar la aplicación, pero por una u otra cosa, no podía hacerlo, pero hace no mucho, conseguí un PS3 y cómo es costumbre, al intentar reproducir un vídeo de los que ya tengo guardados, la consola simplemente se rehusó a hacerlo porque "el archivo de medios no es compatible".

Si hacemos un poco de memoria, el pretexto para empezar con las expresiones regulares fue precisamente VEncoder 2, pero además, en uno de los muchos post dedicados al programa, alguien mencionó que no podía hacer la conversión de sus archivos.

¿2017 fue hace un año, verdad?

Para solucionar ese problema, decidí actualizar un poco la aplicación a la versión 2.1, no son muchos los cambios, pero ahora  por lo menos serás capaz de realizar la conversión de archivos e incluiré un par de archivos con las configuraciones compatibles para el PS3.

Ahora veamos un poco del código que cambió.

private void LoadProfile(string InXMLFile)
{
	using (XmlReader XMLR = new XmlTextReader(InXMLFile))
	{
		try
		{
			XMLR.ReadToFollowing("Name");
			TSSLblProfile.Text = XMLR.ReadElementContentAsString();
			XMLR.ReadToFollowing("WH");
			CboResolucion.Text = XMLR.ReadElementContentAsString();
			XMLR.ReadToFollowing("VBit");
			TXTVBit.Text = XMLR.ReadElementContentAsString();
			XMLR.ReadToFollowing("Ratio");
			CBORatio.Text = XMLR.ReadElementContentAsString();
			XMLR.ReadToFollowing("Profile");
			CBOProfile.Text = XMLR.ReadElementContentAsString();
			XMLR.ReadToFollowing("Nivel");
			CboLevel.Text = XMLR.ReadElementContentAsString();
			XMLR.ReadToFollowing("ASample");
			CboFreq.Text = XMLR.ReadElementContentAsString();
			XMLR.ReadToFollowing("ABit2");
			CBOBitsA.Text = XMLR.ReadElementContentAsString();
			XMLR.ReadToFollowing("Channel");
			CBOAChannels.Text = XMLR.ReadElementContentAsString();
			XMLR.ReadToFollowing("ExtraParams");
			//TXTExtraparams.Text = XMLR.ReadElementContentAsString();

		}
		catch
		{
			MessageBox.Show("Parece que el XML no es valido", "VEncoder 2.0.1", MessageBoxButtons.OK, MessageBoxIcon.Error);
		}

	}
}

private Single GetPositionFromFFline(string FFText)
{
	// (\d+)\.\d+x find speed
	try
	{
		if (FFText.StartsWith("FRAME", StringComparison.OrdinalIgnoreCase)) {
			Single CurrentValue = 0;
			MatchCollection wordColl = Regex.Matches(FFText, @"(\d+):(\d+):(\d+)?(.\d+)");			
			CurrentValue = (Single)TimeSpan.Parse(wordColl[0].ToString()).TotalSeconds;
			return CurrentValue;
		}else{
			return 1;
		}
	} catch (Exception) {
		return 1;
	}
}

private string GetSpeedFromFFLine(string FFText)
{
	try
	{
		if (FFText.StartsWith("FRAME", StringComparison.OrdinalIgnoreCase))
		{
			string CurrentValue = "0x";
			MatchCollection wordColl = Regex.Matches(FFText, @"((\d+)(\.\d+)?)x");
			CurrentValue = wordColl[0].ToString();
			return CurrentValue;
		}
		else
		{
			return "???x";
		}
	}
	catch (Exception)
	{
		return "!!!x";
	}
}

private string BuildAudioCommand( string AudioCodec, string AudioBitRate = "", string AudioFreq = "", string Channels = "")
{
	if (string.Compare(AudioCodec,"copy",true) == 0){
		return " -c:a copy ";
	}else{
		string TempCommand = " -c:a " + AudioCodec;
		if (string.Compare(AudioBitRate, "copy", true) == -1)
			TempCommand += " -b:a " + AudioBitRate + "k";

		if (string.Compare(AudioFreq, "copy", true) == -1)
			TempCommand += " -ar " + AudioFreq ;

		if (string.Compare(Channels, "copy", true) == -1)
			TempCommand += " -ac " + Channels;
		return TempCommand;
	}

}
private string BuildVideoCommand(string VCodec,string VSize = "", string VBit = "", string VAspect = "",string VProfile = "", string VLevel = "", string VFPS = "")
{
	if (string.Compare(VCodec, "copy", true) == 0)
	{
		return " -c:v copy";
	}
	else
	{
		string TempCommand = " -c:v " + VCodec;
		if (string.Compare(VSize, "copy", true) == -1)
			TempCommand += " -s " + VSize;

		if (string.Compare(VBit, "copy", true) == -1)
			TempCommand += " -b:v " + VBit +"k";

		if (string.Compare(VAspect, "copy", true) == -1)
			TempCommand += " -aspect " + VAspect;

		if (string.Compare(VProfile, "copy", true) == -1)
			TempCommand += " -profile:v " + VProfile;

		if (string.Compare(VLevel, "copy", true) == -1)
			TempCommand += " -level " + VLevel;

		if (string.Compare(VFPS, "copy", true) == -1)
			TempCommand += " -r " + VFPS;

		return TempCommand;
	}
}

private string BuildCommandLine(string InFile,string VCodec,string VideoSize,string VideoBit,string VAspect,string VProfile, string VLevel,string FrameRate, string ACodec ,string AudioBit,
								string AudioFreq, string AChannels,string OutPath,bool ForFirstPass= false,bool AfterPass1=false,bool SinglePass= true)
{
	if (ForFirstPass){
		return string.Concat("-y -i ", "\"", InFile, "\"", BuildVideoCommand(VCodec, VideoSize, VideoBit, VAspect, VProfile, VLevel, FrameRate), " -threads 0  -pass 1 -an ", " \"", OutPath, "\\", GetFileName(InFile), ".mp4", "\"");
	}
	if(AfterPass1){
		return string.Concat("-y -i ", "\"", InFile, "\"", BuildVideoCommand(VCodec, VideoSize, VideoBit, VAspect, VProfile, VLevel, FrameRate), " -threads 0 -f mp4 -pass 2", BuildAudioCommand(ACodec, AudioBit, AudioFreq, AChannels), " \"", OutPath, "\\", GetFileName(InFile), ".mp4", "\"");
	}
	if (SinglePass) {
		return string.Concat("-y -i ", "\"", InFile, "\"", BuildVideoCommand(VCodec, VideoSize, VideoBit, VAspect, VProfile, VLevel, FrameRate), " -threads 0 -f mp4", BuildAudioCommand(ACodec, AudioBit, AudioFreq, AChannels), " \"", OutPath, "\\", GetFileName(InFile), ".mp4", "\"");
	}else{
		return "Test";
	}
}

El primer procedimiento en ser modificado fue "LoadProfile()", se le hizo una pequeña corrección, ya que si se cargaba un archivo XML no valido, hacia que la aplicación se detuviera, para arreglarlo, simplemente se agregaron los bloques "try" y "catch", ahora sí se carga un archivo no valido, simplemente se notifica y la aplicación continúa con normalidad.

La siguiente función en ser modificada fue "GetPositionFromFFline" aquí es donde hacemos uso de las expresiones regulares. Cuando FFMpeg está realizando una conversión, informa cual es el progreso usando una linea con aspecto similar al que sigue:
"frame=  553 fps=9.8 q=0.0 size=  160256kB time=00:00:18.53 bitrate=70848.2kbits/s dup=111 drop=0 speed=0.329x".


Y el tiempo estará formateado de la siguiente manera: "HH: MM:SS.CC", pero hay que tener en cuenta que las centésimas de segundo, solo se mostraran si son significativas, por lo que se tenemos algo cómo 00:00:22.10 se mostrará así: 00:00:22.1. Y si tenemos algo cómo 01:01:22.00 se mostrará así: 01:01:22. Tomando en cuenta las consideraciones anteriores, podemos crear una expresión regular cómo la que sigue:
(\d+):(\d+):(\d+)?(.\d+)

Con eso podremos leer el tiempo de manera un poco mas precisa.

Una nueva función que agregué es "GetSpeedFromFFLine()", cómo su nombre lo indica, sirve para leer a que velocidad se está realizando la conversión. Para encontrar el valor, hacemos uso de la siguiente expresion regular:

((\d+)(\.\d+)?)x

El patron anterior tratara de encontrar cualquier cadena que tenga la siguiente forma: D.DDx, pero al igual que el tiempo, solo se muestran los valores más significativos, por lo que si tenemos 10.10x de velocidad, FFMPeg solo devolverá 10.1x. Cuando la se excede cierta velocidad, el valor estará en notación científica, por ejemplo 1e3x, pero no lo implemente ya que no logre ver este tipo de notación cuando realizaba las conversiones (a mi tostadora con FX4100 ya le cuesta), por lo que si notas que el programa devuelve "!!!x" o "???x" muy probablemente sea a esto.

Otro de los cambios que se hicieron fue en las funciones que se usan para crear la linea de comandos, anteriormente estaba limitada (y ahora también pero no tanto) y tareas que requerían solo codificar el stream de video pero no el de audio (y viceversa) no se podían hacer. Ahora gracias a las dos nuevas funciones "BuildVideoCommand" y "BuildAudioCommand"  en adición a la función que ya teníamos "BuildCommandLine" hacen que la conversión se realice aun si no todos los parámetros son requeridos. Imaginemos que tenemos un video con el audio de buena calidad y no queremos convertirlo porque probablemente pierda calidad o no sabemos cuantos canales posee, ahora cuando encontramos una situación similar, simplemente podemos escoger o escribir "copy" en el cuadro de texto del parámetro y las funciones ignoraran ese parámetro. FFMpeg en algunos casos por default escoge los mismos valores que el origen, así si no sabemos o no queremos cambiar ese valor, ya no tendremos que preocuparnos y todo eso aplica para los parámetros que NO sean: "Velocidad de bits (Video)", "Velocidad de bits (audio)", "Codec de video" y "Codec de audio". Para el caso de la velocidad de bits de video y de audio, se tiene que asignar un valor, ya que en el caso del video, cuando no se especifica un valor, el valor es 200k (que hace parecer cualquier cosa una película hecha con bloques; por no decir una marca) y para el caso del audio es igual. Cuando se escoge "copy" en los codecs, simplemente se leerá el stream y se pasara al archivo de salida, es decir no se realizara ninguna conversion y todos los otros parámetros  serán ignorados. Esto es especialmente útil cuando tenemos archivos MKV que sabemos que son compatibles.

Y bien, por ahora es todo, en los siguientes post continuaremos con más de FFMpeg y cómo agregar mas funcionalidades a VEncoder, actualmente hay un par de errores que son vitales corregir (pero que no son tan comunes), cualquier error que encuentres siéntete libre comentarlo. Por ahora solo esta disponible el ejecutable para la descarga (cómo de costumbre desde mi dropbox), el código lo publicaré cuando soluciones los "detalles", pero bastará con agregar y reemplazar la funciones con las que se publicaron en este post al código viejo.

Los leo luego.

Vamos a programar #76 - Neo Clock View.

Hola de nuevo a todos, el día de hoy vamos a ver un poco de algo a lo que decidí llamar "Neo Clock View". Para fabricarlo vamos a hacer uso de los displays de siete segmentos que vimos en el post anterior.

En la versiones anteriores de Clock View, usamos el módulo DS1302. En esta ocasión haremos uso del modulo DS1307 y haremos una versión solo para probar si los "displays" funcionan cómo esperamos.

Antes de comenzar, deberás de instalar la librería para poder usar el modulo de reloj de forma sencilla. La puedes descargar del GitHub del dueño.

Una vez instalada, vamos a realizar las conexiones. Para esto simplemente conectamos de la siguiente manera.

  • SDA - Al pin A4 del arduino.
  • SCL - Al pin A5 del arduino.
  • VCC - A 5v del arduino.
  • GND - A GND del arduino.
Con las conexiones hechas ahora revisemos el código.

//Reloj
// A4 - SDA
// A5 - SCL
#include <FastLED.h>
#define NUM_LEDS 126
#define DATA_PIN 9
#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>

CRGB leds[NUM_LEDS];
int XC = 0;

void setup() {
	// Serial.begin(9600);
	// while (!Serial) ;
	// delay(200);
	LEDS.addLeds<WS2812,DATA_PIN,RGB>(leds,NUM_LEDS);
	LEDS.setBrightness(10);
}

void PrintNumber(int Value, int Digit, int Green, int Red, int Blue){
	for(int i = 0; i < 7; i++) {
		switch (Value)
		{
			case 0:
				if (i == 6){
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(0, 0, 0);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(0, 0, 0);
					leds[i * 3 + (Digit * 21)].setRGB(0, 0, 0);
				}else{
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[(i*3) + 1 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[i*3+ (Digit * 21)].setRGB(Green, Red,Blue);		
				}
				FastLED.show(); 
				break;
			case 1:
				if (i == 0 || i == 1){
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[i * 3 + (Digit * 21)].setRGB(Green, Red,Blue);			
				}else{
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(0,0,0);
					leds[(i*3) + 1 + (Digit * 21)].setRGB(0,0,0);
					leds[i * 3 + (Digit * 21)].setRGB(0,0,0);
				}
			FastLED.show();
			break;
			case 2:
				if (i == 0 || i == 3){
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(0,0,0);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(0,0,0);
					leds[i * 3 + (Digit * 21)].setRGB(0,0,0);
				}else{
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[i * 3 + (Digit * 21)].setRGB(Green, Red,Blue);
				}
			FastLED.show();
			break;
			case 3:
				if (i == 3 || i == 4){
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(0,0,0);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(0,0,0);
					leds[i * 3 + (Digit * 21)].setRGB(0,0,0);
				}else{
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[i * 3 + (Digit * 21)].setRGB(Green, Red,Blue);
				}
			FastLED.show();
			break;
			case 4:		
				if (i == 2 || i == 4 || i == 5){
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(0,0,0);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(0,0,0);
					leds[i * 3 + (Digit * 21)].setRGB(0,0,0);
				}else{
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[i * 3 + (Digit * 21)].setRGB(Green, Red,Blue);
				}
			FastLED.show();
			break;
			case 5:
				if (i == 1 || i == 4){
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(0,0,0);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(0,0,0);
					leds[i * 3 + (Digit * 21)].setRGB(0,0,0);
				}else{
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[i * 3 + (Digit * 21)].setRGB(Green, Red,Blue);
				}
			FastLED.show();
			break;
			case 6:
				if (i == 1){
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(0,0,0);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(0,0,0);
					leds[i * 3 + (Digit * 21)].setRGB(0,0,0);
				}else{
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[i * 3 + (Digit * 21)].setRGB(Green, Red,Blue);
				}
			FastLED.show();
			break;
			case 7:
				if (i == 3 || i == 6 || i == 4 || i == 5){
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(0,0,0);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(0,0,0);
					leds[i * 3 + (Digit * 21)].setRGB(0,0,0);
				}else{
					leds[(i * 3)+ 2 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[(i * 3)+ 1 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[i * 3 + (Digit * 21)].setRGB(Green, Red,Blue);
				}
			FastLED.show();
			break;
			case 8:
				leds[(i * 3) + 2 + (Digit * 21)].setRGB(Green, Red,Blue);
				leds[(i * 3) + 1 + (Digit * 21)].setRGB(Green, Red,Blue);
				leds[i * 3 + (Digit * 21)].setRGB(Green, Red,Blue);
			FastLED.show();
			break;
			case 9:
				if (i == 4){
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(0,0,0);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(0,0,0);
					leds[i * 3 + (Digit * 21)].setRGB(0,0,0);
				}else{
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[i * 3 + (Digit * 21)].setRGB(Green, Red,Blue);
				}
			FastLED.show();
			break;
			default:		
				if (i == 0 || i == 1){
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(0,0,0);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(0,0,0);
					leds[i * 3 + (Digit * 21)].setRGB(0,0,0);
				}else{
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[i * 3 + (Digit * 21)].setRGB(Green, Red,Blue);
				}
			FastLED.show();
			break;
		}
	}
}

void ShowIntro(){
	for(int i = 0; i < NUM_LEDS; i++) {
		leds[i] = CHSV(255, 255, 255);
		FastLED.show(); 
		delay(5);
	}
	Intro = false;
}

void PrintTime(int Hour, int Minutes, int Seconds){
	if(Seconds < 10){
		PrintNumber(Seconds,0,255,0,0);
		PrintNumber(0,1,255,0,0);
	}else{
		PrintNumber(Seconds % 10, 0, 255, 0, 0);
		PrintNumber(Seconds / 10, 1, 255, 0, 0);
	}
	
	if(Minutes < 10){
		PrintNumber(Minutes,2,0,255,0);
		PrintNumber(0,3,0,255,0);
	}else{
		PrintNumber(Minutes % 10, 2, 0, 255, 0);
		PrintNumber(Minutes / 10, 3,0 , 255, 0);
	}
	
	if(Hour < 10){
		PrintNumber(Hour,4,0,0,255);
		PrintNumber(0,5,255,0,0);
	}else{
		PrintNumber(Hour % 10, 4, 0, 0,255);
		PrintNumber(Hour / 10, 5, 0, 0,255);
	}
}

tmElements_t tm;
bool Intro = true;

void loop() {
	
	if (Intro == true) ShowIntro();

	if (RTC.read(tm)) {
		PrintTime(tm.Hour,tm.Minute,tm.Second);
	} else {
		if (RTC.chipPresent()) {
			PrintTime(1025,1024,1023);
			// Serial.println("The DS1307 is stopped.  Please run the SetTime");
			// Serial.println("example to initialize the time and begin running.");
			// Serial.println();
		} else {
			// Serial.println("DS1307 read error!  Please check the circuitry.");
			// Serial.println();
		}
		delay(9000);
	}
	delay(100);
}

El código consta de tres funciones. La primera de ellas en orden de aparicion es "PrintNumber", hay que recordar que diseñamos los segmentos de forma tal; que cada tres LED's representan un segmento. La función recibe cinco parámetros. El primero es el valor que el digito tendrá y técnicamente puede ser cualquier valor (dentro del tipo "integer"), pero debido a que solo podemos mostrar valores dentro del rango 0 ~ 9, deberíamos pasar valores que no excedan nueve. El siguiente parámetro debe de ser un valor que nos indicara que digito es el que queremos usar, dado que empezamos de derecha a izquierda, si decimos que queremos usar el digito uno, estaríamos usando el del lado derecho.
Los ultimos tres parametrosn, son los valores que se usaran para cada color empezando por el rojo, luego el verde y finalmente el azul. El valor para cada uno de estos deberá de estar entre los valores 0 ~ 255, y mezclandolos, podemos crear cualquier color.

la siguiente función es "ShowIntro()", esta función sirve para hacer una animación que recorrerá cada un de los LED de inicio a fin, solo se hará una vez y sirve para demostrar de que otra forma se pueden controlar los LED's.

La siguiente función es "PrintTime()". Recibe tres parámetros del tipo "int", el primero es el valor que representan las horas, el segundo es un valor que representa a los minutos y finalmente el tercero es un valor que representa a los segundos. dentro de la función se usan tres condiciones; la primera para los segundos; cuando se le hace la consulta del tiempo al modulo de reloj, si el tiempo es menor a diez segundos, este devolverá un solo dígito; es decir, si el tiempo tiene solo siete segundos en lugar de devolver "07" que seria el valor que esperamos, solo nos regresaría "7". La comprobación sirve para cuando sucede este caso. Para solucionarlo, si el tiempo (segundos, minutos u horas) es menor a 10, llamamos a la función "PrintTime()" dos veces y en la primera llamada, le pasamos como primer parámetro el valor de los segundos y en el segundo parámetro el valor "0" que indica que queremos usar la primer matriz (el orden ya lo había dicho). Para la segunda llamada, le pasaremos el valor de "0" cómo primer parámetro y "1" cómo segundo, así cuando pase el caso de siete segundos (por ejemplo) tendremos un número para mostrar en el segundo display. Cuando es el caso contrario o mejor dicho cuando los valores son mayores o iguales a diez, lo único que hacemos dividir y en la primera llamada pasamos el resultado del modulo del valor actual (segundos, minutos u horas) y diez. Para la segunda llamada simplemente dividimos en valor entre diez y esto nos dará el valor que debemos de mostrar en la segunda llamada.

Dentro del bucle principal lo primero que hacemos es llamar a la función "ShowIntro" valiendonos de un flag llamado "Intro" que se inicializa en "true", la condición dice que cuando esta variable es verdadero se hace la animación, al se una variable global, cuando la animación acaba, cambia el valor de la variable "Intro" a "false" por lo que la animación solo se produce una sola vez. Acabada la animación, usamos las funciones de la libreria del reloj. Primero comprobamos si se puede leer desde el módulo, si es así llamamos a la función "PrintTime()" con las horas; "tm.Hour", los minutos; "tm.Minute", y los segundos "tm.Seconds". Con esto mostramos la hora en los "displays". Si no se puede obtener la hora del módulo, se trata de comprobar si el chip está disponible, si es así solamente bastará con subir el ejemplo que viene en la librería (disponible en Ejemplos >> DS1307RTC >> SetTime). Solo tendremos que subirlo antes que este código y ya tendrá la hora ajustada.

Y bien, por ahora es todo, en el próximo post veremos cómo mejorar un poco más el reloj, aunque creo que lo ideal seria usar el código de clock view y solamente adaptar las partes que necesitemos.

Los leo luego.

Vamos a programar #75 - Display de 7 segmentos y arduino.

Hola de nuevo a todos, el día de hoy vamos a ver cómo usar un display de 7 segmentos con arduino.

Si tes has dado vueltas por el blog, sabrás que tengo cierta fijacion por las luces parpadeantes. Cualquier tipo de aplicacion que use LED's llamará mi atención sin dudarlo. Hace algún tiempo hice un reloj llamado "clock view" que usaba una matriz de 8 x 8 LED's.


Y si bien funciona bastante bien, no se acerca al estilo del display de 7 segmentos que le da un toque muy especial a la apariencia del reloj.


Pero cómo conseguir display de siete segmentos en mi país es muy caro; y solo para ponerlo en perspectiva, sale casi tan caro conseguir ocho segmentos de 4 * 2 pulgadas que comprar un monitor de PC de 17 pulgadas; por lo que hay que recurrir a alternativas un poco más accesibles.

Una alternativa es usar diodos LED e intentar simular los dígitos, pero usar simples diodos, no le da mucho atractivo a la aplicación.

Para solucionarlo, recientemente adquirí unos cuantos LED's con un chip ws2812b y el día de hoy voy a mostrar un pequeño fragmento de código que se encargara de hacer lucir los LED's cómo si fueran los de siete segmentos, pero con la gran ventaja de que estos pueden soportar colores de hasta ocho bits por lo que una gran gama puede ser creada.

Para empezar, crearemos las conexiones. Para cada dígito usaremos 21 LED's ws2812b, suelen venir en tiras o por si acaso, puedes ver el esquema y conseguirlos de forma individual, el proyecto esta guardado en easy eda y puedes descargarlo aquí. Pero prácticamente solo hay que conectar un pin del arduino a "DIN" del LED y DOUT a la entrada "DIN" del próximo LED y asi sucesivamente hasta completar; en este caso; los 21 LED's que necesitaremos. También conectaremos VCC a 5v (aunque dependiendo del modelo, puede ser hasta 12v) y GND a tierra.



Con las conexiones hechas, pasemos a ver cómo implementar código en arduino para poder usar los LED's cómo si fueran segmentos.


void PrintNumber(int Value, int Digit, int Green, int Red, int Blue){
	for(int i = 0; i < 7; i++) {
		switch (Value)
		{
			case 0:
				if (i == 6){
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(0, 0, 0);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(0, 0, 0);
					leds[i * 3 + (Digit * 21)].setRGB(0, 0, 0);
				}else{
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[(i*3) + 1 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[i*3+ (Digit * 21)].setRGB(Green, Red,Blue);		
				}
				FastLED.show(); 
				break;
			case 1:
				if (i == 0 || i == 1){
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[i * 3 + (Digit * 21)].setRGB(Green, Red,Blue);			
				}else{
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(0,0,0);
					leds[(i*3) + 1 + (Digit * 21)].setRGB(0,0,0);
					leds[i * 3 + (Digit * 21)].setRGB(0,0,0);
				}
			FastLED.show();
			break;
			case 2:
				if (i == 0 || i == 3){
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(0,0,0);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(0,0,0);
					leds[i * 3 + (Digit * 21)].setRGB(0,0,0);
				}else{
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[i * 3 + (Digit * 21)].setRGB(Green, Red,Blue);
				}
			FastLED.show();
			break;
			case 3:
				if (i == 3 || i == 4){
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(0,0,0);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(0,0,0);
					leds[i * 3 + (Digit * 21)].setRGB(0,0,0);
				}else{
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[i * 3 + (Digit * 21)].setRGB(Green, Red,Blue);
				}
			FastLED.show();
			break;
			case 4:		
				if (i == 2 || i == 4 || i == 5){
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(0,0,0);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(0,0,0);
					leds[i * 3 + (Digit * 21)].setRGB(0,0,0);
				}else{
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[i * 3 + (Digit * 21)].setRGB(Green, Red,Blue);
				}
			FastLED.show();
			break;
			case 5:
				if (i == 1 || i == 4){
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(0,0,0);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(0,0,0);
					leds[i * 3 + (Digit * 21)].setRGB(0,0,0);
				}else{
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[i * 3 + (Digit * 21)].setRGB(Green, Red,Blue);
				}
			FastLED.show();
			break;
			case 6:
				if (i == 1){
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(0,0,0);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(0,0,0);
					leds[i * 3 + (Digit * 21)].setRGB(0,0,0);
				}else{
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[i * 3 + (Digit * 21)].setRGB(Green, Red,Blue);
				}
			FastLED.show();
			break;
			case 7:
				if (i == 3 || i == 6 || i == 4 || i == 5){
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(0,0,0);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(0,0,0);
					leds[i * 3 + (Digit * 21)].setRGB(0,0,0);
				}else{
					leds[(i * 3)+ 2 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[(i * 3)+ 1 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[i * 3 + (Digit * 21)].setRGB(Green, Red,Blue);
				}
			FastLED.show();
			break;
			case 8:
				leds[(i * 3) + 2 + (Digit * 21)].setRGB(Green, Red,Blue);
				leds[(i * 3) + 1 + (Digit * 21)].setRGB(Green, Red,Blue);
				leds[i * 3 + (Digit * 21)].setRGB(Green, Red,Blue);
			FastLED.show();
			break;
			case 9:
				if (i == 4){
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(0,0,0);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(0,0,0);
					leds[i * 3 + (Digit * 21)].setRGB(0,0,0);
				}else{
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[i * 3 + (Digit * 21)].setRGB(Green, Red,Blue);
				}
			FastLED.show();
			break;
			default:		
				if (i == 0 || i == 1){
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(0,0,0);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(0,0,0);
					leds[i * 3 + (Digit * 21)].setRGB(0,0,0);
				}else{
					leds[(i * 3) + 2 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[(i * 3) + 1 + (Digit * 21)].setRGB(Green, Red,Blue);
					leds[i * 3 + (Digit * 21)].setRGB(Green, Red,Blue);
				}
			FastLED.show();
			break;
		}
	}
}

Para usar los LED's con el chip ws2812b, haremos uso de la libreria FASTLED que puedes descargar desde su sitio en Github. Una vez instalada, importamos la librería e inicializamos la cantidad de LED's que vamos a usar, seguido del PIN del arduino que usaremos; en este caso el PIN número 9. En este caso en particular, los calculos están hecho para cuando queremos usar tres LED's por segmento (que se ve bien a mi gusto), pero si quieres hacer uso de un número mayor o menor el código deberá de ser modificado, pero está construido de tal forma que se entiende fácilmente.

Ya dentro del iterador, se crea un "case" para cada dígito y se dejan apagados o encendidos los LED's dependiendo del caso. Hay que tomar en cuenta donde es el punto de inicio. En este caso está en la esquina inferior derecha y sigue hacia arriba, luego a la izquierda, luego a la derecha y finalmente llega a la parte media en donde igual el primer LED del segmento estará ubicado a la derecha. Si miras la siguiente imagen, podrás apreciar el orden si sigues la numeración.


Y bien, por ahora es todo, en el siguiente post veremos cómo es que se usa la función que creamos y en el transcurso de la semana, arreglaré el diseño del PCB para que se vea mejor y se pueda expandir (si bien recuerdo hasta 500 LED's se pueden usar antes de agotar la memoria del Arduino) y además crearemos un nuevo reloj llamado Neo Clock View usando seis de estos "displays".

Los leo luego.

Cumplimos 3 años!!

Hola de nuevo a todos, el día de hoy solo escribo para recordar que un día cómo hoy pero de hace tres años, surgió el blog de XWork. Ha sido un camino largo pero gracias a todos los que leen es que ha podido avanzar tanto.



A diferencia de los años anteriores, la celebración solo se limitara a este post, pero vendrán proyectos que son dignos de la playstore, pero que serán totalmente open source para que hagas con ellos lo que quieras.

Hago un recordatorio amable de que buscó gente que me ayude con los post, en mi situación actual es un poco difícil publicar los post, pero siempre tengo la disposición, además de que algunos elementos tales cómo: estilos css, temas para el blog e iconos para los programas podrían ser  mejorados un poco.

Y bien, por ahora es todo, en el siguiente post continuaremos con mas de lo que nos compete.

Gracias nuevamente y los leo luego.

Vamos a programar #74 - Message Sender 1.0.1.

Hola de nuevo a todos, el día de hoy vamos a continuar con Message Sender Windows que es una actualización de Message Sender que se público ya hace algún tiempo.


En el post anterior vimos cómo es que podemos obtener el titulo de una ventana si estamos usando Windows. Si fuiste de los que intento unir todo para que funcionara, lo primero que notaste es que la versión anterior usaba las matrices de LED de 8x8 y en esta ocasión usaremos una pantalla LCD (de acuerdo a lo que pidieron).

El código en C#.


Primero veamos el código en C# para la aplicación de Windows.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace MessageSenderWindows
{
	public partial class FrmMain : Form
	{
		[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		static extern IntPtr GetForegroundWindow();
		[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
		[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		static extern int GetWindowTextLength(IntPtr hWnd); 

		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);
			}

		}

		private string GetCaptionOfActiveWindow()
		{
			string strTitle = string.Empty;
			IntPtr handle = GetForegroundWindow();
			int intLength = GetWindowTextLength(handle) + 1;
			StringBuilder stringBuilder = new StringBuilder(intLength);
			if (GetWindowText(handle, stringBuilder, intLength) > 0)
			{
				strTitle = stringBuilder.ToString();
			}
			return strTitle;
			}

		public FrmMain()
		{
			InitializeComponent();
		}
		private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
		{
			SerialPort sp = (SerialPort)sender;
			string indata = sp.ReadExisting();
			if (indata.Contains("\r\n"))
			{
				ArduinoMessage(GetCaptionOfActiveWindow());

			}
		}

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

		private void BtnConnect_Click(object sender, EventArgs e)
		{
			try
			{
				if (Port.IsOpen == false)
				{
					Port.BaudRate = (int)(NUDSpeed.Value);
					Port.PortName = cboportname.Text;
					Port.Open();
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		private void BtnDis_Click(object sender, EventArgs e)
		{
			if (Port.IsOpen == true)
			{
				Port.Write("Se ha desconectado el cliente");
				Port.Close();
			}	
		}

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

La aplicacion usa varias funciones y procedimientos; en orden de aparacion hacen:


  • "GetForegroundWindow()". Obtiene el "handle" de una ventana.
  • "GetWindowText()". Obtiene el texto de una ventana.
  • "GetWindowTextLength()". Obtiene la longitud del texto de una ventana.
  • "ArduinoMessage()". Intenta escribir datos en un puerto serie. Recibe un parámetro del tipo "string" que es el mensaje que se quiere escribir.
  • "GetCaptionOfActiveWindow()". Esta función sera la encargada de obtener el texto de la ventana que se encuentre activa. Regresa un valor del tipo "string" que representa el título de la ventana con el foco.
  • "DataReceivedHandler()". Este procedimiento es el encargado de procesar el evento "DataReceived" que se genera desde el objeto "Port". Recibe dos parámetros, el primero del tipo "object" que sera el objeto del tipo "Port" que desencadeno el evento, el segundo es un valor del tipo "EventArgs" que contienen algunos datos sobre el evento (que por ahora no usamos).
Con lo anterior tendremos una aplicación que luce mas o menos asi:
Una version reducida de la primer version.

El código para arduino.

El código para arduino es un tanto diferente al de la primer versión por el simple hecho de que usamos una pantalla LCD en lugar del las matrices LED. Lo primero que tenemos que tomar en cuenta es cómo se va a conectar la pantalla. En el post sobre el velocímetro hicimos uso de la pantalla de 16x2 y la conectamos directamente al arduino, pero ahora usaremos una pantalla de 20x4, pero además usaremos un adaptador al que se conectará la pantalla y nos permitirá utilizar I2C para hacer el intercambio de datos.

Lo primero que debemos de hacer es instalar la librería, una vez hecho esto, es importante tener en cuenta que el código está diseñado para una PANTALLA DE 20X4, por lo que si usas una diferente deberás de hacer los ajustes pertinentes (igual incluyo los comentarios que creo son pertinentes). Con todo lo anterior dicho, veamos el código fuente de la aplicación para arduino.

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
//SCL - A5
//SDA - A4
LiquidCrystal_I2C lcd(0x27,20,4);  // set the LCD address to 0x27 for a 16 chars and 2 line display
char Texto[80];

void setup() {
  lcd.init();     
  lcd.backlight();
  Serial.begin(9600);
}
// la mayor parte las pantallas lcd vienen en tamaños de 16x2 o 20x4
// para que la funcion "funcione" en la version mas pequeña, debemos de hacer los calculos
// en  multiplos de 16
void MoveText(String Message){
	lcd.clear();
	if (Message.length() < 21)
		lcd.print(Message);
	if (Message.length() > 20 && Message.length() < 41)
	{
		lcd.setCursor(0 , 0);
		lcd.print(Message.substring(0 , 20));
		lcd.setCursor(0 , 1);
		lcd.print(Message.substring(20 , Message.length()));
	}
	if (Message.length() > 40 && Message.length() < 61)
	{
		lcd.setCursor(0 , 0);
		lcd.print(Message.substring(0 , 20));
		lcd.setCursor(0 , 1);
		lcd.print(Message.substring(20 , 40));
		lcd.setCursor(0 , 2);
		lcd.print(Message.substring(40 , Message.length()));
	}
	if (Message.length() > 60)
	{
		lcd.setcursor(0 , 0);
		lcd.print(Message.substring(0 , 20));
		lcd.setCursor(0 , 1);
		lcd.print(Message.substring(20 , 40));
		lcd.setCursor(0 , 2);
		lcd.print(Message.substring(40 , 60));
		lcd.setCursor(0 , 3);
		lcd.print(Message.substring(60 , Message.length()));
	}
	delay(1500); 
	Serial.println("\0\0");	
}

void loop() {
	int i = 0;
	if (Serial.available()) {
		while (Serial.available() > 0) {
			Texto[i] = Serial.read();
			i++;
		}
		Texto[i] = '\0';

	}
	MoveText(Texto);
}


La aplicación consta de solo una función "MoveText()", en está función es donde se procesa todo el texto y está es la parte a la que debemos de prestar atención. Al iniciar nos encontramos con un "if" que sirve  para comprobar si la longitud del texto es mayor a 20 pero si la pantalla fuera de 16x2 por ejemplo; deberíamos de hacer la comprobación con el número 16, esto para que el texto se muestre en orden (aunque creo que en la pantalla de 16x2 no hay mayor problema, después veremos un poco de cómo es que funciona la pantalla), pero en el caso de la pantalla de 20x4, lo que haremos es separar el texto de tal forma que solo haya 20 caracteres por linea. Si viste el post anterior, notarás que la pantalla hacia un efecto de scroll, pero decidí omitirlo porque tardaba mucho y para no complicar tanto el proyecto, simplemente opté por la solución sencilla.

Y bien, por ahora es todo, en futuros posts veremos una explicación un poco más detallada sobre las pantallas de este tipo. Cómo de costumbre el código fuente lo puedes descargar de mi dropbox para probarlo y no dudes en modificarlo si tienes alguna idea.

Los leo luego.

Vamos a programar #73 - Message Sender 1.0.1 (POC)

Hola de nuevo a todos, el día de hoy vamos a ver una pequeña actualización para Message Sender.

El otro día mientras revisaba los mensajes en twitter, vi uno de alguien que me comentó que quería hacer uso de Message Sender, pero en vez de solo visualizar la información de ITunes, quería saber cual era la ventana que tenia el foco. En windows se dice que una ventana "tiene el foco" cuando está activa, es decir la ventana que estamos usando.
La ventana de Chrome es la que "tiene el foco"
La idea me pareció interesante así que revise el código que me envió. El código que veremos no es mio, yo solamente haré la correcciones para que funcione.

En este post veremos cómo es el código en C# que se usa para obtener el nombre de la ventana que es la que está activa.

Para poder obtener la información que necesitamos, recurriremos a la API de windows. ya que es muy extensa y me da flojera memorizar toda (si hay alguien que sepa TODA la API de windows con gusto le invito una cerveza), podemos buscar en la página de pinvoke.NET, aquí hay mucha de la API ya documentada con algunos ejemplo que nos pueden resultar de utilidad.

En este caso vamos a necesitar la función "GetWindowText()" que cómo su nombre lo sugiere, sirve para obtener el titulo de la ventana activa. Está funcion se encuentra en la biblioteca "user32.dll" y para poder acceder a ella, necesitamos código cómo el que sigue:
//{...}
		[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		static extern IntPtr GetForegroundWindow();
		[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
		[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		static extern int GetWindowTextLength(IntPtr hWnd); 
//{...}

Para poder usar las interfaces, primero debemos de importar el nombre de espacios

using System.Runtime.InteropServices;

Esto nos permitirá acceder a los atributos de la siguiente manera

//{...}
		private string GetCaptionOfActiveWindow()
		{
			string strTitle = string.Empty;
			IntPtr handle = GetForegroundWindow();
			int intLength = GetWindowTextLength(handle) + 1;
			StringBuilder stringBuilder = new StringBuilder(intLength);
			if (GetWindowText(handle, stringBuilder, intLength) > 0)
			{
				strTitle = stringBuilder.ToString();
			}
			return strTitle;
		}
//{..}

Con eso bastará con llamar la función "GetCaptionOfActiveWindow()" cada vez que queramos saber cual es la ventana activa.

Y bien, por ahora es todo, si quieres puedes probar a "unir" el código de Message Sender que ya teníamos con anterioridad con este fragmento, en el siguiente post veremos cómo implementar el código en C# y en arduino.



Los leo luego.

Vamos a programar #72 - Calculadora de resistencias para diodos LED (ver. Java para android).

Hola de nuevo a todo, el día de hoy vamos a ver el código en java para hacer una calculadora de resistencias para diodos LED.

En los post anteriores vimos cómo hacer la calculadora en javascript para los navegadores y en C# para las computadoras de escritorio ahora con está versión tenemos la versión para los teléfonos móviles.

Cómo el código (y su funcionamiento) es muy similar al de las otras versiones vamos a pasar directamente a el. El código que hacer funcionar la calculadora es el siguiente:

package com.mdev.ledrescalc;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;

public class MainActivity extends Activity {
EditText ETVin, ETVLed, ETILed, ETNLed, ETResult;
Button BtnCalc;
RadioButton RConexion1, RConexion2;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		ETVin = findViewById(R.id.ETVin);
		ETVLed = findViewById(R.id.ETVLed);
		ETILed = findViewById(R.id.ETILed);
		ETNLed = findViewById(R.id.ETNLed);
		ETResult = findViewById(R.id.ETResult);
		BtnCalc = findViewById(R.id.BtnCalc);
		RConexion1 = findViewById(R.id.Conexion1);
		RConexion2 = findViewById(R.id.Conexion2);
		BtnCalc.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				ETResult.setText(CalculateRes(ETVin.getText().toString(),ETVLed.getText().toString(),ETILed.getText().toString(),ETNLed.getText().toString(),RConexion1.isChecked()));
			}
		});
	}

	private String CalculateRes(String VIn, String VLed, String ILed, String NLed, boolean Serie)
	{
		double Corriente = 0;
		double DropVol = 0;
		double TotalRes = 0;
		double TotalVolt = 0;
		double Potencia = 0;
		double Vin = Double.parseDouble(VIn);
		double Vled = Double.parseDouble(VLed);
		double Iled = Double.parseDouble(ILed);
		double Nled = Double.parseDouble(NLed);
		if (Serie)
		{
			TotalVolt = Vled * Nled;
			if (Vin < TotalVolt){
				return "El voltaje es bajo";
			}
			Corriente = Iled;
			DropVol = Vin - TotalVolt;
			TotalRes = DropVol/Corriente;
			Potencia = Corriente * DropVol;
			return "Se necesita una resistencia de "+ TotalRes +" ohm(s) de una potencia de superior a "+ Potencia + " Watt(s)";
		}
		else{
			TotalVolt = Vled;
			if (Vin < TotalVolt){
				return "El voltaje es bajo";
			}
			Corriente = Iled * Nled;
			DropVol = Vin - TotalVolt;
			TotalRes = DropVol/Corriente;
			Potencia = Corriente * DropVol;
			return "Se necesita una resistencia de "+ TotalRes +" ohm(s) de una potencia de superior a "+ Potencia + " Watt(s)";
		}
	}
}

EL código anterior solo consta de una funcion llamada "CalculateRes()" qué cómo su nombre lo sugiere, se usa para hacer el cálculo para saber el valor de la resistencia necesaria. Recibe cinco parametros. Los primeros cuatro son del tipo "String" y que en orden de aparicion se usan para:

  1. El voltaje de entrada.
  2. El voltaje del LED.
  3. La corriente del LED.
  4. El número de LED's conectados.
El quinto parámetro es del tipo "boolean" y se usa para indicar se las conexiones son en serie o paralelo.

Con la anterior tenemos la calculadorra lista para poder usarla en el celular. Cómo de costumbre puede bajar el APK ya compilado o puedes descargar el código fuente; recuerda que solo incluye el código fuente y se quito la gran mayoria de archivos generados por android studio.

Los leo luego.

Vamos a programar #71 - Calculadora de resistencias para diodos LED (ver. C#).

Hola de nuevo a todos, el día de hoy vamos a continuar con mas sobre resistencias y diodos LED.
En el post anterior vimos el código en javascript que ya disponible en el blog para que lo revises y para que lo pruebes en linea (ya está corregido un par de errores que tenía).
Cómo el código es realmente similar al anterior, simplemente lo pongo para que lo revises, la explicación es un tanto similar a la versión en Javascript.

El código.


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace LedResCalculator
{
	public partial class FrmMain : Form
	{
		
	private string CalculateRes(decimal VIn, decimal VLed, decimal ILed, int NLed, bool Serie)
	{
		decimal Corriente = 0;
		decimal DropVol = 0;
		decimal TotalRes = 0;
		decimal TotalVolt = 0;
		decimal Potencia = 0;
		if (Serie)
		{
			TotalVolt = VLed * NLed;
			if (VIn < TotalVolt){
				return "El voltaje es bajo";
			}
			Corriente = ILed;
			DropVol = VIn - TotalVolt;
			TotalRes = DropVol/Corriente;
			Potencia = Corriente * DropVol;
			return "Se necesita una resistencia de "+ TotalRes +" ohm(s) de una potencia de superior a "+ Potencia + " Watt(s)";
		}
		else{
			TotalVolt = VLed;
			if (VIn < TotalVolt){
				return "El voltaje es bajo";
				}
				Corriente = ILed * NLed;
				DropVol = VIn - TotalVolt;
				TotalRes = DropVol/Corriente;
				Potencia = Corriente * DropVol;
				return "Se necesita una resistencia de "+ TotalRes +" ohm(s) de una potencia de superior a "+ Potencia + " Watt(s)";
			}
	}
		public FrmMain()
		{
			InitializeComponent();
		}

		private void BtnCalculate_Click(object sender, EventArgs e)
		{
			TxtResult.Text = CalculateRes(NUDVolt.Value, NUDVLed.Value, NUDILed.Value, (int)NUDNLED.Value, RBConexion1.Checked);
		}
	}
}

EL código consta de una sola función llamada "CalculateRes()" y recibe cinco parámetros; los primeros tres son del tipo "decimal" y en orden representan: el voltaje de entrada, el voltaje del LED y la corriente del LED. El siguiente parámetro es del tipo "int" y nos servirá para indicar el número de LED's que se conectan. Finalmente el parámetro llamado "Serie" del tipo "bool" nos indica si la conexión está en serie cuando su valor es "true". La decisión de usar valores del tipo decimal es simple, los controles "NumericUpDown" devuelven un valor de este tipo cuando se accede a la propiedad "Value" y es un tipo bastante adecuado para llevar a cabo la tarea que queremos realizar.

Cómo de costumbre el código lo puedes descargar de mi dropbox para probarlo.

Los leo luego.

Vamos a programar #70 - Calculadora de resistencias para diodos LED (ver. Javascript).

Hola de nuevo a todos, el día de hoy vamos a ver cómo hacer una calculadora en javascript que sirva para calcular que resistencia debemos de usar si queremos utilizar un diodo LED con un voltaje mayor a esperado.
Pero antes, unas consideraciones. Primero: esta no es la forma más eficiente de alimentar un LED, ya que mucha energia es deperdiciada. Segundo: Usar una sola resistencia para limitar la corriente que varios LED's no es lo mas recomendable; si se van a hacer uso de varios LED's lo mas recomendable sería poner una resistencia por cada LED que se conecte, es decir calcular el valor de cada uno y ponerle su resistencia y no cómo la imagen. En la calculadora es meramente ilustrativo.

 Ahora si, con las previsiones hechas, pasemos al código de la calculadora.

El código en javascript que hace funcionar la calculadora es el siguiente:

<html>
<head>
<title>Calculadora de resistencias para LED's</title>
<style type="text/css">
.h1RC
{
    font-size: 16px;
}
.formResCal{
    font: 95% Arial, Helvetica, sans-serif;
    max-width: 400px;
    margin: 10px auto;
    padding: 16px;
    background: #F9F9F9;
}
.textareaRC
{
    width: 100%;
    height: 150px;
    padding: 2px 5px;
    box-sizing: border-box;
    border: 2px solid #229;
    border-radius: 4px;
    resize: none;
    -webkit-transition-duration: 0.4s; /* Safari */
    transition-duration: 0.4s;
}

.textareaRC:focus
{
    width: 100%;
    height: 150px;
    padding: 2px 5px;
    box-sizing: border-box;
    border: 2px solid #0000BB;
    border-radius: 4px;
    resize: none;
}

.TextRC
{
    width: 100%;
    box-sizing: border-box;
    border: 2px solid #999;
    border-radius: 4px;
}

.ButtonRC
{
width: 100%;
    background-color: #57a2e2;
    border: none;
    color: white;
    padding: 15px 32px;
    text-align: center;
    text-decoration: none;
    display: inline-block;
    -webkit-transition-duration: 0.4s; /* Safari */
    transition-duration: 0.4s;
}

.ButtonRC:hover
{
width: 100%;
    background-color: #276775;
    border: none;
    color: white;
    padding: 15px 32px;
    text-align: center;
    text-decoration: none;
    display: inline-block;
}
</style>
<script type="text/javascript">

function RadioSelected(){
var radios = document.getElementsByName('Conexion');
	for (var i = 0, length = radios.length; i < length; i++) {
		if (radios[i].checked) {
			return Number(radios[i].value)
		}
	}
}

function CalculateRes()
{
	var VIn = Number(document.getElementById('InV').value);
	var VLED = Number(document.getElementById('LEDV').value);
	var ILED = Number(document.getElementById('LEDI').value);
	var NLED = Number(document.getElementById('LEDN').value);
	var Corriente = 0;
	var DropVol = 0;
	var TotalRes = 0;
	var TotalVolt = 0;
	var Potencia = 0;
	var ConexionType = RadioSelected();
	if (ConexionType == 1){
		TotalVolt = VLED * NLED;
		if (VIn < TotalVolt){
			alert('El voltaje es bajo');
			return;
		}
		Corriente = ILED;
		DropVol = VIn - TotalVolt;
		TotalRes = Math.ceil(DropVol/Corriente);
		Potencia = Corriente * DropVol;
		document.getElementById('Result').value = "Se necesita una resistencia de "+ TotalRes +"ohm(s) de una potencia de superior a "+ Potencia + " Watt(s)";
		}
		else{
		TotalVolt = VLED;
		if (VIn < TotalVolt){
			alert('El voltaje es bajo');
			return;
		}
		Corriente = ILED * NLED;
		DropVol = VIn - TotalVolt;
		TotalRes = Math.ceil(DropVol/Corriente);
		Potencia = Corriente * DropVol;
		document.getElementById('Result').value = "Se necesita una resistencia de "+ TotalRes +"ohm(s) de una potencia de superior a "+ Potencia + " Watt(s)";
		}

}
</script>
</head>
<body>
		<form class="formResCal">
			<span class="h1RC">Voltaje de alimentacion (V):</span><br />
			<input class="TextRC" id="InV" type="number" value = "5" placeholder = "Voltaje en volts"></input><br />
			<span class="h1RC">Voltaje del LED (V):</span><br />
			<input class="TextRC" id="LEDV" type="number" value = "3.1" placeholder = "Voltaje en volts"></input><br />
			<span class="h1RC">Corriente del LED (A):</span><br />
			<input class="TextRC" id="LEDI" type="number" value = "0.02" placeholder = "Corriente en Amperes"></input><br />
			<span class="h1RC">Numero de LED's:</span><br />
			<input class="TextRC" id="LEDN" type="number" value = "1"></input><br />
			<input type="radio" name="Conexion" value="1" checked> Serie<br>
			<input type="radio" name="Conexion" value="2"> Paralelo con una sola resistencia<br>
		<br>
			<input class="ButtonRC" id="ToAny" onclick="CalculateRes()" type="button" value="Calcular" /><br />
			<br />
			<span class="h1RC">Resultado:</span><br />
			<textarea class="textareaRC" id="Result" rows="5" placeholder = "Resultado"></textarea><br/>
			<br />
		</form>
</body>
</html>

Cómo podrás observar, simplemente pasamos a la computadora los cálculos que hicimos la vez pasada. El programa consta de dos funciones solamente. La primera de ellas es "RadioSelected()", que cómo su nombre lo indica, su trabajo es revisar cual de los controles del tipo "radio" es el que está seleccionado de la colección "Conexion", para ello itera a traves de todos los elementos y cuando encuentra uno que tenga la propiedad "checked" en "true" devuelve el valor del indice de este elemento.

Luego tenemos la funcion "CalculateRes()" primero lo que hacemos es obtener los valores que necesitamos usado la función "document.getElementById()" y los asignamos a la variables, luego se manda a llamar a la función "RadioSelected()" para ver que tipo de conexión es la que tienen los LED, en este caso, cuando es uno, significa que la conexión es en serie en caso contrario es paralelo .
Si revisas los apartado, veras que son muy similares y de hecho lo son, pero la diferencia, es que cuando la conexión es en serie, el voltaje aumenta y la corriente permanece, entonces la diferencia esta en cómo se mide esta, en una será la corriente que tenga la variable "VIn" y en la otra será la que resulte de multiplicar {"VIn * NLED"} donde "NLED" es el número de LED's conectados.

Finalmente, el resultado de los cálculos de la resistencia y la potencia se asignan al control "textarea" llamado "Result".

Cómo podrás ver, los cálculos son realmente sencillos y muy fáciles de portar a otro lenguajes; cosa que haremos la siguiente semana, ya que al igual que otros de los programas que hemos hecho, haremos una versión web, de escritorio y móvil.

El código lo puedes descargar de mi dropbox para que lo pruebes, pero además en breve subiré una versión al blog para que se pueda usar.

Y bien, por ahora es todo, en el próximo post veremos la version de C#.

Los leo luego.

Back to basics #6 - Resistencias y diodos LED.

Hola de nuevo a todos, el día de hoy vamos a ver un poco de cómo usar unos de mi componentes electrónicos favoritos: el diodo LED.
Imaginemos que en algun momento contruyes un velocímetro para una bicicleta y te levantas por las mañanas a hacer tu recorrido, pero un buen dia, por alguna razon las lamparas ddel alumbrado publico no encienden, al inicio supones que es algo normal, ya que las cosas tienden a fallar, pero tras tres semanas te das cuenta que probablemente no las van a reparar en fechas próximas por lo que decides agregarle "iluminacion" a tu bicicleta, pero surge una pregunta ¿Cómo alimentar la iluminacion?

Ultimamente se han hecho bastante accesibles los diodos LED, gracias a su alta eficiencia, son muy utiles a la hora de obtener alimentacion por medio de pilas o baterias pues nos asegura que estas duraran algo de tiempo; cosa que no pasaba antes con las lamparas que usaban filamento incandescentes y que se devoraban las pilas.

Si  no has visto cómo es que hice el velocímetro, aun puedes hacerlo pero haciendo memoria, este funciona con un voltaje de 5 v, si intentamos conectar un diodo directamente, este explotaría.

Dependiendo del tipo de diodo LED, este requerirá mas o menos voltaje, en mi caso conseguí LED's de color rojo, amarillo, azul, verde y blanco. Cada uno posee una hoja de datos en la cual podemos cuales son las condiciones para que cada uno de ellos funcione, en el caso de los LED's rojos, la hoja de datos dice que se le debe de aplicar un voltaje 2.1 v y una corriente de 0.02 A (20 mili Amperes), al conectarlo a una fuente con esas características, el diodo enciende.
La corriente esta limitada a 20mA

Pero si vemos bien en la imagen, si limitamos la corriente a 20 mA, el voltaje no es el que viene descrito en la hoja de datos, hay una diferencia de 0.1 V.

Para trabajar con diodos LED, lo primero que debemos de tomar en cuenta, es que no todos los diodos son iguales, la hoja de datos presentará datos nominales, pero en si cada uno es diferente. Al revisar 10 LED's rojos, si la corriente se mantiene constante, el voltaje varia desde los 1.9 V hasta los 2.2 V. Si a todos estos LED's se les alimenta con los 2.1V que dice la hoja de datos, los que funcionaron a los 1.9 V, se terminarán "acabando" mas rápido, debido que a mayor voltaje (del requerido), mayor corriente va a circular, generado calor que es uno de los enemigos de los LED, ya que a mayor temperatura, mayor corriente circula por ellos.

Si no se dispone de una fuente con control de corriente o cómo en mi caso, no es facil agregar un control de corriente (porque soy flojo), podemos simplemente agregar una resistencia que sirva para limitar la corriente.

En algunos de los post pasados hemos usado LED's y algunos de los visitantes del blog me han preguntado si podía hacer una calculadora que sirva para calcular la resistencia para llevarla en el teléfono y eso es lo que haremos, pero no en este post; al igual que en ocasiones anteriores, primero veremos como hacer los cálculos "a mano".

Aguantando con resistencia.

Para empezar, lo primero que debemos de tomar en cuenta es la ley del Ohm.

  • Donde R es la resistencia que se mide en Ohm y su símbolo es "Ω".
  • V es el voltaje que se mide en volt y su símbolo es "V".
  • I es la corriente que se mide en Ampere y su símbolo es "A".
Para conectar, la resistencia, simplemente debemos de ponerla en serie con el diodo.
De esta forma la resistencia "disipara" el voltaje excedente, es decir si vemos la imagen anterior, tendremos que la resistencia deberá de hacer algo con los 5V de entrada menos los 3.1V del diodo, es decir 5 V - 3.1 V  = 1.9 V y aquí es donde entra la ley del Ohm, el revisar la formula, vemos que necesitamos tres componentes: la resistencia, el voltaje y la corriente, pero de estos solo conocemos dos que son: el voltaje y la corriente por lo tanto si sustituimos los valores en la ecuación, tendremos:
Si tenemos un diodo 20 mA a 3.1 V y lo alimentamos con 5 V, necesitamos una resistencia de 95 Ω para que el diodo no se queme.

Ahora supongamos que conectamos diez diodos de las mismas características en paralelo, es posible poner una sola resistencia, pero debemos de tener algunas cosas en consideración, pero primero los cálculos.

Tenemos una corriente de 20 mA * 10 = 200 mA (hay que recordar que en este caso, en paralelo la corriente aumenta pero no el voltaje. Cosa contraria a cuando se hacen las conexiones en serie), el voltaje que "caerá" en la resistencia es el mismo, ya que usamos los mismos 5V para alimentar el circuito.

Al momento de elegir el tipo de resistencia, debemos de tener en cuenta cuanta potencia es capaz de disipar, en esto casos, la resistencia más común de 0.250W por lo que si excedemos esa potencia, esta se destruirá.

Para calcular cual es la potencia que una resistencia debe de soportar, debemos de recurrir a la ley del Watt que la siguiente:
Donde:
  • P  es la potencia que se mide en Watts y su símbolo es "W".
  • V es el voltaje que se mide en Volts y su símbolo es "V".
  • I es la corriente que se mide en Ampere y su símbolo es "A".
Tomado el ultimo ejemplo tenemos:
Por lo quedemos de usar una resistencia de 9.5Ω a medio Watt (que es lo mas común).

Dado que toda la energía no usada por el LED es disipada en calor por la resistencia, esta es una forma no muy conveniente de regularlo, pero para pocos LED's sirve.

Y bien, por ahora es todo, en el siguiente post haremos una calculadora para android que nos ayude a calcular la resistencia y la potencia de esta para poder usarlas, además veremos otras posibles soluciones.

Los leo luego.