/*
  Author: Technical Shubham
* Date: 02 May 2024
 *Description: This code controls a servo motor using Arduino and displays the angle on an LCD screen. 
                It allows control via serial input or potentiometer, with a menu system on the LCD for mode switching.
               A pushbutton confirms selections. Serial input sets servo angles, while the potentiometer offers analog control.
               Real-time feedback is provided on the LCD display and LED bar graph.
 * Copyright © 2024 Technical Shubham. All rights reserved.
 */

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

Servo myServo;
String inputString = ""; // String to hold incoming serial data
boolean stringComplete = false; // Whether the string is complete
int potPin = A0; // Analog pin connected to potentiometer
int buttonPin = 2; // Digital pin connected to pushbutton
int leds[10] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; // Digital pins connected to LEDs

// Define LCD properties (columns, rows)
LiquidCrystal_I2C lcd(0x27, 16, 2);

// Define mode variables
enum ControlMode { SerialControl, PotControl };
ControlMode mode = SerialControl;

void setup() {
  Serial.begin(9600);
  myServo.attach(9); // Assuming the servo is connected to pin 9
  pinMode(buttonPin, INPUT_PULLUP); // Set button pin as input with internal pull-up resistor
  for (int i = 0; i < 10; i++) {
    pinMode(leds[i], OUTPUT); // Set LED pins as output
  }
  lcd.init(); // Initialize the LCD
  lcd.backlight(); // Turn on backlight
  lcd.setCursor(0, 0);
  lcd.print("Servo: ");
  lcd.setCursor(0, 1);
  lcd.print("Mode: Serial");
}

void loop() {
  if (digitalRead(buttonPin) == LOW) {
    // Button is pressed, switch mode
    switchMode();
  }
  
  if (mode == SerialControl) {
    processSerialInput(); // Process serial input
  } else {
    controlServo(); // Control servo based on potentiometer input
  }
}

void processSerialInput() {
  if (Serial.available() > 0) {
    char inChar = Serial.read(); // Read incoming character

    if (inChar == '\n') { // If newline character is received
      stringComplete = true; // Set string complete flag
    } else { // Otherwise, append character to inputString
      inputString += inChar;
    }
  }

  if (stringComplete) { // If the string is complete
    if (inputString == "H" || inputString == "h") {
      myServo.write(0); // Move servo to 0 degrees
      delay(100); // Add a delay for servo stabilization
      Serial.println("Servo moved to 0 degrees");
      displayAngle(0);
      updateBarGraph(0);
    } else if (inputString == "F" || inputString == "f") {
      myServo.write(180); // Move servo to 180 degrees
      delay(100); // Add a delay for servo stabilization
      Serial.println("Servo moved to 180 degrees");
      displayAngle(180);
      updateBarGraph(180);
    } else {
      int angle = inputString.toInt(); // Convert inputString to integer
      if (angle >= 0 && angle <= 180) { // Ensure angle is within valid range
        myServo.write(angle); // Move servo to specified angle
        delay(100); // Add a delay for servo stabilization
        Serial.print("Servo moved to ");
        Serial.print(angle);
        Serial.println(" degrees");
        displayAngle(angle);
        updateBarGraph(angle);
      } else { // If angle is out of range, print error message
        Serial.println("Invalid angle. Angle must be between 0 and 180 degrees.");
      }
    }
    inputString = ""; // Clear the input string
    stringComplete = false; // Reset the string complete flag
  }
}

void controlServo() {
  // Read potentiometer value and map it to servo angle
  int potValue = analogRead(potPin); // Read potentiometer value (0-1023)
  int servoAngle = map(potValue, 0, 1023, 0, 180); // Map potentiometer value to servo angle
  myServo.write(servoAngle); // Move servo to the mapped angle
  delay(100); // Add a delay for servo stabilization
  displayAngle(servoAngle);
  updateBarGraph(servoAngle);
}

void switchMode() {
  if (mode == SerialControl) {
    mode = PotControl;
    Serial.println("Switched to Pot Control Mode");
    lcd.setCursor(0, 1);
    lcd.print("Mode: Pot    "); // Display current control mode
  } else {
    mode = SerialControl;
    Serial.println("Switched to Serial Control Mode");
    lcd.setCursor(0, 1);
    lcd.print("Mode: Serial "); // Display current control mode
  }
  delay(200); // Delay to debounce button
}

void displayAngle(int angle) {
  lcd.setCursor(7, 0);
  lcd.print("   "); // Clear previous servo angle value
  lcd.setCursor(7, 0);
  lcd.print(angle); // Display current servo angle
}

void updateBarGraph(int angle) {
  int numLeds = map(angle, 0, 180, 0, 10); // Map servo angle to number of LEDs
  for (int i = 0; i < 10; i++) {
    digitalWrite(leds[i], i < numLeds ? HIGH : LOW); // Turn on LEDs up to the mapped number
  }
}