Maquina de estados no transiciona

Buenos días,

Me encuentro programando una maquina expendedora en un Arduino UNO hasta hoy no había tenido ningún problema, definí una maquina de estados y las condiciones necesarias para cada transición.

Hoy quería mejorarla añadiendo estructuras y optimizar el consumo de memoria, tras modificarla actualmente en el estado de entrega no me realiza la transición al siguiente estado(de entrega a recogida) a pesar de que cumple todos los requisitos. He probado cambiando el estado al que avanzar (en lugar de la recogida por ejemplo a la elección nuevamente) y en ese caso la transición funciona sin fallo. También he revisado el cableado y sustituido los cables que pudieran estar en peor estado.

Mi código es el siguiente:

// ---------------------------- Definiciones de Pines ----------------------------
#define PIN_BOTONA 6
#define PIN_BOTONB 7
#define PIN_FIN_CARRERA 11

// ---------------------------- Estructura de Producto ----------------------------
struct Producto {
    int id;
    char nombre[50];
    float precio;
    int stockDeseado;
};
// ---------------------------- Estructura de Compartimentos ----------------------------
struct Compartimento {
    int id;
    int productoId; // Identificador del producto que contiene
    int stock;
};
// ---------------------------- Estructura de Maquina ----------------------------
#define NUM_COMPARTIMENTOS 3
#define NUM_PRODUCTOS 2

struct MaquinaExpendedora {
    Compartimento compartimentos[NUM_COMPARTIMENTOS];
    Producto productos[NUM_PRODUCTOS];
    int numProductos;
    float temperaturaIdeal;
};

// ---------------------------- Definiciones de LEDs y Sensor ----------------------------
const int ledPins[] = {2, 3, 4, 5};
const int LedEntrega = 8;
const int LedFallo = 9;
const int sensorPin = A0;
const int sensorPinTemp = A1;

// ---------------------------- Variables de Tiempo para los Botones ----------------------------
unsigned long tiempoInicioPulsacionA = 0;
unsigned long tiempoPulsacionA = 0;
unsigned long int inicioPulsacionA, botonPulsadoA;
unsigned long int inicioPulsacionB, botonPulsadoB;

// ---------------------------- Variables Misceláneas ----------------------------
unsigned long int valor;
unsigned long int cantidadAIntroducir;
unsigned long int tiempoRecogida;
unsigned long int tiempoReentrega;

bool botonesSoltados = false;
bool devuelto = false;
bool aviso = false;
int Misaldo = 0;
int finCarrera;
int cantidadAAnadir = 0;
int compartimentoSeleccionado = 0;

// ---------------------------- Definición de la Contraseña del Modo Técnico ----------------------------
const int claveTecnico[] = {1, 2, 3, 4, 15};
const int longitudClave = 5;
int claveIntroducida[longitudClave];
int indiceClave = 0;
bool introduciendoClave = false;
bool ignorarSoltarA = false;

// ---------------------------- Enumeraciones de Estados ----------------------------
enum estadoMaquina {
    ELECCION,
    ENTREGA,
    RECOGIDA,
    TECNICO,
    PRECIOS,
    RELLENARMAQUINA,
    INTRODUCIR_CLAVE
} maquina = ELECCION, estadoAnterior = 7;

// Define los posibles estados de un botón
enum Estadoboton {
    PULSADO = 1,
    SUELTO = 2,
    TR_PULS = 3,
    TR_SOLT = 4
} botonA = SUELTO, botonB = SUELTO;

// ---------------------------- Instancia de la Máquina Expendedora (Puntero) ----------------------------
MaquinaExpendedora miMaquina;
MaquinaExpendedora* ptrMaquina = &miMaquina; // Puntero a la instancia de la máquina

// ---------------------------- Funciones ----------------------------

float leerTemperatura() {
    int valorPotenciometro = analogRead(sensorPinTemp);
    float temperatura = (float)valorPotenciometro * (40.0 / 1023.0);
    return temperatura;
}

Estadoboton gestorBoton(Estadoboton* estadoAnteriorBoton, bool lectura) {
    switch (*estadoAnteriorBoton) {
        case PULSADO:
            if (lectura == 1) {
                *estadoAnteriorBoton = TR_SOLT;
            }
            break;
        case SUELTO:
            if (lectura == 0) {
                *estadoAnteriorBoton = TR_PULS;
            }
            break;
        case TR_PULS:
            *estadoAnteriorBoton = PULSADO;
            break;
        case TR_SOLT:
            *estadoAnteriorBoton = SUELTO;
            break;
    }
    return *estadoAnteriorBoton;
}

unsigned long int MuestraSaldo(unsigned long int *valor) {
    int cantidadAIntroducir = *valor;
    for (int i = 0; i < 4; i++) {
        int bit = (*valor >> i) & 1; // Usando puntero para acceder a valor
        digitalWrite(ledPins[i], bit);
    }
}

