Comportamiento extraño segun fuente de alimentación

Hola, perdón si omito alguna norma en el planteo de la pregunta, espero que no.
Ocurre que realicé un juego tipo "simon dice" para mi hija, sobre una Leonardo (con 4 leds y 4 pulsadores). Sinceramente como programar no es mi fuerte, le pedí el código a ChatGPT y a partir de ahi lo he ido modificando hasta que funcione como quiero. Pero me está pasando algo que no puedo encontrar razón y paso a detallar:
En parte del código, hay un while (digitalRead(boton)==HIGH) sin nada dentro que simplemente cumple la función de esperar a que el usuario suelte el pulsador para continuar.
Cuando el juguete está conectado al USB de la PC, funciona todo impecable y como debe.
Cuando lo alimento por otra vía, por ejemplo: la bateria de 9v que le incorporé o el powerbank que tengo, hay uno de los 4 botones que cuando lo presiono, se queda como "tildado" durante algunos segundos, como si siguiera presionado aunque ya lo solté. Digamos, como si el programa se quedara atascado en ese while bastante tiempo a pesar de haber soltado el pulsador.
Ese tiempo varía, a veces es 1 segundo o puede ser hasta 3 segundos, y no sé de qué depende.
No es problema "físico", ya que si modifico el arreglo donde están asignado los pines de los botones, cambia el botón "problemático". Lo único que he identificado es que es el último lugar del arreglo el que falla, sea el pin 2 o el 7 es indistinto.

No encuentro problemas en el código, además insisto si está alimentado por la PC, funciona perfecto sin comprotamientos extraños.

Alguna idea de qué puede ser? Gracias.

Sin ver el código es imposible ayudarte.
Saludos

Efectivamente. Mi sospecha es un índice haciendo referencia a un elemento fuera del arreglo.

Oops!

¿Hay un arreglo? ¿Hay un índice? :thinking:

Hola, me da un poco de verguenza mostrar el código por lo desastroso que debe ser para alguien que sabe programar jaja pero aqui va. Hago antes algunos agregados al post original si no no se va a entender nada:
El juego es un 2 en 1: simon dice y dale al topo. Tengo una selectora y los pines A0 y A1 asignados a las posiciones de ambos juegos. Y si esta en otra posicion, es una version facilitada de Dale al topo (sin aumento de dificultad).
El problema se manifiesta en ambos juegos, en el mismo par boton/led. Es el asociado al ultimo elemento del vector buttonPins[] y ledPins[].
Insisto, el problema NO se manifiesta si esta la placa alimentada por la PC

Gracias por el tiempo dedicado de antemano,

#include <Config.h>

#define topopin A0
#define simonpin A1

const int buttonPins[] = {2, 3, 4, 5};
const int ledPins[] = {6, 7, 8, 9};
const int buzzerPin = 10; // Pin del buzzer
const int noteDuration = 450; // Duración de cada nota en milisegundos

int currentTopo;
int puntos = 0;
int rondas = 0;
unsigned long tiempoAparicion;
boolean ganador = false;
boolean primerjuego = true;
int tiempoencendido = 6000;
int tiempoespera = 1000;
int t_min_esp = 2000;
int t_max_esp = 5000;
boolean facil = false;

int sequence[100]; // Secuencia de notas generada aleatoriamente
int playerSequence[100]; // Secuencia ingresada por el jugador
int sequenceLength = 1; // Longitud de la secuencia actual
int playerIndex = 0; // Índice del jugador en la secuencia

void setup() {
  //Serial.begin(9600);
  pinMode(buzzerPin,OUTPUT);
  pinMode(topopin,INPUT_PULLUP);
  pinMode(simonpin,INPUT_PULLUP);
  
  for (int i = 0; i < 4; i++) {
    pinMode(buttonPins[i], INPUT_PULLUP);
    pinMode(ledPins[i], OUTPUT);
  }
  randomSeed(analogRead(0));
}

void loop() {
  delay(1);
  digitalWrite(buzzerPin,HIGH);
  if (digitalRead(topopin)==LOW){
  topo();
  } else if (digitalRead(simonpin)==LOW) {
  simon(); 
  } else {
    facil = true;
    topo();
  }
}
void topo(){  
  if (!ganador) {
    if (millis() - tiempoAparicion > tiempoencendido) {
          if (!primerjuego) {
            perdioRonda();
          } else {
            primerjuego = false;
            reiniciarJuego();
          }
        }
    for (int i = 0; i < 4; i++) {
      if (digitalRead(buttonPins[i]) == LOW) {
        delay(50);
          if (digitalRead(buttonPins[i]) == LOW) {
            analogWrite(buzzerPin,0);
          while (digitalRead(buttonPins[i]) == LOW) {
            // Espera hasta que se libere el botón
          }
          analogWrite(buzzerPin,255);
          if (i == currentTopo) {
            ganarRonda();
          } else {
            perdioRonda();
          }
        }
      }
    }
  } else {
    delay(tiempoespera); // Espera 1 segundo antes de comenzar un nuevo juego
    reiniciarJuego();
  }
}

