You are on page 1of 24

1

An experiment to measure the shutter


lag of a Canon Digital Single Lens Reflex
(DSLR) camera using an Arduino
microcontroller
Bill Brandt BSc CEng MIET MINCOSE
October 2014
Abstract
A simple low-cost circuit was constructed to measure the shutter lag of a Canon DSLR using an
Arduino microcontroller and some LEDs. Analysis of the circuit concluded that it was suitable for
measuring to a precision of 1ms and an accuracy of 250s and was capable of measuring to 100s
precision with about 50s accuracy at the expense of limited range, although lack of calibrated test
equipment meant that this could not be independently verified.
The circuit was used to measure the shutter lag of different Canon DSLRs and found the shutter lag
of a Canon EOS 400D to be 117ms 3ms and the shutter lag of a Canon EOS 700D to be 97ms 7ms.

2


Contents
Abstract ................................................................................................................................................ 1
Contents ............................................................................................................................................... 2
Introduction .......................................................................................................................................... 4
Aims .................................................................................................................................................. 4
Other measurement methods .......................................................................................................... 4
Method ................................................................................................................................................. 5
Display Method ................................................................................................................................. 5
Electronic Circuit ............................................................................................................................... 6
Physical circuit .............................................................................................................................. 6
Schematic ...................................................................................................................................... 7
Output LEDS .................................................................................................................................. 8
Trigger Circuit ................................................................................................................................ 9
Costs ............................................................................................................................................. 9
Alternative Implementations ........................................................................................................ 9
Control Software ............................................................................................................................. 10
High level software design .......................................................................................................... 10
Code ............................................................................................................................................ 10
Alternative Implementations ...................................................................................................... 12
Testing ............................................................................................................................................ 12
Timing ......................................................................................................................................... 12
Sanity check ................................................................................................................................ 12
Execution time ............................................................................................................................ 13
Camera setup .................................................................................................................................. 13
Flash synchronisation speed ....................................................................................................... 13
Taking a reading .............................................................................................................................. 14
Post-Processing ............................................................................................................................... 14
Results ................................................................................................................................................ 14
Accuracy.......................................................................................................................................... 16
Further work ....................................................................................................................................... 17
Improvements to timer ................................................................................................................... 17
Increased precision ..................................................................................................................... 17
3

Increased accuracy ...................................................................................................................... 17
Decreased number of LEDs ......................................................................................................... 18
Other timing triggers ................................................................................................................... 18
Areas for further study ................................................................................................................... 18
Trigger pulse ............................................................................................................................... 18
Variability .................................................................................................................................... 18
700D two spike distribution ........................................................................................................ 18
Shutter speed inaccuracy ............................................................................................................ 18
References .......................................................................................................................................... 18
Appendix 1 Measurement data ....................................................................................................... 19
Appendix 2 Second implementation, 100s precision ..................................................................... 19
High level software design .......................................................................................................... 19
Timeline ...................................................................................................................................... 19
Code execution time and optimisation ....................................................................................... 20
Code ............................................................................................................................................ 21
Experimental observations ......................................................................................................... 23
Appendix 3 - Binary methods .............................................................................................................. 24


4


Introduction
The time delay between pressing the camera shutter button and the camera recording a digital
image is referred to as the shutter lag. It is important to know this value for a particular camera for
applications where the precise timing of the image capture is important.
One such application is in stereo image recording where it is important to synchronise two or more
cameras to capture images at precisely the same time since the stereo effect relies on the
differences between the images and any difference caused by subject movement will cause errors in
the stereo effect.
Different makes and models of cameras have different shutter lag values and there is variation in the
shutter lag between successive shots from the same camera.
The experiment below provides a method for accurately measuring shutter lag.
Aims
The primary aim of the experiment was to create a method to accurately measure shutter lag for a
Canon DSLR camera. In principle the same method, with minor modifications, could be used for any
DSLR or indeed any camera with a way to remotely activate the shutter button.
The primary success criterion was to achieve a precision of 1ms with an accuracy of 500s.
The secondary goal was to study whether the method was suitable for adaptation to higher
precision and accuracy.
The third goal was to develop electronic circuits and software that could be reused to study other
aspects of camera timing and to provide a method for compensating for variations in timing. To this
end this document points out where adaptations could be made for other applications.
Other measurement methods
Historically the most popular method for measuring shutter lag used a CRT monitor and relied on
capturing the position of the electron beam scanning the screen to provide an accurate timing
reference [Reference 1]. This method is now becoming less and less convenient because of the
obsolescence of CRT monitors.
A modern LCD monitor displaying a software controlled clock can be used as a rough indication of
timing but because the refresh rate of most monitors is only 50Hz or 60Hz this limits the precision to
20ms or 16.7ms, plus there may be accuracy issues due to the interaction between the computer,
graphics card and monitor that may be hard to control.
Mechanical methods such as photographing rapidly spinning discs, swinging pendulums, falling
objects etc. suffer from being physical awkward to set-up and potentially hard to calibrate.
Therefore the method chosen in this experiment was the use of LEDs controlled by an Arduino
microcontroller. This method was preferred because of the low cost of Arduino microcontrollers and
the ease with which the software control allows reconfiguration of the timing.
5

