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

General-purpose computers doing something specific

  • Bafang Motor: Chain Gap Filler

    Bafang Motor: Chain Gap Filler

    When the chain falls off the top of the chainring toward the motor, the part remaining engaged with the chainring will inevitably drag the rest into the gap between the motor and the chainring spider, whereupon it will jam firmly in place and be almost impossible to extract. Preventing this means filling the gap, which required several iterations:

    Bafang motor gap filler - prototypes
    Bafang motor gap filler – prototypes

    The Bafang motor has a cover held in place by seven M3 flat-head screws, shown here below a test filler using pan head screws:

    Bafang motor gap filler - installed
    Bafang motor gap filler – installed

    Contrary to what you might think, the five screws that obviously sit on five points of a hexagon do not in fact sit 60° apart. How you find this out is by making the obvious layout, including the two screws bracketing the pinion gear in the lower right, then applying windage:

    Bafang motor housing gap filler - hole adjustments
    Bafang motor housing gap filler – hole adjustments

    That’s one of the paper templates seen above, with laser-cut holes 60° apart and ugly holes punched at the actual screw locations. Then you scan and overlay that image with the LightBurn layout and twiddle the hole locations to make the answer come out right:

    Bafang motor housing gap filler - hole adjustments - LB overlay
    Bafang motor housing gap filler – hole adjustments – LB overlay

    With that in hand, I cut a 1 mm acrylic shape to measure the clearance between the motor + filler and the chainring spider, with pan-head screws replacing the original flat-head screws:

    Bafang motor gap filler - top view
    Bafang motor gap filler – top view

    That’s a single piece of 2.5 mm acrylic I used after discovering a pair of the 1 mm acrylic shapes fit with space to spare: hooray for rapid prototyping.

    A test chain drop suggested it might suffice:

    Bafang motor gap filler - test
    Bafang motor gap filler – test

    If I were so inclined, 3 mm acrylic with countersunk holes and slightly longer flat-head screws would probably work, but I’ll use this until it fails to prevent a chain snag.

    The careful observer will have noted the stress crack extending radially inward from the upper-right screw, which I am carefully avoiding doing anything about, pending the aforementioned failure.

    The LightBurn layout as a GitHub Gist:

    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.

  • Craft Stick Plant Markers: Print-and-Cut Alignment

    Craft Stick Plant Markers: Print-and-Cut Alignment

    With the text laid out in the template, start LightBurn’s Print-and-Cut Wizard to align the template with the fixture on the laser platform.

    Jog the laser to the upper-right target on the fixture, click the upper-right target in the template, and tell P-n-C that’s the First Target. Jog to the lower-left target, click the lower left target, and that’s the Second P-n-C Target:

    Craft Stick Markers - fixture target detail
    Craft Stick Markers – fixture target detail

    The colored circles indicate the targets on the template:

    Craft Stick Markers - LB PnC layout
    Craft Stick Markers – LB PnC layout

    Select the Align No Scaling option, because the template and the fixture are exactly the same size.

    Click-n-drag to select the entire template (because you should always use Cut Selected Graphics), then frame it Just To Be Sure. The red dot pointer (or whatever you use) should kiss the fixture’s perimeter all the way around.

    Make sure the fill layer happens before the cut layer, then Release The Laser:

    Craft Stick Markers - engraving
    Craft Stick Markers – engraving

    The cut layer trims around the engraved letters to leave them standing in the rectangle:

    Craft Stick Markers - cutting
    Craft Stick Markers – cutting

    Some of the smaller bits won’t fall out as they’re cut, but a sharp thwack ejects them easily enough.

    Producing a set of ten sticks takes maybe seven minutes:

    Craft Stick Markers - fixture second fill
    Craft Stick Markers – fixture second fill

    Because craft sticks aren’t intended for fine woodworking, don’t expect consistent engraving results:

    Craft Stick Markers - wood engraving difference
    Craft Stick Markers – wood engraving difference

    Applying a finish would definitely improve their appearance, but most such chemicals don’t belong in an organic vegetable garden.

    I poked the first few test sticks along the edge of the herb garden:

    Craft Stick Markers - test sticks installed
    Craft Stick Markers – test sticks installed

    The rest will be deployed as their eponymous plants go in, then we’ll see how long they survive out there in the real world.

    They’re kinda cute and definitely improved my fixture / template skillz.

  • Craft Stick Plant Markers: Layout Template and Text Alignment

    Craft Stick Plant Markers: Layout Template and Text Alignment

    Putting the entire fixture layout onto a tool layer produces a template to align the text on the sticks:

    Craft Stick Plant Markers - fixture layout
    Craft Stick Plant Markers – fixture layout

    The rectangles mark where you put cut layer rectangles around the text in each stick. The sticks are 18 mm wide, so a 10 mm cutout leaves what should be enough wood along the edges. The rectangle length is a serving suggestion, as you must adjust the cut rectangle to fit the text.

    Group everything except the four targets into a single object so you won’t inadvertently move only a part of it. The targets must remain separate to work with the Print-and-Cut alignment. With that set up, Lock the position of the entire layout to prevent you from moving any part of it.

    Starting with a blank tag in the template:

    Craft Stick Markers - LB template - base
    Craft Stick Markers – LB template – base

    Draw a rectangle in a cut layer to match the template, which is easy if you have Object Snap set up properly:

    Craft Stick Markers - LB template - rectangle
    Craft Stick Markers – LB template – rectangle

    Add your text in a chunky font like Fira Sans Condensed Heavy, set to 15 mm tall with 5 mm horizontal spacing:

    Craft Stick Markers - LB template - lowercase text
    Craft Stick Markers – LB template – lowercase text

    LightBurn aggressively snaps a new text cursor to the nearest pre-existing text, so you may be forced to click far away from where you want to place the text, type the text, then move the finished string. LightBurn will also snap the text to the display grid as you drag it around, so hold the Ctrl key down to disable snapping while you eyeball the proper alignment with the rectangle. Leave about 2 mm between the left edge of the rectangle and the first letter to make an easily visible space.

    Although you can use lowercase letters, uppercase letters have the compelling advantage of being attached both top and bottom, so retype the text if you forgot about the Caps Lock key:

    Craft Stick Markers - LB template - uppercase aligned
    Craft Stick Markers – LB template – uppercase aligned

    The 15 mm font height I’m using seems to be the overall maximum from the top of the tallest letter to the bottom of the lowest descender, not the height of any specific capital letter, all of which extend beyond the cut rectangle by about half a millimeter. That’s crucial to make this thing work, so tune the font and its height appropriately.

    Select the text string when you have it properly aligned:

    Craft Stick Markers - LB template - text selected
    Craft Stick Markers – LB template – text selected

    Hit Ctrl-D to duplicate the text, tap the ↑ (Up) arrow key to move the copy out of the way, and set it to the fill layer.

    Now the magic happens.

    Select the rectangle, Shift-select the text, and Boolean Subtract (Alt minus) the text from the rectangle:

    Craft Stick Markers - LB template - subtracted text
    Craft Stick Markers – LB template – subtracted text

    Realize that you have screwed up by not shortening the right side of the rectangle to leave about 2 mm of open space. Bang on Ctrl-Z to undo the last step, shorten the rectangle, Shift-select the text again, then subtract the text from the rectangle:

    Craft Stick Markers - LB template - properly subtracted text
    Craft Stick Markers – LB template – properly subtracted text

    Select the filled copy and whack the ↓ (Down) arrow key to move it back over the cut layer:

    Craft Stick Markers - LB template - overlaid text
    Craft Stick Markers – LB template – overlaid text

    Now the filled layer will toast the characters to a nice brown and the cut layer will remove the background rectangle.

    After finishing the text dance for all the markers, the template should look something like this:

    Craft Stick Markers - LB PnC layout
    Craft Stick Markers – LB PnC layout

    The cheerful circles come from LightBurn’s Print-and-Cut Wizard aligning the template with the fixture holding the craft sticks on the laser platform, about which more tomorrow.

  • Craft Stick Plant Markers: Laser Fixture

    Craft Stick Plant Markers: Laser Fixture

    Converting craft sticks into plant markers should be a mass-production process, which means a fixture is in order:

    Craft Stick Markers - fixture first fill
    Craft Stick Markers – fixture first fill

    Admittedly, making ten markers at once barely qualifies as “mass production”, but you (well, I) can think of it a proof of concept.

    The basic shape comes from a 0.25 mm outset around the measured size of a craft stick (150×18 mm), plus an alignment target:

    Craft Stick Plant Markers - fixture cut layout - array base
    Craft Stick Plant Markers – fixture cut layout – array base

    A good rule of thumb says never do any more work than absolutely necessary, so the rest of the fixture comes from linear arrays replicating the stick slots and targets:

    Craft Stick Plant Markers - fixture cut layout - full
    Craft Stick Plant Markers – fixture cut layout – full

    The two strips over on the left (with a common cut down the middle) get glued to the underside of the fixture:

    Craft Stick Markers - fixture rail gluing
    Craft Stick Markers – fixture rail gluing

    They’re exactly 5 mm apart to bracket one of the knife-edge bars supporting the fixture. The bar is upside-down to put its flat side upward:

    Craft Stick Markers - fixture target detail
    Craft Stick Markers – fixture target detail

    Yes, the fixture is made of chipboard, mostly because it’s about the same thickness as a craft stick and it’s cheap & readily available. Each target gets an ink blot to make it more conspicuous; there is also a tiny hole burned through the chipboard at the center to mark the other side for the strips.

    Two knife-edge bars (sharp side up) support the sticks near their ends, well out of the cutting path, to prevent scorch marks:

    Craft Stick Markers - fixture overview
    Craft Stick Markers – fixture overview

    It’s worth noting the knife-edge bars are 5 mm wide and the platform spaces them on 3/8 inch = 9.525 mm centers. Not 10 mm, not 9.5 mm, exactly 3/8 inch. Kinda like the platform leadscrews: a 4 mm lead thread driven by a belt with 0.2 inch pitch. Only in America.

    This doodle captures the key dimensions down there in the corner to work out where the strips should go:

    Craft Stick Plant Markers - fixture vs laser bar spacing doodle
    Craft Stick Plant Markers – fixture vs laser bar spacing doodle

    Now, to convert names from a garden map into plant markers …

  • Tour Easy: SJCAM C100+ Mount

    Tour Easy: SJCAM C100+ Mount

    The batteries (which are no longer available) and the control buttons have worn out on the SJCAM M20 camera on the back of my Tour Easy, so a replacement is in order:

    Tour Easy - SJCAM C100 mount - installed
    Tour Easy – SJCAM C100 mount – installed

    It’s an SJCAM C100+ in its waterproof housing, screwed to a block descended from the M20 mount:

    SJCAM C100 Mount - solid model
    SJCAM C100 Mount – solid model

    The C100+ has a non-replaceable lithium pouch battery that may not last for the hour or so we generally ride, but at least this is a starting point for seeing how the thing works.

    The PrusaSlicer preview shows the support structure inside the seat rail arches:

    SJCAM C100 Mount - slicer
    SJCAM C100 Mount – slicer

    That appears under the four central facets of each arch, where I “painted” the support enforcers, because the automagic supports fill the entire arch and are basically impossible to pry off.

    The hole between the ears on the top holds an aluminum tab intended to diffuse the wobble from that tall camera. A laser-cut chipboard template simplified drilling & cutting the tab from an aluminum sheet:

    Tour Easy - SJCAM C100 mount - test fit
    Tour Easy – SJCAM C100 mount – test fit

    The tab and the brass inserts are held in place with JB Weld Plastic Bonder, my new go-to adhesive for such things.

    The camera includes WiFi and the inevitable app lets you download images without opening the case. Because I’ll be charging the camera after each ride, I may as well just haul the whole thing inside, plug it into a USB port, and proceed as before.

    For future reference, the manual details the operating modes:

    SJCAM C100 Manual - Modes
    SJCAM C100 Manual – Modes

    Because the camera powers up with WiFi enabled and I have no plans to communicate with it while riding, the startup sequence will be:

    • Long-press to power on
    • Double-click to disable WiFi
    • Single-click to start recording

    The OpenSCAD source code as a GitHub Gist:

    // SJCAM C100+ Camera Mount for Tour Easy seat back rail
    // Ed Nisley – KE4ZNU
    // 2023-04
    /* [Layout Options] */
    LookAngle = -20; // camera angle, looking backwards = 0°
    Layout = "Show"; // [Show,Build]
    /* [Hidden] */
    ThreadWidth = 0.40;
    ThreadThick = 0.25;
    HoleWindage = 0.2;
    Protrusion = 0.1;
    ID = 0;
    OD = 1;
    LENGTH = 2;
    //—–
    // Dimensions
    ClampScrew = [5.0,10.0,40.0]; // ID=thread OD=washer LENGTH=total
    ClampInsert = [5.0,7.5,10.5]; // brass insert
    MountScrew = [5.0,10.0,23.0]; // ID=thread OD=washer LENGTH=under nut
    MountInsert = [5.0,7.5,10.5]; // ID=screw OD, OD=knurl dia
    EmbossDepth = 2*ThreadThick + Protrusion; // recess depth + Protrusion beyond surface
    DebossHeight = EmbossDepth; // text height + Protrusion into part
    RailOD = 20.0; // slightly elliptical in bent section
    RailSides = 2*3*4;
    ClampOA = [60.0,40.0,ClampScrew[LENGTH]]; // set clamp size to avoid weird screw spacing
    echo(ClampOA = ClampOA);
    ClampScrewOC = IntegerMultiple(ClampOA.x – ClampScrew[OD] – 10*ThreadWidth,1.0);
    echo(ClampScrewOC = ClampScrewOC);
    ClampOffset = 5.0; // in case we need more room on top
    ClampRadius = 3.0;
    ClampSides = 8;
    Kerf = 1.0; // slice through the middle
    // center mount blade, Z = depth into block
    MountBlade = [15.0 + 2*HoleWindage,
    3.0 + 2*HoleWindage,
    (ClampOA.z – RailOD + ClampOffset)/2 – 4*ThreadThick + Protrusion];
    echo(MountBlade = MountBlade);
    MountRadius = MountBlade.x / 2;
    MountGap = 9.5; // camera mount gap around center blade
    MountOffset = [0,0,7.0]; // mount hole offset from block surface
    FadeColor = "Green";
    FadeAlpha = 0.25;
    //—–
    // Useful routines
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    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);
    }
    //—–
    // Clamp
    // Grips seat frame rail
    // Origin at middle of seat rail, X rearward, Y parallel to seat frame rail
    // Block offset raises whole thing
    module Clamp() {
    difference() {
    translate([0,0,ClampOffset]) {
    difference() {
    union() {
    hull() // the main block
    for (i=[-1,1], j=[-1,1], k=[-1,1])
    translate([i*(ClampOA.x – 2*ClampRadius)/2,
    j*(ClampOA.y – 2*ClampRadius)/2,
    k*(ClampOA.z – 2*ClampRadius)/2])
    sphere(r=ClampRadius/cos(180/ClampSides),$fn=ClampSides);
    hull() // camera mount boss
    for (k=[0,1])
    translate([0,0,k*(MountOffset.z) + ClampOA.z/2])
    rotate([0,90,LookAngle + 90]) rotate(180/12)
    cylinder(r=MountRadius,h=MountScrew[LENGTH],center=true,$fn=12);
    }
    for (i=[-1,1]) // clamp inserts
    translate([i*ClampScrewOC/2,0,-(ClampOA.z/2 + Protrusion)])
    rotate(180/6)
    PolyCyl(ClampInsert[OD],ClampInsert[LENGTH],6);
    for (i=[-1,1]) // clamp screw holes
    translate([i*ClampScrewOC/2,0,-ClampOA.z])
    rotate(180/6)
    PolyCyl(ClampScrew[ID],2*ClampOA.z,6);
    translate([0,0,ClampOA.z/2 – (MountBlade.z/2 – Protrusion/2)]) // camera center blade
    rotate(LookAngle)
    cube(MountBlade,center=true);
    rotate(LookAngle + 90) // camera mount boss slot
    translate([0,0,ClampOA.z/2 + 2*MountRadius])
    cube([MountGap,4*MountRadius,4*MountRadius],center=true);
    translate([0,0,ClampOA.z/2 + MountOffset.z]) // camera mount boss hole
    rotate([90,0,LookAngle])
    cylinder(d=MountScrew[ID],h=4*MountGap,center=true,$fn=6);
    translate([0.3*ClampOA.x, // recess for LookAngle legend
    -(ClampOA.y/2 – (EmbossDepth – Protrusion)/2),
    ClampOA.z/4])
    cube([15,EmbossDepth,8],center=true);
    translate([0,0,-ClampOA.z/2 + (EmbossDepth – Protrusion)/2]) // recess for ID legend
    cube([35,10,EmbossDepth],center=true);
    }
    translate([0.3*ClampOA.x, // LookAngle legend
    -ClampOA.y/2 + DebossHeight + Protrusion/2,
    ClampOA.z/4])
    rotate([90,0,00])
    linear_extrude(height=DebossHeight,convexity=20)
    text(text=str(LookAngle),size=6,spacing=1.20,
    font="Arial:style:Bold",halign="center",valign="center");
    translate([0,0,-ClampOA.z/2]) // ID legend
    linear_extrude(height=DebossHeight,convexity=20)
    mirror([0,1,0])
    text(text="KE4ZNU",size=5,spacing=1.20,
    font="Arial:style:Bold",halign="center",valign="center");
    }
    cube([2*ClampOA.x,2*ClampOA.y,Kerf],center=true); // split across rail
    rotate([90,0,0]) // seat rail
    cylinder(d=RailOD,h=2*ClampOA.y,$fn=RailSides,center=true);
    }
    }
    //—–
    // Build things
    // Layouts for design & tweaking
    if (Layout == "Show") {
    Clamp();
    color(FadeColor,FadeAlpha)
    rotate([90,0,0])
    cylinder(d=RailOD,h=2*ClampOA.y,$fn=RailSides,center=true);
    }
    // Build layout
    if (Layout == "Build") {
    translate([0,0.7*ClampOA.y,0])
    difference() {
    translate([0,0,-Kerf/2])
    Clamp();
    translate([0,0,-ClampOA.z])
    cube(2*ClampOA,center=true);
    }
    translate([0,-0.7*ClampOA.y,-0])
    difference() {
    translate([0,0,-Kerf/2])
    rotate([0,180,0])
    Clamp();
    translate([0,0,-ClampOA.z])
    cube(2*ClampOA,center=true);
    }
    }

  • Aperiodic Monotile Puzzle

    Aperiodic Monotile Puzzle

    The aperiodic monotile discovery prompted some reverse engineering, snapping a path to regular hexagons for the proper lengths and angles without mathing too hard:

    Aperiodic tile - hexagon overlay
    Aperiodic tile – hexagon overlay

    The resulting red path is the “hat” monotile, here shown as a PNG for neatness:

    Aperiodic monotile
    Aperiodic monotile

    With SVG hat in hand, I laid and cut a trial puzzle based on the sample shown in the paper:

    Aperiodic tile layout
    Aperiodic tile layout

    Which looked promising enough to add a few rings around that layout and turn it into an actual, albeit low budget, puzzle:

    Aperiodic tile puzzle - starting
    Aperiodic tile puzzle – starting

    The paper notes that one can build mutually incompatible patches, which is the state I immediately blundered into:

    Aperiodic tile puzzle - progress
    Aperiodic tile puzzle – progress

    The upper and lower left halves cannot be combined to extend rightward, as the middle section is incompatible with both. I began growing patches from the upper and lower right corners, hoping to use them to rectify the left half, but producing a small un-fillable situation in the middle:

    Aperiodic tile puzzle - incorrect layout
    Aperiodic tile puzzle – incorrect layout

    Obviously, I need a cheat code. I’m resolutely not looking at the source layout for a while.

    This would surely look good in fluorescent edge-lit acrylic!

    The LightBurn SVG layout as a GitHub Gist:

    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.

  • Another CatEye Cadence Sensor Magnet Mount

    Another CatEye Cadence Sensor Magnet Mount

    The stock Bafang pedal cranks measure 170 mm on centers between the bottom bracket shaft and the pedal spindle. Having grown accustomed to the 165 mm cranks from Mary’s bike, I got a set of cheap 160 mm cranks to feel if there was any difference:

    Bafang vs ProWheel crank forging
    Bafang vs ProWheel crank forging

    The bottom crank has a quick-and-dirty adaptation of the magnet mount for the Lekkie Buzz Bar offset cranks, but, of course, the 160 mm cranks have an entirely different profile. They are also heavier and more crudely forged, which is about what you’d (well, I’d) expect.

    Also unlike the Lekkie cranks, neither the Bafang nor the Prowheel cranks correct the Bafang motor’s offset, so I’m using the left-side Kneesaver from the old cranks, which turns out to be close enough.

    Modeling the profile started with an infrequently used contour gauge:

    CatEye Magnet holder - ProWheel crank profile tests
    CatEye Magnet holder – ProWheel crank profile tests

    The black 3D printed mount in the upper right fit the Bafang crank and appears in the top photo.

    Transferring the new contour to paper and applying the Chord Equation got the radius of the not-quite circle:

    CatEye magnet crank adapter - chord radius
    CatEye magnet crank adapter – chord radius

    Knowing the size of the magnet and the radius of the circle, drawing the profile in LightBurn was straightforward:

    CatEye magnet crank adapter - framed
    CatEye magnet crank adapter – framed

    Applying the laser cutter to MDF produced the two successive test-fit pieces in the picture while figuring out how much stickout the magnet needed beyond the inner crank face to reach the sensor. LightBurn’s Node Editor simplified adjusting the size: drag-select a group of nodes, then move them in precise increments with the arrow keys.

    Export the profile from LightBurn as an SVG file, import it into OpenSCAD, and extrude it to the proper length:

    module CatEyeMagnet() {
    
    Magnet = [19.0,14.0,8.5];
    
    translate([0,75,0])
        linear_extrude(height=Magnet.y)
            import("CatEye magnet crank adapter.svg");
    
    }
    

    The translate puts the profile approximately at the XY origin. The center = true option moves the profile elsewhere on the XY plane, but does not center it, which may have something to do with the viewport used by LightBurn, the OpenSCAD version I’m using, or something else entirely.

    In any event, the 3D printed mount fits the crank and puts the magnet where it will do the most good:

    CatEye Magnet holder - ProWheel crank - installed
    CatEye Magnet holder – ProWheel crank – installed

    What looks like an obvious curvature mismatch comes from having the tape edge not quite squashed against the crank.

    I should poke a channel through it for a cable tie around the crank, but that 3M foam tape is really good stuff and hasn’t failed me yet.