Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.
In general, you cannot solve a bed bug problem by attracting and trapping bugs: there are simply too many bugs that are breeding ahead of their losses. We had (presumably) brought very few bugs home in our luggage, so every one we trapped was one less bug in the room. In any event, the number of bugs caught in the traps would give some idea of how much trouble we were in.
The bottom line: we trapped one or two bed bug instars and no adults.
Anything labeled for use against bed bugs carries a staggering markup and considerable smoke-and-mirrors marketing, but if you go back to the original sources (see the references in the first post), you’ll find out what actually works, which is quite different from what’s advertised.
The study by Wang, et. al., tested carbon dioxide, heat, and chemical lures. Tested singly: CO2 is pretty good, heat is OK, chemical lures definitely come in last. Basically, I think there are way too many significant figures in their results, but under idealized test conditions in a small arena, they collected about 80% of the bugs after six hours.
One key number: the CO2 flow rate was about 170 ml/min, roughly that produced by an adult human.
Another interesting number:
The visual inspections found ≤23 bed bugs in each apartment and they were considered as low levels of infestations.
CO2 mug and powder trap
Based on that, we decided to build some CO2 traps, which led to those observations. Our version of a dry-ice trap used a huge insulated mug filled with dry ice, perched atop an inverted dog food dish. We deployed two traps like that.
The dog dish has a cloth skirt (so the bed bugs can get traction on the way in) and a layer of talcum powder inside (so they can’t get any traction on the way out). The gas flow rate was in the right ballpark.
After several days, we had collected exactly zero bed bugs.
That wasn’t surprising, of course, because we knew we didn’t have all that many bugs, but we were still getting bitten in other parts of the house. Like, alas, the guest-room bed where we’d moved after gutting our bedroom.
Using dry ice as a CO2 source is relatively expensive and exceedingly inconvenient. We went through two iterations and decided that this was far too expensive, given the expected results.
It turns out that baker’s yeast metabolizes sugar into ethanol and carbon dioxide as the yeast gradually dies in a sea of dilute ethanol; if you have a distillation rig handy, you can probably get a decent yield of vodka from this project. Normally I use the carbon dioxide to stretch bread dough, but in this case it came in handy all by itself.
You can buy, for $50, a Bed Bug Beacon or you can build your own carbon dioxide lure and trap from ordinary household items for pretty close to zero dollars. Your choice.
I built and deployed four yeast reactor lures, built from gallon milk jugs and Tygon tubing from the parts heap. This picture tells you pretty nearly everything you need to know.
Yeast CO2 generator
I used a hollow punch to poke those the neat holes in the lids, but a razor knife will suffice. Seal the opening on the bottle cap with something sticky; nothing adheres well to polyethlyene and Tygon, although the contact cement I got with the dryer rear seal worked well.
Cap detail
Put three quarts / liters of warm water in the jug, add a cup of sugar (lots of sugar = longer production = more gas) and a teaspoon of yeast (lots of yeast = more production = live hard, die young), put on a solid cap, and shake vigorously to mix. Swap in the cap with the tubing and deploy. The recipe is totally non-critical and would make a great science fair project…
The dingus on the other end of the hose is the bottom of a cottage cheese container, artfully sculpted into a shallow dish with four small feet between low arched openings. Basically, it’s a little cover to trap the CO2 in a confined area and let it leak out in relatively concentrated streams. I have no idea if that’s how it works, but it was easy to do and keeps the hose from wandering away.
What they don’t tell you is that the gas production from a small yeast reactor is maybe 5%, tops, of the 150-200 ml/min required to mimic a human: I bubbled the gas into an inverted 60 ml syringe and used a stopwatch. The gas production varies strongly with time; after a week it’s down to essentially zero, so I’d say the “11 day” claims for the BBB’s lifetime are, mmmm, fanciful, at best.
Gas production is proportional to the total number of active yeast. Methinks a cup of sugar in three quarts of water will yield more yeast than a packet of sugar in, what, a pint jar? If you drop fifty bucks on a BBB, make some measurements and let me know, OK?
Maybe they use brewer’s yeast, which is an ethanol-tolerant strain of ordinary baker’s yeast. The end product, after a week, smells strongly of ethanol, so I’m not sure how much difference that would make.
In any event, my opinion is that such a minimal gas flow can attract bugs from only a very limited radius, so the results are far less conclusive than dry ice or pressurized-gas lures. Of course, if you have floors crowded with bed bugs, a few of them will stumble across the lure simply by accident.
Of course, there is one lure that’s absolutely guaranteed to attract bed bugs from across the room: you. I’ll discuss that after covering traps and barriers…
Having ever so many books & papers in the Basement Shop & Office, I must run a dehumidifier to fight the mildew to a standstill. It’s actually under 55% most of the time, but humid summer days are killers.
Being the sort of bear who owns a Kill-A-Watt meter, I jotted down dates, runtime hours, and kWh when I filled each 5-gallon bucket. Eventually, we acquired a cheap scale that found its way under the buckets to weigh the outgoing water.
My data collection foundered on errors of omission, power failures, and general forgetfulness, but, nonetheless, a few interesting numbers emerged.
Outside weather:
Dry weather = 0.14 kWh/elapsed hour
Wet weather = 0.37 kWh/elapsed hour
It draws about 485 W, so the duty cycle works out to
0.14 kWh/hour -> 140 W -> 29%
0.37 kWh/elapsed hour -> 370 W -> 76%
If I were more industrious, I’d grab a plot of daily humidity from the NWS and rub those numbers against it, but … maybe next year.
The thing requires somewhere between 2.0 and 3.5 kWh to extract each pound of water. It’s rated at 1.6 liter / kWh = 3.5 lb / kWh, undoubtedly under standard conditions, so the actual efficiency is in the right ballpark.
Comes a time in the life of every keyboard when you must simply tear it apart to clean out the crud. I’ve been using a Microsoft Comfort Curve keyboard for several years and it’s worked well, but the grunge finally exceeded even my lax standards.
A handful of screws secures the bottom cover; the shortest screws run down the middle. Surprisingly, the giant HEALTH WARNING label doesn’t cover any screws. A row of gentle snap latches along the edges holds the covers together; ease them apart with a small screwdriver or your fingernails.
The lower cover holds the crosspoint matrix under a giant silicone rubber spring mat, with the USB interface board to the upper left. I left those in place, as the top cover captured nearly all the crud.
The keycaps have stems that slide in guide tubes molded into the top cover, with triangular latches that both secure the stem and prevent it from rotating. I used a small pin punch to push the keycaps out, as shown in the top picture; the punch much be small enough to allow the latches to bend inward as they clear the notches.
Keycap retaining latches
The larger keys have equalizing wire bails that latch under guides molded into the top cover. They’ll slide right out, but don’t shove the pin punch too far too fast.
Keycaps with equalizing wires
Many of the keycap stems have ridges along their length to ensure each one fits only in its proper position; the triangular latches also have different orientations. This view shows the numeric pad (from the “screen” side of the keyboard) with a variety of coded guide tubes, wire bail guides, and the surprisingly deep tub underneath the keycaps that may capture much of the inevitable liquid spill and route it out the drain hole near the far edge.
Keyboard top panel
I tossed the keycaps and top cover in the dishwasher, which did a wonderful job of cleaning them out. A dab of silicone grease on the wire bail contact points should keep them sliding freely.
Reassembly is in reverse order, although I defy you to put all the keycaps back in their proper places without referring to another keyboard…
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
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
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
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…
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
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
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
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
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
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!
Those water bottles turned out to have an unexpected feature: the vent hole dribbles when they’re laid sideways. I think it’s an unfortunate side effect of a product cheapnification: the spout is slightly concave on the side that could seal the vent, so surface tension sucks water out of the hole.
The hole is to the right of the spout, in the center of the transverse ridge. Looks like a little black dot here; clicky for a somewhat bigger image.
Water bottle cap showing vent hole
The solution is to put a silicone plug at the Spot Marked X on the spout, which I marked by running a small punch through the vent hole and scarring the spout. You might be able to see the tiny mark on the spout if you look closely.
The spout comes out of the cap with a firm pull, but the sockets in the cap are obviously not intended to take much of that abuse. It’s not clear to me that the designers of these things ever take cleaning into consideration; past experience says you must completely dismantle fluid-facing components to get the crud out.
Anyway.
Chuck the spout up in the Sherline mill, align over the spot, and mill a 1/8 inch diameter flat-bottom hole 0.100 inch deep. The spout has a large finger-friendly flange directly underneath the Spot Marked X, so this setup isn’t quite as precarious as it looks.
Milling socket for vent plug
Dab in a little silicone caulk, leave a mostly flat surface standing just higher than the spout’s concavity, and we’ll see how well it works.
Vent plug
The little nub just to the left of the new plug (on the ball-shaped part of the spout) engages the edge of the socket in the cap to sorta-kinda hold the spout closed. Doesn’t really accomplish much, but it’s a nice thought.
So Kohl’s sent Mary a killer deal coupon and we bought some odds and ends, including a new cutting board to replace the decades-old one that I’ve been flycutting clean every few years. Evidently bamboo is the new Right Stuff for cutting boards; it’s certainly eyeshattering.
The thing spent the last few days soaking up a slathering of canola oil, in the hope it won’t soak up other juices.
Recess for cutting board foot
The instructions say to store the board standing on end so it dries properly. Evidently you’re supposed to hang it from the ring screwed into one end, but a corresponding hook (not supplied) just doesn’t doesn’t have a place on our counter / cabinet / backsplash. However, we could stand it up, leaning against an under-cabinet shelf next to the toaster oven, if only it wouldn’t slide away.
This calls for some aftermarket tweakage!
So I hauled it to the Basement Laboratory Woodworking Wing and installed a pair of silicone rubber feet in little recesses.
I grabbed a 7/16-inch end-cutting end mill in the drill press, because even the manual mill doesn’t have enough height for the board on end and the drill press doesn’t have enough reach for a Forstner bit without fiddling around with the emergency drop stopper clamp. The drill press does have a good vise and an XY table, so I got it pretty close to dead center on the third dark stripe from each edge.
The feet are about 1/4 inch tall: I went down half that in the hopes they wouldn’t bump off quite so easily.
Silicone foot in cutting board
But that didn’t quite work: the adhesive on the feet doesn’t grip the rather porous endgrain bamboo nearly well enough: a foot popped off after a day. I added a layer of Genuine 3M double-stick foam tape to the feet and that’s holding just fine.