Archive for category Software

Homage Tek CC: Subscripts & Superscripts

The GCMC typeset() function converts UTF-8 text into a vector list, with Hershey vector fonts sufficing for most CNC projects. The fonts date back to the late 1960s and lack niceties such as superscripts, so the Homage Tektronix Circuit Computer scale legends have a simpler powers-of-ten notation:

Tek CC - Pilot V5 - plain paper - red blue
Tek CC – Pilot V5 – plain paper – red blue

Techies understand upward-pointing carets, but … ick.

After thinking it over, poking around in the GCMC source code, and sketching alternatives, I ruled out:

  • Adding superscript glyphs to the font tables
  • Writing a text parser with various formatting commands
  • Doing anything smart

Because I don’t need very many superscripts, a trivial approach seemed feasible. Start by defining the size & position of the superscript characters:

SuperScale = 0.75;                                       // superscript text size ratio
SuperOffset = [0mm,0.75 * LegendTextSize.y];            //  ... baseline offset

Half-size characters came out barely readable with 0.5 mm Pilot pens:

Tek CC - Superscript test - 0.5x
Tek CC – Superscript test – 0.5x

They’re legible and might be OK with a diamond drag point.

They work better at 3/4 scale:

Tek CC - Superscript test - 0.75x
Tek CC – Superscript test – 0.75x

Because superscripts only occur at the end of the scale legends, a truly nasty hack suffices:

