Contador Tuercas Automatico

Buenas tardes estoy realizando un contador de tuercas automatico y en la opcion de separar tuercas no me registra na de conteo del sensor comparto el codigo y url Contador Tuercas Automatico - Wokwi ESP32, STM32, Arduino Simulator

agradezco si me pueden orientar

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

// Configuración del LCD 20x4 con interfaz I2C
LiquidCrystal_I2C lcd(0x27, 20, 4);

// Configuración del teclado matricial 4x4
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {11, 10, 8, 7};
byte colPins[COLS] = {6, 5, 4, 2};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

// Configuración del sensor infrarrojo digital
const int sensorPin = 3;
int cantidadPiezas = 0;
int piezasContadas = 0;
unsigned long tiempoAnterior = 0;
const unsigned long intervaloDeEspera = 100; // Intervalo de espera en milisegundos para evitar rebotes
bool separando = false;
String cantidadStr = "";
bool sensorAnteriorEstado = LOW; // Estado anterior del sensor para detectar cambios

Servo servo;
const int pinServo = 13;

// Estado del menú actual
enum MenuState {MAIN_MENU, CONTAR_MENU, SEPARAR_MENU, REGISTRO_MENU};
MenuState currentMenu = MAIN_MENU;

void setup() {
    pinMode(sensorPin, INPUT);
    pinMode(pinServo, OUTPUT);
    servo.attach(9);

    // Inicializar el LCD
    lcd.init();
    lcd.backlight();
   
}

void loop() {
    char key = keypad.getKey();
  
    if (key) {
        switch (currentMenu) {
            case MAIN_MENU:
                handleMainMenu(key);
                break;
                
            case CONTAR_MENU:
                handleContarMenu(key);
                break;
                
            case SEPARAR_MENU:
                handleSepararMenu(key);
                break;

            case REGISTRO_MENU:
                handleRegistroMenu(key);
                break;
        }
    }
}

void handleMainMenu(char key) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("A. Contar Tuercas");
    lcd.setCursor(0, 1);
    lcd.print("B. Separar Tuercas");
    lcd.setCursor(0, 2);
    lcd.print("C. Registro Conteo");
  

    if (key == 'A') {
        currentMenu = CONTAR_MENU;
    } else if (key == 'B') {
        currentMenu = SEPARAR_MENU;
    } else if (key == 'C') {
        currentMenu = REGISTRO_MENU;
    } else if (key == 'D') {
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Saliendo...");
        delay(1000);
        lcd.clear();
        currentMenu = MAIN_MENU;
    }
}

void handleContarMenu(char key) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Contando tuercas...");
    lcd.setCursor(0, 3);
    lcd.print("Press D to salir");

    piezasContadas = 0;
    tiempoAnterior = millis(); // Guardar el tiempo inicial

    while (true) {
        if (millis() - tiempoAnterior >= intervaloDeEspera) {
            bool sensorEstadoActual = digitalRead(sensorPin);
            if (sensorEstadoActual == HIGH && sensorAnteriorEstado == LOW) {
                piezasContadas++;
                lcd.setCursor(0, 1);
                lcd.print("Contadas: ");
                lcd.print(piezasContadas);
                tiempoAnterior = millis(); // Actualizar el tiempo anterior
            }
            sensorAnteriorEstado = sensorEstadoActual;
        }
        
        if (keypad.getKey() == 'D') {
            currentMenu = MAIN_MENU;
            return;
        }
    }
}

void handleSepararMenu(char key) {
    static bool separando = false;

    if (!separando) {
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Ingrese cantidad:");

        if (key >= '0' && key <= '9') {
            cantidadStr += key;
            lcd.setCursor(0, 1);
            lcd.print("Cantidad: ");
            lcd.print(cantidadStr);
        } else if (key == '#') {
            cantidadPiezas = cantidadStr.toInt();
            cantidadStr = "";
            separando = true;
            piezasContadas = 0;
            lcd.clear();
            lcd.setCursor(0, 0);
            lcd.print("Separando tuercas...");
            lcd.setCursor(0, 1);
            lcd.print("Contador: ");
            lcd.print(piezasContadas);
        }
    } else {
        bool sensorEstadoActual = digitalRead(sensorPin);
        if (sensorEstadoActual == HIGH && sensorAnteriorEstado == LOW) {
            piezasContadas++;
            lcd.setCursor(0, 1);
            lcd.print("Contador: ");
            lcd.print(piezasContadas);
            delay(200); // Retardo para evitar múltiples conteos por una sola pieza
            
            if (piezasContadas >= cantidadPiezas) {
                lcd.clear();
                lcd.setCursor(0, 0);
                lcd.print("Separacion completada");
                delay(2000);
                separando = false;
            }
        }
        sensorAnteriorEstado = sensorEstadoActual;

        if (key == 'D') {
            currentMenu = MAIN_MENU;
            return;
        }
    }
}

