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.

Author: Ed

  • Ending an Antenna Rattle

    This Comet HT-224 antenna fits on my Kenwood TH-F6A radio, but the connector fitting is just slightly too long / short / something: it moves just a little bit, even with the nut firmly tightened.

    This isn’t a real fix, but it pretty much eliminates the rattle: a rubber O-ring between the nut and the antenna…

    Antenna connector and O-ring
    Antenna connector and O-ring

    The ring lasts for a few years, then cracks and falls off. My O-ring stash has what could possibly be a lifetime supply.

    Antenna with O-ring in place
    Antenna with O-ring in place

    There’s a wrap of tape around the label, just for neatness.

  • Cusinart Santoku Knife

    The killer discount coupon that brought a new cutting board into the house also produced a new santoku knife from Cusinart (not that that makes much difference), seen here with my favorite knife:

    Santoku knives
    Santoku knives

    Pursuant to that discussion, I’ve been looking for a Teflon-coated Santoku blade, but nobody in the area seems to have one.

    This one claims that the row of dimples on each side make cheese slices slide right off, but that’s not what happens. Maybe it’s not quite so sticky as my knives, but I’d call it a tie.

    Likes:

    • It’s scary sharp, but so are my knives. In fact, I think mine are even more keen, because the blade is thinner.
    • The metal handle feels better than I expected. Dishwasher safe, not that we run ’em through there very often.
    • Thin blade with good keen edge.
    • Surprisingly light, despite the handle.

    Dislikes:

    • Weird balance point, maybe 20 mm back from the heel. It wants to fall out of the dish drainer.
    • The heel is an obtuse angle; I like an acute heel to punch into things like orange peels and milk jugs (to convert them into worm compost bins). Nothing I can’t fix with a grinding wheel, but I’m letting it slide for now.
    • The blade is curved, which means I can’t just whack my way along the veggies.
    • The handle is marginally too close to the cutting board for my fingers: I tend to knock my knuckles on the board when rocking through the cut.

    On the other paw, maybe you can see how the blade near the heel on the old knife is getting ever so slightly hollowed out from my use of the steel. The new flat cutting board makes that embarrassingly obvious; it’s getting to be time for a trip to the slow stone.

    All in all, pretty good. I use it, but not as much as the Good Old Knives.

  • Floor Lamp Base: Poor Planning

    Floor lamp base - cord exit
    Floor lamp base – cord exit

    We have an old floor lamp that’s always been a bit tippy and I finally got around to wondering what’s going on.

    The cord exits through the center of the base, where it passes through a plastic nut that keeps it off the raw metal edge of the central rod holding the lamp together.

    The ruler has 1/16 inch divisions, so the cord requires a bit over half an inch of clearance.

    Floor lamp foot with bumper
    Floor lamp foot with bumper

    Here’s what one of the five molded-in feet look like, with a white rubber bumper that I just added to improve the ground clearance…

    Notice that the foot is barely 1/4 inch tall, so the lamp has always been resting on the cord and two other randomly chosen feet. No wonder the thing was tippy.

    The new rubber feet make it a lot less tippy, but there’s not a lot of clearance under there. When one of those things falls off, I’ll think of something better.

    The lamp was nominally UL approved, of course…

  • Foxconn R20-D2 Case vs. Optical Drive: Button Padding

    Extended optical drive button
    Extended optical drive button

    So I stuck a CD-RW drive into the Foxconn Atom box and discovered that the pushbutton on the front panel doesn’t move quite far enough to actually hit the corresponding button on the drive.

    Popped another drive off the heap and tried it out, just for grins, with the same result. Evidently the cute little ribbed back on the silvered panel button (near the bottom of the picture) isn’t quite long enough.

    Solution: a bit of rubberized high-traction tape stuck to the drive button (near the top of the picture).

    This is a black-on-black situation, so I pushed the contrast enough that you can actually see it.

  • Kitchen Scale: Expedient Button Replacement

    According to the date code on the back, we’ve had this kitchen scale for nigh onto a decade, so I suppose I shouldn’t be surprised that the molded plastic sheet over the buttons has cracked. Mary & I were in the midst of a tag-team baking session when the middle (Power) button cover went crunch and gave out.

    Kitchen scale - cracked buttons
    Kitchen scale – cracked buttons

    Although it’s not obvious, all three buttons are about halfway disconnected from the surface. The rightmost button has a crack nearly all the way around the perimeter.

    Button covers removed
    Button covers removed

    A quick encounter with a razor knife removed the entire block of cracked plastic, revealing the surprisingly off-centered button recesses. No wonder the plastic cracked!

    The junkpile disgorged a thin sheet of laminated cardboard that peeled neatly down the middle. Cut it to fit the rectangle, apply a strip of Kapton tape, and it was back in action in about ten minutes… the bread dough never missed me.

    Kitchen scale - expedient button cover
    Kitchen scale – expedient button cover

    This was a quick-and-dirty fix to get the thing back in operation; when the Kapton tape fails, I’ll do something more permanent and less hideous. I promise…

     

  • Logitech Gamepad as EMC2 Pendant: Eagle Schematics for the Joggy Thing!

    Another pass at my Logitech Dual-Action Gamepad used as an EMC2 control pendant, but this time using an Eagle ULP (User Language Program) that converts a schematic into EMC2 HAL code.

    I tweaked Martin Schöneck’s original ULP a bit, added (some of the) new devices found in EMC2.4, added the corresponding Eagle symbols & devices to the library, then drew up a schematic based on my hand-hewn HAL code with some improvements. Ran the script and shazam the HAL code worked just fine (after a bit of debugging the infrastructure, of course).

    The new ULP and library are not quite ready for prime time, but this is a stick in the ground to mark some progress. You can certainly use the HAL code directly, without fiddling around in the schematic: stuff the whole program (at the end of the post) in your existing (but likely empty) custom_postgui.hal file.

    The schematic is, of course, much fluffier than the corresponding code, particularly because I chopped it into five easily printed pages. Here’s the Big Picture overview of what’s going on in each page; click the pix for bigger images.

    The servo thread interface device in the lower left provides the halui timing. The big block in the upper left has all the Logitech gamepad buttons, including the four big ones used for Z and A axis jogging. I changed the two left-rear buttons to activate the Abort signal rather than Estop, not that I use them all that much anyway.

    The two joystick knobs have pushbuttons, which I combine and use to toggle a flipflop that will select the jogging speed: fast or crawl.

    I also cut the jog deadband from 0.2 to 0.1, which makes the joysticks much more responsive.

    Logitech Gamepad HAL Schematic - Page 1
    Logitech Gamepad HAL Schematic – Page 1

    The big block on the left has all the gamepad’s analog axes. The HAT0X and HAT0Y axes correspond to the top-hat pushbuttons; they’re not really analog at all, although they take on -1.0 / 0.0 / + 1.0 floating point values. The window comparators determine which joystick axes are active, which comes in handy later on.

    Logitech Gamepad HAL Schematic - Page 2
    Logitech Gamepad HAL Schematic – Page 2

    The HAL jogging control has a single input that sets the default speed, but the proper value is vastly different depending on whether you’re jogging with linear or angular motion. This page picks out which ini file MAX_VELOCITY value to use, converts from units/sec to units/min, then does Cool Thing #1: scales the speed so that the fast/crawl speeds work out nicely.

    I use the buttons to jog rapidly from here to there, then creep up on the alignment point using the joysticks. Pressing the joysticks downward switches from Fast to Crawl speeds, which provides sort of a gearshift that’s useful for coarse / fine adjustments.

    The buttons run at two speeds:

    • Fast: the maximum speed for the axis
    • Crawl: 10% of that value

    The joysticks have a lower top speed:

    • Fast: half the maximum speed of the axis
    • Crawl: 10% of that value

    All those values go into the mux4 block and thence to the HAL jog speed control.

    Logitech Gamepad HAL Schematic - Page 3
    Logitech Gamepad HAL Schematic – Page 3

    This page does Cool Thing #2: prioritize the joystick axes and lock out the one that started moving last. The general idea is that it’s painfully easy to move the joysticks diagonally, which is great for gaming and terrible for milling machine control. A pair of flipflops for each joystick remember which axis started moving first.

    If you want to move diagonally, just press the buttons; they’re not locked out, so you can do what you want.

    Logitech Gamepad HAL Schematic - Page 4
    Logitech Gamepad HAL Schematic – Page 4

    The motion comes together on the last page, where scale blocks flip the direction of the Y and Z joystick axes so positive motion is upward. The multiplexers allow only the active axis of each joystick to reach the HAL analog jog inputs; you can vary the speed of that axis up to the maximum as you’d expect. The buttons drive the digital inputs that jog at that maximum speed; the Y and Z button directions get sorted out appropriately.

    Logitech Gamepad HAL Schematic - Page 5
    Logitech Gamepad HAL Schematic – Page 5

    Those five pages boil down into the following code, which I manually insert into my custom_postgui.hal file, along with the tool length probe pin definition.

    # HAL config file automatically generated by Eagle-CAD hal-write.ulp
    # (C) Martin Schoeneck.de 2008
    # Mods Ed Nisley 2010
    
    # Path: [/mnt/bulkdata/Project Files/eagle/projects/EMC2 HAL Configuration/]
    # ProjectName: [Logitech Gamepad]
    # File name: [/mnt/bulkdata/Project Files/eagle/projects/EMC2 HAL Configuration/Logitech Gamepad.hal]
    
    ####################################################
    # Load realtime and userspace modules
    loadrt constant	count=14
    loadrt and2	count=9
    loadrt flipflop	count=4
    loadrt mux2	count=5
    loadrt mux4	count=1
    loadrt not	count=8
    loadrt or2	count=9
    loadrt scale	count=7
    loadrt toggle	count=1
    loadrt wcomp	count=6
    
    ####################################################
    # Hook functions into threads
    addf toggle.0	servo-thread
    addf wcomp.1	servo-thread
    addf wcomp.2	servo-thread
    addf wcomp.3	servo-thread
    addf and2.0	servo-thread
    addf and2.4	servo-thread
    addf and2.3	servo-thread
    addf and2.2	servo-thread
    addf and2.1	servo-thread
    addf constant.6	servo-thread
    addf constant.5	servo-thread
    addf constant.4	servo-thread
    addf constant.3	servo-thread
    addf constant.2	servo-thread
    addf constant.1	servo-thread
    addf constant.0	servo-thread
    addf constant.7	servo-thread
    addf constant.8	servo-thread
    addf scale.1	servo-thread
    addf scale.2	servo-thread
    addf scale.3	servo-thread
    addf mux4.0	servo-thread
    addf mux2.0	servo-thread
    addf scale.4	servo-thread
    addf scale.0	servo-thread
    addf wcomp.5	servo-thread
    addf wcomp.4	servo-thread
    addf wcomp.0	servo-thread
    addf flipflop.1	servo-thread
    addf flipflop.0	servo-thread
    addf and2.5	servo-thread
    addf and2.6	servo-thread
    addf and2.7	servo-thread
    addf and2.8	servo-thread
    addf flipflop.2	servo-thread
    addf flipflop.3	servo-thread
    addf or2.4	servo-thread
    addf or2.8	servo-thread
    addf or2.7	servo-thread
    addf or2.6	servo-thread
    addf or2.5	servo-thread
    addf or2.3	servo-thread
    addf or2.2	servo-thread
    addf or2.1	servo-thread
    addf or2.0	servo-thread
    addf not.1	servo-thread
    addf not.2	servo-thread
    addf not.3	servo-thread
    addf not.4	servo-thread
    addf not.5	servo-thread
    addf not.6	servo-thread
    addf not.7	servo-thread
    addf not.0	servo-thread
    addf constant.9	servo-thread
    addf mux2.1	servo-thread
    addf mux2.2	servo-thread
    addf mux2.3	servo-thread
    addf mux2.4	servo-thread
    addf constant.10	servo-thread
    addf constant.11	servo-thread
    addf scale.5	servo-thread
    addf scale.6	servo-thread
    addf constant.12	servo-thread
    addf constant.13	servo-thread
    
    ####################################################
    # Set parameters
    
    ####################################################
    # Set constants
    setp constant.0.value	+0.02
    setp constant.1.value	-0.02
    setp constant.2.value	60
    setp constant.3.value	1.00
    setp constant.4.value	0.10
    setp constant.5.value	0.50
    setp constant.6.value	0.10
    setp constant.7.value	+0.5
    setp constant.8.value	-0.5
    setp constant.9.value	0.0
    setp constant.10.value	[TRAJ]MAX_LINEAR_VELOCITY
    setp constant.11.value	[TRAJ]MAX_ANGULAR_VELOCITY
    setp constant.12.value	-1.0
    setp constant.13.value	0.1
    
    ####################################################
    # Connect Modules with nets
    net a-button-minus input.0.btn-trigger or2.2.in0 halui.jog.3.minus
    net a-button-plus input.0.btn-thumb2 or2.2.in1 halui.jog.3.plus
    net a-buttons-active or2.2.out or2.3.in0 or2.4.in1
    net a-disable not.7.out and2.5.in1
    net a-enable flipflop.3.out not.7.in mux2.4.sel
    net a-jog wcomp.2.in input.0.abs-z-position mux2.4.in1
    net a-knob-active not.2.out or2.4.in0 and2.7.in1
    net a-knob-inactive wcomp.2.out not.2.in and2.6.in1
    net a-select and2.8.in0 and2.7.out
    net a-set flipflop.3.set and2.8.out
    net angular_motion or2.4.out mux2.0.sel
    net any-buttons-active mux4.0.sel0 or2.8.out
    net az-buttons-active or2.3.out or2.8.in1
    net az-reset flipflop.2.reset and2.6.out flipflop.3.reset
    net button-crawl scale.4.out mux4.0.in3
    net button-fast scale.2.out mux4.0.in1 scale.4.in
    net jog-crawl toggle.0.out mux4.0.sel1
    net jog-speed halui.jog-speed mux4.0.out
    net knob-crawl mux4.0.in2 scale.3.out
    net knob-fast mux4.0.in0 scale.1.out scale.3.in
    net n_1 constant.10.out mux2.0.in0
    net n_2 and2.0.in0 input.0.btn-top2
    net n_3 and2.0.in1 input.0.btn-base
    net n_4 and2.0.out halui.abort
    net n_5 halui.mode.manual input.0.btn-base3
    net n_6 wcomp.0.max wcomp.1.max wcomp.2.max wcomp.3.max constant.0.out
    net n_7 halui.program.resume input.0.btn-base4
    net n_8 wcomp.0.min wcomp.1.min wcomp.2.min wcomp.3.min constant.1.out
    net n_9 mux2.0.in1 constant.11.out
    net n_10 constant.12.out scale.5.gain scale.6.gain
    net n_11 input.0.btn-base5 or2.0.in0
    net n_12 input.0.btn-base6 or2.0.in1
    net n_13 constant.9.out mux2.1.in0 mux2.2.in0 mux2.3.in0 mux2.4.in0
    net n_14 mux2.1.out halui.jog.0.analog
    net n_15 toggle.0.in or2.0.out
    net n_16 constant.2.out scale.0.gain
    net n_17 constant.5.out scale.1.gain
    net n_18 constant.3.out scale.2.gain
    net n_19 constant.4.out scale.3.gain
    net n_20 scale.4.gain constant.6.out
    net n_21 halui.jog.1.analog mux2.2.out
    net n_22 mux2.2.in1 scale.5.out
    net n_23 scale.6.out mux2.3.in1
    net n_24 constant.13.out halui.jog-deadband
    net n_25 wcomp.4.max constant.7.out wcomp.5.max
    net n_26 constant.8.out wcomp.4.min wcomp.5.min
    net n_27 mux2.3.out halui.jog.2.analog
    net n_28 halui.jog.3.analog mux2.4.out
    net vel-per-minute scale.0.out scale.1.in scale.2.in
    net vel-per-second mux2.0.out scale.0.in
    net x-buttons-active or2.7.in0 or2.5.out
    net x-disable not.4.out and2.4.in1
    net x-enable not.4.in flipflop.0.out mux2.1.sel
    net x-hat-jog wcomp.4.in input.0.abs-hat0x-position
    net x-hat-minus wcomp.4.under or2.5.in1 halui.jog.0.minus
    net x-hat-plus or2.5.in0 wcomp.4.over halui.jog.0.plus
    net x-jog wcomp.0.in input.0.abs-x-position mux2.1.in1
    net x-knob-active not.0.out and2.1.in0
    net x-knob-inactive wcomp.0.out not.0.in and2.2.in0 and2.3.in0
    net x-set and2.1.out flipflop.0.set
    net xy-buttons-active or2.7.out or2.8.in0
    net xy-reset flipflop.0.reset and2.2.out flipflop.1.reset
    net y-buttons-active or2.6.out or2.7.in1
    net y-disable not.5.out and2.1.in1
    net y-enable flipflop.1.out not.5.in mux2.2.sel
    net y-hat-jog input.0.abs-hat0y-position wcomp.5.in
    net y-hat-minus wcomp.5.under or2.6.in1 halui.jog.1.plus
    net y-hat-plus or2.6.in0 wcomp.5.over halui.jog.1.minus
    net y-jog wcomp.1.in input.0.abs-y-position scale.5.in
    net y-knob-active not.1.out and2.3.in1
    net y-knob-inactive not.1.in wcomp.1.out and2.2.in1
    net y-select and2.4.in0 and2.3.out
    net y-set flipflop.1.set and2.4.out
    net z-button-minus input.0.btn-thumb or2.1.in0 halui.jog.2.minus
    net z-button-plus input.0.btn-top or2.1.in1 halui.jog.2.plus
    net z-buttons-active or2.1.out or2.3.in1
    net z-disable not.6.out and2.8.in1
    net z-enable not.6.in flipflop.2.out mux2.3.sel
    net z-jog wcomp.3.in input.0.abs-rz-position scale.6.in
    net z-knob-active not.3.out and2.5.in0
    net z-knob-inactive not.3.in wcomp.3.out and2.7.in0 and2.6.in0
    net z-set and2.5.out flipflop.2.set
    

    The ULP script that eats the schematic and poots out the HAL code:

    /******************************************************************************
     * HAL-Configurator
     *
     * Author: Martin Schoeneck 2008
     * Additional gates & tweaks: Ed Nisley KE4ZNU 2010
     *****************************************************************************/
    #usage "<h1>HAL-Configurator</h1>Start from a Schematic where symbols from hal-config.lbr are used!";
    
    string output_path =    "./";
    string dev_loadrt =     "LOADRT";
    string dev_loadusr =    "LOADUSR";
    string dev_thread =     "THREAD";
    string dev_parameter =  "PARAMETER";
    
    string dev_names[] = {
    "CONSTANT",								// must be first entry to make set_constants() work
    "ABS",				// 2.4
    "AND2",
    "BLEND",			// 2.4
    "CHARGE-PUMP",		// 2.4
    "COMP",
    "CONV_S32_FLOAT",	// 2.4
    "DDT",				// 2.4
    "DEADZONE",			// 2.4
    "DEBOUNCE",			// 2.4
    "EDGE",
    "ENCODER",			// 2.4
    "ENCODER-RATIO",	// 2.4
    "ESTOP-LATCH",
    "FLIPFLOP",
    "FREQGEN",			// 2.4
    "LOWPASS",
    "MULT2",			// 2.4
    "MUX2",
    "MUX4",				// 2.4
    "MUX8",				// 2.4
    "NEAR",				// 2.4
    "NOT",
    "ONESHOT",
    "OR2",
    "SAMPLER",			// 2.4
    "SCALE",			// 2.4
    "SELECT8",			// 2.4
    "SUM2",
    "TIMEDELAY",		// 2.4
    "TOGGLE",			// 2.4
    "WCOMP",			// 2.4
    "XOR2",				// 2.4
    ""					// end flag
    };
    
    string init = "# HAL config file automatically generated by Eagle-CAD hal-write.ulp\n# (C) Martin Schoeneck.de 2008\n# Mods Ed Nisley 2010\n";
    
    /*******************************************************************************
     * Global Stuff
     ******************************************************************************/
    
    string FileName;
    string ProjectPath;
    string ProjectName;
    
    void Info(string Message) {
    	dlgMessageBox(";<b>Info</b><p>\n" + Message);
    }
    
    void Warn(string Message) {
    	dlgMessageBox("!<b>Warning</b><p>\n" + Message + "<p>see usage");
    }
    
    void Error(string Message) {
    	dlgMessageBox(":<hr><b>Error</b><p>\n" + Message + "<p>see usage");
    	exit(1);
    }
    
    string replace(string str, char a, char b) {
    	// in string str replace a with b
    	int pos = -1;
    	do {
    		// find that character
    		pos = strchr(str, a);
    		// replace if found
    		if(pos >= 0) {
    			str[pos] = b;
    		}
    	} while(pos >= 0);
    
    	return str;
    }
    
    // the part name contains an index and is written in capital letters
    string get_module_name(UL_PART P) {
    	// check module name, syntax: INDEX:NAME
    	string mod_name = strlwr(P.name);
    	// split string at the : if exists
    	string a[];
    	int c = strsplit(a, mod_name, ':');
    	mod_name = a[c-1];
    	// if name starts with '[' we need uppercase letters
    	if(mod_name[0] == '[') {
    		mod_name = strupr(mod_name);
    	}
    
    	return mod_name;
    }
    
    string comment(string mess) {
    	string str = "\n\n####################################################\n";
    	if(mess != "") {
    		str += "# " + mess + "\n";
    	}
    
    	return str;
    }
    
    // if this is a device for loading a module, load it (usr/rt)
    string load_module(UL_PART P) {
    	string str = "";
    
    	// it's a module if the device's name starts with LOADRT/LOADUSR
    	if((strstr(P.device.name, dev_loadrt) == 0) ||
    	   (strstr(P.device.name, dev_loadusr) == 0)) {
    
    		// now add the string to our script
    		str += P.value + "\n";
    	}
    
    	return str;
    }
    
    // count used digital gates (and, or, etc) and load module if neccessary
    string load_blocks() {
    	string str = "";
    
    	int index;
    
    	int dev_counters[];
    	string dname[];
    
    	// count the gates that are used
    	schematic(S) { S.parts(P) {
    		strsplit(dname,P.device.name,'.');		// extract first part of name
    		if ("" != lookup(dev_names,dname[0],0)) {
    			for (index = 0;  (dname[0] != dev_names[index]) ; index++) {
    				continue;
    			}
    			dev_counters[index]++;
    		}
    	} }
    
    // force lowercase module names...
    
    	for (index = 0; ("" != dev_names[index]) ; index++) {
    		if (dev_counters[index]) {
    			sprintf(str,"%sloadrt %s\tcount=%d\n",str,strlwr(dev_names[index]),dev_counters[index]);
    		}
    	}
    
    	return str;
    }
    
    string hook_function(UL_NET N) {
    	string str = "";
    
    	// is this net connected to a thread (work as functions here)?
    	int    noclkpins       = 0;
    	string thread_name     = "";  // this net should be connected to a thread
    	string thread_position = "";
    	N.pinrefs(PR) {
    		// this net is connected to a clk-pin
    		if(PR.pin.function == PIN_FUNCTION_FLAG_CLK) {
    			// check the part: is it a thread-device?
    			if(strstr(PR.part.device.name, dev_thread) == 0) {
    				// we need the name of the thread
    				thread_name = strlwr(PR.part.name);
    				// and we need the position (position _ is ignored)
    				thread_position = strlwr(PR.pin.name);
    				thread_position = replace(thread_position, '_', ' ');
    			}
    		} else {
    			// no clk-pin, this is no function-net
    			noclkpins++;
    			break;
    		}
    	}
    
    	// found a thread?
    	if(noclkpins == 0 && thread_name != "") {
    		// all the other pins are interesting now
    		N.pinrefs(PR) {
    			// this pin does not belong to the thread
    			if(strstr(PR.part.device.name, dev_thread) != 0) {
    				// name of the pin is name of the function
    				//string function_name = strlwr(PR.pin.name);
    				string function_name = strlwr(PR.instance.gate.name);
    				// if functionname starts with a '.', it will be appended to the modulename
    				if(function_name[0] == '.') {
    					// if the name is only a point, it will be ignored
    					if(strlen(function_name) == 1) {
    						function_name = "";
    					}
    					function_name = get_module_name(PR.part) + function_name;
    				}
    				str += "addf " + function_name + "\t" + thread_name + "\t" + thread_position + "\n";
    			}
    		}
    	}
    
    	return str;
    }
    
    string set_parameter(UL_NET N) {
    	string str = "";
    
    	// is this net connected to a parameter-device?
    	int    nodotpins       = 0;
    	string parameter_value = "";
    	N.pinrefs(PR) {
    		// this net is connected to a dot-pin
    		if(PR.pin.function == PIN_FUNCTION_FLAG_DOT) {
    			// check the part: is it a parameter-device?
    //			str += "** dev name [" + PR.part.device.name + "] [" + dev_parameter + "]\n";
    			if(strstr(PR.part.device.name, dev_parameter) == 0) {
    				// we need the value of that parameter
    				parameter_value = PR.part.value;
    //				str += "**  value [" + PR.part.value +"]\n";
    			}
    		} else {
    			// no clk-pin, this is no function-net
    			nodotpins++;
    			break;
    		}
    	}
    
    	// found a parameter?
    	if(nodotpins == 0 && parameter_value != "") {
    		// all the other pins are interesting now
    		N.pinrefs(PR) {
    //			str += "** dev name [" + PR.part.device.name + "] [" + dev_parameter + "]\n";
    			// this pin does not belong to the parameter-device
    			if(strstr(PR.part.device.name, dev_parameter) != 0) {
    				// name of the pin is name of the function
    				//string parameter_name = strlwr(PR.pin.name);
    				string parameter_name = strlwr(PR.instance.gate.name);
    				// if functionname starts with a '.', it will be appended to the modulename
    //				str += "** param (gate) name [" + parameter_name + "]\n";
    				if(parameter_name[0] == '.') {
    					// if the name is only a point, it will be ignored
    					if(strlen(parameter_name) == 1) {
    						parameter_name = "";
    					}
    					parameter_name = get_module_name(PR.part) + parameter_name;
    //					str += "** param (part) name [" + parameter_name + "]\n";
    				}
    				str += "setp " + parameter_name + "\t" + parameter_value + "\n";
    			}
    		}
    	}
    
    	return str;
    }
    
    // if this is a 'constant'-device, set its value
    // NOTE: this is hardcoded to use the first entry in the dev_names[] array!
    string set_constants(UL_PART P) {
    	string str = "";
    
    	// 'constant'-device?
    	if(strstr(P.device.name, dev_names[0]) == 0) {
    		str += "setp " + get_module_name(P) + ".value\t" + P.value + "\n";
    	}
    
    	return str;
    }
    
    string connect_net(UL_NET N) {
    	string str = "";
    
    	// find all neccessary net-members
    	string pins = "";
    	N.pinrefs(P) {
    		// only non-functional pins are connected
    		if(P.pin.function == PIN_FUNCTION_FLAG_NONE) {
    			string pin_name =  strlwr(P.pin.name);
    			string part_name = strlwr(P.part.name);
    			pin_name =  replace(pin_name,  '$', '_');
    			part_name = replace(part_name, '$', '_');
    			pins += part_name + "." + pin_name + " ";
    		}
    	}
    
    	if(pins != "") {
    		string net_name = strlwr(N.name);
    		net_name = replace(net_name, '$', '_');
    		str += "net " + net_name + " " + pins + "\n";
    	}
    
    	return str;
    }
    
    /*******************************************************************************
     * Main program.
     ******************************************************************************/
    // is the schematic editor running?
    if (!schematic) {
    	Error("No Schematic!<br>This program will only work in the schematic editor.");
    }
    
    schematic(S) {
    	ProjectPath = filedir(S.name);
    	ProjectName = filesetext(filename(S.name), "");
    }
    
    // build configuration
    string cs = init + "\n\n";
    
    FileName = ProjectPath + ProjectName + ".hal";
    
    cs += "# Path: [" + ProjectPath + "]\n";
    cs += "# ProjectName: [" + ProjectName + "]\n";
    //cs += "# File name: [" + FileName + "]\n\n";
    
    // ask for a filename: where should we write the configuration?
    
    FileName = dlgFileSave("Save Configuration", FileName, "*.hal");
    
    if(!FileName) {
    	exit(0);
    }
    
    cs += "# File name: [" + FileName + "]\n\n";
    
    schematic(S) {
    	// load modules
    	cs += comment("Load realtime and userspace modules");
    	S.parts(P) {
    		cs += load_module(P);
    	}
    
    	// load blocks
    	cs += load_blocks();
    
    	// add functions
    	cs += comment("Hook functions into threads");
    	S.nets(N) {
    		cs += hook_function(N);
    	}
    
    	// set parameters
    	cs += comment("Set parameters");
    	S.nets(N) {
    		cs += set_parameter(N);
    	}
    
    	// set constant values
    	cs += comment("Set constants");
    	S.parts(P) {
    		cs += set_constants(P);
    	}
    
    	// build nets and connect them
    	cs += comment("Connect Modules with nets");
    	S.nets(N) {
    		cs += connect_net(N);
    	}
    }
    
    // open/overwrite the target file to save the configuration
    output(FileName, "wt") {
    	printf(cs);
    }
    

    Most of that script is Martin’s work; I just cleaned it up. You can download it by hovering over the code to make the little toolbar pop up near the upper-right corner of the text, then:

    • click a little button to copy it to the clipboard or
    • click another little button to view the source, then save that file

    You’ll also need the Eagle library that goes along with the script, but WordPress doesn’t like .lbr files. Here’s the hal-config-2.4.lbr file with a totally bogus odt extension. Download it, rename it to remove the .odt extension, and it’s all good.

    There is basically no documentation for any of this. I figured out what to do by looking at the source and Martin’s sample schematic, but now you have two sample schematics: the situation is definitely improving!

  • Another Bike Flat: Michelin Hair

    Rode around the big block on some errands, stopped at the Vassar Farm garden to haul some squash home, rode off… and the bike handled poorly. Well, with a few dozen pounds of produce in the panniers that’s not unusual, but this was worse. Yup, another flat.

    This time, however, our daughter was home and could rescue me in the van. Back in the shop, I found this obvious suspect:

     

    Embedded glass fragment
    Embedded glass fragment

     

    Once again, however, this wasn’t the problem, as the tire liner was barely scuffed. Those are glass fragments inside the gash, which might actually be the same one as before.

    There weren’t any other pointy objects embedded in the tire, but the tube wouldn’t stay inflated long enough to find the leak. I took the tube upstairs, submerged it in a pan of water, and found a rash of holes. Not pinholes, not a failed tube, but a series of punctures.

     

    Steel wire fragment
    Steel wire fragment

     

    Examining the tire liner revealed the cause: a strand of what my buddy DBM calls Michelin Hair poking through the tire liner. It’s a fragment of the steel belt from a car or truck tire, most likely shed from a disintegrating semitrailer recapped tire.

    There is absolutely no defense against these things, because they have razor-sharp points on both ends where the wire fractured. When the tire picks one up, every rotation drives it through the rubber, the Kevlar belt, the tire liner, and the tube. The usual symptom is a slow leak, eventually followed by a row of holes in the tube as it shifts position under low pressure.

    In fact, the tube had a slow leak since I installed it a few weeks ago after a tube failure. I wondered if I’d inadvertently installed a fold, but now I think I ran over this wire during the first few rides and it’s been getting worse ever since.

    That tube is a goner! I installed another Schwalbe tube and we’ll see what happens; one has been working fine on Mary’s bike for the last three months.

    Here’s a look at the steel wire from the side:

     

    Steel wire fragment through tire liner
    Steel wire fragment through tire liner

     

    It was completely through the liner, with only a stub sticking out on the tire side. There’s certainly a matching hole somewhere on the tire, but it’ll be indistinguishable from all the other nicks and gashes.