#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <Stepper.h>
// Stepper motor configuration
const int stepsPerRevolution = 200; // Change this to match your motor's specifications
Stepper myStepper(stepsPerRevolution, 10, 11, 12, 13); // Pins for the stepper motor
const uint8_t ROWS = 4; // Four rows
const uint8_t COLS = 4; // Four columns
// Define the keymap
char keys[ROWS][COLS] = {
{ '1', '2', '3', 'A' },
{ '4', '5', '6', 'B' },
{ '7', '8', '9', 'C' },
{ '.', '0', '#', 'D' } // Change '*' to '.'
};
// Define the column and row pins
uint8_t colPins[COLS] = { 5, 4, 3, 2 }; // Pins connected to C1, C2, C3, C4
uint8_t rowPins[ROWS] = { 9, 8, 7, 6 }; // Pins connected to R1, R2, R3, R4
// Create an instance of the Keypad
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// Create an instance of the LCD display (I2C address 0x27, 16x2)
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Use the built-in LED
const int ledPin = 13; // Pin for the built-in LED
// Push button pins
const int BUTTON_RESET = A0; // Pin for reset button
const int BUTTON_REPEAT = A1; // Pin for repeat button
// Variables to store angle input
char angleInput[6]; // To hold 3 digits, 1 '.', and 1 digit + null terminator
int index = 0; // Current index in the angleInput array
int currentSteps = 0; // Track the current position in steps
float totalRotation = 0; // Track the total angle rotation
float userRotation = 0; // Track user-initiated rotations
void setup() {
Serial.begin(9600);
lcd.begin(16, 2);
lcd.backlight();
lcd.clear();
showIntro(1, " Rotary Indexer", " by David");
displayIntroScreen(2, "Use the # key", "for next page");
displayIntroScreen(3, "A Rotate 90 Deg", "B Index to 0 Deg");
displayIntroScreen(4, "C to Clear Input", "D to Input Angle");
displayIntroScreen(5, "Ex.Angle 180.0", "or 72.55");
displayIntroScreen(6, "Then press D key", " to index motor");
pinMode(ledPin, OUTPUT);
pinMode(BUTTON_RESET, INPUT_PULLUP); // Use internal pull-up for reset button
pinMode(BUTTON_REPEAT, INPUT_PULLUP); // Use internal pull-up for repeat button
myStepper.setSpeed(30);
}
void loop() {
char key = keypad.getKey();
// Check for button presses
if (digitalRead(BUTTON_RESET) == LOW) {
returnToZeroPosition(); // Reset the display and return to zero
resetDisplay(); // Clear the input
return; // Skip the rest of the loop
}
if (key != NO_KEY) {
Serial.println(key);
switch (key) {
case '#':
displayNextScreen(); // Only works for intro screens
break;
case 'C':
// Only clear the input if still typing
if (index < 5) {
clearTopRow();
index = 0; // Reset index
angleInput[0] = '\0'; // Clear angle input
}
break;
case 'D':
if (index == 5) {
moveStepperMotor();
userRotation += atof(angleInput); // Update user rotation
displayUserRotation(); // Show user rotation after moving
clearTopRow(); // Clear the top row for the next input
index = 0;
angleInput[0] = '\0';
}
break;
case 'A':
rotateStepperBy90Degrees(); // Rotate motor by 90 degrees
break;
case 'B':
returnToZeroPosition(); // Return to zero position
userRotation = 0; // Reset user rotation count
clearTopRow(); // Clear the top row
lcd.setCursor(0, 1);
lcd.print("User: 0 Deg "); // Display 0 Deg on the bottom row
break;
default:
if (index < 5) {
angleInput[index] = key;
angleInput[index + 1] = '\0';
displayCurrentInput();
index++;
if (index == 5) {
displayAngle(); // Optionally show the input angle before pressing D
}
}
break;
}
}
// Check the repeat button
if (digitalRead(BUTTON_REPEAT) == LOW) {
delay(200); // Debounce delay
}
}
void showIntro(int screenNumber, const char* line1, const char* line2) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(line1);
lcd.setCursor(0, 1);
lcd.print(line2);
delay(3000);
lcd.clear();
}
void displayIntroScreen(int screenNumber, const char* line1, const char* line2) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(line1);
lcd.setCursor(0, 1);
lcd.print(line2);
while (true) {
char key = keypad.getKey();
if (key == '#') {
lcd.clear();
return;
}
}
}
void displayNextScreen() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Next Screen");
lcd.setCursor(0, 1);
lcd.print("Ready to Input");
delay(3000);
lcd.clear();
}
void displayCurrentInput() {
lcd.setCursor(0, 0);
lcd.print(angleInput);
}
void displayAngle() {
lcd.setCursor(0, 0);
lcd.print(angleInput);
lcd.print(" Deg");
}
void clearTopRow() {
lcd.setCursor(0, 0);
lcd.print(" ");
}
void resetDisplay() {
lcd.clear();
index = 0;
angleInput[0] = '\0';
digitalWrite(ledPin, LOW);
}
void moveStepperMotor() {
float angle = atof(angleInput);
int steps = (angle / 360.0) * stepsPerRevolution;
myStepper.step(steps);
currentSteps += steps; // Update current steps
}
void returnToZeroPosition() {
clearTopRow();
lcd.setCursor(0, 0);
lcd.print("Returning to 0");
myStepper.step(-currentSteps);
// totalRotation remains unchanged
currentSteps = 0; // Reset current steps for the position
delay(1000);
clearTopRow();
lcd.setCursor(0, 1);
lcd.print("User: 0 Deg "); // Display 0 Deg on the bottom row
}
void displayUserRotation() {
lcd.setCursor(0, 1);
lcd.print("User: ");
lcd.print(userRotation);
lcd.print(" Deg "); // Display user rotation
}
// Function to rotate stepper motor by 90 degrees
void rotateStepperBy90Degrees() {
lcd.setCursor(0, 0);
lcd.print("Rotating 90 Deg");
myStepper.step(stepsPerRevolution / 4); // Move 90 degrees (1/4 of a revolution)
currentSteps += stepsPerRevolution / 4; // Update current steps
userRotation += 90.0; // Update user rotation as well
displayUserRotation(); // Show updated user rotation
}