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; } }
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]