///////////////////////////////////////////////////////////////// // Time-lapse shutter release control based on an Arduino Nano // ///////////////////////////////////////////////////////////////// // A program/sketch to control the shutter of a Nikon DSLR to create a time-lapse video. // // The program is controlled through the Arduino serial monitor. // The shutter release rate can be adjusted during the filming to adapt to varying // degrees of action taking place. // // // Written in 2013 by Per Magnusson, Axotron, http://axotron.se/index_en.php // v 0.2 // // // (c) 2013, Per Magnusson, Axotron, http://axotron.se/index_en.php // // License: // I make this code available in the hope that it might be educational and/or // inspirational. Feel free to use it, change it, include it in your own commercial // or non-commercial projects as you see fit. You do not have to attribute me, but // you are welcome to do so if you like. // I give no guarantee that it will be good for anything. // Below is some legalese I copied from a version of the BSD license: // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Hardware description const int focusPin = 2; const int shutterPin = 3; const int lampPin = 4; // Constants const unsigned int lampOnTime = 10; // Lamp on time after shutter release in ms const unsigned long initialTimeLapseInterval = 500; // In ms const unsigned int timeLapseDelta = 0; // Lengthening of interval for each frame, ms. // Set timeLapseDelta to 0 for constant interval. // Global variables int incomingByte; byte timeLapseMode = 0; unsigned long timeLapseInterval; unsigned long nextTime; unsigned long photoCounter = 0; void setup() { // initialize serial communication: Serial.begin(9600); // initialize pins pinMode(focusPin, OUTPUT); pinMode(shutterPin, OUTPUT); pinMode(lampPin, OUTPUT); digitalWrite(focusPin, LOW); digitalWrite(shutterPin, LOW); digitalWrite(lampPin, LOW); // Initialize variables timeLapseInterval = initialTimeLapseInterval; printHelp(); } void loop() { static unsigned long startTime = 0; // see if there is an incoming command on the serial port if (Serial.available() > 0) { // read the oldest byte in the serial buffer: incomingByte = Serial.read(); Serial.write(incomingByte); if (incomingByte == 'p') { // Take a single picture expose(); Serial.println("!"); } else if (incomingByte == 'L') { // Lamp on digitalWrite(lampPin, HIGH); Serial.println("!"); } else if (incomingByte == 'l') { // Lamp off digitalWrite(lampPin, LOW); Serial.println("!"); } else if (incomingByte == 'F') { // Start time lapse Serial.println("!"); Serial.print("Entering time lapse mode with interval "); Serial.print(timeLapseInterval); Serial.println(" ms."); timeLapseMode = 1; photoCounter = 0; startTime = millis(); nextTime = startTime; } else if (incomingByte == 'f') { // End time lapse Serial.println("!"); Serial.println("Exiting time lapse mode."); Serial.print(photoCounter); Serial.print(" pictures taken in "); Serial.print((millis()-startTime)/1000); Serial.println(" seconds."); timeLapseMode = 0; } else if (incomingByte == 's') { // Slower (longer interval) nextTime += timeLapseInterval>>2; // Make this longer time effective immediately timeLapseInterval += timeLapseInterval>>2; Serial.println("!"); Serial.print("New rate: "); Serial.print(timeLapseInterval); Serial.println(" ms."); } else if (incomingByte == 'q') { // Quicker (shorter interval) if(timeLapseInterval > 350) { nextTime -= timeLapseInterval>>2; // Make this shorter time effective immediately timeLapseInterval -= timeLapseInterval>>2; Serial.println("!"); Serial.print("New rate: "); Serial.print(timeLapseInterval); Serial.println(" ms."); } else { // We cannot go this fast Serial.println("?"); Serial.println("Already at maximum rate."); } } else { // Unknown command, print help // First empty the serial buffer to not print the help message many times if // there are more characters coming at once. delay(5); // Wait to see if there are more crap characters coming while (Serial.available() > 0) { while (Serial.available() > 0) { // Discard all characters that are coming in this burst in case // there is a lot of crap coming Serial.read(); } // an extra delay and check again for more characters delay(5); // Wait to see if there are more crap characters coming } printHelp(); } } // Handle the time lapse if(timeLapseMode) { if(millis() >= nextTime) { expose(); timeLapseInterval += timeLapseDelta; nextTime += timeLapseInterval; photoCounter += 1; Serial.print(photoCounter); Serial.print(" pictures taken in "); Serial.print((millis()-startTime)/1000); Serial.print(" seconds. Current interval is "); Serial.print(timeLapseInterval); Serial.println(" ms."); } } } void expose() { digitalWrite(lampPin, HIGH); delay(1); digitalWrite(focusPin, HIGH); delay(300); // Seems like this has to be around 300 or more to make the D300 happy digitalWrite(shutterPin, HIGH); delay(50); digitalWrite(shutterPin, LOW); digitalWrite(focusPin, LOW); delay(lampOnTime); // The exposure time might be long, make sure this is longer digitalWrite(lampPin, LOW); } void printHelp() { Serial.println("\nCommands:"); Serial.println("p - take a picture"); Serial.println("L - turn lamp on"); Serial.println("l - turn lamp off"); Serial.print("F - start time lapse mode with "); Serial.print(initialTimeLapseInterval); Serial.println(" ms initial interval."); Serial.println("f - end time lapse mode"); Serial.println("s - take pictures slower (increase period by ~25%)"); Serial.println("q - take pictures quicker (decrease period by ~25%)"); }