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

  • Fixed Point Arithmetic: Percentages

    Problem: increment or decrement a variable by a fixed percentage, without using floating-point arithmetic routines. This is the sort of problem that crops up in microcontroller projects, because there’s just not enough storage for the floating-point library stuff and your program at the same time.

    Suppose you want to increment the variable BaseNum by 10%. Using floating point, that’s just

    BaseNum = 1.1 * BaseNum;

    In fixed point, it would look like

    BaseNum = (BaseNum * 11) / 10;

    Similarly, to increment by 5%, you’d use

    BaseNum = (BaseNum * 105) / 100;

    The general idea is that you scale the operation to eliminate the fractional part, then chop off the scaling to get the final answer. The parentheses are vital: the multiplication must precede the division.

    The integer division operator truncates any fractional part, it does not round. This becomes a nasty gotcha if you’re expecting the final value to change.

    For example, incrementing 15 by 10% would look like

    15 ->  165 -> 16

    But incrementing 15 by 5% goes like

    15 -> 1575 -> 15

    For obvious reasons, you should make sure the multiplication can’t overflow. If you’re incrementing by 5% (multiplying by 105), then an int BaseNum can’t exceed 312, which might come as a surprise. Casting the operands to long int would be prudent for many problems:

    BaseNum = ((long int)BaseNum * 105) / 100;

    Suppose you want to increment BaseNum several times in succession, with that number stored in Ticks. With floating point, you could use the pow() function

    BaseNum = BaseNum * pow(1.1,Ticks);

    In fixed point, the same thing seems like it ought to work

    BaseNum = (BaseNum * pow(11,Ticks)) / pow(10,Ticks);

    Unfortunately, that idea runs out of gas pretty quickly: pow(11,9) falls just outside the range of a long int, so even if BaseNum is, say, 2, you’re screwed. It’s even worse for smaller percentages, as pow(105,5) is about 13e9.

    One solution, which works well for reasonable values of Ticks, is to iterate the process

    for (Counter = Ticks; Counter; --Counter)
    {
        BaseNum = ((long int)BaseNum * 11) / 10;
    }

    That won’t overflow in the middle. However, if the division truncates away the increment you were expecting, then the loop just whirs for a while and accomplishes exactly nothing.

    That could come as a surprise if you were expecting, say, five 5% increments to add up to a 28% boost: pow(1.05,5) = 1.276, right?

    Bottom line: when you use fixed-point arithmetic, always check the low end of the range for underflow and the high end for overflow.

    Hint: store the numerator and demoninator of the fixed-point percentage fraction in variables. You can’t decrement by the same percentage by just swapping the numerator and denominator, but it might be close enough for human-in-the-loop knob twiddling adjustments.

    Memo to Self: always check for underflow and overflow!

  • Reading a Quadrature Knob

    Most analog projects will benefit from an adjustment knob; the notion of pressing those UP and DOWN arrows just doesn’t give that wonderful tactile feedback. These days “knob” means a rotary encoder with quadrature outputs and some software converting digital bits into an adjustment value.

    Sounds scary, doesn’t it?

    This is actually pretty simple for most microcontrollers and the Arduino in particular. The Arduino website has some doc on the subject, but it seems far too complicated for most projects.

    A quadrature rotary encoder has two digital outputs, hereinafter known as A and B. The cheap ones are mechanical switch contacts that connect to a common third terminal (call it C), the fancy ones are smooth optical interrupters. You pay your money and get your choice of slickness and precision (clicks per turn). I take what I get from the usual surplus sources: they’re just fine for the one-off projects I crank out most of the time.

    How does quadrature encoding work?

    On each falling edge of the A signal, look at the B signal. If it’s HIGH, the knob has turned one click thataway. If it’s LOW, the knob has turned one click the other way. That’s all there is to it!

    Here’s how you build a knob into your code…

    Connect one of the outputs to an external interrupt, which means it goes to digital input D2 or D3 on the Arduino board. The former is INT0, the latter INT1, and if you need two interrupts for other parts of your project, then it gets a lot more complex than what you’ll see here. Let’s connect knob output A to pin D2.

    Connect the other output, which we’ll call B, to the digital input of your choice. Let’s connect knob output B to D7.

    Define the pins and the corresponding interrupt at the top of your program (yeah, in Arduino-speak that’s “sketch”, but it’s really a program):

    #define PIN_KNOB_A 2			// digital input for knob clock (must be 2 or 3!))
    #define IRQ_KNOB_A (PIN_KNOB_A - 2)	//  set IRQ from pin
    #define PIN_KNOB_B 7			// digital input for knob quadrature
    

    The external circuitry depends on whether you have a cheap knob or fancy encoder. Assuming you have a cheap knob with mechanical contacts, the C contact goes to circuit common (a.k.a, “ground”). If you have a fancy knob with actual documentation, RTFM and do what it say.

    The two inputs need resistors (“pullups”) connected to the supply voltage: when the contact is open, the pin sees a voltage at the power supply (“HIGH“), when it’s closed the voltage is near zero (“LOW“).

    Ordinary digital inputs have an internal pullup resistor on the ATmega168 (or whatever the Arduino board uses) that will suffice for the B signal. Unfortunately, the external interrupt pins don’t have an internal pullup, so you must supply your own resistor. Something like 10 kΩ will work fine: one end to the power supply, the other to INT0 or INT1 as appropriate.

    With the knob connected, set up the pins & interrupt in your setup() function:

    attachInterrupt(IRQ_KNOB_A,KnobHandler,FALLING);
    pinMode(PIN_KNOB_B,INPUT);
    digitalWrite(PIN_KNOB_B,HIGH);
    

    The first statement says that the interrupt handler will be called when the A signal changes from HIGH to LOW.

    The Arduino idiom for enabling the chip’s internal pullup on a digital input pin is to define the pin as an input, then write a HIGH to it.

    Set up a variable to accumulate the number of clicks since the last time:

    volatile char KnobCounter = 0;

    The volatile tells the compiler that somebody else (the interrupt handler or the main routine) may change the variable’s value without warning, so the value must be read from the variable every time it’s used.

    The variable’s size depends on the number of counts per turn and the sluggishness of the routine consuming the counts; a char should suffice for all but the most pathological cases.

    Define the handler for the knob interrupt:

    void KnobHandler(void)
    {
        KnobCounter += (HIGH == digitalRead(PIN_KNOB_B)) ? 1 : -1;
    }
    

    KnobHandler executes on each falling edge of the A signal and either increments or decrements the counter depending on what it sees on the B signal. This is one of the few places where you can apply C’s ternary operator without feeling like a geek.

    Define a variable that will hold the current value of the counter when you read it:

    char KnobCountIs, Count;
    

    Now you can fetch the count somewhere in your loop() routine:

    noInterrupts();
    KnobCountIs = KnobCounter;	// fetch the knob value
    KnobCounter = 0;		//  and indicate that we have it
    interrupts();
    

    Turning interrupts off while fetching-and-clearing KnobCounter probably isn’t necessary for a knob that will accumulate at most one count, but it’s vital for programs that must not lose a step.

    Now you can use the value in KnobCountIs for whatever you like. The next time around the loop, you’ll fetch the count that’s accumulated since the previous sample.

    Even if you RTFM, apply painstaking logic, and wire very carefully, there’s a 50% chance that the knob will turn the wrong way. In that case, change one of these:

    • In the interrupt handler, change HIGH to LOW
    • In the attachInterrupt() statement, change FALLING to RISING

    There, now, wasn’t that easy? Three wires, a resistor, a dozen lines of code, and your project has a digital quadrature knob!

    If you have a painfully slow main loop, the accumulated counts in KnobCounter could get large. In that case, this code will give you a rubber-band effect: the accumulated count can be big enough that when the knob starts turning in the other direction it’s just decreasing the count, not actually moving count to the other side of zero. Maybe you need some code in the interrupt handler to zero the count when the direction reverses?

    But that’s in the nature of fine tuning… twiddle on!

  • Storm Door Latch Lube

    Storm door latch parts
    Storm door latch parts

    Our old house has storm doors with brass latch bolts and brass strike plates. Brass-on-brass is nicely self-lubricating, unlike the steel-on-steel contraptions available these days, but of late our back door hasn’t been closing smoothly.

    I fiddled with the door closer’s tension and release point to no avail, then (re)discovered that a dab of PTFE lubricant on the latch and strike plate makes the storm door close exceedingly smoothly. The base grease is clear and doesn’t make a black mess of things.

    Duh.

    Maybe everybody knows that and perhaps I knew it at one time.

    I wrote about rebuilding the strike pull and shaft cam of these latches as CNC projects in my Digital Machinist column. Naturally, the replacement latches available in the local hardware stores didn’t fit the door, so the simplest course of action was some quality shop time.

  • Analon Slide Rule

    Whenever I do anything even slightly out of the ordinary with magnetics, I must drag out my trusty Analon slipstick to make sure I haven’t lost a dimension.

    Analon slide rule - front
    Analon slide rule – front

    Go ahead, you verify that the area inside a BH hysteresis curve is proportional to power loss in a given transformer core. I’ll wait…

    Analon slide rule - back
    Analon slide rule – back

    My recollection is that I bought it in the Lehigh University Bookstore in the early 70s, but that doesn’t square up with the Analon’s history: they should have been out of circulation by then. I’m pretty sure I didn’t get it in high school, extreme geek though I was, and it’s for damn sure I wouldn’t have bought one after graduation. Come to think of it, if the LU Bookstore wasn’t among the last bastion of Analon holdouts, where would you look?

    Over the decades I’ve penciled in a few handy dimensions they didn’t think of. Unlike most of the 600 597 (plus one in the Smithsonian) Analons in the wild, this one actually gets used, so it’s not New-In-Box (which means you collectors need not suffer from involuntary hip motions). It’s also not as grubby as it looks: I didn’t spend a lot of time futzing with the scans.

    Anyway, that’s called beausage and it enhances the value.

    Works that way with other antiques, right?

    Links:

    Yeah, OK, it’s a Slide Rule Gloat…

  • AA Cell Dimensions

    Ever wonder why rechargeable AA cells don’t quite fit in older flashlights & gizmos? Somewhat to my surprise, the dimension specs for alkaline and rechargeable cells aren’t quite the same.

    At the bottom of the Wikipedia AA battery page, we find “brand-neutral” drawings (allegedly) based on ANSI specs:

    • Alkaline: 14.0 ± 0.5 dia x 49.85 ± 0.65
    • Rechargeable: 14.1 ± 0.6 dia x 48.9 ± 1.6

    A rechargeable cell can thus be 0.2 mm larger in diameter, but should have the same maximum length.

    Based on my collection, alkalines seem to be near their nominal and NiMH cells near their maximum. Across a four-cell layer, the difference adds up to 1 mm or so, which is enough to strain the plastic.

    8-cell NiMH AA pack
    8-cell NiMH AA pack

    Hint: Put some paper on the negative terminal when you measure the cell length. Steel calipers are pretty good conductors and the short-circuit ratings (even for alkalines) are surprisingly high  …

    When I make up NiMH packs for our bike radios, I lash the cells in place with cable ties. It’s not pretty, but the plastic cases don’t split.

    Connector? Anderson Powerpoles FTW! Make sure you align them properly to mate with anybody’s radio.

  • Power Outlet Contact Failure

    Burnt outlet expander
    Burnt outlet expander

    Ordinary AC power outlets have fairly robust contacts, designed to last basically forever. I have no idea what the actual design life might be, but it’s rare to have an AC outlet fail.

    This one did…

    It’s an outlet expander at the end of an extension cord that provides six outlets. I’d installed it at my parent’s house (I was their go-to guy for electrical things, of course) and everything was fine. One visit involved rearranging some appliances and the adapter went nova when I plugged something into it.

    Me being their go-to electrical guy, I’m pretty sure this gizmo didn’t experience a whole bunch of mate-unmate cycles in my absence. Most likely it was defective from the factory, so sticking a plug in once or twice was enough to break the contact finger.

    dsc00153-detail-of-burnt-socket
    Detail of burnt socket

    Here’s a contrast-enhanced detail of the outlet in the lower-right of the top picture. The broken finger bridged the brass strips carrying the two sides of the AC line in the left side of the compartment.

    Blam: brass smoke!

    Oddly, the fuse didn’t blow. It was pretty exciting to have a small sun in the palm of my hand until the contact finger fell to the bottom of the compartment.

    The bottom picture shows the offending finger. It’s pretty obvious what happened.

    Errant contact finger
    Errant contact finger

    I’ve read of folks applying silicone lubricant (spray, perhaps) to their AC line plugs to reduce the mating friction in the outlet. While that sounds like a good idea, I think it’s misguided: you don’t want to reduce the metal-to-metal contact area by lubing it up with an insulator. In any event, that sliding friction ensures the contacts have a clean mating surface with low resistance.

    Maybe use some Caig DeoxIT, but not an insulating spray!

    For what it’s worth, do you know that the durability of an ordinary USB connector is 1500 cycles? That’s far more than PCI backplane connectors at 100 cycles. Some exotic high-GHz RF connectors can survive only a few dozen cycles.

    Moral of the story: don’t unplug your stuff all the time. Use switches and stay healthy.

    This took place many years ago, so the picture quality isn’t up to contemporary standards.

  • CD V-750 Dosimeter Charger Manual

    V-750 Model 5b Manual Cover
    V-750 Model 5b Manual Cover

    My V-750 dosimeter charger came with two (!) copies of the manual and the modification instructions (stamped JUN–1965) for adding the anti-kick capacitor.

    The paperwork didn’t fare quite as well as the metal-cased charger, sporting far more mildew on the pages than I want on my shelves.

    I cut the worst-looking copy right down the middle, scanned it with some attention to detail, and now there’s a nice version that looks just as bad but lacks the mildew.

    Clicky:

    CD V-750 Model 5b Radiological Dosimeter Charger Operating and Maintenance Manual with Modification Instruction Sheet

    If you’re really clever, you can figure out how to sequence the sheets and print them duplexed so they appear back-to-back, then bind them into a booklet just like the original. There’s a copy of a blank inside cover, too, so you can wrap your booklet in a nice Civil Defense Yellow cover.

    The schematic shows what real engineers could do, back in the days when transistors came individually packaged with a ten-dollar price tag: 1.5 volts in, 200+ volts out, one transistor. Of course, they paid attention to their transformer lessons.

    V-750 Dosimeter Charger Schematic
    V-750 Dosimeter Charger Schematic