Archive for May 23rd, 2013

LED + Photodiode Test Fixture

An upcoming Circuit Cellar column calls for a way to measure LED light output vs. current input, which means I need some way to hold LEDs directly over a photodiode while excluding ambient light. Fortunately, the M2 had black PLA filament already loaded:

LED Photocell Fixture - parts

LED Photocell Fixture – parts

That honkin’ big photodiode is a surplus PIN-10AP that’s been lying in wait for an opportunity just like this. The green filter matches the silicon response to CIE-standard human eye sensitivity, so the output tracks what you’d actually see. That’s irrelevant for testing red LEDs that all have pretty much the same wavelength, but it might come in handy for something.

The main body of the fixture holds the LED about 1 mm from the front of the photodiode, indexed against the LED flange so they’re all at a consistent location. The cap has three locating pins made of 3 mm orange filament, with black foam rubber to push the LED into position and block ambient light.

The business end looks like this:

LED Photocell Fixture - LED view

LED Photocell Fixture – LED view

The most convenient way to mount the thing involves a right-angle BNC adapter in my trusty bench vise:

LED Photocell Fixture - with breadboard

LED Photocell Fixture – with breadboard

The circuitry has a voltage-to-current driver for the LED and a zero-bias current-to-voltage converter for the photocell. The zero-bias trick keeps the voltage across the photodiode at zero, so the current varies linearly with illumination.

The solid model laid out for printing along the X axis:

LED Fixture for PIN-10AP Photodiode - solid model overview

LED Fixture for PIN-10AP Photodiode – solid model overview

It obviously has some improvements over the as-printed one in the pictures, in the unlikely event I need another fixture. The most important: a rear ring covering the back of the photodiode. Turns out that the PIN-10AP filter cap leaks a surprising amount of light around the body; I covered the gap with black tape to make the measurements, but that’s crude.

I added a few screw holes to hold the parts together, but the cap (with the foam and pegs) must come off easily while swapping LEDs. I’d be tempted to sink studs into the body and use wing nuts to hold the lid in place, but I don’t have any 4-40 wing nuts…

There’s a tiny bit of support under the central hole to support the LED flange recess and the trench for some foam under the leads:

LED Fixture for PIN-10AP Photodiode - support

LED Fixture for PIN-10AP Photodiode – support

That’s another improvement; the as-printed one has foam on only one side of the leads.

The OpenSCAD source code:

// LED test fixture for PIN-10AP photodiode
// Ed Nisley KE4ZNU May 2013

// Layouts: Adapter AdapterSupport Cap Shield Build Show

Layout = "Build";

Gap = 8;		// between parts in Show

//- Extrusion parameters must match reality!
//  Print with +1 shells and 3 solid layers

ThreadThick = 0.25;
ThreadWidth = 0.40;

HoleWindage = 0.2;

function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);

Protrusion = 0.1;			// make holes end cleanly

Spacing = 5;				// between parts on build platform

inch = 25.4;

Tap2_56 = 0.070 * inch;
Clear2_56 = 0.082 * inch;
Head2_56 = 0.156 * inch;
Head2_56Thick = 0.055 * inch;

//----------------------
// Dimensions

PhotoDiodeOD = 31.3;
PhotoDiodeStemOD = 16.0;
PhotoDiodeStemLength = 8.0;
PhotoDiodeWindowDia = 17.7;
PhotoDiodeHeight = 14.0;

FixtureOD = PhotoDiodeOD + 2*7.0;

LEDDia = 5.0;				// LED body
LEDFlangeOD = 6.0;			// flange at base of LED
LEDFlangeThick = IntegerMultiple(1.5,ThreadThick);
LEDLength = 10.0;			// overall length
LEDRecess = 4.0;			// tube to fit LED body
LEDSides = 8;

FixtureLength = PhotoDiodeHeight + LEDLength + IntegerMultiple(1.0,ThreadThick);

CapLength = 15.0;			// LED cover

FoamOD = FixtureOD/2;
FoamThick = IntegerMultiple(2.0,ThreadThick);

TrenchDepth = 2*FoamThick;
TrenchWidth = LEDDia;

ShieldThick = 5.0;
ShieldScrewCircle = PhotoDiodeOD + (FixtureOD - PhotoDiodeOD)/2;

PinOD = 3.0;				// alignment pin (filament)
PinLength = 10.0;			//   ... total length
PinCircle = FixtureOD/2;

GrubScrewOD = Tap2_56;

$fn=4*6;					// default cylinder sides

//----------------------
// Useful routines

module PolyCyl(Dia,Height,ForceSides=0) {			// based on nophead's polyholes

  Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2);

  FixDia = Dia / cos(180/Sides);

  cylinder(r=(FixDia + HoleWindage)/2,
           h=Height,
	   $fn=Sides);
}

module ShowPegGrid(Space = 10.0,Size = 1.0) {

  RangeX = floor(95 / Space);
  RangeY = floor(125 / Space);

