Vamos a programar #82 - Actualizando el velocímetro - Spedometer M V2.
Hola de neuvo a todos, el día de hoy vamos a ver cómo actualizar el velocímetro que previamente ya habíamos hecho a la versión 2.0
Después de varios meses de espera, finalmente he decidido actualizar el velocímetro a la siguiente versión. Ademas de hacer mejoras en la construcción, decidí actualizar el código para hacerlo un poco mas eficiente y funcional.
Lo primero que hay que resaltar es que es conveniente darse una vuelta por el post donde hicimos la primera version, ya que todos los fundamentos están en esa serie de posts.
Para esta versión se agregan solo dos resistencias y tres LED's. Hace unas cuantas semanas vimos cómo medir voltajes usando arduino, para eso usamos un divisor resistivo compuesto por dos resistencias de dos mega ohm y la parte media, la conectamos al pin A5. Cómo el voltaje está dividido a la mitad (puedes ver cómo funciona en el post de de divisores resistivos), en lugar de medir los 4.2v solo mediríamos cómo máximo 2.1v que se resuelve si se multiplica por dos.
Algo que es muy importante resaltar, es que el indicador de la batería no es muy preciso, eso se debe principalmente a que no encontré una hoja de datos del modelo especifico y por las que encontre, si hay diferencia entre marca y marca por lo que hay que ajustar los niveles de acuerdo a lo que se especifique según el tipo de batería que uses.
Y bien, por ahora es todo. En el siguiente post, veremos una explicación detallada del código, por ahora intentare buscar una hoja de datos o trataré de implementar una forma un poco mas precisa de desplegar el porcentaje de la batería. El código cómo de costumbre lo puedes descargar de dropbox (es el mismo link pero actualizado).
Los leo luego.
Después de varios meses de espera, finalmente he decidido actualizar el velocímetro a la siguiente versión. Ademas de hacer mejoras en la construcción, decidí actualizar el código para hacerlo un poco mas eficiente y funcional.
"Mejorando la casa"
La primera construcción la hice en una caja de plástico barato y no era muy estable que digamos, y debido a eso en situaciones de terreno muy empedrado, la débil construcción hacia que el velocímetro fallara. Por eso hice una caja de acrílico (los pasos no lo muestro, ya que es una caja y me quedo horrible) unida con silicon frió y tornillos, gracias a eso las pruebas que he hecho en terrenos realmente accidentados, han sido exitosas. Antes cuando estaba en la caja de plástico, en algunos caminos se reiniciaba (supongo que por la vibración), pero ahora ya no.
Además de manera opcional, incluí un LED de color rojo para saber cuando es que está en operación, no ayuda en nada pero siempre resulta util saber que cuando la luz está encendida, lo demás también debería.
En sustitución del la placa Arduino Uno, decidí usar directamente el micro-controlador ATMEGA328p (en un futuro post veremos cómo hacer un "Minimal arduino").
Las conexiones y el código.
Para las conexiones, bastará con mirar el siguiente diagrama:
Y el siguiente código:
// Incluir la libreria para la pantalla
#include <LiquidCrystal.h>
// Pines usados en la pantalla extraido de los ejemplos incluidos en la libreria
const int rs = 13, en = 12, d4 = 11, d5 = 10, d6 = 9, d7 = 8;
// iniciar el objeto LiquidCrystal
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// Este es el valor que debes de modificar para que funcione correctamente
// Puedes ver instrucciones en https://xworkforall.blogspot.mx/2018/03/vamos-programar-49.html
const float Llanta = 1.9572;
//Algunas variables
const int HallSensor1 = 3;
const int LED1 = 7;
const int LED2 = 6;
const int LED3 = 5;
int Vuelta = 0;
unsigned long OldTime = 0;
float Distancia = 0.0;
float Speed = 0.0;
int ledState = LOW;
int VoltSensor = 0;
float BattVolt = 0.0;
const byte Batt100[] = {
B01110,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111
};
const byte Batt75[] = {
B01110,
B10001,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111
};
const byte Batt50[] = {
B01110,
B10001,
B10001,
B10001,
B11111,
B11111,
B11111,
B11111
};
const byte Batt25[] = {
B01110,
B10001,
B10001,
B10001,
B10001,
B10001,
B11111,
B11111
};
const byte Batt0[] = {
B01110,
B10001,
B10001,
B10001,
B10001,
B10001,
B10001,
B11111
};
//Incializar todo
void setup() {
//Serial.begin(9600);
lcd.createChar(0, Batt0);
lcd.createChar(1, Batt25);
lcd.createChar(2, Batt50);
lcd.createChar(3, Batt75);
lcd.createChar(4, Batt100);
lcd.begin(16, 2);
lcd.setCursor(0, 0);
lcd.print("@XWork");
pinMode(HallSensor1, INPUT);
attachInterrupt(digitalPinToInterrupt(HallSensor1), GetVelocity, LOW);
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
pinMode(LED3, OUTPUT);
}
//Dibujar el icono de bateria***
void DrawBatt(int Value){
lcd.setCursor(15, 1);
if (Value > 90)
lcd.write(byte(4));
if (Value < 91 && Value > 75 )
lcd.write(byte(3));
if (Value < 76 && Value > 50 )
lcd.write(byte(2));
if (Value < 51 && Value > 10 )
lcd.write(byte(1));
if (Value < 11 )
lcd.write(byte(0));
}
// Calcular la velocidad
void GetVelocity() {
if (millis() - OldTime > 70) {
Speed = Llanta / ((float)( millis() - OldTime) / 1000) * 3.6;
OldTime = millis();
Distancia = Distancia + Llanta / 1000;
Vuelta += 1;
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
}
}
//Bucle principal
void loop() {
VoltSensor = analogRead(A5);
BattVolt = (VoltSensor * (10 / 1023.0)) * 100;
lcd.setCursor(0, 0);
lcd.print("v=");
lcd.setCursor(2, 0);
lcd.print(Speed);
lcd.setCursor(8, 0);
lcd.print("d=");
lcd.setCursor(10, 0);
lcd.print(Distancia);
lcd.setCursor(0,1);
lcd.print("Rev=");
lcd.setCursor(4,1);
lcd.print(Vuelta);
DrawBatt(map(BattVolt, 300,405,1,100));
digitalWrite(LED1, ledState);
digitalWrite(LED2, ledState);
digitalWrite(LED3, ledState);
if ((millis() - OldTime) > 3000){
ledState = LOW;
Speed = 0.0;
}
}
Lo primero que hay que resaltar es que es conveniente darse una vuelta por el post donde hicimos la primera version, ya que todos los fundamentos están en esa serie de posts.
Para esta versión se agregan solo dos resistencias y tres LED's. Hace unas cuantas semanas vimos cómo medir voltajes usando arduino, para eso usamos un divisor resistivo compuesto por dos resistencias de dos mega ohm y la parte media, la conectamos al pin A5. Cómo el voltaje está dividido a la mitad (puedes ver cómo funciona en el post de de divisores resistivos), en lugar de medir los 4.2v solo mediríamos cómo máximo 2.1v que se resuelve si se multiplica por dos.
Algo que es muy importante resaltar, es que el indicador de la batería no es muy preciso, eso se debe principalmente a que no encontré una hoja de datos del modelo especifico y por las que encontre, si hay diferencia entre marca y marca por lo que hay que ajustar los niveles de acuerdo a lo que se especifique según el tipo de batería que uses.
Y bien, por ahora es todo. En el siguiente post, veremos una explicación detallada del código, por ahora intentare buscar una hoja de datos o trataré de implementar una forma un poco mas precisa de desplegar el porcentaje de la batería. El código cómo de costumbre lo puedes descargar de dropbox (es el mismo link pero actualizado).
Los leo luego.
11/17/2019 04:41:00 p.m.
Arduino
,
programacion
,
velocimetro
Vamos a programar #81 - Midiendo voltajes con arduino
Para poder hacer las pruebas, voy a utilizar un arduino en conjunto a una pantalla LCD, por si quieres adaptar solo la parte que hace la medición, vendrá bien identificado que es lo que hace cada parte.
Para empezar vamos a recordar un par de cosas importantes: Primera. Nunca debemos de exceder los 5v que arduino usa, ingresar un voltaje mayor podría causar daños en tu circuito (en incluso a ti) por lo que hay que extremar las precauciones. Segunda. Cómo sabemos que el voltaje de una batería de iones de litio nunca va a pasar de los 4.2v, podemos hacer los cálculos para que en el divisor resistivo nos de un voltaje similar a este.
Los cálculos.
Primero vamos a empezar con los 14.8v (que serian 4 pilas en serie) y para calcular, tomemos la formula que sirve para calcular el voltaje y sustituyamos valores. Hay que tomar en cuenta que estamos usando el valor nominal por lo que deberiamos de usar resistencia que nos den un valor cercano a 3.7vPara R2 voy a usar una resistencia de 100kΩ, el voltaje que esperamos es menor que 5v pero sabemos que tendremos una mejor medición si usamos múltiplos de 3.7v asi que ese será nuestro objetivo entonces R2 = 100kΩ, V2 = 3.7 y el voltaje de entrada sera 14.8v por lo que V1 = 14.8. Sustituyendo tenemos:
Al realizar los cálculos tenemos que debemos de poner dos resistencias en serie, la primera de ellas de 100kΩ y la segunda de ellas de 300kΩ. Podemos realizar los cálculos con los valores máximos de la pila que serán 4.2v*4 = 16.8 por lo que sustituyendo otra vez tenemos:
Y al realizar las mediciones podemos confirmar que son correctas.
![]() |
| El voltaje de entrada es 16.84v |
![]() |
| Cómo no tengo resistencias de 300kΩ uso tres de 100kΩ, R1 es la resistencia mas cercana a V+ (cable rojo) |
![]() |
| Ahora podemos conectar de manera segura al arduino |
![]() |
| Cómo neuvamente no hay resistencias de 200,003Ω redondeamos al valor decimal mas cercano 200,000Ω |
![]() |
| Ahora tenemos 12.63v |
![]() |
| Igual uso dos resistencias de 100kΩ |
![]() |
| Y nuevamente tenemos un voltaje seguro. |
El código.
El código que sirve para medir el voltaje en arduino es el siguiente:#include <Wire.h>
#include <LiquidCrystal_I2C.h>
//SCL - A5
//SDA - A4
LiquidCrystal_I2C lcd(0x27,20,4);
void setup() {
lcd.init();
lcd.backlight();
}
float Volt = 0.0;
int InVolt = 0;
void loop() {
//Conectamos el voltaje de entrada al pin A0 del arduino
//Recordatorio amable: NO DEBE DE EXCEDER LOS 5 Volts
InVolt = analogRead(A0);
Volt = InVolt * (5.0 / 1023.0);
lcd.setCursor(0 , 0);
lcd.print("Voltaje actual");
lcd.setCursor(0 , 1);
lcd.print(Volt);
delay(1000);
}
Cómo podrás observar el código es realmente sencillo, simplemente conectamos el voltaje que queremos medir al pin A0 (v+ y v- a GND), luego cómo arduino cuenta con convertidor analogo-digital de 10 bits, bastara con leer el valor y multiplicarlo por 5 y dividirlo entre 1023 (hay que recordar que 2^10 = 1023) y con esto tendremos un valor que sera el voltaje de la bateria.
Tras realizar varias mediciones, he detectado que el valor que se despliega en el arduino es bastante cercano al de las baterías por lo que ahora podremos revisar un poco el estado de éstas.
Y bien, por ahora es todo, en el siguiente post veremos cómo incorporar el código al velocímetro.
Los leo luego.
Back to basics #7 - Divisores resistivos.
Hola de nuevo a todos, el día de hoy vamos a ver un poco mas sobre el uso de las resistencias.
Hace algún tiempo hicimos un velocímetro para bicicleta, y todo funciona bien salvo un par de detalles. Un de las cosas que luego me pasa, es que la batería se acaba a la mitad del recorrido y cómo me es importante saber cuanta distancia hago en cada uno, siento que si se apaga el velocímetro a mitad del camino, éste simplemente ya no cuenta. Cómo ya van varias veces que me pasa, decidí mostrar la información de la batería en la pantalla (Aprovechando el espacio disponible en ella.) y así al menos tener una idea de cuando debo de cargar la batería.
Antes de empezar, hay que buscar un método para medir el voltaje; hay que recordar que el velocímetro está basado en arduino, esto significa que en las entradas podemos medir niveles de hasta 5V, la batería tiene niveles de 3v a 4.2v, esto significa que en teoría podríamos simplemente conectar la batería a una de las entradas y "medir", pero en general es una mala practica conectar directamente la batería al micro-controlador.
Para poder medir el voltaje de manera segura vamos a usar un divisor de tensión, pero antes de continuar veamos que es:
Y hay una formula que sirve para calcular cual es el voltaje que queremos usar. Para eso miremos la siguiente imagen:
Donde tenemos:
Ahora tomemos cómo ejemplo la siguiente configuración:
Si observas cuidadosamente, tenemos dos resistencia de 4.7 KΩ y 3v a la entrada que si reemplazamos en la formula
tenemos:
Y al realizar las mediciones, podemos comprobar que en efecto los cálculos son correctos:
Hace algún tiempo hicimos un velocímetro para bicicleta, y todo funciona bien salvo un par de detalles. Un de las cosas que luego me pasa, es que la batería se acaba a la mitad del recorrido y cómo me es importante saber cuanta distancia hago en cada uno, siento que si se apaga el velocímetro a mitad del camino, éste simplemente ya no cuenta. Cómo ya van varias veces que me pasa, decidí mostrar la información de la batería en la pantalla (Aprovechando el espacio disponible en ella.) y así al menos tener una idea de cuando debo de cargar la batería.
Antes de empezar, hay que buscar un método para medir el voltaje; hay que recordar que el velocímetro está basado en arduino, esto significa que en las entradas podemos medir niveles de hasta 5V, la batería tiene niveles de 3v a 4.2v, esto significa que en teoría podríamos simplemente conectar la batería a una de las entradas y "medir", pero en general es una mala practica conectar directamente la batería al micro-controlador.
Para poder medir el voltaje de manera segura vamos a usar un divisor de tensión, pero antes de continuar veamos que es:
Un divisor de tensión es una configuración de un circuito eléctrico que reparte la tensión de una fuente entre una o más impedancias conectadas en serie. wikipedia/divisordetensionPara crearlo, simplemente debemos de unir dos resistencias (o mas) en serie y tomar el voltaje de uno de los puntos medios cómo muestra el siguiente diagrama:
Y hay una formula que sirve para calcular cual es el voltaje que queremos usar. Para eso miremos la siguiente imagen:
Donde tenemos:
- V2 = Voltaje de salida
- R2 = El valor de la resistencia 2
- R1 = Valor de la resistencia 1
- V1 = Voltaje de entrada
Ahora tomemos cómo ejemplo la siguiente configuración:
Arreglo de resistencias
![]() |
| Tenemos 3v a la entrada |
Si observas cuidadosamente, tenemos dos resistencia de 4.7 KΩ y 3v a la entrada que si reemplazamos en la formula
tenemos:
Y al realizar las mediciones, podemos comprobar que en efecto los cálculos son correctos:
![]() |
| Medimos "en medio" de las resistencias |
Si no te gusta usar la formula, bastará con usar resistencias del mismo valor y el voltaje será proporcional, en el ejemplo anterior usamos dos resistencias de 4.7 por lo que al dividir entre dos nos resulta 1.5v.
Ahora de manera intuitiva, si usamos tres resistencias de 100kΩ y medimos en la primer sección tendremos una tercera parte del voltaje y si medimos en la segunda, tendremos dos terceras partes, eso significa que si usamos un voltaje de 3v a la entrada, tendríamos voltajes cercanos a 1v y 2v respectivamente. Ahora lo comprobaremos.
![]() |
| Ahora usamos tres resistencias de 100kΩ |
![]() |
| Voltaje en la primer unión |
![]() |
| Voltaje en la segunda unión |
Con esto podemos medir de manera más segura el voltaje directamente con el arduino, pero cómo siempre hay que tener consideraciones. La primera de ellas es que usar este método para reducir el voltaje no es la más conveniente si queremos alimentar un LED' por ejemplo, ya que implicaría un gasto de corriente adicional, pero además no funciona del todo bien y entre mas sea la corriente peor funcionará. La segunda, que valores son los que debemos de usar, eso depende mucho de la aplicación, pero bastara con resistencias dentro del rango de los kΩ y eso se debe a que el propio divisor resistivo gasta cierta potencia, si recordamos la ley del watt, si tenemos un divisor resistivo con dos resistencias de 4.7kΩ y un voltaje de 3v, la potencia que se disipa en las resistencias es de 0.0003w que no es mucho, pero si usamos resistencias de 10Ω en la misma configuración tendríamos un gasto de 0.15w (haciendo técnicamente nada). Para tener una mejor aproximación al resultado que queremos, debemos de usar resistencias de precisión, las resistencias que usé para el segundo ejemplo, no lo son y ahí es cuando se produce la diferencia (los colores dicen que son de 100kΩ pero en realidad son de 98kΩ~99kΩ).
Y bien, por ahora es todo, en el siguiente post veremos cómo medir el voltaje de una bateria usando arduino.
Los leo luego.
10/20/2019 05:55:00 p.m.
aprende
,
Basics
,
Divisores resistivos
,
Resistencias
Vamos a programar #80 - La conjetura de Collatz (Ver. Pascal).
Hola de nuevo a todos, el día de hoy vamos a continuar con re-fritos de viejos programas, pero ahora en lenguajes que no habíamos usado. En esta ocasión vamos continuar con la prueba de la conjetura de Collatz pero en el lenguaje de programación pascal.
Antes de continuar, es importante mencionar la importancia de saber crear un programa en un lenguaje distinto al que estamos acostumbrados usando los fundamentos del lenguaje, en este caso, deberías de ser capas de crear este programa leyendo los post de aprendizaje que hice para pascal (Para el resto de lenguajes no hay pero recuerda que mi objetivo no es enseñarte a programar). Y bien ahora continuemos con el código.
El código en Pascal que hace funcionar las cosas es el siguiente:
Cómo podrás ver, el programa consta de dos funciones, la primera de ellas en orden de aparición es "IsEven()" y recibe un parámetro del tipo "integer" y sirve para determinar si el número que se paso cómo argumento es par o no; si el valor es par, la funcion devuleve "true", en caso contrario devuelve "false".
La siguiente función es "CalculateCollatz" aquí es donde se hace el calculo (siguiendo las reglas de la conjetura). Si revisaste alguno de los post de aprendizaje para Pascal, pero mas específicamente el post "Learning Machine #5", verás que hacemos uso de "if" y hace poco tiempo alguien me pregunto: "¿Que pasa si tengo más de una línea de código después de mi if?". Y pido una disculpa ya que en su momento lo olvide por completo pero si cambia un poco el uso de "if" si es de una línea o no. Cuando usamos la instrucción "if" si es de múltiples lineas, se deberá de hacer uso de la palabra reservada "begin" seguida de todo el código que se requiera ejecutar en la condición y se termina con "end;" si se usan instrucciones "else" (de igual manera que con if), cada una deberá de ir con su respectivo inicio y final, pero solo la última deberá tener punto y coma.
Y bien, por ahora es todo, cómo de costumbre puedes bajar el código de mi dropbox para que lo pruebes, pero además, puedes probarlo en linea por si no quieres instalar el compilador. En el siguiente post continuaremos con mas programación y con mas refritos hasta que se me ocurra algo bueno.
Los leo luego.
Antes de continuar, es importante mencionar la importancia de saber crear un programa en un lenguaje distinto al que estamos acostumbrados usando los fundamentos del lenguaje, en este caso, deberías de ser capas de crear este programa leyendo los post de aprendizaje que hice para pascal (Para el resto de lenguajes no hay pero recuerda que mi objetivo no es enseñarte a programar). Y bien ahora continuemos con el código.
El código en Pascal que hace funcionar las cosas es el siguiente:
program Collatz (output);
uses crt, sysutils;
var MyVal: integer;
function IsEven (Number:integer): boolean;
begin
if Number mod 2 = 1 then
exit (false)
else
exit (true);
end;
procedure CalculateCollatz (Number:integer);
begin
while Number > 1 do
begin
if IsEven (Number) = false then
begin
Number:= Number * 3 + 1;
writeln (Number);
end
else
begin
Number:= Number div 2;
writeln (Number);
end;
end;
end;
begin
writeln('Inserta un numero');
readln(MyVal);
CalculateCollatz(MyVal);
readln();
end.
Cómo podrás ver, el programa consta de dos funciones, la primera de ellas en orden de aparición es "IsEven()" y recibe un parámetro del tipo "integer" y sirve para determinar si el número que se paso cómo argumento es par o no; si el valor es par, la funcion devuleve "true", en caso contrario devuelve "false".
La siguiente función es "CalculateCollatz" aquí es donde se hace el calculo (siguiendo las reglas de la conjetura). Si revisaste alguno de los post de aprendizaje para Pascal, pero mas específicamente el post "Learning Machine #5", verás que hacemos uso de "if" y hace poco tiempo alguien me pregunto: "¿Que pasa si tengo más de una línea de código después de mi if?". Y pido una disculpa ya que en su momento lo olvide por completo pero si cambia un poco el uso de "if" si es de una línea o no. Cuando usamos la instrucción "if" si es de múltiples lineas, se deberá de hacer uso de la palabra reservada "begin" seguida de todo el código que se requiera ejecutar en la condición y se termina con "end;" si se usan instrucciones "else" (de igual manera que con if), cada una deberá de ir con su respectivo inicio y final, pero solo la última deberá tener punto y coma.
Y bien, por ahora es todo, cómo de costumbre puedes bajar el código de mi dropbox para que lo pruebes, pero además, puedes probarlo en linea por si no quieres instalar el compilador. En el siguiente post continuaremos con mas programación y con mas refritos hasta que se me ocurra algo bueno.
Los leo luego.
10/13/2019 04:57:00 p.m.
aprende
,
conjetura de collatz
,
pascal
,
programacion
Vamos a programar #79 - La conjetura de Collatz (Ver. Python).
Hola de nuevo a todos, el día de hoy vamos a ver un poco más de Python.
En el último post vimos un acercamiento a Python y si bien el blog nunca se ha tratado de "tutoriales" para aprender a programar, mucha gente pidió que adaptara un programa de los que previamente ya habíamos hecho en blog. Ya que solo será la versión adaptada a Python de la conjetura de Collatz, te recomiendo echarle un vistazo a los post que hicimos tanto para Android (Java) cómo para Windows (C#).
Ahora veamos el código que hace funcionar las cosas.
Cómo verás, el programar consta de solo dos funciones; la primera de ellas en orden de aparición es: "IsEven" que recibe un número parámetro y su función es determinar si un número es par o no. Si al hacer la operación "mod" el resultado es uno, eso significa que el número no es par y la funcion devuelve "false" en caso contrario, la función devuelve "true" (que indica que el número es par, valga la redundancia).
La siguiente función es "CalculateCollatz" y tal cómo su nombre lo indica, aqui es en donde se realizan los cálculos y se muestran en pantalla (siguiendo las reglas de la conjetura).
Finalmente solo mandamos a llamar a la función "CalculateCollatz()" que recibirá cómo parámetro un numero que el usuario ingresará.
Y bien, cómo puedes ver, es relativamente fácil portar los programa que ya tenioamos a Python, hay que tomar una cuantas consideraciones en cuanto al lenguaje (en comparación a los otros), pero python resulta realmente poderoso a la hora de escribir un programa. Cómo de costumbre puedes bajar el programa de mi dropbox para que lo pruebes o bien, puedes copiarlo para probarlo en el editor en linea
En el último post vimos un acercamiento a Python y si bien el blog nunca se ha tratado de "tutoriales" para aprender a programar, mucha gente pidió que adaptara un programa de los que previamente ya habíamos hecho en blog. Ya que solo será la versión adaptada a Python de la conjetura de Collatz, te recomiendo echarle un vistazo a los post que hicimos tanto para Android (Java) cómo para Windows (C#).
Ahora veamos el código que hace funcionar las cosas.
#Prueba de la conjetura de collatz en Python
def IsEven(Number):
if (Number % 2 == 1):
return False
else:
return True
def CalculateCollatz(Number):
while Number > 1:
if (IsEven(Number) == False):
Number = Number * 3 + 1
print(Number)
else:
Number = Number / 2
print(Number)
CalculateCollatz(int(input('Ingresa un numero\n')))
input()
Cómo verás, el programar consta de solo dos funciones; la primera de ellas en orden de aparición es: "IsEven" que recibe un número parámetro y su función es determinar si un número es par o no. Si al hacer la operación "mod" el resultado es uno, eso significa que el número no es par y la funcion devuelve "false" en caso contrario, la función devuelve "true" (que indica que el número es par, valga la redundancia).
La siguiente función es "CalculateCollatz" y tal cómo su nombre lo indica, aqui es en donde se realizan los cálculos y se muestran en pantalla (siguiendo las reglas de la conjetura).
Finalmente solo mandamos a llamar a la función "CalculateCollatz()" que recibirá cómo parámetro un numero que el usuario ingresará.
Y bien, cómo puedes ver, es relativamente fácil portar los programa que ya tenioamos a Python, hay que tomar una cuantas consideraciones en cuanto al lenguaje (en comparación a los otros), pero python resulta realmente poderoso a la hora de escribir un programa. Cómo de costumbre puedes bajar el programa de mi dropbox para que lo pruebes o bien, puedes copiarlo para probarlo en el editor en linea
10/06/2019 01:30:00 p.m.
conjetura de collatz
,
programacion
,
python
Vamos a platicar #10 - Hoy hay que celebrar.
Hola de nuevo a todos, el día de hoy solo escribo para platicar sobre un par de acontecimientos de reciente suceso.
Cómo muchos sabrán, soy un seguidor de la serie Pokémon; incluso he escrito en varias ocasiones cosas un tanto relacionadas.
Pero hoy es un día relativamente importante para todos los seguidores de la serie, ya que casi después de 20 años la serie ha dado un paso importante para lo que podría ser su culminación. Antes, un recordatorio amable; si no eres seguidor de la serie, eres libre de ir y leer otro de los artículos, ya que este post solo hablará de lo acontecido con la serie. Bien, retomando lo anterior. Ha pasado tanto tiempo que muchos creímos que Ash nunca ganaría un campeonato, pero si una lección hemos aprendido es que el que persevera alcanza.
Los leo luego.
![]() |
| La imagen no tiene nada que ver con el contenido del post. |
Pero hoy es un día relativamente importante para todos los seguidores de la serie, ya que casi después de 20 años la serie ha dado un paso importante para lo que podría ser su culminación. Antes, un recordatorio amable; si no eres seguidor de la serie, eres libre de ir y leer otro de los artículos, ya que este post solo hablará de lo acontecido con la serie. Bien, retomando lo anterior. Ha pasado tanto tiempo que muchos creímos que Ash nunca ganaría un campeonato, pero si una lección hemos aprendido es que el que persevera alcanza.
— Hugo G (@Mpm88G) September 15, 2019Y bien, por ahora es todo. si notaste la poca actividad del blog, en parte se debía a que estaba a la expectativa de este resultado (en adición a otros deberes), pero en octubre reanudare las publicaciones con nuevos proyectos, para cerrar el año cómo es debido.
Los leo luego.
Vamos a programar #78 - Un vistazo a Python.
Hola de nuevo a todos, el día de hoy vamos a ver un vistazo al lenguaje de programación Python y cómo es posible portar alguno de los programas que previamente ya teníamos hechos a este lenguaje.
Hace poco mientras revisaba twitter, alguien me pregunto la razón por la que no había portado los programas que ya tenia a Python y tras analizar, llegué a la conclusión que simplemente lo olvide (por mucho tiempo al parecer).
Entonces para echarle un vistazo rápido al lenguaje, el día de hoy vamos portar uno de los programas que servia para extraer las imágenes en los archivos mp3.
Primero que nada, vamos a tomar un par de cosas en cuenta. Primero: vamos a hacer una portación de la version que hicimos en LUA, asi que puedes (y recomiendo mucho) visitar el post. Y esto se debe principalmente a que LUA y Python son lenguajes interpretados.
Segundo: No te olvides de revisar la documentación en la página de ID3.org y leer la documentación además de los post que hay aquí en el blog.
Tercero: este post será una versión reducida y el programa solo extraerá la imagen del archivo mp3 si la versión de los TAG's es la 2.3, si quieres hacerlo para las otras versiones o extraer más información, puedes usar cómo ejemplo la versión en LUA.
Con lo anterior dicho, pasemos a ver el código en Python que sirve para extraer una imagen embebida en un archivo MP3.
Cómo podrás ver el código es realmente similar al de LUA, primero preguntamos por la ubicación de un archivo MP3, para un ejemplo insertamos "X:/Dropbox/MUSIC/American Idiot.mp3" y lo abrimos para su lectura en modo binario usando la función "open()"; la función "open()" recibe dos parámetros, el primero es una cadena de texto con la ruta del archivo a abrir y el segundo parámetro es una cadena de texto que indica el modo en el cual el archivo se abrirá, la cadena de texto "rb+" indica que abriremos el archivo en modo de lectura binario. Todo el "stream" lo asignaremos a la variable "Rdata". Al igual que en lua podemos desplazarnos en el archivo con la función "seek()" que recibe un parámetro que será un número que indicará a que lugar del archivo queremos desplazarnos, este puede ir de 0 a la longitud del archivo. Cómo la versión de los TAG's se indica en el byte 3, nos moveremos ahi y lo leeremos (e imprimimos en pantalla que versión es, no es necesario pero ayuda a tener una idea de lo que está sucediendo).
Luego si la versión de los TAG's es la versión 3, nos desplazamos de vuelta al byte 0 (o inicio del archivo) y leemos un KByte de datos que asignaremos a la variable "Idata". Con los datos asignados, procedemos a buscar el frame que nos interesa (APIC en este caso) con la función "bytes.find()", está función regresa -1 si la cadena de texto a buscar no se encuentra, en caso contrario nos devuelve el lugar en donde se encontró el valor.
Luego buscamos el encabezado de las imágenes PNG y leemos los bytes que nos indican la longitud del frame (4 bytes). Si NO se encontró el encabezado que previamente buscamos, asumimos que la imagen es jpg (asumir es malo ;) ) y buscamos el encabezado de los archivo JPG. Luego leemos los datos de la imagen y preguntamos en que carpeta se quiere guardar la imagen resultante, por ahora el nombre es TESTImage.???. Hacemos lo mismo para la imagen PNG
Y el resultado:
Y bien, por ahora es todo. Cómo de costumbre puedes bajar el programa completo de mi dropbox para probarlo o modificarlo. Además puedes bajar el interprete de la página oficial de Python. En la descarga se incluye un manual bastante extenso del lenguaje con ejemplos y lo necesario para portar lo programas que hemos hecho en el blog.
Los leo luego
Hace poco mientras revisaba twitter, alguien me pregunto la razón por la que no había portado los programas que ya tenia a Python y tras analizar, llegué a la conclusión que simplemente lo olvide (por mucho tiempo al parecer).
Entonces para echarle un vistazo rápido al lenguaje, el día de hoy vamos portar uno de los programas que servia para extraer las imágenes en los archivos mp3.
Primero que nada, vamos a tomar un par de cosas en cuenta. Primero: vamos a hacer una portación de la version que hicimos en LUA, asi que puedes (y recomiendo mucho) visitar el post. Y esto se debe principalmente a que LUA y Python son lenguajes interpretados.
Segundo: No te olvides de revisar la documentación en la página de ID3.org y leer la documentación además de los post que hay aquí en el blog.
Tercero: este post será una versión reducida y el programa solo extraerá la imagen del archivo mp3 si la versión de los TAG's es la 2.3, si quieres hacerlo para las otras versiones o extraer más información, puedes usar cómo ejemplo la versión en LUA.
Con lo anterior dicho, pasemos a ver el código en Python que sirve para extraer una imagen embebida en un archivo MP3.
# ---------De la documentación disponible en http://www.id3.org para la versión 2---------------
# -- Para la version 2.2
# -- Header for 'Attached picture', ID: "APIC">
# -- Text encoding $xx
# -- MIME type <text string> $00
# -- Picture type $xx
# -- Description <text string according to encoding> $00 (00)
# -- Picture data <binary data>
#Abrimos el archivo
InputFile = input('Ingresa la ruta de un MP3\n')
Rdata = open(InputFile, 'rb+')
#Buscamos que version ID3 es
Rdata.seek(3)
Version = ord(Rdata.read(1))
#escribimos que version es
print(Version)
if Version == 3:
print('Tag Version 2.3')
Rdata.seek(0)
Idata = Rdata.read(1024)
Found = Idata.find(b'APIC')
if Found > -1:
GotPNG = Idata.find(b"\x89\x50\x4e\x47")
Rdata.seek(Found + 4);
b1 = ord(Rdata.read(1))
b1 = b1 * 256 * 256 *256
b2 = ord(Rdata.read(1))
b2 = b2 * 256 *256
b3 = ord(Rdata.read(1))
b3 = b3 * 256
b4 = ord(Rdata.read(1))
bt = b1 + b2 + b3 + b4 + 10
print(bt)
if GotPNG > -1:
PPicInit = Idata.find(b'\x89PNG')
Rdata.seek(PPicInit)
IMGData = Rdata.read(bt - (PPicInit - Found))
OutPath = input('En que carpeta guardar la imagen\n')
ResultImage = open(OutPath + '/TestImage.png','wb')
ResultImage.write(IMGData)
ResultImage.close()
else:
JPicInit = Idata.find(b'\xFF\xD8\xFF')
Rdata.seek(JPicInit)
IMGData = Rdata.read(bt - (JPicInit - Found))
OutPath = input('En que carpeta guardar la imagen\n')
ResultImage = open(OutPath + '/TestImage.png','wb')
ResultImage.write(IMGData)
ResultImage.close()
else:
print('Not Found')
Rdata.close()
input()
Cómo podrás ver el código es realmente similar al de LUA, primero preguntamos por la ubicación de un archivo MP3, para un ejemplo insertamos "X:/Dropbox/MUSIC/American Idiot.mp3" y lo abrimos para su lectura en modo binario usando la función "open()"; la función "open()" recibe dos parámetros, el primero es una cadena de texto con la ruta del archivo a abrir y el segundo parámetro es una cadena de texto que indica el modo en el cual el archivo se abrirá, la cadena de texto "rb+" indica que abriremos el archivo en modo de lectura binario. Todo el "stream" lo asignaremos a la variable "Rdata". Al igual que en lua podemos desplazarnos en el archivo con la función "seek()" que recibe un parámetro que será un número que indicará a que lugar del archivo queremos desplazarnos, este puede ir de 0 a la longitud del archivo. Cómo la versión de los TAG's se indica en el byte 3, nos moveremos ahi y lo leeremos (e imprimimos en pantalla que versión es, no es necesario pero ayuda a tener una idea de lo que está sucediendo).
Luego si la versión de los TAG's es la versión 3, nos desplazamos de vuelta al byte 0 (o inicio del archivo) y leemos un KByte de datos que asignaremos a la variable "Idata". Con los datos asignados, procedemos a buscar el frame que nos interesa (APIC en este caso) con la función "bytes.find()", está función regresa -1 si la cadena de texto a buscar no se encuentra, en caso contrario nos devuelve el lugar en donde se encontró el valor.
Luego buscamos el encabezado de las imágenes PNG y leemos los bytes que nos indican la longitud del frame (4 bytes). Si NO se encontró el encabezado que previamente buscamos, asumimos que la imagen es jpg (asumir es malo ;) ) y buscamos el encabezado de los archivo JPG. Luego leemos los datos de la imagen y preguntamos en que carpeta se quiere guardar la imagen resultante, por ahora el nombre es TESTImage.???. Hacemos lo mismo para la imagen PNG
Y el resultado:
Y bien, por ahora es todo. Cómo de costumbre puedes bajar el programa completo de mi dropbox para probarlo o modificarlo. Además puedes bajar el interprete de la página oficial de Python. En la descarga se incluye un manual bastante extenso del lenguaje con ejemplos y lo necesario para portar lo programas que hemos hecho en el blog.
Los leo luego
8/18/2019 07:46:00 p.m.
aprende
,
mp3
,
programacion
,
python
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.
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ó.
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:
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:
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.
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.
7/28/2019 12:53:00 p.m.
ffmpeg
,
programacion
,
Vencoder
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.
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.
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.
//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.
7/14/2019 03:05:00 p.m.
ds1302
,
ds1307
,
programacion
,
ws2812b
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.
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.
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.
6/30/2019 05:53:00 p.m.
Arduino
,
programacion
,
ws2812b
Suscribirse a:
Entradas
(
Atom
)













