Initially an approach using a binary display pattern was attempted but this was later abandoned for
the reasons given in Appendix 3 - Binary methods.
Method
Display Method
The basic method used was a pseudo-analogue representation of time. This was achieved by having
a bank of 10 LEDs each illuminated in sequence with a wrap-around from the last LED to the first. By
seeing which LED was illuminated at any given time and knowing how fast the sequence repeated,
the time could be calculated. Three banks of LEDs were used with the sequence in each bank taking
ten times longer to repeat than the bank to its right. This is illustrated in Figure 1.

Figure 1 Display method
This method was used to minimise the risk of errors caused by LEDs being captured in a transitional
state (partially illuminated), since it is easy to infer the true value from this type of display. This is not
true with binary or 7-segment displays where it can be much harder to infer what value is being
displayed if several LEDs are in transition at once
The LEDs in each section represented the numbers 0 to 9 from left to right. The rightmost set of
LEDs appeared in two rows due to a limitation on the size of the prototype breadboard used.
The value represented by each LED was determined by the code being run. In this experiment the
code was configured to give a range of 000 to 999ms with 1ms precision. A secondary experiment
configured the code to give a range of 00.0 to 99.9ms with 100s precision and this is described in
Appendix 2 Second implementation, 100s precision.
An example display is shown below in Figure 2

Figure 2 example display pattern (123ms)
6

In Figure 2 the LED representing 1 in the hundreds of milliseconds and the LED representing 2 in
the tens of milliseconds are fully illuminated. Within the LEDs representing units of milliseconds the
LEDs representing 2and 3 are partially illuminated. This is because during the camera exposure
time the sequence moved between those LEDs. The reading is made from the LED at the front of the
sequence, taking account of wrapping, and so the reading above corresponds to 123ms. If the code
is changed so that each LED represents a shorter time period then one would expect to catch more
LEDs in transition. It is always the LED at the front of the sequence (accounting for wrapping) that is
used as the reading.
A simple sanity check of the LEDs can be made since LEDs in a more significant bank should only be
partially illuminated if the LEDs in a less significant bank have wrapped as in Figure 3.

Figure 3 example display pattern (120ms)
In Figure 3 the LED representing 1 in the hundreds of milliseconds is fully illuminated. Within the
LEDs representing tens of milliseconds the LEDs representing 1and 2 are partially illuminated and
within the LEDs representing units of milliseconds the LEDs representing 0 and 9 are illuminated.
The reading is made from the LED at the front of the sequence, taking account of wrapping, and so
the reading above corresponds to 120ms.
Electronic Circuit
The microcontroller used was an Arduino Mega. With minor modifications cheaper alternatives
could be made, see Alternative Implementations.
The circuit was built using prototyping breadboard to allow rapid reconfiguration. This was found to
have sufficient reliability that a printed circuit board was not made.
Note that the Blinkenlight shield [Reference 2] is an off the shelf circuit that could be used to
handle the display circuitry. This has a limitation of 20 LEDs.
Physical circuit
The physical circuit used is shown in Figure 4.
7


Figure 4 - Arduino based circuit
Schematic
The logical circuit schematic is shown in Figure 5.
Each Arduino output pin was connected through a 220 resistor to the anode of the relevant output
LED with the cathode of that LED connected directly to ground. Since the output voltage from the
Arduino is approximately 5V this gives an output current of approximately 20mA.
8


Figure 5 - circuit schematic
Output LEDS
30 LEDs were used which allowed the representation of shutter lag from 000 to 999ms with a
precision of 1ms. All output LEDs were kept in a single line so that they could be recorded
9

simultaneously, see Flash synchronisation speed for explanation, but this was probably not strictly
necessary at this level of precision and other display configurations might be more convenient.
Different colours of LEDs were used for each digit to enable an easy way of working out which LED
was illuminated on the resultant photographs.
A further four LEDs were permanently illuminated and marked the 0 and 5 positions of the two
rightmost sets of LEDs to further help in identifying which LED was illuminated on the resultant
photograph although these were not strictly necessary.
It should be noted that there is a commercially available shield for the Arduino called the
Blinkenshield [Reference 2] which has 20 LEDs on a circuit board and this could be used instead of
the entire circuit described so far if the limited display and display configuration is acceptable.
Trigger Circuit
The right hand side of the circuit contained a small circuit to trigger the camera shutter. The remote
shutter socket on most Canon DSLRs is a 2.5mm stereo jack. Shorting the middle section to the body
enables the autofocus and shorting the tip to the body fires the shutter. Higher end Canon DSLRs use
a custom socket for the remote shutter but it contains the same three wires so a simple converter
could be made.
This circuit used an optoisolator to protect the cameras circuitry. One output pin from the Arduino
was used as the input to the optoisolator and the output of the optoisolator was connected to the
body and tip of a 2.5mm stereo jack inserted into the cameras remote shutter socket.
This trigger circuit may need to be adapted slightly for other camera makes and models.
Costs
The total component cost for the circuit above, based on an Arduino Mega, was in the order of 15
(approx. $25) as parts were sourced from low cost suppliers.
For those looking for a ready-made solution purchase of an Arduino Uno and Blinkenlight shield
would be in the order of 30 (approx. $45) although a trigger circuit would still need to be created.
Alternative Implementations
The Arduino Mega is one of the more expensive Arduino versions costing in the region of 10 ($16).
Cheaper Arduino versions are available principally the Arduino Uno at a cost of 7 ($11) and the
Arduino Nano at a cost of 3 ($5).
The main constraint is the number of digital output pins available which limits the number of LEDs
that can be driven. If less pins are available then either precision can be sacrificed by making each
LED represent a larger period of time, or alternatively a two stage measurement could be made by
making a rough cut at low precision and then reconfiguring the program to make a fine cut at a
higher precision but assuming that the measured value lies within a given range.
For example if only ten pins and thus ten LEDs were available the experiment could be run first with
each LED representing 10ms. If the experiment showed that the majority of results had the 80ms
LED illuminated. Then the program could be reconfigured so that the 10 LEDs represented 80ms,
81ms 89ms with some error pattern displayed if the result moved out of that range.
10