void nuevoTopo() {
  int topoAleatorio = random(4);
  tiempoespera = random(t_min_esp,t_max_esp);
  delay(tiempoespera);
  digitalWrite(ledPins[currentTopo], LOW);
  currentTopo = topoAleatorio;
  digitalWrite(ledPins[currentTopo], HIGH);
  ruidotopo();
  tiempoAparicion = millis();
}

void ganarRonda() {
  ruidoacierto();
  for (int i=0;i<4;i++) {
    digitalWrite(ledPins[i], LOW);
  }
  puntos += tiempoencendido - (millis() - tiempoAparicion);
  rondas++;
  //Serial.print("Ganaste la ronda! Puntos acumulados: ");
  //Serial.println(puntos);

  if (rondas % 10 == 0) {
    // Disminuir el tiempo de aparición en un 10%
    for (int i=0;i<1+ rondas / 10;i++){
      for (int j=0; j<4;j++) {
        digitalWrite(ledPins[j],HIGH);
        delay(20);
      }
      for (int j=0; j<4;j++) {
        digitalWrite(ledPins[j],LOW);
        analogWrite(buzzerPin,8);
        delay(20);
        analogWrite(buzzerPin,255);
      }
      
    }
    if (!facil) {
    delay(1000);  
    t_min_esp *= 0.75;
    t_max_esp *= 0.75;
    tiempoencendido *= 0.9;
    }
  }

  if (rondas >= 100) {
    ganador = true;
    winner();
    } else {
    nuevoTopo();
  }
}

void winner(){
  int hora = millis();
  while (millis()-hora<10000){
    int i = random(0,4);
    digitalWrite(ledPins[i],HIGH);
    delay(25);
    digitalWrite(ledPins[i],LOW);
    delay(20);
  }
}
void perdioRonda() {
  ruidoperdio();
  t_min_esp = 2000;
  t_max_esp = 5000;
  tiempoencendido = 6000;
  reiniciarJuego();
}


void reiniciarJuego() {
  countdown();
  for (int i = 0; i < 4; i++) {
    digitalWrite(ledPins[i], LOW);
  }
  currentTopo = -1;
  puntos = 0;
  rondas = 0;
  ganador = false;
  nuevoTopo();
}
void countdown() {
  for (int j = 0; j < 4; j++) {
      digitalWrite(ledPins[j], HIGH);    
    }
  for (int i = 4; i > 0; i--) {
    digitalWrite(buzzerPin,LOW);
    delay(5);
    digitalWrite(buzzerPin,HIGH);
    delay(995);
    digitalWrite(ledPins[4-i], LOW);
    }
   delay(1000);    
}
void ruidotopo() {
    for (int i=0; i<3;i++){
      digitalWrite(buzzerPin,LOW);
      delay(20);
      digitalWrite(buzzerPin,HIGH);
      delay(20); // Espera 100 ms entre beeps
    }
 }
void ruidoacierto() {
   analogWrite(buzzerPin,4);;
   delay(200);
   analogWrite(buzzerPin,255);;
   
}
void ruidoperdio() {
  for (int i = 0; i<4; i++){
    digitalWrite(ledPins[0],HIGH);
    digitalWrite(ledPins[1],HIGH);
    digitalWrite(ledPins[2],HIGH);
    digitalWrite(ledPins[3],HIGH);
    analogWrite(buzzerPin,16);
    delay(250);
    digitalWrite(ledPins[0],LOW);
    digitalWrite(ledPins[1],LOW);
    digitalWrite(ledPins[2],LOW);
    digitalWrite(ledPins[3],LOW);
    analogWrite(buzzerPin,255);
    delay(250);
  }
}

void simon() {
  
 if (sequenceLength == 1) {
  countdown();
  generarSecuencia();
  }

  reproducirSecuencia();

  while (playerIndex < sequenceLength) {
    int buttonPressed = esperarBoton();
    if (buttonPressed == sequence[playerIndex]) {
      playerSequence[playerIndex] = buttonPressed;
      playerIndex++;
    } else {
      perderJuego();
    }
  }

  if (playerIndex == sequenceLength) {
    ruidoacierto();
    delay(1000); // Espera 1 segundo antes de la siguiente ronda
    sequenceLength++;
    playerIndex = 0;
  }
}

void generarSecuencia() {
  for (int i = 0; i < 100; i++) {
    sequence[i] = random(4); // Genera una secuencia aleatoria de 0 a 3
  }
}

