Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.
Category: Software
General-purpose computers doing something specific
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:
halrun
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 conv-float-s32.0.in
4 s32 OUT 0 conv-float-s32.0.out
4 bit OUT FALSE conv-float-s32.0.out-of-range
... snippage ...
Parameters:
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 ...
Achtung!
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
CONV_FLOAT_S32.dev
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
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.
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
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:
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':
KERNEL=="2-10"
SUBSYSTEM=="usb"
DRIVER=="usb"
ATTR{configuration}==""
ATTR{bNumInterfaces}==" 2"
ATTR{bConfigurationValue}=="1"
ATTR{bmAttributes}=="80"
ATTR{bMaxPower}==" 90mA"
ATTR{urbnum}=="1354"
ATTR{idVendor}=="050d"
ATTR{idProduct}=="0815"
ATTR{bcdDevice}=="0210"
ATTR{bDeviceClass}=="00"
ATTR{bDeviceSubClass}=="00"
ATTR{bDeviceProtocol}=="00"
ATTR{bNumConfigurations}=="1"
ATTR{bMaxPacketSize0}=="8"
ATTR{speed}=="1.5"
ATTR{busnum}=="2"
ATTR{devnum}=="4"
ATTR{version}==" 1.10"
ATTR{maxchild}=="0"
ATTR{quirks}=="0x0"
ATTR{authorized}=="1"
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:
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.
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
The solid model looks about the way you’d expect:
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
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])
translate([x*Space,y*Space,Size/2])
%cube(Size,center=true);
}
//-------------------
// 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);
cylinder(r=Radius,h=Protrusion,$fn=8);
}
}
//----------------------
// Build it!
ShowPegGrid();
difference() {
translate([0,0,BlockHeight/2])
cube([BlockWidth,BlockLength,BlockHeight],center=true);
for (i = [-1,1])
translate([0,i*(CoreSize[1] + WebWidth)/2,0])
Core(CoreSize,CornerRadius);
for (i = [-1,1])
translate([0,i*3*(CoreSize[1] + WebWidth)/2,0])
Core(CoreSize,CornerRadius);
}
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/
test.dat
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
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!
Mary gave a gardening presentation at the local library, popping a 4 GB USB memory stick with the presentation into a library computer connected to the display projector. Back home, she deleted the presentations and was about to add more files, when she noticed something interesting:
drwx------ 4 ed ed 4096 Dec 31 1969 ./
drwxr-x---+ 3 root root 4096 Jan 31 19:21 ../
-r--r--r-- 1 ed ed 59288 Mar 21 2009 autorun.inf
drwx------ 3 ed ed 4096 Jan 30 19:31 RECYCLER/
drwx------ 4 ed ed 4096 Jan 31 19:10 .Trash-1001/
Ubuntu 12.10 automagically mounts FAT filesystems with the current user as owner and group. The .Trash-1001 directory is the Linux trash heap, but where did all that other stuff come from? The autorun.inf definitely looks Window-y, doesn’t it?
Perforce, the library runs Windows, but that shouldn’t add files to a USB memory stick that just was plugged in and used for a read-only presentation, should it?
Huh. You know where this is going…
Let’s hand autorun.inf to VirusTotal for a second opinion. The first three results from their long list confirm my suspicion:
Antivirus
Result
Update
Agnitum
INF.Conficker.F
20130131
AhnLab-V3
Win32/Conficker.worm
20130131
AntiVir
Worm/Kido.IH.40
20130131
The executable file containing the actual payload is, of course, buried in a subdirectory that might look more innocent on a Windows box: /RECYCLER/S-5-3-42-2819952290-8240758988-879315005-3665/
It sports a randomized name to evade a really stupid malware detector: jwgkvsq.vmx
Here’s what VirusTotal reports from some heavy hitters in the AV field:
Kaspersky
Net-Worm.Win32.Kido.ih
20130131
Kingsoft
Worm.Kido.ih.(kcloud)
20130131
Malwarebytes
Worm.Conficker
20130131
McAfee
W32/Conficker.worm
20130201
McAfee-GW-Edition
W32/Conficker.worm
20130131
Microsoft
Worm:Win32/Conficker.B
20130131
The Wikipedia article gives the details. I suppose that PC got it from somebody else’s USB stick, but the library really should be running some defensive software; Conficker dates back to 2008, so it’s not new news these days.
That kind of Windows Genuine Advantage makes up for all the hassles of running Linux, right there. Mary reported the problem to the library; we’ll never know the rest of the story.
This may not be a LibreOffice problem, but that’s where it shows up: the font selection dialog won’t display fonts with nonstandard Style names. There is, of course, no documentation anywhere (that I can find, anyway) on what Style names are permitted, so you discover this only when a font style that’s properly installed and accessible by other programs (like, say, Inkscape or Scribus) doesn’t render properly and doesn’t appear in the list.
In Xubuntu 12.10, LibreOffice 3.6.2.2 can’t handle the American Typewriter font style called Medium, which is what I’ve been using for the return address field on my (very few, these days) mail envelopes. Over the years, various versions of OpenOffice and LibreOffice have alternately accepted and rejected the Medium style, so this isn’t exactly a regression. It is, however, Yet Another Annoyance.
The solution, hinted at in that thread, involves using FontForge to rename the offending Style to, say, Regular, then saving the font. It’s actually the Weight property, hidden in Element → Font Info → PS Names tab. In this case, I changed the word “Medium” in the Fontname, Name for Humans, and Weight fields to “Regular”, which also updates the values in the TTF Names tab.
I save the modified font files in ~/.local/share/fonts using TrueType format, just to be sure I don’t confuse them with the original Postscript version in /usr/share/fonts/custom, delete the original, and then run fc-cache -v -f to update the caches. This surely isn’t the cleanest way to make it happen and almost certainly isn’t allowed by the Adobe EULA I agreed to, back when I actually bought the fonts, but so it goes.
It turns out that if you put convenient symlinks in your directories, then use them to build a LibreOffice document, LO will cheerfully put those paths into the graphic file links inside its XML files. That will produce horrible breakage on a new system without those links. We’ve come to the conclusion that the only way to keep LO happy is to create a Pictures directory in whatever directory holds the document file, then put all of the document’s image files into that directory, and make sure LO stores relative paths. Of course, this leaves us with the prospect of updating a whole bunch of existing (and, alas, horribly broken) documents by hand, which is unappealing. My previous solution worked for a single file, but now it’s time for some scripting…
This would probably be easier in Python, but Bash works fine after you get the quoting straightened out. This script builds several other scripts that actually do the heavy lifting, because that way you can inspect the scripts before running them to verify that you’re not about to make a bad situation much, much worse. I recommend copying the presentations into another directory, running this script, check the output scripts, run them by hand, and then copy the fixed files and the Pictures directory back where they belong.
You must tweak the actual paths to the pictures to match your situation; for these documents, one simple change sufficed for all the image files. Those paths are not variables, because I can barely keep the quoting straight without adding another layer of indirection. Make sure all the paths match up, verify the scripts before you run them, and don’t trust anything you see.
CAUTION: It’s highly likely that the multiple levels of character escaping required to make these listings appear correctly on the screen will produce incorrect results when copied-and-pasted. You can download the script file as FixGraphics.sh.odt, which is a bare-ASCII TXT file (which you must rename to eliminate the ODT extension, then make executable as a shell script), to see how it compares.
The main FixGraphics.sh script, with some key lines highlighted:
#!/bin/bash
echo "Extract list of images from all ODP files"
rm images.txt
for f in *odp
do
unzip -p "$f" content.xml | sed 's/></>\n</g' | grep Cameras | cut -d \" -f 2 | sort -u >> images.txt
done
echo "Make source file name list"
# strip off leading relative pathing, set actual absolute path, un-quote blanks and special characters, add quotes
sed 's/..\/..\/..\/../\/mnt/' images.txt | sed 's/%20/ /g' | sed 's/&/\&/g' | sed 's/^.*/\"&\"/' > source.lst
echo "Make target file name list"
# set relative to current directory
sed 's/\/mnt\/bulkdata\/Cameras\/MCWN/\.\/Pictures/' source.lst > target.lst
echo "Make target directory list"
# must add trailing quote stripped by dirname
rm dirs.lst
cat target.lst | while read tline ; do
tdir=`dirname "$tline"`
echo ${tdir}\"
done > dirs.lst
echo "Create target directory structure script"
rm mkdirs.sh
sort -u dirs.lst | while read dline ; do
echo mkdir --parents ${dline}
done > mkdirs.sh
chmod u+x mkdirs.sh
echo "Create image file copy script"
rm cpjpgs.sh
cat dirs.lst | while read dline ; do
echo cp -n -t ${dline}
done > cptemp.txt
paste cptemp.txt source.lst > cpjpgs.sh
chmod u+x cpjpgs.sh
echo "Create ODP fixup script"
echo "for f in *odp ; do" > fixodp.sh
echo "unzip -p \"\$f\" content.xml > raw.xml" >> fixodp.sh
echo "sed 's/..\/..\/..\/..\/bulkdata\/Cameras\/MCWN/\.\.\/Pictures/g' raw.xml > content.xml" >> fixodp.sh
echo "zip \"\$f\" content.xml" >> fixodp.sh
echo "done" >> fixodp.sh
echo "rm raw.xml content.xml" >> fixodp.sh
chmod u+x fixodp.sh
Run mkdirs.sh, cpjpgs.sh, and fixodp.sh: then it Just Works.
Some of the tricky parts:
The content.xml file may be stored in unformatted mode, with everything mushed together into one huge line. To make it readable and parse-able, insert a newline between each pair of adjoining angle brackets:
sed 's/></>\n</g'
This burst of line noise un-escapes the file name from the way LO stores it internally. Note that the middle sed command really does have the literal escape sequence ampersand-amp-semicolon in it and the ampersand in the last one is the sed-ism for “the whole matching string”:
sed 's/%20/ /g' | sed 's/&/\&/g' | sed 's/^.*/\"&\"/'
The difference between these two sed strings indicates the actual relative path to the Pictures subdirectory in the filesystem and the faked relative path from the LO pseudo-subdirectory where the document stores its internal state. The string of periods in the second command shows what LO stored for the original files in our documents; your mileage will certainly differ:
sed 's/\/mnt\/bulkdata\/Cameras\/MCWN/\.\/Pictures/' source.lst > target.lst
sed 's/..\/..\/..\/..\/bulkdata\/Cameras\/MCWN/\.\.\/Pictures/' raw.xml > content.xml
I don’t know how they could make the file linkages work better, but it’d be really nice if there were a less horrible way to fix the breakage.