Problema leer analogRead con tolerancias

Muy buenas.

Tengo el siguiente problema para resolver. Tengo puesto en el pin A0 unos pulsadores con sus resistencias, es decir he montado un "teclado" de 7 botones los cuales leo el valor con este código;

#include <LiquidCrystal_I2C.h>
#include <Wire.h>

#define I2C_ADDR    0x3F // La dirección I2C del LCD
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7

LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);

int a=0;
 
void setup()
  {
    lcd.begin(20, 4);
    lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
    lcd.setBacklight(HIGH);
   }
 
void loop()
{
  a = analogRead(0);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("  analogRead() ");
  lcd.setCursor(0,1);
  lcd.print("  valor es :");
  lcd.print(a);
  delay(250);
}

Hasta aquí todo bien. El problema es que es muy inestable. Por ejemplo leyendo el segundo pulsador me da un valor de 145 y a veces 149. Luego tengo otro código por el cual con un case y break defino este dato. El problema es cuando en el case porgo;

case 145:

hay veces que no lo coje debido a que el valor es 148 y entonces no me lee el pulsador, me imagino que es por tolerancias de las resistencias y de la alimentación. Entonces como le puedo decir al case que lea desde 140 hasta 160 y me reconozca el dato dandole amplitud de tolerancia?

Agradezco sus ayudas. Gracias!

Porque no prueba con el condicional if?

Ejemplo:

if(a>144 && a<160){ //Aquí defines un rango en el cual si la var. a se sitúa entre los valores
//de 145 y 159 ejecutarás la acción que esté entre las llaves.
Aquí realizas la acción que necesites
}

Saludos.

Lo he probado y no me funciona.
Este es el código que estoy usando;

