Bienvenido el 2020.
Hola de nuevo a todos, el día de hoy solo escribo para desearles a todos un 2020 muy productivo. Es cierto que en el 2019 el blog bajo en su producción de contenido, pero eso se debe principalmente a que he tenido algunas actividades que requieren mucho de mi tiempo (y son mentalmente desgastantes) pero aprovechando el nuevo año, trataré de organizarme mejor para garantizar al menos un par de post al mes.Aun hay mucho por aprender por lo que espero que sigas dándote una vuelta por acá de vez en cuando.
Feliz 2020 y los sigo leyendo luego.
Vamos a programar #83 - Expresiones regulares en javascript.
Hola de nuevo a todos, el día de hoy vamos a ver un poco más sobre expresiones regulares, pero ahora usando javscript. Hace poco mientras hacia un programa en javascript, me preguntaron por una forma automática de reemplazar los hashtags de twitter con un vinculo a la dirección correspondiente; es decir; si tenemos #anipoke, que se cree un vinculo a https://twitter.com/hashtag/anipoke (usando #anipoke cómo texto). La respuesta simple, es si, si se puede, pero para poder hacerlo, vamos a emplear expresiones regulares.El trabajo de hoy es mas o menos entender cómo es que podemos usarlas en Javascript, la mayor parte de las expresiones que usamos en C#; por ejemplo; se pueden usar en javascript así que para probarlas, podemos usar un programa cómo el que sigue.
<!DOCTYPE html>
<html>
<meta charset="UTF-8">
<Title>Prueba de expresiones regulares</title>
<head>
<style>
.found
{
color: gray;
background-color: yellow;
}
</style>
<script type="text/javascript">
function FindRegex() {
var Texto = document.getElementById('MAIN').innerHTML;
var Patron = new RegExp(document.getElementById('InputRegex').value,"g");
var Result = Texto.match(Patron);
var Words = [];
var Ret = "";
for (var x = 0; x < Result.length; x++)
{
if (Words.includes(Result[x]) == false) Words.push(Result[x]);
}
for(var i = 0; i < Words.length; i++){
var NewExp = new RegExp("\\b"+Words[i]+"\\b", "g");
Ret = Texto.replace(NewExp, '<span class=\"found\">' + Words[i] + '</span>');
Texto = Ret;
}
document.getElementById('demo').innerHTML = Ret;
}
</script>
</head>
<body>
<div>
<form>
<span>Expresion regular para buscar</span>
<br />
<input id="InputRegex" type="text"></input><br />
<input type="button" onclick="FindRegex()" value="Prueba" /><br />
</form>
</div>
<div id="MAIN">Gente humano persona gente hombre mujer familia amigo conocido colega pareja esposo matrimonio amor Partes del Cuerpo Humano cuerpo pie, pierna talón espinilla
rodilla muslo cabeza cara boca labios diente nariz bigote cabello oreja cerebro estómago brazo codo hombro uña mano muñeca palma dedo trasero, culo, cola abdomen
hígado músculo cuello corazón mente alma+ cintura cadera corazón espalda sangre carne piel hueso pecho resfriado diarrea enfermedad Familia abuela abuelo esposa
esposo hermana hermano hija hijo madre nieta nieto padre prima primo tía tío Vida criatura especie ser vida naturaleza campo bosque selva, jungla desierto costa playa
río laguna, lago mar, océano cerro, monte, montaña Animales animal perro gato vaca cerdo caballo, yegua oveja mono ratón, rata tigre conejo dragón ciervo rana león jirafa
elefante pájaro gallina gorrión cuervo águila halcón pez camarón langosta sardina atún calamar pulpo insecto bicho mariposa polilla saltamontes araña mosca
mosquito cucaracha caracol babosa lombriz marisco molusco lagarto serpiente cocodrilo Plantas plantas pasto / césped flor fruta semilla árbol hoja raíz tallo hongo
ciruela pino bambú nuez almendra castaña Cosechas arroz avena cebada trigo vegetal, verdura patatas, papas judías, guisantes, porotos rábano zanahoria manzana naranja plátano pera castaño
durazno tomate sandía Alimentos alimento comida bebida carne gaseosa Espacio tiempo calendario edad época, era fecha segundo minuto hora día semana entre semana
fin de semana mes año ayer hoy mañana amanecer mediodía tarde anochecer noche lunes martes miércoles jueves viernes sábado domingo ambiente espacio entorno
sol luna estrella clima despejado nublado lluvia nieve viento trueno rayo tifón tormenta cielo Direcciones y Posiciones este oeste sur norte derecha izquierda
arriba, encima abajo, debajo adelante, delante atrás, detrás centro, medio encima alrededor diagonal enfrente cerca lejos adentro, dentro afuera, fuera aquí acá ahí allá allí exterior exterior Materiales
agua caliente calor frío fresco hielo vapor fuego gas aire, atmósfera tierra, suelo metal, metálico hierro cobre oro plata plomo sal barro, lodo arcilla yeso
fumar tabaco opio marihuana Pesos y Medidas peso metro litro gramo kilo centi mili Sociedad sociedad comunidad reunión encuentro calidad estructura administración organización
asociación empresa equipo obligación derecho permiso prohibición autoridad cargo campaña club comisión congreso constitución gobierno democracia dictadura presidente ministro director parlamentario, congresista, senador, diputado
representante capital ciudad pueblo villa estado provincia departamento Economía economía consumo demanda compañía comercio producto transacción almacén hotel fábrica cuenta dinero
billete vuelto, cambio máquina expendedora boleto, billete precio, tarifa Objetos hechos por el ser humano Hogar escritorio silla mesa cama puerta dormitorio habitación cuarto oficina panel puerta ventana entrada hogar
casa apartamento, departamento edificio construcción elevador, ascensor escalera cámara Herramientas aparato cámara aguja clavo hilo cuerda, cordel, cordón bolsillo bolso bolsa paraguas parasol pomo llave
trancar arma escultura cuadro grabado energía electricidad corriente Ropa ropa prenda manga solapa, cuello botón cremallera cinturón zapato gafas Transportes transporte, transporte público, transporte privado tránsito, tráfico
vía férrea tren, ferrocarril subterráneo, metropolitano camino vía ruta calle carretera autopista avenida estación, parada avión aeropuerto automóvil, coche, auto bus, autobús, ómnibus Lenguaje número alfabeto raíz, origen, fuente papel carta
periódico, diario diccionario computadora, ordenador idioma extranjero japonés inglés chino alemán español francés Colores color rojo naranja, anaranjado amarillo verde azul violeta blanco negro rosa, rosado
marrón, café gris Actividades cultura autor arte cine dibujo pintura música religión dios artículo educación escuela instituto colegio universidad clase curso estudio
formación análisis investigación conocimiento idea información dato forma estilo figura elemento comunicación expresión periodismo ciencia aritmética historia geografía educación física deporte carrera
competición, competencia documento informe información noticia necesidad falta significado cáracter personalidad pensamiento memoria recuerdo deseo alegria tristeza enojo placer, éxtasis empatía interés aburrimiento
cansancio sorpresa susto miedo, temor ejemplo ayuda favor apoyo búsqueda duda pregunta respuesta cuestión solicitud decisión elección consejo sugerencia orden control sistema
trabajo empleo profesión esfuerzo Números cero uno dos tres cuatro cinco seis siete ocho nueve diez cien mil millón Espacio cosa
lugar aspecto contenido cantidad área volumen ancho, anchura posición dirección tamaño largo, longitud alto, altura aumento reducción crecimiento fondo frente Sustantivos abstractos esto eso aquello
cual secreto automático formalidad presente pasado futuro acción actividad acto acuerdo actitud capacidad concepto condición caso conjunto grupo creación destrucción origen
destino objetivo, meta función intento logro efecto resultado éxito fracaso causa consecuencia beneficio perjuicio ataque defensa conflicto guerra carácter característica crisis cambio
desarrollo progreso avance retroceso mejora6 deterioro comienzo, inicio transcurso fin, final, cabo etapa fase grado corte interrupción espera diferencia similitud sensación dolor sentido conciencia
percepción fuerza potencia existencia experiencia Adjetivos Adjetivos de calidad bueno, buen malo superior inferior central lateral frontal trasero, posterior cierto falso mayor menor absoluto relativo
caro barato viejo joven nuevo cada cualquier dado actual reciente capaz cuyo, cuya fácil, simple, sencillo difícil, complicado estricto serio general particular común especial raro, extraño
fuerte débil correcto, acertado incorrecto, desacertado contrario, opuesto, inverso diferente, distinto parecido, similar otro diverso Adjetivos de forma alto bajo arriba abajo, debajo adelante, delante atrás, detrás gran grande pequeño amplio angosto
compacto delgado grueso Adjetivos sensoriales ligero pesado suave firme flexible duro blando caluroso frío fresco delicioso, apetitoso horrible dulce picante salado amargo anterior
posterior cercano lejano Sentimiento feliz triste solo solitario contento tranquilo calmo agitado ansioso interesado aburrido cansado sorprenido asustado, atemorizado doloroso picante, ardiente apestoso, maloliente
Verbos Auxiliares ser estar haber Existencia aparecer desaparecer existir cambiar crecer vivir nacer morir romper Movimiento ir venir volver hacer empezar, comenzar, iniciar
terminar, acabar abandonar, dejar hacer partir lanzar sujetar golpear patear golpear señalar apuñalar morder clavar levantar pegar moverse bailar andar saltar caer volar
nadar sentarse correr arrojar lanzar coger agarrar poner alcanzar acercar alejar Sensaciones sentir ver oír, escuchar tocar oler percibir Comunicación comunicarse afirmar
negar decir hablar callar escribir leer Emociones amar querer desear odiar, detestar triste llorar reír enfadado admirar, alabar, elogiar alegrarse, encantado consolar interesarse aburrirse cansarse
sorprenderse asustarse, atemorizarse Actividades analizar pensar dormir cantar morder comer beber acordar afectar generar añadir, agregar mejorar empeorar avanzar retroceder ayudar complicar reunirse
entrevistar abrir, desenvolver jugar recoger levantar tomar vender tener faltar dar recibir romper doblar cortar comprar vender llevar puesto cambiar intercambiar sustituir, reemplazar cerrar
buscar encontrar obtener, conseguir crear creer entrar quedarse salir abandonar dejar comunicar considerar comparar evaluar decidir intentar evitar construir destruir deber poder
conocer entender, comprender atar saber cansarse salir trabajar separar, dividir, partir descansar dormir romper aceptar rechazar descartar acompañar pedir, solicitar pretender proponer sugerir usar hacer
fabricar arreglar, reparar explicar mostrar probar, intentar comprobar, verificar variar esperar necesitar, precisar significar Adverbios Adverbios de cantidad más menos muy mucho poco algo casi aproximadamente exactamente
bastante demasiado sólo tan tanto todo nada cómo cuándo cuánto cuál, cuáles dónde Adverbios de calidad bien mal mejor peor regular despacio deprisa tal
como aprisa adrede claro exacto obvio ante contra inclusive además únicamente especialmente incluso viceversa siquiera finalmente Adverbios temporales antes anteriormente ahora ya
todavía aún mientras durante después luego pronto tarde temprano ayer anoche hoy mañana enseguida ahora de nuevo siempre Adverbios de posibilidad siempre nunca jamás
también tampoco quizá(s) acaso tal vez Otros adverbios aun incluso sólo incluso sobre solamente hasta sin embargo pero por ejemplo sólo, solamente justo apenas a en
desde entre hasta ante demasiado también Pronombres adjetivales este, esta ese, esa aquel, aquella algún, alguna alguien algo ambos Interjecciones sí no Conjunciones si tal vez entonces
cuando, si mientras hacia para de, del con sin así asimismo también aunque y o Locuciones acerca de a lo mejor a pesar de por favor gracias </div><br/>
<div id="demo"></div>
</body>
</html>
Por ahora solo vamos a probar algunas expresiones. Al igual que en c#, en el código anterior hay una lista de las 1000 palabras mas comunes y vamos a probar todos los ejemplos que previamente ya habíamos usado.
\ba\w+\b
\ba\w+n\b
\b(\w+[xyz]|\w+[xyz]\w+|[xyz]\w+)\b
\b\w\w\w\w\w\w\w\w\w\w\w\w\w\w\w\b
\b\w{15}\b
\b\w{10,}\b
Hay algunos detalles que hay que "corregir", por ejemplo si buscamos palabras con seis letras veremos que "música" no se resalta, pero eso no quiere decir que no nuestra expresión regular este mal, si no que simplemente está incompleta, y la razón principal es que la letra ú (u con acento) no es considerada una letra "normal" dentro del estándar ascii por lo que simplemente lo considera un carácter NO alfanumérico. La solución para eso es sencilla, pero eso será en el próximo post.
Y bien, por ahora es todo, cómo de costumbre puedes bajar el código de mi dropbox para que lo pruebes y lo edites según lo necesites. En el siguiente post veremos cómo manejar los acentos (y otro tipo de letras) e implementaremos la solución a nuestro problema inicial.
Los leo luego.
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.0Despué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.
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:
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.
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:
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.
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.
#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
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.![]() |
| 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.
# ---------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
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.
//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.
Suscribirse a:
Entradas
(
Atom
)











































No hay comentarios. :
Publicar un comentario