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

  • HP 54602B Oscilloscope: Capturing Screen Images Therefrom

    Back in the early 90s I bought a Hewlett Packard 54602B digital oscilloscope: 150 MHz bandwidth, 4 channels. By and large, a fine piece of gear that’s been worth every penny.

    Over the objections of the HP sales force, I got the HP 54651A RS-232 interface rather than the HP 54650A HPIB (aka GPIB, aka IEEE-488) interface. Reasoning: I’d need a matching interface on the PC side and PC architecture is anything but stable. Turns out that was a good decision, as you’ll realize if you count up the number of different buses since those days and factor in the cost of an IEEE-488 interface for each iteration. RS-232 is slow and not well-suited for complex arrays of test gear, but mostly I needed the scope for screen shots to go with my Circuit Cellar columns.

    Here’s how that works…

    Configure the scope by poking the RS-232 softkey in Print/Utility:

    • Connect to: HP Plot
    • Factors: On (this is absolutely vital)
    • Resolution: High
    • Baud Rate: 19200 (yes, “baud rate” is an oxymoron)
    • Handshake: DTR

    Now the scope will dump HPGL to what it thinks is a plotter, so the trick is to make the PC look like a plotter. Turns out that’s not particularly difficult.

    Despite the name, DTR handshaking does not use the scope’s DTR pin. The relevant manual section looks like this (click the thumbnail for full-size image):

    HP54602B Oscilloscope Serial Port Pin Functions
    HP54602B Oscilloscope Serial Port Pin Functions

    So DTR remains high and the flow-control signaling actually uses the RTS/CTS pins. The cable I’ve been is basically a null modem connection with the appropriate gender on each end:

    PC RS-232 HP 54602B Oscilloscope
    9-pin F Signal 25-pin M Signal
    1 DCD 20 DTR
    2 RxD 2 TxD
    3 TxD 3 RxD
    4 DTR 6 DSR
    also-> 8 DCD
    5 Gnd 7 GND
    6 DSR 20 DTR
    7 RTS 5 CTS
    8 CTS 4 RTS
    9 RI n/c

    Note:

    • PC 1 and 6 <- scope 20
    • PC 4 -> scope 6 and 8

    I wired the cable like that mostly because I have a lifetime supply of nice 9-conductor shielded cable lying around. You could connect the scope’s DTR to its own DSR and DCD pins, apply similar trickery on the PC end, and everybody would be perfectly happy with 5-conductor cable, maybe even 4-conductor if you ran the ground through the shield.

    Using XON/XOFF flow control seems to not work well, although I admit to not trying too hard to figure it out.

    These days, I use a USB-to-RS-232 converter with the now-standard 9-pin connector. The port identifier may be nearly anything after udev/hotplug has its way with the hardware, but it usually works out to /dev/ttyUSB0.

    The script requires C-Kermit (likely the ckermit package), ImageMagick, sed, and hp2xx (yes, spelled exactly like that), all of which should be packages in your favorite Linux distro. Haven’t a clue how this might work with Windows.

    With all that in hand, copy-n-paste the following text into a file (I used gethp54602, for lack of anything more original), make it executable (chmod u+x gethp54602) and run it with a file name (./gethp54602 test). Poke the Print Screen softkey on the scope and settle back for a bit. The scope can’t keep up a steady flow of data at 19200 b/s, so the whole affair takes a minute or three for the 50-ish kB of text in a dual-trace image.

    You’ll end up with three files:

    • test.hgl — raw HPGL text from the ‘scope
    • test.eps — raster conversion from HPGL
    • test.png — bitmapped screen-capture image

    The magic script…

    #!/usr/bin/kermit +
    # Fetches screen shot from HP54602B oscilloscope
    # Presumes it's set up for plotter output...
    # Converts HPGL to PNG image
    
    set modem none
    set line /dev/ttyUSB0
    set speed 19200
    set flow rts/cts
    set carrier-watch off
    
    # Make sure we have a param
    if not defined \%1 ask \%1 {File name? }
    
    set input echo off
    set input buffer-length 200000
    
    # Wait for PRINT button to send the plot
    echo Set HP54602B for HP Plotter, FACTORS OFF, 19200, DTR
    echo Press PRINT SCREEN button on HP54602B...
    
    log session "\%1.hgl"
    
    # Wait for final character
    input 480 \x03
    
    close session
    close
    
    echo Converting HPGL in
    echo --\%1.hgl
    echo to PNG in
    echo --\%1.png
    
    # Without factors
    #run hp2xx -m png -a 1.762 -h 91 -c 14 "\%1.hgl"
    #run mogrify -density 300 -resize 200% "\%1.png"
    
    # With factors
    run sed '/lb/!d' "\%1.hgl" > "\%1-1.hgl"
    run hp2xx -q -m eps -r 270 -a 0.447 -d 300 -w 130 -c 14 -p 34 -f "\%1.eps" "\%1-1.hgl"
    run rm "\%1-1.hgl"
    run convert -density 300 -resize 675x452+2+2 "\%1.eps" "\%1.png"
    
    echo Finished!
    exit 0
    

    [Update: WordPress unpredictably and ruthlessly mangles source code by incorrectly escaping some symbols. The line with sed should have a greater-than symbol to pipe the result into the .hgl file. A more recent version (albeit using my SENA PS410 serial server) hosted as a GitHub gist may help.]

    Here’s a sample of what pops out; it’s the scope’s own calibrator waveform, nothing exciting, but you get the general idea.

    Screen Capture of Calibrator Signal
    Screen Capture of Calibrator Signal

    The commented-out section labeled “Without Factors” converts the format you get with Factors Off. Turns out that there’s no unique ending string without factors, which puts a real crimp in getting the data. The advantage is that the HPGL converts directly to PNG and looks good. The only way I’ve found to capture the scope data is to just time out after a while.

    With Factors On, however, the image data has a unique ending character (ASCII ETX, 0x03) after the label text, but the layout is rotated to plot in landscape mode. A direct conversion to PNG looks awful, perhaps because hp2xx must do software character generation, and I eventually figured out that making a bank shot off EPS vector format produced much better results.

    However, hp2xx dutifully renders the text into the EPS image, but it doesn’t inject a carriage return after each linefeed: the text dribbles off to the far right of the actual screen image. To fix that, the sed editor hacks off the label text following the lb command. Mercifully, hp2xx doesn’t choke on the incomplete command.

    And then convert does its usual magic. The image size is just right to drop into my Circuit Cellar columns; you may want something different for your purposes.

    [Update: The PNG has an alpha channel that selects only the traces, so the background color depends on where you put it. A small tweak is there.]

    For reasons that I absolutely do not understand, I cannot control the oscilloscope through the serial interface. The scope sends data to the PC just fine and I can get the scope to complain about the character format if I send truly bogus junk (like, mismatching the baud settings), but it simply will not respond to commands. Maybe the interface is broken or, more likely, I’m screwing something up. Hasn’t been a problem, though, for my simple needs.

    Memo to Self: One of these days, eBay will have a 54652B serial/parallel interface that might work better.

  • Toner Transfer PCBs: Alignment Accuracy

    Here’s an example of the dimensional accuracy you can get from toner-transfer PCBs in real life.

    I drill the holes with a CNC-ed Sherline mill, so they’re pretty much spot on. Drilling the holes by hand simply isn’t practical: there’s no way to get both global alignment and local accuracy.

    The toner transfer sheet, printed on a laser printer, gets aligned to the existing holes atop a light table. The paper stretches & shrinks and moves around while printing, but I can generally average out the errors so that the 24-mil holes (the smallest I generally use) across the board have no more than a few mils of error: the pads don’t show more than that inside the drilled holes. In the picture below, you can see a dark rim around the corner alignment hole that looks worse than it really is due to the perspective.

    I put the toner transfer sheet on the light table, toner-side up, lay the PCB atop the paper, and adjust for best overall alignment. I then tape them together along one edge with strips of laser-printer address labels: guaranteed to hold up to high temperatures, which is more than you can say for most tapes.

    PCB alignment and taping
    PCB alignment and taping

    Here’s the board after etching both sides, with the black toner and green sealant film still in place. The toner & film are slightly smeared from the solvent I used to clean off the other side before etching it. The brownish dabs on the green areas come from a brown Sharpie that works fine as a touch-up etching resist.

    WWVB Simulator - Top surface toner mask
    WWVB Simulator – Top surface toner mask

    The narrowest traces are 16 mils, most of the others are 32 mils, and the fat ones down the middle of the chip are 40 mils. Click on the images for bigger versions; you’ll get some JPG compression artifacts, but the resolution is good enough to see what’s going on.

    Here’s the same area with the toner removed and a touch of silver plating applied to make it pretty and more easily solderable. The colors aren’t particularly reliable; in real life, it’s a lot more silvery.

    Top surface copper
    Top surface copper

    Fairly obviously, the alignment isn’t nearly as good as you’d expect from the initial taping. In round numbers, the pads to the left side seem offset by about the diameter of the holes; call it 25 mils. The holes in the DIP pads are off by perhaps 10 mils.

    The bottom surface looks pretty much the same, with similar alignment issues.

    Bottom surface copper
    Bottom surface copper

    The misalignments are not uniform, as you’d expect if the toner transfer sheet moved across the board during fusing. The sheet deforms during the fusing process in a completely unpredictable way, despite my trying all of the usual tricks:

    • Pre-shrinking the transfer paper by running it through the printer with a pure-white image (so no toner gets applied)
    • Fusing quickly after printing to prevent moisture absorption (there’s a limit to how fast I can work)
    • Taping more than one edge to lock the paper in place

    It’s fair to say you (well, I) can get within 25 mils of a board hole for sure, less than that most of the time, and be spot on over much of the board. I use large pads and vias for anything I have control over, as witness the pads surrounding the DIP, and avoid very fine features near holes.

    Anyhow, it’s good enough for what I do, but you shouldn’t get your hopes up that toner-transfer circuit boards come anywhere close to commercial quality. If you’re doing a lot of pure surface-mount work, it’ll probably be good enough because there’s no need for global alignment to holes in the underlying board. Obviously, the smaller the board, the better off you’ll be.

    I etched this board by rubbing ferric chloride on it with a sponge (wearing forearm-length rubber gloves and a shop apron!), renewing the solution as it turned black and gooey. Works like a charm, gives good control of the process, doesn’t erode the Sharpie masking, doesn’t over-etch the traces (much, anyway), and uses less etchant than soaking the board in a bath.

    I have other posts describing the process in more detail. Search for PCB, toner-transfer, and other keywords to unearth those entries.

  • Converting Day-of-Year to Month and Day-of-Month, Then Back Again

    I needed the conversions for a WWVB simulator, so it knows when to twiddle the leap-year and leap-second bits.

    The general notion is a table with the Day-of-Year values for the last day of each month. The most expedient way of doing this is with two columns: one for normal years and the other for leap years, thusly…

    Month EOM EOM-LY DOY DOY-LY
    0 0 0 0 0
    1 31 31 31 31
    2 28 29 59 60
    3 31 31 90 91
    4 30 30 120 121
    5 31 31 151 152
    6 30 30 181 182
    7 31 31 212 213
    8 31 31 243 244
    9 30 30 273 274
    10 31 31 304 305
    11 30 30 334 335
    12 31 31 365 366

    Hint: even if you can recite the “Thirty days hath November …” jingle, it’s much better to build a spreadsheet so the additions all work out. It’s even better if you don’t attempt any of this with a late-summer head cold. You might want to check all my work, because I’m still stuffy.

    With table in hand, the code is straightforward.

    Define a structure with all the various bits & pieces of the current time, much of which isn’t used here. It’s all needed in the WWVB simulator:

    enum EVENTSTATE {EVENT_INACTIVE,EVENT_PENDING,EVENT_ACTIVE};
    
    struct timecode_ {			// The current moment in time...
      byte Year;				// 0 - 99
      word DayOfYear;			// 1 - 366
      byte Hour;				// 0 - 23
      byte Minute;				// 0 - 59
      byte Second;				// 0 - 60 (yes!)
      byte Tenth;				// 0 - 9
      enum EVENTSTATE LeapYear;		// 0 = no, 1 = pending, 2 = active
      enum EVENTSTATE LeapSecond;		// 0 = no, 1 = pending, 2 = active in this minute
      enum EVENTSTATE DaylightSavingTime;	// 0 = no, 1 = pending, 2 = active
      char UT1Correction;			// 100 ms units, -10 to +10 range (+/- 1 second)
      byte MinuteLength;			// 60 or 61
      byte Month;				// 1 - 12 (not sent in frame)
      byte DayOfMonth;			// 1 - 28..31	(not sent in frame)
    };
    

    That’s obviously overspecified, because DayOfYear with LeapYear uniquely determines Month and DayOfMonth. It’s handy to have both forms around, sooo there they are.

     

    Then set up the table, glossing over the quick matrix transposition that turns the entries for each year into rows rather than columns:

    prog_uint16_t PROGMEM MonthEnds[2][13] = {
      0,31,59,90,120,151,181,212,243,273,304,334,365,
      0,31,60,91,121,152,182,213,244,274,305,335,366
    };
    

    Conversion from DayOfYear to Month and DayOfMonth requires searching backwards through the appropriate table row until you find the entry that’s smaller than the DayOfYear value, at which point you’ve found the right month.

    void ConvertDOYtoDOM(struct timecode_ *pTime) {
    byte Index,LY;
    word EndOfMonth;
      LY = (EVENT_INACTIVE != pTime->LeapYear) ? 1 : 0;
      Index = 12;
      while ((EndOfMonth = pgm_read_word(&MonthEnds[LY][Index])) >= pTime->DayOfYear) {
    	--Index;
      };
      pTime->Month = Index + 1;									// months start with 1, not 0
      pTime->DayOfMonth = (byte)(pTime->DayOfYear - EndOfMonth);
    }
    

    Converting from Month and DayOfMonth to DayOfYear is much easier, as it’s pretty much just a table lookup:

    word ConvertDOMtoDOY(struct timecode_ *pTime) {
    word EndOfMonth;
      EndOfMonth = pgm_read_word(&MonthEnds[(EVENT_INACTIVE != pTime->LeapYear) ? 1 : 0][pTime->Month - 1]);
      return EndOfMonth + pTime->DayOfMonth;
    }
    

    This code might actually work, but if I were you, I’d test it pretty thoroughly before lashing it into your project…

     

    The PROGMEM and pgm_read_word() stuff is the Arduino mechanism that puts the lookup table into Flash program memory rather than the ATMega168’s exceedingly limited RAM space. The definitive word about that process resides there.

    Memo to Self: Using const simply makes the variable kinda-sorta read-only, but still initializes RAM from Flash. The PROGMEM routines delete the RAM copy entirely.

  • They’re Not Redundant When One’s Broken

    Vassar College Chapel sump pumps
    Vassar College Chapel sump pumps

    The basement of the Vassar College Chapel has a mighty drainage sump, a pair of pumps, and a mass of old-school cast-iron plumbing. I’m not certain, but I think the greenish pipe coming in from the rear is storm drainage from one side of the building.

    Looks like they’ve got backup: two separate pumps and motors, with doubled plumbing on the ejection side.

    Vassar College Chapel sump pumps - broken float switch rod
    Vassar College Chapel sump pumps – broken float switch rod

    On closer inspection, however, you’ll see that the near-side float switch rod coming up through the cover is broken and bent; that motor will never turn on.

    One hopes this is deliberate, but the failed-off motor seems to be the newer / larger / less corroded of the two. We’ve had a lot of rain this summer, so I suppose if this was a problem, it would have already occurred.

    It’s always a good idea to have all your sump pumps ready to run, should you be in a position to need sump pumps in the first place. Believe me, it’s much easier to fix things when you’re not hip-deep in rising water!

  • Camouflaged Katydid

    Katydid on matching umbrella
    Katydid on matching umbrella

    This gadget appeared on an umbrella we’d left outdoors to dry.

    We wonder if the green surface seemed like a leaf…

  • Extended Sewing Machine Quilting Surface

    Extended quilting surface
    Extended quilting surface

    Mary has been quilting up a storm lately and wanted a larger surface to handle a bed-sized quilt. A table in the basement was big enough, but she wanted a larger flat surface around the sewing machine adjacent to the table.

    I converted the typing return (*) from her upstairs desk into a table, then cut a piece of aluminum-clad 1-inch foam insulation board to fit. It’s 4 feet long, a convenient length to cut from the 4×8-foot insulation board, and slightly narrower than the typing return. Cutting it required a long X-Acto knife blade, but a really sharp utility knife would work as well.

    Some stainless-steel tape finished off the edges. The tape itself is lethally sharp-edged, but it’s perfectly harmless if you do a good job of smoothing it against the foam board…

    A pair of closed-cell rigid foam blocks held one end of the board at the proper height around the sewing machine, while a pair of cutoffs from the wood pile were just the right thickness & length to extend under the other end. It turns out that precise height isn’t nearly as vital as we expected; close enough is fine.

    I cannibalized a pair of table-saw feed roller stands for this project; they had just the right height adjustment and shape to support the typing return and the foam board.

    The end result aligns the surface of the sewing machine with both the top of the table and the surface of the foam board. The quilt slides easily over the whole affair and doesn’t bunch up like it did before. Success!

    Foam support blocks
    Foam support blocks

    (*) A “typing return” is the little table that sticks out from a desk, upon which you put a typewriter, back in the day when typewriters ruled the land. Nowadays, she uses it for her sewing machine, which normally lives at her desk, because there’s no practical way to type at right angles to one’s desk.

    That’s the sort of item you can’t do web searches for, because all the terms are so heavily overloaded. Give it a try; you’ll find one or two useful hits. There’s a difference between syntax and semantics; we’re not in the semantic web yet by long yardage.

  • Sherline Z-axis Backlash: Check the Bearing Preload Nut!

    Loose bearing nut
    Loose bearing nut

    I don’t do any fancy 3D milling, so it takes a lot of Z-axis backlash to get my attention. While setting up for some circuit-board drilling, I finally noticed that the backlash far exceeded even my slovenly specs: something like 20 mils.

    The Z-axis backlash adjusting nut on the saddle was as snug as it usually is. Heaving on the saddle, though, pulled it up & down and moved the handwheel on the top of the Z-axis motor.

    Ah-ha! That says the leadscrew itself is moving, which shouldn’t be possible because it’s captured at the bearings in the stepper motor mount.

    Some tedious disassembly later, the top picture shows the Z-axis leadscrew and motor mount, with the nut obviously too far away from the lower ball bearing housing. The nut was finger-loose and I moved it while extracting the leadscrew; it’s supposed to be snug against the bearing in normal operation.

    The solution is a drop of Loctite, which should be applied to the canonical “clean and dry” threads. Hosing this part of the leadscrew down with solvents isn’t a good idea, because you don’t want any inside the lower bearing in the motor mount, so I spent some Quality Shop Time spinning the threads against a (dry) rag, running the nut to the other end (all of a few millimeters), and repeating until most of the oil was gone.

    Properly adjusted nut
    Properly adjusted nut

    Sherline documents how to assemble & install the motor mounts, so there’s not much mystery involved. I loosened the preload nut until the housing spun freely on the shaft, then tightened it a teensy bit; the housing still spun freely and there’s no detectable end play.

    Reinstallation requires putting the motor mount at the same spot on the Z-axis column as before. I moved the saddle to the top of the column, ran the leadscrew into the saddle nut, and then tightened the motor mount screws. That allows the mount to move to suit the saddle nut’s position, rather than going through the tedious saddle alignment process I mentioned as part of the gib adjustment.

    It’s all good… call it 3 mils of backlash on all three axes.

    Memo to Self: It’s possible to run the Z-axis backlash adjusting nut off the top of the leadscrew thread, then re-engage it without removing the motor mount. The trick is to hold the anti-backlash nut firmly against the saddle nut while turning the leadscrew to engage the thread. Remember that it’s a left-hand thread…