void reproducirSecuencia() {
  for (int i = 0; i < sequenceLength; i++) {
    int note = sequence[i];
    digitalWrite(ledPins[note], HIGH);
        for (int i=0; i<note+1;i++){
          digitalWrite(buzzerPin,LOW);
          delay(50);
          digitalWrite(buzzerPin,HIGH);
          delay(50); 
        }
    delay(noteDuration);
    digitalWrite(ledPins[note], LOW);
    delay(50); // Espera 100 ms entre notas
  }
}

int esperarBoton() {
  while (true) {
    for (int i = 0; i < 4; i++) {
      delay(10);
      if (digitalRead(buttonPins[i]) == LOW) {
        delay(50);
        if (digitalRead(buttonPins[i]) == LOW) {
          digitalWrite(buzzerPin,LOW);
          digitalWrite(ledPins[i],HIGH);
          while (digitalRead(buttonPins[i]) == LOW) {
            // Espera hasta que se libere el botón
          }
          digitalWrite(buzzerPin,HIGH);
          digitalWrite(ledPins[i],LOW);
          return i;
        }
      }
    }
  }
}

void perderJuego() {
  ruidoperdio();
  sequenceLength = 0;
  playerIndex = 0;
  for (int i = 0; i < 4; i++) {
    digitalWrite(ledPins[i], LOW);
  }
}

No encuentro explicación para que funcione bien alimentado por USB y mal en otro caso, y encima que la falla sea solo 1 pulsador.
O sea, un error de código provocaría la falla siempre.

Por otro lado esto no me gusta

