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.

Month: April 2013

  • LibreOffice RegEx Backreferences

    I needed to replace all the ordinary spaces between numeric values and their units (as in 3.5 V) with non-breaking spaces. LibreOffice Writer implements regular expression searches, but their notion of marking and replacing references trips me up every time. This part of the Fine Manual describing how parenthesized targets work will come in handy again:

    In the Search for box:

    Defines the characters inside the parentheses as a reference. You can then refer to the first reference in the current expression with “\1”, to the second reference with “\2”, and so on.

    For example, if your text contains the number 13487889 and you search using the regular expression (8)7\1\1, “8788” is found.

    You can also use () to group terms, for example, “a(bc)?d” finds “ad” or “abcd”.

    In the Replace with box:

    Use $ (dollar) instead of \ (backslash) to replace references. Use $0 to replace the whole found string.

    As nearly as I can tell, there is no escape sequence that denotes a non-breaking space, so I had to manually enter one using Shift+Ctrl+Spacebar, copy it, and paste it into the replacement text string.

    The search-and-replace dialog looked like this:

    LibreOffice RegEx Backreferences
    LibreOffice RegEx Backreferences

    Yes, you can search for strings inside parentheses, use parentheses to mark references, and then jam references in the replacement. This makes my head hurt every time: programming as an experimental science…

  • LED Forward Voltages vs. Color

    Running a random set of colored LEDs from the Basement Laboratory Parts Warehouse Wing through the LED Curve Tracer produced this pleasant plot:

    ROYGBUIW

    The white LED doesn’t match up with either the blue or the UV LED. Perhaps the blue LED uses a completely different chemistry that shoves further to the right than seems proper? I suppose I should run a handful of white, blue, and UV LEDs through the thing just to see what’s going on…

    The Bash / Gnuplot source code:

    #!/bin/sh
    numLEDs=8
    #-- overhead
    export GDFONTPATH="/usr/share/fonts/truetype/"
    base="${1%.*}"
    echo Base name: ${base}
    ofile=${base}.png
    echo Input file: $1
    echo Output file: ${ofile}
    #-- do it
    gnuplot << EOF
    #set term x11
    set term png font "arialbd.ttf" 18 size 950,600
    set output "${ofile}"
    set title "${base}"
    set key noautotitles
    unset mouse
    set bmargin 4
    set grid xtics ytics
    set xlabel "Forward Voltage - V"
    set format x "%4.1f"
    set xrange [0.5:4.5]
    #set xtics 0,5
    set mxtics 2
    #set logscale y
    #set ytics nomirror autofreq
    set ylabel "Current - mA"
    set format y "%3.0f"
    set yrange [0:35]
    set mytics 2
    #set y2label "right side variable"
    #set y2tics nomirror autofreq 2
    #set format y2 "%3.0f"
    #set y2range [0:200]
    #set y2tics 32
    #set rmargin 9
    set datafile separator whitespace
    set label 1 "IR" at 1.32,32 center
    set label 2 "R"  at 1.79,32 center
    set label 3 "O"  at 2.10,32 center
    set label 4 "Y"  at 2.65,32 center
    set label 5 "G"  at 2.42,32 center
    set label 6 "B"  at 4.05,32 center
    set label 7 "UV" at 3.90,32 center
    set label 8 "W"  at 3.25,32 center
    #set arrow from 2.100,32 to 2.125,31 lt 1 lw 2 lc 0
    plot \
     "$1" index 0 using (\$5/1000):(\$2/1000) with linespoints pt 1 lw 2 lc rgb "red" ,\
     "$1" index 1 using (\$5/1000):(\$2/1000) with linespoints pt 1 lw 2 lc rgb "orange" ,\
     "$1" index 2 using (\$5/1000):(\$2/1000) with linespoints pt 1 lw 2 lc rgb "dark-yellow" ,\
     "$1" index 3 using (\$5/1000):(\$2/1000) with linespoints pt 1 lw 2 lc rgb "green" ,\
     "$1" index 4 using (\$5/1000):(\$2/1000) with linespoints pt 1 lw 2 lc rgb "blue" ,\
     "$1" index 5 using (\$5/1000):(\$2/1000) with linespoints pt 1 lw 2 lc rgb "purple" ,\
     "$1" index 6 using (\$5/1000):(\$2/1000) with linespoints pt 1 lw 2 lc rgb "magenta" ,\
     "$1" index 7 using (\$5/1000):(\$2/1000) with linespoints pt 1 lw 2 lc rgb "dark-gray"
    EOF
    

    And the raw data file:

    # LED Curve Tracer
    # Ed Nisley - KE4ZNU - March 2013
    # VCC at LED: 4897 mV
    # Bandgap reference voltage: 1041 mV
    
    # Insert LED, press button 1 to start...
    # INOM	ILED		VccLED	VD	VLED		VG	VS	VGS VDS <--- LED 1
    0	0	4892		3889		1002		0	0	0	3889
    5	4613		4892		3264		1627		1990		48	1942		3216
    10	10148	4892		3216		1675		2092		106 1985		3109
    15	15223	4892		3182		1709		2199		159 2039		3022
    20	19836	4892		3148		1743		2271		208 2063		2940
    25	24910	4897		3129		1767		2354		261 2092		2867
    30	30446	4897		3104		1792		2431		319 2111		2785
    
    # Insert LED, press button 1 to start...
    # INOM	ILED		VccLED	VD	VLED		VG	VS	VGS VDS <--- LED 2
    0	0	4892		3884		1007		0	0	0	3884
    5	4613		4892		3124		1767		1985		48	1937		3075
    10	9687		4897		3037		1860		2111		101 2010		2935
    15	14761	4897		2964		1932		2189		155 2034		2809
    20	19836	4897		2906		1990		2271		208 2063		2697
    25	24910	4897		2848		2048		2349		261 2087		2586
    30	30446	4892		2794		2097		2431		319 2111		2475
    
    # Insert LED, press button 1 to start...
    # INOM	ILED		VccLED	VD	VLED		VG	VS	VGS VDS <--- LED 3
    0	0	4892		3826		1065		0	0	0	3826
    5	4613		4897		2862		2034		1990		48	1942		2814
    10	10148	4897		2688		2208		2097		106 1990		2581
    15	15223	4897		2552		2344		2194		159 2034		2392
    20	19836	4892		2436		2455		2276		208 2068		2228
    25	24910	4897		2349		2547		2354		261 2092		2087
    30	29985	4897		2257		2639		2426		314 2111		1942
    
    # Insert LED, press button 1 to start...
    # INOM	ILED		VccLED	VD	VLED		VG	VS	VGS VDS <--- LED 4
    0	0	4892		3734		1157		0	0	0	3734
    5	5074		4892		2935		1956		1976		53	1922		2882
    10	10148	4897		2823		2073		2102		106 1995		2717
    15	15223	4892		2722		2170		2199		159 2039		2562
    20	20297	4897		2649		2247		2276		213 2063		2436
    25	24910	4897		2567		2329		2349		261 2087		2305
    30	29985	4897		2489		2407		2426		314 2111		2174
    
    # Insert LED, press button 1 to start...
    # INOM	ILED		VccLED	VD	VLED		VG	VS	VGS VDS <--- LED 5
    0	0	4892		4485		406 0	0	0	4485
    5	4613		4897		1724		3172		1990		48	1942		1675
    10	10148	4892		1443		3448		2097		106 1990		1336
    15	15223	4897		1249		3647		2199		159 2039		1089
    20	19836	4892		1099		3792		2276		208 2068		891
    25	24910	4897		983 3913		2354		261 2092		721
    30	29985	4892		862 4030		2426		314 2111		547
    
    # Insert LED, press button 1 to start...
    # INOM	ILED		VccLED	VD	VLED		VG	VS	VGS VDS <--- LED 6
    0	0	4892		4165		726 0	0	0	4165
    5	5074		4892		1448		3443		1985		53	1932		1395
    10	10148	4897		1322		3574		2102		106 1995		1215
    15	15223	4892		1220		3671		2194		159 2034		1060
    20	20297	4892		1147		3744		2276		213 2063		934
    25	25372	4892		1075		3816		2354		266 2087		808
    30	29985	4892		1002		3889		2426		314 2111		687
    
    # Insert LED, press button 1 to start...
    # INOM	ILED		VccLED	VD	VLED		VG	VS	VGS VDS <--- LED 7
    0	0	4892		4247		644 0	0	0	4247
    5	5074		4892		3647		1244		1981		53	1927		3594
    10	10148	4892		3618		1273		2107		106 2000		3511
    15	14761	4892		3603		1288		2170		155 2015		3448
    20	20297	4892		3584		1307		2271		213 2058		3371
    25	25372	4892		3574		1317		2354		266 2087		3308
    30	29523	4892		3565		1327		2412		310 2102		3255
    
    # Insert LED, press button 1 to start...
    # INOM	ILED		VccLED	VD	VLED		VG	VS	VGS VDS <--- LED 8
    0	0	4892		4945		-53 0	0	0	4945
    5	5074		4892		2160		2731		1985		53	1932		2107
    10	10148	4892		2034		2857		2097		106 1990		1927
    15	15223	4897		1927		2969		2194		159 2034		1767
    20	19836	4892		1826		3066		2271		208 2063		1617
    25	25372	4897		1734		3162		2349		266 2082		1467
    30	29523	4892		1666		3225		2412		310 2102		1356
    
    # Insert LED, press button 1 to start...
    
  • Broom Handle Screw Thread: Replacement Plug

    Somehow, we wound up with a broom handle and a broom head, the former missing a threaded stub that was firmly lodged in the latter. A few minutes of Quality Shop Time sawed off the end of the handle and unscrewed the stub to produce this array of fragments:

    Broken broom handle thread
    Broken broom handle thread

    It’s a cylindrical Thing tailor-made for (or, back in the day, by!) a lathe. My lathe has quick-change gears that can actually cut a 5 TPI thread, but that seems like a lot of work for such a crude fitting. Instead, an hour or so of desk work produced this:

    Broom Handle Screw - solid model - overview
    Broom Handle Screw – solid model – overview

    Some after-the-fact search-fu revealed that the thread found on brooms and paint rollers is a 3/4-5 Acme. Machinery’s Handbook has 13 pages of data for various Acme screw threads, making a distinction between General Purpose Acme threads and Stub Acme Threads: GP thread depth = 0.5 × pitch, Stub = 0.3 × pitch. For a 5 TPI thread = 0.2 inch pitch, that’s GP = 0.1 inch vs. Stub = 0.06 inch.

    I measured a 5.0 mm pitch (which should be 5.08 mm = 0.2 inch exactly) and a crest-to-root depth of 1.4 mm = 0.055 inch, which makes them look like 3/4-5 Stub Acme threads. But, I didn’t know that at the time; a simple half-cylinder 2.5 mm wide and 1.25 mm tall was a pretty close match to what I saw on the broken plastic part.

    Although OpenSCAD’s MCAD library has some screw forms, they’re either machine screws with V threads or ball screws with spheres. The former obviously weren’t appropriate and the latter produced far too many facets, so I conjured up a simpler shape: 32 slightly overlapping cylinders per turn, sunk halfway in the shaft at their midpoint, and tilted at the thread’s helix angle.

    Broom Handle Screw - thread model closeup
    Broom Handle Screw – thread model closeup

    The OpenSCAD source code has a commented-out section that removes a similar shape from the shaft between the raised thread, but that brought the rendering to its knees. Fortunately, it turned out to be unnecessary, but it’s there if you want it.

    With the shaft diameter set to the “root diameter” of the thread and the other dimensions roughly matching the broken plastic bits, this emerged an hour later:

    Broom handle screw plug - as built
    Broom handle screw plug – as built

    The skirt thread was 0.25 to 0.30 mm thick, so the first-layer height tweak and packing density adjustments worked fine and all the dimensions came out perfectly. The cylindrical thread form doesn’t have much overhang and the threads came out fine; I think the correct straight-sided form would have more problems.

    The hole down the middle accommodates a 1/4-20 bolt that applies enough clamping force to keep the shaft in compression, which ought to prevent it from breaking in normal use. I intended to use a hex bolt, but found a carriage bolt that was exactly the right length and had a head exactly the same diameter as the shaft, so I heated it with a propane torch and mushed its square shank into the top of the hexagonal bolt hole (the source code now includes a square recess):

    Broom handle screw plug - in handle
    Broom handle screw plug – in handle

    The dimples on the side duplicate the method that secured the original plastic piece: four dents punched into the metal handle lock the plastic in place. It seems to work reasonably well, though, and is certainly less conspicuous than the screws I’d use.

    Screwing it in place shows that it’s slightly too long (I trimmed the length in the source code):

    Broom handle installed
    Broom handle installed

    It’s back in service, ready for use…

    The OpenSCAD source code:

    // Broom Handle Screw End Plug
    // Ed Nisley KE4ZNU March 2013
    
    // Extrusion parameters must match reality!
    //  Print with +1 shells and 3 solid layers
    
    ThreadThick = 0.25;
    ThreadWidth = 2.0 * ThreadThick;
    
    HoleWindage = 0.2;
    
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    
    Protrusion = 0.1;			// make holes end cleanly
    
    //----------------------
    // Dimensions
    
    PI = 3.14159265358979;
    
    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; // excludes bolt head extension
    
    $fn=8*4;
    
    echo("Pitch dia: ",PitchDia);
    echo("Root dia: ",PitchDia - ThreadFormOD);
    echo("Crest dia: ",PitchDia + ThreadFormOD);
    
    //----------------------
    // Useful routines
    
    module Cyl_Thread(pitch,length,pitchdia,cyl_radius,resolution=32) {
    
    Cyl_Adjust = 1.25;                      // force overlap
    
        Turns = length/pitch;
        Slices = Turns*resolution;
        RotIncr = 1/resolution;
        PitchRad = pitchdia/2;
        ZIncr = length/Slices;
        helixangle = atan(pitch/(PI*pitchdia));
        cyl_len = Cyl_Adjust*(PI*pitchdia)/resolution;
    
        union() {
            for (i = [0:Slices-1]) {
                translate([PitchRad*cos(360*i/resolution),PitchRad*sin(360*i/resolution),i*ZIncr])
                    rotate([90+helixangle,0,360*i/resolution])
                        cylinder(r=cyl_radius,h=cyl_len,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);
            translate([0,0,(PostLength + FlangeLength)])
                Cyl_Thread(ThreadPitch,(ScrewLength - ThreadFormOD/2),PitchDia,ThreadFormOD/2);
        }
    
        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)])
    //        Cyl_Thread(ThreadPitch,(ScrewLength - ThreadFormOD/2),PitchDia,ThreadFormOD/2);
    
    	for (i = [0:90:270]) {
    		rotate(i)
    			translate([PostOD/2,0,PostLength/2])
    				sphere(r=RecessDia/2,$fn=8);
    	}
    }