function ArcLegendSuper(Text,Super,Radius,Angle,Orient) {

  local tp = scale(typeset(Text,TextFont),LegendTextSize);

  tp += scale(typeset(Super,TextFont),LegendTextSize * SuperScale) + SuperOffset + [tp[-1].x,0mm];

  local tpa = ArcText(tp,[0mm,0mm],Radius,Angle,TEXT_CENTERED,Orient);


The SuperScale constant shrinks the superscript vectorlist, SuperOffset shifts it upward, and adding [tp[-1].x,0mm] glues it to the end of the normal-size vectorlist.

Yup, that nasty.

Creating the legends goes about like you’d expect:

  ArcLegendSuper("pF - picofarad  x10","-12",r,a,INWARD);

Presenting “numeric” superscripts as text keeps the option open for putting non-numeric stuff up there, which seemed easier than guaranteeing YAGNI.

A similar hack works for subscripts:

Tek CC - Subscript test - 0.75x
Tek CC – Subscript test – 0.75x

With even more brutal code:

  Sub_C = scale(typeset("C",TextFont),LegendTextSize * SubScale) + SubOffset;

<<< snippage >>>

    tp = scale(typeset("←----- τ",TextFont),LegendTextSize);
    tp += Sub_C + [tp[-1].x,0mm];
    tp += scale(typeset(" Scale -----→",TextFont),LegendTextSize) + [tp[-1].x,0mm];

The hackage satisfied the Pareto Principle, so I’ll declare victory and move on.


SK6812 RGBW Test Fixture: Row-by-Row Color Mods

The vacuum tube LED firmware subtracts the minimum value from the RGB channels of the SK6812 RGBW LEDs and displays it in the white channel, thereby reducing the PWM value of the RGB LEDs by their common “white” component. The main benefit is reducing the overall power by about two LEDs. More or less, kinda sorta.

I tweaked the SK6812 test fixture firmware to show how several variations of the basic RGB colors appear:

      for (int col=0; col < NUMCOLS ; col++) {              // for each column
        byte Value[PIXELSIZE];                              // figure first row colors
        for (byte p=0; p < PIXELSIZE; p++) {                //  ... for each color in pixel
          Value[p] = StepColor(p,-col*Pixels[p].TubePhase);
        // just RGB
        int row = 0;
        uint32_t UniColor = strip.Color(Value[RED],Value[GREEN],Value[BLUE],0);
        strip.setPixelColor(col + NUMCOLS*row++,UniColor);

        byte MinWhite = min(min(Value[RED],Value[GREEN]),Value[BLUE]);

        // only common white
        UniColor = strip.Color(0,0,0,MinWhite);
        strip.setPixelColor(col + NUMCOLS*row++,UniColor);

        // RGB minus common white + white
        UniColor = strip.Color(Value[RED]-MinWhite,Value[GREEN]-MinWhite,Value[BLUE]-MinWhite,MinWhite);
        strip.setPixelColor(col + NUMCOLS*row++,UniColor);

         // RGB minus common white
        UniColor = strip.Color(Value[RED]-MinWhite,Value[GREEN]-MinWhite,Value[BLUE]-MinWhite,0);
        strip.setPixelColor(col + NUMCOLS*row++,UniColor);

        // inverse RGB
        UniColor = strip.Color(255 - Value[RED],255 - Value[GREEN],255 - Value[BLUE],0);
        strip.setPixelColor(col + NUMCOLS*row++,UniColor);

Which looks like this:

SK6812 Test Fixture - RGBW color variations - diffuser
SK6812 Test Fixture – RGBW color variations – diffuser

The pure RGB colors appear along the bottom row, with the variations proceeding upward to the inverse RGB in the top row. The dust specks show it’s actually in focus.

The color variations seem easier to see without the diffuser:

SK6812 Test Fixture - RGBW color variations - bare LEDs
SK6812 Test Fixture – RGBW color variations – bare LEDs

The white LEDs are obviously “warm white”, which seems not to make much difference.

Putting a jumper from D2 to the adjacent (on an Nano, anyway) ground pin selects the original pattern, removing the jumper displays the modified pattern:

SK6812 test fixture - pattern jumper
SK6812 test fixture – pattern jumper

For whatever it’s worth, those LEDs have been running at full throttle for two years with zero failures!

The Arduino source code as a GitHub Gist:


Leave a comment

LibreOffice Impress vs. NFS Network Shares vs. Caching

Whenever I put together a presentation, LibreOffice Impress gradually grinds to a halt with images in the slide thumbnails repeatedly updating and never stabilizing; eventually, LO crashes and sends a crash report to whoever’s watching. This may be due to my enthusiastic use of images to get my point(s) across, although I’m just not gonna back down from that position:

LibreOffice Impress - Thumbnail thrashing
LibreOffice Impress – Thumbnail thrashing

That’s a screenshot of a small thumbnail, enlarged for visibility, so it doesn’t look that crappy in real life.

Perhaps the problem arises because I insert the images as links, rather than embedding them to create a monolithic presentation file roughly the size of all outdoors?

Searching with the obvious keywords produces tantalizing hints concerning LO’s file locks clashing with NFS network share locking, which seems appropriate for my situation with all the files living on the grandiosely named file server (a headless Optiplex) in the basement.

The suggestions include making sure the NFS locking daemon is active, but I have NFC about how that might work in practice. The lockd daemon is running, for whatever that’s worth.

Seeing as how I’m the only one editing my LO presentations, disabling LO’s locks has little downside and requires tweaking one character in one line inside /usr/bin/libreoffice:

# file locking now enabled by default
<<< blank line to show off underscores above >>>

After a brief bout of good behavior, Impress resumed thrashing and stalling.

Copying the entire presentation + images to the SSD inside my desktop PC didn’t improve the situation.

More searches on less obvious keywords suggested disabling the Impress “background cache”, whatever that might be:

Tools → Options → Impress → General → Settings

Then un-check the ☐ Use background cache item, which may be the last vestige of the now-vanished memory usage and graphics cache size settings from previous versions.

In any event, disabling the cache had no effect, so it’s likely a problem deep inside LibreOffice where I cannot venture.

It autosaves every ten minutes and I must restart it maybe once an hour: survivable, but suboptimal.

There seems to be an improvement from Version 6.0.7 (Ubuntu 18.04 LTS) to Version 6.2.8 (Manjaro rolling release), although it’s too soon to tell whether it’s a fix or just different symptoms.

Leave a comment

Raspberry Pi Shutdown / Start Button

While adding the usual Reset button to a Raspberry Pi destined for a Show-n-Tell with the HP 7475A plotter, I browsed through the latest dtoverlay README and found this welcome surprise:

Name:   gpio-shutdown
Info:   Initiates a shutdown when GPIO pin changes. The given GPIO pin
        is configured as an input key that generates KEY_POWER events.
        This event is handled by systemd-logind by initiating a
        shutdown. Systemd versions older than 225 need an udev rule
        enable listening to the input device:

                ACTION!="REMOVE", SUBSYSTEM=="input", KERNEL=="event*", \
                        SUBSYSTEMS=="platform", DRIVERS=="gpio-keys", \
                        ATTRS{keys}=="116", TAG+="power-switch"

        This overlay only handles shutdown. After shutdown, the system
        can be powered up again by driving GPIO3 low. The default
        configuration uses GPIO3 with a pullup, so if you connect a
        button between GPIO3 and GND (pin 5 and 6 on the 40-pin header),
        you get a shutdown and power-up button.
Load:   dtoverlay=gpio-shutdown,<param>=<val>
Params: gpio_pin                GPIO pin to trigger on (default 3)

        active_low              When this is 1 (active low), a falling
                                edge generates a key down event and a
                                rising edge generates a key up event.
                                When this is 0 (active high), this is
                                reversed. The default is 1 (active low).

        gpio_pull               Desired pull-up/down state (off, down, up)
                                Default is "up".

                                Note that the default pin (GPIO3) has an
                                external pullup.

        debounce                Specify the debounce interval in milliseconds
                                (default 100)

So I added two lines to /boot/config.txt:


The fancy “Moster heatsink” case doesn’t leave much room for wiring:

RPi Shutdown Restart Switch - GPIO 3
RPi Shutdown Restart Switch – GPIO 3

The switch button is slightly shorter than the acrylic sheet, so it’s recessed below the surface and requires a definite push to activate. It’s not as if it’ll get nudged by accident, but ya never know.

I’ll eventually migrate this change to all the RPi boxes around the house, because it just makes more sense than any of the alternatives. Heck, it’ll free up a key on the streaming radio player keypads, although I must move the I²C display to Bus 0 to avoid contention on Pin 3.

For reference, the Raspberry Pi header pinout:

Raspberry Pi pinout
Raspberry Pi pinout

I don’t know if I²C Bus 0 has the same 1.8 kΩ pullups as Bus 1, though; a look at the bus currents will be in order.


Leave a comment

Homage Tek CC Cursor: Pivot Milling

A test to mill the pivot hole in 0.5 mm PETG sheet worked perfectly:

Tek CC - cursor pivot hole milling
Tek CC – cursor pivot hole milling

The cutter is a 3.175 mm = 1/8 inch router bit, one of a ten-pack that came with the CNC 3018 and to which I have no deep emotional attachment, held in a collet in the Sherline. The hole is 5.5 mm to fit an eyelet. The PETG is taped to a thin plywood scrap.

The hole happened by feeding G-Code manually into LinuxCNC, after touching off XYZ=0 at the center of the pivot and jogging up a bit:

g0 y-1.1625
g0 z0.5
g2 p5 z-1.5 i0 j1.1625

Yes, I engraved the hairline using a diamond drag tool on the CNC 3018, cut the cursor outline with a drag knife on the MPCNC, then milled the pivot hole on the Sherline. This seems way over the top, even to me, but that’s just how the tooling worked out right now.

In actual practice, I’d probably mill a stack of cursors and pivot holes on the Sherline in one setup, then engrave the hairlines in a suitable fixture. I think I know enough to fit a spring-loaded diamond drag bit into the Sherline’s 10 mm ID spindle or, worst case, conjure a block for the Z-axis carrier in place of the entire spindle mount.

At least now I can remember what I did to make the hole.


Leave a comment

HP 7475A Plotter Data Sniffing: socat Serial Port Tee

Some hints and examples provided the socat incantation required to sniff serial data between my Superformula demo program (on the Raspberry Pi) and my HP 7475A plotter:

socat /dev/ttyUSB0,raw,echo=0 SYSTEM:'tee /tmp/in.txt | socat - "PTY,link=/tmp/ttyv0,raw,echo=0,wait-slave" | tee /tmp/out.txt'

The out.txt file collects data from the program to the plotter, the in.txt file holds data from the plotter to the program, and both files contain exactly and only the serial data, so some interpretation will be required.

With that in hand, tweak the .chiplotle/ file to aim Chiplotle at the virtual serial port:

serial_port_to_plotter_map = {'/tmp/ttyv0' : 'HP7475A'}

This is dramatically easier than wiring a pair of additional hardware serial ports onto the RS-232 connection between the two:

HP 7475A - serial port adapters - hardcore
HP 7475A – serial port adapters – hardcore

The adapter stack instantly become a custom cable, although I miss Der Blinkenlights.

The HPGL output to the plotter (out.txt) comes from the Chiplotle driver with no embedded linefeed / carriage return characters, as HPGL uses semicolon command terminators, making it one humongous line impervious to the usual text utilities. In addition, several plotter configuration commands have prefix ESC (0x1b) characters without semicolon separators. Each LB (label) command within the stream ends with a 0x03 ETX character.

While one could fix all those gotchas with a sufficiently complex sed script, I manually separated the few lines I needed after each semicolon, then converted the raw ASCII control characters to displayable Unicode glyphs (␛ and ␃), making it legible for a presentation:

head -c 1000 out.txt
LBStarted 2020-01-09 18:03:57.494617␃;
LBPen 1: ␃;
LBm=1.9 n1=0.71 n2=n3=0.26␃;
<<< snippage >>>

The corresponding responses from the plotter to the program (in.txt) are separated by carriage return characters (␍) with no linefeeds (␊), so the entire file piles up at the terminal’s left margin when displayed with the usual text tools. Again, manually splitting the output at the end of each line produces something useful:

<<< snippage >>>

The first number gives the size of the serial FIFO buffer. An inexplicable ten OW; commands from deep in the Chiplotle driver code return the Output Window size in plotter units. No other commands produce any output until the plot finishes, whereupon my code waits for a digitized point from the plotter, with the (decimal) 18 indicating a point isn’t ready.

All that at 9600 bits per second …

Leave a comment

CNC 3018XL: Adding Run-Hold Switches

Although the bCNC GUI has conspicuous Run / Hold buttons, it’s easier to poke a physical switch when you really really need a pause in the action or have finished a (manual) tool change. Rather than the separate button box I built for the frameless MPCNC, I designed a chunky switch holder for the CNC 3018XL’s gantry plate:

CNC 3018-Pro - Run Hold Switches - installed
CNC 3018-Pro – Run Hold Switches – installed

The original 15 mm screws were just slightly too short, so those are 20 mm stainless SHCS with washers.

The switches come from a long-ago surplus deal and have internal green and red LEDs. Their transparent cap shows what might be white plastic underneath:

CNC 3018-Pro - Run Hold Switches - top unlit
CNC 3018-Pro – Run Hold Switches – top unlit

I think you could pry the cap off and tuck a printed legend inside, but appropriate coloration should suffice:

CNC 3018-Pro - Run Hold Switches - lit
CNC 3018-Pro – Run Hold Switches – lit

Making yellow from red and green LEDs always seems like magic; in these buttons, red + green produces a creamy white. Separately, the light looks like what you get from red & green LEDs.

The solid model shows off the recesses around the LED caps, making their tops flush with the surface to prevent inadvertent pokery:

Run Hold Switch Mount - Slic3r
Run Hold Switch Mount – Slic3r

The smaller square holes through the block may require a bit of filing, particularly in the slightly rounded corners common to 3D printing, to get a firm press fit on the switch body. The model now has slightly larger holes which may require a dab of epoxy.

A multi-pack of RepRap-style printer wiring produced the cable, intended for a stepper motor and complete with a 4-pin Dupont socket housing installed on one end. I chopped the housing down to three pins, tucked the fourth wire into a single-pin housing, and plugged them into the CAMtool V3.3 board:

CNC 3018-Pro - Run Hold Switches - CAMtool V3.3 header
CNC 3018-Pro – Run Hold Switches – CAMtool V3.3 header

The CAMtool schematic matches the default GRBL pinout, which comes as no surprise:

CAMtool schematic - Start Hold pinout
CAMtool schematic – Start Hold pinout

The color code, such as it is:

  • Black = common
  • Red = +5 V
  • Green = Run / Start (to match the LED)
  • Blue = Hold (because it’s the only color left)

The cable goes into 4 mm spiral wrap for protection & neatness, with the end hot-melt glued into the block:

CNC 3018-Pro - Run Hold Switches - bottom
CNC 3018-Pro – Run Hold Switches – bottom

The model now includes the wiring channel between the two switches, which is so obviously necessary I can’t imagine why I didn’t include it. The recess on the top edge clears the leadscrew sticking slightly out of the gantry plate.

The LEDs require ballast resistors: 120 Ω for red and 100 Ω for green, producing about 15 mA in each LED. Those are 1/8 W film resistors; I briefly considered SMD resistors, but came to my senses just in time.

A layer of black duct tape finishes the bottom sufficiently for my simple needs.

Note: the CAMtool board doesn’t have enough +5 V pins, so add a row of +5 V pins just below the standard header. If you’ve been following along, you needed them when you installed the home switches:

3018 CNC CAMTool - Endstop power mod
3018 CNC CAMTool – Endstop power mod

A doodle giving relevant dimensions and layouts:

Run Hold Switch Mount - Layout Doodles
Run Hold Switch Mount – Layout Doodles

I originally planned to mount the switches on the other gantry plate and sketched them accordingly, but (fortunately) realized the stepper motor was in the way before actually printing anything.

The OpenSCAD source code as a GitHub Gist:

It seems bCNC doesn’t update its “Restart Spindle” message after a tool change when you poke the green button (instead of the GUI button), but that’s definitely in the nature of fine tuning.

, ,