LED Floor Lamp UI Improvement

A new floor lamp arrived with the usual dark-gray-on-black annotations on an absolutely non-tactile pair of capacitive controls. For a device intended for use in a dim room, this makes little sense, unless you’re both trendy and concerned about manufacturing costs.

A strip of 1/4 inch Kapton tape added just enough tactility to find the damn buttons without looking at the lamp head:

Teckin floor lamp - tactile switch tape
Teckin floor lamp – tactile switch tape

The pole’s non-adjustable length put the lamp head well above eye level, so I removed one pole segment. This required cutting the 12 V zipcord and crimping a pair of connectors:

Teckin floor lamp - spliced wire
Teckin floor lamp – spliced wire

I briefly considered conjuring a skinny connector, but came to my senses: there’s plenty of zipcord if I must chop out the connectors, particularly seeing as how shortening the pole added a foot.

The setscrew at the bottom of the gooseneck crunched the zipcord against the metal shell. A polypropylene snippet made me feel better, even if it makes no difference:

Teckin floor lamp - wire clamp pad
Teckin floor lamp – wire clamp pad

After all that, It Just Worked™:

Teckin floor lamp - installed
Teckin floor lamp – installed


GCMC Radial Text

The Tektronix Circuit Computer needs text along radial lines:

Tek CC - original RC arrow layout
Tek CC – original RC arrow layout

Fortunately, this doesn’t require nearly as much effort as the text-on-arcs code, because GCMC includes functions to rotate paths around the origin:

return rotate_xy(TextPath,Angle) + CenterPt;

The only trick is figuring out how to handle the justification, given the overall path length:

  local pl = TextPath[-1].x;

  local ji = (Justify == TEXT_LEFT)     ? 0mm :
             (Justify == TEXT_CENTERED) ? -pl/2 :
             (Justify == TEXT_RIGHT)    ? -pl :

A testcase showed it worked:

Radial text testcase
Radial text testcase

With that in hand, I took the liberty of slightly simplifying the original Tek layout:

Tek CC - radial text example
Tek CC – radial text example

If lives depended on it, one could duplicate the Tek layout, but they don’t and I didn’t. Fancy typography isn’t a GCMC thing.

And, yeah, laser printing is way crisper than a pen drawing.

The GCMC source code as a GitHub Gist:

