Remote shutter release — Arduino code

The Arduino code and schematic for the Remote camera shutter release project is included here.

This is written for use on a MicroView, which includes a 64 x 48 pixel OLED display. Comments are included in the code and the schematic is below, but for the most part you’ll have to work out what’s going on for yourself (or leave a comment below, and I’ll get back to you).

This project shows a flagrant disregard for debouncing switch input, but it works and saved me the effort of being rigorous about it. Variable range checking isn’t high on the list of things to do either. My apologies if either offends you, but pragmatism wins. 😉


#include <7segment.h>
#include <font5x7.h>
#include <font8x16.h>
#include <fontlargenumber.h>
#include <MicroView.h>
#include <Servo.h>

#define servoPIN 6
#define buttonPIN 2
#define potPIN A1

/*
Use uView to remotely activate camera shutter, via servo arm

- On first boot, read switch pin, if held down, enable Interval mode
- Setup: read Analog pot, adjust to set autofocus rotation for Neutral, Half press & Full
- Enter loop to wait for click & then activate position1, then position2

v0.6
v0.65 corrected variable type for maxIdleTime (was int)
*/

Servo myservo; // create servo object

boolean snap = false, interval = false;
int pNeutral, pFocus, pShutter, vr1, count=0;
unsigned long milliVar, snapTime, wiggleTime;
const unsigned long maxIdleTime = 60000; // millis between keepAlive presses

void setup() {
myservo.attach(servoPIN);
myservo.write(90); // set servo to mid position
pinMode(buttonPIN, INPUT_PULLUP);
pinMode(potPIN, INPUT);

uView.begin(); // begin MicroView
uView.clear(ALL); // erase hardware memory inside the OLED controller
uView.display(); // display the content in the buffer memory, by default it is the uView logo
delay(1000);

for (int i=0; i<10; i++) { // loop a few times, checking to see if button has been held down at boot
if (!digitalRead(buttonPIN)) { // pin is active LOW
interval = true; // if button was down, activate interval timer mode
break;
}
delay(10);
}
if (interval) { // interval mode?
myservo.write(80); // wiggle servo to acknowledge
delay(100);
myservo.write(100);
delay(100);
myservo.write(80);
delay(100);
myservo.write(100);
delay(100);
uView.clear(PAGE); // write message confirming interval mode
uView.setFontType(0); // use 1 for 8x16 font, 0 for 5x7
uView.setCursor(8,13);
uView.print("Interval");
uView.setCursor(20,23);
uView.print("mode");
uView.setCursor(11,33);
uView.print("enabled");
uView.display();
delay(2000);
}

// calibration process, part 1 - set position where camera button not active
uView.clear(PAGE);
uView.setFontType(0); // use 1 for 8x16 font, 0 for 5x7
uView.setCursor(1,1); // drop to lower half of display
uView.print("Calibrate");
uView.line(0,9,56,9);
uView.setCursor(1,12);
uView.print("Set zero");
uView.setCursor(1,20);
uView.print("position,");
uView.setCursor(1,28);
uView.print("then press");
uView.setCursor(1,36);
uView.print("button.");
uView.display(); // display the content in the buffer memory, by default it is the uView logo
pNeutral = readServo(); // neutral is the last position of the servo before click
delay(1000);

// calibration process, part 2 - set position where camera button half pressed
uView.clear(PAGE);
uView.setFontType(0); // use 1 for 8x16 font, 0 for 5x7
uView.setCursor(1,1); // drop to lower half of display
uView.print("Calibrate");
uView.line(0,9,56,9);
uView.setCursor(1,12);
uView.print("Set 1st");
uView.setCursor(1,20);
uView.print("position,");
uView.setCursor(1,28);
uView.print("then press");
uView.setCursor(1,36);
uView.print("button.");
uView.display();
pFocus = readServo(); // focus active is the last position of the servo before click
delay(1000);

// calibration process, part 3 - set position where camera button fully pressed
uView.clear(PAGE);
uView.setFontType(0); // use 1 for 8x16 font, 0 for 5x7
uView.setCursor(1,1); // drop to lower half of display
uView.print("Calibrate");
uView.line(0,9,56,9);
uView.setCursor(1,12);
uView.print("Set 2nd");
uView.setCursor(1,20);
uView.print("position,");
uView.setCursor(1,28);
uView.print("then press");
uView.setCursor(1,36);
uView.print("button.");
uView.display();
pShutter = readServo(); // save the last position of the servo before click
delay(1000);

myservo.write(pNeutral); // back to neutral
attachInterrupt(0,pushButton,FALLING); // start interrupt handler for switch input

// Interval mode?
if (interval) { doInterval(); }

// Ready to go. Display message, set timer
snapTime = millis();
uView.clear(PAGE);
uView.setFontType(1);
uView.setCursor(9,2);
uView.print("Ready");
uView.line(8,17,54,17);
uView.display(); // display the content in the buffer memory, by default it is the uView logo
wiggleTime = millis() + maxIdleTime;

} // end setup

