Optical slave trigger is the easiest way of getting the flash off camera without wires. However, most of the optical slave triggers are quite dumb. They trigger the flash as soon as a light pulse is detected. This works fine if all of your flashes are in manual mode. If you want to use a dumb flash in TTL flash system, or wireless flash system such as Nikon’s Advance Wireless Lighting (AWL), you will run into trouble. The flash will be triggered prematurely when the TTL metering pulse or wireless communication pulses are emitted.

There are some smarter flash triggers that can work around the problem. Here are some examples I found.

All of the above is based on the assumption that the flash synchronization (or main flash) pulse is the last one. This is true in most cases.  The problem is uncertain number of pulses before the last one. If the camera emits fixed number of pulses before the main flash, it is easier to handle. But for the complicated system such as Nikon’s Advanced Wireless Lighting system, the light pulses can have virtually unlimited variations. I have previous post a series article on the communication details of the Nikon system. Interested readers may refer to the 1st article and all the articles in the Related Posts section.

After studying the details, I discovered some characteristic patterns in the pulses and came up with a optical slash flash trigger that can work in Nikon AWL setup as well as normal TTL flash setup. The trigger is built upon the Arduino prototyping platform with some limited and inexpensive parts. Most of the trick is done by the software.

The hardware

The following is the schematic.

A photodiode (BPW34) connected in photo-conductive mode is used to detect the flash pulses. The signal is filtered through a RC network (C1, R2) to remove the ambient light component. The signal goes to an analog pin on the Arduino Duemilanove board. Built-in analog-to-digital conversion (ADC) unit converts the analog signal to digital value representing the signal intensity. A custom software code analyze the pulses and determine when to trigger the flash.

S1 is a simple button switch for test firing the flash. S2 is a 2-bit DIP switch for configuration purposes. The first bit controls the trigger mode. The default is the “smart” mode. The other mode is the dumb mode. The second bit controls the sensitivity of the trigger. Only two sensitivity levels are available: low (default) and high.

An optocoupler (MOC3041M) is used to trigger the flash. Sync voltage lower than 400V is safe based on the datasheet. Other variants of MOC30XX may also work. I simply used the parts I already have.

The software

The software is the heart and soul of the smart trigger, and it is not overly complicated. No attempt was made to fully decode the pulse communication of Nikon’s AWL although it may be possible. So how exactly does the software figure out when to trigger the attached flash?

The communication protocol of Nikon’s AWL is quite compact. It sends out unmodulated pulses  that are normally ~70 microsecond long. The pulses follow simple binary format except for the channel indicator bits. Between groups of pulses, there are delays of various lengths. It turns out that the longest delay between the pulse groups is before the final sync pulse or main flush. That’s when the camera raises the mirror and fully opens the shutter. So if a pulse comes after a specially long delay time (more than ~40ms) of a previous pulse, it is most likely the sync or final flash pulse. There are exceptions but it can be handled by examining the characteristics (number of pulses) of the previous pulse groups preceding a pulse.

For anyone who is interested in testing it out, here is the code for Arduino IDE. Arduino source code

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

// Arduino pins
#define PHOTODIODE 0
#define MODE 3
#define SENSITIVITY 4
#define XSYNC 7
#define BUTTON 8

// 4 microsecond precision timing using 16MHz Arduino
extern volatile unsigned long timer0_overflow_count;
// For timeing measurements
volatile unsigned long prev_pulse, time_passed;    

int ar0;           // Photodiode input
int threshold;     // Pulse height threshold. DIP #2 off: 10, on: 5
int mode;          // DIP #1 off: 1st pulse, on: first pulse.
int pulse_count;   // Pulse count
int prev_pulse_count;   // Pulses in previous pulse groups

// Each tick is 4 microsecond
unsigned long hpticks (void)
{
  return (timer0_overflow_count < < 8) + TCNT0;
}

// Fire the flash with a 15 microsecond pulse on the xsync terminal
void fire() {
  digitalWrite(XSYNC, HIGH);  
  delayMicroseconds(15);
  digitalWrite(XSYNC, LOW);  
}

void dip_config(int info) {
  // DIP switch #1
  if(digitalRead(MODE) == HIGH)
    mode = 1; // Switch Off: smart mode
  else
    mode  = 0; // Switch On: dumb mode

  // DIP switch #2
  if(digitalRead(SENSITIVITY) == HIGH)
    threshold = 50; // Switch off: low sensitivity
  else
    threshold = 15; // Switch on: high sensitivity

  if(info == 1) {
    Serial.print("Mode: ");
    Serial.print(mode);
    Serial.print(", Sensitivity: ");
    Serial.println(threshold);  
  }
}

