Archive for category Software

Cylindrical Cell Adapter: 18650 to 3xAAA

Anker LC40 flashlights can use either one lithium 18650 cell or an adapter holding three AAA cells. I now prefer 18650 cells, but they’re nigh onto 4 mm smaller than the flashlight ID and rattle around something awful.

I can fix that:

Anker LC40 with 18650 cell adapter

Anker LC40 with 18650 cell adapter

Three new entries appear in the cell dimension table of my OpenSCAD inter-series battery adapter program:

NAME = 0;
ID = 0;       // for non-cell cylinders
OD = 1;

Cells = [
  ["AnkerLC40",23.0,55.0]           // Flashlight tube loose-fit for 3xAAA adapter

I took the opportunity of adding OpenSCAD Customizer comments, which means this now works:

OpenSCAD Customizer - dropdown selections

OpenSCAD Customizer – dropdown selections

The model looks about the same as before, although with a few more sides just for pretty:

AnkerLC40 vs. 18650 Sleeve - Slic3r

AnkerLC40 vs. 18650 Sleeve – Slic3r

That was easy …




LF Crystal Tester: Joystick for Oscillator Offset Adjustment

With the joystick button and LM75 temperature sensor running, this chunk of code lets you nudge the nominal DDS oscillator frequency by 1 Hz every 100 ms:

While that’s happening, you compare the DDS output to a reference frequency on an oscilloscope:

Zero-beat oscillator

Zero-beat oscillator

The top trace (and scope trigger) is the GPS-locked 10 MHz reference, the lower trace is the AD9850 DDS output (not through the MAX4165 buffer amp, because bandwidth). If the frequencies aren’t identical, the DDS trace will crawl left or right with respect to the reference: leftward if the DDS frequency is too high, rightward if it’s too low. If the DDS frequency is way off, then the waveform may scamper or run, with the distinct possibility of aliasing on digital scopes; you have been warned.

The joystick acts as a bidirectional switch, rather than an analog input, with the loop determining the step increment and timing. The ad-hoc axis orientation lets you (well, me) push the joystick against the waveform crawl, which gradually slows down and stops when the offset value makes the DDS output match the reference.

The OLED displays the current status:

DDS Offset zero-beat display

DDS Offset zero-beat display

The lurid red glow along the bottom is lens flare from the amber LED showing the relay is turned on. The slightly dimmer characters across the middle of the display show how the refresh interacts with the camera shutter at 1/30 s exposure.

N.B.: Normally, you know the DDS clock oscillator frequency with some accuracy. Dividing that value into 232 (for the AD9850) gives you the delta-phase count / frequency ratio that converts a desired DDS output frequency into the delta-phase value telling the DDS to make it happen.

In this case, I want the output frequency to be exactly 10.000000 MHz, so I’m adjusting the oscillator frequency (nominal 125 MHz + offset), calculating the corresponding count-to-Hz ratio, multiplying the ratio by 10.000000 MHz, stuffing the ensuing count into the DDS, and eyeballing what happens. When the oscillator frequency variable matches the actual oscillator frequency, then the actual output will 10.000000 MHz and the ratio will be correct.

Got it? Took me a while.

Although the intent is to tune for best frequency match and move on, you (well, I) can use this to accumulate a table of frequency offset vs. temperature pairs, from which a (presumably simple) formula can be conjured to render this step unnecessary.

The Arduino source code as a GitHub Gist:



1 Comment

Mailing Tube End Caps: Screw-in Version

The mailing tube arrived with contents intact, although the USPS inlet scanning didn’t work and the tube pretty much teleported across several states without leaving any tracking data behind. The recipient suggested several modifications to the caps:

Review of user experience of tube end:
The ribs on the endcap are very good at holding the cap on, so much so that I had to use a prying implement to remove it, which cracked the flange.
Would consider less depth on the cap, and possibly another layer on the flange.

Some continuous process improvement (a.k.a OpenSCAD hackage) produced a swoopy threaded cap with thumb-and-finger grips:

Mailing Tube Screw Cap - top - Slic3r

Mailing Tube Screw Cap – top – Slic3r

The finger grips are what’s left after stepping a sphere out of the cap while rotating it around the middle:

Mailing Tube Cap - finger grip construction

Mailing Tube Cap – finger grip construction

That worked out surprisingly well, with the deep end providing enough of a vertical-ish surface to push against.

The two hex holes fit a pin wrench, because the grips twist only one way: outward. The wrench eliminates the need for a flange, as you can now adjust the cap insertion before slathering packing tape over the ends. Man, I loves me some good late binding action!

A three-start thread seemed like overkill, but was quick & easy. The “thread form” consists of square rods sunk into the cap perimeter, with one edge sticking out:

Mailing Tube Cap - thread detail

Mailing Tube Cap – thread detail

They’re 1.05 times longer than the cap perimeter facets to make their ends overlap, although they’re not tapered like the ones in the broom handle dingus, because it didn’t (seem to) make any difference to the model’s manifoldhood.

Not needing any endcaps right now, I built one for show-n-tell:

Threaded mailing tube end cap - installed

Threaded mailing tube end cap – installed

The OpenSCAD source code as a GitHub Gist:



LF Crystal Tester: LM75 Temperature Sensor

A strip of NXP (née Philips plus Freescale, including the part of Motorola that didn’t become ON) LM75A I²C temperature sensors arrived from beyond the horizon. To see if they worked, I soldered thin wires directly to the SO-8 pins, entombed it in Kapton tape to prevent spitzensparken, and jammed it under the foam insulation atop the AD9850 DDS module:

AD9850 DDS module with LM75A Temperature Sensor

AD9850 DDS module with LM75A Temperature Sensor

This turns out to be easier than screwing around with thermistors, because the chip reports the temperature directly in Celcius with ⅛ °C resolution. Classic LM75 chips from National (now absorbed by TI) had ½ °C resolution, but the datasheet shows the bits have an easily extensible format:

LM75A Temperature Data Format

LM75A Temperature Data Format

Huh. Fixed-point data, split neatly on a byte boundary. Who’d’a thunk it?

There’s a standard Arduino library using, naturally enough, floating point numbers, but I already have big fixed point numbers lying around and, with the I²C hardware up & running from the X axis DAC and OLED display, this was straightforward:

Temp.fx_32.high =;
Temp.fx_32.low = (uint32_t) << 24;
u8x8.drawString(0,ln,"DDS C          ");
ln += 1;

The next-to-last line squirts the temperature through the serial port to make those nice plots.

Casually ignoring all I²C bus error conditions will eventually lead to heartache and confusion. In particular, the Basement Laboratory temperature must never fall below 0 °C, because I just plunk the two’s-complement temperature data into an unsigned fixed point number.

Which produces the next-to-bottom line:

DDS OLED with LM75 temperature

DDS OLED with LM75 temperature

Alas, the u8x8 font doesn’t include a degree symbol.

Given sufficient motivation, I can now calibrate the DDS output against the GPS-locked 10 MHz standard to get a (most likely) linear equation for the oscillator frequency offset as a function of temperature. The DDS module includes a comparator to square up its sine wave, so an XOR phase detector or something based on filtering the output of an analog switch might be feasible.



Mailing Tube End Caps

Faced with a need to send documents rolled up in a tube, rather than folded flat, I sawed off a suitable length of cardboard tube from the heap, then discovered a distinct lack of end caps.

Well, once again, it’s 3D printing to the rescue:

Mailing Tube Cap - top - Slic3r

Mailing Tube Cap – top – Slic3r

The small ribs probably don’t actually do anything, but seemed like a nice touch.

They’re somewhat less boring from the bottom:

Mailing Tube Cap - bottom - Slic3r

Mailing Tube Cap – bottom – Slic3r

The fancy spider supports that big flat top and provides some crush resistance. The flat flange should collect the edge of the packing tape wrapped around the ends.

A firm shove installs them, so the size worked out perfectly:

Mailing tube end cap - installed

Mailing tube end cap – installed

Add a wrap of tape to each end, affix the USPS label, and they went out with the next day’s mail, PETG hair and all.

The OpenSCAD source code as a GitHub Gist:

1 Comment

Proto Board Holder: Revised Screw Mounts

Improving the crystal tester’s (nonexistent) grounding requires a band of copper tape around the inside of the proto board holder. Rather than cut the tape lengthwise to fit the holder, a new one will be just tall enough:

Proto Board - 80x120 - revised inserts - Slic3r

Proto Board – 80×120 – revised inserts – Slic3r

While I was at it, I deleted the washer recesses, because those didn’t work out well, and fiddled the screw holes to put the inserts in from the bottom:

Proto Board - 80x120 - revised inserts - detail - Slic3r

Proto Board – 80×120 – revised inserts – detail – Slic3r

Although the overhang inside the holes will be ugly, I’ll epoxy the inserts flush with the bottom and nobody will ever know.

The copper tape now makes a tidy ground strap:

Crystal Tester - ground strap - rear

Crystal Tester – ground strap – rear

With a gap in the front to eliminate the obvious loop:

Crystal Tester - ground strap - front gap

Crystal Tester – ground strap – front gap

The OpenSCAD source code as a GitHub Gist:



128×64 OLED Display: I²C Timings

The OLED display has a noticeable delay between writing the first (double-size) line of text and the last line, which seemed odd:

White 128x64 OLED Display - crystal tester

White 128×64 OLED Display – crystal tester

The top trace in this scope shot goes high while the code begins the display update, which involves converting the variable to strings, the characters to bitmaps, then writing the data to the display:

IIC Timing - overall

IIC Timing – overall

The bottom trace shows I²C bus activity pretty much blots up all the time, with very little required for the computations in between the display writes for each text line.

Near the leading edge of the top trace, the code computes the new delta phase value and the X axis DAC output corresponding to that frequency:

TestCount.fx_64 = MultiplyFixedPt(ScanFreq,CtPerHz); // compute DDS delta phase
TestCount.fx_32.low = 0; // truncate count to integer
TestFreq.fx_64 = MultiplyFixedPt(TestCount,HzPerCt); // compute actual frequency

Temp.fx_64 = (DAC_MAX * (ScanFreq.fx_64 - ScanFrom.fx_64)) / ScanWidth.fx_32.high;
XAxisValue = Temp.fx_32.high;

WriteDDS(TestCount.fx_32.high); // set DDS to new frequency
XAxisDAC.setVoltage(XAxisValue,DAC_WR); // and set X axis to match

The burst in the top trace shows the five SPI writes to the DDS (one pulse per byte, with the hardware handling the serialization) and the bottom trace shows four I²C bus writes to the DAC:

IIC Timing - DDS to SPI - IIC to DAC

IIC Timing – DDS to SPI – IIC to DAC

A bit more detail shows writing each I²C byte to the DAC requires nine clock pulses (8 data, 1 ack):

IIC Timing - DDS to SPI - IIC to DAC detail

IIC Timing – DDS to SPI – IIC to DAC detail

The I²C bus ticks along at 400 kHz, with each byte requiring 33.4 µs (including the mandatory downtime around each burst), so the DAC update requires about 100 µs. The MCP4725 datasheet suggests a three byte “fast mode” write, but there’s not much point in doing so for my simple needs.

The display ticks along at the same pace with far more data.

In round numbers, the entire display update hits 6 text lines (1 double-height + 4 single-height) × 16 characters / line × 64 pixels / character = 6144 pixels.

The first scope shot shows the update requires something close to 90 ms, which allows for 2700 bytes = 90 ms / 33.4 µs, the equivalent of 21 k pixels. The SH1106 hardware includes an internal address counter, so there’s no need to transfer an address with each byte; I’m not sure where the factor-of-two overhead goes.

In order to get a faster update, there’s a definite need for lazy screen updates: no writes when there’s no change.

This probably doesn’t matter, because I can’t watch much faster, but it’s good to know the fancy fixed-point arithmetic isn’t the limiting factor.

1 Comment