Crónometro más contador con sensores inductivos y LCD

Para que todo esto?

if (milisegundos % 100 == 0) {
        decimas++;
        // Si han pasado 10 décimas de segundo se cuenta un segundo
        if (decimas == 10) {
          decimas = 0;
          segundos++;
        }
        // Si han pasado 60 segundos se cuenta un minuto
        if (segundos == 60) {
          segundos = 0;
          minutos++;
        }
        // Si han pasado 60 minutos se cuenta una hora
        if (minutos == 60) {
          minutos = 0;
          horas++;
        }

Si ya lleva la cuenta millis(). Volver a contar lo que ya esta contabilizado es una pérdida de tiempo.

Solo opera con dos variables, una que capture el momento en el que empiezas, tu Start o Arranque y otro en el que terminas, tu Stop o Fin. Restas millis() y listo y presentas convirtiendo millis() a h:m:s.xxxx lo que gustes.

Esta es una versión funcional, solo muestra hh:mm:ss asi que amplia a tu gusto

#include <LiquidCrystal_I2C.h>
#include <Wire.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

// Variables para los cálculos internos del cronómetro
int horas         = 0;
int minutos       = 0;
int segundos      = 0;
int decimas       = 0;
int contador      = 0;

unsigned long startTime, stopTime;
bool start, startAnt  = false;
bool stop, stopAnt    = false;
bool reset, resetAnt  = false;

int boton_resta   = 7;   // restar unidades contador
int boton_reset   = 8;   // resetear contador y cronometro
int sensor_inicio = 9; // pulsador_inicio en PIN digital 10
int sensor_fin    = 10;   // pulsador_pausa en PIN digital 9
bool flag = false;
bool freset = false;

// estas variables son para simular
byte estado = 0;
unsigned long demoro;

//char *convertirSegundosAHMmSs(long seconds) {
void convertirSegundosAHMmSs(unsigned long seconds) {
    static unsigned long secondsAnt = 0;
    char tmp[20];

    
    int s = seconds % 60;
    int m = (seconds / 60) % 60;
    int h = (seconds / (60 * 60)) % 24;
    sprintf(tmp, "%02d:%02d:%02d", h,m,s);
    if (seconds != secondsAnt) {
        Serial.println("Recibidos: " + String(seconds));
        Serial.println(tmp);
    }
    secondsAnt = seconds;  
    //return tmp;
}

void setup() {
  Serial.begin(115200);                   // Comienzo de la comunicación serie
  Serial.println("Iniciando programa.");
  pinMode(sensor_inicio, INPUT_PULLUP); // Pin digital 10 como entrada
  pinMode(sensor_fin, INPUT_PULLUP);    // Pin digital 9 como entrada
  pinMode(boton_reset, INPUT);          // Pin digital 8 como entrada
  pinMode(boton_resta, INPUT);          // Pin digital 7 como entrada

  // Inicializamos el LCD
  lcd.begin(16, 2);
  lcd.backlight();
}
void (*resetFunc)(void) = 0; // declare reset function at address 0

void loop() {
  // Si presionamos el pulsador de inicio se pone todo a cero, se reinicia em
  // cronómetro y se suma una unidad al contador
  //start = digitalRead(sensor_inicio);
 // stop  = digitalRead(sensor_fin);
  //reset = digitalRead(boton_reset);
  // switch(estado) {
  //   case 0: // arranco
  //             start = true;
  //             estado = true;
  //             demoro = millis();
  //             break;
  //   case 1:   start = 0;
  //             estado = 2;
  //             break;
  //   case 2:   if (millis() - demoro > 10000UL) {
  //                 stop = true;
  //                 estado = 3;
  //             }
  //             break;
  //   case 3:   stop = 0;
  //             estado = 4;
  //             demoro = millis();
  //             break;
  //   case 4:   if (millis() - demoro > 4000UL) {
  //                 reset = true;
  //                 estado = 5;
  //             }
  //             break;
  //   case 5:   if (millis() - demoro > 5000UL) {
  //                 reset = false;
  //                 estado = 6;
  //             }
  //             break;
  //   case 6:   // vuelvo a empezar
  //             estado = 0;
  //             break;

  // }

  if (start && !startAnt) {   // siempre usa este criterio. El de mirar el flanco no un estado.
      startTime = millis();
      flag = true;
      freset = false;
  }
  startAnt = start;
  
  if (stop && !stopAnt) {   // siempre usa este criterio. El de mirar el flanco no un estado.
      stopTime = millis()-startTime;
      flag = false;
  }
  stopAnt = stop;

  if (reset && !resetAnt) {   // siempre usa este criterio. El de mirar el flanco no un estado.
      freset = true;
  }
  resetAnt = reset;

  if (freset) 
      //Serial.println(convertirSegundosAHMmSs(0));
      convertirSegundosAHMmSs(0);
  else {
      if (flag) {
          //Serial.println(convertirSegundosAHMmSs(startTime/1000));
          convertirSegundosAHMmSs(startTime/1000);
      }
      else {
          //Serial.println(convertirSegundosAHMmSs(stopTime/1000));
          convertirSegundosAHMmSs(stopTime/1000);
      }
        
  } 
}