	for (x=[-RangeX:RangeX])
	  for (y=[-RangeY:RangeY])
		translate([x*Space,y*Space,Size/2])
		  %cube(Size,center=true);

}

//-----------------------
// Parts

module Adapter() {

	difference() {
		cylinder(r=FixtureOD/2,h=FixtureLength);
		translate([0,0,-Protrusion]) {
			PolyCyl(LEDDia,2*FixtureLength,LEDSides);
			PolyCyl(PhotoDiodeWindowDia,(FixtureLength - LEDRecess + Protrusion));
			PolyCyl(PhotoDiodeOD,(PhotoDiodeHeight + Protrusion));
		}
		translate([0,0,(FixtureLength - LEDFlangeThick)])
			PolyCyl(LEDFlangeOD,2*LEDFlangeThick,LEDSides);
		translate([FixtureOD/2,0,(FixtureLength + FoamThick/2 - LEDFlangeThick)]) {
			cube([FixtureOD,TrenchWidth,FoamThick],center=true);
		}
		for (angle = [90:90:270])
			rotate(angle)
				translate([0.75*PinCircle,0,(FixtureLength - PinLength/2)])
					PolyCyl(PinOD,PinLength,6);
		for (angle = [0:120:240])
			rotate(angle)
				translate([ShieldScrewCircle/2,0,-Protrusion])
					rotate(45)
						PolyCyl(Tap2_56,(ShieldThick - 6*ThreadThick + Protrusion));
		if (0)
			translate([0,0,FixtureLength/4])
				rotate([0,90,0])
					PolyCyl(GrubScrewOD,FixtureOD);
	}

}

module AdapterSupport() {

spiderthick = IntegerMultiple(LEDFlangeThick - ThreadThick,ThreadThick);

	color("Yellow")
		union() {
			for (leg = [0:LEDSides/2 - 1])
				rotate(leg*360/LEDSides)
					translate([0,0,spiderthick/2])
					cube([(LEDFlangeOD - 0.5*ThreadWidth),
							2.5*ThreadWidth,
							spiderthick],
							center=true);
			cylinder(r=LEDDia/2,h=spiderthick,$fn=LEDSides);
			for (bar = [-1:1])
				translate([LEDDia/3,(bar*3*ThreadWidth - ThreadWidth),0])
					cube([FixtureOD/2,2*ThreadWidth,IntegerMultiple(LEDFlangeThick - ThreadThick)]);
		}
}

module Cap() {

	difference() {
		cylinder(r=FixtureOD/2,h=CapLength);
		translate([(FixtureOD/2 - LEDDia/2),0,CapLength]) {
			cube([FixtureOD,TrenchWidth,2*TrenchDepth],center=true);
		}
		translate([0,0,(CapLength - FoamThick)])
			PolyCyl(FoamOD,(FoamThick + Protrusion));
		for (angle = [90:90:270])
			rotate(angle)
				translate([0.75*PinCircle,0,(CapLength - PinLength/2)])
					PolyCyl(PinOD,PinLength,6);
	}

}

module Shield() {

	difference() {
		cylinder(r=FixtureOD/2,h=ShieldThick);
		translate([0,0,-Protrusion])
			PolyCyl(PhotoDiodeStemOD,(ShieldThick + 2*Protrusion));
		for (angle = [0:120:240])
			rotate(angle) {
				translate([ShieldScrewCircle/2,0,-Protrusion])
					rotate(180/5)
						PolyCyl(Clear2_56,(ShieldThick + 2*Protrusion));
				if (0)
				translate([ShieldScrewCircle/2,0,(ShieldThick - 1.5*Head2_56Thick)])
					rotate(180/6)
						PolyCyl(Head2_56,4*Head2_56Thick);
			}
	}
}

//-------------------
// Build it...

ShowPegGrid();

if (Layout == "Adapter")
	Adapter();

if (Layout == "Cap")
	Cap();

if (Layout == "Shield")
	Shield();

if (Layout == "Show") {
	translate([0,0,(ShieldThick + Gap)]) {
		translate([0,0,FixtureLength + CapLength + Gap])
			rotate([180,0,0])
				Cap();
		Adapter();

		color("Orange")
		for (angle = [90:90:270])
			rotate(angle)
				translate([0.75*PinCircle,0,(FixtureLength + Gap - PinLength/2)])
					PolyCyl(PinOD,PinLength,6);
	}

	Shield();
}

	if (Layout == "AdapterSupport") {
	translate([0,0,FixtureLength])
		rotate([180,0,0])
			%Adapter();
	AdapterSupport();
}

if (Layout == "Build") {
	translate([(Spacing + FixtureOD),0,0]) {
		translate([0,0,FixtureLength])
			rotate([180,0,0])
				Adapter();
		AdapterSupport();
	}
	translate([0,0,0])
		Cap();

	translate([-(Spacing + FixtureOD),0,0])
		Shield();
}

2 Comments