Obviously this is not ideal so it is recommended that at least an Arduino Uno with 20 digital pins is
used. This has the advantage that it interfaces with the blinkenlight shield which has 20 LEDs making
an almost off the shelf solution.
Control Software
High level software design
The three banks of LEDs are each modelled in software as an array with ten elements and these
arrays are initialised with the pin numbers of the Arduino pins used to control the LEDs.
Arduino code consists of two main functions. The Setup function is run once when the Arduino
system is first powered on or rebooted. The main execution loop function runs continuously in a
loop from then on.
During setup the LED controlling pins were set to output and the function fireShutter was called.
The fireShutter function saved the current value of the real time clock in milliseconds and applied a
pulse to the camera to activate the shutter. The duration of the pulse was configurable by setting
the variable triggerDuration to the wanted value in milliseconds. The recommended value is 20. It
was found that if a shorter pulse was used then the camera did not always fire reliably. This is not
expected behaviour since one would imagine that the camera should detect the edge of the pulse
and not care about the duration and so further investigation is needed in this area, see Trigger pulse.
Note: There is anecdotal evidence of misfires when using remote shutter activations. It is
possible that this may be caused by too short a trigger pulse time since even with a
mechanical push button the pulse time is determined by how quickly the operator releases
the button. It should be noted that during this experiment, using a 20ms pulse time, there
were no misfires recorded in 240 shots.
During each iteration of the main execution loop the real time clock was read and compared with
the value from the previous iteration to calculate if the clock had changed, in other words if one
millisecond had passed, since the previous iteration. If it had then the elapsed time since the shutter
was fired was calculated and converted into the appropriate indices for the LED arrays used to
display the time.
Because this conversion used division and mod operations it took about 220s to execute and meant
that the displayed time was always about 220s behind the actual time. However this was
considered acceptable and the code was written in this way for simplicity. For a more precise and
accurate method see Appendix 2 Second implementation, 100s precision.
Finally the program illuminated those LEDs that had changed, extinguishing the previous LED at the
same time.
In order to automate the taking of many readings the program was designed to detect when the
timer had reached 900ms, wait for a configurable period of time and then fire the shutter again.
This code could easily be commented out if single measurements were needed.
Code

11

/* Shutter lag timer

This sketch fires a camera shutter via the remote shutter socket and then starts
displaying the time since the start of the trigger signal to 1 millisecond resolution.
The trigger duration is configurable. The display of time is via the position of the
LED that is illuminated.

10 LEDs are used to show hundreds of milliseconds
10 LEDs are used to show tens of milliseconds
10 LEDs are used to show units of milliseconds
this gives a display range of 000 to 999 milliseconds

An optional routine in the code will cause the camera to automatically fire the shutter at
configurable interval after the clock reaches 900ms to allow the recording of many
measurements.

The circuit:
- LEDs attached to ground and via 220R resistor to digital pins for time display
- A 4N35 optoisolator triggered by a 150R resistor to pin1, pin2 to ground, camera shutter
between pins 4 and 5 to fire the camera shutter

*/

// set pin numbers:
int hundredMillisecondPins[10] = {
22,23,24,25,26,27,28,29,30,31}; // pins for LEDs representing 100ms
int tenMillisecondPins[10]={
32,33,34,35,36,37,38,39,40,41}; // pins for LEDs representing 10ms
int oneMillisecondPins[10] = {
42,43,44,45,46,47,48,49,50,51}; // pins for representing 1ms
int hundreds = 0; // index into 100ms array
int tens = 0; // index into 10ms array
int ones = 0; // index into 1ms array
int previousHundreds = 0;
int previousTens = 0;
int previousOnes = 0;
int i; // general purpose index for initialisation

const int test = 52; // the test pin for execution time measurement
const int shutter = 53; // the shutter output pin

