//https://wokwi.com/projects/376192337074822145
//V9 Created by Jamie Williams Student Engineer Cummins Meritor 2022-2023//
/*This code is designed to control a stepper motor using an arduino and a rotary
encoder to input various control parameters*/
//More information on the inputs/ the rig can be found in:
//N:\STUDENT FOLDER (MN)\2022 Jamie Williams\Projects\2023-2024 New Students Project Guides\Clockwork Rig V2
#include <LiquidCrystal.h> //screen arduino library
#include <Stepper.h> //Stepper motor library
/*Constant test parameters*/
/*Constant test parameters*/
/*These parameters are mechanically driven from the physical rig itself, it calculates the maximum number of steps the rig may need to achieve (running clerance being 0)*/
//These depend on the components used when building the physical rig
constexpr double pitch = 1.5; //mm
constexpr int lead = 3;
constexpr int max_dist = 44; //mm
constexpr int revolution = 200;
constexpr int max_steps = (revolution*max_dist) / (lead*pitch);
///////////////////////////
LiquidCrystal lcd(13, 12, 11, 10, 9, 8); //declares screen wiring (which ports are used when connecting the screen to the arduino)
Stepper myStepper(revolution, 6, 7); //Setsup stepper motor, how many steps in 1 rev and which ports are being used
// Rotary Encoder Inputs
#define RotaryCLK 2
#define RotaryDT 4
#define PushButton 3 //LABELLED SW (SWITCH) ON ROTARY ENCODER
int menu_counter = 0; // BASE COUNTER TO CONTROL MENUS
int CLKNow; // CURRENT ENCODER POSITION
int CLKPrevious; //PREVIOIUS ENCODER POSITION
/*SELECTS WHICH MENU IS BEING CHANGED*/
bool menu1_selected = false;
bool menu2_selected = false;
bool menu3_selected = false;
bool menu4_selected = false;
bool menu2 = false; //CONDITION TO CHANGE RUNNING CLERANCE
bool btn_pressed = false; // CONDITION TO ACTIVATE MOTOR TO CHANGE RC
bool start_test = false; // start test function cond.
bool start = true;// Cond used to pause rig
int button = -1; //conditon to select option in pause menu
bool pause = false; //pause and play test
float countJunk;
float motor_step;
float stepNUM;
float stepNUM2;
float stepNUM3;
unsigned long S_time = 0; //final calculation
unsigned long S_time2 = 0;
double totalCycles = 100; //value within menu 1
double setRunningClearance = 0; //value within menu 2
double timeOn = 0.50; //value within menu 3 check these
double timeOff = 0.5; //value within menu 4
double rc = 0;
void setup() {
// Set encoder pins as inputs
pinMode(RotaryCLK, INPUT);
pinMode(RotaryDT, INPUT);
pinMode(PushButton, INPUT_PULLUP);
// Setup Serial Monitor
Serial.begin(9600);
// Read the initial state of CLK
CLKPrevious = digitalRead(RotaryCLK);
//LCD setup
lcd.begin(16, 2);
lcd.clear(); //clear the whole LCD
lcd.setCursor(0, 0);
//Welcome
lcd.print("Cummins Meritor");
lcd.setCursor(0, 1);
lcd.print("Clockwork Rig");
delay(1000);
lcd.clear();
/*Interrupt pin means function runs no matter at what point in the test it occurs*/
attachInterrupt(digitalPinToInterrupt(2), rotate, CHANGE); //rotate function always occurs looking for any change in rotation
attachInterrupt(digitalPinToInterrupt(PushButton), pushButton, FALLING); // pushbutton function looks for a 'falling' (press) to occur
}
void loop() {
if (!start) {
pause_test();
}
else if (!menu1_selected && !menu2_selected && !menu3_selected && !menu4_selected && !start_test) {
menu();
} else {
selector();
}
}
void rotate() {
CLKNow = digitalRead(RotaryCLK);
if (CLKNow != CLKPrevious && CLKNow == 1) {
if (digitalRead(RotaryDT) != CLKNow)
{
menu_counter --;
} else {
menu_counter ++;
}
}
CLKPrevious = CLKNow;
}
void pushButton() {
if (menu1_selected) {
menu_counter = 0;
}
if (menu2_selected) {
menu_counter = 1;
}
if (menu3_selected) {
menu_counter = 2;
}
if (menu4_selected) {
menu_counter = 3;
}
switch (menu_counter) {
case 0:
menu1_selected = !menu1_selected;
break;
case 1:
if (!menu2)
menu2_selected = !menu2_selected;
else {
btn_pressed = true;
}
break;
case 2:
menu3_selected = !menu3_selected;
break;
case 3:
menu4_selected = !menu4_selected;
break;
case 4:
start_test = !start_test;
break;
}
if (S_time > 0.1 && button == -1){
pause = !pause;
}
if (button == 0){
start = false;
S_time2 = S_time;
S_time = 0;
totalCycles = 0;
setRunningClearance = 0;
timeOn = 0;
timeOff = 0;
}
else if (button == 1){
pause = !pause;
}
else if (button == 2) {// If cancelled after stopping
start = true;
}
else if (button == 3) {// If continued after stopped
start_test = true;
start = true;
totalCycles = 100;
setRunningClearance = 0.8;
timeOn = 0.25;
timeOff = 0.25;
}
}
///////////////////SELECTS WHICH MENU AND VALUE IS SHOWN///////////////////
void menu(){
switch (menu_counter){
case -1:
menu_counter = 4;
break;
case 0:
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("> Cycle no.: ");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.print(totalCycles, 0);
delay(600);
lcd.clear();
break;
case 1:
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("> RC: ");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.print(rc);
lcd.print("mm");
delay(600);
lcd.clear();
break;
case 2:
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("> Time-on:");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.print(timeOn, 1);
lcd.print("s");
delay(600);
lcd.clear();
break;
case 3:
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("> Time-off:");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.print(timeOff, 1);
lcd.print("s");
delay(600);
lcd.clear();
break;
case 4:
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("> Start test?");
lcd.setCursor(0, 1);
lcd.print(" Yes?");
delay(600);
break;
case 5:
menu_counter = 0;
break;
}
}
///////////////////////////////////////////////////////////////////////////
//////////////////SELECTS WHICH MENU AND VALUE IS SELECTED/////////////////
void selector(){
if (menu1_selected){
lcd.print("X Cycle no.: ");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.print((menu_counter * 100), 0);
totalCycles = menu_counter * 100;
delay(600);
lcd.clear();
if (menu_counter == -1) {
menu_counter = 1000;
} else if (menu_counter >1000){
menu_counter = 0;
}
}
else if (menu2_selected) {
menu_2();
}
else if (menu3_selected){
timeOn = menu_counter / 2;
lcd.print("X Time-on:");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.print(timeOn, 1);
lcd.print("s");
delay(600);
lcd.clear();
if (menu_counter <= 0) {
menu_counter = 10;
} else if (menu_counter > 10) {
menu_counter = 0;
}
}
else if (menu4_selected){
timeOff = menu_counter / 2;
lcd.print("X Time-off:");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.print(timeOff, 1);
lcd.print("s");
delay(600);
lcd.clear();
if (menu_counter <= 0) {
menu_counter = 10;
}else if (menu_counter > 10) {
menu_counter = 1;
}
}
if (start_test) {
calculation();
}
}
///////////////////////////////////////////////////////////////////////////
///////////////////ACCOUNTS FOR RC VALUE IN MAX STEP NO.///////////////////
void menu_2(){
int step = 0;
stepNUM2 = 0;
menu2 = true;
double rc_step;
rc = menu_counter * 0.05;
rc_step = menu_counter * 2;
stepNUM2 = max_steps - rc_step;
motor_step = max_steps - rc_step;
stepNUM = stepNUM2 - stepNUM3;
lcd.print("X RC: ");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.print(rc);
lcd.print("mm");
delay(500);
lcd.clear();
if (menu_counter <= 0) {
menu_counter = 80;
} else if (menu_counter > 80) {
menu_counter = 1;
}
if (btn_pressed) {
rc_step = menu_counter * 2;
menu2_selected = !menu2_selected;
setRunningClearance = max_dist - rc; //total distance travelled is total possible -rc
menu_counter = 1;
lcd.print("Setting RC to:");
lcd.setCursor(0, 1);
lcd.print(rc);
lcd.print("mm");
if (stepNUM < 0) {
step = 4;
stepNUM = stepNUM * -1;
} else {
step = -4;
}
for (float junk = 0.0f; junk <= stepNUM; junk+=1.0f){//BS code
//while(countJunk <= stepNUM){
myStepper.step(step);
//lcd.print(stepNUM);//debugging code
delay(1);
//countJunk += 1.0f;
}
stepNUM3 = stepNUM2;
stepNUM2 = 0;
btn_pressed = false;
menu2 = false;
}
lcd.clear();
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////STEP TIME CALCULATION///////////////////////////
void calculation(){
if (totalCycles != 0 && setRunningClearance != 0 && timeOn != 0 && timeOff != 0){
double total_time = timeOn + timeOff;
S_time = (total_time * totalCycles) / (0.0178 * setRunningClearance * 2.5 * 0.5);
motor(countJunk);
} else {
lcd.clear();
lcd.print("Missing Some");
lcd.setCursor(0, 1);
lcd.print("Variables!");
delay(2000);
start_test = false;
menu();
}
}
//////////////////////////////CONTROLS MOTOR///////////////////////////////
void motor(float &progress){
for (; progress <= (motor_step + 1); progress += 1.0f){
//while(countJunk <= (motor_step + 1)){
if (!start) break;//goes slow thereafter
int complete = 100 * (progress / motor_step);
lcd.setCursor(0, 1);
lcd.print("Progress: ");
lcd.print(complete);
lcd.print(" % ");
for (int n = 0; n < S_time / 10;n++) {
display();
delay(10);
}
myStepper.step(4);
if (complete >= 100.00) {
S_time = 0;
totalCycles = 0;
setRunningClearance = 0;
timeOn = 0;
timeOff = 0;
rc = 0;
delay(100);
start_test = !start_test;
menu();
}
//countJunk += 1.0f;
}
}
///////////////////////////////////////////////////////////////////////////
///////////////////////DISPLAY WHILE TEST IS RUNNING///////////////////////
void display() {
if (menu_counter < -1) {
menu_counter = 0;
}
if (menu_counter > 1) {
menu_counter = 0;
}
if (!pause) {
lcd.setCursor(0, 0);
lcd.print("Test Running ");
} else {
lcd.setCursor(0, 0);
lcd.print("Pause test? ");
switch (menu_counter) {
case -1:
menu_counter = 1;
break;
case 0:
lcd.print ("Y");
button = 0;
break;
case 1:
lcd.print("N");
button = 1;
break;
case 2:
menu_counter = 0;
break;
}
}
}
///////////////////////////////////////////////////////////////////////////
/////////////////////////////////PAUSE TEST////////////////////////////////
void pause_test() {
if (menu_counter > 3) {
menu_counter = 0;
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Test Paused");
lcd.setCursor(0, 1);
lcd.print("End Test: ");
lcd.setCursor(10, 1);
switch (menu_counter) {
case -1:
menu_counter = 2;
break;
case 0:
lcd.print("Y?");
button =2;
delay(500);
break;
case 1:
lcd.print("N?");
delay(500);
button = 3;
break;
case 2:
menu_counter = 0;
break;
}
}
///////////////////////////////////////////////////////////////////////////