void setup() {
  int start;
  Serial.begin(57600) ;

  // Use internal reference voltage of 1.2v for ADC
  analogReference(INTERNAL);

  // Set prescale to 8 for much faster analog read.
  // 16MHz/8 = 2MHz ADC clock speed. Each ADC conversion takes 13 cycles.
  // So 2MHz/13 = 154KHz sampling rate.
  // With other overhead considered, each analogRead takes ~10 microseconds.
  cbi(ADCSRA,ADPS2) ;
  sbi(ADCSRA,ADPS1) ;
  sbi(ADCSRA,ADPS0) ;

  pinMode(PHOTODIODE, INPUT);
  pinMode(MODE, INPUT);
  digitalWrite(MODE, HIGH); // Use the internal pull-up register
  pinMode(SENSITIVITY, INPUT);
  digitalWrite(SENSITIVITY, HIGH); // Use the internal pull-up register
  pinMode(XSYNC, OUTPUT);    
  pinMode(BUTTON, INPUT);

  // I don't why. The first analog read always return 1023 so a dummy read is added
  analogRead(PHOTODIODE);
  ar0 = analogRead(PHOTODIODE);

  dip_config(1);
  Serial.println("Trigger Ready.");  
}

void loop() {
  int new_ar, delta;
  unsigned long now;

  new_ar = analogRead(PHOTODIODE);
  delta = new_ar - ar0;
 
  now = hpticks();
  time_passed = now - prev_pulse;

  // Pulses come in bundles. The minimal seperation seems to be around 4000 microseconds
  if(pulse_count > 0 && time_passed > 1000) {
    prev_pulse_count = pulse_count;
    pulse_count = 0;
  }

  // Reset if > 125000*4 = 500ms.
  if (time_passed > 125000) {
    pulse_count = 0;
    prev_pulse_count = 0;
    prev_pulse = now;
    dip_config(0); // Check the DIP switches. No serial xfer to save time.
  }

  if(delta > threshold) {
    if(mode == 0) { // Dumb mode. Fires upon seeing any light pulse
      fire();
    }
    else { // Smart mode
      // If the delay since the previous pulse is longer than 10000*4 microseconds (40ms)...
      // Checking the previous pulse group is needed to avoid mis-fire.
      if((time_passed > 10000) && (prev_pulse_count == 1 || prev_pulse_count > 5)) {      
          // Fire the flash now!
          fire();
          // Reset the pulse counter
          pulse_count = 0;
          prev_pulse_count = 0;
        }
        else {
          // Get to the end of the pulse. Some pulses are specially wide.
          while(analogRead(PHOTODIODE) > threshold);
          // Increase the pulse count
          pulse_count++;
          // Keep track of the time a pulse is detected
          prev_pulse = now;              
        }
    }
  }
  else {
    // Background. It should be zero most of the time.
    ar0 = new_ar;
  }

  // Read the button status.
  if (digitalRead(BUTTON) == HIGH) {
    fire();
    pulse_count = 0;
    prev_pulse_count = 0;    
    dip_config(1);
    delay(300); // Avoid button contact problem so delay 300ms
  }
}

Sensitivity and distance

The sensitivity is quite good thanks to the large sensing area of the photodiode. Since my current setup is still on a messy breadboard, I was not able to test the trigger distance. But it doesn’t require line-of-sight to work. Reflected flashes from walls can trigger it from at least 15 ft away.

Limitations

It works in TTL mode and in wireless mode (only tested with Nikon D200). Front curtain sync seems to work in all cases but rear curtain only works when the shutter speed is faster than 1/2 second. This limitation needs a complex fix to overcome. It will probably be included in future releases.

Future plans

I will be refining the software for the near term to make it work in all situations, hopefully. Another plan is to design a circuit with minimal number of parts. Using the Arduino board is ok for prototyping purposes but it is a overkill for the application. I welcome any feedbacks, suggestions, and feature requests.

Update

Check out the part II. I have put it on a perfboard so I can use the Arduino Duemilanove board for something else. New schematics is available. Code is unchanged.


Related Posts