void retirarDinero() {
    Serial.println("Se ha devuelto su saldo");
    Serial.print("Saldo actual: ");
    Misaldo = 0;
    devuelto = true;
    Serial.println(Misaldo);
}

int seleccionarProducto() {
    Serial.print("Seleccione el compartimento (1-");
    Serial.print(NUM_COMPARTIMENTOS);
    Serial.println("):");
    if (Serial.available() > 0) {
        compartimentoSeleccionado = Serial.read() - '1';
        if (compartimentoSeleccionado >= 0 && compartimentoSeleccionado < NUM_COMPARTIMENTOS) {
            Serial.print("Compartimento seleccionado: ");
            Serial.println(compartimentoSeleccionado + 1);
        } else {
            Serial.println("Selección inválida.");
            compartimentoSeleccionado = 0;
        }
    }
    return compartimentoSeleccionado;
}

int seleccionarCompartimento() {
    int maxStock = -1;
    int mejorCompartimento = -1;
    for (int i = 0; i < NUM_COMPARTIMENTOS; i++) {
        for (int j = 0; j < ptrMaquina->numProductos; j++) { // Usando puntero para acceder a numProductos
            if (ptrMaquina->compartimentos[i].productoId == ptrMaquina->productos[j].id) { // Usando puntero para acceder a los arrays
                if (ptrMaquina->compartimentos[i].stock > maxStock) { // Accediendo directamente al miembro
                    maxStock = ptrMaquina->compartimentos[i].stock;
                    mejorCompartimento = i;
                }
                break;
            }
        }
    }
    return mejorCompartimento;
}

// ---------------------------- Función de Configuración ----------------------------
void setup() {
    pinMode(PIN_BOTONA, INPUT_PULLUP);
    pinMode(PIN_BOTONB, INPUT_PULLUP);
    for (int i = 0; i < 4; i++) {
        pinMode(ledPins[i], OUTPUT);
    }
    pinMode(LedFallo, OUTPUT);
    pinMode(LedEntrega, OUTPUT);
    pinMode(PIN_FIN_CARRERA, INPUT_PULLUP);
    Serial.begin(9600);
    Serial.println("Aqui empezamos:");

    // ---------------------------- Inicialización de la Máquina ----------------------------
    ptrMaquina->numProductos = NUM_PRODUCTOS;
    ptrMaquina->temperaturaIdeal = 25.0;

    ptrMaquina->productos[0].id = 1;
    strcpy(ptrMaquina->productos[0].nombre, "Refresco A");
    ptrMaquina->productos[0].precio = 2;
    ptrMaquina->productos[0].stockDeseado = 10;

    ptrMaquina->productos[1].id = 2;
    strcpy(ptrMaquina->productos[1].nombre, "Snack B");
    ptrMaquina->productos[1].precio = 1;
    ptrMaquina->productos[1].stockDeseado = 15;

    ptrMaquina->compartimentos[0].id = 1;
    ptrMaquina->compartimentos[0].productoId = 1;
    ptrMaquina->compartimentos[0].stock = 5;

    ptrMaquina->compartimentos[1].id = 2;
    ptrMaquina->compartimentos[1].productoId = 2;
    ptrMaquina->compartimentos[1].stock = 10;

    ptrMaquina->compartimentos[2].id = 3;
    ptrMaquina->compartimentos[2].productoId = 1;
    ptrMaquina->compartimentos[2].stock = 3;
}

