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.

Author: Ed

  • OpenSCAD Layout Grid

    OpenSCAD Build Surface Grid
    OpenSCAD Build Surface Grid

    This OpenSCAD module spreads an array of cubes across the otherwise featureless preview window, so I know whether the gizmo I’m building or the parts I’m arranging actually fit on the Thing-O-Matic’s build platform. This doesn’t get out to the very edge, but if it looks close, then I should pay more attention anyway.

    module ShowPegGrid(Size) {
    
     for (x=[-5:5])
      for (y=[-5:5])
       translate([x*10,y*10,Size/2])
        cube(Size,center=true);
    
    }
    
    ShowPegGrid(1.0);
    

    You obviously don’t want to extrude these things, so put the ShowPegGrid() statement inside an if, so you can turn it off for the final build layout.

  • Stepper Motor Winding Current Rise Time

    Here’s how the stepper drive voltage affects the current rise, using that kludge to sync the scope on one of those motors with L=2.6 mH and R=2.2 Ω. The peak winding current is 1 A, so the first step current-limits at 200 mA.

    At 9 V:

    Current Rise - 9 V 1A 3 RPS
    Current Rise – 9 V 1A 3 RPS

    At 18 V:

    Current Rise - 18 V 1A 3 RPS
    Current Rise – 18 V 1A 3 RPS

    Knowing the rise time and current change, you can calculate the actual voltage across the inductor using:

    VL = L di/dt

    With 9 V drive the motor sees:

    4.4 V = 2.6 mH x 220 mA / 130 us

    With 18 V drive the motor sees:

    14 V = 2.6 mH x 240 mA / 45 us

    So, in round numbers, the driver MOSFETs, winding resistance, and all the crappy solderless breadboard connections soak up about 4 V of the available supply voltage. There’s some back EMF in there, too, but I haven’t measured that part of the puzzle yet.

    The motor is turning at 3 rev/s in 1/8 microstepping mode, so each microstep is:

    200 us = 1/(3 rev/s x 1600 step/rev)

  • Helmet Mirror: Mirror Modifications

    Mirror shaft - 2-56 stud
    Mirror shaft – 2-56 stud

    This 2-56 stud will hold the mirror shaft into whatever helmet mount I eventually decide on. It’s a pan-head screw that miraculously fits snugly inside the cut-down shaft section, held in with a delicate epoxy dribble around the edge.

    The head abuts the end of the smaller shaft section, so the two no longer slide. I think a length of heat-shrink tubing will stabilize them in rotation, although perhaps I should have just slobbered more epoxy into that joint.

    After the epoxy cured, I sliced off all but 2 mm of the screw thread with an abrasive wheel and cleaned up the wreckage with a file. I actually remembered to spin on a nut before cutting, which ensured I finished the threads properly.

    The business end of the mirror has far too many moving parts: two indented plates for the balls on the mirror and shaft, a screw, and a nut. That’s one too many ball joints, at least, and Wouldn’t It Be Nice If the mirror had a watertight seal around its perimeter?

    Mirror ball joint clamp
    Mirror ball joint clamp

    For now, I just epoxied the nut in place after scuffing up the plate and nut with some sandpaper to give the epoxy something to grip:

    Mirror ball joint - epoxied nut
    Mirror ball joint – epoxied nut

    You can’t see the new washer and rubber grommet under the screw head that provides a bit of compliance to hold the balls more securely, plus a dot of low-strength Loctite in the nut to discourage things from falling apart on the road.

  • Inspection Mirror Shaft Innards

    Inspection mirror shaft friction springs
    Inspection mirror shaft friction springs

    With those doodles in mind, I applied an abrasive cutoff wheel to the shaft of an inspection mirror (from the usual eBay supplier) about 15 mm behind the second joint. That puts a short section of the third tube inside the yet-to-be-built helmet mirror mount.

    The two copper-colored springs center the smaller tube inside the larger one and provide enough friction to make the whole thing work. The tubes seem to be chrome-plated brass and the springs  might be phosphor bronze. I suppose they’re Matryoshka-sized from one end to the other.

    I’d never taken one of those shafts apart before; now we both know.

  • Better Bike Mirror Doodles

    Mirror Mount - Unworkable Doodles
    Mirror Mount – Unworkable Doodles

    Having had many bike helmet mirrors disintegrate over the miles and years, I’ve had a background project bubbling along to build something more durable. Whether that’s feasible or not remains to be seen, but here’s another go at it.

    A full-up ball joint seems to be more trouble than it’s worth and, in any event, requires far too much precision to be easily duplicated. That renders those doodles, mmm, inoperative.

    These doodles aren’t workable, either, but they convert the ball joint into two orthogonal rotating joints that could be 3D printed with some attention to detail.

    The general idea:

    • An ordinary inspection mirror has most of the tricky bits
    • An azimuth-elevation mount aligns the shaft relative to the helmet
    •  The mirror shaft extends to put the mirror forward of your eye
    • The existing mirror ball joint aligns the mirror relative to your eye

    What’s not to like:

    • Exposed screw heads
    • Off-center, hard-to-grip adjustments
    • Probably not printable without support due to all the bearing surfaces and cutouts and suchlike
    Mirror Mount - Doodles
    Mirror Mount – Doodles

    A few more days of doodling produced something that seems better. The az-el joint axes and the mirror shaft axis now meet at a common point, so the mirror shaft moves as the radius of a sphere. The elevation screw hides behind the azimuth mount, out of the way, which makes it awkward to adjust the tension.

    The helmet mount plate must be concave to more-or-less match the helmet curvature. I’ve been securing mirrors using double-sided foam tape to good effect, but it requires a fairly large pad to provide enough adhesive force.

    Two glue joints make everything buildable and should have basically the same strength as the parts themselves. The helmet plate builds concave face up. The az and el mounts build with the bearings upward, as do the mating surfaces on the other parts. Maybe the screws need actual nuts embedded in the mating parts, in which case there may be problems.

    The setscrew holding the mirror shaft can crush the tube; I think they’re thin brass, at best. Putting a stud screw on the end will hold the shaft in place, leaving the setscrew to prevent rotation. Perhaps the stud can reinforce the tube.

    What’s not to like:

    • Many parts (but all buildable at once)
    • It sticks out too far from the side of the helmet
    • Ugly on a stick

    But maybe something will come of it.

  • Thing-O-Matic: Large Knots

    Printing tiny knots showed the need for support under the loop takeoff points, which xorxo’s Hi-Res 3D Knot provides:

    Large Knot - scaffolded
    Large Knot – scaffolded

    My Shop Assistant cleaned up a second version:

    Large Knot cleaned - top
    Large Knot cleaned – top

    As the scrawled notation says: printed at 50 mm/s with 100 mm/s moves. The only cleanup: remove the scaffolding and slice off the Reversal zittage.

    If the truth be known, that was actually the third knot. The first suffered a spectacular failure: one corner of the filament spool snagged on the wall behind the printer and jammed the filament:

    Large Knot - failed
    Large Knot – failed

    The filament drive pulled all the slack out of the bundle, broke off three of the six internal guide posts (admittedly, they’re just hot-melt glued in place), and dragged a nasty kink halfway down the feeder tube. Obviously the stepper was shedding steps during that whole process, but it came rather close to doing the Ouroboros thing.

    While that went down, I was puttering around in the far reaches of the Basement Laboratory, attempting to clean up a bit of the clutter, and checking in on the printer every now and again. Seemed like a good idea at the time, is all I can say.

    Perhaps the Lords of Cosmic Jest simply decided this was an appropriate object to mess with. The vertices of the hexagonal filament spool stick out perhaps 10 mm from the printer’s backside and every one has cleared the wall on countless previous rotations. I moved the entire affair a bit further from the wall and maybe it’ll be all good from now on.

  • Stepper Sync Wheel: Group Sync

    A small tweak to that code produces a sync pulse for each full sine wave of microstepping current, aligned with the step pulses. The sync pulse occurs on the rising edge of the current waveform (because I set it up that way) and has 50% duty cycle to allow triggering at either zero-current microstep.

    Then pix like this happen:

    Microstepping Group Sync
    Microstepping Group Sync

    The traces:

    • top = 1/group sync
    • middle = winding current at 500 mA/div
    • bottom = step pulse, 1/microstep

    The big jump just before the zero-current microstep on the decreasing-current sides of the sine wave indicates that it’s hard to get all the current out of the windings at 12 V. A detail view of those steps shows that the current is 50% higher than it should be at the start of the zero-current microstep, having completely missed the last microstep:

    Decreasing Current
    Decreasing Current

    Which, of course, is why I’m doing all this: to explore that kind of behavior.

    You may find the generated sync pulses are off by ±1 microstep from the expected start of the zero-current microstep, because the optical 1/rev signal threshold may line up perversely with the start of a microstep. You can twist the sync wheel just slightly on the shaft, but it’s pretty much a matter of shaking the dice to see if a better combination comes up. Doesn’t make any real difference to the scope triggering, though, as any stable sync alignment is as good as any other.

    The code uses the 1/rev optical sync pulse once, to get the initial alignment, so whacking the wheel as it rotates may cause the generated sync pulses to skip a beat or twenty. The result remains stable, just at a different alignment.

    One could argue that you really don’t need the 1/rev optical signal at all, but I find it comforting to use an absolute rotational reference to lock the pulses in (pretty nearly) the same place every time. If you’re stuck with an in-place motor, then you probably don’t have a 1/rev signal and you must wing it.

    The Arduino source code:

    // Stepper motor driver synchronization
    // Ed Nisley KE4ZNU June 2011
    
    //-- Stepper parameters
    
    #define SYNC_OFFSET            8        // steps from 1/rev pulse to start of first 4-full-step group
    
    #define FULL_STEPS_PER_REV    200
    #define MICROSTEPPING        8
    
    #define GROUPS_PER_REV (FULL_STEPS_PER_REV / 4)
    #define STEPS_PER_GROUP (MICROSTEPPING * 4)
    
    //-- Pin definitions, all of which depend on internal hardware: do *not* change
    
    #define PIN_REV        2                // INT0 = positive 1/rev pulse from optical switch
    #define PIN_STEP    5                // T1 = positive 1/step pulse from stepper driver
    #define PIN_TRIGGER    9                // OC1A = positive trigger pulse to scope
    
    //-- Trace outputs may be chosen freely
    
    #define PIN_TRACE_A    10
    #define PIN_TRACE_B    11
    #define PIN_TRACE_C    12
    
    #define PIN_LED        13                // standard Arduino LED
    
    //---------------------
    // State Variables
    
    word PulseCounter;
    
    //---------------------
    // Useful routines
    
    //--- Input & output pins
    
    void TogglePin(char bitpin) {
    digitalWrite(bitpin,!digitalRead(bitpin));    // toggle the bit based on previous output
    }
    
    //----------------
    // Initializations
    
    void setup() {
    
    pinMode(PIN_REV,INPUT);        // INT0 1/rev pulse from wheel
    
    pinMode(PIN_STEP,INPUT);        // T1 step pulse from stepper driver
    
    pinMode(PIN_LED,OUTPUT);
    digitalWrite(PIN_LED,LOW);
    
    pinMode(PIN_TRACE_A,OUTPUT);
    pinMode(PIN_TRACE_B,OUTPUT);
    pinMode(PIN_TRACE_C,OUTPUT);
    
    //--- Prepare Timer1 to count external stepper drive pulses
    
    TCCR1B = B00001000;                // Timer1: Mode 4 = CTC, TOP = OCR1A, clock stopped
    
    pinMode(PIN_TRIGGER,OUTPUT);        // OC1A to scope trigger
    
    //-- Wait for rising edge of 1/rev pulse from optical switch
    
    TCCR1A = B11000000;                        // COM1A set on compare
    TCNT1 = 0;                                // ensure we start from zero
    OCR1A = SYNC_OFFSET;                        // set step counter
    
    while(!digitalRead(PIN_REV)) {            // stall until 1/rev input rises
    TogglePin(PIN_TRACE_A);
    }
    
    //-- Got it, fire up the timer to count steps to start of first group
    
    TCCR1B |= B00000111;                        // enable clock from T1 pin, rising edge
    
    digitalWrite(PIN_LED,HIGH);                // show we got here
    digitalWrite(PIN_TRACE_A,LOW);
    
    while(!(TIFR1 & _BV(OCF1A))) {            // wait for compare
    digitalWrite(PIN_TRACE_B,digitalRead(PIN_STEP));
    continue;
    }
    TIFR1 |= _BV(OCF1A);                        // clear match flag
    
    //-- Scope sync pulse is now active, we can enter the main loop
    
    }
    
    //----------------
    // The main event
    
    void loop() {
    
    //-- Scope sync pulse active
    
    digitalWrite(PIN_LED,LOW);                // show we got here
    digitalWrite(PIN_TRACE_B,LOW);
    
    //-- Set up for first half of the group, sync high -> low
    
    TCCR1A = B10000000;                        // COM1A clear on compare
    OCR1A = (STEPS_PER_GROUP / 2) - 1;
    
    while(!(TIFR1 & _BV(OCF1A))) {            // wait for compare
    digitalWrite(PIN_TRACE_B,digitalRead(PIN_STEP));
    continue;
    }
    TIFR1 |= _BV(OCF1A);                        // clear match flag
    digitalWrite(PIN_TRACE_B,LOW);
    
    //-- Set up for the second half, sync low -> high
    
    TCCR1A = B11000000;                        // COM1A set on compare
    OCR1A = (STEPS_PER_GROUP - (STEPS_PER_GROUP / 2)) - 1;    // may be odd, so allow for that
    
    while(!(TIFR1 & _BV(OCF1A))) {            // wait for compare
    digitalWrite(PIN_TRACE_B,digitalRead(PIN_STEP));
    continue;
    }
    TIFR1 |= _BV(OCF1A);                        // clear match flag
    digitalWrite(PIN_TRACE_B,LOW);
    
    //-- Shut down counter and wait for end of 1/rev pulse
    
    #if 0
    TCCR1B &= ~B00000111;                        // turn off timer clock
    
    while(digitalRead(PIN_REV)) {                // stall until 1/rev pulse goes low again
    TogglePin(PIN_TRACE_C);
    }
    digitalWrite(PIN_TRACE_B,LOW);
    #endif
    
    }