Comments

  • Todd Holbrook

    I’d love to see a DIY version of the Radiopopper PX system for Nikon CLS – it reads the optical pulses, changes them to radio signals, then back again at the flash. Seems like you’re at least partway there with the CLS analysis and optical triggering. Sadly, outside of what I know how to design myself at this point.

    • http://dptnt.com/ picmax

      This is doable and I think I can get there. I have all the necessary parts, including a pair of RF modules. I just need time.

      Max

  • martin

    Oh, those constant afterwords about Arduino being overkill and all that… This is boring. ;-)

    I’m interested in the X-Sync/Optocoupler part of your project. I would like to do things triggered by sound (having the sound detection part mastered to a certain degree already), but I’m reluctant because I don’t have a flash head available for this. Can any flash unit be triggered by simply grounding the appropriate contact via the optocoupler? Do you know if this is true for studio flash heads as well? And what about the voltage? Several hundred V for those speedlites only, or for every flash head? I found it somewhat hard to retrieve usable information about this from the interwebs. Maybe you can share an insight or two with me. :-)

  • Andy

    Martin:

    Nearly every flash out there is triggered the same way – by grounding the trigger pin. Most studio flashes only have the trigger signal (also called the “sync” signal), all control is done by manually setting the flash power.

    Newer flashes have extra pins that allow for control of the flash by the camera, but many have manual modes where only the sync/trigger pin needs to be grounded.

    On hotshoe flashes, the trigger pin is always the center one.

    Studio flashes often have sync inputs that are 1/8″ mono jacks (ground and trigger) or “PC” (long name: Prontur/Compur) sync inputs.

    Several hundred volts is worst-case, typical for older flashes – Most of these had voltage equal to the capacitor voltage present on their trigger pins. Many newer flashes (designed for DSLRs) have far lower voltages present on their trigger pin. (Anything above 6v or so will fry modern Canon cameras for example.)

    The circuit above should be able to trigger any flash regardless of trigger voltage.

    • Edwin

      Great Circuit. The approach with the software as I understand is to basically wait till there is no more flash for 40 mS, presume that is the last flash and thus fire.

      Good approach

      Some of the other slaves (with a PIC)  are based on a ‘learning’ mode, in which a testflash has to be fired once, to tell the unit what flash is being used.
      Also has its advantages. I may try this one coz the Arduino platform is quite easy

      • http://dptnt.com picmax

        If there is no flash pulses for 40ms, the next one must be the trigger flash. It will fire immediately after it sees the trigger flash, not 40ms later.

        I doubt learning mode would work for Nikon’s complicated TTL wireless flash signaling system. If it works, it will have to learn every time the scene changes.

  • Todd Holbrook

    Martin: Check out the Camera Axe system. I bought a kit and it works great for both laser triggering and sound triggering. http://www.glacialwanderer.com/hobbyrobotics/?p=167

  • martin

    Thanks for your responses, guys.

    I have already built this nice doomsday box http://is.gd/9Aalf for intervalometer triggering of the shutter, but found that shutter response is far too slow to adequately react to acoustic events. Unfortunately, I have never worked with flash beyond sliding a system flash onto my camera, hence the beginner questions.

  • Scott

    Martin, seems the code section you have above has some errors. There are numerous escape characters that probably got inserted through a conversion. Line 30 is particularly bad.

    I’m trying to unravel it but I’m sure it would be good for others to have it fixed up above.

    Thanks for sharing this!

    • http://dptnt.com/ picmax

      Sorry about the code mess. Please check again, I think it is now fixed. I also included the code in a zip file.

      Max

      • Scott

        Looks a lot better! Thanks very much!

  • kkjensen

    Anyone have a programmer for these chips? I’ve only used PICs in the past but would love to pick up a couple pre-programmed chips for testing and playing with!

    Don’t let this idea die! Great news!

    Next project: a DIY radio bridge (been dreaming about doing this on my own for a while now…)

    • http://dptnt.com/ picmax

      You can buy the chip from SparkFun with the Arduino bootloader. With that, you can plug it in an Arduino board and program it that way.

      Or you can buy a programmer from eBay for about $30.

      Max

  • Mario

    Thanks for sharing your project! I would like to trigger my old sunpak gx14 whose sync voltage is 160V. My knowledge about electronics is very poor, but thanks to your post, I managed to turn on a LED when the master flash light is detected (you can see a video here: http://www.youtube.com/watch?v=ejTUEC7APSc). I still have problems in order to activate the slave flash: I bought an optocoupler similar to the one you have used (MOC3041). I wired the central pin of the PC-sync connector with pin 6 and the outer ring of PC-sync connector with pin 4 of the optocoupler. I can measure a voltage of 160 V between pin 4 and 6 but when call fire() method, nothing happens. Are you shure that optocoupler’s output to be used are 4 and 6? What about pin 5? Is the delay amount (duration of the high control signal) critical for the proper functioning?
    Thank in advance.

  • Mj

    I’d connected a optical slave trigger SHINE trough the sync chord to a national PE-201m flash
    and i used it with pop-up flash ,for the first time i think it had worked,but now,it does not work any more,
    every time i knock the sync cable ,it will fire the old flash,but with pop-up it doesn’t wok,what’s the problem?
    Is it because of low volume of light or Some thing thing else?..
    please help!

    • Edwin

      could you be more specific?  What do you mean with ‘knock the sync cable?
      If ‘knocking’ means that you tap it, it probably means there is  some corruption in yr cable:
      1 time-> everything worked, so yr circuit is/was ok
      pop up does not fire it -> either the light level (but was previously OK)  or the circuit (previously ok) or yr cable (also previously ok)
      Knocking yr cable -> flash: Probably a loose contact with some motiondependent  shorting of the wires