Open Source Optical Flash Trigger That Works with Any Camera

Dmitri Popov
6 min readJul 11, 2016

--

Normally, you can’t use a camera without a hot shoe with an external flash unit. But a relatively simple Photon-based device can solve this problem. Many external flash units (including older ones) can be triggered by shorting (i.e., closing the circuit) between its two terminals. If you happen to have a flash with a PC Sync terminal, it most likely can be triggered by shorting the connector’s pins.

Using a Photon board for this purpose is trivial, but the device must trigger the flash at the exact moment when the camera fires. One way to do this is to build an optical slave that reacts when it detects the burst of light from the camera’s built-in flash. This is not a new idea: the same principle is used in various Arduino-based triggers for photographing lightning. Only in this case, the camera’s built-in flash replaces the lightning.

Build the Optical Flash Trigger

For this project, you need the following components:

  • Photon Particle board
  • MOC3021 optoisolator
  • Light-dependent resistor (also called photoresistor)
  • 1KOhm resistor
  • Breadboard
  • Jumper wires
  • microUSB cable
  • External power source (e.g., external rechargable battery with a USB port)
  • PC Sync connector (for use with a flash unit with a PC Sync terminal)

Start with wiring the Photon board, LDR, resistor, and optoisolator as shown on the diagram below.

Connect the flash pin wires to the PC Sync connector or directly to the flash terminals. The result should look something like this:

Next, you need to write a program (or app in Photon’s parlance) that does all the behind-the-scenes work. Make sure that your Photon board is set up properly and connected to the Internet. Open the Build IDE, create a new app, and enter the following code into it:

const int statusPin = D7; // status LED pin
const int flashPin = D1; // flash pin
const int ldrPin = A0; // select the input pin for the LDR
const int pwrPin = A3; // power pin
int sensorValue = 0; // variable to store the value coming from the LDR
void setup()
{
pinMode(ldrPin,INPUT);
pinMode(statusPin, OUTPUT);
pinMode(flashPin, OUTPUT);
pinMode(pwrPin, OUTPUT);
}
void loop()
{
digitalWrite(pwrPin, HIGH);
ldrValue = analogRead(sensorPin); // read the value from the LDR
if(ldrValue > 950){ // you might need to adjust the default 950 value for optimal results
digitalWrite(statusPin, HIGH);
digitalWrite(flashPin, HIGH);
delay(10);
} else {
digitalWrite(statusPin, LOW);
digitalWrite(flashPin, LOW);
}
}

The program reads the current value of the LDR, and if this value is higher than the specified threshold, the program writes the HIGH value to the statusPin and flashPin pins. This turns on Photon’s on-board LED (which indicates that the threshold is exceeded) and makes the optoisolator close the circuit, thus triggering the flash.

Control the Optical Flash Trigger Threshold

The exact threshold value depends on a number of factors, such as the current lighting conditions, the power of the camera’s built-in flash, and the distance between the camera and the external flash. This means that you might need to experiment with different threshold values and change them on the fly to achieve the optimal result. But modifying the value in the program manually and pushing an updated version to the Photon board every time is not very practical. Fortunately, Photon is backed by the Particle cloud platform with its own RESTful API. In practical terms this means that it’s possible to control the Photon board over the Internet using relatively simple requests.

To be able to change the threshold value, you need to define a function for that and slightly modify the program:

const int statusPin = D7; // status LED pin
const int flashPin = D1; // flash pin
const int ldrPin = A0; // select the input pin for the LDR
const int pwrPin = A3; // power pin
int ldrValue = 0; // variable to store the value coming from the LDR
int ldrThreshold = 950;
void setup()
{
Particle.function(“SetThreshold”, setThreshold);
pinMode(ldrPin,INPUT);
pinMode(statusPin, OUTPUT);
pinMode(flashPin, OUTPUT);
pinMode(pwrPin, OUTPUT);
}
void loop()
{
digitalWrite(pwrPin, HIGH);
ldrValue = analogRead(ldrPin); // read the value from the LDR
if(ldrValue > ldrThreshold){
digitalWrite(statusPin, HIGH);
digitalWrite(flashPin, HIGH);
delay(10);
} else {
digitalWrite(statusPin, LOW);
digitalWrite(flashPin, LOW);
}
}
int setThreshold(String param)
{
ldrThreshold = param.toInt();
return ldrThreshold;
}

