Cómo eliminar seis botones en un pin analógico con Arduino

Estoy usando el pin analógico 5 en Arduino para detectar presiones de 6 botones. En la imagen, el botón superior derecho es el número 1 y luego, de derecha a izquierda, van como 2, 3, 4, 5, 6. El programa debe imprimir 0 cuando no se presiona ninguno de los botones y si se presiona uno de ellos, debería imprime su posición como mencioné antes. Actualmente, el problema es que si presiono el segundo botón, a veces (en lugar de solo una vez) imprimirá 2 un par de veces. Supongo que es por el "ruido" cuando se presiona el botón y que debe ser rebotado, pero no sé cómo rebotar el pin analógico.

Mi código:

int old_button = 0;
int button;
int pressed_button;
int z;

void setup () {
  Serial.begin(9600);
  pinMode(A5, INPUT);
}

void loop () {
  z = analogRead(5);
  if (z > 1021) button = 0;                                           
  else if (z > 511 && z < 514) button = 1;                     
  else if (z > 680 && z < 684) button = 2;                
  else if (z > 766 && z < 770) button = 3;                
  else if (z > 817 && z < 822) button = 4;             
  else if (z > 851 && z < 856) button = 5; 
  else if (z > 875 && z < 880) button = 6;
  else button = 0;                                                      

  if (old_button == button) {                                           
    old_button = button;                                              
    pressed_button = 0;                                               
  }  

  else {                                                                
    old_button = button;                                             
    pressed_button = button;                                        
  }
  Serial.println(pressed_button);
}

Circuito (resistencias de 2200 ohmios):

ingrese la descripción de la imagen aquí

Un esquema eléctrico sería mejor que un diagrama de Fritzing, en este caso específico. Utilice el editor de esquemas integrado.
@Mate Solo para tu información. Ahora hay una pila Arduino especializada: arduino.stackexchange.com
¿Puede esta solución funcionar con el modo DEEP SLEEP (usando 0.2 uA en promedio con un ATmega o ATtiny en la placa) y la interrupción de cambio de pin (PCINT)?

Respuestas (4)

Cuando detecte una diferencia significativa en la lectura de ADC, espere 20 milisegundos y luego promedie algunas lecturas y luego tome una decisión. Si una de las lecturas todavía se ve mal cuantificable, espere otro período corto de tiempo.

+1 por respuesta. hola Andy, ¿es prudente conectar a tierra un capacitor de 104 de la línea de botones (para desbaratar)?
@roh no es sabio si significa conectar un interruptor a través de un límite cargado de 100nF porque el pulso actual podría restablecer la MCU. Lo mejor es poner 100 ohmios en serie con el interruptor.
@Andyaka: ¿Podría escribirme un pseudocódigo para su respuesta?
@Mate, soy un tipo verbal (bueno, ¡la mayoría de las veces y algunas veces indescifrable!) Y no he escrito pseudocódigo en milenios. No debería ser difícil visualizar lo que estoy diciendo: me gustan los circuitos, pero no te pedí que cambiaras esa cosa horrible y fritzing a una LOL
@Andyaka ¿El pulso actual de 100nF podría restablecer la MCU? ¡¿Qué tipo de MCU de mierda sin valor es ese?! Tíralo al instante, consigue uno de verdad...
@Lundin No se apresure. Si el diseño de la placa de circuito no es bueno, la corriente (decenas de amperios en unos pocos nanosegundos) puede provocar fácilmente un reinicio.
Mmm. Creo que estaba preguntando si podía agregar una tapa de desacoplamiento y usted respondió acerca de conectar una tapa en serie. En cualquier caso, probablemente querrá esa resistencia en serie allí de todos modos para obtener inmunidad ESD, MCU de mierda o no. Es probable que los botones se pongan en contacto con seres humanos cargados.

Sí, es ruido de rebote. Debe esperar un poco (digamos 50 ms) y leer la entrada analógica nuevamente. Si el resultado coincide, entonces el valor del botón puede considerarse válido. Algo como esto:

int old_button = 0;

int getButton()
{
  int i, z, sum
  int button;

  sum = 0;
  for (i=0; i < 4; i++)
  {
     sum += analogRead(5);
  }
  z = sum / 4;
  if (z > 1021) button = 0;                                           
  else if (z > 511 && z < 514) button = 1;                     
  else if (z > 680 && z < 684) button = 2;                
  else if (z > 766 && z < 770) button = 3;                
  else if (z > 817 && z < 822) button = 4;             
  else if (z > 851 && z < 856) button = 5; 
  else if (z > 875 && z < 880) button = 6;
  else button = 0;

  return button;
}


void loop ()
{
  int button, button2, pressed_button;  
  button = getButton();
  if (button != old_button)
  {
      delay(50);        // debounce
      button2 = getButton();

      if (botton == button2)
      {
         old_button = button;
         presed_button = button;
         Serial.println(pressed_button);
      }
   }
}

Como Andy no quería proporcionar ningún código, agregué algunos promedios al tomar las lecturas de ADC. Entonces, el promedio dentro de getButton tiene en cuenta cualquier ruido que lea la línea analógica que ingresa al ADC, y el retraso de 50 ms se encarga de detectar el rebote del interruptor.

@Mate, agregué el código para promediar el ruido ADC a mi respuesta. Gracias a Andy alias por sugerirlo, fue una buena adición.

