#include <LiquidCrystal_I2C.h>
#include <Stepper.h>
#include <HX711_ADC.h>

// This is a goal for the weight that should be added to the mixer,
// this value would be communicated from the mobile app
const int goal = 10000;

const int stepsPerRev = 200; // The steps per revolution the motor takes
const int speedFactor = 30; // The rate at which the speed of the motor will be incremented at

// Defining the pins as constants
const int RedPin = 6;
const int GreenPin = 5;
const int BluePin = 3;
const int potpin = 0; // Potentiometer Pin
const int buttonPin = 2;

// Defining the objects being used
LiquidCrystal_I2C lcd(0x27, 20, 4); // LCD I2C
Stepper myStepper(stepsPerRev, 8, 9, 10, 11); // Stepper
HX711_ADC LoadCell(A1, A2); // Load sensor

int val; // To read the value from the analog pin
bool locked = false; // Controlling the locked state of the stand mixer
bool current = !locked; // To determine if there was a state change, initially the opposite of locked
volatile byte buttonReleased = false; // Used to deal with interrupts

// Set up function
void setup() {
  // LCD Set up
  lcd.init();
  lcd.backlight();
  lcd.print("Let's Bake!");

  // Load Cell Set up
  LoadCell.begin();
  LoadCell.start(1000);
  LoadCell.setCalFactor(0.42);

  // RGB LED Set up
  pinMode(RedPin, OUTPUT);
  pinMode(BluePin, OUTPUT);
  pinMode(GreenPin, OUTPUT);

  // Button Set up
  pinMode(buttonPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(buttonPin), buttonReleasedInterrupt, FALLING);
}

void loop() {
  // Checking the Locked state of the mixer
  // If the mixer is locked then the mixer can run and there can be no ingredient weighing
  if (locked) {
    // This indicates that there was a state change and the mixer has just been locked
    if (current != locked) {
      // Updating the LCD appropriately
      lcd.clear();
      lcd.setCursor(1, 0);
      lcd.print("Get Mixing!");
      // Turn off LED by setting the values to 0
      setColor(0, 0, 0);
      // Setting current to locked since the states are now aligned
      current = locked;
    }
    
    val = analogRead(potpin); //reads the value of the potentiometer (between 0 & 1023)
    val = map(val, 0, 1023, 0, 8); //scale it down for speed level (between 0 & 8)
    if (val > 0) {
      myStepper.step(stepsPerRev);
      myStepper.setSpeed(val * speedFactor); // Setting the actual speed of the motor based on the speed level
    }
    else {
      myStepper.step(0); // stop the motor from turning
    }
  }
  // If the mixer is not locked then the mixer cannot run and there can be ingredient weighing
  else {
    // This indicates that there was a state change and the mixer has just been unlocked
    if (current != locked) {
      // Updating the LCD appropriately
      lcd.clear();
      lcd.setCursor(1, 0);
      lcd.print("Weighing Scale");
      // Set RGB LED to green
      setColor(0, 255, 0);
      // Resets the scale to 0
      LoadCell.tare();
      // Setting current to locked since the states are now aligned
      current = locked;
    }

    LoadCell.update(); // Updates the load cell object with the weight
    float weightGrams = LoadCell.getData(); // gets the weight on the load cell in grams

    // Displaying the weight of contents on LCD
    lcd.setCursor(4, 1); // Setting the cursor in the second row of the LCD
    lcd.print(weightGrams, 0);
    lcd.println("g");

    int warningPoint = goal * 0.7; // Warning point is at 70% of the goal value
    // Setting the colour of the LED based on the weight of contents
    if (weightGrams > goal) {
      setColor(255, 0, 0); // Set to Red if goal exceeded
    }
    else if (weightGrams == goal) {
      setColor(0, 0, 255); // Set to Blue if goal exactly achieved
    }
    else if (weightGrams >= warningPoint) {
      setColor(255, 255, 0); // Set to Yellow if warning point matched or exceeded
    }
    else {
      setColor(0, 255, 0); // Set to green if none of the above achieved, indicates less than warning point
    }
  }
}

// Supporting function to write the RGB values for the LED
void setColor(int red, int green, int blue) {
  analogWrite(RedPin, 255 - red);
  analogWrite(GreenPin, 255 - green);
  analogWrite(BluePin, 255 - blue);
}

// Supporting function to attach to the button to handle the interrupt
void buttonReleasedInterrupt() {
  buttonReleased = true;
  locked = !locked; // Toggles the stand mixer's locked state
}