Voltimetro con 6 LEDs

Aquí os dejo un programa hecho para el arduino duemilanove, que hace la función de voltímetro usando 6 leds.

Poniendo le un par de diodos de pequeña señal, una resistencia y un condensador se podría convertir en un vúmetro de audio sin mucho problema.

El valor de la variable max establece el fondo de escala, que correspondería a la tensión que haría falta para que los 6 diodos LEDs permaneciesen encendido. El programa viene por defecto para que funcione entre los 0 a 5v, es decir que cada LED se encendería por cada múltiplo de 0,83v.

Las aplicaciones de este programa pueden ser muchas y muy variadas, como medir tensiones, temperaturas, o cualquier magnitud que se pueda traducir a un voltaje de entre 0 a 5 voltios o de 0 hasta 0,029v.

CODIGO:

/*
BUMETRO CON 6 LEDs

HECHO PARA ARDUINO DUEMILANOVE

CONEXIONADO
- Conectar el ánodo de los diodos LEDs a los pines digitales 2,3,4,5,6 y 7
- Colocar una resistencia de 220 ohmios de los cátodos de los diodos a masa

AUTOR: Mario R.P - mariorp@terra.es - 13-6-2012

*/

int sensorValue = 0; // variable de tensión detectada en el pin analógico 0
long max=170;  // El valor de la variable max corresponde al nivel de tensión por el que se encenderán los diodos. 

// Ejmeplo: 170 corresponde a un rango de entrada por el pin 0 de 0v a 5v (0= los 6 diodos apagados y 5v todos los leds encendidos). 
// Si queremos un rango de por ejemplo 0v a 0,5v, el valor de max sería de 17 en vez de 170. 
// NOTA: el pin 0 tiene que estar conectado a masa a través de una resistencia, aunque sea de muy alta resistencia, ya que de lo contrario, las lecturas serán por el efecto campo, es decir, serán lecturas erróneas.



void setup() {                

  pinMode(2, OUTPUT);    //se establecen los pines digitales de salida
   pinMode(3, OUTPUT);    
    pinMode(4, OUTPUT);    
     pinMode(5, OUTPUT);    
      pinMode(6, OUTPUT);    
       pinMode(7, OUTPUT);     
}

void loop() {  

  sensorValue = analogRead(A0);   // se establece el pin 0 como pin analógico de entrada
   
  if(sensorValue>((1023/max)*1)) // Las condiciones anidadas enciende un LED si la tensión es superior a la establecida por max.
  
  {
      digitalWrite(2, HIGH);  
 
        if(sensorValue>((1023/max)*2))
  
        {
            digitalWrite(3, HIGH);   
            digitalWrite(2, HIGH); 
               if(sensorValue>((1023/max)*3))
  
              {
                  digitalWrite(4, HIGH);  
                  digitalWrite(3, HIGH); 
                  digitalWrite(2, HIGH); 
                       if(sensorValue>((1023/max)*4))
  
                        {
                        digitalWrite(5, HIGH);  
                        digitalWrite(4, HIGH); 
                        digitalWrite(3, HIGH); 
                        digitalWrite(2, HIGH); 
                              if(sensorValue>((1023/max)*5))
  
                              {
                              digitalWrite(6, HIGH);   
                              digitalWrite(5, HIGH); 
                              digitalWrite(4, HIGH); 
                              digitalWrite(3, HIGH); 
                              digitalWrite(2, HIGH); 
                                     if(sensorValue>((1023/max)*6))
  
                                      {
                                      digitalWrite(7, HIGH);      
                                      digitalWrite(6, HIGH); 
                                      digitalWrite(5, HIGH); 
                                      digitalWrite(4, HIGH); 
                                      digitalWrite(3, HIGH); 
                                      digitalWrite(2, HIGH);                                      
                                      }
                                      else
                                      {
                                      digitalWrite(7, LOW);    
                                      }                  
                              
                              }
                              else
                              {
                              digitalWrite(6, LOW);   
                              digitalWrite(7, LOW);  
                              }                  
                          }
                        else
                        {
                        digitalWrite(5, LOW);  
                        digitalWrite(6, LOW);
                        digitalWrite(7, LOW);
                        }          
                  
              }
                  else
              {
                  digitalWrite(4, LOW);  
                  digitalWrite(5, LOW);   
                  digitalWrite(6, LOW);
                  digitalWrite(7, LOW);
              }     
            
        }
        else
        {
            digitalWrite(3, LOW);    
            digitalWrite(4, LOW);    
            digitalWrite(5, LOW);   
            digitalWrite(6, LOW);
            digitalWrite(7, LOW);
        }     
  }
  else
  {
      digitalWrite(2, LOW);    
      digitalWrite(3, LOW);   
      digitalWrite(4, LOW);   
      digitalWrite(5, LOW); 
      digitalWrite(6, LOW);
      digitalWrite(7, LOW);
  }
  
  
delay(100); // tiempo de muestreo
}

Lo primero de todo, muchas gracias por el aporte.

Permiteme que te haga unas observaciones que espero te sirvan para mejorar el código:

  1. Utiliza la etiqueta de inserción de código en los post (justo encima de :wink: )

  2. Utilizas max para definir el fondo de escala, pero realmente no es una variable, ya que no la modificas en el código. Si la defines como constante, los umbrales también son constantes y el micro no tiene que realizar la operación dentro de cada if. Puedes utilizar defines:

