Archive for March 31st, 2014

Strobe Photography: Control Program

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

Strobe Photography - control breadboard

Strobe Photography – control breadboard

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:

Ball at 200 mm - detail

Ball at 200 mm – detail

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

}

3 Comments