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: Improvements

Making the world a better place, one piece at a time

  • Depth Gauge Mounting Rods

    Depth Gauge Mounting Rods

    A depth gauge arrived with a 3/8 inch = 9.5 mm mounting rod that fit one of my magnetic bases, but another base in my collection has a 5/16 inch = 7.9 mm clamp. Having recently rummaged through the aluminum rod stash, this happened:

    Depth Gauge mounting rods
    Depth Gauge mounting rods

    The original rod at the top has an M6 thread, the drawer of random M6 screws provided suitable volunteers, and a bit of lathe work removed / shaped their heads accordingly.

    The shorter rod has a blind hole, with a dab of epoxy holding the headless screw in place. Not that it matters, but the lathe held them in alignment for curing:

    Depth Gauge mounting rod - epoxy alignment
    Depth Gauge mounting rod – epoxy alignment

    The longer rod got drilled all the way through, with more epoxy holding the screw, and, even with a relatively loose fit, no worries about alignment.

    The longer rod gets the clamp away from the depth gauge’s base plate for better positioning:

    Depth Gauge mounting rod - in use
    Depth Gauge mounting rod – in use

    They’ll surely come in handy along the way …

  • Tour Easy: Bafang BBS02 Lower Power

    Tour Easy: Bafang BBS02 Lower Power

    It turns out Mary rarely used assist level 6 and had no use for levels 7 and 8 of my derated BBS02 configuration:

    LC=15
    ALC0=0
    ALC1=5
    ALC2=7
    ALC3=16
    ALC4=25
    ALC5=37
    ALC6=51
    ALC7=67
    ALC8=85
    ALC9=100
    

    Level 9 must be 100% of the maximum motor current so the throttle can apply full power to get out of the way in a hurry.

    The new and even more derated configuration allows small-step assist level selection for our usual riding, at the cost of an unused huge step to level 9 for the throttle:

    [Basic]
    LBP=42
    LC=18
    ALC0=0
    ALC1=4
    ALC2=6
    ALC3=9
    ALC4=15
    ALC5=20
    ALC6=25
    ALC7=30
    ALC8=40
    ALC9=100
    ALBP0=0
    ALBP1=100
    ALBP2=100
    ALBP3=100
    ALBP4=100
    ALBP5=100
    ALBP6=100
    ALBP7=100
    ALBP8=100
    ALBP9=100
    WD=12
    SMM=0
    SMS=1
    [Pedal Assist]
    PT=3
    DA=0
    SL=0
    SSM=4
    WM=0
    SC=20
    SDN=4
    TS=15
    CD=8
    SD=5
    KC=100
    [Throttle Handle]
    SV=11
    EV=42
    MODE=1
    DA=10
    SL=0
    SC=5
    

    The LC=18 line limits the maximum motor current to 18 A, rather than the rated 24 A, which may improve controller MOSFET longevity; reliable evidence is hard to come by. Controller failures seem to happen more often to riders who value jackrabbit acceleration on harsh terrain, so it may make little difference for road cyclists.

    So level 5 now selects 75% × 20% = 15% of the motor’s nominal 750 W:

    Tour Easy Bafang - display 26 mi
    Tour Easy Bafang – display 26 mi

    Call it 115 W: we’re both getting plenty of exercise!

  • Tour Easy 1 W Amber Running Light: Holder and First Light

    Tour Easy 1 W Amber Running Light: Holder and First Light

    Wrapping a left-side ball mount around the PVC case produced a holder:

    Fairing 1 W LED Mount - Left side - show view
    Fairing 1 W LED Mount – Left side – show view

    Which looks like this in real life:

    1 W Amber Running Light - installed front
    1 W Amber Running Light – installed front

    The support structure under the arch required a bit more cleanup than it got, so the clamp didn’t quite close around the ball on the first full test:

    1 W Amber Running Light - installed side
    1 W Amber Running Light – installed side

    Both the phone camera and the eyeballometer report the 1 W amber LED isn’t quite as bright as the 400 lumen Anker flashlight on its low setting:

    1 W Amber Running Light - First Light
    1 W Amber Running Light – First Light

    Stir the unusual (for a bike) amber color together with some blinkiness, though, and it’s definitely attention-getting.

    The OpenSCAD source code as a GitHub Gist:

    // Tour Easy Fairing Flashlight Mount
    // Ed Nisley KE4ZNU – July 2017
    // August 2017 –
    // August 2020 – add reinforcing columns under mount cradle
    // August 2021 – 1 W Amber LED
    /* [Build Options] */
    FlashName = "1WLED"; // [AnkerLC40,AnkerLC90,J5TactV2,InnovaX5,Sidemarker,Clearance,Laser,1WLED]
    Component = "BallClamp"; // [Ball, BallClamp, Mount, Plates, Bracket, Complete]
    Layout = "Build"; // [Build, Show]
    Support = true;
    MountSupport = true;
    /* [Hidden] */
    ThreadThick = 0.25; // [0.20, 0.25]
    ThreadWidth = 0.40; // [0.40]
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    Protrusion = 0.01; // [0.01, 0.1]
    HoleWindage = 0.2;
    /* [Fairing Mount] */
    Side = "Right"; // [Right,Left]
    ToeIn = -10; // inward from ahead
    Tilt = 20; // upward from forward (M=20 E=10)
    Roll = 0; // outward from top
    //- Screws and inserts
    /* [Hidden] */
    ID = 0;
    OD = 1;
    LENGTH = 2;
    /* [Hidden] */
    ClampInsert = [3.0,4.2,8.0];
    ClampScrew = [3.0,5.9,35.0]; // thread dia, head OD, screw length
    ClampScrewWasher = [3.0,6.75,0.5];
    ClampScrewNut = [3.0,6.1,4.0]; // nyloc nut
    /* [Hidden] */
    F_NAME = 0;
    F_GRIPOD = 1;
    F_GRIPLEN = 2;
    LightBodies = [
    ["AnkerLC90",26.6,48.0],
    ["AnkerLC40",26.6,55.0],
    ["J5TactV2",25.0,30.0],
    ["InnovaX5",22.0,55.0],
    ["Sidemarker",15.0,20.0],
    ["Clearance",50.0,20.0],
    ["Laser",10.0,30.0],
    ["1WLED",25.4,40.0],
    ];
    //- Fairing Bracket
    // Magic numbers taken from the actual fairing mount
    /* [Hidden] */
    inch = 25.4;
    BracketHoleOD = 0.25 * inch; // 1/4-20 bolt holes
    BracketHoleOC = 1.0 * inch; // fairing hole spacing
    // usually 1 inch, but 15/16 on one fairing
    Bracket = [48.0,16.3,3.6 – 0.6]; // fairing bracket end plate overall size
    BracketHoleOffset = (3/8) * inch; // end to hole center
    BracketM = 3.0; // endcap arc height
    BracketR = (pow(BracketM,2) + pow(Bracket[1],2)/4) / (2*BracketM); // … radius
    //- Base plate dimensions
    Plate = [100.0,30.0,6*ThreadThick + Bracket[2]];
    PlateRad = Plate[1]/4;
    RoundEnds = true;
    echo(str("Base plate thick: ",Plate[2]));
    //- Select flashlight data from table
    echo(str("Flashlight: ",FlashName));
    FlashIndex = search([FlashName],LightBodies,1,0)[F_NAME];
    //- Set ball dimensions
    BallWall = 5.0; // max ball wall thickness
    echo(str("Ball wall: ",BallWall));
    BallOD = IntegerMultiple(LightBodies[FlashIndex][F_GRIPOD] + 2*BallWall,1.0);
    echo(str(" OD: ",BallOD));
    BallLength = IntegerMultiple(min(sqrt(pow(BallOD,2) – pow(LightBodies[FlashIndex][F_GRIPOD],2)) – 2*4*ThreadThick,
    LightBodies[FlashIndex][F_GRIPLEN]),1.0);
    echo(str(" length: ",BallLength));
    BallSides = 8*4;
    //- Set clamp ring dimensions
    //ClampOD = 50;
    ClampOD = BallOD + 2*5;
    echo(str("Clamp OD: ",ClampOD));
    ClampLength = min(20.0,0.75*BallLength);
    echo(str(" length: ",ClampLength));
    ClampScrewOC = IntegerMultiple((ClampOD + BallOD)/2,1);
    echo(str(" screw OC: ",ClampScrewOC));
    TiltMirror = (Side == "Right") ? [0,0,0] : [0,1,0];
    //- Adjust hole diameter to make the size come out right
    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);
    }
    //- Fairing Bracket
    // This part of the fairing mount supports the whole flashlight mount
    // Centered on screw hole
    module Bracket() {
    linear_extrude(height=Bracket[2],convexity=2)
    difference() {
    translate([(Bracket[0]/2 – BracketHoleOffset),0,0])
    offset(delta=ThreadWidth)
    intersection() {
    square([Bracket[0],Bracket[1]],center=true);
    union() {
    for (i=[-1,0,1]) // middle circle fills gap
    translate([i*(Bracket[0]/2 – BracketR),0])
    circle(r=BracketR);
    }
    }
    circle(d=BracketHoleOD/cos(180/8),$fn=8); // dead center at the origin
    }
    }
    //- General plate shape
    // Centered in the middle of the plate
    module PlateBlank() {
    difference() {
    intersection() {
    translate([0,0,Plate[2]/2]) // select upper half of spheres
    cube(Plate,center=true);
    hull()
    if (RoundEnds)
    for (i=[-1,1])
    translate([i*(Plate[0]/2 – PlateRad),0,0])
    resize([Plate[1]/2,Plate[1],2*Plate[2]])
    sphere(r=PlateRad); // nice round ends!
    else
    for (i=[-1,1], j=[-1,1])
    translate([i*(Plate[0]/2 – PlateRad),j*(Plate[1]/2 – PlateRad),0])
    resize([2*PlateRad,2*PlateRad,2*Plate[2]])
    sphere(r=PlateRad); // nice round corners!
    }
    translate([BracketHoleOC,0,-Protrusion]) // punch screw holes
    PolyCyl(BracketHoleOD,2*Plate[2],8);
    translate([-BracketHoleOC,0,-Protrusion])
    PolyCyl(BracketHoleOD,2*Plate[2],8);
    }
    }
    //- Inner plate
    module InnerPlate() {
    difference() {
    PlateBlank();
    translate([-BracketHoleOC,0,Plate[2] – Bracket[2] + Protrusion]) // punch fairing bracket
    Bracket();
    }
    }
    //- Outer plate
    // With optional legend for orientation and parameters
    module OuterPlate(Legend = true) {
    TextRotate = (Side == "Left") ? 0 : 180;
    difference() {
    PlateBlank();
    if (Legend)
    mirror([0,1,0])
    translate([0,0,-Protrusion])
    linear_extrude(height=3*ThreadThick + Protrusion) {
    translate([BracketHoleOC + 15,0,0])
    text(text=">>>",size=5,spacing=1.20,font="Arial",halign="center",valign="center");
    translate([-BracketHoleOC,8,0]) rotate(TextRotate)
    text(text=str("Toe ",ToeIn),size=5,spacing=1.20,font="Arial",halign="center",valign="center");
    translate([-BracketHoleOC,-8,0]) rotate(TextRotate)
    text(text=str("Tilt ",Tilt),size=5,spacing=1.20,font="Arial",halign="center",valign="center");
    translate([BracketHoleOC,-8,0]) rotate(TextRotate)
    text(text=Side,size=5,spacing=1.20,font="Arial",halign="center",valign="center");
    translate([BracketHoleOC,8,0]) rotate(TextRotate)
    text(text=str("Roll ",Roll),size=5,spacing=1.20,font="Arial",halign="center",valign="center");
    translate([0,0,0])
    rotate(90)
    text(text="KE4ZNU",size=4,spacing=1.20,font="Arial",halign="center",valign="center");
    }
    }
    }
    //- Slotted ball around flashlight
    // Print with brim to ensure adhesion!
    module SlotBall() {
    NumSlots = 8*2; // must be even, half cut from each end
    SlotWidth = 2*ThreadWidth;
    SlotBaseThick = 10*ThreadThick; // enough to hold finger ends together
    RibLength = (BallOD – LightBodies[FlashIndex][F_GRIPOD])/2;
    translate([0,0,(Layout == "Build") ? BallLength/2 : 0])
    rotate([0,(Layout == "Show") ? 90 : 0,0])
    difference() {
    intersection() {
    sphere(d=BallOD,$fn=2*BallSides); // basic ball
    cube([2*BallOD,2*BallOD,BallLength],center=true); // trim to length
    }
    translate([0,0,-LightBodies[FlashIndex][F_GRIPOD]])
    rotate(180/BallSides)
    PolyCyl(LightBodies[FlashIndex][F_GRIPOD],2*BallOD,BallSides); // remove flashlight body
    for (i=[0:NumSlots/2 – 1]) { // cut slots
    a=i*(2*360/NumSlots);
    SlotCutterLength = LightBodies[FlashIndex][F_GRIPOD];
    rotate(a)
    translate([SlotCutterLength/2,0,SlotBaseThick])
    cube([SlotCutterLength,SlotWidth,BallLength],center=true);
    rotate(a + 360/NumSlots)
    translate([SlotCutterLength/2,0,-SlotBaseThick])
    cube([SlotCutterLength,SlotWidth,BallLength],center=true);
    }
    }
    color("Yellow")
    if (Support && (Layout == "Build")) {
    for (i=[0:NumSlots-1]) {
    a = i*360/NumSlots;
    rotate(a + 180/NumSlots)
    translate([(LightBodies[FlashIndex][F_GRIPOD] + RibLength)/2 + ThreadWidth,0,BallLength/(2*4)])
    cube([RibLength,2*ThreadWidth,BallLength/4],center=true);
    }
    }
    }
    //- Clamp around flashlight ball
    BossLength = ClampScrew[LENGTH] – 1*ClampScrewWasher[LENGTH];
    BossOD = ClampInsert[OD] + 2*(6*ThreadWidth);
    module BallClamp(Section="All") {
    difference() {
    union() {
    intersection() {
    sphere(d=ClampOD,$fn=BallSides); // exterior ball clamp
    cube([ClampLength,2*ClampOD,2*ClampOD],center=true); // aiming allowance
    }
    hull()
    for (j=[-1,1])
    translate([0,j*ClampScrewOC/2,-BossLength/2])
    cylinder(d=BossOD,h=BossLength,$fn=6);
    }
    sphere(d=(BallOD + 1*ThreadThick),$fn=BallSides); // interior ball with minimal clearance
    for (j=[-1,1]) {
    translate([0,j*ClampScrewOC/2,-ClampOD]) // screw clearance
    PolyCyl(ClampScrew[ID],2*ClampOD,6);
    translate([0,j*ClampScrewOC/2, // insert clearance
    -0*(BossLength/2 – ClampInsert[LENGTH] – 3*ThreadThick) + Protrusion])
    rotate([0,180,0])
    PolyCyl(ClampInsert[OD],2*ClampOD,6);
    translate([0,j*ClampScrewOC/2, // insert transition
    -(BossLength/2 – ClampInsert[LENGTH] – 3*ThreadThick)])
    cylinder(d1=ClampInsert[OD]/cos(180/6),d2=ClampScrew[ID],h=6*ThreadThick,$fn=6);
    }
    if (Section == "Top")
    translate([0,0,-ClampOD/2])
    cube([2*ClampOD,2*ClampOD,ClampOD],center=true);
    else if (Section == "Bottom")
    translate([0,0,ClampOD/2])
    cube([2*ClampOD,2*ClampOD,ClampOD],center=true);
    }
    color("Yellow")
    if (Support) { // ad-hoc supports
    NumRibs = 6;
    RibLength = 0.5 * BallOD;
    RibWidth = 1.9*ThreadWidth;
    SupportOC = ClampLength / NumRibs;
    if (Section == "Top") // base plate for adhesion
    translate([0,0,ThreadThick])
    cube([ClampLength + 6*ThreadWidth,RibLength,2*ThreadThick],center=true);
    else if (Section == "Bottom")
    translate([0,0,-ThreadThick])
    cube([ClampLength + 6*ThreadWidth,RibLength,2*ThreadThick],center=true);
    render(convexity=2*NumRibs)
    intersection() {
    sphere(d=BallOD – 0*ThreadWidth); // cut at inner sphere OD
    cube([ClampLength + 2*ThreadWidth,RibLength,BallOD],center=true);
    if (Section == "Top") // select only desired section
    translate([0,0,ClampOD/2])
    cube([2*ClampOD,2*ClampOD,ClampOD],center=true);
    else if (Section == "Bottom")
    translate([0,0,-ClampOD/2])
    cube([2*ClampOD,2*ClampOD,ClampOD],center=true);
    union() { // ribs for E-Z build
    for (j=[-1,0,1])
    translate([0,j*SupportOC,0])
    cube([ClampLength,RibWidth,1.0*BallOD],center=true);
    for (i=[0:NumRibs]) // allow NumRibs + 1 to fill the far end
    translate([i*SupportOC – ClampLength/2,0,0])
    rotate([0,90,0])
    cylinder(d=BallOD – 2*ThreadThick,
    h=RibWidth,$fn=BallSides,center=true);
    }
    }
    }
    }
    //- Mount between fairing plate and flashlight ball
    // Build with support for bottom of clamp screws!
    module Mount() {
    MountShift = [ClampOD*sin(ToeIn/2),0,ClampOD/2];
    OuterPlate();
    mirror(TiltMirror) {
    intersection() {
    translate(MountShift)
    rotate([-Roll,ToeIn,Tilt])
    BallClamp("Bottom");
    translate([0,0,Plate.x/2 + 3*ThreadThick])
    cube(Plate.x,center=true);
    }
    if (MountSupport) // anchor outer corners at worst overhang
    color("Yellow") {
    RibWidth = 1.9*ThreadWidth;
    SupportOC = 0.1 * ClampLength;
    intersection() {
    difference() {
    rotate([0,0,Tilt])
    translate([(ClampOD – BallOD)*sin(ToeIn/2),0,3*ThreadThick]) // Z = avoid legends
    for (i=[-4.5,-2.5,0,2.0,4.5])
    translate([i*SupportOC – 0.0,0,(5 + Plate[2])/2])
    cube([RibWidth,0.7*ClampOD,(5 + Plate[2])],center=true);
    translate(MountShift)
    rotate([-Roll,ToeIn,Tilt])
    sphere(d=ClampOD – 2*ThreadWidth,$fn=BallSides);
    }
    translate([0,0,ClampOD/2])
    cube([Plate.x,Plate.y,ClampOD],center=true);
    }
    }
    }
    }
    //- Build things
    if (Component == "Bracket")
    Bracket();
    if (Component == "Ball")
    SlotBall();
    if (Component == "BallClamp")
    if (Layout == "Show")
    BallClamp("All");
    else if (Layout == "Build")
    BallClamp("Top");
    if (Component == "Mount")
    Mount();
    if (Component == "Plates") {
    translate([0,0.7*Plate[1],0])
    InnerPlate();
    translate([0,-0.7*Plate[1],0])
    OuterPlate(Legend = false);
    }
    if (Component == "Complete") {
    OuterPlate();
    mirror(TiltMirror) {
    translate([0,0,ClampOD/2 + BossOD*abs(sin(ToeIn))]) {
    rotate([-Roll,ToeIn,Tilt])
    SlotBall();
    rotate([-Roll,ToeIn,Tilt])
    BallClamp();
    }
    }
    }
  • Running Light: 1 W LED Switched Parallel Resistors

    Running Light: 1 W LED Switched Parallel Resistors

    Manually selecting the current through the 1 W amber LED with a switch actually intended for LED flashlights:

    1 W LED Running Light - switched parallel R
    1 W LED Running Light – switched parallel R

    The resistors on the low side of the LED use the MP1584 regulator for current control, with the orange wire feeding the resistor voltage into the error amplifier.

    The 15 Ω unswitched resistor sets the LED current at 53 mA = 0.8 V / 15 Ω, with the LED dissipating about 100 mW. The resistor dissipates 43 mW.

    Closing the switch puts the two parallel 4.7 Ω resistors in parallel with the 15 Ω resistor to produce 2.0 Ω, which sets the LED current to 390 mA and runs it at 950 mW. Each of the 4.7 Ω resistors dissipates 140 mW.

    That much power raises the aluminum body to 50 °C = 120 °F: definitely uncomfortable but probably survivable for the LED inside.

    Eyeballometrically, a decimal order of magnitude difference in the LED current produces an obvious brightness difference. My first try ran the LED at 500 mW (a binary order of magnitude less than 1 W) and wasn’t visually different. Given that the LED will run from the Bafang’s headlight output, saving power isn’t all that important.

    If this is the first time you’ve encountered parallel resistors, this is why your calculator has a reciprocal button: the total resistance is the reciprocal of the sum of the reciprocals of all the resistances:

    1/R = 1/R₁ + 1/R₂ + …

    A real engineering calculator does not have a shifted reciprocal function.

  • Running Light: 1 W LED Heatsink

    Running Light: 1 W LED Heatsink

    The general idea: a cylindrical holder / heatsink for a 1 W LED on the end of a tube clamped in a Tour Easy fairing mount, much like a flashlight.

    A pleasant evening at a virtual Squidwrench meeting produced the raw shape of the front end from a 1 inch aluminum rod:

    1 W LED Running Light - heatsink raw
    1 W LED Running Light – heatsink raw

    Trace the outline of the LED’s PCB inside the cylinder just for comfort, align to the center, and drill two holes with a little bit of clearance:

    1 W LED Running Light - heatsink drilling
    1 W LED Running Light – heatsink drilling

    For the 24 AWG silicone wire I used, a pair of 2 mm holes 8.75 mm out from the center suffice:

    1 W LED Running Light - heatsink fit
    1 W LED Running Light – heatsink fit

    Gnaw some wire clearance in the lens holder:

    1 W LED Running Light - wiring
    1 W LED Running Light – wiring

    Tap the central hole for an M3×0.5 screw, which may come in handy to pull the entire affair together.

    Epoxy the PCB onto the heatsink with the lens holder keeping it aligned in the middle:

    1 W LED Running Light - heatsink clamp
    1 W LED Running Light – heatsink clamp

    Then see how hot it gets dissipating 900 mW with 360 mA of current from a 2.2 Ω resistor:

    1 W LED Running Light - heatsink test
    1 W LED Running Light – heatsink test

    As you might expect, it gets uncomfortably warm sitting on the bench, so it lacks surface area. The first pass will use a PVC cylinder for easy machining, but a full aluminum shell would eventually be a nice touch.

    A doodle with some dimensions and aspirational features:

    Running Light - 1 W LED case doodle
    Running Light – 1 W LED case doodle

    Even without a lens and blinkiness, it’s attention-getting!

  • Sticky Trap Results

    Sticky Trap Results

    In late May we deployed six sticky traps in and around the onion bed, attempting to reduce the number of Onion Fly maggots. By mid-June the sheets were covered with the shredded leaves Mary uses to mulch the onions, but half a dozen flies were out of action:

    Sticky trap - 2021-06
    Sticky trap – 2021-06

    We’re pretty sure that’s what these things are:

    Sticky trap - Onion Fly - 2021-06
    Sticky trap – Onion Fly – 2021-06

    They’re supposed to have red eyes, but being affixed to a sheet of snot for a few weeks doesn’t do the least bit of good for your eyes.

    We replaced the sheets and left them in place until the end of July:

    Sticky trap - 2021-07
    Sticky trap – 2021-07

    The sheets took another half-dozen flies out of circulation, Mary began harvesting the onions, and observed it was the healthiest onion harvest she’s ever had.

    We declared victory, removed the traps, and the remaining onions suffered considerable maggot damage over the next few weeks.

    Anecdotally, it seems reducing the Onion Fly population by (what seems to be) a small amount and maintaining pressure on the population dramatically reduces the number of maggots available to damage the onion crop. At least for a single bed in a non-commercial setting.

    The plural of anecdote is not anecdata, but we’ll try it again next year, leave the traps in place while the onions are in the ground, and see what happens.

  • MP1584 Current Regulator: Arduino Blinkiness

    MP1584 Current Regulator: Arduino Blinkiness

    Mostly because I wanted to verify that it really worked:

    MP1584 current - red LED - Arduino blinkiness
    MP1584 current – red LED – Arduino blinkiness

    The Arduino Nano runs the default Blink program that all the knockoff manufacturers use as their final QC test.

    The MP1584 specs say the Enable input can accept a logic signal up to 6 V, the Nano runs at 5 V regulated down from the 6.3 V from the bench supply, and the 1 W red LED now flashes 1 s ON / 1 s OFF.

    The current feedback works as it did before, too, which is comforting.

    The Nano adds 20 mA to the bench supply, so the whole affair runs at 220 mA = 1.4 W. Of course, it’s now at a 50% duty cycle, so that helps.

    I doubt hand-hewing an astable multivibrator is the right way to add blinkiness, but it’d definitely be playing on hard mode.