// clock
int triggerDuration = 20; // How long a trigger pulse in milliseconds needed for
camera to detect reliably
int repeatInterval = 3000; // number of milliseconds between successive shots in
automatic mode
unsigned long time = 0; // this will hold the time in microseconds since the
program start
unsigned long previousTime = 0; // this holds the time from the previous iteration
unsigned long triggerTime = 0; // this holds the time in microseconds when the triger
signal is first sent
unsigned long elapsedTime = 0; // this holds the time in microseconds from when the
trigger signal was sent

void setup()
{
// set the digital pin modes:
for (i=0; i<10; i++)
{
pinMode(hundredMillisecondPins[i], OUTPUT);
pinMode(tenMillisecondPins[i], OUTPUT);
pinMode(oneMillisecondPins[i], OUTPUT);
}
pinMode(shutter, OUTPUT);
pinMode(test, OUTPUT);

fireShutter(); // fire the shutter once
}

void fireShutter()
{
// Fire the camera shutter once
triggerTime = millis(); // record time since program start in milliseconds
digitalWrite(shutter, HIGH);
delay(triggerDuration); // set the duration to be what is necessary for reliable detection
digitalWrite(shutter, LOW); // stop camera firing multiple shots
previousTime = triggerTime; // initialise previousTime
12

}

void loop()
{
// this loop displays the time since the start of the trigger signal to 1ms precision
// digitalWrite(test, HIGH); // uncomment to test loop execution time
time = millis(); // record time since program start in milliseconds
if (time != previousTime) // if milliseconds have changed since last time measurement
{
previousTime = time; // reset previous time
elapsedTime = time - triggerTime; // contains number of milliseconds since start of
trigger
ones = int(elapsedTime % 10); // calculate milliseconds part of elapsed time
tens = int((elapsedTime / 10) % 10); // calculate tens of milliseconds
hundreds = int((elapsedTime / 100) % 10); // calculate hundreds of milliseconds
if (ones != previousOnes) // if ones have changed
{
digitalWrite(oneMillisecondPins[ones], HIGH); // illuminate 1ms LED
digitalWrite(oneMillisecondPins[previousOnes], LOW); // extinguish previous 1ms LED
previousOnes = ones;
}
if (tens != previousTens) // if tens have changed
{
digitalWrite(tenMillisecondPins[tens], HIGH); // illuminate 10ms LED
digitalWrite(tenMillisecondPins[previousTens], LOW); // extinguish previous 10ms LED
previousTens = tens;
}
if (hundreds != previousHundreds) // if hundreds have changed
{
digitalWrite(hundredMillisecondPins[hundreds], HIGH); // illuminate 100ms LED
digitalWrite(hundredMillisecondPins[previousHundreds], LOW); // extinguish previous
100ms LED
previousHundreds = hundreds;
}
// The following five lines of code can be commented out for manual operation
if (hundreds == 9) // if 900ms have passed since start of program
{
delay(repeatInterval); // wait for a configured amount of milliseconds
fireShutter(); // trigger the shutter again
}
}
// digitalWrite(test, LOW); // uncomment to test execution time
}
Alternative Implementations
A second code implementation is shown in Appendix 2 Second implementation, 100s precision.
Testing
Timing
One channel of an uncalibrated two channel oscilloscope was attached to each of the 1ms output
pins and used to verify that the pin was high for 1ms and low for 9ms. The second channel was
attached to an adjacent pin to check that the pins were synchronised so that as the preceding pin
went low the next pin in the sequence went high. This was repeated for all 1ms pins and a sample
check then done on the other pins.
Sanity check
Some simple sanity testing was conducted by changing the calculations from elapsed time to array
indices to increase the divisors by a factor of 100 which had the effect of slowing the program down
by a factor of 100 and making the LEDs represent tens of seconds, seconds and tenths of seconds so
that the correct sequence could be verified manually. The value of triggerDuration was also set to
several seconds to check that the time displayed included the trigger time.
13

Execution time
An additional output pin was used for testing purposes and set to go HIGH and LOW at various
points in the program. This pin was then monitored with an uncalibrated oscilloscope. In this way it
was possible to measure the execution time of parts of the code to verify that the code was
executing fast enough to prevent errors. The introduction of code to manipulate the test output pin
would of course vary the execution time to some small degree but the results showed enough safety
margin that this was not an issue.
The execution time of the main loop if the millisecond clock had incremented was found to be about
220s. If the clock had not incremented it was about 15 s.
Camera setup
The cameras used were a Canon EOS 400D and a Canon EOS 700D. The camera was set to ISO1600
with a manual exposure of 1/4000s and F/2.8. These values were chosen as they represented the
fastest shutter speed available on the camera and gave a good exposure on the resulting images.
If other cameras are used the settings may need to be adjusted somewhat to suit the capabilities of
the camera, but in general the fastest shutter speed should be used with the widest aperture on the
lens and then whatever ISO setting is needed to get a good exposure. Image quality is not critical, all
that is required is to determine whether an LED is illuminated or not.
Note that a shutter speed of 1/4000s is equivalent to 250s.
The camera was set to manual focus and the image quality set down to the smallest file-size
possible, a small coarse jpeg. This was to minimise the amount of time taken writing data to the
memory card and also to decrease the time spent opening the images for analysis. Image quality is
not important as long as it is clear whether an LED is illuminated or not.
With the camera in manual exposure and manual focus the variability in shutter lag should be
minimised although there may be other factors contributing to variability that need to be
investigated, see Variability.
For cameras with a fold-out display, such as the 700D, the display was left in a visible state. This
was to ensure a direct like-for-like comparison with other cameras with a fixed display. It is not
known whether writing to the camera display could contribute to variability in shutter lag or not but
for the purpose of this experiment this variable was held constant.
Note that mirror lock-up was not used in this experiment. Although this would remove a mechanical
operation that could be a factor in variability it was felt to be unrepresentative of normal use.
Flash synchronisation speed
The flash synchronisation speed of most DSLRs is around 1/200s which is 5000s. At shutter speeds
in excess of the flash synchronisation speed the entire sensor is not uncovered at any one time but is
exposed as a slit between two curtains that travel parallel to the long edge of the sensor.
Thus in order to capture the whole display pattern at the same instant in time the shot must be
composed so that the strip of LEDs is parallel to the long edge of the frame.
14

