Archive for February, 2013

Eagle 6.4 Schematics for LinuxCNC 2.5 HAL Configuration: Device Creation Checklist

I’m updating the Eagle-to-HAL script and library I used to get the Joggy Thing working, with the intent of getting a Nostromo N52 working on another LinuxCNC box.

To that end, here’s a checklist for creating a new Eagle device corresponding to a HAL module.

Remember: although this process has a tremendous number of moving parts, you do it exactly once when you need a device that doesn’t already exist. After that, you just click to add an existing device to your schematic, wire it up, then the tedious write-only HAL overhead happens automagically.

Cross-check the documentation with the actual component code!

The man page lists the names, pins, parameters, and suchlike, but may have typos. This isn’t a criticism, it’s a fact of life.

Before investing a ton o’ time creating an Eagle device, load the module and find out what’s really there:

halcmd: loadrt conv_float_s32
halcmd: show all
Loaded HAL Components:
ID      Type  Name                                      PID   State
     4  RT    conv_float_s32                                  ready
     3  User  halcmd2395                                 2395 ready

Component Pins:
Owner   Type  Dir         Value  Name
     4  float IN              0
     4  s32   OUT             0  conv-float-s32.0.out
     4  bit   OUT         FALSE  conv-float-s32.0.out-of-range

... snippage ...

Owner   Type  Dir         Value  Name
     4  bit   RW          FALSE  conv-float-s32.0.clamp
     4  s32   RO              0  conv-float-s32.0.time
     4  s32   RW              0  conv-float-s32.0.tmax

... snippage ...

Exported Functions:
Owner   CodeAddr  Arg       FP   Users  Name
 00004  fc0a9000  fc0630b8  YES      0   conv-float-s32.0
... snippage ...


  • The module name uses underscores as separators: loadrt conv_float_s32
  • The function name uses h-y-p-h-e-n-s as separators: conv-float-s32.0
  • Unlike in the Linux kernel, the two characters are not equivalent

Add the HAL Module to the Conversion Script

The hal-write.ulp script contains a table of all the module names, so you must update the script in parallel with the hal-config.lbr Eagle library.

However, you can create an Eagle device that is not a HAL module by omitting it from the script. In that case, the Eagle device name will become part of the net names that define and interconnect the pins, but the script will not create a statement to load a module. For example, the hal_input userspace program creates a set of pins for each input device that start with input.n, but there’s no corresponding HAL module. I’ll put up an example of all this in a bit.

Create a Schematic Symbol

  • The name of the symbol is not critical: CONVERT.sym
    • use either dashes or hyphens as you prefer
  • The >NAME string must be on layer 95-Names
  • No need for a >VALUE string, but put it on layer 96-Values if present
  • HAL pins become symbol pins
    • Use the HAL pin name, with hyphens
    • Set Visibility to Pin
    • Set Direction to in / out / io to match the HAL description
    • Set Function to None to indicate an ordinary net connection
  • Verify the pins against the HAL device!

Create a HAL Schematic Device

  • The new device name must match the HAL module name, with underscores, as entered in the conversion script table
  • Set the Prefix to the HAL function name, plus a trailing period, with hyphens
    • CONV-FLOAT-S32.
  • Create the Description using copy-and-paste from the HTML source: use the man page in the LinuxCNC doc
    • Ctrl-U in Firefox reveals the HTML source, Ctrl-A and Ctrl-C, flip windows, then Ctrl-V
    • Delete all the boilerplate at the top, leave the centered Title, ditch the reference links
  • Add the symbol you created earlier or reuse an existing symbol
    • Set the symbol NAME to a single underscore: _
    • Change the Add level to must
  • Add a PIN_FUNCTION symbol to the device
    • Change the symbol name from G$1 (or whatever) to a single period: .
    • Change the Add Level to must
  • Add PIN_PARAMETER symbols as needed
    • Change the symbol name from G$1 (or whatever) to the parameter name preceded by a single period: .CLAMP
    • Change the Add Level to request
    • Change the Direction as needed
  • Add the DUMMY physical package, then connect all the pins to pads

