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.

Tag: Memo to Self

Maybe next time I’ll get it right

  • Arduino Push-Pull PWM

    Push-Pull Drive: OC1A top, OC1B bottom
    Push-Pull PWM Drive: OC1A top, OC1B bottom

    Most of the time you need just a single PWM output, but when you’re driving a transformer (or some such) and need twice the primary voltage, you can use a pair of PWM outputs in push-pull mode to get twice the output voltage.

    A single PWM output varies between 0 and +5 V (well, Vcc: adjust as needed), so the peak-to-peak value is 5 V. Drive a transformer from two out-of-phase PWM outputs, such that one is high while the other is low, and the transformer sees a voltage of 5 V one way and 5 V the other, for a net 10 V peak-to-peak excursion.

    A 50% duty cycle will keep DC out of the primary winding, but a blocking capacitor is always a good idea with software-controlled hardware. A primary winding with one PWM output stuck high and the other stuck low is a short circuit that won’t do your output drivers or power supply any good at all.

    An Arduino (ATMega168 and relatives) can do this without any additional circuitry if you meddle with the default PWM firmware setup. You must use a related pair of PWM outputs that share an internal timer; I used PWM 9 and 10.

    #define PIN_PRI_A   9    // OCR1A - high-active primary drive
    #define PIN_PRI_B   10   // OCR1B - low-active primary drive
    

    I set push-pull mode as a compile-time option, but you can change it on the fly.

    #define PUSH_PULL   true // false = OCR1A only, true = OCR1A + OCR1B
    

    The timer tick rate depends on what you’re trying accomplish. I needed something between 10 & 50 kHz, so I set the prescaler to 1 to get decent resolution: 62.5 ns.

    // Times in microseconds, converted to timer ticks
    //  ticks depend on 16 MHz clock, so ticks = 62.5 ns
    //  prescaler can divide that, but we're running fast enough to not need it
    
    #define TIMER1_PRESCALE   1     // clock prescaler value
    #define TCCR1B_CS20       0x01  // CS2:0 bits = prescaler selection
    

    Phase-Frequency Correct mode (WGM1 = 8) runs at half-speed (it counts both up and down in each cycle), so the number of ticks is half what you’d expect. You could use Fast PWM mode or anything else, as long as you get the counts right; see page 133 of the Fine Manual.

    // Phase-freq Correct timer mode -> runs at half the usual rate
    
    #define PERIOD_US   60
    #define PERIOD_TICK (microsecondsToClockCycles(PERIOD_US / 2) / TIMER1_PRESCALE)
    

    The phase of the PWM outputs comes from the Compare Output Mode register settings. Normally the output pin goes high when the PWM count resets to zero and goes low when it passes the duty cycle setting, but you can flip that around. The key bits are COM1A and COM1B in TCCR1A, as documented on page 131.

    As always, it’s easiest to let the Arduino firmware do its usual setup, then mercilessly bash the timer configuration registers…

    // Configure Timer 1 for Freq-Phase Correct PWM
    //   Timer 1 + output on OC1A, chip pin 15, Arduino PWM9
    //   Timer 1 - output on OC1B, chip pin 16, Arduino PWM10
    
     analogWrite(PIN_PRI_A,128);    // let Arduino setup do its thing
     analogWrite(PIN_PRI_B,128);
    
     TCCR1B = 0x00;                 // stop Timer1 clock for register updates
    
    // Clear OC1A on match, P-F Corr PWM Mode: lower WGM1x = 00
     TCCR1A = 0x80 | 0x00;
    
    // If push-pull drive, set OC1B on match
    #if PUSH_PULL
     TCCR1A |= 0x30;
    #endif
    
     ICR1 = PERIOD_TICKS;           // PWM period
     OCR1A = PERIOD_TICKS / 2;      // ON duration = drive pulse width = 50% duty cycle
     OCR1B = PERIOD_TICKS / 2;      //  ditto - use separate load due to temp buffer reg
     TCNT1 = OCR1A - 1;             // force immediate OCR1x compare on next tick
    
    // upper WGM1x = 10, Clock Sel = prescaler, start Timer 1 running
     TCCR1B = 0x10 | TCCR1B_CS20;
    

    And now you’ll see PWM9 and PWM10 running in opposition!

    Memo to Self: remember that “true” does not equal “TRUE” in the Arduino realm.

  • Arduino Connector & Hole Coordinates

    Arduino Diecimila
    Arduino Diecimila

    If you’re building an Arduino shield, you must align the connectors & holes with the Arduino board underneath. That seems to be easy enough, assuming you start with the Eagle CAD layout found there, but when you’re starting with your own layout, then things get messy.

    Here’s how to verify that you have everything in the right spot, at least for Diecimilla-class boards. Start by holding the Arduino board with the component side facing you, USB connector on the upper left. Rotate your own PCB layout appropriately or stand on your head / shoulders as needed.

    With the exception of J3, the center points of the connectors & holes seem to be on a hard 25-mil grid with the origin at the lower-left corner of the board (below the coaxial power jack):

    • J3 (AREF) @ (1.290,2.000)
    • J1 (RX) @ (2.150,2.000)
    • POWER @ (1.550,0.100)
    • J2 (AIN) @ (2.250,0.100)
    • Upper-left hole = 0.125 dia @ (0.600,2.000)
    • Upper-right hole = 0.087 dia @ (2.600,1.400)
    • Lower-right hole = 0.125 dia @ (2.600,0.300)
    • Reset button = (2.175,1.065)

    Offsets between points of interest:

    • connector rows Y = 1.900
    • right holes Y = 1.100
    • UL hole to UR hole = (2.000,-0.600)
    • UL hole to J3 X = 0.690
    • J3 to J1 X = 0.860
    • J3 to POWER X = 0.260
    • POWER to J2 X = 0.700
    • J1 to UR hole = (0.450,-0.600)
    • J2 to LR hole = (0.350,200)

    Note that the three etched fiducial targets are not on the 25-mil grid. They’re not on a hard metric grid, either, so I don’t know quite what’s going on. Fortunately, they’re not holes, so it doesn’t matter.

    Memo to self: perhaps I’ve measured & calculated & transcribed those values correctly. Double-check before drilling, perhaps by superimposing double-size PCB layouts on a light table or window. Finding it then is much less annoying than after drilling the board… ask me how I know.

  • Shutdown Problems with Xubuntu 8.10 on a Dell 531s

    As described there, I set up a cron job to back up our low-budget file server to an external USB drive and turn it off for the night.

    After a while, it became painfully obvious that

    shutdown -P now

    was, at best, intermittently successful at turning off the power. The shutdown sequence would sometimes hang near the end, with a blank screen, after unmounting all the drives (so there are no logs), with the power on. Keyboard & mouse were dead, tapping the power button produced a display about acpid being unhappy, but nothing I could follow up.

    Oddly, that same command issued from a terminal window would work perfectly for as long as I was willing to restart the machine.

    Even more oddly, the box would shut off properly from the GUI or the GDM login scren.

    A puzzlement…

    After several days of tedious “try this” experimentation and rummaging through the scripts in /etc/init.d/, it seems this command works in the cron job the way it’s supposed to

    halt -p -f

    The -p calls for a power-down and -f says to force the halt (rather than calling shutdown, which we know won’t work).

    So, finally, I can hack 25% off the power bill for that thing.

    Memo to self: some day, figure out exactly how the whole shutdown sequence works.

  • Laser Pointer Annoyances

    Laser pointer battery contact
    Laser pointer battery contact

    Maybe it’s just me, but all of the laser pointers I’ve bought, even the relatively spendy ones, have crappy switches and unstable battery contacts.

    For example, this is the business end of a $12 (!) pen-style pointer. The battery contact was off-center and poorly secured; I pried the white plastic retainer out, bashed the spring into submission, and replaced the retainer with a length of heat-shrink tubing. It wasn’t pretty.

    This pointer has an actual mechanical switch module inside, with a clicky mechanism actuated by the external button. Cheaper pointers seem to rely on bare PCB contacts bridged by the button’s base. Ugh.

    Laser pointer battery orientation: positive DOWN
    Laser pointer battery orientation: positive DOWN

    Memo to Self: The AAA cells fit into the housing with the positive terminal away from the laser head. The white plastic plug has a molded cross that could be mistaken for a + symbol, but it’s not.

  • Backup with Rsnapshot

    Now that our low-budget file server (a stock Dell Inspiron 531S desktop with an additional 500 GB SATA drive) is up & running Xubuntu 8.10, it’s time to get rsnapshot working again.

    All our data files live on the server, so the backup routine need not handle any of the usual /home stuff on our desktop boxes. Rebuilding a dead box is a nuisance, but they’re all pretty much the same and it’s less of a nuisance not worrying about rare failures… haven’t had any failures in many years; they get replaced before they die.

    The backup files go to an external 500 GB USB drive, which is not protection against a catastrophe in the basement. Mostly, this guards against finger fumbles; the external drive gets dumped to another hard drive in the fireproof safe more-or-less monthly.

    So. To begin…

    Install rsnapshot, which will also drag in ssh, the metapackage around the client & server sides of openssh. The server side is already installed so I can sign in using public-key authentication.

    Set /etc/rsnapshot.conf thusly (comments snipped out):

    snapshot_root   /mnt/backup/snapshots
    no_create_root  1
    cmd_cp          /bin/cp
    cmd_rm          /bin/rm
    cmd_rsync       /usr/bin/rsync
    cmd_ssh /usr/bin/ssh
    cmd_logger      /usr/bin/logger
    cmd_du          /usr/bin/du
    cmd_rsnapshot_diff      /usr/bin/rsnapshot-diff
    #interval       hourly  6
    interval        daily   30
    #interval       weekly  4
    interval        monthly 12
    #interval       yearly  1
    logfile /var/log/rsnapshot
    du_args -csh
    backup  /mnt/userfiles/         oyster/
    backup  /mnt/bulkdata/          oyster/
    backup  /mnt/music/             oyster/
    backup  /mnt/diskimages/        oyster/
    

    Basically, that creates a month of daily backups, plus monthly backups for a year. Haven’t ever gotten to a yearly backup, but you get the idea.

    The no-create-root option prevents horrible things from happening if the USB drive wakes up dead and doesn’t mount; you don’t want to back up the drives to the /mnt/bulkdata mount point. The USB drive mounts using a UUID entry in /etc/fstab, as described there.

    Create a pair of scripts in /root to mount the USB drive, do the backup, unmount it, and shut down the system:

    rsnapshot.daily

    #!/bin/sh
    logger "Mounting USB drive"
    mount /mnt/backup
    logger "Starting backup"
    /usr/bin/rsnapshot daily
    logger "Unmounting USB drive"
    umount /mnt/backup
    logger "Power off"
    shutdown -P now
    logger "Done!"
    

    rsnapshot.monthly

    #!/bin/sh
    mount /mnt/backup
    /usr/bin/rsnapshot monthly
    umount /mnt/backup
    shutdown -P now
    

    Note: the rsnapshot executable has moved from /usr/local/bin in Ubuntu 7.10 to /usr/bin in 8.10.

    You could be more clever than that, but, eh, they’re simple & easy.

    The Inspiron 531S 1.0.13 BIOS now powers off dependably with the 2.6.27-14-generic kernel in 8.10, a pleasant change from the 1.0.12 BIOS and the 2.6.22-16-generic kernel used in 7.10. That means the shutdown commands work and I can shave 25% off the server’s power bill. Not that it’s very big to begin with, but every little bit helps.

    Set up /etc/crontab to run the backups (and sync the system clock with reality, for the reasons described there):

    10 23	1 * *	root	/root/rsnapshot.monthly
    30 23	* * *	root	/root/rsnapshot.daily
    #
    00 01	* * *	root	ntpdate north-america.pool.ntp.org
    

    And that’s it.

    Memo to Self: add e2fsck to the monthly backup routine and move it an hour earlier.

  • Devil’s Pie for Xubuntu

    I have six workspaces set up on my left monitor (and, perforce, six on the right, where I need only two) with more-or-less ritualized contents: email, browser, terminals, simulation, graphics, and whatever else comes to mind.

    While I’m not attached to the notion of a particular program being on a specific workspace, I really don’t want to arrange all the programs every time this thing lights up. Alas, Xubuntu’s XFCE desktop doesn’t offer much control over where windows appear, particularly for KDE programs that don’t seem to play nicely with the rest of the desktop.

    Devil’s Pie (installable with package devilspie) solves some of that problem. It doesn’t handle dual monitors with separate X sessions, is utterly undocumented, requires more than a little patience to get working, and produces baffling error messages. But it does essentially everything I need doing.

    So…

    Install devilspie, which is at 0.22 -1 in the Xubuntu 8.10 universe repository.

    Create ~/.devilspie to hold configuration files and create the following files therein:

    firefox.ds

    ; Force Firefox to workspace 2
    (if
    (is (application_name) "Firefox")
    (set_workspace 2)
    )

    gimp.ds

    ; Force Gimp windows to workspace 6
    (begin
    (if
      (matches (application_name) "GNU Image Manipulation Program" )
      (begin
        (set_workspace 6)
      )
    )
    )

    kmail.ds

    ; Force Kmail windows to workspace 1
    (begin
    (if
      (matches (application_name) "^KMail$" )
      (begin
        (set_workspace 1)
        (geometry "960x1200+0+0")
      )
    )
    )

    The syntax is fairly obvious, the semantics rather less so. The man page is basically useless and has been that way forever, but a true heroine has already dug through the source code and documented what you need to know. Go there and read.

    If you’re doing system-wide configs for all users, you can allegedly put those files in /etc/devilspie, but I haven’t tried it out yet.

    The (matches string regex) conditional allows you to do regular expression matching, which is more flexible than string equality (is string string) or substring (contains string substring) matching. What you see above represents cruft from figuring out what works.

    Use the (begin …) syntax to wrap multiple statements in a single file; the parser seems to choke on anything more than one.

    Put (debug) in whatever new config file you create, (re-)start devilspie from a terminal, then start the program you’re trying to control. You’ll get a torrent of information about all the various & sundry windows; pluck readable bits from them and hammer out some regex matches. Tuck one of these into a (begin …) stanza along with stuff you’re trying to make work.

    Attempting starting devilspie before X comes up causes great discomfort, so you can’t start it from /etc/rc.local.

    Instead, add it to XFCE’s Autostarted Applications list: System Settings -> Session and Startup -> Application Autostart, then click the Add button to put /usr/bin/devilspie in the list.

    Memo to Self: this will be ideal for Mom’s PC beyond 8.04, assuming KDE continues to favor foo-foos over basic functionality.

  • Poof: A Post!

    Looking at the calendar shows I missed 16 March… so I’ll just go back and rewrite history.

    I generally have two or three weeks of posts scheduled for release, one a day, just before 8 am Eastern US time. That way I can write ’em when I have the time, edit them a bit to make the answer look better (or, in some cases, come out right), and not have to worry about being somewhere else in the morning.

    Yes, I agree that’s a bit sneaky. Perhaps a blog post should appear when the spirit moves me, but all in all I’d rather not dump a dozen entries in two days, then go dry for a week.

    That’s how it works… and now I’ve filled in the hole.

    Memo to self: scan the dates a little closer next time, eh?