Code updated: 02/23/2018 for bug fixes.
I needed to create a panner that would make 1 rotation over 5 minutes.
I discovered two other projects to base mine on. Construction is pretty much the same as the source projects below.
I rewrote some of the code. My TimeLapse Panner prompts for # of Rotations and Duration in minutes
You may need to update your Time libraries.
Project Sources
Time_Lapse_Panner
Time_Lapse_Panner_PUBLIC_v2
Here are a list of Parts from V2.
1 (SG804A – 4 inch Gearbox Arm
3 (535044 – .250 inch ID x .500 inch OD Flanged Ball Bearing
* I found this at SparkFun
1 (634074) 1/4 inch x 2.500 inch D-Shaft
1 (545588) (06.11.03) 1/4 inch Bore Clamping Hub
1 (RHA32-36-84) 84 Tooth, 32 Pitch Hub Gear (3/16 inch Face)
1 (585484) (03.11.03) 90 Degree Channel Bracket A
1 (585400) (09.12.02) Attachment Blocks (12 pack)
* I did not use these.
1 (585468) (04.11.01) Flat Single Channel Bracket
* I did not use this.
1 (ABS250-625-725) 6.25 inch x 7.25 inch ABS Sheet (1/4 inch Thick)
* I used .220 thick Acrylic sheet from HomeDepot.
4 (534-1857) 6-32x 2.50 inch Aluminum Standoffs (Round)
* I did not use these. I used 4 x 6-32 3-inch bolts.
EBAY
1 ULN2003 5v stepper motor
1 Axial Pinion Gear 32P 12T Steel 5mm Motor Shaft AX30838 NIB
HomeDepot
Misc 6-32 flat head screws and bolts (I used 1/2 lengths)
Amazon
1 SainSmart LCD 1602 Keypad Shield with Mega 2560 Arduino board
* The Mega board is probably overkill for this project.
Video
Wiring Diagram
Sketch Code
// https://markwheeler.com/wordpress/?p=381
#include <LiquidCrystal.h>
#include <Stepper.h>
#include <TimeLib.h> // Updates 2/23/2018
#define STEPS 2048 //Number of steps per revolution
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
Stepper small_stepper(STEPS, 1, 3, 2, 12);
int lcd_key = 0;
int adc_key_in = 0;
int endStep = 1;
int startPos = 0;
int curPos = 0;
float endPos = 14333;
int minutes = 0; // Duration
int endtime = 0;
int timeNow = 0;
float rotations = 0; // How many rotations?
float runtime = 0;
float span = 0;
float stepDelay = 0;
float spanRemain = 0;
int motorPWR = 15; // was 13
int time = 0; // clock holder
int runlength = 0;
int countdown = 10; // Seconds until program runs
#define btnRIGHT 0
#define btnUP 1
#define btnDOWN 2
#define btnLEFT 3
#define btnSELECT 4
#define btnNONE 5
int read_LCD_buttons()
{
adc_key_in = analogRead(0); // read the value from the sensor
// my buttons when read are centered at these valies: 0, 144, 329, 504, 741
// we add approx 50 to those values and check to see if we are close
if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result
// For V1.1 us this threshold
if (adc_key_in < 50) return btnRIGHT;
if (adc_key_in < 250) return btnUP;
if (adc_key_in < 450) return btnDOWN;
if (adc_key_in < 650) return btnLEFT;
if (adc_key_in < 850) return btnSELECT;
return btnNONE; // when all others fail, return this...
}
void setup(){
lcd.begin (16,2);
pinMode(motorPWR, OUTPUT);
}
void loop(){
digitalWrite(motorPWR, HIGH);
while(endStep){
// I do this for no reason.
lcd.setCursor(0,0);
lcd.print("TimeLapse Panner");
lcd.setCursor(0,1);
lcd.print("v3.11282014");
delay(2000);
time_t timeNow = now(); // What time is it right now?
endStep = 0;
}
endStep = 1; // Ask for Rotations
lcd.setCursor(0,0);
lcd.print("Rotations: ");
lcd.setCursor(0,1);
lcd.print("(L/R) then SEL");
while(endStep){
lcd_key = read_LCD_buttons(); // read the buttons
switch (lcd_key) // depending on which button was pushed, we perform an action
{
case btnRIGHT:
{
rotations = rotations + 1;
lcd.setCursor(0,1);
lcd.print("Rotations: ");
lcd.setCursor(11,1);
lcd.print(rotations);
delay(175);
break;
}
case btnLEFT:
{
rotations = rotations - 1;
lcd.setCursor(0,1);
lcd.print("Rotations: ");
lcd.setCursor(11,1);
lcd.print(rotations);
delay(175);
break;
}
case btnSELECT:
{
endStep = 0;
delay(175);
break;
}
case btnNONE:
{
break;
}
}
}
endStep = 1;
// Ask for Duration
lcd.setCursor(0,0);
lcd.print("Duration: ");
lcd.setCursor(0,1);
lcd.print("(L/R) then SEL");
while(endStep){
lcd_key = read_LCD_buttons(); // read the buttons
switch (lcd_key) // depending on which button was pushed, we perform an action
{
case btnRIGHT:
{
minutes = minutes + 1;
lcd.setCursor(0,1);
lcd.print("Minutes: ");
lcd.setCursor(11,1);
lcd.print(minutes);
delay(175);
break;
}
case btnLEFT:
{
minutes = minutes - 1;
lcd.setCursor(0,1);
lcd.print("Minutes: ");
lcd.setCursor(11,1);
lcd.print(minutes);
delay(175);
break;
}
case btnSELECT:
{
endStep = 0;
// Countdown
while (countdown) {
lcd.clear();
lcd.setCursor(0,1);
lcd.print("Countdown: ");
lcd.setCursor(12,1);
lcd.print(countdown);
delay (1000);
countdown = countdown - 1;
}
delay(150);
break;
}
case btnNONE:
{
break;
}
}
}
runtime = (minutes * 60000);
lcd.setCursor(0,0);
lcd.print("Rot ");
lcd.print(rotations);
lcd.print("x - ");
lcd.print(minutes);
lcd.print("mins");
lcd.setCursor(0,1);
lcd.print(" secs left");
if (rotations > 0) {
span = (endPos * rotations) - curPos;
spanRemain = span;
stepDelay = (runtime / span);
// small_stepper.step(span * -1);
while (spanRemain) {
// small_stepper.step(1); // Counterclockwise
small_stepper.step(-1); // Clockwise
runtime = (runtime - stepDelay);
// test for seconds left length
runlength = ((runtime / 1000) + 1);
if (runlength >= 100) {
lcd.setCursor(0,1);
} if (runlength >= 10 and runlength <= 99) {
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(1,1);
} else {
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(2,1);
}
lcd.print((runtime / 1000) + 1 );
// time = millis();
// lcd.print( ( (minutes*60000) - (time) ) / 1000); // Not totally accurate. Fix me.
// lcd.print(endtime - second(now()));
spanRemain--;
// curPos = curPos + 1;
delay(stepDelay);
}
rotations = rotations - 1;
}
endStep = 0;
while(1){
lcd.setCursor(0,0);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print("Program Complete");
delay(500);
lcd.setCursor(0,0);
lcd.print("Program Complete");
lcd.setCursor(0,1);
lcd.print(" ");
delay(500);
}
}
Download Time_Lapse_Panner_v3.zip