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