if (digitalRead(buttonPins[i]) == LOW) { 
  delay(50); 
  if (digitalRead(buttonPins[i]) == LOW) { 
    digitalWrite(buzzerPin,LOW);
    // y todo lo que sigue...

¿Qué ocurre si la pulsación es menor a los 50 ms de antirrebote?
Claramente no entra al segundo if() y no ejecuta lo que debería ejecutar al pulsar un botón.

Yo quitaría el segundo if() dejando todo lo demás, obviamente

if (digitalRead(buttonPins[i]) == LOW) {
  delay(50);
  digitalWrite(buzzerPin,LOW);
  digitalWrite(ledPins[i],HIGH);
  while (digitalRead(buttonPins[i]) == LOW) { 
  // y todo lo que sigue...

Este fragmento es de esperarBoton() pero hay uno similar en topo().

Cada vez que el flujo de ejecución entra aquí, la variable currentTopo vale -1. Entonces esta instrucción tiene resultados impredecibles:

Sospecho que esta instrucción es equivalente a

buttonPins[3] = 0;

lo que haría que dentro de topo() la condición de este while siempre se cumple cuando i=3 (porque ahí no hay ningún botón que liberar) y el ciclo nunca termina.

Comprueba si estoy en lo correcto (con un simple Serial.println(currentTopo)) y en su caso corrige la función.

Puedes también imprimir el valor de buttonPins[3]. El valor correcto debería ser 5.

buttonPins[] está declarada const, no podría modificarse ninguno de sus valores.

Imprimiendo el valor de ledPins[-1] devuelve 0 (simulado en wokwi.com) así que vaya uno a saber a que apunta.
Se supone que apuntaría a la dirección de ledPins[0] - 2 bytes, por ser int, pero no se qué hace realmente el compilador con un índice negativo. En todo caso la sentencia apunta al pin incorrecto..

El valor de buttonPins[3] sigue siendo 5 aún después de la sentencia errónea (como era esperable).

De todos modos, claramente ese es un error.

Mi supuesto es inválido, aunque la función topo() debe ser corregida porque produce resultados impredecibles.


Un intento de modificación directa como

es inválida y rechazada por el compilador. Pero el valor de cualquiera de los elementos del arreglo puede ser modificado de forma indirecta con una función como la siguiente:

void trampa(int a[]) {
	a[3]=0;
}

Una llamada a trampa(buttonPins) equivale a buttonPins[3]=0 y no es rechazada por el compilador.

No, no es así.
ledPins es constante, no se puede modificar.

trampa() no te genera error porque estás pasando el parámetro por valor.
a[] es una copia del array original, para alterar ese array deberías pasarlo por referencia, algo así

void trampa(int &a) {

Y al intentar compilar, en este caso, te daría error porque el array es constante (da un error de conversión de const int a int)
Y si igual pasase por alto ese primer error, obtendrías un error a causa de que el array es read only al querer asignarle un nuevo valor a a[3].


const int buttonPins[] = {2, 3, 4, 5};

void imprime(int a[]) {
  Serial.println("\nImprimiendo " );
  for (int indice = 0; indice < 4; indice++) {
    Serial.print(indice);
    Serial.print(": ");
    Serial.println(a[indice]);
  }
}

void modifica(int a[]) {
	a[3]=12345;
}

void setup() {
  Serial.begin(9600);
  Serial.println("\nArrancando");
  imprime(buttonPins);
  modifica(buttonPins);
  imprime(buttonPins);
}

void loop() {
  
}

Al compilar este programa el compilador arroja algunos avisos y alertas, pero no errores.

Al ejecutarlo verás que el valor de buttonPins[3] se ha modificado dentro de la función modifica()

(Arduino 1.8.19 + Arduino Mega)

Ok.
Ahora corré esto


const int buttonPins[] = {2, 3, 4, 5};

void imprime(int a[]) {
  Serial.println("\nImprimiendo " );
  for (int indice = 0; indice < 4; indice++) {
    Serial.print(indice);
    Serial.print(": ");
    Serial.println(a[indice]);
  }
}

void modifica(int a[]) {
  Serial.println("\nModificando");

  a[3]=12345;

  Serial.println("a[3] = " + String(a[3]));

  Serial.print("buttonPins[3] = ");
  Serial.println(buttonPins[3]);
}

void setup() {
  Serial.begin(9600);
  Serial.println("\nArrancando");
  imprime(buttonPins);
  modifica(buttonPins);
  imprime(buttonPins);
  Serial.print("buttonPins[3] = ");
  Serial.println(buttonPins[3]);
}

void loop() {
  
}

Al ejecutarlo verás que el valor de buttonPins[3] finalmente no se ha modificado nunca porque es constante.

¿Qué sentido tendría la declaración const si la variable igualmente se pudiese modificar?

Agrego:
Te preguntarás por qué hice

void modifica(int a[]) { 
  Serial.println("\nModificando"); 

  a[3]=12345; 

  Serial.println("a[3] = " + String(a[3]));

  Serial.print("buttonPins[3] = "); 
  Serial.println(buttonPins[3]); 
}

Resulta que si lo hago así

void modifica(int a[]) { 
  Serial.println("\nModificando"); 

  a[3]=12345; 

  Serial.println(a[3]);

  Serial.print("buttonPins[3] = "); 
  Serial.println(buttonPins[3]); 
}

Obtengo

Modificando
2345
buttonPins[3] = 5

Pero haciendo

void modifica(int a[]) { 
  Serial.println("\nModificando"); 

  a[3]=12345; 

  Serial.print("a[3] = ");
  Serial.println(a[3]);

  Serial.print("buttonPins[3] = "); 
  Serial.println(buttonPins[3]); 
}

Obtengo

Modificando
a[3] = 5
buttonPins[3] = 5

Y no tengo explicación.

Aca en Buenos Aires son las 3 A.M. y si sigo enganchado con esto amanezco buscando la explicación.
Mejor me voy a dormir. :roll_eyes:

Pienso que el compilador introduce optimizaciones sustituyendo referencias como "buttonPins[3]" por "5".

Y tienes razón, al pasar el arreglo a la función que lo "modifica" o que lo imprime, el compilador pasa una copia y el arreglo original permanece intacto.

He hecho multiples pruebas y he encontrado que hay diferencias cuando el arreglo se declara como global vs local. y también cuando usas Serial.print()

Voy a abrir otro hilo. Saludos desde Chihuahua

Hola, yo tampoco encuentro explicación... por ello mi presencia en este foro!!
Respecto del antirrebote, es difícil presionar el botón por menos de 50ms salvo que sea adrede, pero en un juego como simon dice un rebote es fatal por eso me vi obligado a ponerlo. Lo que no tuve en cuenta es que el solo delay() creo que cumle la función de debounce también (asi lo diseño el gpt) y admite toques mas cortos.
Si bien realizaron sugerencias que voy a seguir, creo que ninguna está asociada al síntoma que estoy teniendo (más all del debate interno que luego se generó).
Sigo investigando y atento a sugerencias, gracias.

Por qué currentTopo = -1 ? currentTopo es led anteriormente asignado en forma aleatoria y encendido a modo de "topo", lo que la funcion nuevotopo() hace es apagar esel led, asignar un nuevo led aleatorio y encenderlo. CurrenTopo tiene, al inicio, el valor de topoAleatorio generado en la ronda anterior

Hay varios casos como éste. La función reiniciarJuego() cambia el valor de currentTopo a -1 y después manda a ejecutar nuevoTopo(). En esta segunda función, la variable en cuestión se utiliza antes de que se le asigne el valor de topoAleatorio y en esa primera vez que se usa (subrayada en la siguiente figura), tiene un valor de -1. La instrucción que sigue le cambia el valor, pero ya fue usada con un valor fuera de rango. Esto suele producir resultados raros, impredecibles.

La solución sería cambiar el orden para que se le asigne el valor deseado antes de usarlo.

¿Para qué le asignas -1?

Parece que gpt genera problemas de difícil solución...

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.