Create a non-HAL Schematic Device

  • The new device name may be anything that’s not in the conversion script table
  • The Prefix must match the desired pin names, plus a trailing period. For hal_input pins:
    • INPUT.
  • Create the Description as above
  • Add the symbol you created earlier
    • Set the symbol NAME to a single underscore: _
    • Change the Add level to must
  • Do not add a PIN_FUNCTION symbol, because it has no corresponding module
  • Add PIN_PARAMETER symbols as needed
    • Change the symbol name from G$1 (or whatever) to the parameter name preceded by a single period: .CLAMP
    • Change the Add Level to request
    • Change the Direction as needed
  • Add the DUMMY physical package, then connect all the pins to pads

Devices may have multiple Symbols, with different Add Level options; can seems appropriate. As nearly as I can tell, you must name each Symbol as a suffix to the full name to differentiate them within the Device; I use a hyphen before the suffix, so that -KEYS generates INPUT.0-KEYS. Those suffixes don’t appear elsewhere in the generated HAL configuration file.

Save the library, update it in the schematic editor (Library → Update ...), and you’re set.

Although it’s tempting, do not include a version number in the library file name, because Eagle stores the file name inside the schematic file along with the devices from that file. As a result, when you bump the library version number and use devices from the new library file, the schematic depends on both library files and there’s no way within Eagle to migrate devices from one library to the other; you must delete the existing devices from the schematic and re-place them from the new library. Or you can do like I did: hand-edit the XML fields inside the library file.

Eagle HAL Device

Eagle HAL Device

You’ll almost certainly drive this procedure off the rails, so let me know what I’ve screwed up. It does, in fact, work wonderfully well and, as far as I’m concerned, makes HAL usable, if only because HAL is a write-only language to start with and now you need not read it to modify it.

, , , ,

1 Comment

HB-415M Stepper Driver Heatsinking: Lack Thereof

Just to see what’s inside, I took those HB-415M drivers apart. They’re not all identical inside:

HB-415M Driver - interior top

HB-415M Driver – interior top

The other side shouldn’t come as much of a surprise:

HB-415M Driver heatsinking

HB-415M Driver heatsinking

Now, admittedly, I’ve applied a heatsink to the top of an epoxy package, but that DIP package has thermal tabs that should connect to the heatsink through a low-thermal-resistance path. A dab (!) of heatsink grease and what might be a thermally conductive plastic sheet atop the package seem, well, insufficient.

The driver chip sports an Allegro A3992 marking that might be genuine. The datasheet goes into some detail as to how you should lay out the PCB; none of its recommendations made it into the finished product. In particular, the hulking current sense resistors surely have more inductance than you’d like.

The resistor color code seems odd: black red red silver brown.

HB-415M current sense resisors

HB-415M current sense resisors

Using black as the first band is unexpected, but it’s probably the only way to indicate a low-value resistance without printing the numbers: 0.22 Ω ±1%.

Ah, well, the peak current isn’t as high as they claim, so it all probably works out in the end.



American Standard Elite Kitchen Faucet: Handle Failure

Strange though it may seem, the kitchen faucet handle broke while Mary was using it. The rear wall of the socket that fits over the cartridge valve stem fractured:

American Standard Faucet Handle - broken mount

American Standard Faucet Handle – broken mount

Having no water in the kitchen is not to be tolerated, so I applied a redneck fix while pondering the problem:

Kitchen Faucet - redneck handle repair

Kitchen Faucet – redneck handle repair

Based on that comment, I called the American Standard hotline (800-442-1920), described the situation, and they’re sending a replacement handle and cartridge. Evidently the new handle won’t fit the old cartridge, which makes me feel better about not stockpiling repair parts, even while I now wonder what the new cartridge part number might be and how you’d tell them apart.

Anyhow, the redneck fix wouldn’t suffice for the next week; I needed something slightly more permanent. The broken wall fit neatly in place on the mount, but:

  • It must withstand far more force than a simple glue joint can provide
  • I can’t machine square holes
  • Wrapping a metal sleeve around the mount seemed like too much work

You undoubtedly saw this coming a while ago:

Am Std Faucet Handle Sleeve - solid model

Am Std Faucet Handle Sleeve – solid model

