//LIBRARY -----------------------------------------------------------------------------------------------------
#include <EEPROM.h> //write and read EEPROM (to save and load settings)
#include <Arduino.h>
//GENERAL -----------------------------------------------------------------------------------------------------
#define BARD 115200
// MOTOR -----------------------------------------------------------------------------------------------------
const long maxflow_20s = 55; //mL per 20 seconds, change according to experiments
// const long maxflow_20s = EEPROM.read(0); //mL per 20 seconds, change according to experiments
const long maxflowrate = maxflow_20s * 3; //mL per minute
const long leastvolt = 7.8;
const byte leastanvolt = leastvolt * 255 / 12;
const long minflow_20s = 35;
const long minflowrate = minflow_20s * 3; //mL per minute, change according to experiments
const long maxsetvolume = 500; //change according to the experiment
const long minsetvolume = 0; //change according to the experiment
//LCD -----------------------------------------------------------------------------------------------------
#include <LiquidCrystal_I2C.h>
#define LCD_COLUMNS 16
#define LCD_ROWS 2
#define LCD_I2C_ADDRESS 0x27
LiquidCrystal_I2C lcd (LCD_I2C_ADDRESS, LCD_COLUMNS, LCD_ROWS);
//BOOTUP LCD -----------------------------------------------------------------------------------------------------
void initializelcd() {
lcd.init(); // Initialize the LCD
lcd.backlight(); // Turn on the backlight
lcd.clear();
}
const char* first_message = "Developed By";
const char* second_message = "MT324-W24 GP 2";
const char* third_message = "Peristaltic Pump";
const char* forth_message = "Booting Up......";
void bootUpMessage(int time, int boottime) {
lcd.setCursor(0, 0); // Set the cursor to the first column of the first row
lcd.print(first_message);
lcd.setCursor(0, 1); // Set the cursor to the first column of the second row
lcd.print(second_message);
delay(time);
lcd.setCursor(0, 0);
lcd.print(third_message);
lcd.setCursor(0, 1); // Set the cursor to the first column of the second row
lcd.print(forth_message);
delay(boottime);
lcd.clear();
}
// MENU -----------------------------------------------------------------------------------------------------
volatile int menulayer = 0;
volatile int menupage = 0;
String menuItems[] = {"Continuous Flow", "Volume Flow", "Calibration"};
const int numMenuItems = sizeof(menuItems) / sizeof(menuItems[0]);
int currentMenuItem = 0;
long previousPosition = 0; // Store previous encoder position
//ROTARY ENCODER -----------------------------------------------------------------------------------------------------
#include <Encoder.h>
#include <Wire.h>
#define PIN_ROTARY_CLK 2 // Used for generating interrupts using CLK signal
#define PIN_ROTARY_DAT 3 // Used for reading DT signal
#define PIN_ROTARY_SW 4 // Used for the Rotary push button switch
#define PIN_ROTARY_5V 5 // Set to HIGH to be the 5V pin for the Rotary Encoder
#define PIN_ROTARY_GND 6 // Set to LOW to be the GND pin for the Rotary Encoder
Encoder knob(PIN_ROTARY_CLK, PIN_ROTARY_DAT);
void initializeencoder(){
pinMode(PIN_ROTARY_CLK, INPUT);
pinMode(PIN_ROTARY_DAT, INPUT);
pinMode(PIN_ROTARY_SW, INPUT_PULLUP);
pinMode(PIN_ROTARY_5V, OUTPUT);
pinMode(PIN_ROTARY_GND, OUTPUT);
digitalWrite(PIN_ROTARY_5V, HIGH);
digitalWrite(PIN_ROTARY_GND, LOW);
}
int lastClk = HIGH;
volatile int rotaryCount = 0;
volatile bool rotaryclick = false;
volatile bool rotarylongpress = false;
int newClk = digitalRead(PIN_ROTARY_CLK);
int dtValue = digitalRead(PIN_ROTARY_DAT);
//MOTOR AND FAN ------------------------------------------------------------------------------------------------
//MOTOR -----------------------------------------------
#define MOTOR_ENM 9
#define MOTOR_IN1 8
#define MOTOR_IN2 7
//FAN -------------------------------------------------
#define MOTOR_ENF 10
#define MOTOR_IN3 11
#define MOTOR_IN4 12
volatile int setflowrate = maxflowrate;
volatile int setvolume = 0;
void initializemotor()
{
pinMode(MOTOR_ENM,OUTPUT);
pinMode(MOTOR_IN1,OUTPUT);
pinMode(MOTOR_IN2,OUTPUT);
pinMode(MOTOR_ENF,OUTPUT);
pinMode(MOTOR_IN3,OUTPUT);
pinMode(MOTOR_IN4,OUTPUT);
analogWrite(MOTOR_ENM,LOW);
digitalWrite(MOTOR_IN1,LOW);
digitalWrite(MOTOR_IN2,LOW);
digitalWrite(MOTOR_ENF,LOW);
digitalWrite(MOTOR_IN3,LOW);
digitalWrite(MOTOR_IN4,LOW);
}
void motor(int flow, int volume)
{
int speed = map(flow, minflowrate, maxflowrate, leastanvolt, 255);
int runtime = 60000 * volume / flow;
analogWrite(MOTOR_ENM, speed);
digitalWrite(MOTOR_IN1, LOW); //control the rotation direction
digitalWrite(MOTOR_IN2, HIGH); //control the rotation direction
Serial.println(speed);
digitalWrite(MOTOR_IN3, LOW);
digitalWrite(MOTOR_IN4, HIGH);
digitalWrite(MOTOR_ENF, HIGH); //start the cooling fan
delay(runtime);
}
void motoroff(){
digitalWrite(MOTOR_IN1, LOW);
digitalWrite(MOTOR_IN2, LOW);
analogWrite(MOTOR_ENM, 0);
digitalWrite(MOTOR_IN3, LOW);
digitalWrite(MOTOR_IN4, LOW);
digitalWrite(MOTOR_ENF, LOW); //turn off the cooling fan
}
// MENU MOTION -------------------------------------------------------------------------------------------------
volatile int newPosition = 0;
volatile int perviousPosition = 0;
volatile bool rotaryCW = false;
volatile bool rotaryCCW = false;
const int bufferdelay = 300;
void encoder(){
int newPosition = knob.read()/4; //change the number according to the step of the encoder
if (newPosition != previousPosition)
{
lcd.clear();
if (currentMenuItem < numMenuItems)
{
currentMenuItem ++;
if (currentMenuItem == numMenuItems)
currentMenuItem = 0;
}
if (previousPosition > newPosition)
{
rotaryCW = true;
rotaryCCW = false;
}
if (previousPosition < newPosition)
{
rotaryCW = false;
rotaryCCW = true;
}
if (previousPosition == newPosition)
{
rotaryCW = false;
rotaryCCW = false;
}
}
previousPosition = newPosition;
if (digitalRead(PIN_ROTARY_SW) == LOW)
{rotaryclick = true;}
else
{rotaryclick = false;}
}
// MENU -----------------------------------------------------------------------------------------------------
void displayMenu() {
// MAIN MENU -------------------------------------------------------------------------
if (menulayer == 0)
{
lcd.setCursor(0,0);
lcd.print("Select function:");
lcd.setCursor(0,1);
lcd.print(menuItems[currentMenuItem]);
while (rotaryclick)
{
menulayer = 1;
menupage = currentMenuItem;
rotaryclick = false;
lcd.clear();
delay(bufferdelay);
}
}
// CONTINUOUS FLOW -------------------------------------------------------------------
// FLOWRATE SELECTION ----------------------------------------------------------------
if(menulayer == 1 && menupage == 0)
{
lcd.setCursor(0,0);
lcd.print(menuItems[0]);
lcd.setCursor(0,1);
lcd.print(setflowrate);
lcd.setCursor(9,1);
lcd.print(" mL/min");
while (rotaryCW){
if (setflowrate < maxflowrate){
setflowrate++;
}
lcd.setCursor(0,1);
lcd.print(setflowrate);
lcd.setCursor(9,1);
lcd.print(" mL/min");
rotaryCW = false;
}
while (rotaryCCW){
if (setflowrate > minflowrate){
setflowrate--;
}
lcd.setCursor(0,1);
lcd.print(setflowrate);
lcd.setCursor(9,1);
lcd.print(" mL/min");
rotaryCCW = false;
}
while (rotaryclick)
{
menulayer = 2;
menupage = 0;
rotaryclick = false;
lcd.clear();
delay(bufferdelay);
}
}
// CONTINOUS FLOW RUNNING -------------------------------------------------------------
if (menulayer == 2 && menupage == 0)
{
lcd.setCursor(0,0);
lcd.print("Pump Running");
motor(setflowrate,0);
while (rotaryclick)
{
motoroff();
lcd.setCursor(0,1);
lcd.print("Stopped");
delay(2000);
menulayer = 0;
menupage = 0;
rotaryclick = false;
lcd.clear();
delay(bufferdelay);
}
}
// VOLUME FLOW -----------------------------------------------------------------------
// VOLUME SELECTION ------------------------------------------------------------------
if(menulayer == 1 && menupage == 1)
{
lcd.setCursor(0,0);
lcd.print(menuItems[1]);
lcd.setCursor(0,1);
lcd.print(setvolume);
lcd.setCursor(14,1);
lcd.print("mL");
while (rotaryCW){
if (setvolume < maxsetvolume){
setvolume++;
}
lcd.setCursor(0,1);
lcd.print(setvolume);
lcd.setCursor(14,1);
lcd.print("mL");
rotaryCW = false;
}
while (rotaryCCW){
if (setvolume > 0){
setvolume--;
}
lcd.setCursor(0,1);
lcd.print(setvolume);
lcd.setCursor(14,1);
lcd.print("mL");
rotaryCCW = false;
}
while (rotaryclick)
{
menulayer = 2;
menupage = 1;
rotaryclick = false;
lcd.clear();
delay(bufferdelay);
}
}
// VOLUME FLOW RUNNING -------------------------------------------------------------
if (menulayer == 2 && menupage == 1)
{
rotaryclick = false;
lcd.setCursor(0,0);
lcd.print("Pump Running");
motor(maxflowrate,setvolume);
motoroff();
lcd.setCursor(0,1);
lcd.print("Finished");
delay(2000);
menulayer = 0;
menupage = 0;
lcd.clear();
}
}
// -----------------------------------------------------------------------------------------------------
void setup() {
// put your setup code here, to run once:
initializemotor();
initializelcd();
initializeencoder();
// bootUpMessage(3000,1500);
Serial.begin(BARD);
}
void loop() {
// put your main code here, to run repeatedly:
encoder();
displayMenu();
}