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

  • Tour Easy: Bafang 48 V 11.6 A·h Battery Mount

    Tour Easy: Bafang 48 V 11.6 A·h Battery Mount

    Bafang BBS02 batteries should mount on the water bottle bosses along a more-or-less standard bicycle’s downtube, which a Tour Easy recumbent has only in vestigial form. The battery does, however, fit perfectly along the lower frame tubes:

    Tour Easy Bafang mid-drive - battery
    Tour Easy Bafang mid-drive – battery

    You might be forgiven for thinking Gardner Martin (not to be confused with Martin Gardner of Scientific American fame) designed the Tour Easy frame specifically to hold that battery, but the design dates back to the 1970s and it’s just a convenient coincidence.

    The battery slides into a flat baseplate and locks in place, although it’s definitely not a high-security design. Mostly, the lock suffices to keep honest people honest and prevent the battery from vibrating loose while riding:

    Tour Easy Bafang battery mount - baseplate installed
    Tour Easy Bafang battery mount – baseplate installed

    The flat enclosure toward the rear was obviously designed for more complex circuitry than it now contains:

    Tour Easy Bafang battery mount - interior
    Tour Easy Bafang battery mount – interior

    Those are all neatly drilled and tapped M3 machine screw holes. The cable has no strain relief, despite the presence of suitable holes at the rear opening. I tucked the spare cable inside, rather than cut it shorter, under the perhaps unwarranted assumption they did a good job crimping / soldering the wires to the terminals.

    The red frame tubes are not parallel, so each of the four mounting blocks fits in only one location. They’re identified by the side-to-side tube measurement at their centerline and directional pointers:

    Bafang Battery Mount - Show bottom
    Bafang Battery Mount – Show bottom

    The first three blocks have a hole for the mounting screw through the battery plate. The central slot fits around the plate’s feature for the recessed screw head. The two other slots clear the claws extending downward from the battery into the plate:

    Bafang Battery Mount - Show view
    Bafang Battery Mount – Show view

    The rear block has a flat top and a recessed screw head, because the fancy metal enclosure doesn’t have a screw hole:

    Tour Easy Bafang battery mount - top detail
    Tour Easy Bafang battery mount – top detail

    I thought of drilling a hole through the plate, but eventually put a layer of carpet tape atop the block to encourage it to not slap around, as the whole affair isn’t particularly bendy. We’ll see how well it works on the road.

    I had intended to put an aluminum plate across the bottom to distribute the clamping force from the screw, but found a suitable scrap of the institutional-grade cafeteria tray we used as a garden cart seat:

    Tour Easy Bafang battery mount - bottom detail
    Tour Easy Bafang battery mount – bottom detail

    I traced around the block, bandsawed pretty close to the line, then introduced it to Mr Disk Sander for final shaping.

    The round cable runs from the rear wheel speed sensor through all four blocks to join the motor near the bottom bracket. Because a recumbent bike’s rear wheel is much further from its bottom bracket, what you see is actually an extension cable with a few extra inches doubled around its connection just ahead of the battery.

    Each of the four blocks takes about an hour to print, so I did them individually while making continuous process improvements to the solid model:

    Bafang Battery Mount - Build view
    Bafang Battery Mount – Build view

    The heavy battery cable runs along the outside of the left frame tube, with enough cable ties to keep it from flopping around:

    Tour Easy Bafang battery mount - bottom view
    Tour Easy Bafang battery mount – bottom view

    I wanted to fit it between the tubes, but there just wasn’t enough room around the screw in the front block where the tubes converge. It’s still pretty well protected and should be fine.

    The chainline worked out much better than I expected:

    Tour Easy Bafang battery mount - chainline
    Tour Easy Bafang battery mount – chainline

    That’s with the chain on the lowest (most inboard) rear sprocket, so it’s as close to the battery as it gets. I’m sure the battery will accumulate oily chain grime, as does everything else on a bike.

    Lithium batteries have a vastly higher power density than good old lead acid batteries, but seven pounds is still a lot of weight!

    The OpenSCAD source code as a GitHub Gist:

    // Tour Easy Bafang Battery Mount
    // Ed Nisley KE4ZNU 2021-04
    Layout = "Build"; // [Frame,Block,Show,Build,Bushing,Cateye]
    FrameWidths = [60.8,62.0,63.4,66.7]; // last = rear overhang support block
    Support = true;
    //- Extrusion parameters must match reality!
    /* [Hidden] */
    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);
    ID = 0;
    OD = 1;
    LENGTH = 2;
    //———-
    // Dimensions
    // Bike frame lies along X axis, rear to +X
    FrameTube = [350,22.6 + HoleWindage,22.6 + HoleWindage]; // X = longer than anything else
    FrameAngle = atan((65.8 – 59.4)/300); // measured distances = included angle between tubes
    TubeAngle = FrameAngle/2; // .. frame axis to tube
    FrameSides = 24;
    echo(str("Frame angle: ",FrameAngle));
    SpeedOD = 3.5; // speed sensor cable along frame
    PowerOD = 6.7; // power cable between frame tubes
    BatteryBoss = [5.5,16.0,2.5]; // battery mount boss, center is round
    BossSlotOAL = 32.0; // .. end bosses are elongated
    BossOC = 65.0; // .. along length of mount
    LatchWidth = 10.0; // battery latches to mount plate
    LatchThick = 1.5;
    LatchOC = 56.0;
    WallThick = 5.0; // thinnest wall
    Block = [25.0,78.0,FrameTube.z + 2*WallThick]; // must be larger than frame tube spacing
    echo(str("Block: ",Block));
    // M5 SHCS nyloc nut
    Screw = [5.0,8.5,5.0]; // OD, LENGTH = head
    Washer = [5.5,10.1,1.0];
    Nut = [5.0,9.0,5.0];
    // 10-32 Philips nyloc nut
    Screw10 = [5.2,9.8,3.6]; // OD, LENGTH = head
    Washer10 = [5.5,11.0,1.0];
    Nut10 = [5.2,10.7,6.2];
    Kerf = 1.0; // cut through middle to apply compression
    CornerRadius = 5.0;
    EmbossDepth = 2*ThreadThick; // lettering depth
    //———————-
    // 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);
    }
    // clamp overall shape
    module ClampBlock() {
    difference() {
    hull()
    for (i=[-1,1], j=[-1,1])
    translate([i*(Block.x/2 – CornerRadius),j*(Block.y/2 – CornerRadius),-Block.z/2])
    cylinder(r=CornerRadius,h=Block.z,$fn=4*8);
    translate([0,0,-(Block.z/2 + Protrusion)])
    rotate(0*180/6)
    PolyCyl(Screw[ID],Block.z + 2*Protrusion,6);
    cube([2*Block.x,2*Block.y,Kerf],center=true);
    translate([0,-(Block.y/2 – PowerOD + Protrusion/2),-PowerOD/2])
    cube([2*Block.x,2*PowerOD + Protrusion,PowerOD],center=true);
    }
    }
    // frame tube layout with measured side-to-side width
    module Frame(Outer = FrameWidths[0],AdjustDia = 0.0) {
    TubeOC = Outer – FrameTube.y/cos(TubeAngle); // increase dia for angle
    for (i=[-1,1])
    translate([0,i*TubeOC/2,0])
    rotate([0,90,i*TubeAngle]) rotate(180/FrameSides)
    cylinder(d=FrameTube.z + AdjustDia,h=FrameTube.x,center=true,$fn=FrameSides);
    }
    // complete clamp block
    module Clamp(Outer = FrameWidths[0]) {
    TubeOC = Outer – FrameTube.y/cos(TubeAngle); // increase dia for angle
    difference() {
    ClampBlock();
    Frame(Outer);
    translate([0,(TubeOC/2 – FrameTube[OD]/2),-SpeedOD/2])
    cube([2*Block.x,2*SpeedOD,SpeedOD],center=true);
    translate([0,15,Block.z/2 – EmbossDepth/2 + Protrusion])
    cube([9.0,8,EmbossDepth],center=true);
    translate([0,22,-Block.z/2 + EmbossDepth/2 – Protrusion])
    cube([9.0,26,EmbossDepth],center=true);
    if (Outer == FrameWidths[len(FrameWidths) – 1]) { // special rear block
    translate([0,0,Block.z/2 – 2*Screw10[LENGTH]])
    PolyCyl(Washer10[OD],2*Screw10[LENGTH] + Protrusion,6);
    }
    else { // other blocks have channels
    translate([0,0,Block.z/2 – BatteryBoss[LENGTH]/2 + Protrusion])
    cube([BossSlotOAL,BatteryBoss[OD],BatteryBoss[LENGTH] + Protrusion],center=true);
    for (i=[-1,1])
    translate([0,i*LatchOC/2,Block.z/2 – LatchThick/2 + Protrusion])
    cube([BossSlotOAL,LatchWidth,LatchThick + Protrusion],center=true);
    }
    }
    translate([0,15,Block.z/2 – EmbossDepth])
    linear_extrude(height=EmbossDepth)
    rotate(90)
    text(text="^",size=5,spacing=1.00,font="Bitstream Vera Sans:style=Bold",
    halign="center",valign="center");
    translate([0,22,-Block.z/2])
    linear_extrude(height=EmbossDepth)
    rotate(-90) mirror([0,1,0])
    text(text=str("^ ",Outer),size=4.5,spacing=1.00,font="Bitstream Vera Sans:style=Bold",
    halign="center",valign="center");
    if (Support)
    color("Yellow") {
    NumRibs = 7;
    RibOC = Block.x/(NumRibs – 1);
    intersection() {
    translate([0,0,Block.z/2 + Kerf/2])
    cube([2*Block.x,2*Block.y,Block.z],center=true);
    union() for (j=[-1,1]) {
    translate([0,j*TubeOC/2,Kerf/2])
    cube([1.1*Block.x,FrameTube.y – 2*ThreadThick,4*ThreadThick],center=true);
    for (i=[-floor(NumRibs/2):floor(NumRibs/2)])
    translate([i*RibOC,j*TubeOC/2,0])
    rotate([0,90,0]) rotate(180/FrameSides)
    cylinder(d=FrameTube.z – 2*ThreadThick,h=2*ThreadWidth,$fn=FrameSides,center=true);
    }
    }
    }
    }
    // Half clamp sections for printing
    module HalfClamp(i = 0, Section = "Upper") {
    render()
    intersection() {
    translate([0,0,Block.z/4])
    cube([Block.x,Block.y,Block.z/2],center=true);
    if (Section == "Upper")
    translate([0,0,-Kerf/2])
    Clamp(FrameWidths[i]);
    else
    translate([0,0,Block.z/2])
    Clamp(FrameWidths[i]);
    }
    }
    // Handlebar bushing for controller
    BushingSize = [16.0,22.2,15.0];
    module Bushing() {
    difference() {
    cylinder(d=BushingSize[OD],h=BushingSize[LENGTH],$fn=24);
    translate([0,0,-Protrusion])
    cylinder(d=BushingSize[ID],h=2*BushingSize[LENGTH],$fn=24);
    translate([0*(BushingSize[OD] – BushingSize[ID])/4,0,BushingSize[LENGTH]/2])
    cube([2*BushingSize[OD],2*ThreadWidth,2*BushingSize[LENGTH]],center=true);
    }
    }
    // Cateye cadence sensor bracket
    module Cateye() {
    Pivot = [3.0,10.0,8.0];
    Slot = [4.2,14.0,14.0];
    Clip = [8.0,Slot.y,Slot.z + Pivot[OD]/2];
    translate([0,0,Clip.z])
    difference() {
    union() {
    translate([0,0,-Clip.z/2])
    cube(Clip,center=true);
    translate([-Clip.x/2,0,0])
    rotate([0,90,0])
    cylinder(d=Clip.y,h=Clip.x,$fn=12);
    }
    translate([-Clip.x,0,0])
    rotate([0,90,0]) rotate(180/6)
    PolyCyl(3.0,2*Clip.x,6);
    translate([0,0,-(Clip.z – Slot.z/2)])
    cube(Slot + [0,Protrusion,Protrusion],center=true);
    }
    }
    //———-
    // Build them
    if (Layout == "Frame")
    Frame();
    if (Layout == "Block")
    ClampBlock();
    if (Layout == "Bushing")
    Bushing();
    if (Layout == "Cateye")
    Cateye();
    if (Layout == "Upper" || Layout == "Lower")
    HalfClamp(0,Layout);
    if (Layout == "Show") {
    Clamp();
    color("Red", 0.3)
    Frame();
    }
    if (Layout == "Build") {
    n = len(FrameWidths);
    gap = 1.2;
    for (i=[0:n-1]) {
    j = i – ceil((n-1)/2);
    translate([-gap*Block.y/2,j*gap*Block.x,0])
    rotate(90)
    HalfClamp(i,"Upper");
    translate([gap*Block.y/2,j*gap*Block.x,0])
    rotate([0,0,90])
    HalfClamp(i,"Lower");
    }
    }

  • Tour Easy: Bafang BBS02 Configuration

    Tour Easy: Bafang BBS02 Configuration

    The Bafang BBS02 motor claims a 750 W power output, although I suspect that’s measured at the instant before it flings its guts across the test lab:

    Tour Easy Bafang BBS02 motor
    Tour Easy Bafang BBS02 motor

    With a nominal 48 V battery supplying the motor’s nominal 24 A (some say 25 A) current, it dissipates well over 1100 W, although that’s obviously a short-term thing. With 750 W calling for 15-ish A, most likely it will (ideally) suffer thermal shutdown long before the battery runs out.

    Torque being more-or-less proportional to current, its nominal 160 N·m torque at 24 A scales downward by the same factor as the current, for 100 N·m at 15 A.

    The as-received Bafang BBS02 motor controller configuration provided far too much torque for our riding style; I think it’s intended for much younger folks tackling off-road trails on what used to be called mountain bikes, rather than assisting us with normal street riding.

    For example, the default maximum current was 24 A and the first step of pedal assistance was 28% = 6.7 A → 45 N·m: a pretty hefty shove right off the starting line. The Tour Easy was pretty much uncontrollable in the driveway, which is a Bad Sign.

    I started with the “Limitless” configuration (wherein the assistance for all steps continues up to the 20 mph overall speed limit) and reduced the maximum current to 15 A.

    The first assistance step of 5% = 0.8 A → 5 N·m now compensates for the additional weight of the Bafang motor + battery and feels like the unloaded bike.

    The second step was 37% = 8.9 A → 59 N·m and is now 7% = 1 A → 7 N·m, so Mary can ride along with a little oomph for minor hills.

    The third step was 46% = 11 A → 74 N·m and is now 16% = 2.4 A → 16 N·m, enough for the admittedly gentle hills along Vassar Road.

    The throttle uses the ninth step setting (100% = 15 A → 100 N·m) to provide a “get out of Dodge” boost at intersections.

    So far, the BBS02 configuration file looks like this:

    [Basic]
    LBP=42
    LC=15
    ALC0=0
    ALC1=5
    ALC2=7
    ALC3=16
    ALC4=25
    ALC5=37
    ALC6=51
    ALC7=67
    ALC8=85
    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=3
    WM=0
    SC=10
    SDN=5
    TS=10
    CD=8
    SD=1
    KC=100
    [Throttle Handle]
    SV=11
    EV=42
    MODE=1
    DA=10
    SL=0
    SC=10
    

    Mary says she’s getting entirely enough exercise and, frankly, so am I. We have yet to try faster paces and steeper hills.

  • Tour Easy: Bafang BBS02 Mid-Drive Motor

    Tour Easy: Bafang BBS02 Mid-Drive Motor

    For reasons not relevant here, Mary’s Tour Easy recumbent now sports a Bafang BBS02 Mid-drive motor:

    Tour Easy Bafang mid-drive - overview
    Tour Easy Bafang mid-drive – overview

    It pretty much Just Fit, although the lithium battery sits atop mounts conjured from the vasty digital deep:

    Tour Easy Bafang mid-drive - battery
    Tour Easy Bafang mid-drive – battery

    Many cables connect all the doodads, which a custom-made e-bike can hide inside the frame, but … that’s not an option for us.

    The Bafang BBS02 kit is basically plug-n-play, at least if you own a standard-ish bike. I included some useful options for our setup:

    Changing the controller parameters, usually called “programming”, required firing up the Token Windows Laptop:

    As you might expect, I set up a relatively sedate and low-powered pedal assist mode in place of the default rocket sled mode.

    The motor design seems a decade old, so Bafang (née 8Fun) has had time to work out some of the original design misfeatures. It definitely has shortcomings, but nothing insurmountable so far.

    Early results suggest Mary is now riding her familiar bike over much flatter terrain.

    Some background reading:

    More on all of this as I compile my notes …

  • Tek CC Milled Cursor: MVP

    Tek CC Milled Cursor: MVP

    What a difference 100 µm can make:

    Hairline V tool tests - 0.3 mm 10 kRPM 24 ipm
    Hairline V tool tests – 0.3 mm 10 kRPM 24 ipm

    All three hairlines have 0.3 mm depth of cut, with the spindle running at 10 kRPM and the cut proceeding at 24 inch/min = 600 mm/min. All three cuts went through a strip of water + detergent along their length, which seems to work perfectly.

    The cuts start on the left side:

    Hairline V tool tests - 0.3 mm 10 kRPM 24 ipm - start
    Hairline V tool tests – 0.3 mm 10 kRPM 24 ipm – start

    I cut the red hairline through the PET cursor’s protective film to confirm doing it that way is a Bad Idea™; the gnarly appearance is sufficient proof.

    The cuts end on the right:

    Hairline V tool tests - 0.3 mm 10 kRPM 24 ipm - end
    Hairline V tool tests – 0.3 mm 10 kRPM 24 ipm – end

    Eyeballometrically, the cuts are the same depth on both ends, with a slight texture difference at the start as the X axis ramps up to full speed.

    They’d be a bit stout on an old-school engraved slide rule, but look just fine laid against a laser-printed Homage Tek Circuit Computer:

    Hairline V tool tests - 0.3 mm 10 kRPM 24 ipm - Tek CC
    Hairline V tool tests – 0.3 mm 10 kRPM 24 ipm – Tek CC

    Flushed with success, here’s a fresh-cut red hairline in action:

    Tek CC cursor hairline - V tool red fill
    Tek CC cursor hairline – V tool red fill

    The end of the cursor sticks out 1 mm over the rim of the bottom deck, because I wanted to find out whether that would make it easier to move. It turns out the good folks at Tek knew what they were doing; a too-long cursor buckles too easily.

    The trick will be touching off the V tool accurately enough on the cursor surface to get the correct depth of cut. The classic machinist’s technique involves a pack of rolling papers, which might be coming back into fashion here in NY.

  • Tek CC Milled Cursor vs. Speed vs. Coolant

    Tek CC Milled Cursor vs. Speed vs. Coolant

    After getting the Sherline running with the Mesa 5I25, I could return to milling cursor hairlines for the Tek Circuit Computer:

    Hairline V tool - fixture
    Hairline V tool – fixture

    That’s the fixture intended for Gyros circular saw blades, repurposed for V tool engraving. The V tool in the Sherline tool holder collet is one of the ten-pack from the CNC 3018, unused until this adventure.

    The actual setup had a scrap cursor secured with a strip of Kapton tape:

    Hairline V tool - 0.2 0.3 0.4 DOC 10K RPM - Kapton fixture
    Hairline V tool – 0.2 0.3 0.4 DOC 10K RPM – Kapton fixture

    Those are three passes at (nominal) depths of 0.2, 0.3, and 0.4 mm (bottom to top) with a pre-existing hairline visible just above the second pass. The spindle ran at the Sherline’s top speed of just under 10 kRPM with no coolant on the workpiece.

    I touched off the 0.2 mm cut by lowering the tool 0.1 mm at a time until it just left a mark on the Kapton tape, after a coarse touch-off atop a 0.5 mm plastic card, and calling it zero.

    Scribbling over the cuts with a red Industrial Sharpie looked downright gory:

    Hairline V tool - 0.2 0.3 0.4 DOC - Kapton Sharpie
    Hairline V tool – 0.2 0.3 0.4 DOC – Kapton Sharpie

    Peeling the tape and applying a cloth moistened with denatured alcohol showed three gnarly hairlines:

    Hairline V tool - 0.2 0.3 0.4 DOC 10K RPM - Kapton start
    Hairline V tool – 0.2 0.3 0.4 DOC 10K RPM – Kapton start

    The top hairline shows distinct signs of melted PET plastic along the trench, with poor color fill due to the Sharpie not sticking to / wiping off the smooth-ish trench bottom. The next one is the existing saw-cut hairline with the lead-in cut over on the left.

    The 0.3 and 0.2 mm hairlines look much better, with less debris and more complete fill. Unfortunately, the right side of the Sherline’s tooling plate seems to be a few tenths of a millimeter lower than the left, causing the 0.2 mm hairline to … disappear … where the cutter skipped up onto the Kapton tape:

    Hairline V tool - 0.2 0.3 0.4 DOC 10K RPM - Kapton mid
    Hairline V tool – 0.2 0.3 0.4 DOC 10K RPM – Kapton mid

    Now, in practical terms, this is the first time I’ve actually needed platform alignment to within a hundred microns in subtractive machining. As some folks discover to their astonishment, however, 3D printing does require that level of accuracy:

    Thinwall Box - platform height
    Thinwall Box – platform height

    Engraving through a layer of tape isn’t the right way to do it and some coolant will definitely improve the results, so I ignored the alignment issue, remounted the same scrap cursor with the red hairlines on the bottom, pulled a strip of water + detergent along the tool path, cut the same hairlines, and colored the trenches with blue Industrial Sharpie:

    Hairline V tool - 0.2 0.3 0.4 DOC 10K RPM - water cool start
    Hairline V tool – 0.2 0.3 0.4 DOC 10K RPM – water cool start

    The 0.2 mm hairline on the bottom becomes a line as the V bit begins sliding along the surface at 10 kRPM without cutting:

    Hairline V tool - 0.2 0.3 0.4 DOC 10K RPM - water cool mid
    Hairline V tool – 0.2 0.3 0.4 DOC 10K RPM – water cool mid

    The 0.3 mm hairline looks pretty good and the 0.4 mm hairline remains too rugged by the end of the passes. I think the actual depth of cut is at least 0.05 mm less than at the start:

    Hairline V tool - 0.2 0.3 0.4 DOC 10K RPM - water cool end
    Hairline V tool – 0.2 0.3 0.4 DOC 10K RPM – water cool end

    Obviously, neurotically precise touchoff carries a big reward, as will aligning the tooling plate to an absurd degree.

    A real machinist simply flycuts the top of an offending part / fixture / tooling plate to align it with the machine’s spindle, but I have a sneaky suspicion the real problem is a speck (or ten) of swarf between the Sherline’s table and the tooling plate; better cleanliness and attention to detail may improve the situation.

  • Sherline CNC Driver Step Pulse Width Puzzle

    Sherline CNC Driver Step Pulse Width Puzzle

    Long long ago, as part of tidying up the power distribution inside the Sherline CNC controller PCB, I wrote a cleanroom reimplementation of its PIC firmware and settled on a 25 µs Step pulse width with a minimum 50 µs period:

    [PARPORT]
    ADDRESS = 0x378
    RESET_TIME = 10000
    STEPLEN = 25000
    STEPSPACE = 25000
    DIRSETUP = 50000
    DIRHOLD = 50000
    

    Even shorter values for the Direction signal worked with the initial pncconf setup for the Mesa 5I25 FPGA card:

    DIRSETUP   = 25000
    DIRHOLD    = 25000
    STEPLEN    = 25000
    STEPSPACE  = 25000
    
    

    After thrashing through enough of the Kicad-to-HAL converter to get a HAL file sufficiently tasty to prevent LinuxCNC from spitting it out, the X and A axes moved with a gritty sound and the two other axes were pretty much inert.

    After eliminating everything else, including having Tiny Scope™ confirm the pulses were exactly the right duration, I increased them by 10 µs:

    DIRSETUP   = 35000
    DIRHOLD    = 35000
    STEPLEN    = 35000
    STEPSPACE  = 35000
    

    After which, all the axes suddenly worked perfectly.

    At some point along the way, I (re)discovered that Sherline Step pulses are active-low, although in practical terms getting the pulse upside-down just delays the active edge by its width. Given that the Sherline’s top speed is 24 inch/min = 0.4 inch/s, the minimum step period is 156 µs and even a wrong-polarity step should work fine.

    For the record, here’s a perfectly good Step pulse:

    Mesa 5I25 35us active-low Step pulse
    Mesa 5I25 35us active-low Step pulse

    Gotta wipe off that screen more often …

  • Logitech Gamepad: HAL Startup Holdoff

    Logitech Gamepad: HAL Startup Holdoff

    The HAL configuration for the Logitech gamepad has only a few changes from the decade-ago (!) version.

    The HAL driver will reverse the direction of the Y and Z axes by feeding -127.5 to the scale inputs:

    Sherline HAL schematic - Logitech YZ invert
    Sherline HAL schematic – Logitech YZ invert

    The flat inputs make sure the joystick knob has a dead zone around the rest position; otherwise, the axes tend to crawl off without a hand on the controls.

    The gamepad doesn’t emit any values until you poke a button or move a joystick, the driver starts up with all zeros in its counts registers, so the HAL axis position pins emit XYZA = -1 +1 +1 -1 just as if you had the joysticks jammed hard at their upper-left corners. The only way out is to squelch the joystick until a button gets pressed and the machine is turned on:

    Sherline HAL schematic - gamepad valid - parameters
    Sherline HAL schematic – gamepad valid – parameters

    The Gamepad-Valid signal forcibly disables all four axes:

    Gamepad Axis Priority - schematic sample
    Gamepad Axis Priority – schematic sample

    That’s from an earlier iteration of the axis priority logic, before I got the Kicad annotate-starting-at-100 code working.

    I somewhat finessed the startup issue by dedicating two of the gamepad’s back-panel buttons to clearing the initial E-Stop and turning the machine on:

    Sherline HAL schematic - gamepad EStop logic
    Sherline HAL schematic – gamepad EStop logic

    However, if you flip those two conditions with the LinuxCNC GUI, the joysticks will remain zeroed until you do poke a button or move a knob. So, without the Gamepad-Valid logic, the Sherline will take off for the far upper left corner at a pretty good clip, which is not what you want.

    The gamepad outputs remain valid for the rest of the session, so there’s no need to reset the Gamepad-Valid flipflop. HAL does not take kindly to hotplugging USB devices, so (AFAICT) you can’t hard-reset the gamepad + driver to the initial no-data-yet condition.

    Earlier versions of the top two schematics used CONSTANT symbols, but it turns out PARAMETER symbols work just as well and the resulting setp commands eliminate the need for nets and real time functions.