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: Electronics Workbench

Electrical & Electronic gadgets

  • NB-5L Holder: Internal Springs

    The first pass at a holder for a Canon NB-5L battery didn’t quite work, but the failure was instructive. The overall layout was fine; the battery fit well, it’s just that the pins and springs didn’t function properly.

    NB-5L Holder - Internal spring - solid model
    NB-5L Holder – Internal spring – solid model

    I thought a simple straight music wire spring pushed into a hole with a pocket to limit the pin travel would suffice. Watching it build showed that the pocket came out too small and the spring hole was almost completely closed, despite a bit of HoleWindage.

    Here’s a closer look at the spring arrangement, with a pocket at the bottom for an epoxy blob after it became obvious the ABS couldn’t properly anchor the pin.

    NB-5L Holder - Internal spring - detail
    NB-5L Holder – Internal spring – detail

    Drilling out the holes to allow free pin movement ended up with #52 for the tip, #40 for the shaft, and #31 for the ferrule at the end; choose for an easy slip fit.

    I used 0.024 inch (0.6 mm) music wire, which fit neatly into the pin’s inspection hole. Drilling that (#73 drill) into the nearly closed hole through the bottom showed that things weren’t working well: far too much resistance along what should be a half-open channel.

    With the pin in place and a stub of wire pushed upward into the pin, the pins moved very stiffly and tended to not return to their rest position. Minus the spring wire, they slid freely.

    After a bit of this and that, I tried 0.020 music wire, which didn’t have enough return force at all.

    The end of the spring wire moved around a lot more than I think it should have, gradually turning the hole into an oval. I drilled two pockets in the bottom (and changed the solid model to match what you see above) and cast a dab of epoxy into each hole; that solved the moving-around problem, but the pins were still too stiff.

    NB-5L Holder - first version - bottom
    NB-5L Holder – first version – bottom

    The weird orange color comes from a few layers of Safety Orange filament that melded into white in mid-flight. No reason to use fancy filament on a prototype, methinks; that’s what was in place when I started.

    But I glued the cap on anyway to see if the pins would work well enough to run some early battery tests. This is what it looked like before gluing the cap:

    NB-5L Holder - first version
    NB-5L Holder – first version

    The Powerpole connectors came from one of those packs, with the wires soldered into the ends of the pins so as to not block the inspection holes that I’m using for the music wire springs.

    The OpenSCAD source code:

    // Holder for Canon NB-5L Li-Ion battery
    // Ed Nisley KE4ZNU August 2011
    
    include </home/ed/Thing-O-Matic/lib/MCAD/units.scad>
    include </home/ed/Thing-O-Matic/lib/MCAD/boxes.scad>
    include </home/ed/Thing-O-Matic/Useful Sizes.scad>
    
    // Layout options
    
    Layout = "Show";					// Case Lid Show Build Fit
    
    //- Extrusion parameters - must match reality!
    //  Print with +2 shells and 3 solid layers
    
    ThreadThick = 0.33;
    ThreadWidth = 2.0 * ThreadThick;
    
    HoleWindage = 0.2;
    
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    
    Protrusion = 0.1;			// make holes end cleanly
    
    BuildOffset = 3.0;			// clearance for build layout
    
    //- Battery dimensions - rationalized from several samples
    //  Coordinate origin at battery corner by contact plates on bottom surface
    
    BatteryLength = 45.25;
    BatteryWidth = 32.17;
    BatteryThick =  7.85;
    
    ContactWidth = 2.10;
    ContactLength = 4.10;
    ContactRecess = 0.85;
    
    ContactOC = 3.18;			// center-to-center across contact face
    ContactOffset = 4.45;		// offset from battery edge
    ContactHeight = 3.05;		// offset from battery bottom plane
    
    AlignThick = 2.2;			// alignment recesses on contact face
    AlignDepth = 2.0;			// into face
    AlignWidth1 = 0.7;			// across face at contacts
    AlignWidth2 = 2.8;			//  ... other edge
    
    //- Pin dimensions
    
    PinTipDia = 1.6;
    PinTipLength = 10.0;
    
    PinTaperLength = 2.3;
    
    PinShaftDia = 2.4;
    PinShaftLength = 6.8;
    
    PinFerruleDia = 3.0;
    PinFerruleLength = 2.0;
    
    PinLength = PinTipLength + PinTaperLength + PinShaftLength + PinFerruleLength;
    
    PinHoleOffset = 13.9;			// tip to spring hole
    
    //- Spring dimensions
    
    ExtendRelax = 1.5 + ContactRecess;		// pin extension when no battery is present
    ExtendOvertravel = 1.0;					//  ... beyond engaged position
    
    SpringDia = 0.024 * inch;	// music wire spring
    SpringTravel = ExtendRelax + ExtendOvertravel;
    SpringLength = 4.0 + PinShaftDia/2;			// free length below pin centerline
    
    //- Holder dimensions
    
    GuideRadius = ThreadWidth;						// friction fit ridges
    GuideOffset = 10;
    WallThick = 4*ThreadWidth;						// holder sidewalls
    
    BaseThick = IntegerMultiple(6.0,ThreadThick);	// bottom of holder to bottom of battery
    TopThick = 6*ThreadThick;	// top of battery to top of holder
    
    ThumbRadius = 10.0;			// thumb opening at end of battery
    
    CornerRadius = 3*ThreadThick;			// nice corner rounding
    
    CaseLength = 2*WallThick + PinLength - ExtendRelax + ExtendOvertravel + BatteryLength + GuideRadius;
    CaseWidth = 2*WallThick + 2*GuideRadius + BatteryWidth;
    CaseThick = BaseThick + BatteryThick + TopThick;
    
    //- XY origin at front left battery corner, Z on platform below that
    
    CaseLengthOffset = -(WallThick + PinLength - ExtendRelax + ExtendOvertravel);
    CaseWidthOffset = -(WallThick + GuideRadius);
    CaseThickOffset = BaseThick;
    
    LidLength = ExtendRelax - CaseLengthOffset;
    
    SpringPlugDia = 3.0;			// filament snippet holding spring wire
    SpringPlugLength = IntegerMultiple(1.0,ThreadThick);
    
    echo(str("Spring wire from end: ",WallThick + PinLength - PinHoleOffset));
    echo(str("            from side: ",WallThick + GuideRadius + ContactOffset));
    echo(str("Pin spacing on centers: ",ContactOC));
    
    //----------------------
    // 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);
    
    }
    
    //-------------------
    
    //-- Guides for tighter friction fit
    
    module Guides() {
      	  translate([GuideOffset,-GuideRadius,CaseThickOffset])
    		PolyCyl(2*GuideRadius,(BatteryThick - Protrusion),4);
    	  translate([GuideOffset,(BatteryWidth + GuideRadius),CaseThickOffset])
    		PolyCyl(2*GuideRadius,(BatteryThick - Protrusion),4);
    	  translate([(BatteryLength - GuideOffset),-GuideRadius,CaseThickOffset])
    		PolyCyl(2*GuideRadius,(BatteryThick - Protrusion),4);
    	  translate([(BatteryLength - GuideOffset),(BatteryWidth + GuideRadius),CaseThickOffset])
    		PolyCyl(2*GuideRadius,(BatteryThick - Protrusion),4);
    	  translate([(BatteryLength + GuideRadius),GuideOffset/2,CaseThickOffset])
    		PolyCyl(2*GuideRadius,(BatteryThick - Protrusion),4);
    	  translate([(BatteryLength + GuideRadius),(BatteryWidth - GuideOffset/2),CaseThickOffset])
    		PolyCyl(2*GuideRadius,(BatteryThick - Protrusion),4);
    
    }
    
    //-- Contact pins (holes therefore)
    
    module PinShape() {
    
    PolyPin = false;
    
      union() {
    	if (PolyPin)
    	  PolyCyl(PinTipDia,(PinTipLength + Protrusion));
    	else
    	  cylinder(r=(PinTipDia + HoleWindage)/2,h=(PinTipLength + Protrusion),$fn=6);
    
    	translate([0,0,PinTipLength])
    	  if (PolyPin)
    		PolyCyl(PinShaftDia,(PinTaperLength + PinShaftLength + Protrusion));
    	  else
    		cylinder(r=(PinShaftDia + HoleWindage)/2,
    				 h=(PinTaperLength + PinShaftLength + Protrusion),$fn=6);
    
    	translate([0,0,(PinLength - PinFerruleLength)])
    	  if (PolyPin)
    		PolyCyl(PinFerruleDia,(PinFerruleLength + Protrusion));
    	  else
    		cylinder(r=(PinFerruleDia + HoleWindage)/2,
    				 h=(PinFerruleLength + Protrusion),$fn=6);
    
    	translate([0,0,PinLength])
    	  if (PolyPin)
    		PolyCyl(PinFerruleDia,PinLength);			// very long holes to punch case
    	  else
    		cylinder(r=(PinFerruleDia + HoleWindage)/2,h=PinLength,$fn=6);
      }
    
    }
    
    module PinAssembly() {
    
      translate([ExtendRelax,ContactOffset,CaseThickOffset + ContactHeight]) {	// pins
    	rotate([0,270,0]) {
    	  PinShape();
    	  translate([0,(2*ContactOC),0])
    		PinShape();
    	}
      }
    
      translate([-(PinHoleOffset - ExtendRelax + SpringTravel/2),
    			 ContactOffset,
    			 (CaseThickOffset + ContactHeight - SpringLength/2 + Protrusion/2)]) {
    	  cube([SpringTravel,
    		    (2*SpringDia),
    			(SpringLength + Protrusion)],
    		   center=true);										// spring deflection pocket
    	  translate([0,(2*ContactOC),0])
    		cube([SpringTravel,
    			 (2*SpringDia),
    			 (SpringLength + Protrusion)],
    			 center=true);
      }
    
      translate([-(PinHoleOffset - ExtendRelax),
    			 ContactOffset,
    			 (-Protrusion/2)]) {
    	PolyCyl(SpringDia,(BaseThick + ContactHeight + Protrusion));		// spring wire
    	PolyCyl(SpringPlugDia,(SpringPlugLength + Protrusion));				// wire holder
    	translate([0,(2*ContactOC),0]) {
    	  PolyCyl(SpringDia,(BaseThick + ContactHeight + Protrusion));
    	  PolyCyl(SpringPlugDia,(SpringPlugLength + Protrusion));
    	}
      }
    
    }
    
    //-- Case with origin at battery corner
    
    module Case() {
    
      difference() {
    
    	union() {
    
    	  difference() {
    		translate([(CaseLength/2 + CaseLengthOffset),
    				  (CaseWidth/2 + CaseWidthOffset),
    				  (CaseThick/2)])
    		  roundedBox([CaseLength,CaseWidth,CaseThick],CornerRadius); 	// basic case shape
    
    		translate([-ExtendOvertravel,-GuideRadius,CaseThickOffset])
    		  cube([(BatteryLength + GuideRadius + ExtendOvertravel),
    				(BatteryWidth + 2* GuideRadius),
    				(BatteryThick + Protrusion)]);						// battery space
    
    	  }
    
    	  Guides();
    
    	  translate([-ExtendOvertravel,-GuideRadius,BaseThick])
    		cube([(AlignDepth + ExtendOvertravel),
    			  (AlignWidth1 + GuideRadius),
    			  AlignThick]);											// alignment blocks
    	  translate([-ExtendOvertravel,
    				 (BatteryWidth - AlignWidth2),
    				 BaseThick])
    		cube([(AlignDepth + ExtendOvertravel),
    			  (AlignWidth2 + GuideRadius),
    			  AlignThick]);
    
    	}
    
    	translate([(-ExtendOvertravel),
    			   (CaseWidthOffset - Protrusion),
    			   (CaseThickOffset + BatteryThick)])
    	  cube([CaseLength,
    		    (CaseWidth + 2*Protrusion),
    		    (TopThick + Protrusion)]);								// battery access
    
    	translate([(CaseLengthOffset - Protrusion),
    			   (CaseWidthOffset - Protrusion),
    			   (CaseThickOffset + BatteryThick)])
    	  cube([(CaseLength + 2*Protrusion),
    		    (CaseWidth + 2*Protrusion),
    		    (TopThick + Protrusion)]);								// battery insertion allowance
    
    	translate([(BatteryLength - Protrusion),
    			    (CaseWidth/2 + CaseWidthOffset),
    			    (CaseThickOffset + ThumbRadius)])
    	  rotate([90,0,0])
    		rotate([0,90,0])
    		  cylinder(r=ThumbRadius,
    				   h=(WallThick + GuideRadius + 2*Protrusion),
    				   $fn=22);											// remove thumb notch
    
    	PinAssembly();
    
      }
    
    }
    
    module Lid() {
    
      difference() {
    	translate([0,0,(CaseThick/2 - BaseThick - BatteryThick)])
    	  roundedBox([LidLength,
    				 CaseWidth,CaseThick],CornerRadius);
    
    	translate([0,0,-(CaseThick/2)])
    	  cube([(LidLength + 2*Protrusion),
    		    (CaseWidth + 2*Protrusion),
    		    (CaseThick)],center=true);
      }
    
    }
    
    //-------------------
    // Build it!
    
    ShowPegGrid();
    
    if (Layout == "Case")
      Case();
    
    if (Layout == "Lid")
      Lid();
    
    if (Layout == "Show") {								// reveal pin assembly
      difference() {
    	Case();
    
    	translate([(CaseLengthOffset - Protrusion),
    			   (CaseWidthOffset - Protrusion + WallThick + ContactOffset + ContactOC),
    			   (BaseThick + ContactHeight)])
    	  cube([(-CaseLengthOffset + Protrusion),
    			 (CaseWidth + 2*Protrusion),
    			 CaseThick + BaseThick - ContactHeight + Protrusion]);
    
    	translate([(CaseLengthOffset - Protrusion),
    			   (CaseWidthOffset - Protrusion),
    			   -Protrusion])
    	  cube([(-CaseLengthOffset + Protrusion),
    			 (WallThick + GuideRadius + ContactOffset + Protrusion),
    			 CaseThick]);
      }
    
      translate([ExtendRelax,ContactOffset,CaseThickOffset + ContactHeight]) {	// pins
    	rotate([0,270,0]) {
    	  %PinShape();
    //	  translate([0,(2*ContactOC),0])
    //		%PinShape();
    	}
      }
    
    }
    
    if (Layout == "Build") {
      translate([-(CaseLength/2 + CaseLengthOffset),-(CaseWidthOffset - BuildOffset),0])
    	Case();
      translate([0,(CaseLengthOffset/2 - BuildOffset),0])
    	rotate([0,0,90])
    	  Lid();
    
    }
    
    if (Layout == "Fit") {
      Case();
      translate([(-LidLength/2 + ExtendRelax),
    			(CaseWidth/2 + CaseWidthOffset),
    			(BaseThick + BatteryThick)])
    	  Lid();
      translate([ExtendRelax,ContactOffset,CaseThickOffset + ContactHeight]) {	// pins
    	rotate([0,270,0]) {
    	  %PinShape();
    	  translate([0,(2*ContactOC),0])
    		%PinShape();
    	}
      }
    
    }
    
  • Silent Headphone: It’s Always the Cable

    Before trotting off to college, my Shop Assistant repaired a pair of headphones she’d scrounged at the end of her Senior year; they were nice Skullcandy on-ear phones with one dead cup. The previous owner evidently wasn’t into fixing things and bequeathed them to her.

    She dismantled the offending cup to find some really grody soldering that ought to have produced a cold solder joint: the common lead wasn’t making contact with the plug. Alas, the only way to proceed was to slice the injection-molded cover off the plug and see what was inside; to our surprise, everything looked fine.

    We then cut an inch off the cable and pulled the conductors out:

    Broken headphone cable conductors
    Broken headphone cable conductors

    Well, that was easy! Here’s a closer look:

    Broken headphone conductor - detail
    Broken headphone conductor – detail

    The three broken wires (only one of which was completely disconnected) failed exactly at the end of that plug covering. It had strain relief notches, but we guessed the previous owner had no qualms about bending the cable hard against the end of the plug.

    She soldered it up, shrank some heat stink tubing around the plug and its sliced cover, wrapped self-vulcanizing tape around the junction for better strain relief, and it’s all good. She’ll get plenty of use out of the headphones in the dorm …

  • Canon NB-5L Battery Holder Doodles

    A first cut at a holder for Canon NB-5L batteries with those dimensions, with the intent of connecting them to a battery tester.

    NB-5L Battery Holder Doodle
    NB-5L Battery Holder Doodle

    The cloud in the middle of the bottom holds pin dimensions, which I measured after I’d been doodling for a bit. They come from my heap; they’re nice heavy-gold plated male pins (yeah, I have the other gender, too) intended for a multi-pin connector. They have a convenient hole that’s normally used to verify you’ve actually soldered the wire properly. I plan to stick a music-wire spring in the hole and secure it through the bottom of the holder with a rectangular pocket below the pin that limits the travel in both directions. Drilling the hole completely through the pin to let the spring wire stick out would prevent having it fall out at the end of travel.

    The spring pocket dimensions are right down around the very limit of the feature sizes my TOM can achieve. I’m not sure those blind holes will actually open up far enough.

    I’m also not sure the Powerpoles will actually fit in there like that. There’s nothing wrong with pigtail leads.

    It’s obviously styled after the Official Canon NB-5L charger, although they use nice bent-steel spring contacts that are trivially easy to make in mass production:

    Canon NB-5L charger
    Canon NB-5L charger
  • Stepper Dynamometer: Mechanical Resonance

    Here’s a howling 1200 Hz mechanical resonance excited by a full-step drive at 290 rpm, with the resonance running at 5 cycles for every 4 full steps.:

    1 Step Res 240 Hz
    1 Step Res 240 Hz

    [Update: top trace is voltage from big stepper-as-generator, bottom is small stepper winding current = 1 A/div.]

    Switching to 1/2 microstepping at the same rotational speed pretty well tamps it down:

    Half Step Res 240 Hz
    Half Step Res 240 Hz

    However, here’s the same resonance excited by 1/2 microstepping at half the rotational speed, oscillating at 9 irregular cycles for every 8 half-steps:

    Half res 120 Hz
    Half res 120 Hz

    The resonance was strong enough to back the setscrew completely out of the generator end of the shaft, leaving me wondering why the output was dead when the motor was obviously running. Yes, several times…

  • Stepper Dynamometer: Microstepping Mode vs. Torque

    Conventional wisdom has it that stepper motor torque decreases as the number of microsteps per full step increases. One reasonably careful measurement trumps a kilo-opinion, so here’s a chart (clicky for bigger) of measurements to mull over:

    Microstepping Mode vs Speed - 17PM-J034
    Microstepping Mode vs Speed – 17PM-J034

    Each group of like-colored dots marks the results for full, 1/2, 1/4, 1/8, and 1/16 microstepping with the same load resistance. The caret marks the full-step data point within each group. The load resistances range from a dead short (about 1 Ω due to winding resistance) on the left to 50 Ω on the right.

    While I’ll grant the existence of the occasional data-collection error, it’s pretty obvious that:

    • Torque is reasonably constant regardless of microstepping mode
    • Full-step mode doesn’t produce more torque and, indeed, produces considerably less under heavy loads

    Now, one can argue that the A4988 doesn’t operate in real full-step mode, because it energizes both windings at 1/√2 of the maximum current setting for each full step rather than energizing a single winding at the maximum current. That may be true, but conventional wisdom seems to not bother with such details when opining about torque, either…

    As nearly as I can tell, 1/8 microstepping gives as much torque as you’re likely to get from the motor, while having reasonably smooth motion that avoids exciting mechanical resonances.

    That chart (or one remarkably like it) will appear in an upcoming Circuit Cellar column. The tonnage of data supporting those dots suggests building an automated dynamometer would be a Good Idea …

  • Stepper Dynamometer: High Speed Pullout Torque

    Just to see what happened, I reversed the stepper dynamometer and drove the larger (480-ish mN·m) stepper in 1/8 step mode while recording the short-circuit current from the smaller (anonymous) stepper. Slowly cranking the step frequency upward produced this trace when the stepper stalled:

    48 mm motor - pullout - 30 rps
    48 mm motor – pullout – 30 rps

    The bottom trace shows 30.4 rev/s = 191 rad/s = 1824 rev/min: a pretty good speed for a loaded stepper! The rotor began slowing just before the last sync pulse, but hadn’t lost any appreciable speed.

    The current scale is 0.5 A/div (set on the Tek probe amp), which makes the winding current (500 mA/div × 10.4 mVpk / 10 mV/div) = 520 mApk. The scope’s computed rms value includes the waveform after the stall, which isn’t helpful.

    The small stepper has a 2.1 Ω winding resistance, so a short-circuit load dissipates (0.52 A)2 × 2.1 Ω = 567 mWpk in each winding. The rms equivalent is half of that, so the total rms power is about half a watt, essentially all internal to the motor.

    The pull-out torque depends on the peak torque load, not the rms and not the sum of the two windings, so it’s 0.6 W / (191 rad/s) = 3 mN·m, which doesn’t sound a lot for a 480 mN·m motor until you consider the screaming 6000 full step/s speed: pretty much off-scale high on most of the torque-vs-speed graphs you’ll see. Not much torque left out at that end of the curve, indeed.

    In order to stall the motor at lower speeds, the load stepper must generate enough voltage into the load resistor (here, the winding resistance) to push the power/speed ratio (the torque!)  above the drive motor’s ability. That implies the load stepper must always be larger than the drive stepper, which means I must conjure up a bracket for that NEMA 23 motor that’s been holding down a stack of papers on my desk…

    Incidentally, the voltage required to produce that load current is 0.52 mA × 2.1 Ω = 1.1 V. The 0.58 v/(rev/s) open-circuit generator constant for the smaller motor predicts 0.58 v/rev/s × 30.4 rev/s = 17.7 V. Obviously, you can’t get from the open-circuit unloaded generator constant to the short-circuit loaded voltage… Lenz’s Law gets in the way.

  • Canon NB-5L Battery Dimensions

    Having acquired a bunch of cheap batteries from the usual eBay suppliers for my new Canon SX230HS pocket camera, it’d be nice to measure their actual (and undoubtedly pathetic) capacity, which implies the need for a holder to make firm contact with the terminals. Sounds like a 3D printer might come in handy for that, doesn’t it?

    The first step: measure the dimensions of actual batteries:

    NB-5L Battery Dimensions
    NB-5L Battery Dimensions

    The terminals lie on what looks to be hard 1/8 inch centers, which must be pure coincidence. They’re recessed anywhere from 0.75 mm to 1.0 mm, depending on who made the thing, into the battery’s endplate.

    The Canon charger has three spring-loaded bent-wire contacts, arranged so the (-) terminal touches first as the battery slides into the holder, then (+), and finally the thermistor (T), with about 0.5 mm between each pair. That spring loading provides enough force to hold the battery in the charger.

    FWIW, the thermistor is 7.5 kΩ w.r.t. (-) at room temperature.

    The plan so far: use three big old gold-plated terminal pins as contacts, with flexible wires to a PowerPole connector that matches the battery tester. Cross-drill the pins to fit music wire lever springs, because the contact spacing is smaller than the smallest coil springs in the Big Box o’ Little Springs. I only need two terminals, so maybe I can force-fit a pair of small coil springs in there, which would be nice.