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:

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:

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

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:

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:

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();
}
Comments
2 responses to “LED + Photodiode Test Fixture”
[…] « LED + Photodiode Test Fixture […]
[…] upper trace comes from a PIN-10AP photodiode in the LED measurement fixture, minus the black cap holding the LED. The photodiode connects directly to the oscilloscope input, […]