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.

Tag: Improvements

Making the world a better place, one piece at a time

  • Sewing Machine Bulb: LED Replacement Doodle

    Mary wants more light directly around the needle of her Kenmore Model 158 sewing machine, as the existing light (a 120 V 15 W incandescent bulb tucked inside the end housing) casts more of a diffuse glow than a directed beam:

    Kenmore Model 158 Sewing Machine - lamp
    Kenmore Model 158 Sewing Machine – lamp

    The end cap fits snugly around the bulb, but I thought a pair of 10 mm white LEDs, mounted side-by-side and aimed downward at the cover plate, would work. Of course, plugging a pair of white LEDs into a 120 VAC socket won’t work, but some judicious rewiring and a new 12 V DC wall wart will take care of that.

    The bulb has a dual-contact bayonet base, with both pins isolated from the shell and connected to the non-polarized (!) line cord through the power switch. I didn’t know it was called a BA15d base, but now I do.

    A 12 V automotive brake/taillight bulb (type 1157, I think) pulled from the Big Box o’ Bulbs has a slightly different pin arrangement that keys the filaments (which are not isolated from the shell) to the surrounding reflector:

    BA15d Bayonet Bulb Bases - 120V vs. 12V pins
    BA15d Bayonet Bulb Bases – 120V vs. 12V pins

    So I conjured a mockup to see if it would fit, using 2-56 screws to mimic whatever hardware might be practical:

    BA15d Bulb - LED Adapter
    BA15d Bulb – LED Adapter

    The solid model shows how it all fits together:

    Sears Lamp LED Adapter - Show view
    Sears Lamp LED Adapter – Show view

    The two tiny ruby-red pins represent filament snippets in alignment holes, barely visible in real life:

    LED holder parts
    LED holder parts

    I glued those pieces together, using a tiny machinist’s square as a jig to keep them perpendicular:

    LED holder clamping
    LED holder clamping

    Some random 10 mm LEDs served for testing:

    BA15d Bulb - 10 mm LEDs
    BA15d Bulb – 10 mm LEDs

    It actually fit pretty well, ignoring the fact that the LEDs point 90° from the intended direction (so I could see how the holes came out inside the pivot, honest), and lit up the area quite well, but it’s such a delicate affair that removing the entire socket and replacing it with a dedicated metal bracket / heatsink for two high-power SMD LEDs will be better.

    The OpenSCAD source code:

    // Adapter for LEDs in Sears sewing machine lamp socket
    // Ed Nisley - KE4ZNU - January 2014
    
    Layout = "Show";		// Build Show LEDTab LEDPlate ShellMount
    
    //- Extrusion parameters must match reality!
    //  Print with 2 shells and 3 solid layers
    
    ThreadThick = 0.20;
    ThreadWidth = 0.40;
    
    HoleWindage = 0.2;			// extra clearance
    
    Protrusion = 0.1;			// make holes end cleanly
    Gap = 2.0;					// spacing between Show parts
    
    AlignPinOD = 1.70;			// assembly alignment pins: filament dia
    
    inch = 25.4;
    
    //----------------------
    // Dimensions
    
    //-- LED mounting plate
    
    LEDDia = 10.0;				// LED case OD
    LEDFlangeOD = 10.7;
    
    LEDPlateThick = 2.0;		// mounting plate thickness
    LEDMargin = 2.0;
    
    LEDSpaceOC = LEDDia + LEDMargin;		// LED center-to-center distance (single margin between!)
    
    LEDTabLength = 15.0;		// base to screw hole center
    
    LEDTabThick = 4.0;			// tab with hole for mounting screw
    LEDTabScrewOD = 2.0;
    LEDTabWidth = (3.0*2) + LEDTabScrewOD;
    
    LEDMountHeight = 25.0;		// estimated mounting screw centerline to bottom of LEDs
    
    //-- Lamp base adapter
    //		hard inch dimensions!
    
    ShellOD = 0.600 * inch;				// dia of metallic shell
    ShellOAL = 0.66 * inch;				//  ... total length
    ShellInsert = 7/16 * inch;			//  ... length engaging socket
    
    ShellSides = 4*4;
    
    BulbOD = 0.75 * inch;				// glass bulb
    BulbLength = 1.14 * inch;
    
    InsulOD = 0.485 * inch;				// insulating stub around contact pins
    InsulThick = 0.070 * inch;			//  ... beyond end of shell
    
    ContactOD = 2.0;					// contact holes through base (not heads)
    ContactOC = 0.300 * inch;			//  ... center-to-center spacing
    
    BayonetOD = 0.080 * inch;			// bayonet pin diameter
    BayonetOffset = 0.125 * inch;		// from end of metal base
    
    LampOAL = InsulThick + ShellOAL + BulbLength;
    echo(str("Overall Length: ",LampOAL));
    
    //-- Miscellany
    
    //----------------------
    // 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) {
    
      Range = floor(50 / Space);
    
    	for (x=[-Range:Range])
    	  for (y=[-Range:Range])
    		translate([x*Space,y*Space,Size/2])
    		  %cube(Size,center=true);
    }
    
    //-- Tab for screw mounting LED holder
    //		AddLength remains below Z=0 for good union
    
    module LEDTab() {
    
    	difference() {
    		linear_extrude(height=LEDTabThick)
    			hull() {
    				circle(d=LEDTabWidth);
    				translate([LEDTabLength/2,0,0])
    					square([LEDTabLength,LEDTabWidth],center=true);
    			}
    		translate([0,0,-Protrusion])
    			rotate(180/6)
    				PolyCyl(LEDTabScrewOD,(LEDTabThick + 2*Protrusion),6);
    		for (i=[-1,1])
    			translate([LEDTabLength/2,i*LEDTabWidth/4,LEDTabThick/2])
    				rotate([0,90,0]) rotate(180/4)
    					PolyCyl(AlignPinOD,(LEDTabLength/2 + Protrusion),4);
    	}
    }
    
    //-- Plate holding LEDs
    
    module LEDPlate() {
    
    	difference() {
    		union() {
    			linear_extrude(height=LEDPlateThick)
    				hull() {
    					for (i=[-1,1])
    						translate([i*LEDSpaceOC/2,0,0])
    							circle(d=(LEDDia + 2*LEDMargin));
    					translate([0,(LEDFlangeOD/2 + LEDTabWidth/2),0])
    						square([LEDTabThick,LEDTabWidth],center=true);
    				}
    		}
    		for (i=[-1,1])
    			translate([i*LEDSpaceOC/2,0,-Protrusion])
    				rotate(180/12)
    					PolyCyl(LEDDia,(LEDPlateThick + 2*Protrusion),12);
    		for (i=[-1,1])
    			translate([0,(i*LEDTabWidth/4 + LEDFlangeOD/2 + LEDTabWidth/2),3*ThreadThick]) rotate(180/4)
    				PolyCyl(AlignPinOD,(LEDTabLength/2 + Protrusion),4);
    
    	}
    }
    
    //-- Bulb shell mounting adapter
    
    module ShellMount() {
    
    	difference() {
    		union() {
    			cylinder(r1=InsulOD/2,r2=ShellOD/2,h=(InsulThick + Protrusion),$fn=ShellSides);
    			translate([0,0,InsulThick])
    				cylinder(r=ShellOD/2,h=(LampOAL - LEDMountHeight + LEDTabWidth/2),$fn=ShellSides);
    		}
    
    		translate([0,ShellOD,(InsulThick + BayonetOffset)])		// bayonet pin hole
    			rotate([90,0,0]) rotate(180/4)
    				PolyCyl(BayonetOD,2*ShellOD,4);
    
    		translate([0,ShellOD,(InsulThick + LampOAL - LEDMountHeight)])		// LED mount screw hole
    			rotate([90,0,0])
    				PolyCyl(LEDTabScrewOD,2*BulbOD,6);
    
    		translate([0,0,(InsulThick + ShellOAL + LampOAL/2)])		// slot for LEDTab mount
    			cube([2*ShellOD,(LEDTabThick + 2*Protrusion),LampOAL],center=true);
    
    		for (i=[-1,1])											// contact pin holes
    			translate([i*ContactOC/2,0,-Protrusion])
    				rotate(180/6)
    					PolyCyl(ContactOD,2*LampOAL,6);
    	}
    
    }
    
    //- Build it
    
    ShowPegGrid();
    
    if (Layout == "LEDTab")
    	LEDTab();
    
    if (Layout == "LEDPlate")
    	LEDPlate();
    
    if (Layout == "ShellMount")
    	ShellMount();
    
    if (Layout == "Show") {
    	LEDPlate();
    	translate([-LEDTabThick/2,(LEDFlangeOD/2 + LEDTabWidth/2),(LEDTabLength + LEDPlateThick + Gap)])
    		rotate([0,90,0])
    			LEDTab();
    	for (i=[-1,1])
    #	translate([0,(i*LEDTabWidth/4 + LEDFlangeOD/2 + LEDTabWidth/2),(LEDPlateThick + Gap/4)])
    		rotate(180/4)
    		cylinder(r=AlignPinOD/2,h=Gap/1,$fn=4);		// fake the pins
    
    	translate([0,(LEDFlangeOD/2 + LEDTabWidth/2),(LampOAL - LEDTabWidth/2)])
    		rotate([0,180,0]) rotate(90)
    			ShellMount();
    }
    
    if (Layout == "Build") {
    	translate([0,LEDDia,0])
    		LEDPlate();
    
    	translate([-10,-(LEDMargin + LEDTabWidth),0])
    		rotate(-90)
    			LEDTab();
    
    	translate([10,-(LEDMargin + LEDTabWidth),0])
    		ShellMount();
    }
    

    The original doodles for the bulb dimensions and adapter layout:

    Bulb dimensions - adapter doodles
    Bulb dimensions – adapter doodles
  • Modified Quilting Foot: Speed Wrench Knob

    The Nyloc nut atop that modified quilting foot requires more grip than fingers can provide:

    Modified Darning Foot - in action
    Modified Darning Foot – in action

    The “precision” wrench I adapted to that nut works for small adjustments, but for larger ones it’s easier to take the foot off and spin this knob:

    Quilting Foot Knob - knurling
    Quilting Foot Knob – knurling

    It has a hex opening in each end that fits the nut, with a through hole for the bolt. The top looks exactly like you’d expect:

    Quilting Foot Knob - top
    Quilting Foot Knob – top

    The bottom needs a bit of support:

    Quilting Foot Knob - bottom support
    Quilting Foot Knob – bottom support

    The solid model shows off the support in color:

    Quilting Foot Knob
    Quilting Foot Knob

    The OpenSCAD source code doesn’t have many surprises:

    // Quilting foot knob
    // Ed Nisley KE4ZNU January 2013
    
    use <knurledFinishLib_v2.scad>
    
    //- Extrusion parameters must match reality!
    //  Print with +1 shells and 3 solid layers
    
    ThreadThick = 0.20;
    ThreadWidth = 0.40;
    
    HoleWindage = 0.2;
    
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    
    Protrusion = 0.1;			// make holes end cleanly
    
    //----------------------
    // Dimensions
    
    KnobOD = 20.0;
    KnobLength = 25.0;
    KnobSides = 12;
    
    DiamondLength = KnobLength/3;
    DiamondWidth = DiamondLength/2;
    DiamondDepth = 1.0;
    
    NutOD = 7.0;				// across flats!
    NutLength = 6.0;
    ScrewOD = 4.0;
    
    DoSupport = true;
    
    //----------------------
    // 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);
    
    }
    
    module Knob() {
    	rotate(180/Sides) {
    		difference() {
    //			cylinder(r=KnobOD/2,h=KnobLength,$fn=KnobSides);
    			render(convexity=10)
    			knurl(k_cyl_hg=KnobLength,
    				  k_cyl_od=KnobOD,
    				  knurl_wd=DiamondWidth,
    				  knurl_hg=DiamondLength,
    				  knurl_dp=DiamondDepth,
    				  e_smooth=DiamondLength/2);
    			translate([0,0,-Protrusion])
    				PolyCyl(ScrewOD,(KnobLength + 2*Protrusion),6);
    			translate([0,0,(KnobLength - NutLength)])
    				PolyCyl(NutOD,(NutLength + Protrusion),6);
    			translate([0,0,-Protrusion])
    				PolyCyl(NutOD,(NutLength + Protrusion),6);
    		}
    	}
    }
    
    module Support() {
    	color("Yellow")
    		for (Seg=[0:5]) {
    			rotate(360*Seg/6)
    			translate([0,0,(NutLength - ThreadThick)/2])
    				cube([(NutOD - 1*ThreadWidth),
    						2*ThreadWidth,
    						(NutLength - ThreadThick)],
    						center=true);
    			}
    }
    
    //----------------------
    // Build them!
    
    ShowPegGrid();
    
    Knob();
    
    if (DoSupport)
    	Support();
    

    Mary likes it… and thinks I’m being silly. She’s right, of course.

  • Forcing Pulseaudio DisplayPort Volume Initialization

    It turns out that the audio-over-HDMI/DisplayPort channel which, for whatever reason, is the only way to get audio out of the Optiplex 980 with the big Dell U2711 monitor starts up AT MAXIMUM VOLUME! regardless of the GUI’s Pulseaudio mixer setting that’s diligently saved-and-restored across sessions. That makes a certain perverse sense, as the digital-to-analog converter & power amp live inside the monitor.

    Manually adjusting the GUI mixer by one click, either up or down, forces the new setting out over the digital link to the monitor, after which the audio output corresponds to the mixer; I never remember that until just after some dipshit auto-play video lights up with a fanfare.

    Setting the mixer to the same value doesn’t force an update, so the obvious solution (at least to me) of sending a fixed initial value doesn’t work; it’s optimized away. I think that’s why the initial update doesn’t happen: the stored volume is the same as the, ah, stored volume, so there’s no need to tell the monitor.

    The automatic solution involves putting two more commands in my ever-growing ~/.config/startup.sh:

    amixer -D pulse sset Master 26%
    amixer -D pulse sset Master 1%-
    

    That sets a rational level (which might be the same as the existing one from the previous session), then changing it by one tiny click to force the new value out to the monitor.

    And then It Just Works…

  • Whirlpool Refrigerator Drawer Strut Repair

    The strut supporting the two drawers in the bottom of the refrigerator came out in two pieces during a recent cleaning session. To judge from the condition of the joint, I’d done this once before in its history:

    Refrigerator strut - tab clamps
    Refrigerator strut – tab clamps

    That tab inserts into a slot in the front of the elaborate frame that supports the drawers, where it’s captured by a metal bar. Should you lift the rear of the strut without first removing the bar, the tab snaps off at the base. I’ve annotated the top of the strut in the hopes of reminding me the next time around.

    A pair of bumps at the front of the drawer guides should hold the drawers closed, but it’s pretty obvious that’s not working as intended:

    Refrigerator strut - worn retainers
    Refrigerator strut – worn retainers

    I shaped strips of phosphor bronze spring stock around the bumps:

    Refrigerator strut - phosphor bronze covers - top
    Refrigerator strut – phosphor bronze covers – top

    The bottom view shows they’re held in place by crimps and a generous dollop of faith:

    Refrigerator strut - phosphor bronze covers - bottom
    Refrigerator strut – phosphor bronze covers – bottom

    That should serve until I know whether the plastic drawer rail will carve through the metal. The drawers slide out with much more enthusiasm now, so it’s a Good Thing until something else breaks.

    Yes, this is the refrigerator with the Freezer Dog

  • Thing-O-Matic 286 Conversion: Marlin Firmware Tweaks

    Azteeg X3 - inside TOM286
    Azteeg X3 – inside TOM286

    Although the TOM286 conversion won’t need any fancy firmware, I forked Marlin’s Github repository and created a TOM286 branch based on the Marlin_v1 branch for the Azteeg X3 modifications; in theory, we can blend in future Marlin updates without too much hassle.

    The Pronterface serial port tops out at 115200, so that’s a mandatory change right up front. [grin]

    Marlin has a motherboard definition for an Azteeg X3 (type 67), but without the optional thermocouple inputs. I added motherboard 671, following the lead of the Megatronics board definitions (types 70, 701, and 702). In addition to the changes below, any test for motherboard 67 now includes 671, as the other pins and suchlike (should) be the same.

    Motherboard 671 selects new pin definitions for the temperature inputs in pins.h:

      #if MOTHERBOARD == 671
    	#define TEMP_0_PIN         11   // TC1 on shield
    	#define TEMP_1_PIN          4   // TC2 on shield
    	#define TEMP_2_PIN         13   // T0 thermistor on Azteeg X3 motherboard
      #else
        #define TEMP_0_PIN         13   // ANALOG NUMBERING
        #define TEMP_1_PIN         15   // ANALOG NUMBERING
        #define TEMP_2_PIN         -1   // ANALOG NUMBERING
    #endif
    

    There’s now a TCOUPLE_AMP_TYPE definition in Configuration.h to select AD595 or AD849x thermocouple interfaces:

    // Thermocouple sensor amplifier type
    //  0 = AD595 gain = 10 mV/C
    //  1 = AD849[4567] gain = 5 mV/C
    
    #define TCOUPLE_AMP_TYPE 1
    

    That picks the proper offset and gain definitions in Configuration_adv.h:

    // The AD849[4567] has 5 mv/C gain, half that of the AD595, and requires _GAIN = 2
    
    #if TCOUPLE_AMP_TYPE == 1
      #define TEMP_SENSOR_AD595_OFFSET 0.0
      #define TEMP_SENSOR_AD595_GAIN   2.0
    #else
      #define TEMP_SENSOR_AD595_OFFSET 0.0
      #define TEMP_SENSOR_AD595_GAIN   1.0
    #endif
    

    With those in hand, these temperature sensor selections in Configuration.h will work:

    #define TEMP_SENSOR_0 -1
    #define TEMP_SENSOR_1 0
    #define TEMP_SENSOR_2 0
    #define TEMP_SENSOR_BED 1
    

    I tweaked the temperature limits and preheat settings; the absolute minimum temperatures are now 10 °C, although I have not verified that a disconnected thermocouple or thermistor will actually trip that limit.

    Given the completely arbitrary stepper motor wiring connections, I set all the direction inversions to false and then swapped wires to make the motors turn in the proper direction.

    I enabled EEPROM_SETTINGS, but haven’t verified that values can actually store and recall themselves.

    The XYZ=0 origin is in the middle of the platform, just where I like it, but that will require some fine tuning:

    // Travel limits after homing
    #define X_MAX_POS 55
    #define X_MIN_POS -50
    #define Y_MAX_POS 60
    #define Y_MIN_POS -60
    #define Z_MAX_POS 120
    #define Z_MIN_POS 0
    

    I think it’s possible to use the Z_SAFE_HOMING position to force Z-minimum homing on the platform height switch I built for the original firmware, but that operation also seems to be tied in with the three-point auto-leveling firmware and rotating switch assembly. Right now, the firmware homes to the Z-max switch as a stock Thing-O-Matic should, but I’ve never liked that arrangement; don’t start with me, you know how I get.

    I backed the speeds and accelerations down from the values I’d been using, mostly because the driver hardware and currents are different:

    // Computing steps/mm
    // for XY = (motor steps/rev * microstepping) / (pulley teeth * tooth pitch)
    // for  Z = (motor steps/rev * microstepping) / (screw lead) // for  E = (motor steps/rev * microstepping) / (gear ratio * drive circumference) //  make sure ratios use floating point to avoid integer division!
    #define DEFAULT_AXIS_STEPS_PER_UNIT   {(200*16)/(17*2.0), (200*16)/(17*2.0), (200*8)/8.0, (200*4)/((7*30.23)/51)}
    #define DEFAULT_MAX_FEEDRATE          {5000/60, 5000/60, 1500/60, 4000/60}    // (mm/sec)
    #define DEFAULT_MAX_ACCELERATION      {5000, 2500, 1000, 250}    // X, Y, Z, E maximum start speed for accelerated moves. E default values are good for skeinforge 40+, for older versions raise them a lot.
    
    #define DEFAULT_ACCELERATION          10000   // X, Y, Z and E max acceleration in mm/s^2 for printing moves
    #define DEFAULT_RETRACT_ACCELERATION  10000   // X, Y, Z and E max acceleration in mm/s^2 for retracts
    
    

    I’m not sure how to calculate the “jerk” settings, but taken as the maximum un-accelerated speed, these seem conservative:

    // The speed change that does not require acceleration (i.e. the software might assume it can be done instantaneously)
    #define DEFAULT_XYJERK                25    // (mm/sec)
    #define DEFAULT_ZJERK                 5    // (mm/sec)
    #define DEFAULT_EJERK                 5     // (mm/sec)
    

    More tuning is in order; that should at least start it up.

  • Free Motion Quilting Darning Foot Modification: The Home Shop Way

    Mary has been learning free motion quilting, which uses a special sewing-machine foot that holds the fabric in place. Leah Day describes modifying a standard darning foot, but I suggested deploying a bit more shop-fu to do it right. The notion of “adjusting” something with a twisted rubber band just made my skin crawl…

    The starting point is a Brewer BP1814 “FOOT Darning/Quilting low shank with clear base”, two of which appear next to an older version that she’s had for quite some time. The rightmost one has my modifications:

    Brewer BP1814 Quilting Foot - assortment
    Brewer BP1814 Quilting Foot – assortment

    The older (mostly metal) foot works much better for its intended purpose, but the newer white plastic version seems easier to modify for free-motion quilting. The older spring is much softer than the new ones, for whatever that’s worth. After the modification, the spring pressure becomes largely irrelevant, as it only acts when something pushes up on the base.

    The first modification improves visibility by cutting out part of the transparent plastic base. Leah suggests chopping it with a diagonal cutter (“jewelry clippers”), but I deployed a slitting saw in the Dremel tool at low speed to avoid melting. Mary wanted angled cuts, so that’s what she got:

    Modified Darning Foot - opened base
    Modified Darning Foot – opened base

    A bit of touchup with a fine file smoothed out the edges so the base slides easily over the fabric. There’s no way to remove the red guide lines; the un-modified foot on the left emerged from its bag with that smeared line.

    Then drive out the top metal pin with a small drift punch, hold the base and shaft, remove the C-clip, capture the spring, and extract the base and shaft. The 4.0 mm diameter metal shaft cries out to be threaded, so that’s what I did; this picture shows the reassembled shaft and spring:

    Modified Darning Foot - threaded shaft
    Modified Darning Foot – threaded shaft

    That’s significantly harder to accomplish than it looks, because there’s no practical way to remove the plastic base (it’s pinned in place, but one side of the cross-hole is blocked). I filed the end of the shaft to a taper that started the M4.0x0.7 die a bit more easily, clamped the shaft in the bench vise, applied nasty sulfur-based tapping fluid, crossed my fingers and eyes, held my nose, and managed to make it happen without cracking the plastic.

    I reamed out the Nyloc nut with a hand-twisted series of drills, through about #24 = 3.861 mm, to reduce the locking torque. It’s now just slightly more than finger-tight, which should suffice.

    In use, the foot fits under the sewing machine’s arm and puts the nut where fingers can’t reach. I filed a 6.0 mm “precision wrench” to fit the 6.8 mm nut flats and it’s All Good:

    Modified Darning Foot - assembled with wrench
    Modified Darning Foot – assembled with wrench

    A staged photo op atop some trial quilting:

    Modified Darning Foot - in action
    Modified Darning Foot – in action

    With a Nyloc nut instead of a rubber band, it will stay exactly where she wants it…

  • Sandisk 32 GB Flash Drive: Now With String!

    So I drill two holes in the dust caps of those teensy Sandisk drives and added a cheerful red string:

    Sandisk 32 GB Flash Drive - cap string
    Sandisk 32 GB Flash Drive – cap string

    That this should not be necessary goes without saying…