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

  • Camouflaged Katydid

    Katydid on matching umbrella
    Katydid on matching umbrella

    This gadget appeared on an umbrella we’d left outdoors to dry.

    We wonder if the green surface seemed like a leaf…

  • Extended Sewing Machine Quilting Surface

    Extended quilting surface
    Extended quilting surface

    Mary has been quilting up a storm lately and wanted a larger surface to handle a bed-sized quilt. A table in the basement was big enough, but she wanted a larger flat surface around the sewing machine adjacent to the table.

    I converted the typing return (*) from her upstairs desk into a table, then cut a piece of aluminum-clad 1-inch foam insulation board to fit. It’s 4 feet long, a convenient length to cut from the 4×8-foot insulation board, and slightly narrower than the typing return. Cutting it required a long X-Acto knife blade, but a really sharp utility knife would work as well.

    Some stainless-steel tape finished off the edges. The tape itself is lethally sharp-edged, but it’s perfectly harmless if you do a good job of smoothing it against the foam board…

    A pair of closed-cell rigid foam blocks held one end of the board at the proper height around the sewing machine, while a pair of cutoffs from the wood pile were just the right thickness & length to extend under the other end. It turns out that precise height isn’t nearly as vital as we expected; close enough is fine.

    I cannibalized a pair of table-saw feed roller stands for this project; they had just the right height adjustment and shape to support the typing return and the foam board.

    The end result aligns the surface of the sewing machine with both the top of the table and the surface of the foam board. The quilt slides easily over the whole affair and doesn’t bunch up like it did before. Success!

    Foam support blocks
    Foam support blocks

    (*) A “typing return” is the little table that sticks out from a desk, upon which you put a typewriter, back in the day when typewriters ruled the land. Nowadays, she uses it for her sewing machine, which normally lives at her desk, because there’s no practical way to type at right angles to one’s desk.

    That’s the sort of item you can’t do web searches for, because all the terms are so heavily overloaded. Give it a try; you’ll find one or two useful hits. There’s a difference between syntax and semantics; we’re not in the semantic web yet by long yardage.

  • Sherline Z-axis Backlash: Check the Bearing Preload Nut!

    Loose bearing nut
    Loose bearing nut

    I don’t do any fancy 3D milling, so it takes a lot of Z-axis backlash to get my attention. While setting up for some circuit-board drilling, I finally noticed that the backlash far exceeded even my slovenly specs: something like 20 mils.

    The Z-axis backlash adjusting nut on the saddle was as snug as it usually is. Heaving on the saddle, though, pulled it up & down and moved the handwheel on the top of the Z-axis motor.

    Ah-ha! That says the leadscrew itself is moving, which shouldn’t be possible because it’s captured at the bearings in the stepper motor mount.

    Some tedious disassembly later, the top picture shows the Z-axis leadscrew and motor mount, with the nut obviously too far away from the lower ball bearing housing. The nut was finger-loose and I moved it while extracting the leadscrew; it’s supposed to be snug against the bearing in normal operation.

    The solution is a drop of Loctite, which should be applied to the canonical “clean and dry” threads. Hosing this part of the leadscrew down with solvents isn’t a good idea, because you don’t want any inside the lower bearing in the motor mount, so I spent some Quality Shop Time spinning the threads against a (dry) rag, running the nut to the other end (all of a few millimeters), and repeating until most of the oil was gone.

    Properly adjusted nut
    Properly adjusted nut

    Sherline documents how to assemble & install the motor mounts, so there’s not much mystery involved. I loosened the preload nut until the housing spun freely on the shaft, then tightened it a teensy bit; the housing still spun freely and there’s no detectable end play.

    Reinstallation requires putting the motor mount at the same spot on the Z-axis column as before. I moved the saddle to the top of the column, ran the leadscrew into the saddle nut, and then tightened the motor mount screws. That allows the mount to move to suit the saddle nut’s position, rather than going through the tedious saddle alignment process I mentioned as part of the gib adjustment.

    It’s all good… call it 3 mils of backlash on all three axes.

    Memo to Self: It’s possible to run the Z-axis backlash adjusting nut off the top of the leadscrew thread, then re-engage it without removing the motor mount. The trick is to hold the anti-backlash nut firmly against the saddle nut while turning the leadscrew to engage the thread. Remember that it’s a left-hand thread…

  • Clothes Rack Dowel Splicing

    Clothes Rack Dowel Glue
    Clothes Rack Dowel Glue

    Mary picked up a rather well-used wooden-dowel clothes drying rack at a tag sale for essentially nothing; one of the dowels was missing. That’s easy enough to fix, as I have a stash of dowels from what seems to be another rack of the same type on my wood stockpile…

    Of course, those dowels are just an inch or two shorter than needed.

    So…

    • Turn down the ends of two dowels to 0.29″ x 3/4″ to fit the holes in the support struts
    • Sand a small taper on the ends
    • Pull the staples, insert the longer dowel and mash the staple back in place
    • Eyeball the length of the other dowel, hacksaw to fit, install similarly
    • Find a length of brass tubing that slips over the dowels
    • Cut some heat stink shrink tubing to fit
    Spliced dowels
    Spliced dowels

    I used urethane adhesive, because it expands as it cures and will fill the gaps inside the brass tubing. The heat stink tubing is just for nice… although it does make for a rather stunning contrast to the aged wood dowels, I’ll agree.

    And it’s all good!

    (Use it up, wear it out, repair it, wear it out again, then save the pieces because they’ll come in handy for something else.)

  • Arduino: Dividing an External Frequency

    A friend asked for a Totally Featureless Clock (it’s a long story) and in order to build that, I must concoct a WWVB simulator. Not needing really precise atomic time, I can use ordinary bench-grade crystals and suchlike; microsecond-level jitter isn’t a big problem.

    Anyhow, I must divide down an external 60 kHz signal to produce a 10-Hz interrupt to drive the modulator. The 60 kHz comes from a 12 MHz crystal, through a divide-by-200 counter, and feeds the ATmega T1 input (aka Arduino pin 5, normally the PWM5 output).

    The key is setting up Timer1 to divide T1 inputs by 6000 (count 0 through 5999), then have it produce an interrupt when the maximum count occurs. You’ll want to read Chapter 15 of The Fine Manual to learn how the hardware works.

    I used CTC Mode 12, so that the counts occur on the falling edge of the signal on T1 with the maximum value stored in ICR1. That causes the Input Capture Interrupt to occur when ICR1 == TCNT1.

    Review the avr-lib interrupt doc to get the proper interrupt vector names. You want the ICF interrupt, enabled with ICIE1.

    Note that Table 15-4 is misleading. The TOV1 Flag may be set when TCNT == MAX, but unless ICR1 == MAX it’ll never get there. You (well, I) can spend a distressing amount of time figuring out why TOV1 doesn’t happen.

    With that in mind, Timer1 setup is straightforward:

    TCCR1B = 0;                // stop Timer 1 by shutting off the clock
    TCNT1 = 0;                 // force count to start from scratch
    TCCR1A = 0;                // no compare outputs to OC1A OC1B, WGM1 1:0 = 00
    TCCR1C = 0;                // no forced compares
    ICR1 = 5999;               // count 0 through 5999 = divide by 6000
    TIMSK1 = 1 << ICIE1; // allow interrupt on capture event (TCNT == ICF)
    TCCR1B = B00011110;        // start Timer 1: CTC mode = 12, TOP=ICR1, ext clock on T1, falling edge
    

    The interrupt handler can do whatever you want. This one just flips an output bit (ultimately connected to the modulator) to show it’s arrived:

    ISR(TIMER1_CAPT_vect) {
       PINB |= _BV(1);
    }
    

    That weird-looking line takes advantage of an Arduino hardware feature: if you write a 1 to a bit in the PIN register, the corresponding PORT value toggles. It’s documented in The Fine Manual on page 74, section 13.2.2 and mentioned there.

    Then the rest of the simulator is just a simple matter of software…

  • Arduino vs. ATMega168 Chip Pinouts

    The Arduino pin names are silkscreened right on the board, but sometimes you must know the corresponding ATMega168 pin name. I printed out The Fine Manual and penciled in the Arduino names, but that’s getting smudgy.

    Herewith, the ATmega168 pinout with neatly printed Arduino pin names.

    Arduino vs ATMega168 chip pinouts
    Arduino vs ATMega168 chip pinouts

    [Update:Turns out there’s an Official Version.]

    Sometimes, you also must know the relation between hardware Timers and PWM output pins:

    OC0A PWM6 PWM3 OC2B
    OC0B PWM5 PWM5 OC0B
    OC1A PWM9 PWM6 OC0A
    OC1B PWM10 PWM9 OC1A
    OC2A PWM11 PWM10 OC1B
    OC2B PWM3 PWM11 OC2A
  • Arduino: Be Careful With the Preprocessor

    This trips me up every damn time…

    Although the Arduino language looks like C and parses like C and runs like C, it’s not really C. It’s a specialized language gnawed on by a vast conversion monster and eventually shat out as executable ATmega-style instructions.

    Here’s a (rather contrived) trivial working program…

    #define PIN_ARDUINOLED    13     // standard LED
    
    typedef struct {
     int initial;
     int on;
     int off;
     int reps;
    } timeout_ ;
    
    timeout_ Trial = {2000,100,1000,5};
    
    void setup() {
     pinMode(PIN_ARDUINOLED,OUTPUT);
    }
    
    void loop() {
    
    int counter;
    
     digitalWrite(PIN_ARDUINOLED,LOW);
     delay(Trial.initial);
    
     for (counter = 0; counter < Trial.reps; ++counter) {
     digitalWrite(PIN_ARDUINOLED,HIGH);
     delay(Trial.on);
     digitalWrite(PIN_ARDUINOLED,LOW);
     delay(Trial.off);
     }
    
    }

    That compiles and runs just like you’d expect: a long delay, followed by five blinks, repeating endlessly.

    Now, use some preprocessor conditionals to gut-and-replace the code, with a bit of garish colorization to make things more obvious. This is Bad Programming Practice, but work with me…

    #define PIN_ARDUINOLED    13        // standard LED
    
    #if 0
    typedef struct {
     int initial;
     int on;
     int off;
     int reps;
    } timeout_ ;
    #endif
    
    #if 0
    timeout_ Trial = {2000,100,1000,5};
    #else
    int LoopCount = 50;
    #endif
    
    void setup() {
     pinMode(PIN_ARDUINOLED,OUTPUT);
    }
    
    void loop() {
    
    int counter;
    
    #if 0
     digitalWrite(PIN_ARDUINOLED,LOW);
     delay(Trial.initial);
    
     for (counter = 0; counter < Trial.reps; ++counter) {
     digitalWrite(PIN_ARDUINOLED,HIGH);
     delay(Trial.on);
     digitalWrite(PIN_ARDUINOLED,LOW);
     delay(Trial.off);
     }
    #else
     digitalWrite(PIN_ARDUINOLED,LOW);
     for (counter = 0; counter < LoopCount; ++counter) {
     digitalWrite(PIN_ARDUINOLED,HIGH);
     delay(250);
     digitalWrite(PIN_ARDUINOLED,LOW);
     delay(500);
     }
    
    #endif
    
    }

    The error message lump resulting from compiling that looks like:

    In function ‘void setup()’:
    error: ‘OUTPUT’ was not declared in this scope In function ‘void loop()’:
     In function ‘int main()’:

    Really?

    Delete the lines already removed by the preprocessor, the lines that shouldn’t be there when the code reaches the compiler, and you have:

    #define PIN_ARDUINOLED        13              // standard LED
    
    int LoopCount = 50;
    
    void setup() {
     pinMode(PIN_ARDUINOLED,OUTPUT);
    }
    
    void loop() {
    
    int counter;
    
     digitalWrite(PIN_ARDUINOLED,LOW);
     for (counter = 0; counter < LoopCount; ++counter) {
     digitalWrite(PIN_ARDUINOLED,HIGH);
     delay(250);
     digitalWrite(PIN_ARDUINOLED,LOW);
     delay(500);
     }
    
    }

    Which compiles and runs like a champ, of course. It blinks merrily away, more off than on, forever. The LoopCount variable doesn’t do much for us, but it’s the thought that counts.

    Put the original lines back, comment out the new stuff, and you get:

    #define PIN_ARDUINOLED        13              // standard LED
    
    typedef struct {
     int initial;
     int on;
     int off;
     int reps;
    } timeout_ ;
    
    timeout_ Trial = {2000,100,1000,5};
    
    #if 0
    int LoopCount = 50;
    #endif
    
    void setup() {
     pinMode(PIN_ARDUINOLED,OUTPUT);
    }
    
    void loop() {
    
    int counter;
    
     digitalWrite(PIN_ARDUINOLED,LOW);
     delay(Trial.initial);
    
     for (counter = 0; counter < Trial.reps; ++counter) {
     digitalWrite(PIN_ARDUINOLED,HIGH);
     delay(Trial.on);
     digitalWrite(PIN_ARDUINOLED,LOW);
     delay(Trial.off);
     }
    
    #if 0
     digitalWrite(PIN_ARDUINOLED,LOW);
     for (counter = 0; counter < LoopCount; ++counter) {
     digitalWrite(PIN_ARDUINOLED,HIGH);
     delay(250);
     digitalWrite(PIN_ARDUINOLED,LOW);
     delay(500);
     }
    
    #endif
    
    }

    And that compiles and runs perfectly, just like you’d expect. Uh-huh. Right.

    Basically, there’s a complex interaction between ordinary C preprocessor directives, ordinary C language elements, and the inscrutable innards of the Arduino IDE & compiler chain.

    As nearly as I can tell, you can wrap #if whatever around simple declarations and most executable code with impunity, but putting anything more elaborate than that, like a simple typedef struct, inside the conditionals causes bizarre problems.

    In fact, just typedef can cause problems, particularly if you attempt to use the ensuing tag in a function declaration. Don’t even think about anything along these lines:

    typedef struct {whatever} taggit_;
    ... snippage ...
    void SomeFunction(taggit_ *pThing) {
    ... snippage ...
    }

    However, this seems to work fine:

    struct taggit_ {whatever};
    ... snippage ...
    void SomeFunction(struct taggit_ *pThing) {
    ... snippage ...
    }

    Trying to make sense of this will drive you mad (well, it drives me mad), but when you get bizarre error messages that can’t be explained by the usual operator screwups, well, most likely you’re being too clever with the preprocessor.

    Or you’ve (also) made a trivial typo that cannot be discovered by inspection.

    It seems the right (only?) way to handle typedef definitions is to put ’em in a separate header file, as described there, then add the header file to your sketch in a separate tab. That seems to insert the definitions in the proper order within the *.cpp file that actually goes into the compiler.

    However, some limited fiddling reveals that I don’t understand the nuances or I’m screwing it up. Probably both.

    Memo to Self: define the structs & variables without typedefs, then prepare for some protracted tweakage…