comment("RadialText test");
ctr = [0mm,0mm];
r = 20mm;
a = 0deg;
tp = scale(typeset("Left Inward",TextFont),LegendTextSize);
tpr = RadialText(tp,ctr,r,a,TEXT_LEFT,INWARD);
tp = scale(typeset("Left Outward",TextFont),LegendTextSize);
tpr = RadialText(tp,ctr,r,a,TEXT_LEFT,OUTWARD);
a = 90deg;
tp = scale(typeset("Right Inward",TextFont),LegendTextSize);
tpr = RadialText(tp,ctr,r,a,TEXT_RIGHT,INWARD);
tp = scale(typeset("Right Outward",TextFont),LegendTextSize);
tpr = RadialText(tp,ctr,r,a,TEXT_RIGHT,OUTWARD);
a = 180deg;
tp = scale(typeset("Center Inward",TextFont),LegendTextSize);
tpr = RadialText(tp,ctr,r,a,TEXT_CENTERED,INWARD);
tp = scale(typeset("Center Outward",TextFont),LegendTextSize);
tpr = RadialText(tp,ctr,r,a,TEXT_CENTERED,OUTWARD);
a = 270deg;
RadialLegend("Offset to radius",ctr,r,a,TEXT_CENTERED,INWARD,-0.5);
// Write text on a radial line
function RadialText(TextPath,CenterPt,Radius,Angle,Justify,Orient) {
local pl = TextPath[-1].x; // path length
local ji = (Justify == TEXT_LEFT) ? 0mm : // justification indent
(Justify == TEXT_CENTERED) ? -pl/2 :
(Justify == TEXT_RIGHT) ? -pl :
if (Orient == INWARD) {
TextPath = rotate_xy(TextPath,180deg);
ji = -ji;
TextPath += [Radius + ji,0mm];
return rotate_xy(TextPath,Angle) + CenterPt;
// Draw a radial legend
// Offset in units of char height: 0 = baseline on radius, +/- = above/below
function RadialLegend(Text,Center,Radius,Angle,Justify,Orient,Offset) {
local tp = scale(typeset(Text,TextFont),LegendTextSize) + [0mm,Offset * LegendTextSize.y];
local tpr = RadialText(tp,Center,Radius,Angle,Justify,Orient);

GCMC Text on Arcs: Improved Version

The Tektronix Circuit Computer scale annotations read both inward (from the center) and outward (from the rim):

Text on Arcs - orientation
Text on Arcs – orientation

It’s surprisingly difficult (for me, anyhow) to see the middle FL Scale as reading upside-down, rather than mirror-image backwards.

This turned into a rewrite of the the read-outward annotation code I used for the vacuum tube reflectors. Eventually the justification and orientation options came out right:

Text-on-arcs example
Text-on-arcs example

The text baseline sits at the specified radius from the center point, regardless of its orientation, so you must offset the text path by half its height in the proper direction before handing it to the ArcText function.

The testcase shows the invocation ritual:

    ctr = [0mm,0mm];

    tp = scale(typeset("Right Inward",TextFont),ScaleTextSize);
    tpa = ArcText(tp,ctr,30mm,45deg,TEXT_RIGHT,INWARD);
    tp = scale(typeset("Right Outward",TextFont),ScaleTextSize);
    tpa = ArcText(tp,ctr,30mm,45deg,TEXT_RIGHT,OUTWARD);

    tp = scale(typeset("Center Inward",TextFont),ScaleTextSize);
    tpa = ArcText(tp,ctr,20mm,45deg,TEXT_CENTERED,INWARD);
    tp = scale(typeset("Center Outward",TextFont),ScaleTextSize);
    tpa = ArcText(tp,ctr,20mm,45deg,TEXT_CENTERED,OUTWARD);

    tp = scale(typeset("Left Inward",TextFont),ScaleTextSize);
    tpa = ArcText(tp,ctr,10mm,45deg,TEXT_LEFT,INWARD);
    tp = scale(typeset("Left Outward",TextFont),ScaleTextSize);
    tpa = ArcText(tp,ctr,10mm,45deg,TEXT_LEFT,OUTWARD);


A utility function to draw scale legends stuffs some of that complexity into a bottle:

function ArcLegend(Text,Radius,Angle,Orient) {

  local tp = scale(typeset(Text,TextFont),LegendTextSize);
  local tpa = ArcText(tp,[0mm,0mm],Radius,Angle,TEXT_CENTERED,Orient);


Which means most of the text uses a simpler invocation:

  r = Radius + TickMajor + 2*TickGap + LegendTextSize.y;

  logval = MinLog + 1.5;
  a = offset + logval * Arc;
  ArcLegend("nH - nanohenry  x10^-9",r,a,INWARD);

  logval += 3;
  a = offset + logval * Arc;
  ArcLegend("μH - microhenry  x10^-6",r,a,INWARD);

Arc determines the angular span of each decade, with positive values going counterclockwise. MinLog is the logarithm of the scale endpoint, so adding 1.5 puts the text angle one-and-a-half decades from MinLog and multiplying by Arc moves it in the right direction. The offset angle rotates the entire scale with respect to the 0° reference sticking out the X axis over on the right. The top picture has its 0° reference pointing north-northeast.

The GCMC source code as a GitHub Gist:

INWARD = -1; // text and tick alignment (used as integers)
TEXT_LEFT = -1; // text justification
// Bend text around an arc
function ArcText(TextPath,CenterPt,Radius,BaseAngle,Justify,Orient) {
local pl = TextPath[-1].x; // path length
local c = 2*pi()*Radius;
local ta = to_deg(360 * pl / c); // subtended angle
local ja = (Justify == TEXT_LEFT ? 0deg : // assume OUTWARD
(Justify == TEXT_CENTERED) ? -ta / 2 :
(Justify == TEXT_RIGHT) ? -ta :
ja = BaseAngle + Orient*ja;
local ArcPath = {};
local pt,r,a;
foreach(TextPath; pt) {
if (!isundef(pt.x) && !isundef(pt.y) && isundef(pt.z)) { // XY motion, no Z
r = (Orient == OUTWARD) ? Radius - pt.y : Radius + pt.y;
a = Orient * 360deg * (pt.x / c) + ja;
ArcPath += {[r*cos(a) + CenterPt.x, r*sin(a) + CenterPt.y,-]};
elif (isundef(pt.x) && isundef(pt.y) && !isundef(pt.z)) { // no XY, Z up/down
ArcPath += {pt};
else {
error("ArcText - Point is not pure XY or pure Z: " + to_string(pt));
return ArcPath;

GCMC treats variables defined inside a function as local, unless they’re already defined in an enclosing scope, whereupon you overwrite the outer variables. This wasn’t a problem in my earlier programs, but I fired the footgun with nested functions using the same local / temporary variables. Now, I ruthlessly declare truly local variables as local, except when I don’t, for what seem good reasons at the time.

Wood Board Cheese Slicer Rebolting

Long ago, a wood-base countertop cheese slicer arrived with a tenuous connection between its screw-on knob / handle and the bolt securing the cutting wire. The problem seemed to be boogered bolt threads:

Cheese slicer - original bolt
Cheese slicer – original bolt

The knob screwed firmly onto a known-good 10-24 screw, not the M5 bolt I expected, so the slicer may be old enough to be Made In America. Ya never know around here.

However, the hex head is essential, because you must hold it while tightening the nut capturing the slicing wire. Not having a 10-24 or even 10-32 bolt in hand, I went full-frontal metric with an M5 bolt.

Even with a full face shield, I don’t like standing in the plane of an abrasive cutting tool, even a piddly Dremel disk, so the slot through the head isn’t the best work I’ve ever presented:

Cheese slicer - slotted bolt head
Cheese slicer – slotted bolt head


Cheese slicer - skewed slot
Cheese slicer – skewed slot

But it’s hereby defined to be Good Enough™ for the purpose.

As you might expect, I ran an M5×0.8 tap into the existing 10-24 knob thread, hand-turning the lathe chuck and lining up the tap wrench with the tailstock.

Drill out the slicer’s frame hole to clear the bolt, re-string wire through slot, tighten jam nut, add a locking nut on the other side, screw on the knob, and it’s All Good:

Cheese slicer - repaired
Cheese slicer – repaired

Ugly, but good.

I expect the re-wrapped wire will break in short order, because you just can’t re-bend steel wire with impunity. So far, so good.

Wasabi NP-BX1 Teardown

Thinking of strapping a big external cell next to my Sony HDR-AS30V helmet camera, I took a look inside a defunct NP-BX1 lithium batteries:

Wasabi NP-BX1 - intact
Wasabi NP-BX1 – intact

It’s a scant 43 mm long, 9.5 mm thick, and 29.5 mm wide. You’ll find more grisly details in the solid models for the NP-BX1 battery test fixture and battery holders for blinky LEDs.

Peeling off the wrapper holding the end caps in place reveals some ID on the cell:

Wasabi NP-BX1 - cell data
Wasabi NP-BX1 – cell data

The bottom end cap is just a plastic bumper held on with glue and tape:

Wasabi NP-BX1 - base bumper
Wasabi NP-BX1 – base bumper

The top end cap has keying indents, identifies the terminals, and falls off without the wrapper:

Wasabi NP-BX1 - contact cover plate
Wasabi NP-BX1 – contact cover plate

The C and – terminals use the same PCB pad.

The black plastic around the PCB pulls just far enough away from the cell to expose the tabs welded to the terminals:

Wasabi NP-BX1 - cell to PCB tabs
Wasabi NP-BX1 – cell to PCB tabs

Snip ’em and it’s done:

Wasabi NP-BX1 - bare cell tabs
Wasabi NP-BX1 – bare cell tabs

As with all lithium cells, the can is + and the isolated tab is -.

Given that I have a stack of tabbed 18650 cells, I’m thinking of building a cell into a structure snapping into the AS30V’s helmet mount, along with a charge controller PCB. I normally remove the camera from the helmet and bring it inside and, since I must open the back to extract the MicroSD card, plugging a USB cable into the charger isn’t much of an imposition.

More pondering is in order.

CNC 3018-ProXL: Y-axis Extension

The CNC 3018-Pro uses cheap & readily available parts, so extending the Y axis went smoothly:

CNC 3018-34 - overview
CNC 3018-34 – overview

The 2040 side rails are now 450 mm long, as is the 8 mm leadscrew. I ordered 500 mm guide rods to forestall small length mismatches, then marked them to match the rails:

CNC 3018-ProXL - marking guide rods
CNC 3018-ProXL – marking guide rods

Cut them off slightly beyond the mark, face the raw ends to length, drill-and-tap for M5 screws, then put a pair of just-under-50-mm stubs in the bar stockpile. They ought to come in handy for something, right?

The original side rails & guide rods were 290 (not 300!) mm long, so the table gained another 160 mm of travel for a total of 340 mm; I suppose it’s now a CNC 3034-Pro. Seeing as how it’s the only one and I don’t want to kill my snicker SEO, let’s call it a CNC 3018-ProXL or a maybe 3018-Pro34. Whatever.

The embiggened 300×340 mm platform dates back to the original 1955 kitchen: genuine Formica over plywood. It sits atop the previous 300×180 mm table, now demoted to being a riser, and a sheet of closed-cell foam, with the same 50 mm long M6 screws holding everything to T-nuts in the 3018’s original aluminum platform.

And, yes, the identical Formica underneath the machine originally covered a freestanding kitchen cabinet; I knew I kept it around for some good reason. Kinda disorienting to see a piece of the pattern moving against the same background, though.

The GRBL setup now extends the Y-axis length ($131=338) and puts the G54 coordinate at the new middle, with the Z-axis origin kissing the ball-point pen on the new surface:

 G10 L2 P1 X-145 Y-169 Z-24.6

While I was at it, I set the G28 position at the far left side of the gantry, with the table sticking out to the front, and the Z axis at the top:

 G28.1 X-298 Y-1 Z-1

Those are absolute machine coordinates, with Y and Z pulled off home by 1 mm. I set one of bCNC’s buttons to emit G28 and park the tool carrier over there, out of the way.

With all that prepared, a full-size Tek Circuit Computer disk plots the way it should on a sheet of Letter-size paper:

CNC 3018-34 - first light
CNC 3018-34 – first light

I suspect the longer rods wouldn’t work quite so well for actual milling / machining any material tougher than, say, rigid foam blocks. For engraving and pen plotting, they’re all good.

Some measurements show this countertop isn’t quite as flat as the previous one, but a pair of tweaks got it within -0.15 / +0.1 mm:

CNC 3018-ProXL - table flatness - 2019-11-09
CNC 3018-ProXL – table flatness – 2019-11-09

Which I defined to be Good Enough™ for use with spring-loaded implements of cutting & drawing.

Your mileage will certainly vary.

Always Bring Duct Tape

My pre-trip checklist now includes “Duct Tape”, so, when the tiny screw holding my sunglasses together went spung and dropped the lens on the parking lot gravel, I was prepared:

Sunglasses - duct tape FTW
Sunglasses – duct tape FTW

I continued the mission in full-frontal Harry Potter mode.

Fortunately, it’s a captive screw and returned home with us. Back in the Basement Laboratory, with a Philips 00 screwdriver and threadlocker at hand, the repair was no big deal:

Sunglasses - loose lens screw
Sunglasses – loose lens screw

You’re looking at the screw head, believe it or not.

And, no, I’m not packing a Philips 00 screwdriver on our next trip.