If a camera other than a Canon DSLR is used then the shutter design should be checked and this part
amended accordingly.
Note that even with the strip of LEDs parallel to the sensor, since the shutter curtains take 5000s to
traverse the distance represented by the short edge of the sensor there may still be many
microseconds between the camera exposing the top edge of the LED and the bottom edge of the
same LED and this puts a practical limit on the precision of this method which is independent of the
shutter speed.
Taking a reading
In automatic mode the program takes a reading at a period determined by the variable
repeatInterval . Alternatively this code could be commented out and a reading taken by pressing
the reset button on the Arduino, once for each reading.
The interval between readings was set to ensure that any card-writing operations had finished
before the next shot was taken since otherwise these might contribute to variability. This was
double-checked by observing the card-writing LED on the back of the camera and ensuring that it
had ceased activity prior to taking the next shot.
Post-Processing
The resultant images were viewed in an image editing program and the shutter lag determined by
reading the pattern.
If Adobe Photoshop is available then use of the File -> Scripts -> Load files into stack can be used to
quickly load all images into a single Photoshop file separated as layers. This then makes it very quick
to spot any variation by simply viewing each layer in turn and observing if the pattern changes. This
is one reason why it is advisable to take the lowest resolution image possible since it then allows
many images to be loaded at once without consuming large amounts of computer memory.
A text layer may also be added at the top of the stack to label each LED if there is any doubt as to
which LED is which.
Results
The results of 121 readings taken on a Canon EOS 400D are shown in Figure 6 Canon EOS 400D
results histogram.
15


Figure 6 Canon EOS 400D results histogram
From this it seems fairly clear that the normal shutter lag is in the range 115 to 121ms. However
5% of the measurements lie outside of this range and it requires further investigation as to why
there is such variation.
The full measurement data is shown in
Appendix 1 Measurement data.
The same experiment repeated on a Canon EOS 700D gave a mean shutter lag of 97 ms and a
variation of 7 ms. However the results exhibited a curious two-spike distribution pattern of 93ms
3ms and 103ms 1ms which requires further investigation, see 700D two spike distribution.
0
5
10
15
20
25
30
35
40
115 116 117 118 119 120 121 122 123 124 125 126 More
F
r
e
q
u
e
n
c
y

Shutter lag in ms
Histogram
16


Figure 7 Canon EOS 700D results histogram
Accuracy
No calibrated test equipment was available to objectively test the accuracy of the measurements so
it is not possible to state the accuracy of the system. However a subjective assessment is as follows.
The accuracy of the timer depends mostly upon the accuracy of the real time clock in the Arduino
system. It seems reasonable to expect that such a clock would be accurate to within a few seconds a
day and thus over the small measurement periods of this experiment the error in the clock itself
would be negligible.
There will be some error caused by the delay between reading the Arduino real time clock and
outputting the resultant pattern on the LEDs. This delay should remain fairly constant and is mainly
due to the conversion from elapsed time to the array indices, but there will be some small variation
caused by the if statements in the code and potentially some variability in the time taken to
execute the in-built functions.
Taking all the factors above into account would suggest that the absolute accuracy of the LEDs is
within about 250s and the relative accuracy between two readings is within about 25s.
The goal of this experiment was to achieve a precision of one millisecond and this has been met. For
the purposes of synchronising two cameras the relative measurements between two cameras is
more important than absolute accuracy so it is believed that the accuracy is sufficient for the
primary purpose.
17

