#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Encoder.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// "UIC Logo", 128x64px
static const unsigned char PROGMEM logo_bmp [] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,
0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00,
0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00,
0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00,
0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00,
0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00,
0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00,
0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00,
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80,
0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0,
0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0,
0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x01, 0xff, 0xff, 0xf8,
0x1f, 0xfc, 0x00, 0x1f, 0xfe, 0x00, 0x0f, 0xe0, 0x00, 0xff, 0xff, 0x00, 0x00, 0x07, 0xff, 0xf8,
0x3f, 0xfc, 0x00, 0x1f, 0xfe, 0x00, 0x0f, 0xe0, 0x00, 0xff, 0xf8, 0x00, 0x00, 0x00, 0xff, 0xfc,
0x3f, 0xfc, 0x00, 0x1f, 0xfe, 0x00, 0x0f, 0xe0, 0x00, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x7f, 0xfc,
0x3f, 0xfc, 0x00, 0x1f, 0xfe, 0x00, 0x0f, 0xe0, 0x00, 0xff, 0x80, 0x00, 0x00, 0x00, 0x1f, 0xfc,
0x7f, 0xfc, 0x00, 0x1f, 0xfe, 0x00, 0x0f, 0xe0, 0x00, 0xff, 0x00, 0x01, 0xf8, 0x00, 0x0f, 0xfe,
0x7f, 0xfc, 0x00, 0x1f, 0xfe, 0x00, 0x0f, 0xe0, 0x00, 0xfe, 0x00, 0x07, 0xff, 0x00, 0x3f, 0xfe,
0x7f, 0xfc, 0x00, 0x1f, 0xfe, 0x00, 0x0f, 0xe0, 0x00, 0xfe, 0x00, 0x1f, 0xff, 0x87, 0xff, 0xfe,
0x7f, 0xfc, 0x00, 0x1f, 0xfe, 0x00, 0x0f, 0xe0, 0x00, 0xfc, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xfe,
0x7f, 0xfc, 0x00, 0x1f, 0xfe, 0x00, 0x0f, 0xe0, 0x00, 0xfc, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xfe,
0x7f, 0xfc, 0x00, 0x1f, 0xfe, 0x00, 0x0f, 0xe0, 0x00, 0xfc, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xfe,
0x7f, 0xfc, 0x00, 0x1f, 0xfe, 0x00, 0x0f, 0xe0, 0x00, 0xfc, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xfe,
0x7f, 0xfc, 0x00, 0x1f, 0xfe, 0x00, 0x0f, 0xe0, 0x00, 0xfc, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xfe,
0x7f, 0xfc, 0x00, 0x1f, 0xfe, 0x00, 0x0f, 0xe0, 0x00, 0xfc, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xfe,
0x7f, 0xfc, 0x00, 0x1f, 0xfe, 0x00, 0x0f, 0xe0, 0x00, 0xfe, 0x00, 0x0f, 0xff, 0x9f, 0xff, 0xfe,
0x3f, 0xfe, 0x00, 0x0f, 0xfc, 0x00, 0x1f, 0xe0, 0x00, 0xfe, 0x00, 0x07, 0xff, 0x00, 0x7f, 0xfc,
0x3f, 0xfe, 0x00, 0x03, 0xf0, 0x00, 0x1f, 0xe0, 0x00, 0xff, 0x00, 0x00, 0xf8, 0x00, 0x0f, 0xfc,
0x3f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xe0, 0x00, 0xff, 0x80, 0x00, 0x00, 0x00, 0x1f, 0xfc,
0x1f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xe0, 0x00, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x3f, 0xf8,
0x1f, 0xff, 0xc0, 0x00, 0x00, 0x00, 0xff, 0xe0, 0x00, 0xff, 0xf8, 0x00, 0x00, 0x00, 0xff, 0xf8,
0x0f, 0xff, 0xf8, 0x00, 0x00, 0x07, 0xff, 0xe0, 0x00, 0xff, 0xff, 0x00, 0x00, 0x07, 0xff, 0xf0,
0x07, 0xff, 0xff, 0xe0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x01, 0xff, 0xff, 0xe0,
0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80,
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00,
0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00,
0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00,
0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00,
0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00,
0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00,
0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00,
0x00, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00,
0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// Encoder pins
#define CLK 2
#define DT 3
#define SW 4
const int travDirPin = 7;
const int travStepPin = 6;
const int rotDirPin = 10;
const int rotStepPin = 11;
Encoder myEnc(CLK, DT);
int lastPos = -999;
int pos;
int modeSelected = -1; // No mode selected initially
boolean confirmed = false;
int lastMode = -1;
#define minTravDist 25 //Defines minimum, maximum and increment of travel distance
#define maxTravDist 975
#define initialRotAng 180 //Define rotation initial setting, minimum, maximum and increment
#define minRotAng 20
#define maxRotAng 360
#define rotAngInc 10
const float pulsesPerMM = 50; //Number of motor pulses for 1mm travel
const float pulsesPerDeg = 4.4444; //Number of motor pulses for 1 degree of rotation
float currentAngle = 0;
int travelDir = 0;
void setup() {
Serial.begin(9600);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.drawBitmap(0, 0, logo_bmp, 128, 64, WHITE);
display.display();
Serial.println("OLED Display Setup complete");
pinMode(SW, INPUT_PULLUP); // Set the button pin as input with pull-up
pinMode(travDirPin, OUTPUT); // Define the travel stepper motor pins
pinMode(travStepPin, OUTPUT);
delay(5000); // Displays logo for 5 seconds
display.clearDisplay();
display.display();
Serial.println("Camera Slider");
Serial.print("Enter mode: 0 = Pan, 1 = Rotate, 2 = Pan & Rotate or use encoder:\n");
}
void loop() {
static long oldPosition = -999;
long newPosition = myEnc.read() / 4; // Encoder position
// Handle encoder input
if (!confirmed) {
if (newPosition != oldPosition) {
oldPosition = newPosition;
if (newPosition < 0) newPosition = 2; // Wrap around logic
if (newPosition > 2) newPosition = 0;
updateDisplay(newPosition);
}
if (digitalRead(SW) == LOW) {
modeSelected = newPosition;
confirmed = true;
Serial.print("Mode selected via Encoder: ");
Serial.println(modeSelected);
displayModeSelection(modeSelected);
}
}
if (!confirmed && Serial.available() > 0) { // Handle serial input
int serialInput = Serial.parseInt();
if (serialInput >= 0 && serialInput <= 2) {
modeSelected = serialInput;
confirmed = true;
Serial.print("Mode selected via Serial: ");
Serial.println(modeSelected);
displayModeSelection(serialInput);
} else {
Serial.println("Invalid input. Please enter a value between 0 and 2"); // displays this. Maybe add a stop motors
}
}
// Execute the selected mode
if (confirmed && modeSelected != lastMode) {
lastMode = modeSelected;
executeMode(modeSelected);
}
}
void updateDisplay(int pos) {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(28, 4);
display.print(F("Camera Slider"));
display.setCursor(25, 20);
if (pos == 0) {
display.print(F("> "));
} else {
display.print(F(" "));
}
display.print(F("Pan"));
display.setCursor(25, 30);
if (pos == 1) {
display.print(F("> "));
} else {
display.print(F(" "));
}
display.print(F("Rotate"));
display.setCursor(25, 40);
if (pos == 2) {
display.print(F("> "));
} else {
display.print(F(" "));
}
display.print(F("Pan & Rotate"));
display.display();
}
const char* getModeName(int pos) {
switch (pos) {
case 0: return "Pan";
case 1: return "Rotate";
case 2: return "Pan & Rotate";
default: return "";
}
}
void displayModeSelection(int mode) {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(10, 30);
display.print(F("Mode selected: \n"));
display.print(getModeName(mode));
display.display();
}
void executeMode(int mode) {
switch (mode) {
case 0:
runPan();
break;
case 1:
runRotate();
break;
case 2:
runPanAndRotate();
break;
}
}
// Function to get user input for travel distance
float inputPanData() {
// works but randomly chooses 0mm as distance
// user needs to type distance really fast
int startTime = millis();
int duration = 5000;
// Wait for user input
while (true) {
int pause = millis();
if (pause - startTime >= duration){
Serial.println("Took too long to respond");
break;
}
if (Serial.available()){
break;
}
}
delay(500);
// Serial.println("Enter travel distance: "); // Doesnt work with simulator
delay(50); // Small delay to ensure input is processed
float currentDist = Serial.parseFloat();
delay(5000); // Reduced delay
return currentDist;
}
float inputRotData(){
// works but randomly chooses 0mm as distance
// user needs to type distance really fast
int startTime = millis();
int duration = 5000;
// Wait for user input
while (true) {
int pause = millis();
if (pause - startTime >= duration){
Serial.println("Took too long to respond");
break;
}
if (Serial.available()){
break;
}
}
delay(500);
delay(50); // Small delay to ensure input is processed
float currentDist = Serial.parseFloat();
delay(5000); // Reduced delay
return currentDist;
}
// Main function to run the pan operation
void runPan() {
//inputPanData();
Serial.println("Enter travel distance: "); // Doesnt work with simulator
float value = inputPanData();
delay(2000);
float travP = value*pulsesPerMM;
Serial.print("Travel Pulses: ");
Serial.print(travP);
Serial.println("mm");
delay(500);
digitalWrite(travDirPin, HIGH); // Sets Direction High = clockwise LOW = Counter Clockwise
for(int i = 0; i < travP; i++) { // Pulses the motor to move the required distance in the required time
digitalWrite(travStepPin, HIGH);
delay(50); // Wait to
digitalWrite(travStepPin, LOW);
delay(100);
}
}
void runRotate() {
// Code to run Rotate mode
Serial.println("Running Rotate");
}
void runPanAndRotate() {
// Code to run Pan & Rotate mode
Serial.println("Running Pan and Rotate");
}