void handleRegistroMenu(char key) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Conteo registrado:");
    lcd.setCursor(0, 1);
    lcd.print(piezasContadas);

    if (key == 'D') {
        currentMenu = MAIN_MENU;
       
    }
}

Su publicacion se MUEVE a su ubicacion actual ya que es mas adecuada.

Los gestores del menu se ejecutan solo si hay una tecla pulsada. En el caso de separar, la primera parte va bien porque se van entrado dígitos. Pero cuando dejan de entrarse, como "key" es null, no entra ya en el gestor de separar.
En contador te funciona porque alli tienes un while y se va contando dentro del gestor de conat sin salir de él hasta que se solicita salida.
De hecho, si en wokwi que has hecho vas pulsando B y "activando" el sensor, verás que cuenta algo. Al pulsar B se provoca que entre de nuevo en el gestor de separar.
Quizás no me explico muy bien, lo siento.

Solo reparé la parte de separar piezas pero usa ese criterio si se te presentan mas problemas
Acá la simulación y el código corregido. Tmb hice que al iniciar te muestre el menú.

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

// Configuración del LCD 20x4 con interfaz I2C
LiquidCrystal_I2C lcd(0x27, 20, 4);

// Configuración del teclado matricial 4x4
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {11, 10, 8, 7};
byte colPins[COLS] = {6, 5, 4, 2};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

// Configuración del sensor infrarrojo digital
const int sensorPin = 3;
int cantidadPiezas = 0;
int piezasContadas = 0;
unsigned long tiempoAnterior = 0;
const unsigned long intervaloDeEspera = 100; // Intervalo de espera en milisegundos para evitar rebotes
bool separando = false;
String cantidadStr = "";
bool sensorAnteriorEstado = LOW; // Estado anterior del sensor para detectar cambios

Servo servo;
const int pinServo = 13;

// Estado del menú actual
enum MenuState {MAIN_MENU, CONTAR_MENU, SEPARAR_MENU, REGISTRO_MENU};
MenuState currentMenu = MAIN_MENU;

void muestro_menu() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("A. Contar Tuercas");
  lcd.setCursor(0, 1);
  lcd.print("B. Separar Tuercas");
  lcd.setCursor(0, 2);
  lcd.print("C. Registro Conteo");
}

void separandoPiezas () {
  bool sensorEstadoActual = digitalRead(sensorPin);
  //Serial.println(sensorEstadoActual ? " ON" : "OFF");
  lcd.setCursor(0, 2);
  lcd.print(sensorEstadoActual ? "true" : "false");
  if (sensorEstadoActual && !sensorAnteriorEstado) {
    piezasContadas++;
    lcd.setCursor(0, 1);
    lcd.print("Contador: ");
    lcd.print(piezasContadas);
    delay(200); // Retardo para evitar múltiples conteos por una sola pieza
    if (piezasContadas >= cantidadPiezas) {
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Separacion completada");
      delay(2000);
      separando = false;
    }
  }
  if (sensorEstadoActual != sensorAnteriorEstado) {
    Serial.println(sensorEstadoActual ? " ON" : "OFF");
    sensorAnteriorEstado = sensorEstadoActual;
  }

}

void setup() {
  Serial.begin(9600);
  pinMode(sensorPin, INPUT);
  pinMode(pinServo, OUTPUT);
  servo.attach(9);

  // Inicializar el LCD
  lcd.init();
  lcd.backlight();
  muestro_menu();
}

void loop() {
  char key = keypad.getKey();

  if (key) {
    switch (currentMenu) {
      case MAIN_MENU:
        handleMainMenu(key);
        break;

      case CONTAR_MENU:
        handleContarMenu(key);
        break;

      case SEPARAR_MENU:
        handleSepararMenu(key);
        break;

      case REGISTRO_MENU:
        handleRegistroMenu(key);
        break;
    }
  }
  //Serial.println("Key: "+ String(key));
  if (separando) {
    separandoPiezas();
  }
}

