The Smell of Molten Projects in the Morning

Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.

Category: Machine Shop

Mechanical widgetry

  • Vacuum Tube LEDs: Grinding Off a 0D3 Base Spigot

    Rummaging in the Hollow State Electronics box produced the shapely 0D3 regulator tube with an intact spigot / key post in its base:

    0D3 voltage regulator tube in socket
    0D3 voltage regulator tube in socket

    Because the glass envelope (1.5 inch = 38.1 mm OD) extends beyond the base (1.125 inch = 28.6 mm OD), the simple base clamp must let the tube extend over the workbench:

    0D3 Octal tube - V-block clamp
    0D3 Octal tube – V-block clamp

    There’s no way to clamp that mess in the Sherline, so, rather than freehanding the shell drill, I misused a Dremel slitting wheel to grind away the end of the spigot, which normally extends a bit beyond the pins so you can’t possibly insert the tube into the socket the wrong way:

    0D3 Octal tube - ground-off spigot tip
    0D3 Octal tube – ground-off spigot tip

    The missing end exposed the hole in the middle of the post and showed this tube’s evacuation tip didn’t extend into the spigot. Emboldened by that, I continued the mission until the wheel wouldn’t reach any further:

    0D3 Octal tube - Dremel grinding
    0D3 Octal tube – Dremel grinding

    That didn’t work well, but at least I didn’t break anything and nobody will ever see those mauled pin tips.

    Obviously, the only way to do this right is to clamp the tube properly and mill the spigot flush with the socket; it’s time for more 3D printing…

  • Vacuum Tube LEDs: Octal Tube Base Drilling

    Clamping the octal tube into the Sherline let me set the XY=0 origin to the center of the base with the laser dot (visible near the front):

    Octal tube clamped on Sherline mill
    Octal tube clamped on Sherline mill

    Find the edges, touch off the half the 32.2 mm diameter, then align the drill at XY=0 directly over the exposed evacuation tip:

    Octal Tube - drill alignment
    Octal Tube – drill alignment

    Make a very shallow cut to verify the alignment:

    Octal Tube - drill first touch
    Octal Tube – drill first touch

    Just inside the scuffed ring from the drill, you can see the fractured ring where the original one-piece Bakelite spigot / key / post broke off.

    Then extract the drill from the chuck, file more relief behind the cutting edges so they actually cut, re-chuck, and continue the mission:

    Octal Tube - drilling
    Octal Tube – drilling

    Pick a nice Bakelite ring out of the drill:

    Octal Tube - drilled ring
    Octal Tube – drilled ring

    And eventually you can see all the way to the glass envelope:

    Octal Tube - base opening
    Octal Tube – base opening

    The (knockoff) Neopixel LED sits directly below the evacuation tip and is about the same diameter, so much of that enlarged opening will be in shadow. Despite that, the tube does seem noticeably brighter:

    Octal Tube - drilled base opening
    Octal Tube – drilled base opening

    Drilling that tube was so harrowing that I can’t imagine similar surgery on an intact octal base.

    Perhaps just slicing off the tip of the Bakelite spigot and gluing a single very bright red/orange LED in place, rather using than a (knockoff) Neopixel a few millimeters away, will suffice.

    Or just give up, top-light these tubes, and move on?

  • Octal Tube Base Clamp

    One of the octal tubes in my collection has a broken spigot / key post that lets some light in through the bottom of the normally opaque Bakelite base:

    Octal socket in CD - LED diffraction
    Octal socket in CD – LED diffraction

    Perhaps drilling out the base would let more light pass around the evacuation tip, but that requires a shell drill to clear the tip. Some doodling suggested a drill with 12 mm OD and 8 mm ID, which was close enough to one of the smaller homebrew drills in my collection that I decided to see how it worked:

    Shell drill assortment
    Shell drill assortment

    You (well, I) can’t freehand such a hole, particularly with a glass tip in the middle, so I needed a way to clamp the tube in either the drill press or the Sherline. A pad for the clamp screw in a V-block seemed appropriate:

    Vacuum Tube Lights - Octal base clamp - Slic3r preview
    Vacuum Tube Lights – Octal base clamp – Slic3r preview

    The screw hole sits at the 1/3 point to put more pressure near the pin end of the base. Maybe that matters.

    The setup looks like this, with a small red laser dot near the front of the base:

    Octal tube clamped on Sherline mill
    Octal tube clamped on Sherline mill

    The tube rests on a random scrap of plastic, with the hope that the drill won’t apply enough pressure to break the glass envelope.

    In normal use, the V-block would be oriented the other way to let you cross-drill the cylinder. In this end-on orientation, drilling torque can rotate the tube; compliant padding for more traction may be in order.

    The OpenSCAD source code as a GitHub Gist now includes a module that spits out the clamp:

    // Vacuum Tube LED Lights
    // Ed Nisley KE4ZNU February … September 2016
    Layout = "TubeClamp"; // Cap LampBase USBPort Bushings
    // Socket(s) Cap (Build)FinCap Platter[Base|Fixture]
    // TubeClamp
    DefaultSocket = "Mini7";
    Section = false; // cross-section the object
    Support = true;
    //- Extrusion parameters must match reality!
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    HoleWindage = 0.2;
    Protrusion = 0.1; // make holes end cleanly
    inch = 25.4;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    //———————-
    // Dimensions
    // https://en.wikipedia.org/wiki/Tube_socket#Summary_of_Base_Details
    // punch & screw OC modified for drive platter chassis plate
    // platter = 25 mm ID
    // CD = 15 mm ID with raised ring at 37 mm, needs screw head clearance
    T_NAME = 0; // common name
    T_NUMPINS = 1; // total, with no allowance for keying
    T_PINBCD = 2; // tube pin circle diameter
    T_PINOD = 3; // … diameter
    T_PINLEN = 4; // … length (must also clear evacuation tip / spigot)
    T_HOLEOD = 5; // nominal panel hole from various sources
    T_PUNCHOD = 6; // panel hole optimized for inch-size Greenlee punches
    T_TUBEOD = 7; // envelope or base diameter
    T_PIPEOD = 8; // light pipe from LED to tube base (clear evac tip / spigot)
    T_SCREWOC = 9; // mounting screw holes
    // Name pins BCD dia length hole punch tube pipe screw
    TubeData = [
    ["Mini7", 8, 9.53, 1.016, 7.0, 16.0, 25.0, 18.0, 5.0, 35.0], // punch 11/16, screw 22.5 OC
    // ["Octal", 8, 17.45, 2.36, 11.0, 36.2, (8 + 1)/8 * inch, 32.0, 11.5, 47.0], // screw 39.0 OC
    ["Octal", 8, 17.45, 2.36, 11.0, 36.2, 25.0, 32.0, 11.5, 42.0], // platter + 4 mm screws
    ["Noval", 10, 11.89, 1.1016, 7.0, 22.0, 25.0, 21.0, 7.5, 35.0], // punch 7/8, screw 28.0 OC
    ["Magnoval", 10, 17.45, 1.27, 9.0, 29.7, (4 + 1)/4 * inch, 46.0, 12.4, 38.2], // similar to Novar
    // ["Duodecar", 13, 19.10, 1.05, 9.0, 32.0, (4 + 1)/4 * inch, 38.0, 12.5, 47.0], // screw was 39.0 OC
    ["Duodecar", 13, 19.10, 1.05, 9.0, 25.0, 25.0, 38.0, 12.5, 42.0], // fit un-punched drive platter
    ];
    ID = 0;
    OD = 1;
    LENGTH = 2;
    Pixel = [7.0,10.0,3.0]; // ID = contact patch, OD = PCB dia, LENGTH = overall thickness
    SocketNut = // socket mounting: threaded insert or nut recess
    // [3.5,5.2,7.2] // 6-32 insert
    [4.0,6.0,5.9] // 4 mm short insert
    ;
    NutSides = 8;
    SocketShim = 2*ThreadThick; // between pin holes and pixel top
    SocketFlange = 1.5; // rim around socket below punchout
    PanelThick = 1.5; // socket extension through punchout
    FinCutterOD = 1/8 * inch;
    FinCapSize = [(Pixel[OD] + 2*FinCutterOD),30.0,(10.0 + 2*Pixel[LENGTH])];
    USBPCB =
    // [28,16,6.5] // small Sparkfun knockoff
    [36,18 + 1,5.8 + 0.4] // Deek-Robot fake FTDI with ISP header
    ;
    Platter = [25.0,95.0,1.26]; // hard drive platter dimensions
    //———————-
    // 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(d=(FixDia + HoleWindage),h=Height,$fn=Sides);
    }
    //———————-
    // Tube cap
    CapTube = [4.0,3/16 * inch,10.0]; // brass tube for flying lead to cap LED
    CapSize = [Pixel[ID],(Pixel[OD] + 2.0),(CapTube[OD] + 2*Pixel[LENGTH])];
    CapSides = 8*4;
    module Cap() {
    difference() {
    union() {
    cylinder(d=CapSize[OD],h=(CapSize[LENGTH]),$fn=CapSides); // main cap body
    translate([0,0,CapSize[LENGTH]]) // rounded top
    scale([1.0,1.0,0.65])
    sphere(d=CapSize[OD]/cos(180/CapSides),$fn=CapSides); // cos() fixes slight undersize vs cylinder
    cylinder(d1=(CapSize[OD] + 2*3*ThreadWidth),d2=CapSize[OD],h=1.5*Pixel[LENGTH],$fn=CapSides); // skirt
    }
    translate([0,0,-Protrusion]) // bore for wiring to LED
    PolyCyl(CapSize[ID],(CapSize[LENGTH] + 3*ThreadThick + Protrusion),CapSides);
    translate([0,0,-Protrusion]) // PCB recess with clearance for tube dome
    PolyCyl(Pixel[OD],(1.5*Pixel[LENGTH] + Protrusion),CapSides);
    translate([0,0,(1.5*Pixel[LENGTH] – Protrusion)]) // small step + cone to retain PCB
    cylinder(d1=(Pixel[OD]/cos(180/CapSides) + HoleWindage),d2=Pixel[ID],h=(Pixel[LENGTH] + Protrusion),$fn=CapSides);
    translate([0,0,(CapSize[LENGTH] – CapTube[OD]/(2*cos(180/8)))]) // hole for brass tube holding wire loom
    rotate([90,0,0]) rotate(180/8)
    PolyCyl(CapTube[OD],CapSize[OD],8);
    }
    }
    //———————-
    // Heatsink tube cap
    module FinCap() {
    CableOD = 3.5; // cable + braid diameter
    BulbOD = 3.75 * inch; // bulb OD; use 10 inches for flat
    echo(str("Fin Cutter: ",FinCutterOD));
    FinSides = 2*4;
    BulbRadius = BulbOD / 2;
    BulbDepth = BulbRadius – sqrt(pow(BulbRadius,2) – pow(FinCapSize[OD],2)/4);
    echo(str("Bulb OD: ",BulbOD," recess: ",BulbDepth));
    NumFins = floor(PI*FinCapSize[ID] / (2*FinCutterOD));
    FinAngle = 360 / NumFins;
    echo(str("NumFins: ",NumFins," angle: ",FinAngle," deg"));
    difference() {
    union() {
    cylinder(d=FinCapSize[ID],h=FinCapSize[LENGTH],$fn=2*NumFins); // main body
    for (i = [0:NumFins – 1]) // fins
    rotate(i * FinAngle)
    hull() {
    translate([FinCapSize[ID]/2,0,0])
    rotate(180/FinSides)
    cylinder(d=FinCutterOD,h=FinCapSize[LENGTH],$fn=FinSides);
    translate([(FinCapSize[OD] – FinCutterOD)/2,0,0])
    rotate(180/FinSides)
    cylinder(d=FinCutterOD,h=FinCapSize[LENGTH],$fn=FinSides);
    }
    rotate(FinAngle/2) // cable entry boss
    translate([FinCapSize[ID]/2,0,FinCapSize[LENGTH]/2])
    cube([FinCapSize[OD]/4,FinCapSize[OD]/4,FinCapSize[LENGTH]],center=true);
    }
    for (i = [1:NumFins – 1]) // fin inner gullets, omit cable entry side
    rotate(i * FinAngle + FinAngle/2) // joint isn't quite perfect, but OK
    translate([FinCapSize[ID]/2,0,-Protrusion])
    rotate(0*180/FinSides)
    cylinder(d=FinCutterOD/cos(180/FinSides),h=(FinCapSize[LENGTH] + 2*Protrusion),$fn=FinSides);
    translate([0,0,-Protrusion]) // PCB recess
    PolyCyl(Pixel[OD],(1.5*Pixel[LENGTH] + Protrusion),FinSides);
    PolyCyl(Pixel[ID],(FinCapSize[LENGTH] – 3*ThreadThick),FinSides); // bore for LED wiring
    translate([0,0,(FinCapSize[LENGTH] – 3*ThreadThick – 2*CableOD/(2*cos(180/8)))]) // cable inlet
    rotate(FinAngle/2) rotate([0,90,0]) rotate(180/8)
    PolyCyl(CableOD,FinCapSize[OD],8);
    if (BulbOD <= 10.0 * inch) // curve for top of bulb
    translate([0,0,-(BulbRadius – BulbDepth + 2*ThreadThick)]) // … slightly flatten tips
    sphere(d=BulbOD,$fn=16*FinSides);
    }
    }
    //———————-
    // Aperture for USB-to-serial adapter snout
    // These are all magic numbers, of course
    module USBPort() {
    translate([0,USBPCB[0]])
    rotate([90,0,0])
    linear_extrude(height=USBPCB[0])
    polygon(points=[
    [0,0],
    [USBPCB[1]/2,0],
    [USBPCB[1]/2,0.5*USBPCB[2]],
    [USBPCB[1]/3,USBPCB[2]],
    [-USBPCB[1]/3,USBPCB[2]],
    [-USBPCB[1]/2,0.5*USBPCB[2]],
    [-USBPCB[1]/2,0],
    ]);
    }
    //———————-
    // Box for Leviton ceramic lamp base
    module LampBase() {
    Insert = [3.5,5.2,7.2]; // 6-32 brass insert to match standard electrical screws
    Bottom = 3.0;
    Base = [4.0*inch,4.5*inch,20.0 + Bottom];
    Sides = 12*4;
    Retainer = [3.5,11.0,1.0]; // flat fiber washer holding lamp base screws in place
    StudSides = 8;
    StudOC = 3.5 * inch;
    Stud = [Insert[OD], // insert for socket screws
    min(15.0,1.5*(Base[ID] – StudOC)/cos(180/StudSides)), // OD = big enough to merge with walls
    (Base[LENGTH] – Retainer[LENGTH])]; // leave room for retainer
    union() {
    difference() {
    rotate(180/Sides)
    cylinder(d=Base[OD],h=Base[LENGTH],$fn=Sides);
    rotate(180/Sides)
    translate([0,0,Bottom])
    cylinder(d=Base[ID],h=Base[LENGTH],$fn=Sides);
    translate([0,-Base[OD]/2,Bottom + 1.2]) // mount on double-sided foam tape
    rotate(0)
    USBPort();
    }
    for (i = [-1,1])
    translate([i*StudOC/2,0,0])
    rotate(180/StudSides)
    difference() {
    cylinder(d=Stud[OD],h=Stud[LENGTH],$fn=StudSides);
    translate([0,0,Bottom])
    PolyCyl(Stud[ID],(Stud[LENGTH] – (Bottom – Protrusion)),6);
    }
    }
    }
    //———————-
    // Base for hard drive platters
    module PlatterBase(TubeName = DefaultSocket) {
    PCB =
    [36,18,3] // Arduino Pro Mini
    ;
    Tube = search([TubeName],TubeData,1,0)[0];
    SocketHeight = Pixel[LENGTH] + SocketShim + TubeData[Tube][T_PINLEN] – PanelThick;
    echo(str("Base for ",TubeData[Tube][0]," socket"));
    Overhang = 5.5; // platter overhangs base by this much
    Bottom = 4*ThreadThick;
    Base = [(Platter[OD] – 3*Overhang), // smaller than 3.5 inch Sch 40 PVC pipe…
    (Platter[OD] – 2*Overhang),
    2.0 + max(PCB[1],(2.0 + SocketHeight + USBPCB[2])) + Bottom];
    Sides = 24*4;
    echo(str(" Height: ",Base[2]," mm"));
    Insert = // platter mounting: threaded insert or nut recess
    // [3.5,5.2,7.2] // 6-32 insert
    [3.9,5.0,8.0] // 3 mm – long insert
    ;
    NumStuds = 4;
    StudSides = 8;
    Stud = [Insert[OD], // insert for socket screws
    2*Insert[OD], // OD = big enough to merge with walls
    Base[LENGTH]]; // leave room for retainer
    StudBCD = floor(Base[ID] – Stud[OD] + (Stud[OD] – Stud[ID])/2);
    echo(str("Platter screw BCD: ",StudBCD," mm"));
    PCBInset = Base[ID]/2 – sqrt(pow(Base[ID]/2,2) – pow(PCB[0],2)/4);
    union() {
    difference() {
    rotate(180/Sides)
    cylinder(d=Base[OD],h=Base[LENGTH],$fn=Sides);
    rotate(180/Sides)
    translate([0,0,Bottom])
    cylinder(d=Base[ID],h=Base[LENGTH],$fn=Sides);
    translate([0,-Base[OD]/2,Bottom + 1.2]) // mount PCB on foam tape
    rotate(0)
    USBPort();
    }
    for (a = [0:(NumStuds – 1)]) // platter mounting studs
    rotate(180/NumStuds + a*360/(NumStuds))
    translate([StudBCD/2,0,0])
    rotate(180/StudSides)
    difference() {
    cylinder(d=Stud[OD],h=Stud[LENGTH],$fn=2*StudSides);
    translate([0,0,Bottom])
    PolyCyl(Stud[ID],(Stud[LENGTH] – (Bottom – Protrusion)),StudSides);
    }
    intersection() { // microcontroller PCB mounting plate
    rotate(180/Sides)
    cylinder(d=Base[OD],h=Base[LENGTH],$fn=Sides);
    translate([-PCB[0]/2,(Base[ID]/2 – PCBInset),0])
    cube([PCB[0],Base[OD]/2,Base[LENGTH]],center=false);
    }
    difference() {
    intersection() { // totally ad-hoc bridge around USB opening
    rotate(180/Sides)
    cylinder(d=Base[OD],h=Base[LENGTH],$fn=Sides);
    translate([-1.25*USBPCB[1]/2,-(Base[ID]/2),0])
    cube([1.25*USBPCB[1],2.0,Base[LENGTH]],center=false);
    }
    translate([0,-Base[OD]/2,Bottom + 1.2]) // mount PCB on foam tape
    rotate(0)
    USBPort();
    translate([0,-(Base[ID]/2 – 2.0 + 1*ThreadWidth),Bottom – 3*ThreadThick]) // legend
    rotate([90,0,180])
    linear_extrude(height=1*ThreadWidth + Protrusion) {
    translate([0,(Base[LENGTH] – 5.5),0])
    text(text=TubeName,size=4,font="Arial:style=Bold",halign="center");
    // translate([0,(Base[LENGTH] – 8.5),0])
    // text(text=str("BCD ",StudBCD),size=2,font="Arial",halign="center");
    translate([0,(Base[LENGTH] – 11),0])
    text(text="KE4ZNU",size=3,font="Arial",halign="center");
    }
    }
    }
    }
    //———————-
    // Drilling fixture for disk platters
    module PlatterFixture() {
    StudOC = [1.16*inch,1.16*inch]; // Sherline tooling plate screw spacing
    StudClear = 5.0;
    BasePlate = [(20 + StudOC[0]*ceil(Platter[OD] / StudOC[0])),(Platter[OD] + 10),7.0];
    PlateRound = 10.0; // corner radius
    difference() {
    hull() // basic block
    for (i=[-1,1], j=[-1,1])
    translate([i*(BasePlate[0]/2 – PlateRound),j*(BasePlate[1]/2 – PlateRound),0])
    cylinder(r=PlateRound,h=BasePlate[2],$fn=4*4);
    for (i=[-1:1], j=[-1:1]) // index marks
    translate([i*100/2,j*100/2,BasePlate[2] – 2*ThreadThick])
    cylinder(d=1.5,h=1,$fn=6);
    for (i=[-1,1], j=[-1,0,1]) // holes for tooling plate studs
    translate([i*StudOC[0]*ceil(Platter[OD] / StudOC[0])/2,j*StudOC[0],-Protrusion])
    PolyCyl(StudClear,BasePlate[2] + 2*Protrusion,6);
    translate([0,0,-Protrusion]) // center clamp hole
    PolyCyl(StudClear,BasePlate[2] + 2*Protrusion,6);
    translate([0,0,BasePlate[2] – Platter[LENGTH]]) // disk locating recess
    linear_extrude(height=(Platter[LENGTH] + Protrusion),convexity=2)
    difference() {
    circle(d=(Platter[OD] + 1),$fn=8*4);
    circle(d=Platter[ID],$fn=8*4);
    }
    translate([0,0,BasePlate[2] – 4.0]) // drilling recess
    linear_extrude(height=(4.0 + Protrusion),convexity=2)
    difference() {
    circle(d=(Platter[OD] – 10),$fn=8*4);
    circle(d=(Platter[ID] + 10),$fn=8*4);
    }
    }
    }
    //———————-
    // Tube Socket
    module Socket(Name = DefaultSocket) {
    NumSides = 6*4;
    Tube = search([Name],TubeData,1,0)[0];
    echo(str("Building ",TubeData[Tube][0]," socket"));
    echo(str(" Punch: ",TubeData[Tube][T_PUNCHOD]," mm = ",TubeData[Tube][T_PUNCHOD]/inch," inch"));
    echo(str(" Screws: ",TubeData[Tube][T_SCREWOC]," mm =",TubeData[Tube][T_SCREWOC]/inch," inch OC"));
    OAH = Pixel[LENGTH] + SocketShim + TubeData[Tube][T_PINLEN];
    BaseHeight = OAH – PanelThick;
    difference() {
    union() {
    linear_extrude(height=BaseHeight) // base outline
    hull() {
    circle(d=(TubeData[Tube][T_PUNCHOD] + 2*SocketFlange),$fn=NumSides);
    for (i=[-1,1])
    translate([i*TubeData[Tube][T_SCREWOC]/2,0])
    circle(d=2.0*SocketNut[OD],$fn=NumSides);
    }
    cylinder(d=TubeData[Tube][T_PUNCHOD],h=OAH,$fn=NumSides); // boss in chassis punch hole
    }
    for (i=[0:(TubeData[Tube][T_NUMPINS] – 1)]) // tube pins
    rotate(i*360/TubeData[Tube][T_NUMPINS])
    translate([TubeData[Tube][T_PINBCD]/2,0,(OAH – TubeData[Tube][T_PINLEN])])
    rotate(180/4)
    PolyCyl(TubeData[Tube][T_PINOD],(TubeData[Tube][T_PINLEN] + Protrusion),4);
    for (i=[-1,1]) // mounting screw holes & nut traps / threaded inserts
    translate([i*TubeData[Tube][T_SCREWOC]/2,0,-Protrusion]) {
    PolyCyl(SocketNut[OD],(SocketNut[LENGTH] + Protrusion),NutSides);
    PolyCyl(SocketNut[ID],(OAH + 2*Protrusion),NutSides);
    }
    translate([0,0,-Protrusion]) { // LED recess
    PolyCyl(Pixel[OD],(Pixel[LENGTH] + Protrusion),8);
    }
    translate([0,0,(Pixel[LENGTH] – Protrusion)]) { // light pipe
    rotate(180/TubeData[Tube][T_NUMPINS])
    PolyCyl(TubeData[Tube][T_PIPEOD],(OAH + 2*Protrusion),TubeData[Tube][T_NUMPINS]);
    }
    for (i=[-1,1]) // cable retaining slots
    translate([i*(Pixel[OD] + TubeData[Tube][T_SCREWOC])/4,0,(Pixel[LENGTH] – Protrusion)/2])
    cube([Pixel[LENGTH],TubeData[Tube][T_SCREWOC],(Pixel[LENGTH] + Protrusion)],center=true);
    }
    // Totally ad-hoc support structures …
    if (Support) {
    color("Yellow") {
    for (i=[-1,1]) // nut traps
    translate([i*TubeData[Tube][T_SCREWOC]/2,0,(SocketNut[LENGTH] – ThreadThick)/2])
    for (a=[0:5])
    rotate(a*30 + 15)
    cube([2*ThreadWidth,0.9*SocketNut[OD],(SocketNut[LENGTH] – ThreadThick)],center=true);
    if (Pixel[OD] > TubeData[Tube][T_PIPEOD]) // support pipe only if needed
    translate([0,0,(Pixel[LENGTH] – ThreadThick)/2])
    for (a=[0:7])
    rotate(a*22.5)
    cube([2*ThreadWidth,0.9*Pixel[OD],(Pixel[LENGTH] – ThreadThick)],center=true);
    }
    }
    }
    //———————-
    // Greenlee punch bushings
    module PunchBushing(Name = DefaultSocket) {
    PunchScrew = 9.5;
    BushingThick = 3.0;
    Tube = search([Name],TubeData,1,0)[0];
    echo(str("Building ",TubeData[Tube][0]," bushing"));
    NumSides = 6*4;
    difference() {
    union() {
    cylinder(d=Platter[ID],h=BushingThick,$fn=NumSides);
    cylinder(d=TubeData[Tube][T_PUNCHOD],h=(BushingThick – Platter[LENGTH]),$fn=NumSides);
    }
    translate([0,0,-Protrusion])
    PolyCyl(PunchScrew,5.0,8);
    }
    }
    //———————-
    // Tube clamp
    module TubeClamp(Name = DefaultSocket) {
    Tube = search([Name],TubeData,1,0)[0];
    echo(str("Building ",TubeData[Tube][0]," clamp"));
    ClampWidth = 37.0; // inside of clamp arch
    ClampLength = 20; // along tube base
    ClampScrew = [6.0,7.8,6.0]; // nose of clamp screw
    ClampArc = 3*45; // angle subtended by block bottom
    ClampChordWidth = 2 * (TubeData[Tube][T_TUBEOD]/2) * sin(ClampArc/2);
    ClampChordDepth = (ClampChordWidth / 2) * tan(ClampArc/4);
    echo(str("Chord width: ",ClampChordWidth," depth: ",ClampChordDepth));
    ClampBlock = [ClampWidth,(ClampChordDepth + 2*ClampScrew[LENGTH]),ClampLength];
    difference() {
    translate([0,(ClampBlock[1]/2 + TubeData[Tube][T_TUBEOD]/2 – ClampChordDepth),0])
    intersection() {
    translate([0,0,ClampLength/2])
    cube(ClampBlock,center=true);
    translate([0,(ClampBlock[1]/2 – ClampWidth/2),0])
    cylinder(d=ClampWidth,h=ClampLength);
    }
    translate([0,0,-Protrusion]) // remove tube base (remains centered)
    cylinder(d=TubeData[Tube][T_TUBEOD],h=(ClampLength + 2*Protrusion));
    translate([0,(TubeData[Tube][T_TUBEOD]/2 + ClampScrew[LENGTH]),ClampBlock[LENGTH]/3])
    rotate([-90,0,0])
    PolyCyl(ClampScrew[ID],2*ClampScrew[LENGTH],6);
    }
    }
    //———————-
    // Build it
    if (Layout == "Cap") {
    if (Section)
    difference() {
    Cap();
    translate([-CapSize[OD],0,CapSize[LENGTH]])
    cube([2*CapSize[OD],2*CapSize[OD],3*CapSize[LENGTH]],center=true);
    }
    else
    Cap();
    }
    if (Layout == "FinCap") {
    if (Section) render(convexity=5)
    difference() {
    FinCap();
    // translate([0,-FinCapSize[OD],FinCapSize[LENGTH]])
    // cube([2*FinCapSize[OD],2*FinCapSize[OD],3*FinCapSize[LENGTH]],center=true);
    translate([-FinCapSize[OD],0,FinCapSize[LENGTH]])
    cube([2*FinCapSize[OD],2*FinCapSize[OD],3*FinCapSize[LENGTH]],center=true);
    }
    else
    FinCap();
    }
    if (Layout == "BuildFinCap")
    translate([0,0,FinCapSize[LENGTH]])
    rotate([180,0,0])
    FinCap();
    if (Layout == "LampBase")
    LampBase();
    if (Layout == "PlatterBase")
    PlatterBase();
    if (Layout == "PlatterFixture")
    PlatterFixture();
    if (Layout == "USBPort")
    USBPort();
    if (Layout == "TubeClamp")
    TubeClamp("Octal");
    if (Layout == "Bushings")
    PunchBushing();
    if (Layout == "Socket")
    if (Section) {
    difference() {
    Socket();
    translate([-100/2,0,-Protrusion])
    cube([100,50,50],center=false);
    }
    }
    else
    Socket();
    if (Layout == "Sockets") {
    translate([0,50,0])
    Socket("Mini7");
    translate([0,20,0])
    Socket("Octal");
    translate([0,-15,0])
    Socket("Duodecar");
    translate([0,-50,0])
    Socket("Noval");
    translate([0,-85,0])
    Socket("Magnoval");}
  • Miniblind Bottom Rail Caps

    A few days after installing the replacement cord caps, I bumped the bottom rail of the miniblind while opening the window and had one endcap disintegrate; apparently window hardware isn’t hardened against prolonged UV exposure. Who knew?

    Fortunately, I can fix that:

    Miniblind bottom rail caps
    Miniblind bottom rail caps

    Making the walls three threads wide provides enough room for a single solid infill thread:

    Miniblind Endcaps - Slic3r Preview
    Miniblind Endcaps – Slic3r Preview

    The exterior shape comes from a hull wrapped around six circles: four to define the corner radius and a pair that bump the center out by the calculated chord height. The interior shape comes from a pair of chord-radius polygonal circles (they only have three facets across the length of the inside wall) that fit the bottom rail almost perfectly.

    As always, natural PETG has a crystalline, slightly transparent, appearance:

    Miniblind bottom rail cap installed
    Miniblind bottom rail cap installed

    I should spring for some opaque white filament, but that way lies madness; I might start caring what these things look like.

    You can buy entire miniblinds for a few bucks a pop, but the last time we did that, they were different than the ones we had before. That wouldn’t matter if the standard miniblind mounting brackets fit our 1955 Anderson windows, but noooo they don’t: the custom adapters I machined for the first miniblind brackets, of course, didn’t fit the new miniblinds.

    Now I can just snap the replacement endcaps (and cord pulls) in place, declare victory, and move on.

    The OpenSCAD source code as a GitHub Gist:

    // Cap for miniblind cord and bottom rail endcaps
    // Ed Nisley KE4ZNU – September 2016
    Layout = "BaseEndCap"; // CordCap BaseEndCap
    //- Extrusion parameters – must match reality!
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    Protrusion = 0.1;
    HoleWindage = 0.2;
    //——
    // Dimensions
    OD1 = 0;
    OD2 = 1;
    LENGTH = 2;
    //———————-
    //- Build it
    if (Layout == "CordCap") {
    Cap = [9.0,16.0,25.0];
    Cord = [2.5,7.0,Cap[LENGTH] – 5];
    NumSides = 8;
    difference() {
    hull() { // overall shape
    translate([0,0,Cap[LENGTH] – Cap[OD1]/2])
    sphere(d=Cap[OD1],$fn=NumSides);
    translate([0,0,0.5*Cap[OD2]/2])
    sphere(d=Cap[OD2],$fn=2*NumSides); // round the bottom just a bit
    }
    translate([0,0,-Cap[LENGTH]/2]) // trim bottom
    cube([2*Cap[OD2],2*Cap[OD2],Cap[LENGTH]],center=true);
    translate([0,0,Cap[LENGTH] + 0.8*Cap[OD1]]) // trim top (arbitrarily)
    cube([2*Cap[OD1],2*Cap[OD1],2*Cap[OD1]],center=true);
    translate([0,0,-Protrusion])
    cylinder(d=Cord[OD1],h=(Cap[LENGTH] + 2*Protrusion),$fn=NumSides);
    translate([0,0,-Protrusion])
    cylinder(d1=Cord[OD2],d2=Cord[OD1],h=(Cord[LENGTH] + Protrusion),$fn=NumSides);
    }
    }
    if (Layout == "BaseEndCap") {
    Base = [25.2,9.0,12.2]; // base outside dimensions
    Edge = 8.0; // size of sqare ends
    CornerRadius = 0.75;
    Wall = 3; // wall thickness in threads
    ChordHeight = (Base[1] – Edge) / 2;
    ChordRadius = (pow(ChordHeight,2) + pow(Base[0],2)/4) / (2*ChordHeight);
    NumSides = 4*4;
    echo(str("Chord height: ",ChordHeight," radius: ",ChordRadius));
    difference() {
    linear_extrude(height=Base[2],convexity=2) {
    hull() {
    for (i=[-1,1], j=[-1,1])
    translate([i*(Base[0]/2 – CornerRadius + Wall*ThreadWidth),j*(Base[1]/2 – CornerRadius + Wall*ThreadWidth)])
    circle(r=CornerRadius,$fn=4*4,center=true);
    for (j=[-1,1])
    translate([0,j*(ChordHeight + Base[1]/2 – CornerRadius + Wall*ThreadWidth)])
    rotate(180/(2*4))
    circle(r=CornerRadius,$fn=2*4,center=true);
    }
    }
    translate([0,0,3*ThreadThick])
    linear_extrude(height=Base[2],convexity=2)
    intersection() {
    intersection_for (j=[-1,1])
    translate([0,j*(ChordHeight + Base[1]/2 – ChordRadius)])
    circle(r=ChordRadius,$fn=32*4,center=true);
    square([Base[0],2*Base[1]],center=true);
    }
    }
    }

    The original doodle with some dimensions that didn’t withstand careful measurements:

    Miniblind Endcap dimension doodle
    Miniblind Endcap dimension doodle
  • Vacuum Tube LEDs: Miniature 7-pin Tubes With a Bottom Shield

    Apart from the Bakelite bases on octal tubes, I figured there should be no problem shining a light up through the glass envelope. Come to find out that some of the tubes with Miniature 7 bases have an electrostatic shield (?) across the bottom that pretty well blocks the light.

    This 6BJ6 has a neatly trimmed octagon:

    6BJ6 - octagon shield
    6BJ6 – octagon shield

    The shield plate, if that’s what it is, doesn’t have a standardized shape. This 6CB6 sports a simple square:

    6CB6 Square Shield
    6CB6 Square Shield

    The Box o’ Hollow State Electronics contains one 6BE6 tube (a heptode with five grids connected to four pins) without a shield:

    6BE6 - Clear base
    6BE6 – Clear base

    Yeah, those pins are rather grotendous.

    And another 6BE6 with a semitransparent smudge not connected to anything else; it would look accidental if it weren’t inside the tube:

    6BE6 - Tinted Base
    6BE6 – Tinted Base

    All the shielded tubes are pentodes, for whatever difference that makes.

    These tubes may be a bit too small compared to the hard drive platters; Novals will work just fine for my simple purposes.

  • Vacuum Tube LEDs: Fully Dressed 21HB5A

    Black PETG definitely looks better than cyan for this job:

    21HB5A - Black PETG base - flash
    21HB5A – Black PETG base – flash

    Holding the plate cap to the tube with a thin ring of opaque epoxy cuts down on the glare under its edge:

    21HB5A - Black PETG base - cyan phase
    21HB5A – Black PETG base – cyan phase

    Fire in the bottle!

    21HB5A - Black PETG fittings - punched drive platter - purple phase
    21HB5A – Black PETG fittings – punched drive platter – purple phase

    It’s still running basically the same Arduino code as before, but I have some ideas about that

  • Hard Drive Platter Punch Bushing

    The last time I punched a hard drive platter, I lathe-turned a bushing to center the Greenlee punch:

    Greenlee punched drive platter
    Greenlee punched drive platter

    This will work better:

    Vacuum Tube Lights - Greenlee punch bushing
    Vacuum Tube Lights – Greenlee punch bushing

    The OD centers the bushing inside the punch body, the ID captures the screw, and the raised boss captures the platter.

    After drilling the platter on the new fixture, it’s ready for punching:

    Hard drive platter - Greenlee punch bushing
    Hard drive platter – Greenlee punch bushing

    Line everything up, turn the screw, and It Just Works:

    Hard drive platter - punched
    Hard drive platter – punched

    The masking tape holds the platter to the bushing, eliminating the need for a third hand. The bushing emerges unscathed, ready for another platter. Overall, I think that’s faster and less messy than milling the platter ID on the Sherline.

    Printing out a base to fit the Duodecar socket and assembling all the parts:

    21HB5A in socket on platter - detail
    21HB5A in socket on platter – detail

    The Duodecar pin circle (19.1 BCD + 1.05 pin diameter) will actually fit inside a hard drive platter’s 25 mm unpunched ID. It might look a bit squinched, but the less you see of the socket, the better. I’ll try that on the next one.

    The OpenSCAD source code is the same as before; set Layout = Bushings; and a bushing will pop out.

    The original bushing doodle with dimensions:

    Greenlee 1.25 inch punch bushing for hard drive platter - dimension doodle
    Greenlee 1.25 inch punch bushing for hard drive platter – dimension doodle