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

  • Broom Handle Screw Thread: Now With Dedendum

    Although I don’t need another threaded plug, the most recent OpenSCAD version can handle a model including the thread dedendum:

    Broom Handle Screw - full thread - solid model
    Broom Handle Screw – full thread – solid model

    This hyper-close view (as always, clicky for more dots) shows the problem: the region where the addendum and dedendum meet at the pitch cylinder consists of a bazillion tiny faces:

    Broom Handle Screw - full thread - detail
    Broom Handle Screw – full thread – detail

    The previous version simply couldn’t handle that many elements, but the new version has a parameter that I tweaked (to 100,000), allowing it to complete the rendering. Compiling to a solid model requires about 45 minutes, most of which probably involves those unprintably small facets.

    The thread elements now taper slightly in the downhill direction, so that each quasi-cylinder nests cleanly inside the next to avoid the tiny slivers that stuck out of the joints in the previous model.

    And the new Slic3r version (from GitHub) has better internal support for those indentations around the base, which means that AC vent plug might be build-able, too.

    The OpenSCAD source code, with a few tweaks to nest the thread cylinders and properly locate the dedendum:

    // Broom Handle Screw End Plug
    // Ed Nisley KE4ZNU June 2013
    
    //- Extrusion parameters must match reality!
    //  Print with 2 shells and 3 solid layers
    
    HoleWindage = 0.2;
    
    Protrusion = 0.1;			// make holes end cleanly
    
    //----------------------
    // Dimensions
    
    PostOD = 22.3;				// post inside metal handle
    PostLength = 25.0;
    
    FlangeOD = 24.0;			// stop flange
    FlangeLength = 3.0;
    
    PitchDia = 15.5;			// thread center diameter
    ScrewLength = 20.0;
    
    ThreadFormOD = 2.5;			// diameter of thread form
    ThreadPitch = 5.0;
    
    BoltOD = 7.0;				// clears 1/4-20 bolt
    BoltSquare = 6.5;			// across flats
    BoltHeadThick = 3.0;
    
    RecessDia = 6.0;			// recesss to secure post in handle
    
    OALength = PostLength + FlangeLength + ScrewLength;
    
    $fn=8*4;					// default cylinder sides
    
    echo("Pitch dia: ",PitchDia);
    echo("Root dia: ",PitchDia - ThreadFormOD);
    echo("Crest dia: ",PitchDia + ThreadFormOD);
    
    Pi = 3.14159265358979;
    
    //----------------------
    // Useful routines
    
    // Wrap cylindrical thread segments around larger plug cylinder
    
    module CylinderThread(Pitch,Length,PitchDia,ThreadOD,PerTurn=32) {
    
    CylFudge = 1.02;				// force overlap
    
        RotIncr = 1/PerTurn;
        PitchRad = PitchDia/2;
    
        Turns = Length/Pitch;
        NumCyls = Turns*PerTurn;
    
        ZStep = Pitch / PerTurn;
    
        HelixAngle = atan(Pitch/(Pi*PitchDia));
        CylLength = CylFudge * (Pi*(PitchDia + ThreadOD) / PerTurn) / cos(HelixAngle);
    
    	for (i = [0:NumCyls-1]) {
    		assign(Angle = 360*i/PerTurn)
    			translate([PitchRad*cos(Angle),PitchRad*sin(Angle),i*ZStep])
    				rotate([90+HelixAngle,0,Angle])
    					cylinder(r1=ThreadOD/2,
    							r2=ThreadOD/(2*CylFudge),
    							h=CylLength,
    							center=true,$fn=12);
    	}
    }
    
    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);
    }
    
    module ShowPegGrid(Space = 10.0,Size = 1.0) {
    
      Range = floor(50 / Space);
    
    	for (x=[-Range:Range])
    	  for (y=[-Range:Range])
    		translate([x*Space,y*Space,Size/2])
    		  %cube(Size,center=true);
    
    }
    
    //-------------------
    // Build it...
    
    ShowPegGrid();
    
    difference() {
        union() {
            cylinder(r=PostOD/2,h=PostLength);
            cylinder(r=PitchDia/2,h=OALength);
            translate([0,0,PostLength])
                cylinder(r=FlangeOD/2,h=FlangeLength);
            color("Orange")
            translate([0,0,(PostLength + FlangeLength)])
                CylinderThread(ThreadPitch,(ScrewLength - ThreadFormOD/2),PitchDia,ThreadFormOD);
        }
    
        translate([0,0,-Protrusion])
            PolyCyl(BoltOD,(OALength + 2*Protrusion),6);
        translate([0,0,(OALength - BoltHeadThick)])
            PolyCyl(BoltSquare,(BoltHeadThick + Protrusion),4);
    
        translate([0,0,(PostLength + FlangeLength + ThreadFormOD/2)])
    		rotate(-90)
            CylinderThread(ThreadPitch,ScrewLength,PitchDia,ThreadFormOD);
    
    	for (i = [0:90:270]) {
    		rotate(i)
    			translate([PostOD/2,0,PostLength/2])
    				sphere(r=RecessDia/2,$fn=8);
    	}
    }
    
  • Mesa 5i25 + 7i76: First Light!

    Rather than start with the stepper, I wired an LED and resistor between output bit 07 and Field Ground at the power supply:

    Mesa 5i26-7i76 with LED
    Mesa 5i26-7i76 with LED

    It’s worth noting that the terminals labeled GND on TB2 and TB3 are isolated from the Field GROUND terminal on TB1. When Mesa says “isolated power supply”, that’s exactly what they mean.

    The digital output bits connect +24 VDC Field Power to the load, which should then connect to Field GROUND. I picked a good-looking 5 V panel LED from the pile, simply because it had wires soldered to it from a previous life, and put a 1 K resistor in series to drop the other 19 V.

    Then you start up HAL, load the Mesa drivers, and twiddle the bit:

    halrun
    halcmd: loadrt threads name1=th period1=1000000
    halcmd: loadrt hostmot2
    halcmd: loadrt hm2_pci
    halcmd: addf hm2_5i25.0.pet_watchdog th
    halcmd: addf hm2_5i25.0.read th
    halcmd: addf hm2_5i25.0.write th
    halcmd: start
    halcmd: setp hm2_5i25.0.7i76.0.0.output-07 true
    halcmd: setp hm2_5i25.0.7i76.0.0.output-07 false
    halcmd: quit
    

    The thread runs with a 1 ms period, mostly because it’s convenient. The .read and .write pins transfer data from and to the 5i25 FPGA each time the thread runs; if you forget those, nothing happens.

    Setting the output bit true activates the output bit, turns on the MOSFET driver, and connects the terminal to Field Power = 24 VDC. The 7i76 outputs do not sink current, they source it.

    A journey of a thousand 3D printed objects starts with a single LED…

    The watchdog timer ought to be connected to something more fragile and UI-related than the main thread, but I haven’t figured out how to do that yet.

  • Mesa 5i25 + 7i76: HAL Pins

    Some notes on setting up the Mesa 5i25 FPGA card (the manual) with the 7i76 daughter card (the manual) inside a new-to-me off-lease Dell Optiplex 760

    First up: note that Mesa uses a capital I (“eye”) in the part numbers, a decision which they’ve surely had plenty of time to regret, as many common fonts exhibit nearly identical capital-I and digit-1 characters.

    The 7i76 connects to the 5i25 in the PC through a Mesa-supplied IEEE-1284 printer cable. I cobbled up a 24 VDC power supply (which I’ll eventually be using for the M2 motors) to provide “field power” and let the firmware identify the daughtercard:

    24 VDC power supply - Mesa 7i76 - stepper driver
    24 VDC power supply – Mesa 7i76 – stepper driver

    The default jumper positions on both cards work fine.

    The unconnected stepper driver brick and motor will serve as a simple demonstration after I’ve built the Eagle parts to represent the 5i25’s components. However, the first demo of any new hardware must be a blinking LED.

    To see whether the cards work and are detected, load the hostmot2 drivers in halrun and dump all the information:

    halrun
    halcmd: loadrt hostmot2
    halcmd: loadrt hm2_pci
    halcmd: show all
    Loaded HAL Components:
    ID      Type  Name                                      PID   State
         5  RT    hm2_pci                                         ready
         3  User  halcmd5010                                 5010 ready
         4  RT    hostmot2                                        ready
    
    Component Pins:
    Owner   Type  Dir         Value  Name
         5  bit   OUT         FALSE  hm2_5i25.0.7i76.0.0.input-00
         5  bit   OUT         FALSE  hm2_5i25.0.7i76.0.0.input-00-not
         5  bit   OUT         FALSE  hm2_5i25.0.7i76.0.0.input-01
    ... snippage ...
         5  bit   OUT         FALSE  hm2_5i25.0.7i76.0.0.input-30
         5  bit   OUT         FALSE  hm2_5i25.0.7i76.0.0.input-30-not
         5  bit   OUT         FALSE  hm2_5i25.0.7i76.0.0.input-31
         5  bit   OUT         FALSE  hm2_5i25.0.7i76.0.0.input-31-not
         5  bit   IN          FALSE  hm2_5i25.0.7i76.0.0.output-00
         5  bit   IN          FALSE  hm2_5i25.0.7i76.0.0.output-01
    ... snippage ...
         5  bit   IN          FALSE  hm2_5i25.0.7i76.0.0.output-15
         5  bit   IN          FALSE  hm2_5i25.0.7i76.0.0.spindir
         5  bit   IN          FALSE  hm2_5i25.0.7i76.0.0.spinena
         5  float IN              0  hm2_5i25.0.7i76.0.0.spinout
         5  s32   OUT             0  hm2_5i25.0.encoder.00.count
         5  s32   OUT             0  hm2_5i25.0.encoder.00.count-latched
         5  bit   I/O         FALSE  hm2_5i25.0.encoder.00.index-enable
         5  bit   IN          FALSE  hm2_5i25.0.encoder.00.latch-enable
         5  bit   IN          FALSE  hm2_5i25.0.encoder.00.latch-polarity
         5  float OUT             0  hm2_5i25.0.encoder.00.position
         5  float OUT             0  hm2_5i25.0.encoder.00.position-latched
         5  s32   OUT             0  hm2_5i25.0.encoder.00.rawcounts
         5  s32   OUT             0  hm2_5i25.0.encoder.00.rawlatch
         5  bit   IN          FALSE  hm2_5i25.0.encoder.00.reset
         5  float OUT             0  hm2_5i25.0.encoder.00.velocity
         5  s32   OUT             0  hm2_5i25.0.encoder.01.count
    ... snippage ...
         5  float OUT             0  hm2_5i25.0.encoder.01.velocity
         5  bit   OUT         FALSE  hm2_5i25.0.gpio.000.in
         5  bit   OUT          TRUE  hm2_5i25.0.gpio.000.in_not
         5  bit   OUT         FALSE  hm2_5i25.0.gpio.001.in
    ... snippage ...
         5  bit   OUT          TRUE  hm2_5i25.0.gpio.032.in
         5  bit   OUT         FALSE  hm2_5i25.0.gpio.032.in_not
         5  bit   OUT          TRUE  hm2_5i25.0.gpio.033.in
         5  bit   OUT         FALSE  hm2_5i25.0.gpio.033.in_not
         5  bit   IN          FALSE  hm2_5i25.0.led.CR01
         5  bit   IN          FALSE  hm2_5i25.0.led.CR02
         5  u32   IN     0x00000000  hm2_5i25.0.sserial.channel
         5  u32   IN     0x00000000  hm2_5i25.0.sserial.parameter
         5  u32   IN     0x00000000  hm2_5i25.0.sserial.port
         5  u32   OUT    0x00000000  hm2_5i25.0.sserial.port-0.fault-count
         5  u32   OUT    0x00000000  hm2_5i25.0.sserial.port-0.port_state
         5  bit   IN           TRUE  hm2_5i25.0.sserial.port-0.run
         5  bit   IN          FALSE  hm2_5i25.0.sserial.read
         5  u32   OUT    0x00000000  hm2_5i25.0.sserial.state
         5  u32   IN     0x00000000  hm2_5i25.0.sserial.value
         5  bit   IN          FALSE  hm2_5i25.0.sserial.write
         5  bit   IN          FALSE  hm2_5i25.0.stepgen.00.control-type
         5  s32   OUT             0  hm2_5i25.0.stepgen.00.counts
         5  float OUT             0  hm2_5i25.0.stepgen.00.dbg_err_at_match
         5  float OUT             0  hm2_5i25.0.stepgen.00.dbg_ff_vel
         5  float OUT             0  hm2_5i25.0.stepgen.00.dbg_pos_minus_prev_
         5  float OUT             0  hm2_5i25.0.stepgen.00.dbg_s_to_match
         5  s32   OUT             0  hm2_5i25.0.stepgen.00.dbg_step_rate
         5  float OUT             0  hm2_5i25.0.stepgen.00.dbg_vel_error
         5  bit   IN          FALSE  hm2_5i25.0.stepgen.00.enable
         5  float IN              0  hm2_5i25.0.stepgen.00.position-cmd
         5  float OUT             0  hm2_5i25.0.stepgen.00.position-fb
         5  float IN              0  hm2_5i25.0.stepgen.00.velocity-cmd
         5  float OUT             0  hm2_5i25.0.stepgen.00.velocity-fb
         5  bit   IN          FALSE  hm2_5i25.0.stepgen.01.control-type
    ... snippage ...
         5  float OUT             0  hm2_5i25.0.stepgen.09.velocity-fb
         5  bit   I/O         FALSE  hm2_5i25.0.watchdog.has_bit
    
    ... snippage ...
    
    Parameters:
    Owner   Type  Dir         Value  Name
         5  bit   RW          FALSE  hm2_5i25.0.7i76.0.0.output-00-invert
         5  bit   RW          FALSE  hm2_5i25.0.7i76.0.0.output-01-invert
    ... snippage ...
         5  bit   RW          FALSE  hm2_5i25.0.7i76.0.0.output-15-invert
         5  u32   RO     0x100000A5  hm2_5i25.0.7i76.0.0.serial-number
         5  bit   RW          FALSE  hm2_5i25.0.7i76.0.0.spindir-invert
         5  bit   RW          FALSE  hm2_5i25.0.7i76.0.0.spinena-invert
         5  float RW            100  hm2_5i25.0.7i76.0.0.spinout-maxlim
         5  float RW              0  hm2_5i25.0.7i76.0.0.spinout-minlim
         5  float RW            100  hm2_5i25.0.7i76.0.0.spinout-scalemax
         5  u32   RO     0x00000000  hm2_5i25.0.7i76.0.0.status
         5  bit   RW          FALSE  hm2_5i25.0.encoder.00.counter-mode
         5  bit   RW           TRUE  hm2_5i25.0.encoder.00.filter
         5  bit   RW          FALSE  hm2_5i25.0.encoder.00.index-invert
         5  bit   RW          FALSE  hm2_5i25.0.encoder.00.index-mask
         5  bit   RW          FALSE  hm2_5i25.0.encoder.00.index-mask-invert
         5  float RW              1  hm2_5i25.0.encoder.00.scale
         5  float RW            0.5  hm2_5i25.0.encoder.00.vel-timeout
         5  bit   RW          FALSE  hm2_5i25.0.encoder.01.counter-mode
    ... snippage ...
         5  float RW            0.5  hm2_5i25.0.encoder.01.vel-timeout
         5  bit   RW          FALSE  hm2_5i25.0.gpio.000.invert_output
         5  bit   RW          FALSE  hm2_5i25.0.gpio.000.is_opendrain
         5  bit   RW          FALSE  hm2_5i25.0.gpio.001.invert_output
    ... snippage ...
         5  bit   RW          FALSE  hm2_5i25.0.gpio.030.invert_output
         5  bit   RW          FALSE  hm2_5i25.0.gpio.030.is_opendrain
         5  bit   RW          FALSE  hm2_5i25.0.gpio.030.is_output
         5  bit   RW          FALSE  hm2_5i25.0.io_error
         5  s32   RO              0  hm2_5i25.0.pet_watchdog.time
         5  s32   RW              0  hm2_5i25.0.pet_watchdog.tmax
         5  s32   RO              0  hm2_5i25.0.read.time
         5  s32   RW              0  hm2_5i25.0.read.tmax
         5  s32   RO              0  hm2_5i25.0.read_gpio.time
         5  s32   RW              0  hm2_5i25.0.read_gpio.tmax
         5  u32   RW     0x00000001  hm2_5i25.0.sserial.port-0.fault-dec
         5  u32   RW     0x0000000A  hm2_5i25.0.sserial.port-0.fault-inc
         5  u32   RW     0x000000C8  hm2_5i25.0.sserial.port-0.fault-lim
         5  u32   RW     0x00077FE2  hm2_5i25.0.stepgen.00.dirhold
         5  u32   RW     0x00077FE2  hm2_5i25.0.stepgen.00.dirsetup
         5  float RW              1  hm2_5i25.0.stepgen.00.maxaccel
         5  float RW              0  hm2_5i25.0.stepgen.00.maxvel
         5  float RW              1  hm2_5i25.0.stepgen.00.position-scale
         5  u32   RW     0x00000000  hm2_5i25.0.stepgen.00.step_type
         5  u32   RW     0x00077FE2  hm2_5i25.0.stepgen.00.steplen
         5  u32   RW     0x00077FE2  hm2_5i25.0.stepgen.00.stepspace
         5  u32   RW     0x00077FE2  hm2_5i25.0.stepgen.01.dirhold
    ... snippage ...
         5  u32   RW     0x00077FE2  hm2_5i25.0.stepgen.09.stepspace
         5  u32   RW     0x004C4B40  hm2_5i25.0.watchdog.timeout_ns
         5  s32   RO              0  hm2_5i25.0.write.time
         5  s32   RW              0  hm2_5i25.0.write.tmax
         5  s32   RO              0  hm2_5i25.0.write_gpio.time
         5  s32   RW              0  hm2_5i25.0.write_gpio.tmax
    
    Parameter Aliases:
     Alias                                      Original Name
    
    Exported Functions:
    Owner   CodeAddr  Arg       FP   Users  Name
     00005  fc3d2582  f1b17000  NO       0   hm2_5i25.0.pet_watchdog
     00005  fc3c49dc  f1b17000  YES      0   hm2_5i25.0.read
     00005  fc3c4906  f1b17000  YES      0   hm2_5i25.0.read_gpio
     00005  fc3c4936  f1b17000  YES      0   hm2_5i25.0.write
     00005  fc3c48d6  f1b17000  YES      0   hm2_5i25.0.write_gpio
    
    ... snippage ...
    

    Extract the 5i25 pin assignments from the kernel log file:
    dmesg | grep hm2

    Which produces this:

    [ed@lcnc-m2 LinuxCNC for M2]$ dmesg | grep hm2
    [ 7299.887856] hm2: loading Mesa HostMot2 driver version 0.15
    [ 7407.514601] hm2_pci: loading Mesa AnyIO HostMot2 driver version 0.7
    [ 7407.514631] hm2_pci 0000:04:02.0: PCI INT A -> GSI 18 (level, low) -> IRQ 18
    [ 7407.514634] hm2_pci: discovered 5i25 at 0000:04:02.0
    [ 7407.514656] hm2: no firmware specified in config modparam!  the board had better have firmware configured already, or this won't work
    [ 7407.515018] hm2/hm2_5i25.0: Smart Serial Firmware Version 38
    [ 7407.632326] hm2/hm2_5i25.0: 34 I/O Pins used:
    [ 7407.632329] hm2/hm2_5i25.0:     IO Pin 000 (P3-01): StepGen #0, pin Direction (Output)
    [ 7407.632331] hm2/hm2_5i25.0:     IO Pin 001 (P3-14): StepGen #0, pin Step (Output)
    [ 7407.632334] hm2/hm2_5i25.0:     IO Pin 002 (P3-02): StepGen #1, pin Direction (Output)
    [ 7407.632336] hm2/hm2_5i25.0:     IO Pin 003 (P3-15): StepGen #1, pin Step (Output)
    [ 7407.632338] hm2/hm2_5i25.0:     IO Pin 004 (P3-03): StepGen #2, pin Direction (Output)
    [ 7407.632340] hm2/hm2_5i25.0:     IO Pin 005 (P3-16): StepGen #2, pin Step (Output)
    [ 7407.632343] hm2/hm2_5i25.0:     IO Pin 006 (P3-04): StepGen #3, pin Direction (Output)
    [ 7407.632345] hm2/hm2_5i25.0:     IO Pin 007 (P3-17): StepGen #3, pin Step (Output)
    [ 7407.632347] hm2/hm2_5i25.0:     IO Pin 008 (P3-05): StepGen #4, pin Direction (Output)
    [ 7407.632349] hm2/hm2_5i25.0:     IO Pin 009 (P3-06): StepGen #4, pin Step (Output)
    [ 7407.632352] hm2/hm2_5i25.0:     IO Pin 010 (P3-07): Smart Serial Interface #0, pin TxData0 (Output)
    [ 7407.632354] hm2/hm2_5i25.0:     IO Pin 011 (P3-08): Smart Serial Interface #0, pin RxData0 (Input)
    [ 7407.632356] hm2/hm2_5i25.0:     IO Pin 012 (P3-09): IOPort
    [ 7407.632358] hm2/hm2_5i25.0:     IO Pin 013 (P3-10): IOPort
    [ 7407.632360] hm2/hm2_5i25.0:     IO Pin 014 (P3-11): Encoder #0, pin Index (Input)
    [ 7407.632362] hm2/hm2_5i25.0:     IO Pin 015 (P3-12): Encoder #0, pin B (Input)
    [ 7407.632364] hm2/hm2_5i25.0:     IO Pin 016 (P3-13): Encoder #0, pin A (Input)
    [ 7407.632367] hm2/hm2_5i25.0:     IO Pin 017 (P2-01): StepGen #5, pin Direction (Output)
    [ 7407.632369] hm2/hm2_5i25.0:     IO Pin 018 (P2-14): StepGen #5, pin Step (Output)
    [ 7407.632371] hm2/hm2_5i25.0:     IO Pin 019 (P2-02): StepGen #6, pin Direction (Output)
    [ 7407.632373] hm2/hm2_5i25.0:     IO Pin 020 (P2-15): StepGen #6, pin Step (Output)
    [ 7407.632376] hm2/hm2_5i25.0:     IO Pin 021 (P2-03): StepGen #7, pin Direction (Output)
    [ 7407.632378] hm2/hm2_5i25.0:     IO Pin 022 (P2-16): StepGen #7, pin Step (Output)
    [ 7407.632380] hm2/hm2_5i25.0:     IO Pin 023 (P2-04): StepGen #8, pin Direction (Output)
    [ 7407.632382] hm2/hm2_5i25.0:     IO Pin 024 (P2-17): StepGen #8, pin Step (Output)
    [ 7407.632385] hm2/hm2_5i25.0:     IO Pin 025 (P2-05): StepGen #9, pin Direction (Output)
    [ 7407.632387] hm2/hm2_5i25.0:     IO Pin 026 (P2-06): StepGen #9, pin Step (Output)
    [ 7407.632389] hm2/hm2_5i25.0:     IO Pin 027 (P2-07): IOPort
    [ 7407.632391] hm2/hm2_5i25.0:     IO Pin 028 (P2-08): IOPort
    [ 7407.632392] hm2/hm2_5i25.0:     IO Pin 029 (P2-09): IOPort
    [ 7407.632394] hm2/hm2_5i25.0:     IO Pin 030 (P2-10): IOPort
    [ 7407.632396] hm2/hm2_5i25.0:     IO Pin 031 (P2-11): Encoder #1, pin Index (Input)
    [ 7407.632398] hm2/hm2_5i25.0:     IO Pin 032 (P2-12): Encoder #1, pin B (Input)
    [ 7407.632401] hm2/hm2_5i25.0:     IO Pin 033 (P2-13): Encoder #1, pin A (Input)
    [ 7407.632443] hm2/hm2_5i25.0: registered
    [ 7407.632445] hm2_5i25.0: initialized AnyIO board at 0000:04:02.0
    [ 7487.136417] hm2_5i25.0: dropping AnyIO board at 0000:04:02.0
    [ 7487.136422] hm2/hm2_5i25.0: unregistered
    [ 7487.136440] hm2_pci 0000:04:02.0: PCI INT A disabled
    [ 7487.136459] hm2_pci: driver unloaded
    [ 7487.138640] hm2: unloading
    

    I am, perhaps, easily confused, but it took me a while to realize those pin assignments apply to the 5i25 back panel and on-card connectors, not the 7i76 daughter card’s screw terminals. Yeah, it says 5i25 right there in the dump, but …

    The Fine 7i76 Manual gives the 7i76 pin connections, so they’re not even slightly hidden. [sigh]

    Next, to see if it actually works …

  • LinuxCNC: Optiplex 760 Setup

    I planned to use an old Dell Inspiron 531S AMD desktop for the LinuxCNC installation, but it turned out to have terrible interrupt latency, despite fiddling with all the available BIOS settings and video drivers. Mostly, it ran fine, but would occasionally burp up a millisecond-long latency spike for no apparent reason. So it’s now on the harvest / recycle heap.

    A new-to-me off-lease Dell Optiplex 760 Core 2 Duo in the SDT (Small Desktop Tower) configuration has similar latency numbers:

    Optiplex 760 latency - isolcpu 1
    Optiplex 760 latency – isolcpu 1

    What’s important here is that the latency remains rock-solid stable at those numbers. Contrary to my experience with the D520 and D525 Atoms, isolating one CPU for the real-time tasks didn’t make any noticeable difference, but it’s running that way because the overall performance isn’t a problem.

    Latency around 20 μs is near the upper limit for successful software step generation at any reasonable pace; the LinuxCNC description has more details. In round numbers, running the M2 at 500 mm/s needs a 40 kHz step rate in 1/16 microstep mode = a 25 μs period, which means 20 μs of jitter wouldn’t work well at all. Which is why I’m using Mesa FPGA card to get hardware step generation: it makes such problems Go Away.

    The Optiplex arrived with Windows Vista Business preinstalled; it dates back to mid-2009. I used System Rescue CD to shrink the Windows partition, added a few more, then installed LinuxCNC direct from the CD image (based on Ubuntu 10.04 LTS) and Xubuntu 13.04. The latter serves as a general-purpose installation for times when I don’t need LinuxCNC, because 10.04 is pretty much obsolete for anything other than real-time control.

    Digression 1: Yes, 10.04 LTS. TheRTAI project hasn’t released the patches that will slip the real-time kernel under the stock 3.x Linux kernel: LinuxCNC remains stuck at 10.04 LTS. Those changes have been coming Real Soon Now for quite a while; as with most Open Source projects, they could use more manpower and money. This isn’t a problem, as LinuxCNC is used for motion control, not a general-purpose operating system.

    The SDT case has room for two PCI cards and one PCI-E video card, so I installed the dual-head video card that couldn’t handle the U2711 monitor’s dual-DVI connection (although I’m using only DVI Output 1) and a Mesa 5i25. The middle “card” is actually a tiny PCB connected to a ribbon cable that brings out a second serial port (remember serial ports?) and what could be either or both of a PS2 keyboard or mouse connection (remember PS/2?).

    Optiplex 760 SDT - dual DVI - serial - 5i25
    Optiplex 760 SDT – dual DVI – serial – 5i25

    The back panel has a parallel printer port (which may come in handy for something) and a serial port, although you’re expected to have USB mice and keyboards these days. The front panel even has a floppy drive…

    Digression 2: LinuxCNC does not require a parallel printer port; this seems to be a common misconception among folks who don’t actually know how it works. The Mesa 5i25 FPGA card with a 7i76 step-direction daughter board provides high-resolution timing for five axes, rotary encoder inputs, a bunch of buffered digital I/O bits, a watchdog timer, plus various other useful odds and ends, all behind handy screw terminals.

    The Optiplex 760 has on-board VGA-class video that would also work fine, but the monitor I’m using has its VGA input connected to the box driving the Sherline mill and an unused DVI input. Having that dual-DVI monitor card lying around, I figured I could attach the same monitor to both systems and just poke the monitor’s input section button; I’ve found KVM switches unreliable in this application.

    The usual setup preps the system for public-key SSH on a nonstandard port, sets up the NFS mounts, and tweaks this-and-that: it’s running just fine.

    Digression 3: SSH kvetches when you swap server boxes at the same IP address, as well it should. If you’re foolish enough to have two separate Linux installs on the same box with the same IP, SSH reminds you every time you boot the other distro…

  • Portable AC Vent Plug

    This plug for a portable air conditioner’s window vent may be un-buildable with my current state of 3D printer-fu. The top view shows the recess for a disk of insulating foam:

    Portable AC Vent Plug - solid model - top
    Portable AC Vent Plug – solid model – top

    The side view shows the thread profile and the groove for the O-ring seal:

    Portable AC Vent Plug - solid model - side
    Portable AC Vent Plug – solid model – side

    The bottom view shows the hemispheric finger grip recess:

    Portable AC Vent Plug - solid model - bottom
    Portable AC Vent Plug – solid model – bottom

    Basically, that design requires extensive support material no matter how it’s laid out. I tried the obvious way without any support, but that huge flat surface popped off the glass:

    AC Vent Plug - flat build
    AC Vent Plug – flat build

    The thread and groove overhangs in that orientation would require support and then extensive cleanout. Slic3r doesn’t do a good job of supporting internal layers, so the bottom of the recesses tend to flop into the hexagonal infill. I’m not sure building internal support all the way up the inside of the threads would be a Good Thing, though.

    Rotating by 90 degrees and cutting it in half failed because the automagic support structure popped off the platform:

    AC Vent Plug - split build
    AC Vent Plug – split build

    Admittedly, that was before I started using hairspray (on the platform!), but both orientations require far too much support. In fact, the rotated version might weigh half a kilo…

    I’m also generating the thread elements incorrectly; the joints don’t meet smoothly at the junctions. I think tapering each element so the smaller end nests inside the larger end will work better. Perhaps using a scaled hexagonal element would be better / faster than the current extruded 2D shape?

    I think the correct way to proceed will be a 3D print of the finger grip and flange section, oriented so the hemisphere points upward, with the threaded section made from a length of PVC pipe with lathe-turned threads and O-ring groove, butted against the flange around the grip section.

    Problem: that’s an 8 mm pitch thread and my inch-size lathe doesn’t do metric:

    • Need: 8 mm = 0.315 in → 3.175 TPI
    • Closest: 3-1/4 TPI → 0.308 in = 7.82 mm
    • Worse: 3 TPI → 0.333 in = 8.47 mm

    The plug needs about three turns, which means the 3-1/4 TPI = 7.82 mm pitch thread would be off by 0.54 mm, roughly a third of the thread form’s crest. That might actually work, as the “thread” on the inside of the pipe this thing fits into is actually a thin ridge, rather than an actual thread shape, and the plug is supposed to jam against the flange anyway.

    Maybe a four-axis setup in the Sherline, with the rotary table holding the PVC pipe (or whatever) aligned with the X axis? It would just barely fit under the spindle with the end mill in a collet.

    The pipe rack doesn’t hold any suitable plastic pipe.

    The OpenSCAD source code:

    // Portable AC Vent Plug
    // Ed Nisley KE4ZNU - June 2013
    
    Layout = "Show";			// Show Build Grip Helix HelixUnit Ring Plug Stiffener
    //-------
    //- Extrusion parameters must match reality!
    //  Print with +0 shells and 3 solid layers
    
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    
    HoleWindage = 0.3;
    
    Protrusion = 0.1;			// make holes end cleanly
    
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    
    //-------
    // Plug Dimensions
    
    PlugLength = 53.0;
    PlugOD = 128.0;
    
    FlangeThick = 5.0;
    FlangeWidth = 4.0;
    FlangeOD = 2*FlangeWidth + PlugOD;
    
    HelixPitch = 8.0;
    HelixOffset = 29.0 - HelixPitch/2;		// add half turn to ensure snug fit against flange
    HelixLength = PlugLength - HelixOffset;
    HelixDepth = 3.0;
    HelixBase = 3.5;
    HelixTip = 1.8;
    
    ORingDia = 3.4;
    ORingDepth = 0.75 * ORingDia;
    ORingOffset = ORingDia/2;
    
    GripOD = 90.0;
    GripDepth = 20.0;
    GripBar = 15.0;
    
    AlignDia = 3.0;
    AlignDepth = 5.0;
    
    FoamOD = PlugOD - 4*HelixDepth;
    FoamDepth = PlugLength - GripDepth;
    
    NumSides = 12*4;
    $fn = NumSides;
    
    echo("Flange OD: ",FlangeOD);
    echo("Overall length:",(FlangeThick + PlugLength));
    
    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);
    }
    
    module ShowPegGrid(Space = 10.0,Size = 1.0) {
    
      RangeX = floor(100 / Space);
      RangeY = floor(125 / Space);
    
    	for (x=[-RangeX:RangeX])
    	  for (y=[-RangeY:RangeY])
    		translate([x*Space,y*Space,Size/2])
    		  %cube(Size,center=true);
    
    }
    
    //-------
    // Recessed finger grip
    // Includes Protrusion below Z=0 for clean removal from plug
    
    module GripShape(Length,Width,Depth) {
    
    Radius = (pow(Depth,2) + pow(Length,2)/4) / (2*Depth);
    
    	difference(convexity=2) {
    		intersection() {
    			translate([0,0,(-Radius + Depth)])
    				sphere(r=Radius);
    			translate([0,0,(Depth/2 - Protrusion)])
    				cube([2*Radius,2*Radius,(Depth + Protrusion)],center=true);
    		}
    		translate([0,0,Depth/2])
    			cube([2*Radius,Width,(Depth + Protrusion)],center=true);
    	}
    }
    
    //-------
    // One section of the screw thread helix
    
    module HelixUnit() {
    
    UnitAdjust = 1.25;
    
    	linear_extrude(height=UnitAdjust*PI*PlugOD/NumSides,center=true)
    		polygon(points=[
    				[0,0],
    				[-HelixDepth,(HelixBase - HelixTip)/2],
    				[-HelixDepth,HelixBase - (HelixBase - HelixTip)/2],
    				[0,HelixBase],
    				[Protrusion,HelixBase],
    				[Protrusion,0],
    				[0,0]
    		]);
    }
    
    //-------
    // Helix Element Shape
    
    module HelixShape() {
    
        Turns = HelixLength/HelixPitch;
        Slices = Turns*NumSides;
        RotIncr = 1/NumSides;
        PitchRad = PlugOD/2;
        ZIncr = HelixLength/Slices;
        helixangle = atan(HelixPitch/(3.14159265358979*PlugOD));
    
        for (i = [0:Slices-1]) {
    		translate([PitchRad*cos(360*i/NumSides),PitchRad*sin(360*i/NumSides),i*ZIncr])
    			rotate([90+helixangle,0,360*i/NumSides])
    				HelixUnit();
    	}
    
    }
    
    //-------
    // O Ring Shape
    
    module ORingShape(MajorDia,MinorDia,Recess) {
    	rotate_extrude()
    		translate([MajorDia/2,0,0])
    			polygon(points=[[0,0],
    							[-Recess,0.25*MinorDia],
    							[-Recess,0.75*MinorDia],
    							[0,MinorDia],
    							[Protrusion,MinorDia],
    							[Protrusion,0],
    							[0,0]]);
    }
    
    //-------
    // Overall plug shape
    
    module PlugShape() {
    
    	difference(convexity=5) {
    		union() {
    			cylinder(r=FlangeOD/2,h=FlangeThick);
    			cylinder(r=PlugOD/2,h=(PlugLength + FlangeThick));
    		}
    		translate([0,0,(PlugLength + FlangeThick - FoamDepth)])
    			cylinder(r=FoamOD/2,h=(FoamDepth + Protrusion));
    		translate([0,0,(ORingOffset + FlangeThick - ORingDia/2)])
    			ORingShape(PlugOD,ORingDia,ORingDepth);
    		GripShape(GripOD,GripBar,GripDepth);
    /*		for (x=[-1,1])
    			translate([(x*PlugOD/4),AlignDepth,(FlangeThick + PlugLength/2)])
    				rotate([90,0,0])
    					PolyCyl(AlignDia,2*AlignDepth,6);
    */
    	}
    }
    
    //-------
    // Plug assembly
    
    module PlugAssembly() {
    
    	difference(convexity=5) {
    		render(convexity=5) PlugShape();
    		translate([0,0,(FlangeThick + HelixOffset)])
    			HelixShape();
    	}
    }
    
    //-------
    // Build it!
    
    ShowPegGrid();
    
    if (Layout == "Plug") {
    	PlugShape();
    }
    
    if (Layout == "Show") {
    	PlugAssembly();
    }
    
    if (Layout == "Build") {
    	difference() {
    		intersection(convexity=10) {
    			translate([0,0,FlangeOD/2])
    				cube([2*FlangeOD,3*PlugLength,FlangeOD],center=true);
    			union() {
    				translate([0,10,0])
    					rotate([90,0,180])
    						PlugAssembly();
    				translate([0,-10,0])
    					rotate([-90,0,180])
    						PlugAssembly();
    			}
    		}
    	}
    }
    
    if (Layout == "Grip")
    	GripShape(GripOD,GripBar,GripDepth);
    
    if (Layout == "Ring")
    	ORingShape(PlugOD,ORingDia,ORingDepth);
    
    if (Layout == "HelixUnit")
    	HelixUnit();
    
    if (Layout == "Helix")
    	HelixShape();
    
  • TC4Server: Eagle HAL Device

    Dan Newman’s TC4Server turns the TC4 thermocouple board into a USB HID input device that’s compatible with HAL’s hal_input module:

    TC4 on ProtoScrewShield on Leonardo
    TC4 on ProtoScrewShield on Leonardo

    For simplicity (i.e., to avoid writing a special driver), TC4Server misrepresents itself as a nine-axis joystick-like device suited for RC airplane control:

    halrun
    halcmd: loadusr -W hal_input +A Leonardo
    halcmd: show
    ... snippage ...
    Component Pins:
    Owner   Type  Dir         Value  Name
         5  s32   OUT          2941  input.0.abs-rudder-counts
         5  s32   IN           4095  input.0.abs-rudder-flat
         5  s32   IN            255  input.0.abs-rudder-fuzz
         5  bit   OUT          TRUE  input.0.abs-rudder-is-neg
         5  bit   OUT         FALSE  input.0.abs-rudder-is-pos
         5  float IN        32767.5  input.0.abs-rudder-offset
         5  float OUT    -0.9102464  input.0.abs-rudder-position
         5  float IN        32767.5  input.0.abs-rudder-scale
         5  s32   OUT          2947  input.0.abs-rx-counts
         5  s32   IN           4095  input.0.abs-rx-flat
         5  s32   IN            255  input.0.abs-rx-fuzz
         5  bit   OUT          TRUE  input.0.abs-rx-is-neg
         5  bit   OUT         FALSE  input.0.abs-rx-is-pos
         5  float IN        32767.5  input.0.abs-rx-offset
         5  float OUT    -0.9100633  input.0.abs-rx-position
         5  float IN        32767.5  input.0.abs-rx-scale
         5  s32   OUT         65535  input.0.abs-ry-counts
         5  s32   IN           4095  input.0.abs-ry-flat
         5  s32   IN            255  input.0.abs-ry-fuzz
         5  bit   OUT         FALSE  input.0.abs-ry-is-neg
         5  bit   OUT          TRUE  input.0.abs-ry-is-pos
         5  float IN        32767.5  input.0.abs-ry-offset
         5  float OUT             1  input.0.abs-ry-position
         5  float IN        32767.5  input.0.abs-ry-scale
         5  s32   OUT         65535  input.0.abs-rz-counts
         5  s32   IN           4095  input.0.abs-rz-flat
         5  s32   IN            255  input.0.abs-rz-fuzz
         5  bit   OUT         FALSE  input.0.abs-rz-is-neg
         5  bit   OUT          TRUE  input.0.abs-rz-is-pos
         5  float IN        32767.5  input.0.abs-rz-offset
         5  float OUT             1  input.0.abs-rz-position
         5  float IN        32767.5  input.0.abs-rz-scale
         5  s32   OUT         65535  input.0.abs-throttle-counts
         5  s32   IN           4095  input.0.abs-throttle-flat
         5  s32   IN            255  input.0.abs-throttle-fuzz
         5  bit   OUT         FALSE  input.0.abs-throttle-is-neg
         5  bit   OUT          TRUE  input.0.abs-throttle-is-pos
         5  float IN        32767.5  input.0.abs-throttle-offset
         5  float OUT             1  input.0.abs-throttle-position
         5  float IN        32767.5  input.0.abs-throttle-scale
         5  s32   OUT          2957  input.0.abs-wheel-counts
         5  s32   IN           4095  input.0.abs-wheel-flat
         5  s32   IN            255  input.0.abs-wheel-fuzz
         5  bit   OUT          TRUE  input.0.abs-wheel-is-neg
         5  bit   OUT         FALSE  input.0.abs-wheel-is-pos
         5  float IN        32767.5  input.0.abs-wheel-offset
         5  float OUT    -0.9097581  input.0.abs-wheel-position
         5  float IN        32767.5  input.0.abs-wheel-scale
         5  s32   OUT          2942  input.0.abs-x-counts
         5  s32   IN           4095  input.0.abs-x-flat
         5  s32   IN            255  input.0.abs-x-fuzz
         5  bit   OUT          TRUE  input.0.abs-x-is-neg
         5  bit   OUT         FALSE  input.0.abs-x-is-pos
         5  float IN        32767.5  input.0.abs-x-offset
         5  float OUT    -0.9102159  input.0.abs-x-position
         5  float IN        32767.5  input.0.abs-x-scale
         5  s32   OUT          2942  input.0.abs-y-counts
         5  s32   IN           4095  input.0.abs-y-flat
         5  s32   IN            255  input.0.abs-y-fuzz
         5  bit   OUT          TRUE  input.0.abs-y-is-neg
         5  bit   OUT         FALSE  input.0.abs-y-is-pos
         5  float IN        32767.5  input.0.abs-y-offset
         5  float OUT    -0.9102159  input.0.abs-y-position
         5  float IN        32767.5  input.0.abs-y-scale
         5  s32   OUT          2940  input.0.abs-z-counts
         5  s32   IN           4095  input.0.abs-z-flat
         5  s32   IN            255  input.0.abs-z-fuzz
         5  bit   OUT          TRUE  input.0.abs-z-is-neg
         5  bit   OUT         FALSE  input.0.abs-z-is-pos
         5  float IN        32767.5  input.0.abs-z-offset
         5  float OUT     -0.910277  input.0.abs-z-position
         5  float IN        32767.5  input.0.abs-z-scale
         5  s32   OUT          2941  input.1.abs-rudder-counts
         5  s32   IN           4095  input.1.abs-rudder-flat
         5  s32   IN            255  input.1.abs-rudder-fuzz
         5  bit   OUT          TRUE  input.1.abs-rudder-is-neg
         5  bit   OUT         FALSE  input.1.abs-rudder-is-pos
         5  float IN        32767.5  input.1.abs-rudder-offset
         5  float OUT    -0.9102464  input.1.abs-rudder-position
         5  float IN        32767.5  input.1.abs-rudder-scale
         5  s32   OUT          2947  input.1.abs-rx-counts
         5  s32   IN           4095  input.1.abs-rx-flat
         5  s32   IN            255  input.1.abs-rx-fuzz
         5  bit   OUT          TRUE  input.1.abs-rx-is-neg
         5  bit   OUT         FALSE  input.1.abs-rx-is-pos
         5  float IN        32767.5  input.1.abs-rx-offset
         5  float OUT    -0.9100633  input.1.abs-rx-position
         5  float IN        32767.5  input.1.abs-rx-scale
         5  s32   OUT         65535  input.1.abs-ry-counts
         5  s32   IN           4095  input.1.abs-ry-flat
         5  s32   IN            255  input.1.abs-ry-fuzz
         5  bit   OUT         FALSE  input.1.abs-ry-is-neg
         5  bit   OUT          TRUE  input.1.abs-ry-is-pos
         5  float IN        32767.5  input.1.abs-ry-offset
         5  float OUT             1  input.1.abs-ry-position
         5  float IN        32767.5  input.1.abs-ry-scale
         5  s32   OUT         65535  input.1.abs-rz-counts
         5  s32   IN           4095  input.1.abs-rz-flat
         5  s32   IN            255  input.1.abs-rz-fuzz
         5  bit   OUT         FALSE  input.1.abs-rz-is-neg
         5  bit   OUT          TRUE  input.1.abs-rz-is-pos
         5  float IN        32767.5  input.1.abs-rz-offset
         5  float OUT             1  input.1.abs-rz-position
         5  float IN        32767.5  input.1.abs-rz-scale
         5  s32   OUT         65535  input.1.abs-throttle-counts
         5  s32   IN           4095  input.1.abs-throttle-flat
         5  s32   IN            255  input.1.abs-throttle-fuzz
         5  bit   OUT         FALSE  input.1.abs-throttle-is-neg
         5  bit   OUT          TRUE  input.1.abs-throttle-is-pos
         5  float IN        32767.5  input.1.abs-throttle-offset
         5  float OUT             1  input.1.abs-throttle-position
         5  float IN        32767.5  input.1.abs-throttle-scale
         5  s32   OUT          2957  input.1.abs-wheel-counts
         5  s32   IN           4095  input.1.abs-wheel-flat
         5  s32   IN            255  input.1.abs-wheel-fuzz
         5  bit   OUT          TRUE  input.1.abs-wheel-is-neg
         5  bit   OUT         FALSE  input.1.abs-wheel-is-pos
         5  float IN        32767.5  input.1.abs-wheel-offset
         5  float OUT    -0.9097581  input.1.abs-wheel-position
         5  float IN        32767.5  input.1.abs-wheel-scale
         5  s32   OUT          2942  input.1.abs-x-counts
         5  s32   IN           4095  input.1.abs-x-flat
         5  s32   IN            255  input.1.abs-x-fuzz
         5  bit   OUT          TRUE  input.1.abs-x-is-neg
         5  bit   OUT         FALSE  input.1.abs-x-is-pos
         5  float IN        32767.5  input.1.abs-x-offset
         5  float OUT    -0.9102159  input.1.abs-x-position
         5  float IN        32767.5  input.1.abs-x-scale
         5  s32   OUT          2942  input.1.abs-y-counts
         5  s32   IN           4095  input.1.abs-y-flat
         5  s32   IN            255  input.1.abs-y-fuzz
         5  bit   OUT          TRUE  input.1.abs-y-is-neg
         5  bit   OUT         FALSE  input.1.abs-y-is-pos
         5  float IN        32767.5  input.1.abs-y-offset
         5  float OUT    -0.9102159  input.1.abs-y-position
         5  float IN        32767.5  input.1.abs-y-scale
         5  s32   OUT          2940  input.1.abs-z-counts
         5  s32   IN           4095  input.1.abs-z-flat
         5  s32   IN            255  input.1.abs-z-fuzz
         5  bit   OUT          TRUE  input.1.abs-z-is-neg
         5  bit   OUT         FALSE  input.1.abs-z-is-pos
         5  float IN        32767.5  input.1.abs-z-offset
         5  float OUT     -0.910277  input.1.abs-z-position
         5  float IN        32767.5  input.1.abs-z-scale
    
    ... snippage ...
    Parameters:
    Owner   Type  Dir         Value  Name
         5  s32   RO          65535  input.0.abs-rudder-max
         5  s32   RO              0  input.0.abs-rudder-min
         5  s32   RO          65535  input.0.abs-rx-max
         5  s32   RO              0  input.0.abs-rx-min
         5  s32   RO          65535  input.0.abs-ry-max
         5  s32   RO              0  input.0.abs-ry-min
         5  s32   RO          65535  input.0.abs-rz-max
         5  s32   RO              0  input.0.abs-rz-min
         5  s32   RO          65535  input.0.abs-throttle-max
         5  s32   RO              0  input.0.abs-throttle-min
         5  s32   RO          65535  input.0.abs-wheel-max
         5  s32   RO              0  input.0.abs-wheel-min
         5  s32   RO          65535  input.0.abs-x-max
         5  s32   RO              0  input.0.abs-x-min
         5  s32   RO          65535  input.0.abs-y-max
         5  s32   RO              0  input.0.abs-y-min
         5  s32   RO          65535  input.0.abs-z-max
         5  s32   RO              0  input.0.abs-z-min
         5  s32   RO          65535  input.1.abs-rudder-max
         5  s32   RO              0  input.1.abs-rudder-min
         5  s32   RO          65535  input.1.abs-rx-max
         5  s32   RO              0  input.1.abs-rx-min
         5  s32   RO          65535  input.1.abs-ry-max
         5  s32   RO              0  input.1.abs-ry-min
         5  s32   RO          65535  input.1.abs-rz-max
         5  s32   RO              0  input.1.abs-rz-min
         5  s32   RO          65535  input.1.abs-throttle-max
         5  s32   RO              0  input.1.abs-throttle-min
         5  s32   RO          65535  input.1.abs-wheel-max
         5  s32   RO              0  input.1.abs-wheel-min
         5  s32   RO          65535  input.1.abs-x-max
         5  s32   RO              0  input.1.abs-x-min
         5  s32   RO          65535  input.1.abs-y-max
         5  s32   RO              0  input.1.abs-y-min
         5  s32   RO          65535  input.1.abs-z-max
         5  s32   RO              0  input.1.abs-z-min
    ... snippage ...
    

    Dan’s program assigns the outputs thusly:

    • Wheel – ambient temperature as measured on TC4 board
    • X Y Z Rudder – thermocouples – channels 1 through 4
    • RX RY RZ  Throttle – thermistors – channels 5 through 8

    I created a huge Eagle device that encapsulates the whole thing. A simple demo schematic includes the constants that make the temperatures come out in °C:

    TC4Server - Eagle Schematic
    TC4Server – Eagle Schematic

    That picture produces this HAL file:

    # HAL config file automatically generated by Eagle-CAD ULP:
    # [/mnt/bulkdata/Project Files/eagle/ulp/hal-write-2.5.ulp]
    # (C) Martin Schoeneck.de 2008
    # Charalampos Alexopoulos 2011
    # Mods Ed Nisley KE4ZNU 2010 2013
    # Path        [/mnt/bulkdata/Project Files/eagle/projects/LinuxCNC for M2/]
    # ProjectName [LinuxCNC M2 - TC4Server Test]
    # File name   [/mnt/bulkdata/Project Files/eagle/projects/LinuxCNC for M2/TC4Server.hal]
    # Created     [20:03:16 03-Jun-2013]
    
    ####################################################
    # Load realtime and userspace modules
    loadusr -W hal_input -A +Leonardo
    loadrt threads name1=servo-thread period1=1000000
    loadrt constant        count=4
    loadrt conv_float_s32        count=2
    
    ####################################################
    # Hook functions into threads
    addf constant.0        servo-thread
    addf constant.1        servo-thread
    addf constant.2        servo-thread
    addf constant.3        servo-thread
    addf conv-float-s32.0        servo-thread
    addf conv-float-s32.1        servo-thread
    
    ####################################################
    # Set parameters
    
    ####################################################
    # Set constants
    setp constant.0.value    10
    setp constant.1.value    2732
    setp constant.2.value    0
    setp constant.3.value    0
    
    ####################################################
    # Connect Modules with nets
    net n_2 constant.2.out conv-float-s32.1.in
    net n_3 constant.3.out conv-float-s32.0.in
    net tc4-ambient input.0.abs-wheel-position
    net tc4-flat input.0.abs-wheel-flat input.0.abs-x-flat input.0.abs-y-flat input.0.abs-z-flat input.0.abs-rudder-flat input.0.abs-rx-flat input.0.abs-ry-flat input.0.abs-rz-flat input.0.abs-throttle-flat conv-float-s32.1.out
    net tc4-fuzz input.0.abs-throttle-fuzz input.0.abs-rz-fuzz input.0.abs-ry-fuzz input.0.abs-rx-fuzz input.0.abs-rudder-fuzz input.0.abs-z-fuzz input.0.abs-y-fuzz input.0.abs-x-fuzz input.0.abs-wheel-fuzz conv-float-s32.0.out
    net tc4-offset input.0.abs-wheel-offset input.0.abs-x-offset input.0.abs-y-offset input.0.abs-z-offset input.0.abs-rudder-offset input.0.abs-rx-offset input.0.abs-ry-offset input.0.abs-rz-offset input.0.abs-throttle-offset constant.1.out
    net tc4-scale input.0.abs-wheel-scale input.0.abs-x-scale input.0.abs-y-scale input.0.abs-z-scale input.0.abs-rudder-scale input.0.abs-rx-scale input.0.abs-ry-scale input.0.abs-rz-scale input.0.abs-throttle-scale constant.0.out
    net tcouple-1 input.0.abs-x-position
    net tcouple-2 input.0.abs-y-position
    net tcouple-3 input.0.abs-z-position
    net tcouple-4 input.0.abs-rudder-position
    net tmistor-5 input.0.abs-rx-position
    net tmistor-6 input.0.abs-ry-position
    net tmistor-7 input.0.abs-rz-position
    net tmistor-8 input.0.abs-throttle-position
    

    Fire it up with halrun to see the temperatures (alphabetically by the pin name):

    halrun -I -f TC4Server.hal
    halcmd: start
    halcmd: show pin *position
    Component Pins:
    Owner   Type  Dir         Value  Name
         5  float OUT          20.9  input.0.abs-rudder-position ==> tcouple-4
         5  float OUT          21.5  input.0.abs-rx-position ==> tmistor-5
         5  float OUT        6280.3  input.0.abs-ry-position ==> tmistor-6
         5  float OUT        6280.3  input.0.abs-rz-position ==> tmistor-7
         5  float OUT        6280.3  input.0.abs-throttle-position ==> tmistor-8
         5  float OUT          22.5  input.0.abs-wheel-position ==> tc4-ambient
         5  float OUT            21  input.0.abs-x-position ==> tcouple-1
         5  float OUT            21  input.0.abs-y-position ==> tcouple-2
         5  float OUT          20.8  input.0.abs-z-position ==> tcouple-3
    

    The sensors do not correspond to the picture at the top: only the first thermocouple and first thermistor are connected ; the ADC returns bogus data for disconnected inputs, which means you must be careful about tightening the wires and checking the result. Dan’s firmware has the ability to disable unused sensors, in which case you get a huge value; when used for heater control, a sensor failing high means the heater will turn off, but, should you use this gadget in a freezer, you might want them to fail low (so modify the code for your own use).

    The ambient temperature reported for the board runs 1 or 2 °C higher than the actual ambient air temperature, probably because of all those components doing useful things up close to the sensor chip. That particular ambient temperature serves as the cold junction reference for the thermocouples; the other temperatures don’t change very much as the board warms up, so it’s all good.

    Remember to issue the start command in halrun, because otherwise nothing changes.

    Also remember that you must configure TC4Server with the thermistor characteristics before you use it as a hal_input device.

  • Stapler Latch Spring Replacement

    It’s garden fence stapling season, which means it’s time for the annual stapler annoyance. There’s supposed to be a plastic tab angling under the right side of the latch, pressing against the bottom of the staple channel and forcing the triangular tab on the top out through a small opening. All that’s left is the stub:

    Stapler latch - broken spring tab
    Stapler latch – broken spring tab

    It looks like it ought to be a great 3D printing project, but I came to my senses and snipped off a length of phosphor bronze spring stock, rolled it up, and positioned it inside the opening:

    Stapler latch - metal spring fitting
    Stapler latch – metal spring fitting

    The latch slides into the staple channel until the pin on the side you can see above engages a hole in the channel. The spring looked like this on the way in:

    Stapler latch - before closing
    Stapler latch – before closing

    And now it’s ready for action:

    Stapler latch - in place
    Stapler latch – in place

    Works like a champ and took maybe 15 minutes, tops, to accomplish. Shame it took me a few decades to get around to fixing it, but I finally dropped the clipped-and-filed nail I’d been jamming between the latch and the channel one too many times…