// No muestra bien los tercios a modificar
// Pr algún motivo si uso //  lcd.setCursor(0, 0); //  lcd.print("     N: "); no me imprime bien los números en el puerto en serie.
// Como uso pines analógicos necesito poner resistencias pulldown en cada fotodetector.
// El estado 0 significa que no está midiendo nada.
// El estado 1023 (o casi) significa que el sensor está midiendo algo.
// Como se usa el Timer 1 no se puede usar digitalWrite en los pines: 9 y 10.
// Display: SDA va a A4 y SCL va a A5.
// El encoder trucho que conseguí funciona teniendo siempre los pines A y B abiertos (en 1 con el internal pullup). En cada click, pasa a ambos por el cero (el orden depende del sentido de giro).
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#define pin_sensor_1 A1
#define pin_sensor_2 A2
#define pin_sensor_3 A3
#define pin_transistor_leds_IR 2
#define pin_led_indicador 3
#define pin_boton_PAUSA 4
#define pin_Encoder_SW 5
#define pin_Encoder_A 6
#define pin_Encoder_B 7
#define pin_Buzzer 8
//Crear el objeto lcd  dirección  0x27 (a veces es 0x3F) y 16 columnas x 2 filas
LiquidCrystal_I2C lcd(0x27, 16, 2);
int tercios_totales_de_vuelta = 0;
int umbral_de_deteccion = 500;  // Sale de probar con la rueda enebradora.
boolean deteccion_sensor_1 = false;
boolean deteccion_sensor_2 = false;
boolean deteccion_sensor_3 = false;
int estado_sensor_1_actual = 0;
int estado_sensor_1_actual_IR = 0;
int estado_sensor_1_actual_Ruido = 0;
int estado_sensor_1_anterior = 0;
int estado_sensor_2_actual = 0;
int estado_sensor_2_actual_IR = 0;
int estado_sensor_2_actual_Ruido = 0;
int estado_sensor_2_anterior = 0;
int estado_sensor_3_actual = 0;
int estado_sensor_3_actual_IR = 0;
int estado_sensor_3_actual_Ruido = 0;
int estado_sensor_3_anterior = 0;
volatile boolean time_to_blink = false;
boolean blink_on = false;
// Estados:
enum tipo_estado { est_Contador,
                   est_Modificador,
                   est_Pausa };