void loop() {

milliVar = millis();

uView.setFontType(0); // use 1 for 8x16 font, 0 for 5x7
uView.setCursor(9,20);
uView.print("n="); // display count of images taken
uView.print(count);
uView.setCursor(9,30);
uView.print("t="); // display time since last shutter press
uView.print((milliVar-snapTime)/1000);
uView.print(" ");
uView.display(); // display the content in the buffer memory, by default it is the uView logo

if(snap) { // Button interrupt been called?
takePic(); // take photo
count++; // increment counter
snap = false; // reset interrupt
milliVar = millis();
wiggleTime = milliVar + maxIdleTime; // run keepAlive motion again in maxIdleTime millis
}

if(milliVar > wiggleTime) { // half press button every maxIdleTime millis to keep camera awake
delay(50);
myservo.write(pFocus);
delay(500);
myservo.write(pNeutral);
wiggleTime = milliVar + maxIdleTime; // run keepAlive motion again in maxIdleTime millis
}
delay(250); // just to slow the loop down a bit

} // end of main loop

// Interrupt Service Routine for remote shutter button
void pushButton() {
snap = true;
} // end pushbutton

// Move servo to pFocus, delay, then pShutter, delay, then back to pNeutral
void takePic() {
delay(50);
myservo.write(pFocus);
delay(1000);
myservo.write(pShutter);
delay(750);
myservo.write(pNeutral);
snapTime = millis();
} // end takePic

// Move servo around, based on analogue input from pot.
int readServo(){ // read from potentiometer, rotate servo in sync with pot. Return servo angle when button is pressed
int angle;

while(digitalRead(buttonPIN)) {
vr1 = analogRead(potPIN);
angle = map(vr1,0,1023,45,135); // mapping 45 degrees either side of the 90 degree position
myservo.write(angle);
delay(50);
}
return angle;
} // end readServo

// Interval timer mode -- setup, then run infinite loop
void doInterval(){
unsigned long snapTime, timeLeft;
int sInt;

uView.clear(PAGE);
uView.setFontType(0); // use 1 for 8x16 font, 0 for 5x7
uView.setCursor(1,1);
uView.print("Timing");
uView.line(0,9,38,9);
uView.setCursor(1,12);
uView.print("Set interval");
uView.setCursor(1,20);
uView.print("then press");
uView.setCursor(1,28);
uView.print("button.");
uView.display(); // display the content in the buffer memory, by default it is the uView logo

while(digitalRead(buttonPIN)) { // read pot and display delay time, save value when button pressed
vr1 = analogRead(potPIN);
sInt = map(vr1,5,1020,240,5); // mapping interval to between 5 and 240 seconds
uView.setCursor(1,36);
uView.print(sInt);
uView.print("s ");
uView.display(); // display the content in the buffer memory, by default it is the uView logo
delay(75);
}
sInt = sInt * 1000; // convert seconds to millis
delay(1000);
uView.clear(PAGE);
uView.setFontType(0); // use 1 for 8x16 font, 0 for 5x7
uView.setCursor(8,2);
uView.print("Interval");
uView.line(7,11,57,11);
uView.display(); // display the content in the buffer memory, by default it is the uView logo

snapTime = millis() + sInt;
snap = false; // just make sure the interrupt is cleared
// set up complete -- begin interval mode

// start interval photos, never exit this loop
while (true) {
timeLeft = (snapTime - millis())/1000;

if (snap) { // if the button was pressed, and there's time to take a photo...
if (timeLeft > 2) {
takePic();
count++;
}
snap = false;
}

uView.setFontType(0); // use 1 for 8x16 font, 0 for 5x7
uView.setCursor(9,20);
uView.print("n=");
uView.print(count);
uView.setCursor(9,30);
uView.print("t=");
uView.print(timeLeft);
uView.print(" ");
uView.display(); //
if(timeLeft <= 0) {
takePic();
count++;
snapTime = millis() + sInt;
}
delay(300);
}

} // doInterval

Leave a Reply

Your email address will not be published. Required fields are marked *