Muchos interruptores del estilo que está usando son difíciles de contrarrestar bien, y tratar de usar un multiplexor resistivo no lo hará más fácil. Aún así, si puede pagar dos pines de puerto (uno analógico), puedo ofrecerle una receta para el éxito que debería funcionar incluso si sus interruptores son bastante malos. Sugeriría mover la resistencia de la barra colectora (ignore la barra colectora inferior de la placa de pruebas) a la izquierda de la que está más a la izquierda y conectarla al pin del puerto no analógico. El cable común en la parte superior debe conectarse al pin del puerto analógico. El cable de la resistencia más a la derecha debe conectarse a tierra.

Cuando su unidad está "inactiva" [no cree que se presione ningún botón] configure la salida común en alto y haga flotar la otra. Se leerá bajo cuando no se presione ningún botón y se leerá cerca de VDD cuando se presione cualquier botón.

Para saber qué botón se presiona, haga flotar el cable común y lleve el cable del lado izquierdo hacia arriba. Configure brevemente el cable común alto o bajo (vea la nota) y hágalo flotar nuevamente, luego lea el voltaje en ese pin un poco más tarde (manteniendo alto el cable del lado izquierdo). Una vez que se ha tomado la lectura, si lo desea, puede apagar el cable del lado izquierdo (volver a encenderlo antes del siguiente ciclo de lectura).

Mientras se presiona un botón, el voltaje en el cable común debe ser una buena fracción de VDD (si hay seis botones y siete resistencias, los botones deben leer 1/7, 2/7, 3/7, etc. hasta 6 /7); el voltaje no debe verse demasiado afectado por si el pin común se pulsó brevemente hacia arriba o hacia abajo. Si no se presiona ningún botón, las lecturas después de que el pin haya sido pulsado alto serán mucho más altas que después de haber sido pulsado bajo. Esto indicará que se ha soltado el botón.

Una vez que se ha soltado el botón, puede volver a la configuración "inactiva". Cuando la resistencia izquierda se eleva, la cadena de resistencia consumirá corriente ya sea que se presione o no algún botón, pero cuando el pin izquierdo flota y el cable común está alto, no se extraerá corriente hasta que se presione un botón.

Para obtener buenos resultados con interruptores baratos, debe demorar lo suficiente después de cada pulso momentáneo de "tierra" o "VDD" en su cable común para que, si el interruptor hace algún contacto, produzca una buena lectura (intente colocar una resistencia de 100K en paralelo con un interruptor, y ajuste el retardo y la sensibilidad para que "apenas" registre que el interruptor se mantiene presionado). Los interruptores baratos tienen una resistencia de más de un mega cuando se sueltan por completo, y menos de 10 ohmios cuando se presionan por completo, pero su resistencia puede vagar por todos lados entre esos estados, y el tiempo de rebote convencional no ayudará. Lo que ayudará es tener un circuito que no detecte la pulsación de un botón hasta que la resistencia sea bastante baja, y considerará un botón como presionado a menos que su resistencia sea mucho mayor. Para el circuito que he descrito,

Esto suena muy bien, pero necesita algún tipo de gráfico.
Completamente de acuerdo. Un diagrama de circuito (incluso un boceto aproximado) realmente ayudaría a dilucidar la excelente respuesta.

Recientemente comencé a trabajar con Arduino, y aunque esta es una vieja pregunta, encontré este hilo cuando intentaba ampliar la cantidad de botones que podía escuchar.

Trabajé a partir de la ilustración original de Fritzing y produje una ligera variante tanto en hardware como en software.

Arduino Analógico escuchando 6 pulsadores

Modifiqué el conjunto de resistencias para usar diferentes valores para tratar de producir un paso más consistente entre los interruptores en lugar de los pasos logarítmicos producidos al usar las mismas resistencias en todo momento.

A partir de 5V, las resistencias son:

  • 1kΩ
  • 180Ω
  • 240Ω
  • 330Ω
  • 510Ω
  • 800Ω

Esto produce pasos de alrededor de 0,5 V entre cada interruptor.

También construí un software básico que busca el mismo valor para 3 lecturas seguidas antes de reconocer un cambio en el voltaje. Esto elimina pequeñas variaciones en la lectura.

int sensorPin = A5; // The input port
int sensorValue; // Current reading
int outputValue; // The reported reading
int lastValues[3] = {0,0,0}; // The last 3 readings

void setup() {
  Serial.begin(9600);
}

void loop() {
  // read the value from the sensor:
  sensorValue = analogRead(sensorPin);
  // Initialise variables for checks
  int i;
  int updateOutput = 1;
  // Loop through previous readings
  for( i = 0 ; i<3 ; i++ ){
    // If this historic value doesn't match the current reading,
    // we will not update the output value
    if( lastValues[i] != sensorValue ){
      updateOutput = 0;
    }
    // Shift the array elements to make room for new value
    if( i>0 ){
      lastValues[(i-1)] = lastValues[i];
    }
  }
  // Update if needed
  if( updateOutput == 1 ){
    outputValue = sensorValue;
  }
  // Append the new value
  lastValues[2] = sensorValue;
  // Debugging output
  Serial.print(sensorValue);
  Serial.print(" ");
  Serial.println(outputValue);
}

Obviamente, la lastValuesmatriz podría tener la longitud que desee: cuanto más larga sea la matriz, más tiempo se debe presionar un interruptor para que se detecte.

En mis pruebas, los valores del sensor informados fueron:

  • 1023: Sin botón
  • 0: Botón #1
  • 196: Botón #2
  • 301: Botón #3
  • 405: Botón #4
  • 508: Botón #5
  • 613: Botón #6

(Probado según el diagrama.)

Mis cálculos aproximados sugieren que incluso podría extender esto a 8 botones, agregando las siguientes resistencias a continuación (que deberían hacer que el voltaje vuelva a caer en pasos de 0.5V):

  • 1.7kΩ
  • 5kΩ