#define MAXIMO 170
#define NIVEL_1 (1023/MAXIMO)*1)
#define NIVEL_2 (1023/MAXIMO)*2)
...
if(sensorValue>NIVEL_1)...
  1. Al hacer los if anidados, estás repitiendo código para una misma acción: Si sensorValue>((1023/max)*6) enciendes 6 veces el led de la pata 2 (digitalWrite(2, HIGH)). Una solución más sencilla (sin repeticiones) podría ser:
if(sensorValue>((1023/max)*1)) // Las condiciones anidadas enciende un LED si la tensión es superior a la establecida por max.
{
  digitalWrite(2, HIGH);  
  if(sensorValue>((1023/max)*2))
  {
    digitalWrite(3, HIGH);   
    if(sensorValue>((1023/max)*3))
    {
      digitalWrite(2, HIGH); 
      if(sensorValue>((1023/max)*4))
      {
        digitalWrite(5, HIGH);  
        if(sensorValue>((1023/max)*5))
        {
          digitalWrite(6, HIGH);   
          if(sensorValue>((1023/max)*6))
          {
            digitalWrite(7, HIGH);      
          }
          else
            digitalWrite(7, LOW);    
        }
        else
          digitalWrite(6, LOW);   
      }
      else
        digitalWrite(5, LOW);  
    }
    else
      digitalWrite(4, LOW);  
  }
  else
    digitalWrite(3, LOW);    
}
else
  digitalWrite(2, LOW);

Otra opción sería no hacer los ifs anidados.

if(sensorValue>((1023/max)*1)) // Las condiciones anidadas enciende un LED si la tensión es superior a la establecida por max.
  digitalWrite(2, HIGH);  
else
  digitalWrite(2, LOW);
  
if(sensorValue>((1023/max)*2))
  digitalWrite(3, HIGH);   
else
  digitalWrite(3, LOW);
...

Ya contarás que te parecen las observaciones

Muchas gracias por tus comentarios y consejos, los voy a tener en cuenta.

Efectivamente max no es una variable, es una constante y habría que definirla como tal.

Con respecto a los anidamientos, lo he hecho de muchas formas pero siempre me da problemas, sobre todo, cuando la señal de entrada es más rápida que la velocidad de muestreo, que está definida con un delay a unos 100 mili segundos. La velocidad de muestreo tiene que ser esa, ya que he probado con otras velocidades y la lectura visual a velocidades mayores es difícil de leer si hablamos de lecturas de tensión variables, que varían con mucha rapidez.

Los anidamientos están hechos de esa manera por que me surgieron problemas, haciendo lo de otra forma. El problema principal es que si las fluctuaciones en la tensión de entrada son más rápidas que la velocidad de muestreo, el led que leyó la tensión más alta, permanece encendido, mientas que los leds de menos peso, siguen indicando otro nivel de tensión. Es decir, que si la tensión varía mucho en muy poco tiempo, la fila de leds, muestran lecturas extrañas.

El código que muestro en este hilo desde luego que se puede depurar, pero funciona correctamente, ya que lo he probado con diferentes sensores y en diferentes aplicaciones y siempre funciona correctamente, aunque sea un código realmente largo, para algo tan simple, es la única forma que he encontrado de hacer lo que quería y que funcionase bien.

Pero no digo que no se pueda mejorar, que conste, aun así, muchas gracias nuevamente por tus comentarios y consejos.

Como en breve posteare un poco de codigo de una cuenta atrás te comento un poco tu codigo desde mi punto de vista asi me pillas manía y lo pones verde.

Realmente no estas tomando muestras y luego actúas en consecuencia sino que tomas un valor actúas y pausas la aplicación. Lo que entiendo por tomar muestras seria medir n veces y mostrar resultados o bien leer y si hay cambios en la lectura mostrarlo con los leds.

Como aproximación seguro que el sketch es correcto y mas cuando hace lo que debe. Los "costes" en cálculos creo que son buenos y es mas acertado los if anidados, visualmente feo pero eso da igual.

Lo que podrías hacer es una acción que tome muestras durante n tiempo y finalizado este muestre los resultados. Ya sea dentro el loop con millis() o con interrupciones. O Poner algún tipo de filtro pasa altas para evitar esos efectos de cambios rápidos.

Al igual te gustaría saber que con amplificadores operaciones puedes hacer lo mismo que estas haciendo.

También puedes mirarte un 74hc595 donde puedes encender n leds con solo 4 pines.

En fin son maneras de rizar el rizo para tampoco llegar a ninguna parte... Bueno jugar con el arduino.

Miniduino tiene razón en el tema de la toma de muestras. Aplicando ese método evitarás cualquier fluctuación que te hubiese aparecido antes y parpadeos rápidos de los leds que aún te pueden aparecer ahora, incluso el problema de la velocidad de muestreo que también comentas.

El tema del coste de tiempo de los cálculos no es por la aplicación, sino por principios. Es un tema que hay que tener siempre en cuenta, porque sino, cuando sea necesario no se va a aplicar (el hombre es un animal de costumbres, ya se sabe). Para mi, está al mismo nivel que el uso de la función delay() (función que odio y evito siempre que puedo) ya que supone un derroche de recursos, aunque para la gran mayoría de aplicaciones nos sobren.

En cuanto al anidamiento de los ifs...La verdad es que lo puse porque es una segunda opción que parece más simple (y también funciona), aunque como habéis dicho (y yo pienso igual) no es lo adecuado.

miniduino:
En fin son maneras de rizar el rizo para tampoco llegar a ninguna parte... Bueno jugar con el arduino.

Esa es la finalidad: jugar con el arduino, ¿no? XD

Bien gracias a todos por vuestros comentarios, lo tendré en cuenta.