void handleKeys() {
 // unsigned short keyState = analogRead(0);
  //const int keyState = analogRead(0);
  int keyState = analogRead(0);
	if (keyState == 1023 || millis() - lastKeyPress < 100) return;
 
	switch (keyState) {
    if (keyState >2 && keyState <15) return;
    		case 1:
      
			if (radio.state.volume < 15) {
				radio.setVolume(radio.state.volume + 1);
				displayTimeout = millis() + 5000;
				currentDisplay = SHOW_VOLUME;
        radio.setMute(false);
        lcd.setCursor(0, 3);
        lcd.print("                    "); 
       // lcd.setCursor(14, 1);
       // lcd.print("STEREO"); 
			}
   
			break;

Ese código es el del teclado del LCD Shield.
Este es un código que puede servirte, mira cual se adapta mejor a tu necesidad.
LCD SHIELD Keyboard

//Sample using LiquidCrystal library
#include <LiquidCrystal.h>

/*******************************************************
This program will test the LCD panel and the buttons
Mark Bramwell, July 2010
********************************************************/
// select the pins used on the LCD panel
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

// define some values used by the panel and buttons
int lcd_key     = 0;
int adc_key_in  = 0;
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5

// read the buttons
int read_LCD_buttons() {
 adc_key_in = analogRead(0);      // read the value from the sensor 
 // my buttons when read are centered at these valies: 0, 144, 329, 504, 741
 // we add approx 50 to those values and check to see if we are close
 if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result
 // For V1.1 us this threshold
 if (adc_key_in < 50)   return btnRIGHT;  
 if (adc_key_in < 250)  return btnUP; 
 if (adc_key_in < 450)  return btnDOWN; 
 if (adc_key_in < 650)  return btnLEFT; 
 if (adc_key_in < 850)  return btnSELECT;  

 // For V1.0 comment the other threshold and use the one below:
/*
 if (adc_key_in < 50)   return btnRIGHT;  
 if (adc_key_in < 195)  return btnUP; 
 if (adc_key_in < 380)  return btnDOWN; 
 if (adc_key_in < 555)  return btnLEFT; 
 if (adc_key_in < 790)  return btnSELECT;   
*/


 return btnNONE;  // when all others fail, return this...
}

void setup() {
 lcd.begin(16, 2);              // start the library
 lcd.setCursor(0,0);
 lcd.print("Push the buttons"); // print a simple message
}
 
void loop() {
 lcd.setCursor(9,1);            // move cursor to second line "1" and 9 spaces over
 lcd.print(millis()/1000);      // display seconds elapsed since power-up

 lcd.setCursor(0,1);            // move to the begining of the second line
 lcd_key = read_LCD_buttons();  // read the buttons

 switch (lcd_key) {             // depending on which button was pushed, we perform an action
   case btnRIGHT:
     lcd.print("RIGHT ");
     break;
   case btnLEFT:
     lcd.print("LEFT   ");
     break;
   case btnUP:
     lcd.print("UP    ");
     break;
   case btnDOWN:
     lcd.print("DOWN  ");
     break;
   case btnSELECT:
     lcd.print("SELECT");
     break;
     case btnNONE:
     lcd.print("NONE  ");
     break;
 }
}

Este código funciona para el LCD SHIELD de modo que si tu no usas la configuración de resistencias del mismo, no va a responder del mismo modo pero la idea es esa.
Para cada pulsador tendras un min y un máximo, ve anotándolos para cada uno.
Al final tendras una tabla.
La idea es que fijes un extremo de cambio qeu aseguro que toda fluctuación me represente ESA tecla y no otra.
Si miras el código acá hay 5 teclas y tu hablas de 7.
Entonces mira el primero if
if (adc_key_in < 50)  return btnRIGHT;
dice que si una tecla en este caso el boton derecho es presionado, se esperan valores menores a 50 unidades del ADC.
Si presionas la tecla P estarás entre 50 y 249

if (adc_key_in < 250)  return btnUP;

y de ese simple modo se conforman todas las opciones y te aseguras que no importa como varíe siempre responderá a la tecla correcta.

Muchas gracias :slight_smile: Al final . lo he solucionado.
@surbyte ayer estuve toda la tarde con esto, leyendo, revisando he incluso creo que ví la forma que lo hacia la Shield LCD pero aun así no me llegó a funcionar y @mike_117 me dio un poco la luz.

Vale entonces he realizado lo siguiente he definido las variables;

// Definir los botones (Pulsadores)
#define btnVOLMAS     0
#define btnARRIBA     1
#define btnABAJO      2
#define btnVOLMENOS   3
#define btnMUTING     4
#define btnNINGUNO    5
#define btnSTEREO     6
#define btnRESET      7

int tecla_pulsada = 0;

Luego el programa de los valores de las variables;

// Convertir los valores analógicos del pin A0 de Arduino
// Los botones (pulsadores) tienen tolerancias por esto le pongo que no superen
// el valor real y dando un margen del 15% de tolerancia o quizás mejor el 20%
  int leer_botones() {
  int valor_boton = analogRead(0);
  if (valor_boton > 1000)                            return btnNINGUNO;   // Ningun boton
  if (valor_boton >2 && valor_boton <10)       return btnVOLMAS;    // Volumen +
  if (valor_boton >499 && valor_boton <515)  return btnVOLMENOS;  // Volumen -
  if (valor_boton >140 && valor_boton <150)  return btnABAJO;     // Escanear MHz -
  if (valor_boton >319 && valor_boton <355)  return btnARRIBA;    // Escanear MHz +
  if (valor_boton >720 && valor_boton <740)  return btnMUTING;    // Boton Mute
  if (valor_boton >830 && valor_boton <845)  return btnSTEREO;    // Forzar Stereo
  if (valor_boton >210 && valor_boton <230)  return btnRESET;     // Boton Rset
  return btnNINGUNO;
}

Si te fijas he logrado de cada botón (pulsador) darle un valor comprendido entre su valor REAL, usando 5 botones no hay problema, el problema viene cuando le he puesto 7, por ello le he dado su "tolerancia"

Entonces con;

void funciones_botones() {
   tecla_pulsada = leer_botones();
  
    switch (tecla_pulsada) {
      case btnVOLMAS:
		  	if (radio.state.volume < 15) {
		       radio.setVolume(radio.state.volume + 1);
			displayTimeout = millis() + 5000;
			currentDisplay = SHOW_VOLUME;
        radio.setMute(false);
        lcd.setCursor(0, 3);
        lcd.print("                    "); 
        lcd.setCursor(14, 1);
        lcd.print("STEREO"); 
			}
   
			break;

...
...
...
 // mas opciones
..
..
..
}

Con poner en el case la variable del botón, el valor recogido según este ultimo. Como dije estuve toda la tarde y no daba con la solución por ello les pedí ayuda y veo que todo el tiempo empleado aunque no funcionará de esta forma es como uno aprende. Tema solucionado y daros las gracias sin el cual no hubiese funcionado.

Saludos. :slight_smile:

El tema de mike y el que yo te puse son lo mismo.
Cada if del ejemplo SHIELD LCD tiene incluido una condición que ya no puede darse.
Te lo expliqué asi que no entiendo como no lo entendiste.
Si el primer if responde a

if (adc_key_in < 50)   return btnRIGHT;

el segundo

if (adc_key_in < 250)  return btnUP;

responde a todo lo que esta entre 50 y 249, entonces para que poner esto?

[color=#222222]if ([/color][color=#222222]adc_key_in >=50 && [/color][color=#222222]adc_key_in < 250)  return btnUP;[/color]

es redundante porque ya esta hecho por la instrucción previa.
Es un código que funciona de ese modo, no puede cambiar el orden de los if porque cada uno esta anidado
al anterior y la función return sale de la función con el valor encontrado.
Si ya lo resolviste no interesa pero mira que código mas simple te queda de este modo comparado
con el tuyo, que nada tiene de malo (aclaro). Solo es relevante cuando tienes mucho código por escribir y
cada byte cuenta.