Vamos a programar #100 - Speedometer M. Versión Final.
Hola de nuevo a todos, el día de hoy empezaremos a ver la versión final del velocímetro.
Este proyecto fue iniciado mas o menos por el 2018 y ha sufrido una serie de cambios a lo largo del tiempo, si bien la última versión que fue publicada funcionaba bien, poco a poco se fueron agregando funciones para hacerlo funcionar de manera optima con el mínimo de hardware posible (de ahí que nunca le agregue ningún tipo de botón). Finalmente, tras días de pruebas y pruebas, he decidido finalizar con este proyecto porque finalmente ha alcanzado el nivel de confiabilidad para poder usarse en el día a día.
Primero que nada, veamos el código que hace funcionar a "Speeedometer M"
// 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;
// v=99.99
char BuffVelocity[15];
//d=999.99
char BuffDistance[15];
//R=999999
char BuffRevolution[15];
//V=4.2
char BuffBattery[15];
//Incializar todo
void setup() {
//Serial.begin(9600);
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);
}
// 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.0 / 1023.0));
dtostrf(Speed , 5 , 2 , BuffVelocity);
dtostrf(Distancia , 6 , 2, BuffDistance);
sprintf(BuffRevolution , "R=%6d", Vuelta);
dtostrf(BattVolt , 5 , 1, BuffBattery);
lcd.setCursor(0, 0);
lcd.print("d=");
lcd.setCursor(2 , 0);
lcd.print(BuffDistance);
lcd.setCursor(9, 0);
lcd.print("v=");
lcd.setCursor(11, 0);
lcd.print(BuffVelocity);
lcd.setCursor(0,1);
lcd.print(BuffRevolution);
lcd.setCursor(9,1);
lcd.print("V=");
lcd.print(BuffBattery);
digitalWrite(LED1 , ledState);
digitalWrite(LED2 , ledState);
digitalWrite(LED3 , ledState);
if ((millis() - OldTime) > 3000){
ledState = LOW;
Speed = 0.0;
}
}
Vamos a programar #94 - Formateando números en arduino - Camino a speedometer version final.
Hola de nuevo a todos, el día de hoy vamos a continuar con mas partes para llegar a la versión final del velocímetro.
Leyendo algunos comentarios que hace la gente sobre que más se le podría modificar al velocímetro, muchos de ellos coincidían que había que corregir algunos de ellos. Los principales "detalles" estaban en la pantalla. Si bien no eran errores, aprovechando que se va a actualizar, decidí arreglarlos de una vez.
Supongamos que en la pantalla tenemos una velocidad de 10.09 km/h pero si desaceleramos, a por ejemplo 9.1 la pantalla mostraba algo cómo 9.19, y la cosa era peor si nos deteníamos ya que se mostraba 0.09. La razón por la que pasaba esto es por cómo se maneja la pantalla. Para ponerlo de forma simple, solo cuando se escribe algo en cierta posición, la pantalla se actualiza pero solo en esa zona, cuando escribimos "v=10.09" se usan siete espacios y mientras los usemos con números que usen el mismo "ancho" no se verá ningún problema, pero cuando tenemos cualquier número inferior a 10.00 cómo 9.9 ahora solo se usan menos espacios por lo que en lugar de actualizar los mismos siete de antes, solo se actualizan cinco o seis. Para poder solucionarlo, podemos escribir número que si importar cual sea, siempre ocupen la misma cantidad de espacios.
En C existen varias formas de formatear números, pero algunas de ellas no están disponibles para usarse en arduino (y debo de admitirlo, eso no lo sabia hasta apenas) y por eso veremos cómo es posible usar el reemplazo que arduino ofrece.
Primero que nada, hay que definir cual es el "estilo" que queremos que cada número tenga. En el caso del velocímetro, hay cuatro números importantes: la velocidad, la distancia, las revoluciones (o vuletas) y el voltaje de la batería. En el caso de la velocidad, una velocidad máxima de 99.99km/h es mas que suficiente para una bicicleta por lo que el numero ocuparía cinco espacios (con el punto incluido). Para las distancia, un viaje de 999.99 km es mas que suficiente para la mayoría de los ciclistas, por lo que seis digitos parecen suficiente, para las revoluciones, 999999 parecen suficientes por lo que seis espacios parecen suficientes y finalmente la batería, ya que el voltaje máximo de una sola pila es 4.2v, con solo reservar tres espacios es mas que suficiente.
Ahora veamos un código hipotético en C que serviría para usarse en un programa de computadora (por ejemplo) pero que no produciría el resultado esperado en arduino. EL código es el siguiente:
#include <stdio.h>
float Speed = 9.1;
float Distance = 9.04;
int Revolution = 16384;
float Battery = 2.0;
char BuffSpeed[20];
char BuffDistance[20];
char BuffRevolution[20];
char BuffBattery[20];
int main()
{
sprintf(BuffBattery , "R=%3.1f\n", Battery);
sprintf(BuffRevolution , "R=%6d\n", Revolution);
sprintf(BuffDistance , "d=%6.2f\n", Distance);
sprintf(BuffSpeed, "V=%5.2f\n", Speed);
printf(BuffSpeed);
printf(BuffDistance);
printf(BuffRevolution);
printf(BuffBattery);
return 0;
}Al compilarlo con los valores asignados, produce el siguiente resultado:
V= 9.10
d= 9.04
R= 16384
R=2.0
Pero cómo no es muy fácil de apreciar, solo para este ejemplo vamos a poner "0" en lugar de espacios en blanco para que se mas fácil de apreciar (en la pantalla no para poder preservarla un poco mas.) por lo que probamos con el siguiente código:
#include <stdio.h>
float Speed = 9.1;
float Distance = 9.04;
int Revolution = 16384;
float Battery = 2.0;
char BuffSpeed[20];
char BuffDistance[20];
char BuffRevolution[20];
char BuffBattery[20];
int main()
{
sprintf(BuffBattery , "R=%3.1f\n", Battery);
sprintf(BuffRevolution , "R=%06d\n", Revolution);
sprintf(BuffDistance , "d=%06.2f\n", Distance);
sprintf(BuffSpeed, "V=%05.2f\n", Speed);
printf(BuffSpeed);
printf(BuffDistance);
printf(BuffRevolution);
printf(BuffBattery);
return 0;
}Al compilarlo con los valores asignados, produce el siguiente resultado:
V=09.10
d=009.04
R=016384
R=2.0
Ahora resulta mas sencillo observar que por ejemplo la velocidad siempre ocupara cinco espacios a pesar de ser menor a 10km/h y que por ejemplo la distancia ocupa seis espacios a pesar de ser menor a 100km.
Pero que pasa si tratamos de ejecutar el mismo código en arduino?
Cómo puede observarse, la mayor parte del código se compila pero no funciona. Esto se debe principalmente a que a la hora de formatear los números del tipo "float" requiere mas poder de procesamiento por lo que en arduino no se implementó (**o eso es lo que quiero creer**), para el caso del los enteros es posible usar la misma función "sprintf()".
Para poder hacer algo similar, podemos usar la funcion "dtostrf()". Esta funcion lo que hace es convertir un número "double" a su equivalente en "string", para eso la funcion recibe cuatro parametros.
- char * dtostrf (double __val, signed char __width, unsigned char __prec, char *__s)
- __val es el valor que se va a convertir
- __width es el tamaño que debe de tener la cadena.
- __prec Es la cantidad de decimales que usaran
- __s Es el sitio donde se almacenara el resultado.
dtostrf(Speed , 5 , 2 , BuffVelocity);
//YWROBOT
//Compatible with the Arduino IDE 1.0
//Library version:1.1
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Conexiones de la pantalla usando el convertidor a i2c
//SCL - A5
//SDA - A4
LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27 for a 16 chars and 2 line display
float Speed = 9.1;
float Distance = 9.04;
int Revolution = 16384;
float Battery = 2.0;
char BuffSpeed[20];
char BuffDistance[20];
char BuffRevolution[20];
char BuffBattery[20];
void setup()
{
lcd.init();
lcd.backlight();
}
void loop()
{
dtostrf(Speed , 5 , 2 , BuffSpeed);
dtostrf(Distance , 6 , 2, BuffDistance);
sprintf(BuffRevolution , "R=%6d", Revolution);
dtostrf(Battery , 4 , 2, BuffBattery);
lcd.setCursor(0, 0);
lcd.print("v=");
lcd.setCursor(2 , 0);
lcd.print(BuffSpeed);
lcd.setCursor(8, 0);
lcd.print("d=");
lcd.setCursor(10, 0);
lcd.print(BuffDistance);
lcd.setCursor(0,1);
lcd.print(BuffRevolution);
lcd.setCursor(10,1);
lcd.print("v=");
lcd.print(BuffBattery);
}
Back to basics #10 - Control de LED's (con transistores) - Camino a Spedometer v. Final
Hola de nuevo a todos, el día de hoy vamos a ver cómo instalar una mejora a las luces del velocímetro y además, veremos cómo es que los transistores nos pueden ayudar a manejar algunas cosas que no podríamos directamente desde el arduino.
Si hacemos memoria la primera vez que construimos el velocímetro, usábamos un par de diodos LED para hacernos visibles a la hora de manejar en la noche, pero los diodos que se usan al ser de 20mA @ 3.1v solo funcionaban en lugares pequeños (llámese calle promedio), pero cuando se trata de recorridos por caminos más amplio, la idea es ser visible desde la mayor distancia posible, por lo que se hace necesario usar un fuente luminosa mas intensa.
Cuando controlamos los diodos LED directamente desde el arduino, disponemos de una cantidad limitada de potencia. Normalmente el arduino UNO es capaz de suministrar un voltaje de 5v en los pines, pero tienen un limite de corriente de solo 40mA, si conectamos algo que requiera más corriente las cosas pueden ponerse feas.
Por los lugares que suelo andar en bicicleta, los diodos antes mencionados cumplen con su trabajo, pero me llegaron algunas peticiones a mi twitter preguntando por la posibilidad de agregar algo mas fuerte. La mayoría quería agregar los módulos que consisten de tres (o seis) diodos LED 5050.
Si conectamos uno de estos módulos al arduino, simplemente nada pasará, ya que requieren un voltaje de 12V (si hago bien memoria cuando es un solo led 5050 de color blanco consume 60mA a 3.2v), para poder agregar uno de estos módulos, primero que nada, necesitaremos agregar un elevador de voltaje y para controlarlas el transistor.
Para este caso voy a usar un transistor TIP31c (que era lo que tenía a la mano) pero el siguiente diagrama aplica a cualquier transistor NPN. Pero primero que nada ¿Que es un transistor?.
El transistor es un dispositivo electrónico semiconductor utilizado para entregar una señal de salida en respuesta a una señal de entrada. Cumple funciones de amplificador, oscilador, conmutador o rectificador. El término «transistor» es la contracción en inglés de transfer resistor («resistor de transferencia»). Actualmente se encuentra prácticamente en todos los aparatos electrónicos de uso diario tales como radios, televisores, reproductores de audio y video, relojes de cuarzo, computadoras, lámparas fluorescentes, tomógrafos, teléfonos celulares, aunque casi siempre dentro de los llamados circuitos integrados.
Tal y cómo lo dice la definición, necesitamos una señal de entrada para obtener una señal de salida, por lo que en este ejemplo el transistor lo usaremos cómo un apagador (se puede usar para más muchas más aplicaciones pero por ahora solo esto). Antes de continuar, quiero resaltar que usaremos un transistor NPN, existen de más tipos (que eventualmente veremos) pero por ahora y cómo el objetivo no es aprender a usarlos, bastará con usar un transitor del mismo tipo y con las caracteristicas que veremos un poco más adelante.
Para el transistor TIP31c los pines son de la siguiente manera:
- Base
- Colector
- Emisor
.Para hacer las conexiones solo deberás de identificar cual corresponde a cada pin del transistor que elijas y todo debería de funcionar.
Y el diagrama correspondiente sería:
SI todo salió bien las luces deberían de encender cuando el PIN de arduino este en "HIGH".
Cabe aclarar que lo mas recomendable para está situación, sería usar una fuente de energía adicional, puesto que el velocímetro es alimentado por una pila, agregar dos elevadores de voltaje podría reducir la durabilidad de esta de forma drástica. En cambio si agregamos otra pila aparte si se agota el velocímetro podría seguir operando con normalidad.
Aunque la idea me agrado, está no es la solución que agregué a mi bicicleta, la solución que yo le puse la veremos en el siguiente post. Por cierto, se que al inicio del año prometí post cada quincena, pero por razones que son difíciles de entender (aun para mi) los post saldrán pero no de forma periódica.
Los leo luego.
Vamos a programar #89 - Midiendo voltajes con arduino - Camino a Spedometer v. Final
Para medir el voltaje, lo que hacemos es ingresarlo por un pin del arduino, internamente, este posee un mecanismo que traslada el voltaje un valor digital. Cuando conectamos 0v a un pin analogo del arduino y hacemos la lectura con "analogread()" está devolverá un valor de 0. EN cambio si conectamos 5v esta devolverá un valor de 1023. ¿Por que ocurre esto? Simple arduino cuenta con convertidor análogo-digital con una resolución de 8 bits por lo que cada valor entre 0 y 1023 sera igual a un valor de 0v a 5v. Para saber que valor le corresponde a cada voltaje, bastará con mirar la siguiente formula:
donde "n" es el valor de la lectura que "analogread()" regresa . Para entender un poco lo que pasa, supongamos que toamos la lectura usando arduino y este nos regreso un valor de 155.
al sustituirlo tenemos:
Y si lo medimos usando un multimetro tenemos:
Que es bastante acertado. Pero ahora lo que nos interesa hacer es ganar mas definición. Cuando arduino "mide" un voltaje en realidad lo que hace es compararlo con otro, por default este es de 5v, pero se puede elegir otro.
Para indicar que queremos usar un voltaje externo de referencia, podemos usar la función "analogReference()" que recibe un parámetro que puede ser cualquiera de los siguientes:
- DEFAULT (el valor de la alimentación, si se usan 5v, esta sera la referencia, si se usan 3.3v esta lo será).
- INTERNAL (Usa un voltaje interno de 1.1v pero solo en el modelo uno, en otros modelos es de 2.56v y elgunos no esta disponible).
- EXTERNAL (Usa el PIN AREF y se puede usar un valor mayor a cero y menor o igual a 5v)
Existen otros parámetros, pero por ahora nos centraremos en estos.
Primero usaremos el parámetro "INTERNAL" y para que pruebes, puedes usar el siguiente código:
int ValIn = 0;
int CurVal = 0;
float CurVolt =0;
void setup()
{
analogReference(INTERNAL);
Serial.begin(9600);
}
void loop()
{
CurVal=analogRead(ValIn);
Serial.println(CurVal);
CurVolt=CurVal*1.1;
Serial.print("Valor");
Serial.print(CurVal);
Serial.print("mV:");
Serial.println(CurVolt);
Serial.println("Done");
delay(2000);
}
Que es un valor mas cercano al que nos dio el multímetro.
Y sabiendo lo anterior ahora podemos hacer que la referencia sea la indicada para el nivel de la batería. Primero modificaremos un poco el código para hacer uso de la referencia externa, para eso prueba copiar el siguiente código:
int ValIn = 0;
int CurVal = 0;
float CurVolt =0;
void setup()
{
analogReference(EXTERNAL);
Serial.begin(9600);
}
void loop()
{
CurVal=analogRead(ValIn);
Serial.println(CurVal);
CurVolt=CurVal*1.1;
Serial.print("Valor");
Serial.print(CurVal);
Serial.print("mV:");
Serial.println(CurVolt);
Serial.println("Done");
delay(2000);
}
Algo que es importante aclarar para finalizar el post, es que para mostrar el porcentaje de batería no podemos simplemente usar la formula del porcentaje ya que todas las pilas son diferentes y a pesar de que la capacidad de una pila se puede medir de acuerdo al voltaje que esta nos da, depende meramente del fabricante cómo es "la curva de descarga" (que veremos en otro post) cómo dije al inicio, en el caso del velocímetro, los iconos son meramente ilustrativos de "la pila recien se cargo", "Está medio vacía" y "ya hace falta una recarga".
Y bien, por ahora es todo, lo ejemplos de código cómo son sencillos puedes simplemente copiarlos y pegarlos para probarlos y hago énfasis en que al pin "AREF" del arduino SIEMPRE se le debe aplicar un voltaje mayor a cero y menor o igual a 5.
Los leo luego.
Vamos a programar #82 - Actualizando el velocímetro - Spedometer M V2.
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"
Las conexiones y el 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 platicar #8 - Reseña del velocímetro.
En abril del 2018 fue cuando empece con la construcción del velocímetro, y desde ese día lo he usado casi a diario, muy pocas le he modificado. Algunas de las cosas que tenía planeadas simplemente ya nos las implementé, si hacemos memoria, implementar las direccionales era algo que tenia planeado hacer (y no he hecho), pero a medida que lo usaba me di cuenta que con una luz intermitente tradicional y con la pantalla de 16x2 era más que suficiente; eso hasta cierto punto.
Normalmente lo uso en la mañana, pero me di cuenta de algunas cosas que podrían ser realmente útiles y que gracias al arduino se pueden implementar de manera un tanto sencilla. Un reloj en tiempo real, eso es algo que sin duda podría resultar útil. En algunas ocasiones he ido en bicicleta a lugares donde tengo compromisos y en ocasiones no dimensiono cuanto tiempo es el que he gastado en el camino. Puedo consultar mi teléfono y ver la hora (o comprar un G-Shock de Casio), pero a veces en medio del trafico hay que poner atención al camino, entonces ahi es cuando se dificulta un poco ver la hora. En cambio si se le agregara el modulo, bastaría con mirar un instante a la pantalla y ya sabría que hora es.
En la parte del software, he pensado en agregar algunas funciones, pero para poder desplegarlas todas, es necesario una pantalla más grande. Hace tres meses ordene una pantalla de 20x4 en ali-express, pero no ha llegado; en ese tiempo estaba pasando todos los componentes a una caja de acrilico que hice, entonces pensé que seria buena idea actualizar el hardware antes de sellar medianamente bien la caja, pero espere y la pantalla que tenia tiempo de espera estimado para su llegada de dos meses; aun no llega.
El motivo para usar una pantalla más grande es simple: así no es necesario agregar botones; si se agregaran botones para la operación del velocímetro, entonces sería lo mismo que sacar mi teléfono de mi mochila, presionar un botón, ver la hora y volverlo a guardar.
Por lo demás, lo que es el "Sistema básico" funciona de maravilla, a pesar de que el cálculo de la velocidad depende en buena medida de la llanta de enfrente, el margen de error está dentro del 5% por cada cien metros en la presentación; es decir, al momento de mostrar la distancia solo se usan dos decimales y por lo tanto se redondea, pero internamente el calculo se hace bien (dentro del margen de error) y las distancias que he medido con respecto a las mediciones que ya hay suelen estar bien (dentro de lo que cabe).
Y bien, por ahora es todo, si no has intentado construir tu propia versión del velocímetro, puedes hacerlo ahora siguiendo las instrucciones que hay en los post.
Los leo luego.
Vamos a programar #51 - Terminando el velocímetro.
![]() |
| Así luce en el empaque menos prolijo. |
El hardware
Para empezar veamos las conexiones.- De la pantalla 1602A
- VSS va a tierra.
- VDD va a5v.
- VO controla el contraste de la pantalla, un valor aceptable son 2.2KOhms.
- RS va al pin 12 del arduino (se puede modificar).
- RW va a tierra.
- E va al pin 11 del arduino.
- D4 al pin 9 (se puede modificar).
- D5 al pin 8.
- D6 al pin 7.
- D7 al pin 6.
- A va a 5V.
- K va a tierra.
- Del sensor de efecto Hall 1
- Data va al pin 3.
- Del sensor de efecto Hall 2
- Data va al pin 4.
![]() |
| Dicen que en una bicicleta el polvo es sinónimo de uso rudo (o de que no se limpia seguido). |
Para alimentar todo el circuito, uso un power bank, al inicio planeaba usar una batería de litio de un teléfono viejo, pero por cuestiones de espacio, no fue posible, además por ahora, la carcasa será temporal por lo que todo se puede remover fácilmente (exceptuando la caja). Toda la alimentación se hace directo del power bank, incluso si miras bien el esquema, verás que se hace uso de los pines VIN y GND del arduino.
El software.
// 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 = 12, en = 11, d4 = 9, d5 = 8, d6 = 7, d7 = 6;
// 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 LED = 13;
int HallState = 0;
int Vuelta = 0;
int OldState = 0;
unsigned long OldTime = 0;
float Distancia = 0;
float Speed = 0;
//Incializar todo
void setup() {
Serial.begin(9600);
lcd.begin(16, 2);
lcd.setCursor(0, 0);
lcd.print("@XWork");
pinMode(HallSensor1, INPUT);
attachInterrupt(digitalPinToInterrupt(HallSensor1), GetVelocity, LOW);
pinMode(LED, OUTPUT);
}
// Calcular la velocidad
void GetVelocity() {
if (millis() - OldTime > 70) {
Speed = Llanta / ((float)( millis() - OldTime) / 1000) * 3.6;
OldTime = millis();
Distancia = Distancia + Llanta / 1000;
}
}
//Bucle principal
void loop() {
Serial.println("Velocidad en m/s");
Serial.println(Speed);
lcd.setCursor(0, 0);
lcd.print("v= Km/h D = Km");
lcd.setCursor(0, 1);
lcd.print(Speed);
lcd.setCursor(8, 1);
lcd.print(Distancia);
if ((millis() - OldTime) > 2000)
Speed=0.0;
}
Las conexiones se hacen de acuerdo al hardware.
Conclusion.
Los leo luego.
Vamos a programar #50 - ¿Debo usar las interrupciones de arduino?
En algunos de los post anteriores, vimos código que se encargaba de contar las vueltas que llevaba un motor (usando un sensor de efecto hall y un par de imanes), pero poco después, alguien me contacto en twitter y me dijo que esa no era la manera correcta de hacer la medición, que arduino posee las herramientas necesarias para medir de una manera más precisa. En arduino, es posible asignarle prioridad a uno de los pines, dependiendo de modelo de la placa, al menos dos pines tendrán esta capacidad.
Las interrupciones, se dan cuando se detecta un valor en un pin, siempre y cuando este se haya inicializado con attachinterrupt(), esta es una manera de indicar que uno de los pines tendrá prioridad y cuando su valor cambie (ahorita veremos de que modo), se llevará a cabo cierta acción, esto es independiente a lo que arduino este haciendo.
La función attachinterrupt(), toma 3 argumentos. El primero; es el pin en el cual arduino "vigilará". El segundo; es la función que se ejecutará cuando se detecte un cambio en el pin designado. El tercero, será la forma en que arduino se dará cuenta del cambio.
Para designar el pin, debemos de tomar en cuenta el modelo de arduino, en mi caso, el arduino uno dispone de los pines 2 y 3 (para los modelos Mega y Micro tambien aplica).
La función que se usará cómo segundo parametro, no debe de requerir de ningun parametro y tampoco debería de devolver nigun valor.
Para el modo en que arduino detectará el cambio, disponemos de los siguientes:
- LOW
- CHANGE
- RISING
- FALLING
- HIGH
Para apreciar mejor, veamos el siguiente fragmento de codigo:
#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 9, d5 = 8, d6 = 7, d7 = 6;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
const float Llanta = 1.9572;
//Algunas variables
const int HallSensor = 3;
const int LED = 13;
int HallState = 0;
int Vuelta = 0;
int OldState = 0;
unsigned long OldTime = 0;
float Distancia = 0;
float Speed = 0;
//Incializar todo
void setup() {
Serial.begin(9600);
lcd.begin(16, 2);
lcd.setCursor(0, 0);
lcd.print("@XWork");
pinMode(HallSensor, INPUT);
attachInterrupt(digitalPinToInterrupt(HallSensor), GetVelocity, LOW);
pinMode(LED, OUTPUT);
}
void GetVelocity() {
if (millis()-OldTime > 70) {
Speed=Llanta/((float)(millis()-OldTime)/1000)*3.6;
OldTime=millis();
Distancia=Distancia+Llanta/1000;
}
}
//Bucle principal
void loop() {
Serial.println("Velocidad en m/s");
Serial.println(Speed);
lcd.setCursor(0, 0);
lcd.print("v= Km/h D = Km");
lcd.setCursor(0, 1);
lcd.print(Speed);
lcd.setCursor(8, 1);
lcd.print(Distancia);
if ((millis()-OldTime)>2000){
Speed=0.0;
}
}
Podemos observar que además de la declaración normal del pin (3), justo debajo hacemos la inicialización del pin que se usara para las interrupciones y hacemos uso de la función digitalPinToInterrupt() y le asignamos cómo parámetro el pin que antes habíamos iniciado (el pin 3), luego pasamos cómo parámetro la función GetVelocity(), esta es la que se ejecutará cada vez que se de la interrupción. Finalmente en el ultimo parámetro, le decimos que la interrupción se dará cuando el pin este en LOWEl código anterior es la mejor forma de hacer la cuenta, el código de post anterior cumplía y funcionaba, pero si por alguna razón la rueda girara muy rápido, llegaría el punto en que algunas de las lecturas del sensor del lector serian omitidas. Y bien, por ahora es todo en el próximo post terminaremos con este preyecto.
Los leo luego.
Vamos a programar #49 - Inútil apps #3 - Cómo calcular la velocidad.
Para que el velocimetro quede bien, pienso que al menos debe de medir lo siguiente:
- Velocidad (obviamente).
- Distancia.
- RPM (revoluciones por minuto).
Al mirar el enunciado anterior, podemos determinar que es lo que ya tenemos; la velocidad, es lo que queremos saber, distancia, esta la podemos obtener y el tiempo; para este, arduino cuenta con varias formas de "contar el tiempo".
Obteniendo la distancia.
| Diámetro | Medida rueda (mm) |
|---|---|
| 16x1.75x2 | 1272 |
| 20x1.75x2 | 1590 |
| 24x1 3/8 | 1948 |
| 24x1.75x2 | 1907 |
| 26x1 | 1973 |
| 26x1.5 | 2026 |
| 26x1.6 | 2051 |
| 26x1.75x2 | 2070 |
| 26x1.90 | 2089 |
| 26x1.95 | 2050 |
| 26x2.00 | 2114 |
| 26x2.125 | 2133 |
| 26x1 3/8 | 2105 |
| 26x1 3/8x11/2 | 2086 |
| 26x3/4 | 1954 |
| 27x1 1/4 | 2199 |
| 27x1 1/4 Fifty | 2174 |
| 27.5x2.10 | 2150 |
| 27.5x2.25 | 2195 |
| 28x1.5 | 2224 |
| 28x1.75 | 2268 |
| 28x1 1/2 | 2265 |
| 28x1 3/8x1 5/8 | 2205 |
| 29 x 2.10 | 2300 |
Otra opción y la mas viable, es medir la circunferencia dando una vuelta, es decir poner una marca en la rueda y en el suelo; pedalear hasta dar una vuelta completa y medir la longitud de la linea.En mi caso la mediad resultante fue de 194.6 cm y esta medida es la mejor, porque ya cuenta con mi peso y si vemos no es muy diferente a la del calculo anterior.
Ahora ya podemos tener la distancia, sabemos, que en mi caso, cada vuelta representan 195.72 cm, la manera simple, es sumar cada medición, por lo que si al dar una vuelta sumamos 195.72 cm, al dar la segunda tendremos 391.44 cm y asi sucesivamente, por lo que la distancia la podemos definir de la siguiente manera: d=195.72 * N° de vuelta, para hacer más fácil la visualización, vamos a convertir los 195.72 cm a m lo que resulta que cada vuelta son 1.9572 m.
Obteniendo la velocidad.
// {...}
//obtener la velocidad
float GetVelocity(){
float Velocity = Distancia/((float)(millis()-OldTime)/1000);
OldTime=millis();
return Velocity;
}
// {...}
Con esta estaremos midiendo la velocidad en metros por segundo. para hacer la conversion a Km/h, debemos de multiplicar por 3600 (segundos en una hora) y dividir entre 1000 (metros por cada kilometro).
Hay que tener en cuenta algunas consideraciones, ¿cuando es prudente decir que la bicicleta se detuvo? en mi experiencia, el usuario normal, no puede ir muy lento asi que haremos las preparaciones para que cuando el sensor no se active en al menos tres segundos, asumamos que la bicicleta se ha detenido.
Y bien, por ahora es todo, en el siguiente post implementaremos todo el código neceario.
Los leo luego.
Vamos a programar #48 - Inútil apps #3 -Velocímetro para bicicleta, sensor de efecto Hall.
Cómo algunos sabrán, tengo cierta afición por hacer viajes en bicicleta, pero cómo soy un hombre de números, siempre me ha gustado cuantificar lo que hago (en medida de lo posible), así que decidí llevar la cuenta de la actividad que hacía. Por fortuna, muchos de los teléfonos actuales, tienen aplicaciones que ayudan a llevar el registro (gracias a sensores y al GPS).
Hasta hace poco tenía la idea de hacer una versión propia de un programa que lleve todos esos datos, pero un par de eventos bastante desafortunados, bastaron para pensarlo dos veces. Entonces opté por comprar un velocímetro, lo encargue desde China y tenía un tiempo estimado de entrega de dos meses, pero por sismos y varias cosas, no llegaron (ya va medio año prácticamente, pero aun espero su llegada junto con la de algunos paquetes mas grandes).
Al revisar cómo es que funcionan, noté que la gran mayoría funcionan de la misma manera; se pone un sensor en la tijera que sostiene una llanta y cada vez que ésta da una vuelta, de alguna manera se notifica. En el caso del velocímetro que encargue, en el manual (de internet) decía que se debía de pegar un imán a un rayo de la llanta y el sensor ponerse fijo en la tijera, al revisar más a detalle, averigüe que se trataba de un sensor de efecto Hall.
El sensor de efecto Hall.
Ahora veremos cómo conectar el sensor de efecto hall, para hacerlo, necesitas lo siguiente:El sensor de efecto Hall o simplemente sensor Hall o sonda Hall (denominado según Edwin Herbert Hall) se sirve del efecto Hall para la medición de campos magnéticos o corrientes o para la determinación de la posición en la que está.Si fluye corriente por un sensor Hall y se aproxima a un campo magnético que fluye en dirección vertical al sensor, entonces el sensor crea un voltaje saliente proporcional al producto de la fuerza del campo magnético y de la corriente. Si se conoce el valor de la corriente, entonces se puede calcular la fuerza del campo magnético; si se crea el campo magnético por medio de corriente que circula por una bobina o un conductor, entonces se puede medir el valor de la corriente en el conductor o bobina.Si tanto la fuerza del campo magnético como la corriente son conocidos, entonces se puede usar el sensor Hall como detector de metales. Wikipedia/SensorHall
- Sensor efecto hall.
- Resistencia 1KΩ ~10KΩ.
- Arduino
- Supply (4.5~24V).
- Ground.
- OutPut.
Con lo anterior pasemos al código.
//Pin al que coenctaremos el sensor
const int HallSensor = 3;
//LED para mostrar el cambio en caso de no disponer del monitor serie
const int LED = 13;
//Estado del sensor
int HallSensorVal = 0;
//Número de vuelta
int Lap = 0;
//Estado anterior del sensor
int OldState = 0;
//Inicializar lo necesario
void setup() {
Serial.begin(9600);
pinMode(LED, OUTPUT);
pinMode(HallSensor, INPUT);
}
//Bucle princial
void loop() {
HallSensorVal = digitalRead(HallSensor);
if (OldState != HallSensorVal) {
if (HallSensorVal == LOW) {
digitalWrite(LED, HIGH);
Lap += 1;
Serial.println("Vuelta:");
Serial.println(Lap);
delay(1);
}
else {
digitalWrite(LED, LOW);
}
}
if (Lap > 99)
Lap = 1;
OldState = HallSensorVal;
}
El código anterior, lo único que hace, es verificar cual es el estado del sensor, cuando esta en modo "LOW", prende el LED que viene por default en el pin 13 y a la variable "Lap" le suma 1 (indicando una vuelta). Para probar el código anterior, necesitaras también un par de imanes, en mi caso, los extraje de un quemador de CD viejo, y los pegué en un motor (también extraído del lector de CD).
El resultado:
Con esto podemos probar que el sensor funciona de manera correcta.
Y bien, por ahora es todo, en el siguiente post, veremos cómo medir distancia y velocidad con el mínimo hardware posible. Aun no he decidido si usar las matrices para mostrar los datos o usar una pantalla LCD (cómo la del nokia) o incluso pantalla de siete segmentos. El código lo puedes copiar y pegar para usarlo, en cuanto este completo lo subire a mi dropbox.
Los leo luego.































