#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <HX711.h>
// LCD initialization
LiquidCrystal_I2C lcd(0x27, 16, 2);
// HX711 Pin assignments
const int DT1 = 3; // Data pin for HX711 #1
const int SCK1 = 2; // Clock pin for HX711 #1
const int DT2 = 5; // Data pin for HX711 #2
const int SCK2 = 4; // Clock pin for HX711 #2
HX711 scale1; // HX711 object for Part A
HX711 scale2; // HX711 object for Part B
// Button pin and potentiometer assignments
const int incrementButtonPin = 6; // Blue button
const int selectButtonPin = 7; // Red button
const int pot = A0; // Potentiometer pin
// A4988 Stepper Motor Driver pin assignments
const int stepPinA = 11; // STEP for motor A
const int dirPinA = 10; // DIR for motor A
const int stepPinB = 9; // STEP for motor B
const int dirPinB = 8; // DIR for motor B
// Variables
int currentOption = 10; // Initial option (10ml)
const int minOption = 10; // Minimum value
const int maxOption = 700; // Maximum value
const int stepSize = 5; // Step increment after 5ml
bool isSelected = false; // Flag to check if TOTAL is selected
bool partASelected = false; // Flag for Part A
bool partBSelected = false; // Flag for Part B
int totalSelected = 0; // Store the selected TOTAL
const float density = 1.07; // Density of the product (g/ml)
void setup() {
pinMode(incrementButtonPin, INPUT_PULLUP);
pinMode(selectButtonPin, INPUT_PULLUP);
pinMode(pot, INPUT);
// Initialize stepper motor driver pins
pinMode(stepPinA, OUTPUT);
pinMode(dirPinA, OUTPUT);
pinMode(stepPinB, OUTPUT);
pinMode(dirPinB, OUTPUT);
// Initialize HX711 scales
scale1.begin(DT1, SCK1); // Setup HX711 #1
scale2.begin(DT2, SCK2); // Setup HX711 #2
lcd.init();
lcd.backlight();
updateLCD(); // Display the initial selection screen
}
void loop() {
// Read potentiometer value and map to ml range
int potValue = analogRead(pot); // Read potentiometer value (0-1023)
currentOption = map(potValue, 0, 1023, minOption, maxOption);
currentOption = (currentOption / stepSize) * stepSize; // Round to nearest stepSize
// Increment button logic
if (digitalRead(incrementButtonPin) == LOW) {
delay(200); // Debounce delay
if (!isSelected) {
incrementOption(); // Navigate TOTAL options
updateLCD(); // Update the screen
}
}
// Select button logic
if (digitalRead(selectButtonPin) == LOW) {
delay(200); // Debounce delay
if (!isSelected) {
totalSelected = currentOption; // Store the selected TOTAL
isSelected = true; // Mark as selected
displayPartSelection("Part A"); // Show Part A selection
} else if (!partASelected) {
partASelected = true; // Mark Part A as completed
moveStepper(stepPinA, dirPinA, "Part A"); // Move stepper for Part A
displayPartSelection("Part B"); // Show Part B selection
} else if (!partBSelected) {
partBSelected = true; // Mark Part B as completed
moveStepper(stepPinB, dirPinB, "Part B"); // Move stepper for Part B
displayFinished(); // Show finished message
} else {
resetSelection(); // Restart the process
}
}
delay(100); // Small delay to reduce flickering on LCD
}
// Increment through the dispensing options (this can be adjusted to work with the potentiometer)
void incrementOption() {
currentOption += stepSize;
if (currentOption > maxOption) {
currentOption = minOption; // Wrap around to the minimum value (10ml)
}
}
// Update LCD for navigating TOTAL options
void updateLCD() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Select Amount:");
lcd.setCursor(0, 1);
lcd.print(currentOption);
lcd.print("ml");
}
// Display part selection (Part A or Part B)
void displayPartSelection(String part) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Select:");
lcd.setCursor(0, 1);
lcd.print(part);
}
// Move the stepper motor for a specific part
void moveStepper(int stepPin, int dirPin, String part) {
int stepsToMove = calculateSteps(totalSelected / 2); // Calculate steps (half of TOTAL)
long initialWeight = 0;
// Read initial weight from the corresponding load cell
if (part == "Part A" && scale1.is_ready()) {
initialWeight = scale1.get_units();
} else if (part == "Part B" && scale2.is_ready()) {
initialWeight = scale2.get_units();
}
// Set direction (change HIGH/LOW for direction as needed)
digitalWrite(dirPin, HIGH);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(part + " Dispensing");
// Move the motor and update the LCD with dispensed volume in ml
for (int i = 0; i < stepsToMove; i++) {
digitalWrite(stepPin, HIGH);
delayMicroseconds(800); // Adjust speed (lower value = faster)
digitalWrite(stepPin, LOW);
delayMicroseconds(800); // Adjust speed (lower value = faster)
// Update dispensed volume on the LCD
if (i % 10 == 0) { // Update LCD every 10 steps for efficiency
long currentWeight = 0;
if (part == "Part A" && scale1.is_ready()) {
currentWeight = scale1.get_units();
} else if (part == "Part B" && scale2.is_ready()) {
currentWeight = scale2.get_units();
}
long dispensedWeight = currentWeight - initialWeight;
float dispensedVolume = dispensedWeight / density; // Convert g to ml
lcd.setCursor(0, 1);
lcd.print("Dispensing:");
lcd.print(dispensedVolume, 1); // Show 1 decimal place
lcd.print("ml ");
}
}
delay(1000); // Pause after dispensing
}
// Calculate steps from ml (placeholder for your equation)
int calculateSteps(int ml) {
return ml * 10; // Example conversion (adjust as per your system)
}
// Display finished message
void displayFinished() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Dispensing Done!");
lcd.setCursor(0, 1);
lcd.print("Press to Restart");
}
// Reset the selection process
void resetSelection() {
isSelected = false;
partASelected = false;
partBSelected = false;
currentOption = minOption;
updateLCD();
}