// start
// PINS DEFINITION
const int encoder_pin_A = 25,  // pulled up externally, and a 100nF cap to Gnd
          encoder_pin_B = 33,  // pulled up externally, and a 100nF cap to Gnd
          motor_input_A1 = 26,
          motor_input_A2 = 27,
          
          not_used_pin_here = 99;
// END PINS
#ifndef CONFIG_IDF_TARGET_ESP32
#error "Please Choose ESP32 Dev Board !!!"
#endif
// variables
// encoder variables
uint32_t interval_to_calculate_rpm = 200; // in millisecs
float rpm_multiplier = 1.0;
float actual_rpm = 0;
//
int previous_output;
int encoder_count;
int going_clockwise = 0;
uint32_t rpm_counter = 0;
uint32_t snap_start_timer = 0;
// motor variables
// Setting PWM properties
uint16_t freq = 50000; // 50kHz
int resolution = 8; // this gives 0-255, 12 gives 0-4095
int channel_input_1 = 1;
int channel_input_2 = 2;
const bool motor_cw = false,
           motor_ccw = !motor_cw;
// task handlers
TaskHandle_t encoder_task,
             motor_task;
void setup() {
  Serial.begin(115200);
  Serial.println("\n\nBooting...");
  create_tasks_here();
}
void loop() {
  encoder_debug_task();
}
void create_tasks_here() {
  // disable watchdogs on both CPUs so multitasking could run properly
  disableCore0WDT();
  disableCore1WDT();
  // create tasks
  xTaskCreate(encoder_task_loop, "encoder_task_loop", 1 * 1024, NULL, 1, &encoder_task);
  delay(20);
  xTaskCreate(motor_task_loop, "motor_task_loop", 1 * 1024, NULL, 2, &motor_task);
  delay(20);
}
void motor_task_loop(void *parameter) {
  pinMode(motor_input_A1, OUTPUT);
  pinMode(motor_input_A2, OUTPUT);
  ledcSetup(channel_input_1 /* LEDChannel */, freq /* freq */, resolution /* resolution */);
  ledcAttachPin(motor_input_A1, channel_input_1 /* LEDChannel */);
  ledcSetup(channel_input_2 /* LEDChannel */, freq /* freq */, resolution /* resolution */);
  ledcAttachPin(motor_input_A2, channel_input_2 /* LEDChannel */);
  set_motor_speed_and_direction(0, motor_cw);
  while (1) {
    delay(1);
    Serial.println("Going Clockwise accelerating");
    for (int x = 0; x < 100; x++) { //
      set_motor_speed_and_direction(x, motor_cw);
      delay(100); // total of 10000ms == 10 seconds
    }
    Serial.println("Stay at maximum speed for 5 secs");
    set_motor_speed_and_direction(100, motor_cw);
    delay(5000);
    Serial.println("Going Counter Clockwise decelerating");
    for (int x = 100; x > 0; x--) { //
      set_motor_speed_and_direction(x, motor_ccw);
      delay(100); // total of 10000ms == 10 seconds
    }
    Serial.println("Stay at 0 speed for 5 secs");
    set_motor_speed_and_direction(0, motor_ccw);
    delay(5000);
  }
}
// eg: set_motor_speed_and_direction(45,motor_cw) means 45% of maximuam speed in motor_cw == clockwise direction, motor_ccw == counter clockwise
void set_motor_speed_and_direction(int speed_perc, bool direction_) {
  //ledcWrite(1 /* LEDChannel */, 0); /* 0-255 */
  int duty_cycle = map(0, 255, 0, 100, speed_perc);
  // ledcWrite(channel_input_1, duty_cycle);
  // ledcWrite(channel_input_2, duty_cycle);
  if (speed_perc == 0) { // both LOW
    ledcWrite(channel_input_1, 0);
    ledcWrite(channel_input_2, 0);
    return;
  }
  if (direction_) {
    ledcWrite(channel_input_1, duty_cycle);
    ledcWrite(channel_input_2, 0);
  }
  else {
    ledcWrite(channel_input_1, 0);
    ledcWrite(channel_input_2, duty_cycle);
  }
}
void encoder_task_loop(void *parameter) {
  pinMode(encoder_pin_A, INPUT);
  pinMode(encoder_pin_B, INPUT);
  previous_output = digitalRead(encoder_pin_A); //Read the inital value of Output A
  while (1) {
    delay(1);
    static int flag = 0;
    static uint32_t encoder_moved_timer = 0;
    static uint32_t debounce_last = 0;
    uint32_t current_millis = millis();
    //  if (digitalRead(encoder_pin_A) != previous_output && current_millis - debounce_last >=10  ) {
    //   debounce_last = current_millis;
    if (digitalRead(encoder_pin_A) != previous_output) {
      if (digitalRead(encoder_pin_B) != previous_output) {
        encoder_count++;
        going_clockwise = 0;
        if ( flag != 3 ) {
          flag = 3;
          rpm_counter = 0; // reset this once as change in direction detected
          snap_start_timer = current_millis; // capture time
        }
        rpm_counter++; // increment
      }
      else {
        encoder_count--;
        going_clockwise = 1;
        if ( flag != 4 ) {
          flag = 4;
          rpm_counter = 0; // reset this once as change in direction detected
          snap_start_timer = current_millis; // capture time
        }
        rpm_counter++; // increment
      }
      encoder_moved_timer = current_millis;
    }
    else { // encoder hasn't moved in a long time, reset these
      if ( current_millis - encoder_moved_timer >= 1000UL ) {
        rpm_counter = 0;
        flag = 0;
      }
    }
    previous_output = digitalRead(encoder_pin_A);
    // getting RPM here
    if ( current_millis - snap_start_timer >= interval_to_calculate_rpm ) {
      actual_rpm = 1000.0 * rpm_multiplier * rpm_counter / (1.0 * interval_to_calculate_rpm); // that 1000 is because we have a intervbal in ms, to change to secs
      actual_rpm = 60 * actual_rpm; // change from RPS to RPM
      // reset these
      snap_start_timer = current_millis;
      rpm_counter = 0;
    }
  }
}
void encoder_debug_task() { // for serial printing
  static int prev_count = 0;
  static const char* direction_[] = {"clockwise", "anti-clockwise"};
  if ( prev_count != encoder_count ) {
    prev_count = encoder_count;
    Serial.printf("counter: %d\ngoing: %s\nRPM: %0.1f\n",
                  encoder_count, direction_[going_clockwise],
                  actual_rpm
                 );
  }
  uint32_t current_millis = millis();
  static uint32_t aa = current_millis;
  if ( current_millis - aa >= 200 ) {
    aa = current_millis;
    Serial.printf("RPM: %0.1f\n",
                  actual_rpm
                 );
  }
}
// end