A solderless breadboard sufficed for the simple circuitry behind the strobe controller:

I used a separate 7.5 V supply for the Arduino Pro Mini to keep the relay noise out of the VCC circuit, but that’s probably not really necessary; you could back-drive the Pro Mini’s regulator with +5 V and it’d be perfectly happy. There’s a +5 V wall wart for the relay, LEDs, and so forth.
Protip: you do not want to drive all the other circuitry through the Pro Mini’s tiny little regulator. Work out the power dissipation in the regulator caused by a 130 Ω relay, about 10 mA for the laser, 100 mA for the white LED, and whatever the Pro Mini draws. Yeah, some of those are intermittent loads, but work it out anyway.
A 1.5 V bench supply powers the Xenon strobe in place of the AA alkaline cell I used at first. The boost circuit pins the supply at 3 A for a few seconds, then settles at about 350 mA (!) while idling; no wonder the poor little AA cells don’t last very long!
The control program is also dead simple; it’s mostly a state machine that notices when the photocurrent drops to zero, then steps through a series of fixed delays while turning the laser, LED, and strobe outputs on and off.
The default values highlight a falling object about 200 mm below the laser beam-break sensor, assuming you release the object just above the beam:

The laser beam is at the 200 mm mark, so that ball passing 400 mm has dropped 200 mm.
The quadrature encoder knob recycles the same interrupt handler I used earlier, with the shaft button selecting either the LED delay (pushed) or the Xenon strobe delay (released). There’s precious little error checking, as befits a quick hack job, so use at your own risk…
The Arduino source code:
// Optical flash triggering
// Ed Nisley - KE4ANU - March 2014
//----------
// Pin assignments
const byte PIN_KNOB_A = 2; // knob A switch - must be on ext interrupt 2
const byte PIN_KNOB_B = 4; // .. B switch
const byte PIN_KNOB_SWITCH = A3; // .. shaft push switch
const byte PIN_PHOTOCURRENT = A0; // photodiode current input
const byte PIN_LASER = 8; // laser drive -active
const byte PIN_LED = 7; // LED drive -active
const byte PIN_FLASH = 12; // Xenon flash relay -active
const byte PIN_SYNC = 13; // scope sync - and Arduino LED
//----------
// Constants
enum FALLING_STATES {F_IDLE,F_WAIT,F_DETECT,F_PREFALL,F_LED,F_MD,F_FLASH,F_CLEAR};
enum KNOB_STATES {KNOB_CLICK_0,KNOB_CLICK_1};
//----------
// Globals
const unsigned long UPDATEMS = 250; // update displays only this many ms apart
volatile char KnobCounter = 0;
volatile byte KnobState;
byte Button, PrevButton;
byte Falling = F_IDLE; // cold start the detection state machine
unsigned long FallStart; // when we we detected the falling object
unsigned int DetectLevel = 200; // ADC reading for object detection
unsigned int DelayLED = 1; // ms from trigger detect to LED preflash
unsigned int DelayFlash = 180; // ... to Xenon flash
unsigned int DelayClear = 6000; // ... after impact to allow camera restart
const byte PulseLED = 50; // ms LED on to pass motion detection threshold
const byte PulseFlash = 20; // ms Xenon flash relay on
const unsigned int RelayAdvance = 3; // ms relay activation to Xenon flash
unsigned long MillisNow;
unsigned long MillisThen;
//-- Helper routine for printf()
int s_putc(char c, FILE *t) {
Serial.write(c);
}
//-- Knob interrupt handler
void KnobHandler(void)
{
byte Inputs;
Inputs = digitalRead(PIN_KNOB_B) << 1 | digitalRead(PIN_KNOB_A); // align raw inputs
// Inputs ^= 0x02; // fix direction
switch (KnobState << 2 | Inputs) {
case 0x00 : // 0 00 - glitch
break;
case 0x01 : // 0 01 - UP to 1
KnobCounter++;
KnobState = KNOB_CLICK_1;
break;
case 0x03 : // 0 11 - DOWN to 1
KnobCounter--;
KnobState = KNOB_CLICK_1;
break;
case 0x02 : // 0 10 - glitch
break;
case 0x04 : // 1 00 - DOWN to 0
KnobCounter--;
KnobState = KNOB_CLICK_0;
break;
case 0x05 : // 1 01 - glitch
break;
case 0x07 : // 1 11 - glitch
break;
case 0x06 : // 1 10 - UP to 0
KnobCounter++;
KnobState = KNOB_CLICK_0;
break;
default : // something is broken!
KnobCounter = 0;
KnobState = KNOB_CLICK_0;
}
}
//------------------
// Set things up
void setup() {
pinMode(PIN_SYNC,OUTPUT);
digitalWrite(PIN_SYNC,LOW); // show we arrived
pinMode(PIN_KNOB_B,INPUT_PULLUP);
pinMode(PIN_KNOB_A,INPUT_PULLUP);
pinMode(PIN_KNOB_SWITCH,INPUT_PULLUP);
pinMode(PIN_LASER,OUTPUT);
digitalWrite(PIN_LASER,HIGH);
pinMode(PIN_LED,OUTPUT);
digitalWrite(PIN_LED,HIGH);
pinMode(PIN_FLASH,OUTPUT);
digitalWrite(PIN_FLASH,HIGH);
KnobState = digitalRead(PIN_KNOB_A);
Button = PrevButton = !digitalRead(PIN_KNOB_SWITCH);
attachInterrupt((PIN_KNOB_A - 2),KnobHandler,CHANGE);
Falling = F_IDLE;
Serial.begin(9600);
fdevopen(&s_putc,0); // set up serial output for printf()
printf("Xenon Flash Trigger\r\nEd Nisley - KE4ZNU - March 2014\r\n");
MillisThen = millis();
}
//------------------
// Go flash!
void loop() {
MillisNow = millis();
if (KnobCounter) {
Button = !digitalRead(PIN_KNOB_SWITCH);
if (Button)
DelayLED += KnobCounter;
else
DelayFlash += KnobCounter;
DelayLED = min(DelayLED,DelayFlash - PulseLED);
printf("Knob: %d, LED: %d, Flash: %d\n",KnobCounter,DelayLED,DelayFlash);
KnobCounter = 0;
}
digitalWrite(PIN_SYNC,HIGH);
switch (Falling) {
case F_IDLE : // turn on laser for object detection
digitalWrite(PIN_LASER,LOW);
printf("Laser on, stabilizing... ");
while (analogRead(PIN_PHOTOCURRENT) <= DetectLevel) {
printf("*");
}
printf("\nReady!\n");
Falling = F_WAIT;
break;
case F_WAIT : // record starting time of beam break
if (analogRead(PIN_PHOTOCURRENT) < DetectLevel) {
FallStart = millis();
Falling = F_DETECT;
}
break;
case F_DETECT : // turn off laser to signal detection
digitalWrite(PIN_LASER,HIGH);
Falling = F_PREFALL;
break;
case F_PREFALL : // turn on LED to trigger camera motion detection
if ((millis() - FallStart) >= DelayLED) {
digitalWrite(PIN_LED,LOW);
Falling = F_LED;
}
break;
case F_LED : // turn off LED
if ((millis() - FallStart) >= (DelayLED + PulseLED)) {
digitalWrite(PIN_LED,HIGH);
Falling = F_MD;
}
break;
case F_MD : // fire the strobe to take picture
if ((millis() - FallStart) >= (DelayFlash - RelayAdvance)) {
digitalWrite(PIN_FLASH,LOW);
Falling = F_FLASH;
}
break;
case F_FLASH : // turn off strobe relay
if ((millis() - FallStart) >= (DelayFlash - RelayAdvance + PulseFlash)) {
digitalWrite(PIN_FLASH,HIGH);
printf("Flash with LED delay: %d, Xenon delay: %d ...",DelayLED,DelayFlash);
Falling = F_CLEAR;
}
break;
case F_CLEAR : // wait for camera to recycle
if ((millis() - FallStart) >= DelayClear) {
printf("done\n");
Falling = F_IDLE;
}
break;
default :
printf("** Bad Falling state: %02X",Falling);
Falling = F_IDLE;
}
digitalWrite(PIN_SYNC,LOW);
if ((MillisNow - MillisThen) > UPDATEMS) {
// printf("State: %02X\n",Falling);
MillisThen = MillisNow;
}
}
Comments
3 responses to “Strobe Photography: Control Program”
Hey, did you pick up any of the $3 Pro Minis at the Sparkfun Arduino Day sale?
Nope; I already have a pretty sizable stash of knockoffs with interesting QC problems… [grin]
[…] knockoff Arduino Pro Mini I used for the strobe photography controller ran the default Blink sketch perfectly, but didn’t respond to its own Reset pushbutton. […]