void handleMainMenu(char key) {
  muestro_menu();


  if (key == 'A') {
    currentMenu = CONTAR_MENU;
  } else if (key == 'B') {
    currentMenu = SEPARAR_MENU;
  } else if (key == 'C') {
    currentMenu = REGISTRO_MENU;
  } else if (key == 'D') {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Saliendo...");
    delay(1000);
    lcd.clear();
    currentMenu = MAIN_MENU;
  }
}

void handleContarMenu(char key) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Contando tuercas...");
  lcd.setCursor(0, 3);
  lcd.print("Press D to salir");

  piezasContadas = 0;
  tiempoAnterior = millis(); // Guardar el tiempo inicial

  while (true) {
    if (millis() - tiempoAnterior >= intervaloDeEspera) {
      bool sensorEstadoActual = digitalRead(sensorPin);
      //Serial.println(sensorEstadoActual ? " ON" : "OFF");
      if (sensorEstadoActual && !sensorAnteriorEstado) {
        piezasContadas++;
        lcd.setCursor(0, 1);
        lcd.print("Contadas: ");
        lcd.print(piezasContadas);
        tiempoAnterior = millis(); // Actualizar el tiempo anterior
      }
      sensorAnteriorEstado = sensorEstadoActual;
    }

    if (keypad.getKey() == 'D') {
      currentMenu = MAIN_MENU;
      return;
    }
  }
}

void handleSepararMenu(char key) {
  bool sensorEstadoActual;

  lcd.setCursor(0, 3);
  lcd.print(separando ? "true" : "false");
  //Serial.println(separando ? "separando =  ON" : "separando = OFF");
  if (!separando) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Ingrese cantidad:");
    if (key >= '0' && key <= '9') {
      cantidadStr += key;
      lcd.setCursor(0, 1);
      lcd.print("Cantidad: ");
      lcd.print(cantidadStr);
    } else if (key == '#') {
      cantidadPiezas = cantidadStr.toInt();
      cantidadStr = "";
      separando = true;
      piezasContadas = 0;
      //lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Separando tuercas...");
      lcd.setCursor(0, 1);
      lcd.print("Contador: ");
      lcd.print(piezasContadas);
    }
    // } else {


    //   if (key == 'D') {
    //     currentMenu = MAIN_MENU;
    //     return;
    //   }
  }
}

void handleRegistroMenu(char key) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Conteo registrado:");
  lcd.setCursor(0, 1);
  lcd.print(piezasContadas);

  if (key == 'D') {
    currentMenu = MAIN_MENU;
  }
}

Gracias, tengo otra pregunta le voy agregar un opcion para que me mida el nivel de bateria voy a usar una bateria de 8.4 voltios y uso un divisor resisstivo de 10k y 10k lo que meda ala salida 4.2 voltios , y quiero que me mida en un rango de 6v = 0% y 8.4= 100% , pero no logro que me mida. cuando hay 6v me da un porcenraje de 4% y cuando hay 8.4v ne da un porcentaje de 73% comparto el codigo

float getBatteryLevel() {
  int sensorValue = analogRead(batteryPin);
  // Convertir el valor del ADC a voltaje (0-1023 a 0-5V)
  float voltage = sensorValue * (4.2 / 1023.0) * 2 ;
  // Calcular el voltaje real de la batería (divisor de voltaje 10k y 10k)
 float batteryVoltage = voltage * ((10.0 + 10.0) / 20.0);
  // Calcular el nivel de batería en porcentaje
  float batteryLevel = (batteryVoltage - 6.0 ) /( 8.4 - 6.0) * 100.;
  // Limitar el resultado entre 0 y 100
  batteryLevel = constrain(batteryLevel, 0.0, 100.0);
  
  return batteryLevel;

Concentrate en que tu divisor de tensión te entregue 5V para los 8.4V de las baterias totalmente cargadas en serie.
Lo demás se hace con código.
Te doy estos datos mas o menos aprox

Con R1 = 6k8 y R2 = 10K tienes 5V
O sea 8.4V entregan 5V en tu entrada ADC.
Listo, entonces 5V son 100%
cuando recibas 6V será tu 0%
El resto es código, una recta
8.4V ---------- 100%
6.0V ----------- 0%
m = 100/(8.4-6) = 100/2.4 = 125/3
y = mx+b
y(8.4) = 100 => obtengo b
b = 100-m*8.4= 100-350= -250 = b

y = 125/3*x-250
cuando x = 6.0V => y = 0%
cuando x = 8.4V => y = 100%
listo