Further work
Improvements to timer
The code used has been made simple to aid understanding and to limit the possibility of error since
calibrated test equipment was not available. It is believed that the basic techniques could easily be
expanded to produce more precision and more accuracy if needed. A number of future
improvements could be made as below:
Increased precision
A second implementation that displays to 100s precision and 50s accuracy is described in
Appendix 2 Second implementation, 100s precision.
This could be expanded further and more LEDs could be added to show higher precision. For
instance another 10 LEDs representing 10 s could be added.
The main restriction on this is that the main execution loop needs to run fast enough to execute in
less than the period represented by each new LED otherwise there would be some gaps in the
display sequence. This would require some more efficient code which would run the risk of
introducing errors which might be hard to find without the use of calibrated test equipment.
Note that once the time that an LED represents becomes less than the shutter speed of the camera
there will be a greater probability of the LED switching on or off during the exposure . So for instance
if a shutter speed of 1/4000s was used then LEDs representing 10s would switch on and off 2.5
times each within the camera exposure. This would have the effect of making all the 10s LEDs
appear to be more or less the same brightness.
One way to overcome this would be to have more LEDs at the lower precisions, so that the total
time represented by the strip of LEDs is more than the camera exposure time, so for instance if there
were a strip of 100 LEDs each representing 10us, so that the total time represented by the strip was
thus 1000s then it would be possible to detect what time was represented. If this were done some
sort of multiplexing would be needed as there would not be enough output pins to control each LED
separately.
In this situation, if each LED is only illuminated for the time it represents, for example 10s, the
apparent brightness may be very low since it would only be illuminated for 1/25 of the exposure
(assuming a 250s exposure). To overcome this each LED could be left illuminated for longer, say
50s.
It should be noted that the Blinkenlight website [http://blog.blinkenlight.net] has a number of
resources to do with fast counting and LED display and this could prove valuable if a more precise
timer were required.
Increased accuracy
The main cause of inaccuracy is the fixed delay between sampling the real time clock and converting
that to the array indices. This could be solved by pre-calculating the values for the next update
immediately after the current update has been displayed, since when displaying at 1ms precision the
program spends nearly all its time just waiting for the real time clock to advance by one millisecond.
18

If this were done it is believed that the delay between sample and display could be limited to about
10s, and the accuracy would then only be determined by the accuracy of the Arduino clock.
Decreased number of LEDs
If an implementation using less LEDs were needed then a binary clock is much more efficient. So for
instance eight LEDs could cover a range of 0-255ms to 1ms precision or ten LEDs could cover the
range 0-99ms at 100s precision. This runs the risk that LEDs caught in transition are more difficult
to detect but using Gray codes rather than binary codes would minimise this risk. Alternatively a
hybrid system of using binary for the milliseconds and analogue LEDs for the microseconds could
cover both a large range and high precision with a small number of LEDs.
Other timing triggers
It might be possible to create a circuit to directly measure the shutter lag without taking a
photograph. For instance monitoring the flash output pin on the camera hot shoe (or PC socket), or
potentially using a sound activated trigger to detect movement of the shutter curtains.
These methods would have the benefit that measurements could be totally automated and thus
potentially a very large statistical sample could be made.
The disadvantage is that they are only indirectly measuring the shutter lag and unless the entire
system is understood they might lead to a false conclusion.
Areas for further study
Trigger pulse
Experiment suggested that the trigger pulse needed to be 20ms long for reliable triggering. Does this
imply some polling of the input and if so could this contribute to variability?
Variability
With the camera in manual focus and manual exposure what factors contribute to variability in the
shutter lag? Is it all mechanical or could it be caused by scheduling within the camera software.
700D two spike distribution
The 700D distribution showed two spikes. What could cause a distribution like this?
Shutter speed inaccuracy
When measuring at 100s precision, up to 6 LEDs were illuminated simultaneously. This would
imply a shutter speed closer to 1/2000s rather than the 1/4000s set on the camera. Is the shutter
speed inaccurate or are there other explanations?
References
Reference 1 http://www.3dtv.at/knowhow/Synctest_en.aspx
Reference 2 http://blog.blinkenlight.net
Reference 3 http://www.doc-diy.net/photo/shutter_lag


19

Appendix 1 Measurement data
400d.xlsx
700d.xlsx

Appendix 2 Second implementation, 100s precision
High level software design
The main problem to be solved when working at higher precision is to ensure that the main
execution loop can be completed in all cases in less than the time represented by the least
significant LED, in this case 100s.
Most of the code can run this fast but the conversion from elapsed time to array indices takes about
250s due the divide and mod operations. To overcome this, the conversion is only done once in
the initialiseTimer function and from then on the entire code works only on the difference between
one iteration and the next.
The initialiseTimer function reads the real time clock and calculates the elapsed time since the clock
was saved in the fireShutter function. It then converts this elapsed time into the appropriate indices
for the LED arrays to display the time.
During each iteration of the main execution loop the real time clock is read and compared with the
value from the previous iteration to calculate the time difference from the previous iteration. This is
then used to calculate how much to increment each of the LED array indices.
The code first checks to see if adding the time difference to the previous microsecond value has
caused this to go over 1000 and if so calculates the new values for the milliseconds. Note that if the
microseconds has increased to over 2000 this would not initially be detected but as long as the
execution time of the loop is significantly less than this it will quickly correct.
Timeline
The time line involved is shown in Figure 8 which is not drawn to scale
20


Figure 8 code timeline
When the program starts the real time clock is at 0. The first reading is made during the fireShutter
function and is called triggerTime. The second reading is made during the initialiseTimer function
and is called previousTime. The difference between these two readings is calculated as
elapsedTime and will be at least as long as the trigger duration, probably about 20ms. The next
reading is made during the first iteration of the loop and is called time and the difference between
this and the previous reading is the timeDifference which will probably be around 250s. Time is
then set as previousTime for the next iteration and a new reading of time is made in the next
iteration. Each iteration calculates its own timeDifference which is in the order of 30s for all
iterations except the first.
Code execution time and optimisation
The code has been kept quite simple in order to ease understanding and to try and minimise the
possibility of errors. However because of the requirement to keep the execution time of the main
loop to below 100s some optimisation has had to be made.
The simplest and least error-prone way to display the time would be to simply sample the real time
clock in the main execution loop, convert it to the array indices and then illuminate the LEDs
according to those values. This has the advantage that a fresh calculation is made every time in the
loop which prevents any errors building up.
Unfortunately the division by 10 and mod by 10 instructions needed are very expensive in processor
time, especially as the microsecond clock must be declared as a long integer. Thus the current code
only converts the real time to array indices once in the initialiseTimer routine and from then on
simply works on the difference from the previous iteration.
This has more potential for error and can potentially allow errors to build up over time but was
necessary in order to bring the execution time below 100s.
21

Potentially some more efficient code could be written but again this is likely to introduce more
possibility of error.
In general accuracy has been considered more important than precision in this experiment.
Code
/* Shutter lag timer

This sketch fires a camera shutter via the remote shutter socket and then starts
displaying the time since the start of the trigger signal to 100 microsecond resolution.
The trigger duration is configurable. The display of time is via the position of the
LED that is illuminated.

10 LEDs are used to show tens of milliseconds
10 LEDs are used to show units of milliseconds
10 LEDs are used to show 100 microseconds each
this gives a display range of 00.0 to 99.9 milliseconds

The code used in the initialiseTimer function is quite processor intensive taking about 200us
to execute so it is only called once. From then on the program works on calculating only the
difference in each iteration which is much less intensive taking only about 25us

The circuit:
- LEDs attached to ground and via 220R resistor to digital pins for time display
- A 4N35 optoisolator triggered by a 150R resistor to pin1, pin2 to ground, camera shutter
between pins 4 and 5 to fire the camera shutter

Testing:
By replacing the 'micros()' function calls with 'millis()' function calls the clock will run
1000
times slower allowing the LED patterns to be manually checked. The trigger duration should
be
set to 1000 times its normal value to check that part at the same time.
During testing the two lines of code writing to the test pin can be uncommented in order to
measure the execution time of the main program loop.

*/

// set pin numbers:
int tenMillisecondPins[10] = {
22,23,24,25,26,27,28,29,30,31}; // pins for LEDs representing 10ms
int oneMillisecondPins[10]={
32,33,34,35,36,37,38,39,40,41}; // pins for LEDs representing 1ms
int oneHundredMicrosecondPins[10] = {
42,43,44,45,46,47,48,49,50,51}; // pins for representing 100us
int tens = 0; // index into 10ms array
int units = 0; // index into 1ms array
int oneHundredMicroseconds = 0; // index into 100us array
int microseconds = 0; // value of microseconds
int previousTens = 0; // value of tens from previous iteration
int previousUnits = 0; // value of units from previous iteration
int previousOneHundredMicroseconds = 0; // value of 100us from previous iteration
int previousMicroseconds = 0; // value of microseconds from previous iteration
int timeDifference = 0; // time difference between successive iterations
int i; // general purpose index for initialisation
const int test = 52; // the test pin for loop execution time
const int shutter = 53; // the shutter output pin

// clock
int triggerDuration = 20; // How long a trigger pulse in milliseconds needed for
camera to detect reliably
unsigned long time = 0; // this will hold the time in microseconds since the
program start
unsigned long previousTime = 0; // this will hold the time in microseconds since the
program start from last iteration
unsigned long triggerTime = 0; // this holds the time in microseconds when the triger
signal is first sent
unsigned long elapsedTime = 0; // this holds the time in microseconds from when the
trigger signal was sent

void setup()
{
// set the digital pin modes:
for (i=0; i<10; i++)
22

{
pinMode(tenMillisecondPins[i], OUTPUT);
pinMode(oneMillisecondPins[i], OUTPUT);
pinMode(oneHundredMicrosecondPins[i], OUTPUT);
}
pinMode(shutter, OUTPUT);
pinMode(test, OUTPUT);

fireShutter(); // fire the shutter once
initialiseTimer(); // initialise the timer
}

void fireShutter()
{
// Fire the camera shutter once
triggerTime = micros(); // note the time for start of the trigger pulse
// triggerTime = millis(); // uncomment this line and comment line above for test
digitalWrite(shutter, HIGH);
delay(triggerDuration); // set the duration to be what is necessary for reliable detection
digitalWrite(shutter, LOW); // stop camera firing multiple shots
}
void initialiseTimer()
{
// This routine preloads the 'previous' variables ready for the first iteration of the main
loop
previousTime = micros(); // get microseconds since program began
// previousTime = millis(); // uncomment this line and comment line above for test
elapsedTime = previousTime - triggerTime; // calculate microseconds since trigger signal
previousMicroseconds = int(elapsedTime % 1000); // calculate microseconds part of elapsed
time
previousUnits = int((elapsedTime / 1000) % 10); // calculate units of milliseconds
previousTens = int((elapsedTime / 10000) % 10); // calculate tens of milliseconds
}
void loop()
{
// this loop displays the time since the start of the trigger signal to 100us precision
// to avoid a lot of processor intensive divide and mod operations on variables declared as
// long, it works using the time difference since last iteration, or for the first iteration
// the time difference since the initialiseTimer routine
digitalWrite(test, HIGH); // uncomment this line to test loop execution time
time = micros(); // get microseconds since program began
// time = millis(); // uncomment this line and comment line above for test
timeDifference = int(time - previousTime); // contains number of microseconds since last
iteration
previousTime = time; // save time for next iteration
microseconds = previousMicroseconds + timeDifference; // contains new microseconds
units = previousUnits; // assume units has not changed
tens = previousTens; // assume tens has not changed
if (microseconds > 999)
{
microseconds = microseconds - 1000;
units = units + 1;
}
if (units > 9)
{
units = units - 10;
tens = tens + 1;
}
if (tens >9)
{
tens = tens - 10; // the timer wraps at 99ms so no other action needed
}
// in almost all cases microseconds will now contain a value less than 1000 since the main
execution loop
// only takes about 18us to 36us to execute. The first iteration could potentially still
have a value over 1000
// due to the time taken in the initialiseTimer routine but this will get caught over the
next iterations
// until microseconds is below 1000.

// if LEDs have changed since last time illuminate LEDs to show current elapsed time
// and extinguish LEDs for previous elapsed time

if (units != previousUnits)
{
digitalWrite(oneMillisecondPins[units], HIGH); // illuminate 1ms LED
digitalWrite(oneMillisecondPins[previousUnits], LOW); // extinguish previous 1ms LED
23

previousUnits = units;
}
if (tens != previousTens)
{
digitalWrite(tenMillisecondPins[tens], HIGH); // illuminate 10ms LED
digitalWrite(tenMillisecondPins[previousTens], LOW); // extinguish previous 10ms LED
previousTens = tens;
}
// microseconds will always have changed so no need to do a test
// The microsecond LEDs represent 100us so need to convert from microseconds to array index
// We want to do this without a divide or mod instruction to save execution time
if (microseconds > 899) // note this value could be over 1000 still
{
oneHundredMicroseconds = 9;
}
else if (microseconds > 799)
{
oneHundredMicroseconds = 8;
}
else if (microseconds > 699)
{
oneHundredMicroseconds = 7;
}
else if (microseconds > 599)
{
oneHundredMicroseconds = 6;
}
else if (microseconds > 499)
{
oneHundredMicroseconds = 5;
}
else if (microseconds > 399)
{
oneHundredMicroseconds = 4;
}
else if (microseconds > 299)
{
oneHundredMicroseconds = 3;
}
else if (microseconds > 199)
{
oneHundredMicroseconds = 2;
}
else if (microseconds > 99)
{
oneHundredMicroseconds = 1;
}
else
{
oneHundredMicroseconds = 0;
}
if (oneHundredMicroseconds != previousOneHundredMicroseconds)
{
digitalWrite(oneHundredMicrosecondPins[oneHundredMicroseconds], HIGH); // illuminate 100us
LED
digitalWrite(oneHundredMicrosecondPins[previousOneHundredMicroseconds], LOW); //
extinguish previous 100us LED
previousOneHundredMicroseconds = oneHundredMicroseconds;
}
// save values for next iteration
previousMicroseconds = microseconds;
previousUnits = units;
previousTens = tens;
digitalWrite(test, LOW); // uncomment this line to test loop execution time
}

Experimental observations
It was noted that in the majority of photographs taken using this method, six or seven of the 100s
LEDs were illuminated. This was not expected as theoretically only three or four should be
illuminated in a 250s exposure. This requires more investigation, see Shutter speed inaccuracy.
24

Appendix 3 - Binary methods
The initial approach that was taken was to output a binary pattern on the LEDs in a manner similar
to that described in [Reference 3]. This was because it allows a very economical use of LEDs, only
eight LEDs are needed to represent times from 0 to 255ms at 1ms precision.
However this was found to be unsatisfactory because of the possibility of LEDs changing state during
the camera exposure. If the fastest switching LED changes at an interval of 1ms and the camera
exposure is 1/4000s then it can be predicted that the LED will be captured in a transitional state 50%
of the time as shown in Figure 9 LED transition during exposure.

Figure 9 LED transition during exposure
Any exposure which is started up to 250s before the pattern changes or finishes up to 250 s after
the pattern changes will capture some LEDs in a transitional state. This will manifest itself as the LED
looking less bright than LEDs that have not changed state but it would be hard for a human to detect
the differences in most cases.
So for instance if a 1/4000s (250s) exposure was started at 31.875ms it would end at 32.125ms.
This would capture the transition between the pattern 00011111 and 00100000 and would be seen
on the exposure as 00xxxxxx where the x represents an LED at half brightness. This could easily be
misinterpreted as 00111111 which is 63 in decimal. Thus the true reading of 31 to 32ms could be
misinterpreted as 63ms.
For this reason the approach described in the rest of this document was used.

You might also like