A Little Off Code, Computers, Photography and Guns

20Feb/099

High(ish)-speed Photography (followup)

DSC_3299

So this Tuesday I got my Arduino in the mail. I really mean just in the mail... it was in an anti-static bag in a bubble-wrap envelope! Anyway I managed to waste about 6 hours screwing around with it the first night I got it. Since then after a lot of researching, googling, swearing and scratching my head I've successfully setup the first major part for the camera trigger project.

I discovered that the delay functions built into the Arduino C library are mostly useless. The microsecond delay function is most useless of all because it will only produce accurate delays between 4 microseconds and 16383 microseconds. So I gave up on that and just wrote a custom delay function. But before I wrote the custom delay function I looked at creating my own timer using Timer2 and giving up digital pins 11 and 3 for accurate times. Eventually I ditched this method because I never got any of the sample code to work at all.

Eventually I arrived at this at least reliable if not accurate delay function:

1
2
3
4
void customDelay(unsigned long time) {
    unsigned long end_time = micros() + time;
    while(micros() < end_time);
}

The advantage to this over the delayMicroseconds() function is that this will at least pay attention to how long it's actually been since it started where the delayMicroseconds() function just blindly sleeps for an arbitrary amount of time and wakes up when it sees fit. The function for oscillating the IR led at the frequency I needed for triggering the camera is of the same form just with a bit of code for switching the LED on and off twice every clock cycle in the while loop. It also turns out that changing the state of a digital pin isn't near instantaneous either, which is to be expected. I've compensated for this using a constant defined for the number of microseconds it takes for it to turn a digital pin on or off.

Once I solved the timing issues it was essentially about cleaning up the code from that point on. I ended up storing the sequence of oscillation and pausing that Nikon SLR's use in two arrays, one for oscillation and one for the matching pause between each oscillation period. Which makes for a clean section for firing the led and pausing all inside a for loop that iterates over each item in the two arrays.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/*
Author:         BeMasher
Description:    Code sample in C for firing the IR sequence that mimics the
                ML-L1 or ML-L3 IR remote control for most Nikon SLR's.

Based off of:   http://make.refractal.org/?p=3
                http://www.cibomahto.com/2008/10/october-thing-a-day-day-7-nikon-camera-intervalometer-part-1/
                http://ilpleut.be/doku.php/code:nikonremote:start
                http://www.bigmike.it/ircontrol/

Notes:          This differs slightly from the other 3 versions I found in that this doesn't use the built in
                delay functions that the Arduino comes with. I discovered that they weren't accurate enough for
                the values I was trying to give them. The delayMicrosecond() function is only accurate between about
                4uS and 16383uS which isn't a very workable range for the values we need to delay in for this project.
                The ASM code that Matt wrote works well but is limited to only pin 12 and I haven't got a good enough
                grasp of the architecture to modify the code to work on any pin. So this is what I've come up with to
                produce the same result.
*/


#define IR_LED 13        //Pin the IR LED is on
#define DELAY 13         //Half of the clock cycle of a 38.4Khz signal
#define DELAY_OFFSET 4   //The amount of time the micros() function takes to return a value
#define SEQ_LEN 4        //The number of long's in the sequence

unsigned long seq_on[] = {2000, 390, 410, 400};        //Period in uS the LED should oscillate
unsigned long seq_off[] = {27830, 1580, 3580, 0};      //Period in uS that should be delayed between pulses

void setup() {
    Serial.begin(19200);        //Initialize Serial at 19200 baud
    pinMode(IR_LED, OUTPUT);    //Set the IR_LED pin to output
}

void customDelay(unsigned long time) {
    unsigned long end_time = micros() + time;    //Calculate when the function should return to it's caller
    while(micros() < end_time);                  //Do nothing 'till we get to the end time
}

void oscillationWrite(int pin, int time) {
    unsigned long end_time = micros() + time;    //Calculate when function should return to it's caller
    while(micros() < end_time) {                 //Until we get to the end time oscillate the LED at 38.4Khz
        digitalWrite(pin, HIGH);
        customDelay(DELAY);
        digitalWrite(pin, LOW);
        customDelay(DELAY - DELAY_OFFSET);        //Assume micros() takes about 4uS to return a value
    }
}

void triggerCamera() {
    for(int i = 0; i < SEQ_LEN; i++) {            //For each long in the sequence
        oscillationWrite(IR_LED, seq_on[i]);      //Oscillate for the current long's value in uS
        customDelay(seq_off[i]);                  //Delay for the current long's value in uS
    }
    customDelay(63200);                            //Wait about 63mS before repeating the sequence
    for(int i = 0; i < SEQ_LEN; i++) {
        oscillationWrite(IR_LED, seq_on[i]);
        customDelay(seq_off[i]);
    }
}

void loop() {
    if(Serial.available()) {        //Wait 'till something is connected
        if(Serial.read() != 0) {    //If anything but 0 is sent take a photo
            triggerCamera();        //Take a photo
        }
        delay(100);                 //Delay an arbitrary amount of time, serial isn't instantaneous
    }
}
Comments (9) Trackbacks (1)
  1. Thanks for posting your code. I’m a Flash programmer and designer who’s trying to make the leap into physical computing.. so a lot of this stuff is very new to me. My goal is to use your code as a jumping off point to make a custom remote for my Robosapien.. It worked flawlessly with my nikon d40x :-)

    I’ve got the IR codes for the robosapien, but they are listed in hexadecimals. Any idea how to convert numbers like: 583, 555, 55C, and 5f1 so they’re usable with your code? Thanks in advance for any help you can offer.

    -Ben

    • It looks like this site has the information you need about your robosapien. The way i think the codes work is that each hexadecimal value corresponds to low and high oscillation of the IR LED based on each digit of the value in binary.

      So 0x3C81 = 0b11110010000001 where each 1 corresponds to 1 period of 400ms of oscillation and each 0 corresponds to 400ms of logic low, or something to that effect. I think i’m going to try modifying my code for this.

  2. Thanks for the quick reply. I’m going to give this a try today.. I’ll let you know how things go!

  3. I have tried to use your code to fire my Nikon D60 but with no luck as yet I am using a standard ATMEGA 328. I can see that the IR led is firing because pin 13 fires and I have the remote selected on the camera. Any thoughts on this? I am sure that any problem is with me and not your code.

  4. It’s possible that the particular LED you’re using isn’t far enough into infrared wavelength for the camera to be sensitive to it.

  5. Does anyone know what wavelength of IR the Nikon DXX cameras are designed to be sensitive to? 800nm or 900 nm?

    FYI. For you guys playing with IR LEDs. Here is a trick I learned. This is a easy way of seeing if IR LEDs are working. Pull out your video camera. Aim the video camera at your IR LED and the camera sees the IR light coming out of the it and you will see it on the camera display screen.

    Otherwise you can wire up an IR phototransistor circuit to drive a visible LED to check to make sure it is working. Much more fun to see it on your camcorder display screen though.

    Good Luck.

  6. The IR led I’ve been using is near visible so it’s got a very faint red glow which helps me to figure out if it’s working or not. Because of this I’ve got the distinct feeling that the range of IR light the camera’s remote sensor is sensitive to is very broad.

  7. Also worked great with my D40X, thanks a bunch. I modified the code to add an LCD screen, and adjustable minute, second timer as well as the total number of pictures taken.

  8. Perfect with Arduino Mega / Nikon D80.
    Thanks for sharing the code.


Leave a comment