#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <Encoder.h> // Include the Rotary Encoder library
LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 20, 4); // Initialize 20x4 LCD
#define buzzer 45
#define motorDriver 2
#define sensor A1
volatile long pulse;
unsigned long lastTime;
float volume;
float desiredFlowRate = 5.0; // Default desired flow rate
float desiredVolume = 0.0; // Variable to store the desired volume set by the user
Encoder myEncoder(A0, A1); // Initialize Rotary Encoder
bool backlightOn = true; // Variable to track the backlight status
// configure keypad (from LAB03)
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}};
const byte rowPins[ROWS] = {22, 24, 26, 28};
const byte colPins[COLS] = {30, 32, 34, 36};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
int animationStep = -2000; // Initial animation step with larger waves
unsigned long startTime;
const unsigned long animationDuration = 5000; // Duration of animation in milliseconds
bool menuDisplayed = false; // Track if the menu is currently displayed or not
bool animationRunning = true; // Flag to control animation running
char selectedMode = ' '; // Default selected mode is none
bool flowDetectionRunning = false; // Flag to indicate if flow detection is running
void animationSetup()
{
for (int i = 0; i < 8; i++)
{
byte charLine[8];
for (int j = 0; j < 8; j++)
{
if (j > i)
charLine[7 - j] = B00000;
else
charLine[7 - j] = B11111;
}
lcd.createChar(i, charLine);
}
}
void setup()
{
pinMode(buzzer, OUTPUT);
pinMode(sensor, INPUT);
pinMode(motorDriver, OUTPUT);
Serial.begin(9600);
lcd.init();
lcd.backlight();
animationSetup();
startTime = millis(); // Record the start time
//Liters per Minute
attachInterrupt(digitalPinToInterrupt(sensor), increase, RISING);
}
void displayMenu()
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("A: Continuous");
lcd.setCursor(0, 1);
lcd.print("B: Flow Control");
lcd.setCursor(0, 2);
lcd.print("C: Automatic");
lcd.setCursor(0, 3);
lcd.print("D: Backlight On/Off");
menuDisplayed = true;
}
void stopAnimation()
{
animationRunning = false;
}
void animation3()
{
if (!animationRunning)
return; // If animation not running, exit
byte character[2];
unsigned long currentTime = millis();
unsigned long elapsedTime = currentTime - startTime;
if (elapsedTime >= animationDuration)
{
// Stop the animation after 5 seconds
animationRunning = false;
lcd.clear();
lcd.setCursor(6, 0);
lcd.print("Flow Rate");
lcd.setCursor(3, 1);
lcd.print("Control System");
lcd.setCursor(1, 2);
lcd.print("Press '*' to Start");
lcd.setCursor(3, 3);
lcd.print("and Select Mode");
return;
}
for (int column = 0; column < 20; column++)
{
// Adjusted coefficients for large to medium waves
int actualValue = cos(animationStep * (0.1 + column * 0.01)) * 8.5 + 9;
if (actualValue == 17)
{
character[0] = byte(255);
character[1] = byte(255);
}
else if (actualValue > 9)
{
character[0] = byte(actualValue - 10);
character[1] = byte(255);
}
else if (actualValue == 9)
{
character[0] = byte(32);
character[1] = byte(255);
}
else if (actualValue == 8)
{
character[0] = byte(32);
character[1] = byte(255);
}
else if (actualValue == 0)
{
character[0] = byte(32);
character[1] = byte(32);
}
else
{
character[0] = byte(32);
character[1] = byte(actualValue - 1);
}
lcd.setCursor(19 - column, 2); // Adjust cursor position for 20x4 LCD (starting from row 3)
lcd.write(character[0]);
lcd.setCursor(19 - column, 3); // Adjust cursor position for 20x4 LCD (starting from row 4)
lcd.write(character[1]);
}
animationStep++;
}
void loop() {
animation3();
char key = keypad.getKey(); // Get the key pressed
if (key != NO_KEY) {
tone(buzzer, 1300, 50); // Play tone for keypress
if (key == '*') { // If asterisk (*) key is pressed
if (!menuDisplayed) { // If menu is not already displayed
displayMenu(); // Display the menu
stopAnimation(); // Stop the animation
}
} else if (key == '#') { // If '#' key is pressed
stopMode(); // Stop the current mode and display mode selection
} else if (key == 'D') { // If 'D' key is pressed
// Toggle the backlight of the LCD
if (backlightOn) {
lcd.noBacklight(); // Turn off backlight
backlightOn = false; // Update backlight status
} else {
lcd.backlight(); // Turn on backlight
backlightOn = true; // Update backlight status
}
} else if (menuDisplayed) { // If menu is displayed
if (key >= 'A' && key <= 'C' && selectedMode == ' ') { // If a valid mode key is pressed and no mode is currently running
selectedMode = key;
switch (selectedMode) {
case 'A':
modeA(); // Enter Mode A
break;
case 'B':
modeB(); // Enter Mode B
break;
case 'C':
modeC(); // Enter Mode C
break;
}
} else if (selectedMode != ' ') { // If mode is selected
// Handle mode-specific button presses here
}
}
}
}
void stopMode() {
tone(buzzer, 1300, 50);
// Stop the current mode
if (flowDetectionRunning) {
stopFlowDetection(); // Stop flow detection if running
}
// Reset mode-related variables and return to mode selection screen
selectedMode = ' ';
menuDisplayed = false;
animationRunning = true;
}
void stopFlowDetection() {
// Stop the flow detection process
// You may need to implement this function based on your requirements
// Reset PWM to zero
analogWrite(motorDriver, 0);
flowDetectionRunning = false; // Reset flow detection flag
}
void displayModeA()
{
lcd.clear();
lcd.setCursor(7, 0);
lcd.print("Mode A");
lcd.setCursor(2, 1);
lcd.print("Continuous Flow");
lcd.setCursor(0, 2);
lcd.print("Set Point:");
lcd.setCursor(0, 3);
lcd.print("Actual: ");
}
void modeA() {
// Display Mode A menu
displayModeA();
// Play buzzer tone when entering the mode
tone(buzzer, 1300, 50);
// Get the set point from the user
int setPoint = 0;
bool setPointEntered = false;
while (!setPointEntered) {
char key = keypad.getKey();
if (key >= '0' && key <= '9') {
setPoint = setPoint * 10 + (key - '0');
lcd.setCursor(10, 2);
lcd.print(setPoint);
tone(buzzer, 1300, 50);
} else if (key == '*') {
setPointEntered = true;
flowDetectionRunning = true; // Set flow detection flag
tone(buzzer, 1300, 50); // Play buzzer tone when '*' is pressed
} else if (key == '#') {
stopMode(); // Stop the current mode and return to mode selection
return;
}
}
// Start flow detection
if (flowDetectionRunning) {
detectFlow(setPoint);
}
}
void displayModeB()
{
lcd.clear();
lcd.setCursor(7, 0);
lcd.print("Mode B");
lcd.setCursor(0, 1);
lcd.print("Rotary Encoder Input");
lcd.setCursor(0, 2);
lcd.print("Set Point:");
lcd.setCursor(0, 3);
lcd.print("Actual: ");
}
void modeB() {
// Display Mode B menu
displayModeB();
// Read the set point from the rotary encoder
int setPoint = myEncoder.read();
// Start flow detection
if (flowDetectionRunning) {
detectFlow(setPoint);
}
// Check for '#' character during operation
if (keypad.getKey() == '#') {
stopMode(); // Stop the current mode and return to mode selection
}
}
void displayModeC(float actualVolume)
{
lcd.clear();
lcd.setCursor(7, 0);
lcd.print("Mode C");
lcd.setCursor(0, 1);
lcd.print("Automatic Dispensing");
lcd.setCursor(0, 2);
lcd.print("Set Point:");
lcd.setCursor(0, 3);
lcd.print("Actual: ");
lcd.print(actualVolume, 1); // Display the actual accumulated volume
lcd.print(" mL");
}
void modeC() {
// Display Mode C menu with initial volume (0.0 mL)
displayModeC(0.0);
// Get the desired volume from the user
float setVolume = getSetVolume();
// Start flow detection
if (flowDetectionRunning) {
detectFlow(setVolume);
}
// Check for '#' character during operation
if (keypad.getKey() == '#') {
stopMode(); // Stop the current mode and return to mode selection
}
}
float getSetVolume() {
// Get the desired volume from the user using the keypad
// This function should wait until the user presses '*' to confirm the input
float volume = 0.0;
bool volumeEntered = false;
while (!volumeEntered) {
char key = keypad.getKey();
if (key >= '0' && key <= '9') {
volume = volume * 10 + (key - '0');
lcd.setCursor(10, 2);
lcd.print(volume);
} else if (key == '*') {
volumeEntered = true;
flowDetectionRunning = true; // Set flow detection flag
}
}
return volume;
}
void detectFlow(float setVolume) {
unsigned long lastTime = 0;
unsigned long interval = 1000; // Interval for flow rate calculation (in milliseconds)
unsigned long totalPulses = 0;
float totalVolume = 0.0;
lcd.setCursor(8, 3);
lcd.print(" "); // Clear previous value
while (true) {
// Check for '#' character during operation
if (keypad.getKey() == '#') {
stopMode(); // Stop the current mode and return to mode selection
return;
}
unsigned long currentTime = millis();
if (currentTime - lastTime >= interval) {
// Calculate flow rate every 'interval' milliseconds
float flowRate = (pulse - totalPulses) * 60.0 / (interval * 7.5); // Assuming 7.5 pulses per liter
totalPulses = pulse;
lastTime = currentTime;
// Update total volume
totalVolume += flowRate / 1000.0; // Convert flow rate from L/min to L/s, then accumulate
// Update LCD with actual accumulated volume under "Actual: " section
lcd.setCursor(8, 3);
lcd.print(totalVolume, 1); // Print actual accumulated volume with 1 decimal place
lcd.print(" mL");
// Check if accumulated volume meets or exceeds set volume
if (totalVolume >= setVolume) {
// If accumulated volume meets or exceeds set volume, stop the pump
analogWrite(motorDriver, 0); // Stop the pump
flowDetectionRunning = false; // Reset flow detection flag
menuDisplayed = false; // Return to mode selection
break;
}
}
}
}
void increase()
{
pulse++;
}