// ---------------------------- Bucle Principal ----------------------------
void loop() {
    cantidadAIntroducir = (analogRead(A0) >> 6);
    MuestraSaldo(&cantidadAIntroducir);

    bool lecturaA = digitalRead(PIN_BOTONA);
    bool lecturaB = digitalRead(PIN_BOTONB);
    finCarrera = digitalRead(PIN_FIN_CARRERA);

    botonA = gestorBoton(&botonA, lecturaA);
    if (botonA == TR_PULS) {
        inicioPulsacionA = millis();
    }
    if (botonA == PULSADO) {
        botonPulsadoA = millis() - inicioPulsacionA;
    }

    botonB = gestorBoton(&botonB, lecturaB);
    if (botonB == TR_PULS) {
        inicioPulsacionB = millis();
    }
    if (botonB == PULSADO) {
        botonPulsadoB = millis() - inicioPulsacionB;
    }

    // ---------------------------- Máquina de Estados Principal ----------------------------


switch (maquina) {
    case ELECCION:
        if (estadoAnterior != ELECCION) {
            Serial.println("En eleccion, A(mas cerca pot) para introducir saldo, B para elegir producto");
            Serial.println(finCarrera);
            estadoAnterior = maquina;
            botonesSoltados = false;
            devuelto = false;
            indiceClave = 0;
            introduciendoClave = false;
        }
        if (!botonesSoltados && botonA == SUELTO && botonB == SUELTO) {
            botonesSoltados = true;
        }
        if (botonA == TR_SOLT && botonesSoltados == true && devuelto == false) {
            Serial.print("Tu Saldo:");
            Misaldo = cantidadAIntroducir + Misaldo;
            Serial.println(Misaldo);
        }
        if (botonB == TR_SOLT && botonesSoltados == true) {
            maquina = ENTREGA;
            estadoAnterior = 7;
        }
        if (botonA == PULSADO && botonPulsadoA > 3000 && botonesSoltados == true && devuelto == false) {
            retirarDinero();
        }
        if (botonA == PULSADO && botonB == PULSADO && botonPulsadoA > 2000 && botonPulsadoB > 2000 && botonesSoltados == true) {
            maquina = INTRODUCIR_CLAVE;
            Serial.println("Introduce la clave tecnica:");
            indiceClave = 0;
            for (int i = 0; i < longitudClave; i++) {
                claveIntroducida[i] = -1;
            }
            introduciendoClave = true;
            ignorarSoltarA = true;
            estadoAnterior = 7;
        }
        break;

    case INTRODUCIR_CLAVE:
        if (estadoAnterior != INTRODUCIR_CLAVE) {
            Serial.print("Introduce el digito ");
            Serial.println(indiceClave + 1);
            Serial.println("Usa el potenciometro (0-15) y A para confirmar.");
            estadoAnterior = maquina;
        }
        if (botonA == TR_SOLT && introduciendoClave && ignorarSoltarA) {
            ignorarSoltarA = false;
        } else if (botonA == TR_SOLT && introduciendoClave && !ignorarSoltarA) {
            claveIntroducida[indiceClave] = cantidadAIntroducir;
            Serial.print("Digit ");
            Serial.print(indiceClave + 1);
            Serial.print(" introducido: ");
            Serial.println(claveIntroducida[indiceClave]);
            indiceClave++;

            if (indiceClave == longitudClave) {
                bool claveCorrecta = true;
                for (int i = 0; i < longitudClave; i++) {
                    if (claveIntroducida[i] != claveTecnico[i]) {
                        claveCorrecta = false;
                        break;
                    }
                }

                if (claveCorrecta) {
                    Serial.println("Clave correcta. Accediendo al modo tecnico.");
                    maquina = TECNICO;
                    estadoAnterior = 7;
                } else {
                    Serial.println("Clave incorrecta. Volviendo a Eleccion.");
                    maquina = ELECCION;
                    estadoAnterior = 7;
                }
                introduciendoClave = false;
                indiceClave = 0;
                ignorarSoltarA = false;
            } else {
                Serial.print("Introduce el digito ");
                Serial.println(indiceClave + 1);
            }
        }
        break;

    case ENTREGA:
        if (estadoAnterior != ENTREGA) {
            Serial.println("Estoy en entrega");
            estadoAnterior = maquina;
        }
        digitalWrite(LedEntrega, LOW);
        int compartimentoSeleccionadoEntrega = seleccionarCompartimento();
        if (compartimentoSeleccionadoEntrega != -1) {
            int productoIdSeleccionado = ptrMaquina->compartimentos[compartimentoSeleccionadoEntrega].productoId;
            float precioProducto = 0;
            for (int i = 0; i < ptrMaquina->numProductos; i++) {
                if (ptrMaquina->productos[i].id == productoIdSeleccionado) {
                    precioProducto = ptrMaquina->productos[i].precio;
                    Serial.println(precioProducto);
                    break;
                }
            }
            if (botonB == TR_PULS && (Misaldo >= precioProducto) && ptrMaquina->compartimentos[compartimentoSeleccionadoEntrega].stock > 0) {
                ptrMaquina->compartimentos[compartimentoSeleccionadoEntrega].stock--;
                Serial.print("Entregando producto del compartimento ");
                Serial.println(compartimentoSeleccionadoEntrega + 1);
                digitalWrite(LedEntrega, HIGH);
                Misaldo -= precioProducto;
                Serial.print("Tu nuevo saldo es:");
                Serial.println(Misaldo);
                maquina = RECOGIDA;
                Serial.println("TRANSICION A RECOGIDA (SALDO OK)");
                estadoAnterior = 7;
            } else if (botonB == TR_PULS) {
                Serial.println("No hay suficiente saldo.");
                Serial.print("Saldo: ");
                Serial.println(Misaldo);
                digitalWrite(LedFallo, HIGH);
                Serial.println(finCarrera);
                maquina = RECOGIDA;
                Serial.println("TRANSICION A RECOGIDA (SIN SALDO)");
                estadoAnterior = 7;
            }
        } else if (botonB == TR_PULS) {
            Serial.println("No hay productos disponibles.");
            digitalWrite(LedFallo, HIGH);
            maquina = RECOGIDA;
            Serial.println("TRANSICION A RECOGIDA (SIN PRODUCTOS)");
            estadoAnterior = 7;
        }
        break;

    case RECOGIDA:
        Serial.print("ESTADO: RECOGIDA - Valor de estadoAnterior: ");
        Serial.println(estadoAnterior);
        Serial.print("ESTADO: RECOGIDA - Valor de finCarrera: ");
        Serial.println(finCarrera);
        if (estadoAnterior != RECOGIDA) {
            tiempoRecogida = millis();
            estadoAnterior = maquina;
            Serial.println("ESTOY EN RECOGIDA");
        }
        if (finCarrera == 0) {
            Serial.println("Vuelvo a Eleccion");
            maquina = ELECCION;
            digitalWrite(LedFallo, LOW);
            digitalWrite(LedEntrega, LOW);
            estadoAnterior = 7;
        }
        if (estadoAnterior == RECOGIDA) {
            tiempoReentrega = millis() - tiempoRecogida;
        }
        if (tiempoReentrega > 10000 && aviso == false) {
            Serial.println("Se ha reintentado entrega de producto");
            tiempoReentrega = millis();
            aviso = true;
        }
        break;

    case TECNICO:
        if (estadoAnterior != TECNICO) {
            Serial.println("Estoy en el modo Tecnico, pulse A para cambiar el precio y B para añadir existencias");
            estadoAnterior = maquina;
            botonesSoltados = false;
        }
        if (botonA == TR_SOLT && botonesSoltados == true) {
            maquina = PRECIOS;
            estadoAnterior = 7;
        }
        if (botonB == TR_SOLT && botonesSoltados == true) {
            maquina = RELLENARMAQUINA;
            estadoAnterior = 7;
        }
        if (!botonesSoltados && botonA == SUELTO && botonB == SUELTO) {
            botonesSoltados = true;
        }
        if (botonA == PULSADO && botonB == PULSADO && botonPulsadoA > 2000 && botonPulsadoB > 2000 && botonesSoltados == true) {
            maquina = ELECCION;
            devuelto = true;
            estadoAnterior = 7;
        }
        break;

    case RELLENARMAQUINA:
        if (estadoAnterior != RELLENARMAQUINA) {
            Serial.println("Modo Rellenar Maquina:");
            Serial.print("Compartimento: ");
            Serial.println(compartimentoSeleccionado + 1);
            Serial.println("Use A para cambiar, B para añadir.");
            estadoAnterior = maquina;
        }
        if (botonA == TR_SOLT) {
            compartimentoSeleccionado = (compartimentoSeleccionado + 1) % NUM_COMPARTIMENTOS;
            Serial.print("Compartimento: ");
            Serial.println(compartimentoSeleccionado + 1);
        }
        if (botonB == TR_SOLT) {
            cantidadAAnadir = cantidadAIntroducir;
            ptrMaquina->compartimentos[compartimentoSeleccionado].stock += cantidadAAnadir;
            Serial.print("Añadido ");
            Serial.print(cantidadAAnadir);
            Serial.print(" al compartimento ");
            Serial.println(compartimentoSeleccionado + 1);
            Serial.print("El compartimento seleccionado tiene:");
            Serial.println(ptrMaquina->compartimentos[compartimentoSeleccionado].stock);
            maquina = TECNICO;
            estadoAnterior = 7;
        }
        break;

    case PRECIOS:
        if (estadoAnterior != PRECIOS) {
            Serial.println("Estoy en Precios. Pulse A para establecer un nuevo precio y B para volver a Tecnico.");
            estadoAnterior = maquina;
        }
        if (botonA == TR_PULS) {
            Serial.print("Estableciendo nuevo precio para el producto del compartimento ");
            Serial.print(compartimentoSeleccionado + 1);
            Serial.print(" a: ");
            int productoIdPrecio = ptrMaquina->compartimentos[compartimentoSeleccionado].productoId;
            for (int i = 0; i < ptrMaquina->numProductos; i++) {
                if (ptrMaquina->productos[i].id == productoIdPrecio) {
                    ptrMaquina->productos[i].precio = cantidadAIntroducir;
                    Serial.println(ptrMaquina->productos[i].precio);
                    break;
                }
            }
        }
        if (botonB == TR_PULS) {
            maquina = TECNICO;
            estadoAnterior = 7;
        }
        break;
}

    // Aquí termina el switch de la máquina de estados
}