The mount tapers slightly from the handle body toward the open end to provide draft for the molding process. I applied a hull() operator to two thin rectangles spaced the right distance apart along the Z axis to create a positive model of the mount, which then gets subtracted from the blocky outer rectangle. The hole clears a 10-32 screw that fits the standard setscrew threads (normally hidden behind the handle’s red-and-blue button).

Unlike most printed parts I’ve done recently, the sleeve suffered from severe shrinkage along the outside walls:

Faucet handle sleeve - build distortion

Faucet handle sleeve – build distortion

The inside maintained the right shape, so I cleared the nubs with a file and pressed it in place around the mount with the rear wall snapped into position. The black plastic socket evidently isolates the handle from the valve stem and I used a stainless 10-32 screw to prevent the nightmare scenario of having the sleeve slide downward along the tapered mount and block the setscrew. Overall, it came out fine:

American Standard faucet handle - compression sleeve

American Standard faucet handle – compression sleeve

However, the chunky sleeve didn’t clear the opening in the escutcheon cap, which put the cap on the windowsill for the next week. The result works much better than the redneck fix and looks almost presentable. It’s certainly less conspicuous:

American Standard faucet handle - temporary repair

American Standard faucet handle – temporary repair

I hope the new handle has a much more robust socket…

The OpenSCAD source code:

// Quick fix for broken American Standard Elite 4454 faucet handle
// Ed Nisley KE4ZNU February 2013

//- Extrusion parameters must match reality!
//  Print with +2 shells and 3 solid layers

ThreadThick = 0.25;
ThreadWidth = 2.0 * ThreadThick;

HoleFinagle = 0.4;
HoleFudge = 1.00;

function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
function HoleAdjust(Diameter) = HoleFudge*Diameter + HoleFinagle;

Protrusion = 0.1;           // make holes end cleanly

// Dimensions

Wall = 5.0;

Slice = ThreadThick;                // minimal thickness for hull object

ShaftEnd = [11.6,17.8,Slice];
ShaftBase = [12.1,18.8,Slice];
ShaftLength = 19.0;

Block = [(ShaftBase[0] + 2*Wall),(ShaftBase[1] + 2*Wall),ShaftLength - Protrusion];

ScrewOffset = 6.5;          // from End
ScrewDia = 5.0;             // clearance

// Useful routines

module ShowPegGrid(Space = 10.0,Size = 1.0) {

    Range = floor(50 / Space);

    for (x=[-Range:Range])
        for (y=[-Range:Range])


module PolyCyl(Dia,Height,ForceSides=0) {           // based on nophead's polyholes
  Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2);
  FixDia = Dia / cos(180/Sides);

// Model the handle's tapered shaft

module Shaft() {