// estado_actual = tipo_estado::menu_inicial;
tipo_estado estado_actual = est_Contador;
tipo_estado estado_anterior = est_Contador;
void setup() {
  Serial.begin(9600);
  pinMode(pin_led_indicador, OUTPUT);
  pinMode(pin_transistor_leds_IR, OUTPUT);
  digitalWrite(pin_led_indicador, HIGH);
  digitalWrite(pin_transistor_leds_IR, LOW);
  pinMode(pin_boton_PAUSA, INPUT_PULLUP);
  pinMode(pin_Encoder_SW, INPUT_PULLUP);
  pinMode(pin_Encoder_A, INPUT);
  pinMode(pin_Encoder_B, INPUT);
  pinMode(pin_Buzzer, OUTPUT);
  // Inicializar el LCD
  lcd.init();
  //Encender la luz de fondo.
  lcd.backlight();
  lcd.clear();
//  lcd.setCursor(0, 0);
//  lcd.print("     N: ");
  muestra_vueltas(tercios_totales_de_vuelta, 0);
  setupTimer1();
}
void loop() {
  // Verifica estados:
    estado_actual = est_Modificador;
    delay(50);
    while (digitalRead(pin_Encoder_SW) == LOW) {}  // Espera mientras el botón se mantenga pulsado. Cuando se suelta el botón se pasa al estado modificador. Tal vez, si se aprieta demasiado rápido, esta linea trae problemas.
    delay(50);
    Estado_Modificador();  // Entra en estado Modificador.
}
void setupTimer1() {
  // Prepara el timer 1 para compararlo con 1 segundo.
  // https://www.arduinoslovakia.eu/application/timer-calculator
  noInterrupts();
  // Clear registers
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;
  // 1 Hz (16000000/((15624+1)*1024))
  OCR1A = 15624;
  // CTC: Clear Timer on Compare Match Mode
  TCCR1B |= (1 << WGM12);
  // Prescaler 1024 (también se podría usar un prescaler de 256 y un OCR1A=62496)
  TCCR1B |= (1 << CS12) | (1 << CS10);
  // Output Compare Match A Interrupt Enable. Se podría comparar otro tiempo como muestra GreatScott usando el March B.
  TIMSK1 |= (1 << OCIE1A);
  interrupts();
}
ISR(TIMER1_COMPA_vect) {
  // Debe ser lo más corta posible, solo para "levantar alguna bandera" y listo.
  // Al principio intenté prender y apagar el display desde acá y se me colgaba.
  // Se ejecuta cada vez que se dispara la condición del timer 1.
  TCNT1 = 0;
  time_to_blink = true;
}
void Estado_Modificador() {
  // Entra en este estado cuando se detecta que el botón del encoder está presionado.
  // Sale de la función cuando se vuelve a presionar el botón del encoder.
  // Al salir, siempre cambia el contador por el valor ingresado, así que si no se quiere modificar, hay que volver a elegir el mismo que tenía antes.
  int encoder_A_actual;  // uint8_t es equivalente a byte y es lo que está definido como argumento en la función digitalWrite().
  int encoder_A_anterior;
  int encoder_B_actual;
  int tercios_totales_de_vuelta_a_modificar;
  encoder_A_anterior = digitalRead(pin_Encoder_A);
  tercios_totales_de_vuelta_a_modificar = tercios_totales_de_vuelta;
  while (1) {
    encoder_A_actual = digitalRead(pin_Encoder_A);
    if (encoder_A_actual != encoder_A_anterior) {            // Si se rotó la perilla. Creo que en cada click el A pasa a cero y, luego a 1, por lo que solo hay que contabilizar uno de los dos estados.
			if (encoder_A_actual == 1) {            // Si se rotó la perilla. Creo que en cada click el A pasa a cero y, luego a 1, por lo que solo hay que contabilizar uno de los dos estados.
        if (digitalRead(pin_Encoder_B) != encoder_A_actual) {  // Se rotó en sentido horaio.
          tercios_totales_de_vuelta_a_modificar++;
        } else {
          tercios_totales_de_vuelta_a_modificar--;
        }
/*
Serial.print("encoder_A_anterior: ");
Serial.println(encoder_A_anterior);
Serial.print("encoder_A_actual: ");
Serial.println(encoder_A_actual);
Serial.print("Encoder_B: ");
Serial.println(digitalRead(pin_Encoder_B));
*/
Serial.print("tercios_a_modificar:");
Serial.println(tercios_totales_de_vuelta_a_modificar);
  
      muestra_vueltas(tercios_totales_de_vuelta_a_modificar, 1);
      }
      encoder_A_anterior = encoder_A_actual;
    }
    delay(1);  // Es el debouncer más simple que existe.
  }
}
void muestra_vueltas(int tercios_de_vuelta, int fila) {
  // fila indica la fila del display que debe ser cambiada con el dato de las vueltas.
  int vueltas;
  byte fraccion_de_vuelta;
  char char_texto_linea_display[16];
  vueltas = abs(tercios_de_vuelta / 3);
  fraccion_de_vuelta = abs(tercios_de_vuelta % 3);  // Toma valores 0, 1 o 2.
  if (fila == 0) {
    sprintf(char_texto_linea_display, "      N: %s%03d %d/3", tercios_de_vuelta < 0 ? "-" : "", vueltas, fraccion_de_vuelta); // Muchos dicen que usar sprintf es lento, pero acá no me importa.
  }
  else {
    sprintf(char_texto_linea_display, " Change: %s%03d %d/3", tercios_de_vuelta < 0 ? "-" : "", vueltas, fraccion_de_vuelta); // Muchos dicen que usar sprintf es lento, pero acá no me importa.
  }
  lcd.setCursor(0, fila);
  lcd.print(char_texto_linea_display);
//Serial.print("Vueltas: ");
//Serial.println(tercios_de_vuelta);
Serial.println(char_texto_linea_display);
/*
Serial.print("  ");
Serial.print(fraccion_de_vuelta);
Serial.println("/3");
*/
}
void Actualiza_tercios_totales(boolean incrementa) {
  if (incrementa) {
    tercios_totales_de_vuelta++;
  }
  else {
    tercios_totales_de_vuelta--;
  }
  muestra_vueltas(tercios_totales_de_vuelta, 0);
  if ((tercios_totales_de_vuelta % 3) == 0) {
    tone(pin_Buzzer, 330, 50);
  }
  else {
    tone(pin_Buzzer, 165, 50);
  }
//  digitalWrite(pin_led_indicador, HIGH);  // se puede mejorar con algún contador para que se mantenga encendido más tiempo.
}