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: Software

General-purpose computers doing something specific

  • Adafruit TFT LCD: Color Indicator Spots

    These spots might come in handy as status indicators and tiny mode control buttons:

    Resistor Color Code Spots
    Resistor Color Code Spots

    The montage is 800% of the actual 8×8 pixel size that’s appropriate for the Adafruit TFT LCD.

    They’re generated from the standard colors, with the “black” patch being a dark gray so it doesn’t vanish:

    # create resistor-coded color spots
    # Ed Nisley - KE4ZNU
    # January 2015
    
    SZ=8x8
    
    convert -size $SZ canvas:gray10 -type truecolor Res0.bmp
    convert -size $SZ canvas:brown	-type truecolor Res1.bmp
    convert -size $SZ canvas:red	-type truecolor Res2.bmp
    convert -size $SZ canvas:orange	-type truecolor Res3.bmp
    convert -size $SZ canvas:yellow -type truecolor Res4.bmp
    convert -size $SZ canvas:green	-type truecolor Res5.bmp
    convert -size $SZ canvas:blue	-type truecolor Res6.bmp
    convert -size $SZ canvas:purple	-type truecolor Res7.bmp
    convert -size $SZ canvas:gray80	-type truecolor Res8.bmp
    convert -size $SZ canvas:white	-type truecolor Res9.bmp
    
    montage Res*bmp -tile 5x -geometry +2+2 -resize 800% Res.png
    

    For a pure indicator, it’d be easier to slap a spot on the screen with the Adafruit GFX library’s fillRect() function. If you’re setting up a generic button handler, then button bitmap images make more sense.

  • Adafruit TFT Shield: Firmware Heartbeat Spot

    Being that type of guy, I want a visible indication that the firmware continues trudging around the Main Loop.  The standard Arduino LED works fine for that (unless you’re using hardware SPI), but the Adafruit 2.8 inch Touch-screen TFT LCD shield covers the entire Arduino board, so I can’t see the glowing chip.

    Given a few spare pixels and the Adafruit GFX library, slap a mood light in the corner:

    Adafruit TFT - heartbeat spot
    Adafruit TFT – heartbeat spot

    The library defines the RGB color as a 16 bit word, so this code produces a dot that changes color every half second around the loop() function:

    #define PIN_HEARTBEAT 13
    
    unsigned long MillisThen,MillisNow;
    #define UPDATEMS 500
    
    ... snippage ...
    
    void loop() {
    	MillisNow = millis();
    
    ... snippage ...
    
    	if ((MillisNow - MillisThen) > UPDATEMS) {
    
    		TogglePin(PIN_HEARTBEAT);
    		tft.fillCircle(315,235,4,(word)MillisNow);			// colorful LCD heartbeat
    
    		MillisThen = MillisNow;
    	}
    }
    

    millis() produces an obvious counting sequence of colors. If that matters, you use random(0x10000).

    A square might be slightly faster than a circle. If that matters, you need an actual measurement in place of an opinion.

    Not much, but it makes me happy…

    There’s an obvious extension for decimal values: five adjacent spots in the resistor color code show you an unsigned number. Use dark gray for black to prevent it from getting lost; light gray and white would be fine. Prefix it with a weird color spot for the negative sign, should you need such a thing.

    Hexadecimal values present a challenge. That’s insufficient justification to bring back octal notation.

    In this day and age, color-coded numeric readouts should be patentable, as casual searching didn’t turn up anything similar. You saw it here first… [grin]

    Now that I think about it, a set of tiny buttons that control various modes might be in order.

  • Kenmore 158 UI: Automatic Button Builder

    Given the glacially slow Arduino touch-screen TFT display as a first pass UI for the Kenmore 158 sewing machine, I need some UI elements.

    I need buttons. Lots of buttons.

    Each button will have several different states that must be visually distinct:

    • Disabled – not available for pressing
    • Released – can be pressed and is inactive
    • Pressed – has been pressed and is now active

    There may be other states, but those should be enough to get started.

    I’d rather not draw that detail by hand for each button, so some tinkering with the Bash script driving the Imagemagick routines produced these results:

    Buttons
    Buttons

    Aren’t those just the ugliest buttons you’ve ever seen?

    The garish colors identify different functions, the crude shading does a (rather poor) job of identifying the states, and the text & glyphs should be unambiguous in context. Obviously, there’s room for improvement.

    The point is that I can begin building the UI code that will slap those bitmaps on the Arduino’s touch-panel LCD while responding to touches, then come back and prettify the buttons as needed. With a bit of attention to detail, I should be able to re-skin the entire UI without building the data into the Arduino sketch, but I’ll start crude.

    The mkAll.sh script that defines the button characteristics and calls the generator script:

    ./mkBFam.sh NdDn springgreen4 ⤓
    ./mkBFam.sh NdUp springgreen4 ⤒
    ./mkBFam.sh NdAny springgreen4 ⟳ 80 80 40
    ./mkBFam.sh PdOne sienna One 120 80
    ./mkBFam.sh PdFol sienna Follow 120 80
    ./mkBFam.sh PdRun sienna Run 120 80
    ./mkBFam.sh SpMax maroon1  🏃 80 80 40
    ./mkBFam.sh SpMed maroon2  🐇 80 80 40
    ./mkBFam.sh SpLow maroon3  🐌
    montage *bmp -tile 3x -geometry +2+2 Buttons.png
    display Buttons.png
    

    As before, if you don’t see rabbit and snail glyphs, then your fonts don’t cover those Unicode blocks.

    The quick-and-dirty mkBFam.sh script that produces three related buttons for each set of parameters:

    # create family of simple beveled buttons
    # Ed Nisley - KE4ZNU
    # January 2015
    
    [ -z $1 ] && FN=Test || FN=$1
    [ -z $2 ] && CLR=red || CLR=$2
    [ -z $3 ] && TXT=x   || TXT=$3
    [ -z $4 ] && SX=80   || SX=$4
    [ -z $5 ] && SY=80   || SY=$5
    [ -z $6 ] && PT=25   || PT=$6
    [ -z $7 ] && BDR=10  || BDR=$7
    
    echo fn=$FN clr=$CLR txt=$TXT sx=$SX sy=$SY pt=$PT bdr=$BDR
    
    echo Working ...
    
    echo Shape
    convert -size ${SX}x${SY} xc:none \
    -fill $CLR -draw "roundrectangle $BDR,$BDR $((SX-BDR)),$((SY-BDR)) $((BDR-2)),$((BDR-2))" \
    ${FN}_s.png
    
    echo Highlights
    convert ${FN}_s.png \
      \( +clone -alpha extract -blur 0x12 -shade 110x2 \
      -normalize -sigmoidal-contrast 16,60% -evaluate multiply .5\
      -roll +4+8 +clone -compose Screen -composite \) \
      -compose In  -composite \
      ${FN}_h.png
    
    convert ${FN}_s.png \
      \( +clone -alpha extract -blur 0x12 -shade 110x0 \
      -normalize -sigmoidal-contrast 16,60% -evaluate multiply .5\
      -roll +4+8 +clone -flip -flop -compose Screen -composite \) \
      -compose In  -composite \
      ${FN}_l.png
    
    echo Borders
    convert ${FN}_h.png \
      \( +clone -alpha extract  -blur 0x2 -shade 0x90 -normalize \
      -blur 0x2  +level 60,100%  -alpha On \) \
      -compose Multiply -composite \
       ${FN}_bh.png
    
    convert ${FN}_l.png \
      \( +clone -alpha extract  -blur 0x2 -shade 0x90 -normalize \
      -blur 0x2  +level 60,100%  -alpha On \) \
      -compose Multiply -composite \
       ${FN}_bl.png
    
    echo Buttons
    convert ${FN}_s.png \
      -font /usr/share/fonts/custom/Symbola.ttf  -pointsize ${PT}  -fill black  -stroke black \
      -gravity Center  -annotate 0 "${TXT}"  -trim -repage 0x0+7+7 \
      \( +clone -background navy -shadow 80x4+4+4 \) +swap \
      -background snow4  -flatten \
      ${FN}0.png
    
    convert ${FN}_bl.png \
      -font /usr/share/fonts/custom/Symbola.ttf  -pointsize ${PT}  -fill black  -stroke black \
      -gravity Center  -annotate 0 "${TXT}"  -trim -repage 0x0+7+7 \
      \( +clone -background navy -shadow 80x4+4+4 -flip -flop \) +swap \
      -background snow4  -flatten \
      ${FN}1.png
    
    convert ${FN}_bh.png \
      -font /usr/share/fonts/custom/Symbola.ttf  -pointsize $PT  -fill black  -stroke black \
      -gravity Center  -annotate 0 "${TXT}"  -trim -repage 0x0+7+7 \
      \( +clone -background navy -shadow 80x4+4+4 \) +swap \
      -background snow4  -flatten \
      ${FN}2.png
    
    echo BMPs
    for ((i=0 ; i <= 2 ; i++))
    do
     convert ${FN}${i}.png -type truecolor ${FN}${i}.bmp
    # display -resize 300% ${FN}${i}.bmp
    done
    
    echo Done!
    

    Now, to get those bitmaps from the SD card into the proper place on the LCD panel…

  • Kenmore 158: Useful Unicode Glyphs

    It turns out, for some reasons that aren’t relevant here, that I’ll be using the Adafruit Arduino LCD panel for the sewing machine control panel, at least to get started. In mulling that over, the notion of putting text on the buttons suggests using getting simple pictures with Unicode characters.

    Herewith, some that may prove useful:

    • Needle stop up: ↥ = U+21A5
    • Needle stop up: ⤒=U+2912
    • Needle stop down: ⤓ = U+2913
    • Needle stop any: ↕ = U+2195
    • Needle stop any: ⟳ = U+27F3
    • Needle stop any: ⇅ = U+21C5
    • Rapid speed: ⛷ = U+26F7 (skier)
    • Rapid speed: 🐇  = U+1F407 (rabbit)
    • Slow speed: 🐢 = U+1F422 (turtle)
    • Dead slow: 🐌 = U+1F40C (snail)
    • Maximum speed: 🏃 = U+1F3C3 (runner)
    • Bobbin: ⛀ = U+26C0 (white draughts man)
    • Bobbin: ⛂ = U+26C2 (black draughts man)
    • Bobbin winding: 🍥 = U+1F365 (fish cake with swirl)

    Of course, displaying those characters require a font with deep Unicode support, which may explain why your browser renders them as gibberish / open blocks / whatever. The speed glyphs look great on the Unicode table, but none of the fonts around here support them; I’m using the Droid font family to no avail.

    Blocks of interest:

    The links in the fileformat.info table of Unicode blocks lead to font coverage reports, but I don’t know how fonts get into those reports. The report for the Miscellaneous Symbols block suggested the Symbola font would work and a test with LibreOffice show it does:

    Symbola font test
    Symbola font test

    An all-in-one-page Unicode symbol display can lock up your browser hard while rendering a new page.

    Unicode is weird

  • Rounded Cable Clips

    This isn’t quite the smoothly rounded clip I had in mind:

    LED Cable Clip - rounded channel
    LED Cable Clip – rounded channel

    It seems somewhat better looking than the square design, though:

    LED Cable Clips
    LED Cable Clips

    I ran off a few of both styles to have some on hand:

    Cable clips - on platform
    Cable clips – on platform

    They’re in a bag until I install the new LED strips and needle light.

    The OpenSCAD source code:

    // LED Cable Clips
    // Ed Nisley - KE4ZNU - October 2014
    
    Layout = "Oval";			// Oval Square Build
    
    //- Extrusion parameters must match reality!
    
    ThreadThick = 0.20;
    ThreadWidth = 0.40;
    
    HoleWindage = 0.2;			// extra clearance
    
    Protrusion = 0.1;			// make holes end cleanly
    
    AlignPinOD = 1.70;			// assembly alignment pins: filament dia
    
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    
    //----------------------
    // Dimensions
    
    Base = [12.0,12.0,IntegerMultiple(2.0,ThreadThick)];	// base over sticky square
    
    CableOD = 2.0;
    
    BendRadius = 3.0;
    
    Bollard = [BendRadius,(sqrt(2)*Base[0]/2 - CableOD - BendRadius),2*CableOD];
    B_BOT = 0;
    B_TOP = 1;
    B_LEN = 2;
    
    NumSides = (Shape == "Square") ? 5*4 : 6*3;
    
    //----------------------
    // Useful routines
    
    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);
    
    }
    
    //-- Square clip with central bollard
    
    module SquareBollard() {
    
    	intersection() {
    		translate([0,0,(Base[2] + Bollard[B_LEN])/2])			// overall XYZ outline
    			cube(Base + [0,0,Bollard[2]],center=true);
    
    		union() {
    			translate([0,0,Base[2]/2])						// oversize mount base
    				scale([2,2,1])
    					cube(Base,center=true);
    
    			for (i=[-1,1] , j=[-1,1]) {						// corner bollards
    				translate([i*Base[0]/2,j*Base[1]/2,(Base[2] - Protrusion)])
    					rotate(180/NumSides)
    					cylinder(r=Bollard[B_BOT],h=(Bollard[B_LEN] + Protrusion),center=false,$fn=NumSides);
    
    			translate([0,0,(Base[2] - Protrusion)])			// center tapered bollard
    				cylinder(r1=Bollard[B_BOT],r2=Bollard[B_TOP],
    						 h=(Bollard[B_LEN] + Protrusion),
    						 center=false,$fn=NumSides);
    			}
    		}
    	}
    
    }
    
    //-- Oval clip with central passage
    
    module OvalPass() {
    
    	intersection() {
    		translate([0,0,(Base[2] + Bollard[B_LEN])/2])		// overall XYZ outline
    			cube(Base + [0,0,2*CableOD],center=true);
    
    		union() {
    			translate([0,0,Base[2]/2])						// oversize mount base
    				scale([2,2,1])
    					cube(Base,center=true);
    
    			for (j=[-1,1])									// bending ovals
    				translate([0,j*Base[1]/2,(Base[2] - Protrusion)])
    					resize([Base[0]/0.75,0,0])
    						cylinder(d1=0.75*(Base[1]-CableOD),d2=(Base[1]-CableOD)/cos(180/NumSides),
    								h=(Bollard[B_LEN] + Protrusion),
    								center=false,$fn=NumSides);
    		}
    	}
    /*
    #	translate([0,0,6])
    		rotate([0,90,0])
    			cylinder(d=CableOD,h=10,center=true,$fn=48);
    */
    }
    
    //----------------------
    // Build it
    
    ShowPegGrid();
    
    if (Layout == "Square")
    	SquareBollard();
    
    if (Layout == "Oval")
    	OvalPass();
    
  • The Windows Update That Replaces the Update That Prevented Further Updates

    Category killer in the “You can’t make this stuff up” category:

    Update to prevent update prevention error
    Update to prevent update prevention error

    I vaguely recall similar errors in Ubuntu’s updater and I suppose everybody gets it wrong occasionally.

  • OpenSCAD: Quantized Vertices

    Back when I started fiddling with 3D printed chain mail, the whole process from model to plastic worked wonderfully well. That continued with the larger sheets, but now, occasionally, the OpenSCAD model would produce weirdly sliced links. Depending on nothing repeatable, some links wouldn’t bridge correctly: the thread paths in the bottom layer across the gap would mysteriously stop just short of one pillar, return to the start, and leave an unsupported shelf that would, of course, fall into the gap.

    Shortly before Christmas, I managed to get a consistent failure that manifested differently: upon loading the STL file, Slic3r would quietly perform dozens of automatic corrections that (sometimes!) produced bizarrely distorted results. Feeding a failing model into Meshlab showed an irregular assortment of “self intersecting faces”, highlighted in red:

    Chain Mail Square Armor - open - 2x2 - Meshlab self-intersecting faces
    Chain Mail Square Armor – open – 2×2 – Meshlab self-intersecting faces

    Although all four outer links in that image come from the same OpenSCAD module with identical sizes, they don’t all exhibit the same problem in the (nominally identical) faces on each of their four corners. In fact, those faces come from the intersection of two square slabs, carefully sized and positioned to avoid creating coincident planes:

    Chain Mail Link - Outer shape
    Chain Mail Link – Outer shape

    The central opening comes from a similar, slightly smaller, intersected-squares shape, but all four interior corner faces in each link show that they’re self-intersecting.

    The STL looked fine in Meshlab, except for the highlit self-intersecting faces, so the geometry seemed OK.

    When Slic3r autocorrected the “problems”, it apparently removed one vertex on the bottom surface of each bar, deleted the triangles connected to that vertex, then repaired the mesh to produce a delightfully symmetric pattern:

    Chain Mail Square Armor - open - 2x2 - Slic3r corrections
    Chain Mail Square Armor – open – 2×2 – Slic3r corrections

    Although the links are resolutely symmetric, Slic3r seemed happy with the identical vertices at the other end of the bar.

    Unfortunately, the resulting G-Code won’t produce good links:

    Chain Mail Square Armor - open - 2x2 - first layer G-code visualization
    Chain Mail Square Armor – open – 2×2 – first layer G-code visualization

    So, shortly before Christmas, I filed an issue on OpenSCAD’s Github repository.

    The ensuing discussion showed that Meshlab flags faces as “self intersecting” when they have different vertices, even if their values are numerically equal, as well as vertices that differ by teeny amounts. Slic3r applies slightly different criteria to vertices & faces when it automagically corrects “problems” in the STL file, so that Meshlab may:

    • Highlight faces that don’t bother Slic3r
    • Apply the same highlight to faces that cause horrible problems

    I don’t profess to understand much of that and may have the details wrong, but, apparently, OpenSCAD formerly used quantized coordinates that ensured all vertices within a tiny volume would have the same numeric value. In particular, all three faces that meet at a common point would, in fact, have numerically equal coordinate values for that point. The STL file format consists of a list of separate triangles, each with three coordinates for each of the three axes, and (without quantization) it was entirely possible for each of the three triangles with a common point to have three very slightly different positions for that point.

    In theoretic terms, quantized coordinates cause horrible problems during geometric manipulation, because numeric values that aren’t exact can make repeated transformations come out wrong; running an object through a transformation and it’s inverse might not yield an object identical to the original one.

    In practical terms, it seems that slicers and STL repair algorithms can reach incorrect conclusions based on minute differences produced by floating-point operations and numeric-to-text conversions. Those differences depend on slight changes in position, rotation, and size, so doing anything to the model produces completely different results.

    That notwithstanding, the day after Christmas brought a new OpenSCAD version that uses quantized coordinates. A bit of rummaging in the source shows that the 3D grid (defined in src/grid.h) isn’t all that coarse:

    const double GRID_FINE   = 0.00000095367431640625;
    

    STL files don’t carry units, so that could be in either millimeters (the Slic3r / RepRap convention) or inches (Sketchup, but we won’t go there). It’s exactly 1/10242, in case you were wondering, which produces a 5% speedup in the geometry engine compared to the more human-readable 1/10002.

    With that commit in hand, all the chain mail links slice perfectly again.

    A very nice Christmas present, indeed!

    Thanks, Marius…