    hull() {
        translate([0,0,ShaftLength - Slice/2])
            cube(ShaftEnd, center=true);
            cube(ShaftBase, center=true);


// Build it!


difference() {
    translate([0,0,ShaftLength - ScrewOffset])



LinuxCNC HAL Pin Names: Belkin Nostromo N52 SpeedPad

A (formerly Belkin, now Razer, which is evidently unrelated to Mazer Rackham) Nostromo N52 SpeedPad might not be a perfect CNC pendant, but it does have plenty of buttons and an (oddly oriented) XY joypad that might be useful for, say, a 3D printer controller running LinuxCNC.

Belkin Nostromo N52 SpeedPad

Belkin Nostromo N52 SpeedPad

Following the same path as with the Logitech Dual Action Gamepad that became the Joggy Thing, we find that the N52 reports itself as a keyboard and a mouse:

less /proc/bus/input/devices

... snippage ...
I: Bus=0003 Vendor=050d Product=0815 Version=0110
N: Name="Honey Bee  Nostromo SpeedPad2 "
P: Phys=usb-0000:00:02.0-10/input0
S: Sysfs=/devices/pci0000:00/0000:00:02.0/usb2/2-10/2-10:1.0/input/input3
U: Uniq=
H: Handlers=kbd event3
B: EV=100013
B: KEY=e080ffdf 1cfffff ffffffff fffffffe
B: MSC=10

I: Bus=0003 Vendor=050d Product=0815 Version=0110
N: Name="Honey Bee  Nostromo SpeedPad2 "
P: Phys=usb-0000:00:02.0-10/input1
S: Sysfs=/devices/pci0000:00/0000:00:02.0/usb2/2-10/2-10:1.1/input/input4
U: Uniq=
H: Handlers=mouse1 event4
B: EV=20017
B: KEY=70000 0 0 0 0 0 0 0 0
B: REL=103
B: MSC=10
B: LED=7

The corresponding device files belong to the root user:

ll /dev/input
... snippage ...
crw-r-----  1 root root 13, 67 2013-02-08 11:06 event3
crw-r-----  1 root root 13, 68 2013-02-08 11:06 event4
... snippage ...
crw-r-----  1 root root 13, 33 2013-02-08 11:06 mouse1

Which calls for some udev trickery to change the owner & permissions during boot / hotplugging so that it becomes available to mere mortals such as I.

First, get the bus connection information:

... snippage ...
Bus 002 Device 004: ID 050d:0815 Belkin Components Nostromo n52 HID SpeedPad Mouse Wheel
... snippage ...

Then dump the attributes:

udevadm info --query=all --attribute-walk --name=/dev/bus/usb/002/004

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/devices/pci0000:00/0000:00:02.0/usb2/2-10':
    ATTR{bNumInterfaces}==" 2"
    ATTR{bMaxPower}==" 90mA"
    ATTR{version}==" 1.10"
    ATTR{manufacturer}=="Honey Bee "
    ATTR{product}=="Nostromo SpeedPad2 "

... snippage ...

Note the trailing blank in the manufacturer and product values.

Create a new rules file /etc/udev/rule/90-Nostromo.rules to change the group and permissions:

# Belkin Nostromo N52 SpeedPad controller for LinuxCNC
# Ed Nisley - KE4ZNU - February 2013

ATTRS{product}=="Nostromo SpeedPad2",GROUP="plugdev",MODE="0660"

Note that the file name must start with a number around 90- to avoid being clobbered by a rule in /lib/udev/rules.d/50-udev-default.rules that (re)sets the permissions to 0640; the doc suggests that rules without numbers happen after all the number rules, so perhaps you could just use meaningful names. That took an embarrassingly long time to figure out…

There’s no need for the trailing blank in that rule, as the match proceeds left-to-right and stops at the end of the test string.

You must, perforce, be in the plugdev group. If not, add yourself.

You need not unplug the N52 to test the rule. Just use:

sudo udevadm trigger

Which produces the desired result:

crw-rw----  1 root plugdev 13, 67 2013-02-08 15:09 event3
crw-rw----  1 root plugdev 13, 68 2013-02-08 15:09 event4
... snippage ...
crw-rw----  1 root plugdev 13, 33 2013-02-08 15:09 mouse1

Poking around using, among other things, xev reveals:

  • Main keypad is the left half of a standard QWERTY keyboard
  • Joypad returns cursor keys, center push = nothing
  • Orange button is, weirdly, the Left-Alt key
  • Wheel is a mouse scroll wheel
  • Push wheel = middle mouse button
  • Thumb pad = space
  • RGB LEDs are, respectively, num-, caps-, and scroll-lock

Then load HAL:

loadusr -W hal_input -K +Nostromo:0 -KRL +Nostromo:1
show pin input.*
loadusr halmeter

The + prefix tells HAL to capture the named device and prevent its events from reaching X. The KRL codes suggest which functions you’re interested in for that particular device. The suffix digit selects successive devices for multiple gadgets matching the same name string.

Apparently, the N52 reports it can produce all the usual keyboard and mouse values & buttons, even if they’re not connected to physical hardware. I suspect it has generic keyboard / mouse controllers inside, with just a few of the usual matrix crosspoints connected to switches.

The basic key mapping, sorted by the Nostromo functions:

Type Dir Name Nostromo key
bit OUT input.0.key-tab F1
bit OUT input.0.key-q F2
bit OUT input.0.key-w F3
bit OUT input.0.key-e F4
bit OUT input.0.key-r F5
bit OUT input.0.key-capslock F6
bit OUT input.0.key-a F7
bit OUT input.0.key-s F8
bit OUT input.0.key-d F9
bit OUT input.0.key-f F10
bit OUT input.0.key-leftshift F11
bit OUT input.0.key-z F12
bit OUT input.0.key-x F13
bit OUT input.0.key-c F14
bit OUT input.0.key-space F15
bit OUT input.0.key-leftalt Orange button
bit OUT input.0.key-right Pad bottom
bit OUT input.0.key-down Pad front
bit OUT input.0.key-up Pad rear
bit OUT input.0.key-left Pad top
bit OUT input.1.btn-middle Wheel press
s32 OUT input.1.rel-wheel-counts Scroll wheel
bit IN input.1.led-numl Red LED
bit IN input.1.led-capsl Green LED
bit IN input.1.led-scrolll Blue LED

The bit pins also have inverted values available on the corresponding -not pins. The LEDs have an -invert that flips the sense of the input pin. The rel-wheel pin has other useful tidbits as suffixes; the count changes by ±1 for each wheel detent.

The Tab key and all the letters auto-repeat, the various Shift and Alt keys do not. That seems to make no difference to the bit values reported by HAL.

The complete table, sorted by HAL pin name:

Type Dir Name Nostromo key
bit OUT input.0.key-0
bit OUT input.0.key-0-not
bit OUT input.0.key-1
bit OUT input.0.key-1-not
bit OUT input.0.key-102nd
bit OUT input.0.key-102nd-not
bit OUT input.0.key-2
bit OUT input.0.key-2-not
bit OUT input.0.key-3
bit OUT input.0.key-3-not
bit OUT input.0.key-4
bit OUT input.0.key-4-not
bit OUT input.0.key-5
bit OUT input.0.key-5-not
bit OUT input.0.key-6
bit OUT input.0.key-6-not
bit OUT input.0.key-7
bit OUT input.0.key-7-not
bit OUT input.0.key-8
bit OUT input.0.key-8-not
bit OUT input.0.key-9
bit OUT input.0.key-9-not
bit OUT input.0.key-a F7
bit OUT input.0.key-a-not
bit OUT input.0.key-apostrophe
bit OUT input.0.key-apostrophe-not
bit OUT input.0.key-b
bit OUT input.0.key-b-not
bit OUT input.0.key-backslash
bit OUT input.0.key-backslash-not
bit OUT input.0.key-backspace
bit OUT input.0.key-backspace-not
bit OUT input.0.key-c F14
bit OUT input.0.key-c-not
bit OUT input.0.key-capslock F6
bit OUT input.0.key-capslock-not
bit OUT input.0.key-comma
bit OUT input.0.key-comma-not
bit OUT input.0.key-compose
bit OUT input.0.key-compose-not
bit OUT input.0.key-d F9
bit OUT input.0.key-d-not
bit OUT input.0.key-delete
bit OUT input.0.key-delete-not
bit OUT input.0.key-dot
bit OUT input.0.key-dot-not
bit OUT input.0.key-down Pad left
bit OUT input.0.key-down-not
bit OUT input.0.key-e F4
bit OUT input.0.key-e-not
bit OUT input.0.key-end
bit OUT input.0.key-end-not
bit OUT input.0.key-enter
bit OUT input.0.key-enter-not
bit OUT input.0.key-equal
bit OUT input.0.key-equal-not
bit OUT input.0.key-esc
bit OUT input.0.key-esc-not
bit OUT input.0.key-f F10
bit OUT input.0.key-f-not
bit OUT input.0.key-f1
bit OUT input.0.key-f1-not
bit OUT input.0.key-f10
bit OUT input.0.key-f10-not
bit OUT input.0.key-f11
bit OUT input.0.key-f11-not
bit OUT input.0.key-f12
bit OUT input.0.key-f12-not
bit OUT input.0.key-f2
bit OUT input.0.key-f2-not
bit OUT input.0.key-f3
bit OUT input.0.key-f3-not
bit OUT input.0.key-f4
bit OUT input.0.key-f4-not
bit OUT input.0.key-f5
bit OUT input.0.key-f5-not
bit OUT input.0.key-f6
bit OUT input.0.key-f6-not
bit OUT input.0.key-f7
bit OUT input.0.key-f7-not
bit OUT input.0.key-f8
bit OUT input.0.key-f8-not
bit OUT input.0.key-f9
bit OUT input.0.key-f9-not
bit OUT input.0.key-g
bit OUT input.0.key-g-not
bit OUT input.0.key-grave
bit OUT input.0.key-grave-not
bit OUT input.0.key-h
bit OUT input.0.key-h-not
bit OUT input.0.key-home
bit OUT input.0.key-home-not
bit OUT input.0.key-i
bit OUT input.0.key-i-not
bit OUT input.0.key-insert
bit OUT input.0.key-insert-not
bit OUT input.0.key-j
bit OUT input.0.key-j-not
bit OUT input.0.key-k
bit OUT input.0.key-k-not
bit OUT input.0.key-kp0
bit OUT input.0.key-kp0-not
bit OUT input.0.key-kp1
bit OUT input.0.key-kp1-not
bit OUT input.0.key-kp2
bit OUT input.0.key-kp2-not
bit OUT input.0.key-kp3
bit OUT input.0.key-kp3-not
bit OUT input.0.key-kp4
bit OUT input.0.key-kp4-not
bit OUT input.0.key-kp5
bit OUT input.0.key-kp5-not
bit OUT input.0.key-kp6
bit OUT input.0.key-kp6-not
bit OUT input.0.key-kp7
bit OUT input.0.key-kp7-not
bit OUT input.0.key-kp8
bit OUT input.0.key-kp8-not
bit OUT input.0.key-kp9
bit OUT input.0.key-kp9-not
bit OUT input.0.key-kpasterisk
bit OUT input.0.key-kpasterisk-not
bit OUT input.0.key-kpdot
bit OUT input.0.key-kpdot-not
bit OUT input.0.key-kpenter
bit OUT input.0.key-kpenter-not
bit OUT input.0.key-kpminus
bit OUT input.0.key-kpminus-not
bit OUT input.0.key-kpplus
bit OUT input.0.key-kpplus-not
bit OUT input.0.key-kpslash
bit OUT input.0.key-kpslash-not
bit OUT input.0.key-l
bit OUT input.0.key-l-not
bit OUT input.0.key-left Pad top
bit OUT input.0.key-left-not
bit OUT input.0.key-leftalt Orange button
bit OUT input.0.key-leftalt-not
bit OUT input.0.key-leftbrace
bit OUT input.0.key-leftbrace-not
bit OUT input.0.key-leftctrl
bit OUT input.0.key-leftctrl-not
bit OUT input.0.key-leftmeta
bit OUT input.0.key-leftmeta-not
bit OUT input.0.key-leftshift F11
bit OUT input.0.key-leftshift-not
bit OUT input.0.key-m
bit OUT input.0.key-m-not
bit OUT input.0.key-minus
bit OUT input.0.key-minus-not
bit OUT input.0.key-n
bit OUT input.0.key-n-not
bit OUT input.0.key-numlock
bit OUT input.0.key-numlock-not
bit OUT input.0.key-o
bit OUT input.0.key-o-not
bit OUT input.0.key-p
bit OUT input.0.key-p-not
bit OUT input.0.key-pagedown
bit OUT input.0.key-pagedown-not
bit OUT input.0.key-pageup
bit OUT input.0.key-pageup-not
bit OUT input.0.key-pause
bit OUT input.0.key-pause-not
bit OUT input.0.key-q F2
bit OUT input.0.key-q-not
bit OUT input.0.key-r F5
bit OUT input.0.key-r-not
bit OUT input.0.key-right Pad bottom
bit OUT input.0.key-right-not
bit OUT input.0.key-rightalt
bit OUT input.0.key-rightalt-not
bit OUT input.0.key-rightbrace
bit OUT input.0.key-rightbrace-not
bit OUT input.0.key-rightctrl
bit OUT input.0.key-rightctrl-not
bit OUT input.0.key-rightmeta
bit OUT input.0.key-rightmeta-not
bit OUT input.0.key-rightshift
bit OUT input.0.key-rightshift-not
bit OUT input.0.key-s F8
bit OUT input.0.key-s-not
bit OUT input.0.key-scrolllock
bit OUT input.0.key-scrolllock-not
bit OUT input.0.key-semicolon
bit OUT input.0.key-semicolon-not
bit OUT input.0.key-slash
bit OUT input.0.key-slash-not
bit OUT input.0.key-space F15
bit OUT input.0.key-space-not
bit OUT input.0.key-sysrq
bit OUT input.0.key-sysrq-not
bit OUT input.0.key-t
bit OUT input.0.key-t-not
bit OUT input.0.key-tab F1
bit OUT input.0.key-tab-not
bit OUT input.0.key-u
bit OUT input.0.key-u-not
bit OUT input.0.key-up Pad right
bit OUT input.0.key-up-not
bit OUT input.0.key-v
bit OUT input.0.key-v-not
bit OUT input.0.key-w F3
bit OUT input.0.key-w-not
bit OUT input.0.key-x F13
bit OUT input.0.key-x-not
bit OUT input.0.key-y
bit OUT input.0.key-y-not
bit OUT input.0.key-z F12
bit OUT input.0.key-z-not
bit OUT input.1.btn-middle Wheel press
bit OUT input.1.btn-middle-not  … inverted
bit OUT input.1.btn-mouse
bit OUT input.1.btn-mouse-not
bit OUT input.1.btn-right
bit OUT input.1.btn-right-not
bit IN input.1.led-capsl Green LED
bit IN input.1.led-capsl-invert
bit IN input.1.led-numl Red LED
bit IN input.1.led-numl-invert
bit IN input.1.led-scrolll Blue LED
bit IN input.1.led-scrolll-invert
s32 OUT input.1.rel-wheel-counts Scroll wheel
float OUT input.1.rel-wheel-position  =count/scale
bit IN input.1.rel-wheel-reset Clear pos’n
float IN input.1.rel-wheel-scale  count→pos’n
s32 OUT input.1.rel-x-counts
float OUT input.1.rel-x-position
bit IN input.1.rel-x-reset
float IN input.1.rel-x-scale
s32 OUT input.1.rel-y-counts
float OUT input.1.rel-y-position
bit IN input.1.rel-y-reset
float IN input.1.rel-y-scale



Printing Scale Model Concrete Blocks

For reasons that undoubtedly make sense to him, my buddy Aitch is moving to coastal NC. Seeing as how we lived in Raleigh for half a decade, I figure he needs some hints on how to blend in…

Toy cars up on blocks

Toy cars up on blocks

The solid model looks about the way you’d expect:

Concrete block - solid model

Concrete block – solid model

The webs are slightly thinner than in real life, but it looks OK to me. The web came out slightly over 3 thread widths = 1.5 mm, to ensure they get a bit of fill rather than being two distinct threads. I originally tried making the web exactly 3 threads wide, which produced tiny dots of fill on the sides and corners. They printed with 0.20 infill; they’d print faster with 1.00 infill or all-solid layers.

You’ll want to create a pile o’ blocks at once, of course, although this array took about two hours:

Concrete blocks - build platform

Concrete blocks – build platform

The OpenSCAD source code:

// Scale model concrete block
// Ed Nisley KE4ZNU February 2013

// Extrusion parameters must match reality!
// Print with +0 shells and 3 solid layers

ThreadThick = 0.25;
ThreadWidth = 2.0 * ThreadThick;

function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);

Protrusion = 0.1;           // make holes end cleanly

// Dimensions

Scale = (1/25) * (3*ThreadWidth);

BlockWidth = Scale * 190;
BlockLength = Scale * 390;
BlockHeight = BlockWidth;

WebWidth = Scale * 30;

CoreSize = [(BlockWidth - 2*WebWidth),(BlockLength - 4*WebWidth)/2,BlockHeight];

CornerRadius = WebWidth/2;

// Useful routines

module ShowPegGrid(Space = 10.0,Size = 1.0) {

    Range = floor(50 / Space);

    for (x=[-Range:Range])
        for (y=[-Range:Range])


// Component parts

module Core(Size,Radius) {
    translate([0,0,(Size[2] - Protrusion)/2])
        minkowski() {
            cube([(Size[0] - 2*Radius),(Size[1] - 2*Radius),Size[2]],center=true);

// Build it!


difference() {
    for (i = [-1,1])
        translate([0,i*(CoreSize[1] + WebWidth)/2,0])
    for (i = [-1,1])
        translate([0,i*3*(CoreSize[1] + WebWidth)/2,0])


HB-415M Stepper Driver Measurements

As mentioned there, the usual eBay vendor shipped HB-415M drivers instead of the advertised 2M415 drivers. Based on the Chinese datasheet and some poking around, I got a test setup working with a bench supply, a signal generator, and a NEMA 17 stepper motor with 2 Ω windings.

HB-415M Driver - test setup

HB-415M Driver – test setup

Yes, it’s that stepper motor and interrupter wheel.

First observation: the ENA input is active high. Pulling it low to turn on the optocoupler disables the drive output, which is exactly the opposite of what’s shown in the datasheet, which means that the driver will run quite happily with nothing connected to the ENA pin. The optoisolator current runs about 11 mA from a 5 V supply, close enough to the 10 mA typical spec, but the signal generator thinks it’s providing a TTL pulse output.

Second observation: the driver’s actual winding current doesn’t match the DIP switch setting.

Here’s the 1/8 microstep winding current for the 1.50 A peak setting, with a 0.5 A/div vertical calibration:

HB-415M 8-step 1.5A 20V

HB-415M 8-step 1.5A 20V

Sure looks like 1 A peak, doesn’t it?

The ratio seems close to 0.707 and remains consistent across all current settings, so I’d lay long money that the designer confused “peak” and “RMS” values, then figured the current sense resistor or chose the internal coefficients to produce the corresponding RMS current for the peak value.

The reduced current produces not very much torque at all; negotiations are in progress for a partial refund based on eBay’s “item not as described” process…



Capacity Test For USB Flash Drive Memory

Centon 4 GB USB Flash Drives

Centon 4 GB USB Flash Drives

It’s always a good idea to verify that a USB flash drive works and has its rated capacity, even when you buy them from a reputable vendor.

The easiest way to measure their capacity (quite different than measuring battery capacity):

  • Create a monster file of random data
  • Copy it to the drive
  • Verify that the copy matches the original
  • Delete the copy

That doesn’t verify that you can successfully create a bazillion little files, but it’s a good rough-and-ready check that you haven’t gotten, say, a 2 GB drive mis-labeled as 4 GB. It could happen…

Assuming you’ve deleted any shovelware (these were clean) and that the drives are now empty (as these were), find out how big they claim to be:

df /media/ed/CENTON\ USB/
Filesystem     1K-blocks  Used Available Use% Mounted on
/dev/sdb1        4107284     4   4107280   1% /media/ed/CENTON USB

Pour /dev/urandom into a file that will fill the available space (not the total space), which will take several minutes:

time dd bs=1K count=4107280 if=/dev/urandom of=/tmp/test.dat
4107280+0 records in
4107280+0 records out
4205854720 bytes (4.2 GB) copied, 450.883 s, 9.3 MB/s

real	7m31.162s
user	0m0.712s
sys	6m54.166s

Copy it to the drive, using rsync with a progress indicator:

time rsync --progress /tmp/test.dat /media/ed/CENTON\ USB/
  4205854720 100%    8.45MB/s    0:07:54 (xfer#1, to-check=0/1)

sent 4206368202 bytes  received 31 bytes  8772405.07 bytes/sec
total size is 4205854720  speedup is 1.00

real	7m59.035s
user	0m24.490s
sys	0m17.433s

Verify that the two files match:

time diff /tmp/test.dat /media/ed/CENTON\ USB
real	3m32.576s
user	0m0.588s
sys	0m6.268s

Then delete the file:

rm /media/ed/CENTON\ USB/test.dat

Repeat as needed for the other flash drives, using the same test.dat file. All these drives worked; one subsequently caught a disease at the library.

And, yes, one of them is noticeably darker; four of the others seem lighter and five darker gray. Most likely, the cases came from three different anodizing batches and, I suppose, if I were to pry them apart, the innards could be radically different. Ya never know!