The Smell of Molten Projects in the Morning

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

Category: Machine Shop

Mechanical widgetry

  • Pogo Pins

    A Pogo Pin reference may be useful:

    • P.. and R.. refer to Pin and Receptacle (a.k.a. socket), respectively
    • Pxx  and Rxx = nominal pin diameter in 0.01 mm units: P50 = 0.48 mm

    For pins, the suffix -hn indicates pin head shape, the most useful of which may be:

    • B1: 45° cone
    • J1: dome end
    • Dx: large dome, also 1D
    • Gx: cylinder
    • Ex: large 90° cone, sometimes 1E
    • T2 – large chisel

    For sockets, the suffix -ntl gives:

    • n – entry shape: 1 = shaped entry, 2 = straight entry
    • t – termination: C = crimp, S = solder, W = wire
    • l – length of wire in 100 mm units: 7 = 700 mm

    From what I can find on eBay, all pins have 6 mm travel with typically 75 / 100 / 180 g spring force.

    A picture ripped from the reference to forestall link rot:

    P75 Spring Test Probes
    P75 Spring Test Probes

    Memo to Self: US-based eBay sellers charge three times more than Chinese sellers, but deliver in one-third the time.

    [Update: Simon sends a link to Everett Charles Technologies, a pogo-pin manufacturer providing “Probably much more information than anyone should ever want”. Of course, eBay / Amazon junk may not meet any particular specs, so scale your expectations accordingly.]

  • MPCNC: Plotter Pen Holder Spring Constant

    Watching the MPCNC plot Spirograph patterns led me to wonder about how much force the printed drag knife holder applies to the pen:

    Spirograph - liquid ink pen - detail
    Spirograph – liquid ink pen – detail

    The HP 7475A plotter spec calls for 19 g = 0.67 oz of downward force on the pen, so, in an ideal world, one might want to use one’s collection of aging plotter pens in a similar manner.

    Plotter pen, meet digital scale:

    MPCNC - Plotter pen force test
    MPCNC – Plotter pen force test

    Stepping the pen downward in 0.1 mm increments produced a set of numbers and a tidy linear fit graph:

    MPCNC Plotter Pen Holder - Spring Constant
    MPCNC Plotter Pen Holder – Spring Constant

    I hereby swear I’m not making things up: the spring constant really is a nice, round 100 g/mm!

    I set plot_z = -1.0 in the GCMC program, with Z=0.5 touched off atop a defunct ID card on the paper surface to compensate for any tabletop warp / bow / misalignment, plus any errors from the tool length probe. An eyeballometric scan against a straightedge shows pretty nearly no misalignment, which means the holder mashes the pen against the paper with about 100 g of force, five times the HP spec.

    A distinct case of pen abuse rears its ugly head.

    It’s time to conjure a height probe for the tool holder.

  • Spirograph Random Numbers: What Are The Odds?

    The GCMC Spirograph Generator program chooses parameters using pseudo-random numbers based on a seed fed in from the Bash script, so I was surprised to see two plots overlap exactly:

    Overlaid pattern - G-Code simulator
    Overlaid pattern – G-Code simulator

    The two overlapping traces are the 15 inward-pointing wedges around the central rosette.

    The first one:

    (PRNG seed: 38140045)
    (Paper size: [16.50in,14in])
    (PlotSize: [15.50in,13.00in])
    (Stator 3: 150)
    (Rotor  4: 40)
    (GCD: 10)
    (Offset: -0.94)
    (Dia ratio: -0.27)
    (Lobes: 15)
    (Turns: 4)
    (Plot scale: [5.11in,4.29in])
    (Tool change: 1)
    T1
    M6
    

    The second one:

    (PRNG seed: 74359295)
    (Paper size: [16.50in,14in])
    (PlotSize: [15.50in,13.00in])
    (Stator 3: 150)
    (Rotor  4: 40)
    (GCD: 10)
    (Offset: -0.93)
    (Dia ratio: -0.27)
    (Lobes: 15)
    (Turns: 4)
    (Plot scale: [5.12in,4.30in])
    (Tool change: 3)
    T3
    M6
    

    The Offset isn’t quite the same, but the pen width covers up the difference.

    With only four Stators and 17 Rotors, the probability of picking the same pair works out to 0.25 × 0.059 = 1.4%. You can sometimes get the same number of Lobes and Turns from several different Stator + Rotor combinations, but these were exact matchs with the same indices.

    The Pen Offset within the Rotor comes from a fraction computed with ten bit resolution, so each Offset value represents slightly under 0.1% of the choices. If any four adjacent values look about the same, then it’s only eight bits of resolution and each represents 0.4%.

    The Rotor and Stator set the Diameter ratio, but the sign comes from what’s basically a coin flip based on the sign of a fraction drawn from 256 possibilities; call it 50%.

    Overall, you’re looking at a probability of 28 ppm = 0.0028%, so I (uh, probably) won’t see another overlay for a while …

    I don’t know how to factor the PRNG sequence into those numbers, although it surely affects the probability. In this case, two different seeds produced nearly the same sequence of output values, within the resolution of my hack-job calculations.

    Whatever. It’s good enough for my simple purposes!

  • MPCNC: Spirograph Generator with Tool Changes

    An improved version of my GCMC Spirograph pattern generator, now with better annotation and tool changes:

    Spirograph pattern - overview
    Spirograph pattern – overview

    The GCMC code sets the stator and rotor gear tooth counts, the rotor diameter, and the pen offset using a pseudo-random number generator. This requires randomizing the PRNG seed, which I do in the calling script with the nanosecond of the current second: rnd=$(date +%N).

    The G-Code file name also comes from the timestamp:

    ts=$(date +%Y%m%d-%H%M%S)
    fn='Spiro_'${ts}'.ngc'
    # blank line to make the underscore visible
    

    Which means you must call the Bash script slowly to generate a pile o’ plots:

    for i in {1..60} ; do sh /mnt/bulkdata/Project\ Files/Mostly\ Printed\ CNC/Patterns/spiro.sh ; sleep 1 ; done
    

    Sift through the heap with drag-n-drop action using an online G-Code previewer. There seems no clean way to convert G-Code to a bitmap on the command line, although you can do it manually, of course.

    The GCMC program spits out the G-code for one plot at a time, so the Bash script calls it four times to fill a sheet of paper with random patterns:

    for p in $(seq 4)
    do
      rnd=$(date +%N)
      gcmc -D Pen=$p -D $Paper -D PRNG_Seed=$rnd $Flags $LibPath -q "$Spirograph" >> $fn
    done
    

    The -q parameter tells GCMC to not include the prolog and epilog files, because the calling script glues those onto the lump of G-Code for all four plots.

    The -D Pen=$p parameter tells the GCMC program which “tool” to select with a Tn M6 tool change command before starting the plot. Although plotter pens have a well-defined position in the holder and a pretty nearly constant length, you must have a tool length probe installed and configured:

    MPCNC Tool Length Probe - Plotter Pen
    MPCNC Tool Length Probe – Plotter Pen

    Set the overall sheet size in inches or millimeters to get a plot centered in the middle of the page with half-inch margins all around:

    Paper='PaperSize=[16.5in,14in]
    

    With all that in hand, those good old black ceramic-tip pens give impeccable results:

    Spirograph pattern - black ceramic pen - detail
    Spirograph pattern – black ceramic pen – detail

    The surviving ones, anyhow. I must apply my collection of Sakura Micron pens to this task.

    The other three colors come from fiber pens with reasonably good tips:

    Spirograph pattern - central details
    Spirograph pattern – central details

    They’re a lot like diatoms: all different and all alike.

    The GCMC and Bash source code as a GitHub Gist:

    # Spirograph G-Code Generator
    # Ed Nisley KE4ZNU – December 2017
    Paper='PaperSize=[16.5in,14in]'
    Flags="-P 2"
    LibPath="-I /opt/gcmc/library"
    Spirograph='/mnt/bulkdata/Project Files/Mostly Printed CNC/Patterns/Spirograph.gcmc'
    Prolog="/home/ed/.config/gcmc/prolog.gcmc"
    Epilog="/home/ed/.config/gcmc/epilog.gcmc"
    ts=$(date +%Y%m%d-%H%M%S)
    fn='Spiro_'${ts}'.ngc'
    echo Output: $fn
    rm -f $fn
    echo "(File: "$fn")" > $fn
    cat $Prolog >> $fn
    for p in $(seq 4)
    do
    rnd=$(date +%N)
    gcmc -D Pen=$p -D $Paper -D PRNG_Seed=$rnd $Flags $LibPath -q "$Spirograph" >> $fn
    done
    cat $Epilog >> $fn
    view raw spiro.sh hosted with ❤ by GitHub
    // Spirograph simulator for MPCNC used as plotter
    // Ed Nisley KE4ZNU – 2017-12-23
    // Spirograph equations:
    // https://en.wikipedia.org/wiki/Spirograph
    // Loosely based on GCMC cycloids.gcmc demo:
    // https://gitlab.com/gcmc/gcmc/tree/master/example/cycloids.gcmc
    // Required command line parameters:
    // -D Pen=n pen selection for tool change and legend position
    // -D PaperSize=[x,y] overall sheet size: [17in,11in]
    // -D PRNG_Seed=i non-zero random number seed
    include("tracepath.inc.gcmc");
    include("engrave.inc.gcmc");
    //—–
    // Greatest Common Divisor
    // https://en.wikipedia.org/wiki/Greatest_common_divisor#Using_Euclid's_algorithm
    // Inputs = integers without units
    function gcd(a,b) {
    local d=0;
    if (!isnone(a) || isfloat(a) || !isnone(b) || isfloat(b)) {
    warning("Values must be dimensionless integers");
    }
    while (!((a | b) & 1)) { // remove and tally common factors of two
    a >>= 1;
    b >>= 1;
    d++;
    }
    while (a != b) {
    if (!(a & 1)) {a >>= 1;} // discard non-common factor of 2
    elif (!(b & 1)) {b >>= 1;} // … likewise
    elif (a > b) {a = (a – b) >> 1;} // gcd(a,b) also divides a-b
    else {b = (b – a) >> 1;} // … likewise
    }
    local GCD = a*(1 << d); // form gcd
    // message("GCD: ",GCD);
    return GCD;
    }
    //—–
    // Max and min functions
    function max(x,y) {
    return (x > y) ? x : y;
    }
    function min(x,y) {
    return (x < y) ? x : y;
    }
    //—–
    // Pseudo-random number generator
    // Based on xorshift:
    // https://en.wikipedia.org/wiki/Xorshift
    // http://www.jstatsoft.org/v08/i14/paper
    // Requires initial state from calling script
    // -D "PRNG_Seed=$(date +%N)"
    function XORshift() {
    local x = PRNG_State;
    x ^= x << 13;
    x ^= x >> 17;
    x ^= x << 5;
    PRNG_State = x;
    return x;
    }
    //—–
    // Spirograph tooth counts mooched from:
    // http://nathanfriend.io/inspirograph/
    // Stators has both inside and outside counts, because we're not fussy
    Stators = [96, 105, 144, 150];
    Rotors = [24, 30, 32, 36, 40, 45, 48, 50, 52, 56, 60, 63, 64, 72, 75, 80, 84];
    //—–
    // Start drawing things
    // Set these variables from command line
    comment("PRNG seed: ",PRNG_Seed);
    PRNG_State = PRNG_Seed;
    // Define some useful constants
    AngleStep = 2.0deg;
    Margins = [0.5in,0.5in] * 2;
    comment("Paper size: ",PaperSize);
    PlotSize = PaperSize – Margins;
    comment("PlotSize: ",PlotSize);
    //—–
    // Set up gearing
    s = (XORshift() & 0xffff) % count(Stators);
    StatorTeeth = Stators[s];
    comment("Stator ",s,": ",StatorTeeth);
    r = (XORshift() & 0xffff) % count(Rotors);
    RotorTeeth = Rotors[r];
    comment("Rotor ",r,": ",RotorTeeth);
    GCD = gcd(StatorTeeth,RotorTeeth); // reduce teeth to ratio of least integers
    comment("GCD: ",GCD);
    StatorN = StatorTeeth / GCD;
    RotorM = RotorTeeth / GCD;
    L = to_float((XORshift() & 0x3ff) – 512) / 100.0; // normalized pen offset in rotor
    comment("Offset: ", L);
    sgn = sign((XORshift() & 0xff) – 128);
    K = sgn*to_float(RotorM) / to_float(StatorN); // normalized rotor dia
    comment("Dia ratio: ",K);
    Lobes = StatorN; // having removed all common factors
    Turns = RotorM;
    comment("Lobes: ", Lobes);
    comment("Turns: ", Turns);
    //—–
    // Crank out a list of points in normalized coordinates
    Path = {};
    Xmax = 0.0;
    Xmin = 0.0;
    Ymax = 0.0;
    Ymin = 0.0;
    for (a=0.0deg ; a <= Turns*360deg ; a += AngleStep) {
    x = (1 – K)*cos(a) + L*K*cos(a*(1 – K)/K);
    if (x > Xmax) {Xmax = x;}
    elif (x < Xmin) {Xmin = x;}
    y = (1 – K)*sin(a) – L*K*sin(a*(1 – K)/K);
    if (y > Ymax) {Ymax = y;}
    elif (y < Ymin) {Ymin = y;}
    Path += {[x,y]};
    }
    //message("Max X: ", Xmax, " Y: ", Ymax);
    //message("Min X: ", Xmin, " Y: ", Ymin); // min will always be negative
    Xmax = max(Xmax,-Xmin); // odd lobes can cause min != max
    Ymax = max(Ymax,-Ymin); // … need really truly absolute maximum
    //—–
    // Scale points to actual plot size
    PlotScale = [PlotSize.x / (2*Xmax), PlotSize.y / (2*Ymax)];
    comment("Plot scale: ", PlotScale);
    Points = scale(Path,PlotScale); // fill page, origin at center
    //—–
    // Set up pen
    if (Pen > 0) {
    comment("Tool change: ",Pen);
    toolchange(Pen);
    }
    //—–
    // Plot the curve
    feedrate(3000.0mm);
    safe_z = 1.0mm;
    plot_z = -1.0mm;
    tracepath(Points, plot_z);
    //—–
    // Put legend in proper location
    feedrate(500mm);
    TextSize = [3.0mm,3.0mm];
    TextLeading = 1.5; // line spacing as multiple of nominal text height
    MaxPen = 4;
    line1 = typeset("Seed: " + PRNG_Seed + " Stator: " + StatorTeeth + " Rotor: " + RotorTeeth,FONT_HSANS_1);
    line2 = typeset("Offset: " + L + " GCD: " + GCD + " Lobes: " + Lobes + " Turns: " + Turns,FONT_HSANS_1);
    maxlength = TextSize.x * max(line1[-1].x,line2[-1].x);
    textpath = line1 + (line2 – [-, TextLeading, -]); // undef – n -> undef to preserve coordinates
    if (Pen == 1 || Pen > MaxPen ) { // catch and fix obviously bogus pen selections
    textorg = [PlotSize.x/2 – maxlength,-(PlotSize.y/2 – TextLeading*TextSize.y)];
    }
    elif (Pen == 2) {
    textorg = [-PlotSize.x/2,-(PlotSize.y/2 – TextLeading*TextSize.y)];
    }
    elif (Pen == 3) {
    textorg = [PlotSize.x/2 – maxlength, PlotSize.y/2 – TextSize.y];
    }
    elif (Pen == 4) {
    textorg = [-PlotSize.x/2, PlotSize.y/2 – TextSize.y];
    }
    else {
    Pen = 0; // squelch truly bogus pens
    textorg = [0mm,0mm]; // just to define it
    }
    if (Pen) { // Pen = 0 suppresses legend
    placepath = scale(textpath,TextSize) + textorg;
    comment("Legend begins");
    engrave(placepath,safe_z,plot_z);
    }
    if (Pen == 1) { // add attribution along right margin
    attrpath = typeset("Ed Nisley – KE4ZNU – softsolder.com",FONT_HSANS_1);
    attrpath = rotate_xy(attrpath,90deg);
    attrorg = [PlotSize.x/2,5*TextLeading*TextSize.y – PlotSize.y/2];
    placepath = scale(attrpath,TextSize) + attrorg;
    comment("Attribution begins");
    engrave(placepath,safe_z,plot_z);
    }
    goto([-,-,25.0mm]);
    view raw Spirograph.gcmc hosted with ❤ by GitHub
  • A Spirograph for Christmas

    Gotta play with my new toy:

    Spirograph - liquid ink - ceramic tip
    Spirograph – liquid ink – ceramic tip

    That’s with a set of liquid ink and ceramic tip plotter pens. They’re unbelievably cranky, but produce wonderfully fine lines:

    Spirograph - liquid ink pen - detail
    Spirograph – liquid ink pen – detail

    Text comes out exactly the way vector lettering should look:

    Spirograph - liquid ink pen text - detail
    Spirograph – liquid ink pen text – detail

    There’s a slight shake visible at 500 mm/min = 8.3 mm/s, but it’s Good Enough.

    All the pen-and-ink traffic around the center produced a ring of damp green fuzz:

    Spirograph - liquid ink - ceramic tip - center detail
    Spirograph – liquid ink – ceramic tip – center detail

    The artsy part of the plot ran at 1800 mm/min = 30 mm/s, with little of the wobbulation at 6000 mm/min = 100 mm/s. None of that would matter with a router, of course.

    It’s a nice, Christmasy design in kinda-red and sorta-green.

    From the stack of plots accumulating near the MPCNC bench:

    This slideshow requires JavaScript.

    Plots 7 and 9 show the tape sutures required to produce a 26×18 inch sheet covering the MPCNC’s full work area. The squat plots fit on B-size sheets and the rest come from 17×14 inch artist’s sketchpad sheets.

    I used Google PhotoScan to capture and rectangularize paper sheets from the floor or atop the bench, then battered the contrast and crushed the file size with a one-liner:

    i=1 ; for f in 1* ; do printf -v dn "Spiro %02d.jpg" $(( i++ )) ; convert $f -level '10,80%' -density 300 -define jpeg:extent=300KB tweaked/"$dn" ; done
    

    The plots look great in person (modulo some incremental software improvements), but the slideshow images look awful because:

    • Google PhotoScan produces surprisingly low-res images
    • I’m overly compressing the results

    They’re not (yet) art and there’s no point in a high-quality workflow.

    Enjoy the day …

  • Epson R380: Re-re-routed CISS Tubing

    Alas, what seemed like a better tube route didn’t work any better and, in fact, the generous loop snagged crosswise between the print head box and the R380’s frame. So I deployed the big diagonal cutters and a nasty end cutter to chop a channel through the side of the box:

    Epson R380 - modified print head box
    Epson R380 – modified print head box

    As far as I can tell, the thin section above the reinforcing gridwork exists specifically to get in the way of routing CISS tubes, but I suppose it could be just for pretty.

    With the tubes coming directly off the top of the tanks and folding neatly as the print head moves under the frame, I could rearrange the supports to hold the tubes in a nearly straight line throughout their motion:

    Epson R380 - straight CISS tube route
    Epson R380 – straight CISS tube route

    So far, so good.

    Although the yellow ink now feeds properly and all the nozzles appear up on the test page, the printer output has an overall cyan tinge that gave the Annual Christmas Letter a gloomy aspect. Maybe the latest bottle of cheap Light Cyan ink isn’t quite as light as it should be?

  • Cycliq Fly6: Battery Replacement

    After verifying my defunct Cycliq Fly6 has a dead battery, I ordered a handful of 18650 cells from Batteryspace (a known-reputable source):

    Fly6 - battery replacement - Samsung label
    Fly6 – battery replacement – Samsung label

    The transplanted protection PCB goes between the tabs, with a nickel strip snippet because I didn’t cut the old strip in the right place:

    Fly6 - battery replacement - PCB
    Fly6 – battery replacement – PCB

    The PCB goes under a manila paper layer, the ends get similar caps, and the whole affair receives an obligatory Kapton tape wrap:

    Fly6 - battery replacement - endcap
    Fly6 – battery replacement – endcap

    Reassembly is in reverse order. I now know the Fly6 will reset / start up when the battery connector snaps into place, but, because it emits identical battery-charge beeps when it starts and shuts off, there’s no way to tell what state it’s in. I don’t see any good way to install the ribbon cable from the LED PCB before plugging in the battery, so just blindly press-and-hold the power button to shut it off.

    After an overnight charge, it makes videos of my desk just fine and will, I expect, do the same on the bike.

    Now that I’ve taken the thing apart, I should open it up and tinker with the (glued-down) camera focus adjustment to discover whether:

    We’ll find out when the weather warms up in a week or two.