Espero haber realizado bien el post pues es el primero que hago.

¿Cuál sería el estado 7?


El post está correctamente presentado. :clap:t2:

Es un estado que siempre va a ser diferente de los estados reales de la maquina, lo utilizo como bandera para que al realizar la primera iteración de cada estado me imprima por el serial una única vez el mensaje de cada estado.

Claro, pero se supone que una variable de tipo enum tiene que tener un valor definido en el enum.
En tu caso el valor máximo asociado a las constantes es 6.
Probablemente no tenga nada que ver con el problema pero, para mayor claridad, sería conveniente que agregues un valor al enum (por ej. BANDERA).

Por otro lado, probá poner el bloque de instrucciones de cada case entre llaves, excepto break.
O sea

case LOQUESEA:
  {
    // sentencias dentro del case
  }
  break;

Hoy he implementado lo que me has comentado y he revisado bien los warnings del programa y después de corregirlos ha cambiado correctamente de estado.

Me gustaría conocer el porque no funcionaba ¿Quizá sobrescribía datos en la memoria el estado 7 sin definir o tenia un problema a la hora de acceder?

Sea cual sea el caso, muchas gracias.

Dejo el código por si a alguien le sirviese.

  // ---------------------------- Definiciones de Pines ----------------------------
  #define PIN_BOTONA 6
  #define PIN_BOTONB 7
  #define PIN_FIN_CARRERA 11

  // ---------------------------- Estructura de Producto ----------------------------
  struct Producto {
      int id;
      char nombre[50];
      float precio;
      int stockDeseado;
  };
  // ---------------------------- Estructura de Compartimentos ----------------------------
  struct Compartimento {
      int id;
      int productoId; // Identificador del producto que contiene
      int stock;
  };
  // ---------------------------- Estructura de Maquina ----------------------------
  #define NUM_COMPARTIMENTOS 3
  #define NUM_PRODUCTOS 2

  struct MaquinaExpendedora {
      Compartimento compartimentos[NUM_COMPARTIMENTOS];
      Producto productos[NUM_PRODUCTOS];
      int numProductos;
      float temperaturaIdeal;
  };

  // ---------------------------- Definiciones de LEDs y Sensor ----------------------------
  const int ledPins[] = {2, 3, 4, 5};
  const int LedEntrega = 8;
  const int LedFallo = 9;
  const int sensorPin = A0;
  const int sensorPinTemp = A1;

  // ---------------------------- Variables de Tiempo para los Botones ----------------------------
  unsigned long tiempoInicioPulsacionA = 0;
  unsigned long tiempoPulsacionA = 0;
  unsigned long int inicioPulsacionA, botonPulsadoA;
  unsigned long int inicioPulsacionB, botonPulsadoB;

  // ---------------------------- Variables Misceláneas ----------------------------
  unsigned long int valor;
  unsigned long int cantidadAIntroducir;
  unsigned long int tiempoRecogida;
  unsigned long int tiempoReentrega;

  bool botonesSoltados = false;
  bool devuelto = false;
  bool aviso = false;
  int Misaldo = 0;
  int finCarrera;
  int cantidadAAnadir = 0;
  int compartimentoSeleccionado = 0;

  // ---------------------------- Definición de la Contraseña del Modo Técnico ----------------------------
  const int claveTecnico[] = {1, 2, 3, 4, 15};
  const int longitudClave = 5;
  int claveIntroducida[longitudClave];
  int indiceClave = 0;
  bool introduciendoClave = false;
  bool ignorarSoltarA = false;

  // ---------------------------- Enumeraciones de Estados ----------------------------
  enum estadoMaquina {
      ELECCION,
      ENTREGA,
      RECOGIDA,
      TECNICO,
      PRECIOS,
      RELLENARMAQUINA,
      INTRODUCIR_CLAVE,
      BANDERA
  } maquina = ELECCION, estadoAnterior = BANDERA;

  // Define los posibles estados de un botón
  enum Estadoboton {
      PULSADO = 1,
      SUELTO = 2,
      TR_PULS = 3,
      TR_SOLT = 4
  } botonA = SUELTO, botonB = SUELTO;

  // ---------------------------- Instancia de la Máquina Expendedora (Puntero) ----------------------------
  MaquinaExpendedora miMaquina;
  MaquinaExpendedora* ptrMaquina = &miMaquina; // Puntero a la instancia de la máquina

  // ---------------------------- Funciones ----------------------------

  float leerTemperatura() {
      int valorPotenciometro = analogRead(sensorPinTemp);
      float temperatura = (float)valorPotenciometro * (40.0 / 1023.0);
      return temperatura;
  }

  Estadoboton gestorBoton(Estadoboton* estadoAnteriorBoton, bool lectura) {
      switch (*estadoAnteriorBoton) {
          case PULSADO:
              if (lectura == 1) {
                  *estadoAnteriorBoton = TR_SOLT;
              }
              break;
          case SUELTO:
              if (lectura == 0) {
                  *estadoAnteriorBoton = TR_PULS;
              }
              break;
          case TR_PULS:
              *estadoAnteriorBoton = PULSADO;
              break;
          case TR_SOLT:
              *estadoAnteriorBoton = SUELTO;
              break;
      }
      return *estadoAnteriorBoton;
  }

  void MuestraSaldo(unsigned long int *valor) {
      for (int i = 0; i < 4; i++) {
          int bit = (*valor >> i) & 1; // Usando puntero para acceder a valor
          digitalWrite(ledPins[i], bit);
      }
  }

  void retirarDinero() {
      Serial.println("Se ha devuelto su saldo");
      Serial.print("Saldo actual: ");
      Misaldo = 0;
      devuelto = true;
      Serial.println(Misaldo);
  }

  int seleccionarProducto() {
      Serial.print("Seleccione el compartimento (1-");
      Serial.print(NUM_COMPARTIMENTOS);
      Serial.println("):");
      if (Serial.available() > 0) {
          compartimentoSeleccionado = Serial.read() - '1';
          if (compartimentoSeleccionado >= 0 && compartimentoSeleccionado < NUM_COMPARTIMENTOS) {
              Serial.print("Compartimento seleccionado: ");
              Serial.println(compartimentoSeleccionado + 1);
          } else {
              Serial.println("Selección inválida.");
              compartimentoSeleccionado = 0;
          }
      }
      return compartimentoSeleccionado;
  }

  int seleccionarCompartimento() {
      int maxStock = -1;
      int mejorCompartimento = -1;
      for (int i = 0; i < NUM_COMPARTIMENTOS; i++) {
          for (int j = 0; j < ptrMaquina->numProductos; j++) { // Usando puntero para acceder a numProductos
              if (ptrMaquina->compartimentos[i].productoId == ptrMaquina->productos[j].id) { // Usando puntero para acceder a los arrays
                  if (ptrMaquina->compartimentos[i].stock > maxStock) { // Accediendo directamente al miembro
                      maxStock = ptrMaquina->compartimentos[i].stock;
                      mejorCompartimento = i;
                  }
                  break;
              }
          }
      }
      return mejorCompartimento;
  }

  // ---------------------------- Función de Configuración ----------------------------
  void setup() {
      pinMode(PIN_BOTONA, INPUT_PULLUP);
      pinMode(PIN_BOTONB, INPUT_PULLUP);
      for (int i = 0; i < 4; i++) {
          pinMode(ledPins[i], OUTPUT);
      }
      pinMode(LedFallo, OUTPUT);
      pinMode(LedEntrega, OUTPUT);
      pinMode(PIN_FIN_CARRERA, INPUT_PULLUP);
      Serial.begin(9600);
      Serial.println("Aqui empezamos:");

      // ---------------------------- Inicialización de la Máquina ----------------------------
      ptrMaquina->numProductos = NUM_PRODUCTOS;
      ptrMaquina->temperaturaIdeal = 25.0;

      ptrMaquina->productos[0].id = 1;
      strcpy(ptrMaquina->productos[0].nombre, "Refresco A");
      ptrMaquina->productos[0].precio = 2;
      ptrMaquina->productos[0].stockDeseado = 10;

      ptrMaquina->productos[1].id = 2;
      strcpy(ptrMaquina->productos[1].nombre, "Snack B");
      ptrMaquina->productos[1].precio = 1;
      ptrMaquina->productos[1].stockDeseado = 15;

      ptrMaquina->compartimentos[0].id = 1;
      ptrMaquina->compartimentos[0].productoId = 1;
      ptrMaquina->compartimentos[0].stock = 5;

      ptrMaquina->compartimentos[1].id = 2;
      ptrMaquina->compartimentos[1].productoId = 2;
      ptrMaquina->compartimentos[1].stock = 10;

      ptrMaquina->compartimentos[2].id = 3;
      ptrMaquina->compartimentos[2].productoId = 1;
      ptrMaquina->compartimentos[2].stock = 3;
  }

  // ---------------------------- Bucle Principal ----------------------------
  void loop() {
      cantidadAIntroducir = (analogRead(A0) >> 6);
      MuestraSaldo(&cantidadAIntroducir);

      bool lecturaA = digitalRead(PIN_BOTONA);
      bool lecturaB = digitalRead(PIN_BOTONB);
      finCarrera = digitalRead(PIN_FIN_CARRERA);

      botonA = gestorBoton(&botonA, lecturaA);
      if (botonA == TR_PULS) {
          inicioPulsacionA = millis();
      }
      if (botonA == PULSADO) {
          botonPulsadoA = millis() - inicioPulsacionA;
      }

      botonB = gestorBoton(&botonB, lecturaB);
      if (botonB == TR_PULS) {
          inicioPulsacionB = millis();
      }
      if (botonB == PULSADO) {
          botonPulsadoB = millis() - inicioPulsacionB;
      }

      // ---------------------------- Máquina de Estados Principal ----------------------------


  switch (maquina) {
      case BANDERA://Estado que no hace nada pero sirve de bandera para el resto
      case ELECCION:
      {
          if (estadoAnterior != ELECCION) {
              Serial.println("En eleccion, A(mas cerca pot) para introducir saldo, B para elegir producto");
              Serial.println(finCarrera);
              estadoAnterior = maquina;
              botonesSoltados = false;
              devuelto = false;
              indiceClave = 0;
              introduciendoClave = false;
          }
          if (!botonesSoltados && botonA == SUELTO && botonB == SUELTO) {
              botonesSoltados = true;
          }
          if (botonA == TR_SOLT && botonesSoltados == true && devuelto == false) {
              Serial.print("Tu Saldo:");
              Misaldo = cantidadAIntroducir + Misaldo;
              Serial.println(Misaldo);
          }
          if (botonB == TR_SOLT && botonesSoltados == true) {
              maquina = ENTREGA;
              estadoAnterior = BANDERA;
          }
          if (botonA == PULSADO && botonPulsadoA > 3000 && botonesSoltados == true && devuelto == false) {
              retirarDinero();
          }
          if (botonA == PULSADO && botonB == PULSADO && botonPulsadoA > 2000 && botonPulsadoB > 2000 && botonesSoltados == true) {
              maquina = INTRODUCIR_CLAVE;
              Serial.println("Introduce la clave tecnica:");
              indiceClave = 0;
              for (int i = 0; i < longitudClave; i++) {
                  claveIntroducida[i] = -1;
              }
              introduciendoClave = true;
              ignorarSoltarA = true;
              estadoAnterior = BANDERA;
          }
      }
          break;

      case INTRODUCIR_CLAVE:{
          if (estadoAnterior != INTRODUCIR_CLAVE) {
              Serial.print("Introduce el digito ");
              Serial.println(indiceClave + 1);
              Serial.println("Usa el potenciometro (0-15) y A para confirmar.");
              estadoAnterior = maquina;
          }
          if (botonA == TR_SOLT && introduciendoClave && ignorarSoltarA) {
              ignorarSoltarA = false;
          } else if (botonA == TR_SOLT && introduciendoClave && !ignorarSoltarA) {
              claveIntroducida[indiceClave] = cantidadAIntroducir;
              Serial.print("Digit ");
              Serial.print(indiceClave + 1);
              Serial.print(" introducido: ");
              Serial.println(claveIntroducida[indiceClave]);
              indiceClave++;

              if (indiceClave == longitudClave) {
                  bool claveCorrecta = true;
                  for (int i = 0; i < longitudClave; i++) {
                      if (claveIntroducida[i] != claveTecnico[i]) {
                          claveCorrecta = false;
                          break;
                      }
                  }

                  if (claveCorrecta) {
                      Serial.println("Clave correcta. Accediendo al modo tecnico.");
                      maquina = TECNICO;
                      estadoAnterior = BANDERA;
                  } else {
                      Serial.println("Clave incorrecta. Volviendo a Eleccion.");
                      maquina = ELECCION;
                      estadoAnterior = BANDERA;
                  }
                  introduciendoClave = false;
                  indiceClave = 0;
                  ignorarSoltarA = false;
              } else {
                  Serial.print("Introduce el digito ");
                  Serial.println(indiceClave + 1);
              }
          }
      }
          break;

      case ENTREGA:
      {
          if (estadoAnterior != ENTREGA) {
              Serial.println("Estoy en entrega");
              estadoAnterior = maquina;
          }
          digitalWrite(LedEntrega, LOW);
          int compartimentoSeleccionadoEntrega = seleccionarCompartimento();
          if (compartimentoSeleccionadoEntrega != -1) {
              int productoIdSeleccionado = ptrMaquina->compartimentos[compartimentoSeleccionadoEntrega].productoId;
              float precioProducto = 0;
              for (int i = 0; i < ptrMaquina->numProductos; i++) {
                  if (ptrMaquina->productos[i].id == productoIdSeleccionado) {
                      precioProducto = ptrMaquina->productos[i].precio;
                      Serial.println(precioProducto);
                      break;
                  }
              }
              if (botonB == TR_PULS && (Misaldo >= precioProducto) && ptrMaquina->compartimentos[compartimentoSeleccionadoEntrega].stock > 0) {
                  ptrMaquina->compartimentos[compartimentoSeleccionadoEntrega].stock--;
                  Serial.print("Entregando producto del compartimento ");
                  Serial.println(compartimentoSeleccionadoEntrega + 1);
                  digitalWrite(LedEntrega, HIGH);
                  Misaldo -= precioProducto;
                  Serial.print("Tu nuevo saldo es:");
                  Serial.println(Misaldo);
                  maquina = RECOGIDA;
                  Serial.println("TRANSICION A RECOGIDA (SALDO OK)");
                  estadoAnterior = BANDERA;
              } else if (botonB == TR_PULS) {
                  Serial.println("No hay suficiente saldo.");
                  Serial.print("Saldo: ");
                  Serial.println(Misaldo);
                  digitalWrite(LedFallo, HIGH);
                  Serial.println(finCarrera);
                  maquina = RECOGIDA;
                  Serial.println("TRANSICION A RECOGIDA (SIN SALDO)");
                  estadoAnterior = BANDERA;
              }
          } else if (botonB == TR_PULS) {
              Serial.println("No hay productos disponibles.");
              digitalWrite(LedFallo, HIGH);
              maquina = RECOGIDA;
              Serial.println("TRANSICION A RECOGIDA (SIN PRODUCTOS)");
              estadoAnterior = BANDERA;
          }
      }
          break;

      case RECOGIDA:
          if (estadoAnterior != RECOGIDA) {
              tiempoRecogida = millis();
              estadoAnterior = maquina;
              Serial.println("ESTOY EN RECOGIDA");
          }
          if (finCarrera == 0) {
              Serial.println("Vuelvo a Eleccion");
              maquina = ELECCION;
              digitalWrite(LedFallo, LOW);
              digitalWrite(LedEntrega, LOW);
              estadoAnterior = BANDERA;
          }
          if (estadoAnterior == RECOGIDA) {
              tiempoReentrega = millis() - tiempoRecogida;
          }
          if (tiempoReentrega > 10000 && aviso == false) {
              Serial.println("Se ha reintentado entrega de producto");
              tiempoReentrega = millis();
              aviso = true;
          }
          break;

      case TECNICO:
          if (estadoAnterior != TECNICO) {
              Serial.println("Estoy en el modo Tecnico, pulse A para cambiar el precio y B para añadir existencias");
              estadoAnterior = maquina;
              botonesSoltados = false;
          }
          if (botonA == TR_SOLT && botonesSoltados == true) {
              maquina = PRECIOS;
              estadoAnterior = BANDERA;
          }
          if (botonB == TR_SOLT && botonesSoltados == true) {
              maquina = RELLENARMAQUINA;
              estadoAnterior = BANDERA;
          }
          if (!botonesSoltados && botonA == SUELTO && botonB == SUELTO) {
              botonesSoltados = true;
          }
          if (botonA == PULSADO && botonB == PULSADO && botonPulsadoA > 2000 && botonPulsadoB > 2000 && botonesSoltados == true) {
              maquina = ELECCION;
              devuelto = true;
              estadoAnterior = BANDERA;
          }
          break;

      case RELLENARMAQUINA:
          if (estadoAnterior != RELLENARMAQUINA) {
              Serial.println("Modo Rellenar Maquina:");
              Serial.print("Compartimento: ");
              Serial.println(compartimentoSeleccionado + 1);
              Serial.println("Use A para cambiar, B para añadir.");
              estadoAnterior = maquina;
          }
          if (botonA == TR_SOLT) {
              compartimentoSeleccionado = (compartimentoSeleccionado + 1) % NUM_COMPARTIMENTOS;
              Serial.print("Compartimento: ");
              Serial.println(compartimentoSeleccionado + 1);
          }
          if (botonB == TR_SOLT) {
              cantidadAAnadir = cantidadAIntroducir;
              ptrMaquina->compartimentos[compartimentoSeleccionado].stock += cantidadAAnadir;
              Serial.print("Añadido ");
              Serial.print(cantidadAAnadir);
              Serial.print(" al compartimento ");
              Serial.println(compartimentoSeleccionado + 1);
              Serial.print("El compartimento seleccionado tiene:");
              Serial.println(ptrMaquina->compartimentos[compartimentoSeleccionado].stock);
              maquina = TECNICO;
              estadoAnterior = BANDERA;
          }
          break;

      case PRECIOS:
          if (estadoAnterior != PRECIOS) {
              Serial.println("Estoy en Precios. Pulse A para establecer un nuevo precio y B para volver a Tecnico.");
              estadoAnterior = maquina;
          }
          if (botonA == TR_PULS) {
              Serial.print("Estableciendo nuevo precio para el producto del compartimento ");
              Serial.print(compartimentoSeleccionado + 1);
              Serial.print(" a: ");
              int productoIdPrecio = ptrMaquina->compartimentos[compartimentoSeleccionado].productoId;
              for (int i = 0; i < ptrMaquina->numProductos; i++) {
                  if (ptrMaquina->productos[i].id == productoIdPrecio) {
                      ptrMaquina->productos[i].precio = cantidadAIntroducir;
                      Serial.println(ptrMaquina->productos[i].precio);
                      break;
                  }
              }
          }
          if (botonB == TR_PULS) {
              maquina = TECNICO;
              estadoAnterior = BANDERA;
          }
          break;
  }
  
      // Aquí termina el switch de la máquina de estados
  }

Modifica esto, agrega break para "cerrar" ese case

case BANDERA://Estado que no hace nada pero sirve de bandera para el resto
      break;

sino el código pasa al siguiente case.
En este caso nunca se va a dar que maquina tenga ese valor así que no importaría pero mejor acostumbrarse a hacerlo bien porque te puede dar dolores de cabeza en algún otro código.
Incluso quitaría ese case, no tiene sentido de ser.

Respecto al enum me llama la atención que te dejase asignar un valor numérico sin siquiera "quejarse" el compilador.
Me ha pasado de querer asignar, por ej., 0 en lugar de ELECCION (que sería básicamente lo mismo) y saltase el error.

Sobre las llaves, no lo tengo muy claro, es relacionado al ámbito de las variables y funciones, alguna vez lo leí en el foro en inglés pero no me quedo del todo claro el porque.
Cómo suelo encerrar cada bloque dentro de funciones (o sea, lo de ahora tienes entre llaves) no he tropezado con el problema.
Por ej.

case LOQUESEA:
  lo_que_sea();
  break;

Agrego:
Obviamente hay que crear la función con lo que antes estaba dentro de ese case.

void lo_que_sea() {
  // Sentencias que estaban en el cuerpo del case
}