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.

Tag: Art-ish

They might be Art

  • High Impact Art(ifact)

    High Impact Art(ifact)

    At first we thought a mighty crunch in the morning meant the trash collection truck had dropped a garbage bin from a great height, but the sound of sirens and a myriad flashing lights revealed the true cause in our neighbor’s front yard:

    NHR Crash - frontal view
    NHR Crash – frontal view

    The extent of the damage was more apparent from the road side:

    NHR Crash - passenger side
    NHR Crash – passenger side

    Another one that ain’t gonna buff right out.

    The driver was walking around uninjured and the ambulance left quietly.

    A day later, the trajectory became apparent:

    NHR Crash - trajectory
    NHR Crash – trajectory

    The right side barely kissed the tree on the right, but the front wheel hooked the utility pole (that’s the new pole in the picture), snapped it off at ground level in addition to the usual break maybe ten feet up, and bounced a piece off the other tree:

    NHR Crash - utility pole
    NHR Crash – utility pole

    I didn’t know you could shatter a cast aluminum alloy wheel, but the missing half of the outer face was lying amid the rather scrambled stone wall along driveway.

    We’re reasonably sure we know the cause. Feel free to draw your own conclusions.

    After the flatbed hauled away the car and everybody left, I harvested a few pounds of interesting debris from the lawn:

    NHR Crash - tempered glass
    NHR Crash – tempered glass

    It’s tempered glass from the driver-side windows, shattered into small chunks and barely hanging together in those sheets. Laminated windshield glass is entirely different stuff.

    The smaller chunks glitter like jewels:

    NHR Crash - tempered glass fragments
    NHR Crash – tempered glass fragments

    Obviously, the window had a bit of tint.

    The smallest chunk, seen from its flat surface, shows the cuboid fragments:

    NHR Crash - tempered glass fragment - front
    NHR Crash – tempered glass fragment – front

    A side view shows more complexity:

    NHR Crash - tempered glass fragment - side
    NHR Crash – tempered glass fragment – side

    Tempering prevents a glass sheet from shattering into long knife-blade shards. Although the edges of the fragments are not keen, we are dealing with broken glass: they are sharp.

    How sharp? They make glass knives for slicing eyes and cells.

    Broken tempered glass also sheds razor-edged flakes perfectly shaped to penetrate bike tires, although most roadside glass comes from ordinary beverage bottles. The tiniest flakes can make a mess of your eyes, so exercise at least some rudimentary shop safety practices.

    Those slabs ought to be good for something, even if they fall apart at the slightest touch …

  • Vacuum Tube Lights: Urethane Coated Plate Cap

    Vacuum Tube Lights: Urethane Coated Plate Cap

    With a generous dollop of JB Plastic Bonder left over from a set of Bafang brake sensor magnets, I tried coating the ersatz plate cap of a triode tube:

    Triode - urethane coated plate cap
    Triode – urethane coated plate cap

    That’s the result after leaving it hanging upside-down while it cured to push all the drips to the top.

    For comparison, the uncoated cap back in the day:

    Triode - plate cap plug
    Triode – plate cap plug

    Seeing as how the urethane is an adhesive, not a coating, I’d say it looks about as bad as expected.

    As with all 3D printed things, one must embrace imperfections and striations, rather than endlessly strive for perfection.

    Now, if I had a resin printer …

  • Discrete LM3909: Blue LED

    Discrete LM3909: Blue LED

    Once again, the discrete LM3909 circuitry can blink a blue LED while running a pair of alkaline cells all the way down to about 1 V, with one cell ending at 0.2 V and the other at 0.8 V. They started out discharged to 1.2 V each during their useful life, then blinked for a month; it’s as good a use for dead cells as I can think of.

    With another pair of not-dead-yet cells providing 2.4 V, it started up again:

    Blue LM3909 2.4V alkaline - 042
    Blue LM3909 2.4V alkaline – 042

    That’s a frame from a short video taken in subdued light, just to show it really does work.

  • Astable Multivibrator: Red RGB Piranha

    Astable Multivibrator: Red RGB Piranha

    A red LED has a sufficiently low forward voltage to run with a MOSFET astable multivibrator and a pair of run-down AA alkaline cells:

    Astable AA Alkaline - red
    Astable AA Alkaline – red

    The red LED is actually part of an RGB Piranha, just to see how it compares to an as-yet-unbuilt version with a single red LED in the same package.

    The LED drops 1.9 V of the 2.75 V from the mostly used-up AA cells:

    Astable Piranha Red - 2.75 alkaline - V LED
    Astable Piranha Red – 2.75 alkaline – V LED

    The original 33 Ω ballast resistor showed a peak current of 11 mA in a 30 ms pulse:

    Astable Piranha Red - 2.75 alkaline - V 33 ohm
    Astable Piranha Red – 2.75 alkaline – V 33 ohm

    Replacing it with a 12 Ω resistor boosts the current all the way to 12 mA:

    Astable Piranha Red - 2.75 alkaline - V 12 ohm
    Astable Piranha Red – 2.75 alkaline – V 12 ohm

    The 2N7000 gate sees a just bit more than 2 V, barely enough to get the poor thing conducting, which makes the ballast resistor mostly decorative. The MOSFET datasheet puts its 1 mA threshold somewhere between 0.8 and 3 V, so it could be worse.

    Keep in mind the DSO150’s 1 MΩ input impedance sat in parallel with the 1 MΩ gate pulldown resistor forming the RC differentiator when I measured the gate voltage; I’ll leave the simulation as an exercise for the interested reader. The blinks were noticeably dimmer and perhaps a bit shorter, although eyeballometric calibration is notoriously hard.

    The slightly revised schematic-layout doodle stacks the transistors along the negative bus bar:

    Astable wiring layout - stacked 2N7000
    Astable wiring layout – stacked 2N7000

    Flipping the bottom transistor over to snuggle the two timing caps next to each other would eliminate the long jumper wire and probably look better.

  • Astable Multivibrator: Amber LED

    Astable Multivibrator: Amber LED

    Adding an amber LED to the collection:

    Astable AA - Amber - overview
    Astable AA – Amber – overview

    Because a yellow / amber LED runs at a lower voltage than blue and green LEDs, it sits atop an astable multivibrator, rather than a discrete LM3909. The battery holder has a pair of carbon-zinc “Extra-Heavy Duty” AAA cells, so corrosion and leakage pose a foreseeable hazard.

    The voltage across the 100 Ω LED ballast indicates a 9 mA peak LED current, which is somewhat dim in ordinary room light:

    Astable AA - Amber - LED current 100 ohm
    Astable AA – Amber – LED current 100 ohm

    The corresponding LED voltage says the LED runs at 2.1 V for that much current:

    Astable AA - Amber - LED V
    Astable AA – Amber – LED V

    Something around 39 Ω should make it more visible.

  • Astable Multivibrator: Dressed-up LED Spider

    Astable Multivibrator: Dressed-up LED Spider

    Adding a bit of trim to the bottom of the LED spider makes it look better and helps keep the strut wires in place:

    Astable Multivibrator - Alkaline - Radome trim
    Astable Multivibrator – Alkaline – Radome trim

    It’s obviously impossible to build like that, so it’s split across the middle of the strut:

    Astable Multivibrator - Alkaline - Radome trim
    Astable Multivibrator – Alkaline – Radome trim

    Glue it together with black adhesive and a couple of clamps:

    LED Spider - glue clamping
    LED Spider – glue clamping

    The aluminum fixtures (jigs?) are epoxied around snippets of strut wire aligning the spider parts:

    LED Spider - gluing fixture
    LED Spider – gluing fixture

    Those grossly oversized holes came pre-drilled in an otherwise suitable aluminum rod from the Little Tray o’ Cutoffs. I faced off the ends, chopped the rod in two, recessed the new ends, and declared victory. Might need better ones at some point, but they’ll do for now.

    Next step: wire up an astable with a yellow LED to go with the green and blue boosted LEDs.

  • Discrete LM3909: Blue LED Radome

    Discrete LM3909: Blue LED Radome

    Dropping a simplified ping-pong ball radome for a Piranha RGB LED atop a discrete LM3909 on the AA alkaline cell holder:

    Discrete LM3909 Radome - AA alkaline
    Discrete LM3909 Radome – AA alkaline

    The solid model has screw holes for the lid and the revised LED spider:

    Astable Multivibrator - Alkaline AA Base - radome - solid model
    Astable Multivibrator – Alkaline AA Base – radome – solid model

    The RGB LED needs only two wires, as the LM3909 circuit can blink only one LED. I tried all three colors, but only blue and green justify the LM3909 hairball; red can get along with the astable circuit.

    The LED wires connect across a 1 MΩ resistor serving as a mechanical strut between the 9.1 kΩ resistor on the left and the 10 Ω ballast resistor on the right.

    Fresh alkaline cells at 3.0 V put 3.3 V across the blue LED with a 37 mA peak current. Older cells at 2.3 V produce 2.9 V at 15 mA. Dead cells at 1.9 V still fire the LED with 2.7 V at 4.2 mA, although the flash is barely visible in ordinary room light.

    The lovely blue ball looks better in person!

    The OpenSCAD source code as a GitHub Gist:

    // Astable Multivibrator
    // Holder for Alkaline cells
    // Ed Nisley KE4ZNU August 2020
    // 2020-09 add LED radome
    /* [Layout options] */
    Layout = "Build"; // [Build,Show,Lid,Spider]
    /* [Hidden] */
    CellName = "AA"; // [AA] — does not work with anything else
    NumCells = 2; // [2] — likewise
    Struts = -1; // [0:None, -1:Dual, 1:Quad] — Quad is dead
    // Extrusion parameters
    /* [Hidden] */
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    HoleWindage = 0.2;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    function IntegerLessMultiple(Size,Unit) = Unit * floor(Size / Unit);
    Protrusion = 0.1; // make holes end cleanly
    inch = 25.4;
    //- Basic dimensions
    WallThick = IntegerMultiple(3.0,ThreadWidth);
    CornerRadius = WallThick/2;
    FloorThick = IntegerMultiple(3.0,ThreadThick);
    TopThick = IntegerMultiple(2.0,ThreadThick);
    WireOD = 1.5; // battery & LED wiring
    WireOC = 4;
    Gap = 5.0;
    // Cylindrical cell sizes
    // https://en.wikipedia.org/wiki/List_of_battery_sizes#Cylindrical_batteries
    CELL_NAME = 0;
    CELL_OD = 1;
    CELL_OAL = 2;
    // FIXME search() needs special-casing to properly find AAA and AAAA
    // Which is why CellName is limited to AA
    CellData = [
    ["AAAA",8.3,42.5],
    ["AAA",10.5,44.5],
    ["AA",14.5,50.5],
    ["C",26.2,50],
    ["D",34.2,61.5],
    ["A23",10.3,28.5],
    ["CR123A",17.0,34.5],
    ["18650",18.8,65.2], // bare 18650 with button end
    ["18650Prot",19.0,70.0], // protected 18650 = 19670 plus a bit
    ];
    CellIndex = search([CellName],CellData,1,0)[0];
    echo(str("Cell index: ",CellIndex," = ",CellData[CellIndex][CELL_NAME]));
    //- Contact dimensions
    CONTACT_NAME = 0;
    CONTACT_WIDE = 1;
    CONTACT_HIGH = 2;
    CONTACT_THICK = 3; // plate thickness
    CONTACT_TIP = 4; // tip to rear face
    CONTACT_TAB = 5; // solder tab width
    ContactData = [
    ["AA+",12.2,12.2,0.3,1.7,3.5], // pos bump
    ["AA-",12.2,12.2,0.3,5.0,3.5], // half-compressed neg spring
    ["AA+-",28.2,12.2,0.3,5.0,0], // pos-neg bridge
    ["Li+",18.5,16.0,0.3,2.8,5.5],
    ["Li-",18.5,16.0,0.3,6.0,5.5],
    ];
    function ConDat(name,dim) = ContactData[search([name],ContactData,1,0)[0]][dim];
    ContactRecess = 2*ConDat(str(CellName,"+"),CONTACT_THICK);
    ContactOC = CellData[CellIndex][CELL_OD];
    WireBay = 6.0; // room for wiring to contacts
    //- Wire struts
    StrutDia = 1.6; // AWG 14 = 1.6 mm
    StrutSides = 3*4;
    ID = 0;
    OD = 1;
    LENGTH = 2;
    StrutBase = [StrutDia,StrutDia + 2*5*ThreadWidth, // ID = wire, OD = buildable
    FloorThick + CellData[CellIndex][CELL_OD]]; // LENGTH = base is flush with cell top
    //- Holder dimensions
    BatterySize = [CellData[CellIndex][CELL_OAL] + // cell
    ConDat(str(CellName,"+"),CONTACT_TIP) + // pos contact
    ConDat(str(CellName,"-"),CONTACT_TIP) – // neg contact
    2*ContactRecess, // sink into wall
    NumCells*CellData[CellIndex][CELL_OD],
    CellData[CellIndex][CELL_OD]
    ];
    echo(str("Battery space: ",BatterySize));
    CaseSize = [3*WallThick + // end walls + wiring partition
    BatterySize.x + // cell
    WireBay, // wiring bay
    2*WallThick + BatterySize.y,
    FloorThick + BatterySize.z
    ];
    BatteryOffset = (CaseSize.x – (2*WallThick +
    CellData[CellIndex][CELL_OAL] +
    ConDat(str(CellName,"-"),CONTACT_TIP))
    ) /2 ;
    ThumbRadius = 0.75 * CaseSize.z;
    StrutOC = [IntegerLessMultiple(CaseSize.x – 2*CornerRadius -2*StrutBase[OD],5.0),
    IntegerMultiple(CaseSize.y + StrutBase[OD],5.0)];
    StrutAngle = atan(StrutOC.y/StrutOC.x);
    echo(str("Strut OC: ",StrutOC));
    LidSize = [2*WallThick + WireBay + ConDat(str(CellName,"+"),CONTACT_THICK), CaseSize.y, FloorThick/2];
    LidScrew = [2.0,3.8,7.0]; // M2 pan head screw (LENGTH = threaded)
    LidScrewOC = CaseSize.y/2 – CornerRadius – LidScrew[OD]; // allow space around screw head
    //- Piranha LEDs
    PiranhaBody = [8.0,8.0,8.0]; // Z = heatsink fins + body + lens height
    PiranhaPin = 0.0; // trimmed pin length beyond heatsink
    PiranhaPinsOC = [5.0,5.0]; // pin XY distance
    PiranhaRecess = PiranhaBody.z + PiranhaPin/2; // minimum LED recess depth
    BallOD = 40.0; // radome sphere
    BallSides = 4*StrutSides; // nice smoothness
    BallPillar = [norm([PiranhaBody.x,PiranhaBody.y]), // ID
    norm([PiranhaBody.x,PiranhaBody.y]) + 3*WallThick, // OD
    StrutBase[OD] + PiranhaBody.z]; // height to base of chord
    echo(str("Pillar OD: ",BallPillar[OD]));
    BallChordM = BallOD/2 – sqrt(pow(BallOD/2,2) – (pow(BallPillar[OD],2))/4);
    echo(str("Ball chord depth: ",BallChordM));
    //———————-
    // 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);
    }
    // Spider for single LED atop struts, with the ball
    module DualSpider() {
    difference() {
    union() {
    for (j=[-1,1]) {
    translate([0,j*StrutOC.y/2,StrutBase[OD]/2])
    rotate(180/StrutSides)
    sphere(d=StrutBase[OD]/cos(180/StrutSides),$fn=StrutSides);
    translate([0,j*StrutOC.y/2,0])
    rotate(180/StrutSides)
    cylinder(d=StrutBase[OD],h=StrutBase[OD]/2,$fn=StrutSides);
    }
    translate([0,0,StrutBase[OD]/4]) // connecting bars
    cube([StrutBase[OD]*cos(180/StrutSides),StrutOC.y,StrutBase[OD]/2],center=true);
    cylinder(d=BallPillar[OD],h=BallPillar[LENGTH],$fn=BallSides);
    }
    for (j=[-1,1]) // strut wires
    translate([0,j*StrutOC.y/2,-Protrusion])
    PolyCyl(StrutBase[ID],StrutBase[OD]/2,6);
    for (n=[-1,1]) // LED wiring
    rotate(n*90)
    translate([StrutOC.x/3,0,-Protrusion])
    PolyCyl(StrutBase[ID],StrutBase[OD],6);
    translate([0,0,BallOD/2 + BallPillar[LENGTH] – BallChordM]) // ball inset
    sphere(d=BallOD);
    translate([0,0,BallPillar.z – PiranhaRecess + BallPillar.z/2]) // LED inset
    cube(PiranhaBody + [HoleWindage,HoleWindage,BallPillar.z],center=true); // XY clearance
    translate([0,0,StrutBase[OD]/2 + WireOD/2 + 0*Protrusion]) // wire channels
    cube([WireOD,BallPillar[OD] + 2*WallThick,WireOD],center=true);
    }
    }
    //– Overall case with origin at battery center
    module Case() {
    union() {
    difference() {
    union() {
    hull()
    for (i=[-1,1], j=[-1,1])
    translate([i*(CaseSize.x/2 – CornerRadius),
    j*(CaseSize.y/2 – CornerRadius),
    0])
    cylinder(r=CornerRadius/cos(180/8),h=CaseSize.z,$fn=8); // cos() fixes undersize spheres!
    if (Struts)
    for (i = (Struts == 1) ? [-1,1] : -1) { // strut bases
    hull()
    for (j=[-1,1])
    translate([i*StrutOC.x/2,j*StrutOC.y/2,0])
    rotate(180/StrutSides)
    cylinder(d=StrutBase[OD],h=StrutBase[LENGTH],$fn=StrutSides);
    translate([i*StrutOC.x/2,0,StrutBase[LENGTH]/2])
    cube([2*StrutBase[OD],StrutOC.y,StrutBase[LENGTH]],center=true); // blocks for fairing
    for (j=[-1,1]) // hemisphere caps
    translate([i*StrutOC.x/2,
    j*StrutOC.y/2,
    StrutBase[LENGTH]])
    rotate(180/StrutSides)
    sphere(d=StrutBase[OD]/cos(180/StrutSides),$fn=StrutSides);
    }
    }
    translate([BatteryOffset,0,BatterySize.z/2 + FloorThick]) // cells
    cube(BatterySize + [0,0,Protrusion],center=true);
    translate([BatterySize.x/2 + BatteryOffset + ContactRecess/2 – Protrusion/2, // contacts
    0,
    BatterySize.z/2 + FloorThick])
    cube([ContactRecess + Protrusion,
    ConDat(str(CellName,"+-"),CONTACT_WIDE),
    ConDat(str(CellName,"+-"),CONTACT_HIGH)
    ],center=true);
    translate([-(BatterySize.x/2 – BatteryOffset + ContactRecess/2 – Protrusion/2),
    ContactOC/2,
    BatterySize.z/2 + FloorThick])
    cube([ContactRecess + Protrusion,
    ConDat(str(CellName,"+"),CONTACT_WIDE),
    ConDat(str(CellName,"+"),CONTACT_HIGH)
    ],center=true);
    translate([-(BatterySize.x/2 – BatteryOffset + ContactRecess/2 – Protrusion/2),
    -ContactOC/2,
    BatterySize.z/2 + FloorThick])
    cube([ContactRecess + Protrusion,
    ConDat(str(CellName,"-"),CONTACT_WIDE),
    ConDat(str(CellName,"-"),CONTACT_HIGH)
    ],center=true);
    translate([-CaseSize.x/2 + WireBay/2 + WallThick, // wire bay with screw bosses
    0,
    BatterySize.z/2 + FloorThick + Protrusion/2])
    cube([WireBay,
    2*LidScrewOC – LidScrew[ID] – 2*4*ThreadWidth,
    BatterySize.z + Protrusion
    ],center=true);
    for (j=[-1,1]) // screw holes
    translate([-CaseSize.x/2 + WireBay/2 + WallThick,
    j*LidScrewOC,
    CaseSize.z – LidScrew[LENGTH] + Protrusion])
    PolyCyl(LidScrew[ID],LidScrew[LENGTH],6);
    for (j=[-1,1])
    translate([-(BatterySize.x/2 – BatteryOffset + WallThick/2), // contact tabs
    j*ContactOC/2,
    BatterySize.z + FloorThick – Protrusion])
    cube([2*WallThick,
    ConDat(str(CellName,"+"),CONTACT_TAB),
    (BatterySize.z – ConDat(str(CellName,"+"),CONTACT_HIGH))
    ],center=true);
    if (false)
    translate([0,0,CaseSize.z]) // finger cutout
    rotate([90,00,0])
    cylinder(r=ThumbRadius,h=2*CaseSize.y,center=true,$fn=22);
    translate([0,0,ThreadThick – Protrusion]) // recess around name
    cube([0.6*CaseSize.x,8,2*ThreadThick],center=true);
    if (Struts)
    for (i2 = (Struts == 1) ? [-1,1] : -1) { // strut wire holes and fairing
    for (j=[-1,1])
    translate([i2*StrutOC.x/2,j*StrutOC.y/2,FloorThick])
    rotate(180/StrutSides)
    PolyCyl(StrutBase[ID],2*StrutBase[LENGTH],StrutSides);
    for (i=[-1,1], j=[-1,1]) // fairing cutaways
    translate([i*StrutBase[OD] + (i2*StrutOC.x/2),
    j*StrutOC.y/2,
    -Protrusion])
    rotate(180/StrutSides)
    PolyCyl(StrutBase[OD],StrutBase[LENGTH] + 2*Protrusion,StrutSides);
    }
    }
    translate([0,0,0])
    linear_extrude(height=2*ThreadThick + Protrusion,convexity=10)
    mirror([0,1,0])
    text(text="KE4ZNU",size=6,spacing=1.20,font="Arial:style:Bold",halign="center",valign="center");
    }
    }
    module Lid() {
    difference() {
    hull()
    for (i=[-1,1], j=[-1,1], k=[-1,1])
    translate([i*(LidSize.x/2 – CornerRadius),
    j*(LidSize.y/2 – CornerRadius),
    k*(LidSize.z – CornerRadius)]) // double thickness for flat bottom
    sphere(r=CornerRadius/cos(180/8),$fn=8);
    translate([0,0,-LidSize.z]) // remove bottom
    cube([(LidSize.x + 2*Protrusion),(LidSize.y + 2*Protrusion),2*LidSize.z],center=true);
    for (j=[-1,1]) // wire holes
    translate([0,j*WireOC,-Protrusion])
    PolyCyl(WireOD,2*LidSize.z,6);
    for (j=[-1,1])
    translate([0,j*LidScrewOC,-Protrusion])
    PolyCyl(LidScrew[ID],2*LidSize.z,6);
    }
    }
    //——————-
    // Build it!
    if (Layout == "Case")
    Case();
    if (Layout == "Lid")
    Lid();
    if (Layout == "Spider")
    if (Struts == -1)
    DualSpider();
    else
    cube(10,center=true);
    if (Layout == "Build") {
    rotate(90)
    Case();
    translate([0,-(CaseSize.x/2 + LidSize.x/2 + Gap),0])
    rotate(90)
    Lid();
    if (Struts == -1)
    translate([CaseSize.x/2,0,0])
    DualSpider();
    }
    if (Layout == "Show") {
    Case();
    translate([-CaseSize.x/2 + LidSize.x/2,0,(CaseSize.z + Gap)])
    Lid();
    }