The setThreshold() function in this case sets the ldrThreshold variable to the value received via a REST request. The request itself is a special URL that calls the name of the function and submits the specified value through Cloud API. Since the request is just a URL, you can submit it using the curl tool:

https://api.particle.io/v1/devices/PHOTON_DEVICE_ID/FUNCTION -d access_token=ACCESS_TOKEN -d param=VALUE

To make this request work, you have to replace four placeholders with the actual values:

  • PHOTON_DEVICE_ID A unique Photon board identifier. Use the Build IDE to obtain it.
  • ACCESS_TOKEN A unique alphanumeric code. Use the Build IDE to obtain it.
  • FUNCTION The name of the function to call. In this case it’s setThreshold.
  • VALUE The actual numeric value passed to the function, e.g., 1500.

The following curl command changes the ldrThreshold value in the program to 1500 :

curl https://api.particle.io/v1/devices/3b0032001447343432313035/setThreshold -d access_token=9bd62d5d970799bc1f6d969bf1d257133d28aee7 -d param=1500

Run the command in the terminal, and if the request has been submitted successfully, you should see the following response:

{
“id”: “180029000347343337373739”,
“last_app”: “”,
“connected”: true,
“return_value”: 1500
}

Instead of issuing the command, you can create a simple Bash shell script:

#!/bin/bash
device_id=”PHOTON_DEVICE_ID”
access_token=”ACCESS_TOKEN”
curl https://api.particle.io/v1/devices/$device_id/SetThreshold -d access_token=$access_token -d param=$1

Save the script under the threshold.sh name, make it executable using the chmod +x threshold.sh command, then run it with the ./threshold.sh command followed by the desired value (e.g., ./threshold.sh 975).

While changing the threshold value from the command-line using curl does the trick, it’s not always convenient or practical. To solve the problem, you can whip up a HTML page with a simple form.

<!DOCTYPE>
<html>
<head>
<meta http-equiv=”content-type” content=”text/html; charset=UTF-8" />
<title>Set Threshold</title>
</head>
<body>
<center>
<h1>Set Threshold</h1>
<form action=”https://api.particle.io/v1/devices/PHOTON_DEVICE_ID/setThreshold?access_token=ACCESS_TOKEN" method=”POST”>
<input type=”text” name=”param”><br>
<input id=”btn” type=”submit” value=”submit”>
</form>
</center>
</body>
</html>

The form consists of a text input field and a submit button. When the button is pressed, the contents of the input field is sent as a parameter of the REST request.

This page works locally with practically any browser. Copy the page to your Android device, open the copied file in the default browser (use the file:/// prefix before the file path, e.g., file:///storage/sdcard/threshold.html), and, voila, you have a mobile web interface for controlling the threshold.

Camera Settings

To achieve optimal results, you may need to experiment with positioning the optical flash trigger in relation to the camera and try different camera settings. I use a SUNPAK auto 4205G Thyristor flash with a SONY NEX-3N camera. The latter has a tilting built-in flash head, so I can adjust its angle to trigger the external flash. As for the camera settings, here is the configuration that works in most situations:

  • Shooting mode: Manual (M)
  • Shutter speed 1/60
  • ISO: 200
  • Aperture: variable (you can use the aperture to control exposure)
  • Flash mode: Fill in

Source Code and Files

All files and source code for this project are released under the GPLv3 license and available on GitHub.

Example Photos

The photos below were taken with a SONY NEX-3N camera and a SUNPAK auto 4205G external flash equipped with the described optical flash trigger.

About Me

I write about Linux and Open Source for a living. I’m an amateur photographer, and I use Linux as my photography platform. I’m the author of the Linux Photography book on setting up an automated and streamlined photographic workflow on Linux.

--

--

Dmitri Popov

Technical writer and amateur photographer. Author of the Linux Photography book | https://gum.co/linux-photography