Introduction: Pimp My Cam
Here's where this project is coming from.
A while back I thought about filming some timelapses. "How?" I asked myself? The first answer was "Well.. you just film something and speed it up and that's it". But is it really that simple? First, I want to use my DSLR for that, and my Nikon D3100 has a 10min time limit for filming video. Second, even if I had a camera with no time limit on filming video, what if I want to make a realllly long timelapse, like 12 hours long? I make a 12 hours long 1080p video. I doubt the battery would last that long and, it's not very practical, is it? Alright, crossing "filming video idea". Well, then there are pictures. Taking a photo on the camera at a certain interval and ending up with hundreds of images which I than process through software to make a video.. ?
Seemed like an okay idea so I decided to give it a shot. So I ended up with wanting to make a device into which I can input a time period, and based on that period it would trigger my camera constantly. And while we're at it, why not add some other stuff like motion-trigger and so on?
Step 1: But.. How?
HOW? is our next question that's missing an answer. Because of timing, triggering, sensors and such things it will be no surprise that the first which came to mind was, of course, an Arduino. Alright, but still, we need to learn how to trigger the shutter on our camera. Hm.. servo hot glued to the body camera? Absolutely not, we want this to be silent and power efficient. Power efficient - why? Because I want to make it portable and stick a battery in it, I wont be near a power plug every time. So how do we trigger it then.. it's actually pretty simple.
Nikon already knew that you are going to want a remote and other accessories and they said "okay, we'll give them all that, but we will make a special port so we can make more money on those accessories", shame on you Nikon. That port is (in my case) called MC-DC2, and the cheapest way to get our hands on it is to buy a remote shutter release on eBay for 2-3$ and just use the cable.
*Some other cameras, like Canon, have a simple 3.5mm headphone jack made for the same use so you can use some cable from old speakers/headphones.
Step 2: Learning How to Trigger the Camera
Anyway, here's the deal, the port will have 3 connections that will be of our interest (Ground, Focus and Shutter) and you will have those on the end of your cable of the newly bought remote shutter that you just destroyed. Those three connections are important to us because if we short the Ground and Focus the camera will focus just like you're pressing the focus button and then, while that connection remains, you can short the Ground and Shutter and the camera will take a picture just as if you pressed the shutter button on the camera.
You can test this out by literally shorting the live wires on the end of the cable to identify which wire is which. Once you done that, for the sake of easier identifying, we'll color them like so:
Ground = BLACK; Focus = WHITE; Shutter = RED.
Alright, now we need to teach the Arduino to do this for us.
Step 3: Ways to Trigger
The simplest thing we can tell an Arduino to send into the outer world is it's digital output signal. This signal can either be HIGH(logical '1') or LOW(logical '0'), hence the name"digital", or when converted into it's core meaning: 5V for a logical HIGH, and 0V for a logical LOW.
What are we to do with this digital signals? We can't just simply connect them to the camera and expect the camera to know what we want. As we've seen, we need to short the connections on the camera in order for it to react, so we need to use the digital signals of the Arduino to drive some components that can short their terminals depending on these electrical signal we send it. *The way I described it, you might be thinking "Ah, Relays!" but no no. Relay would do the job but we are dealing with such small currents that we can easily use the black magic of semiconductors.
First component that I'll try is an optocoupler. I've seen them implemented the most for this and it's probably the best solution. Optocoupler is an electrical component with which you control the output circuit while the input circuit is completely isolated from it. This is achieved by transmitting information by light, input circuit lights up an LED, and the phototransistor on the output switches accordingly.
So we'll use the optocoupler this way: we tell our Arduino to send a digital HIGH on one if it's digital pins, that signal is practically 5V which will drive the LED inside of the optocoupler and the phototransistor inside of it will "short" it's output terminals when it detects that light, and vice versa, it will "detach" it's terminals since there is no light from the LED when we send a digital LOW through the Arduino.
Practically, this means: one of the digital pins of the Arduino is attached to the ANODE pin of the optocoupler, Arduino's GND is attached to the CATHODE, camera's GND is attached to the EMITTER and FOCUS(or SHUTTER) to the COLLECTOR. Refer to the data sheet of the optocoupler you're using to find these pins on yours. I'm using 4N35 so you can follow mine schematic blindly if you don't really care about what happens inside of the optocoupler. Needless to say, we'll need two of these, since we need to control both camera's FOCUS and SHUTTER.
Since we saw how that works, with a phototransistor on the output, why don't we try it solely with a simple NPN transistor. This time, we'll bring the digital signal directly(across a resistor) to the base of the transistor and connect both camera's and Arduino's GND to the emitter and camera's focus/shutter to the collector of the transistor.
Again, we'll need two of these since we're controlling two signals. I'm using the BC547B and you can basically use any NPN for this since the current we're controlling is a single milliamp.
Both of these components will work, but choosing the optocoupler is probably the better idea because it's safer. Choose the transistors only if you know what you're doing.
Step 4: Writing the Code for Triggering
As we said before, we'll use the digital pins of the Arduino for signaling. The Arduino can use these both for reading data from it, or writing to it so the first thing we need to do specify in the setup() function that we'll use two of Arduino's digital pins for the output like so:
pinMode(FOCUS_PIN, OUTPUT); pinMode(SHUTTER_PIN, OUTPUT);
where FOCUS_PIN and SHUTTER_PIN can either be defined with "#define NAME value" or as an int before the setup() function because you might change the pin so it's easier to change the value on just one spot rather than the whole code afterwards.
Next thing we'll do is write a trigger() function which will do just that when it's run. I'll just include a picture with the code. All you need to know is that first we hold the FOCUS_PIN on HIGH for a certain period of time because we need to wait for camera to focus on the subject we're pointing it at and then for just a moment(while FOCUS_PIN is still HIGH) put the SHUTTER_PIN on HIGH just to take the picture.
I also included the ability to skip the focusing because there will be no need for it if we're shooting a timelapse of something that is not changing it's distance from the camera through time.
Step 5: Class Interval{};
Now that we've got triggering the camera out of the way we need to make this into an intervalometer by adding the functionality of manipulating the time period between two shots. Just so you get the picture of what we're doing here's some primitive code to demonstrate the functionality we want:
void loop(){ delay(interval); trigger(); }
I want to be able to change this interval from, let's say, 5 second all the way up to maybe 20-30minutes. And here's the problem, if I want to change it from 5s to 16s or anything in between I'll use 1s increment, where for each of mine request to increase the interval, the interval would increment for 1s. That's great, but what if I want to go from 5s to 5min? It would take me 295 requests to that in 1s increments so I obviously need to increase the increment value to something larger, and I need to define on which exact interval value(threshold) to change the increment. I implemented this:
5s-60s : 1s increment; 60s-300s : 10s increment; 300s-3600s : 60s increment;
but I wrote this class to be adjustable so you can define your own thresholds and increments (everything is commented in the .h file so you can know where to change which values).
The example I've given of manipulating the interval is obviously done on a PC, now we need to move it to the Arduino. This whole class, Interval, is put inside one header file which is used to store declarations and definitions(not really, but it can be done in this example without doing any harm) of our class/functions. To introduce this header file to our arduino code we use the " #include "Interval.h" "(files must be in the same directory), which makes sure that we can use the functions defined in the header file in our main code.
Step 6: Manipulating the Interval Through Arduino
Now we want to be able to change the value of the interval, either increase or decrease it. So that's two things so we'll use two digital signals which will be controlled by two buttons. We'll repeatedly read the values on the digital pins we assigned to the buttons and parse those values to the function checkButtons(int,int); which will increase the interval if the button "up" is pressed and decrease the interval if the button "down". Also, if both buttons are pressed it will change the value of the variable focus which controls whether or not to focus when triggering.
Part of code ((millis() - prevBtnPress) >= debounceTime) is used for debouncing. The way I wrote it, it means that I register the first button press with boolean variable btnPressed and remember the time it happened. Than I wait for a certain amount of time(debounceTime) and if the button is still pressed I react. It also makes a "pause" between every other press of the button so it avoids multiple presses where there are none.
And finally, with:
if ((millis() - prevTrigger) / 1000 >= interval.getVal()) { prevTrigger = millis(); trigger(); }
we firstly check if the amount of time between the last triggering(prevTrigger) and the current time(millis()) (everything is divided by a 1000 because it's in milliseconds and the interval is in seconds) is equal to or greater than the interval we want, and if it is we remember the current time as the last time we triggered the camera and then trigger it.
With this complete, we basically made an intervalometer, but we're far from over. We still don't see the value of the intervalometer. It's only displayed on the Serial Monitor and we wont be near a computer always so now we'll implement something that will show us the interval as we change it.
Step 7: Displaying the Interval
This is where we introduce the display. I used the 4 digit module which is driven by TM1637 because I need to use it only for displaying time and nothing else. The easiest way to use these modules made for an Arduino is to use already made libraries for them. On the Arduino site there's a page describing the TM1673 chip and a link to a suggested library. I downloaded this library and there are two ways you can introduce these libraries to the Arduino IDE:
- from the Arduino software go to Sketch>Include Library>Add .ZIP library and locate the .zip file you just downloaded
- you can do what the Arduino does manually and just unzip the library in the folder in which the Arduino stores libraries, on Windows: C:\Users\Username\Documents\Arduino\libraries\.
Once you've included the library you should read the "ReadMe" file in which you'll find the summary of what the various functions do. Sometimes this isn't enough so you'll want to go a bit deeper and explore the header files in which you can see how the functions are implemented and what they require as input arguments. And of course the best way to get a feel of what a library is capable of it usually offers an example which you can run from the Arduino software through File>Examples>LibraryName>ExampleName. This library offers one example which I recommend you run on your display just to see if your display is working properly and than I encourage you to tweak the code you see in the example and see for yourself what each function does and how the display reacts to it. I've done so and this is what I figured out:
it uses 4 unsigned integers of 8 bits for each digit (0bB7,B6,B5,B4,B3,B2,B1,B0). And each of those bits B6-B0 are used for each segment of a certain digit and if the bit is 1 the segment controlled by it lights up. These integers are stored in an array called data[]. Setting these bits onto the display is accomplished by display.setSegments(data); or you can naturally access any of the digits particularly and set them either manually (data[0] = 0b01111001) or you can use the function encodeDigit(int); and convert the digit you send it into according bits(data[0] = display.encodeDigit(3));. Bit B7 is used only by the second digit, or data[1], for activating the colon.
Since I wrote the functions in the INTERVAL class witch which I can get certain digits of the interval in the form of M1M0:S1S0, where M stands for minutes and S for seconds, it is natural that I use the encodeDigitFunction(int); for displaying the interval like so :
displayInterval(){ data[0] = display.encodeDigit(interval.getM1()); data[1] = 0x80 | display.encodeDigit(interval.getM0()); data[2] = display.encodeDigit(interval.getS1()); data[3] = display.encodeDigit(interval.getS0()); display.setSegments(data); }
Now, any time that I need to display the Interval onto the display, I can call the displayInterval() function.
*Note the "0x80 | ... " on the data[1]. It is used to ensure that the bit B7 of the data[1] is always 1 so the colon lights up.
Last thing about the display, power consumption. It may not be of great importance since we wont keep it on for a long time, but if you're interested in making this even more battery friendly then consider lowering the brightness of the display since it draws 3 times more current on the maximum brightness than on the lowest.
Step 8: Putting It All Together
We know how to trigger the camera, how to manipulate the interval and how to display that same interval onto a display. Now we just need to merge all of these things together. We'll start, of course, from the loop() function. We'll constantly check for button presses and react accordingly with the checkButtons(int,int) and change the interval accordingly and display the changed interval. Also in the loop() we'll be constantly checking if enough time has passed from the last triggering or button press and call the trigger() function if needed. For the sake of lower power consumption we'll turn off the display after some time.
I added a bi-color led, (Red and Green, common cathode) which will light up green while the trigger() and it will light up red along with the display if the focusing is on and it will stay off if the focusing is off.
Also, we'll be migrating to an even smaller Arduino, Pro Mini.
Step 9: Adding One Last Thing
So far.. we've only created an Intervalometer. Useful, but we can do better.
Here's what I had in mind: the Intervalometer does it's thing by default EXCEPT when we attach some kind of external switch/sensor which then stops the intervalometer and responds to the input of the switch/sensor. Let's call it a sensor, it wont necessarily be a sensor that's connected but I'll refer to it as that.
Firstly, how do we detect that we've attached the sensor?
The sensors we'll use/make will all need three wires connecting them to the arduino(Vcc,GND,Signal). That means that we can use a 3.5mm audio jack as an input jack for the sensor. And how does that solve our problem? Well, there are types of a 3.5mm jack "with a switch" which have pins that are shorted to the pins of the connector if there is no male connector in them, and they detach when there's a connector present. That means we have the information based on the presence of the sensor. I'll use the pull-down resistor as shown(the digital pin will read HIGH without the sensor, and LOW with the sensor attached) in the image or you could also attach to the digital pin to the pin of the connector which is normally connected to ground and define that digital pin as INPUT_PULLUP, it will work either way. So now we have to tweak our code so it does all we've written so far only if the sensor is not present, or when the digital pin checking that is HIGH. I also tweaked it so it shows "SENS" on the display instead of the interval which is useless in this mode, but the focusing is still relevant to us we we'll keep the functionality of alternating the focusing with the press of both buttons and showing the focus state through the red led.
What does the sensor actually do?
All it need to do is to put 5V on it's Signal pin when we want to trigger the camera. That means that we'll need another digital pin of the Arduino checking the state of this pin and when it registers HIGH, all it needs to do is call the trigger() function and the camera will snap a picture. The easiest example, and the one we'll use to test if this works, is a simple button with a pull-down resistor. Attach the button between the Vcc of the sensor and the Signal pin and add a resistor between Signal pin and GND, this way the Signal pin will be on GND when the button is not pressed since there is no current flowing through the resistor, and when the button is pressed we put the Signal pin directly on HIGH and the Arduino reads that and triggers the camera.
With this we concluded writing the code.
*I would like to note some problems that I had with the audio jacks I used. While inserting the male jack into the connector, the GND and either one of the other two pins would sometimes short. This happens instantly and only while putting in the connector, but it's still long enough for Arduino to register a short so the Arduino would just restart. This doesn't happen that often but can still be a hazard and there's a potential of destroying the Arduino so avoid the connectors that I used.
Step 10: Containing the Mess
You can see from the images that the breadboard is getting messy and we're done so we need to transfer everything to a perfboard/PCB. I went for PCB because I think I'll be making more of these so this way I can easily reproduce them.
I used Eagle for designing the PCB and found designs for all the parts I used. There is one tiny thing in my design that I wish I hadn't done and that is a wire pad for the Vcc of the display. I've seen it too late and didn't want to ruin what I previously designed and went the lazy way of adding wire pads and later having to add wire to these connections instead of copper traces so mind that if you're using mine design.
The Arduino Board and the display are connected to the PCB through female pin headers rather than being soldered directly onto the PCB, for apparent reasons. This way there is plenty of space for other components under the display for other components such as resistors, transistors and even the audio jack.
I've put the micro push buttons which, by the design, should be soldered directly but you could also use the holes for female pin headers and connect buttons with wire if you want them mounted on the enclosure and not on the PCB.
We'll also put another female audio jack to plug in the cable that connects to the camera. This way the board becomes more versatile since that way we'll be able to connect to other cameras with other connectors.
Step 11: Sens0rs
Let's consider ways to implement the sensor.
So the sensor will have the supply voltage of 5V, and it will need to be able to provide a digital HIGH on it's signal pin when we want to trigger the camera. First thing that came to my mind is a motion sensor, PIR to be specific. There are modules sold for Arduino which have this sensor on them and do just what we want. They are powered on 5V and have a output pin on which they put 5V when they're triggered, we just need to connect it's pins to a 3.5mm audio jack and we can plug right into the board. One thing to note though is that this sensor need time to heat up and start working properly so don't expect it to work properly as soon as you plug it in, give it some time and then set it up and whatever alive walks into its range will trigger the camera.
Since we're thinking in the direction of already made Arduino sensor boards another one comes to mind, sound. These boards are usually made in such way that they have one pin that outputs analog value of the sound it picks up and another one, digital one, that outputs a logical HIGH if the sound it picks up crosses a certain level. We can set this level such that the sensor ignores our voice but registers a clap. That way, any time you clap, you trigger the camera.
Step 12: PoweeEeEer
I think that the easiest way to power this thing is with a power bank, and not externally. We'll keep the functionality of charging our phone or whatever and control the current flow to the board through a switch. We'll locate the pins of the output USB connector on the circuit board in the power bank which are GND and Vcc(5V) and Solder wires directly onto them and from there into our board.
Step 13: Enclosure.. Kinda
I really struggled with this. When I bough the box I wanted to put the existing PCB in, I realized that there's no nice way to fit everything as I wanted and then I decided to design a new PCB, this time with optocouplers. I wanted to place the PCB right beneath the side on which I would drill holes for certain components that need to be seen/touched. For this to work I would need to solder the display and Arduino directly to the board, without sockets or headers, and that's where the first problem lies. It was absolutely horrible to troubleshoot anything since I wasn't ready to solder it on right away until I test out that everything is working, and I couldn't really test anything out since I couldn't solder it and so on.. don't do this. Problem numero dos, making holes on the case. I guess I took measurements wrong because none of the holes on the case were aligned with the components on the PCB and I had to enlarge them and the buttons were too high on the PCB and they would always be pressed when I put the board in place aaand since I wanted the audio jacks on the side, I had to enlarge those holes too to fit the jacks first and then lower the board for the display and buttons to come through.. the outcome is terrible.
I kinda made the terrible holes less terrible by overlaying the top with some thin cardboard in which I cut out more reasonable holes for the components and.. it's still terrible but easier on the eye I think.
Verdict, I suggest you do this by buying components that mount to the enclosure, and not directly onto the PCB. That way you have more freedom in placement of the components and less places to make mistakes on.
Step 14: Fin
I'm done, but here are some thing that I would have done differently:
Use better quality 3.5mm audio jacks. The ones I used tend to short the terminals while inserting or pulling out the jack which results in either shorting the supply thus reseting the Arduino or it just produces fake triggers. I've said this in the previous step but I'll say it again.. don't solder the Arduino board without headers/socket, it just makes any kind of troubleshooting or uploading new code and so on much harder. I also think that having an led signaling that the thing is on would have been useful because I often can't tell without pressing the button since the display turns off. And the last thing, a pause function. I imagine it being useful when for example when plugging in the PIR sensor so because it needs time to heat up, or just when moving it around you don't want it to trigger so you could just pause everything, but you can also simply turn off the camera so.. whatever.
Another neat thing is to Velcro it on the tripod since it's most likely to be used on there.
Feel free to ask anything about this project in the comments and I would love to know if you build it and how it turned out for you.