Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.
Category: Software
General-purpose computers doing something specific
Given an STL file generated from a height map image, import it into OpenSCAD:
SqWr solid model – OpenSCAD – oblique view
Then slide a plate under six copies to produce a positive model for a casting mold:
SqWr Positive Mold Framework – 2×3
This is one of the few cases where the compiled-and-rendered version looks better, as though you’d shrink-wrapped it in gold foil:
SqWr Positive Mold Framework – 2×3 – gold
The height map STLs each have a bazillion tiny facets that take forever-and-a-day (well, the better part of half an hour for this set) to render, not to mention that the whole array would take two hours to print… and then be used once or twice to produce the flexy silicone negative mold.
So it’s better to have a generic frame with alignment pin holes that you print once:
SqWr Positive Mold Framework – 2×3 pins
Better yet, just CNC-drill those holes in a nice, flat acrylic / polycarbonate slab.
The OpenSCAD program can punch matching holes in the back of the small mold:
SqWr solid model – OpenSCAD – oblique bottom
Or you could print out an array of the things with holes:
SqWr solid model – 2×3 array – bottom
It’s not clear having OpenSCAD labor for half an hour to generate and emit a single STL file spanning all six molds is a win. Given that you don’t care about the mold-to-mold spacing, having Slic3r duplicate the same small STL file half a dozen (or more!) times would probably be a net win.
There’s no reason the OpenSCAD program that creates the original STL from the height map image can’t punch alignment pin holes, too, which would avoid this import-and-recompile step. If you’re going with a CNC-drilled plate, then it would make even more sense to not have a pair of OpenSCAD programs.
Anyhow.
Apply a handful of small molds to the backing plate with tapeless sticky, butter it up with mold release agent, slather on silicone putty, flip it over to produce a smooth surface “under” the small molds (so you can rest it flat on a table when pouring molten chocolate into the cavities), cure, peel, and you’d get a pretty good negative mold.
This may not make any practical sense, but it was easy & fun to see what’s possible…
The OpenSCAD source code:
// Positive mold framework for chocolate slabs
// Ed Nisley - KE4ZNU - January 2014
Layout = "FramePins"; // Molds FramePins FrameMolds Frame Single Pin
//- Extrusion parameters must match reality!
// Print with 2 shells and 3 solid layers
ThreadThick = 0.20;
ThreadWidth = 0.40;
Protrusion = 0.1; // make holes end cleanly
HoleWindage = 0.2;
//----------------------
// Dimensions
FileName = "SqWr-press.stl"; // overrride with -D
Molds = [2,3]; // count of molds within framework
MoldOC = [40.0,40.0]; // on-center spacing of molds
MoldSlab = 1.0; // thickness of slab under molds
BaseThick = 5.0;
BaseSize = [(Molds[0]*MoldOC[0] + 0),(Molds[1]*MoldOC[1] + 0),BaseThick];
echo(str("Overall base: ",BaseSize));
PinOD = 1.75; // locating pin diameter
PinLength = 2.0; // ... total length
PinSpace = 15.0; // spacing within mold item
//----------------------
// Useful routines
//- Put peg grid on build surface
module ShowPegGrid(Space = 10.0,Size = 1.0) {
RangeX = floor(100 / Space);
RangeY = floor(125 / Space);
for (x=[-RangeX:RangeX])
for (y=[-RangeY:RangeY])
translate([x*Space,y*Space,Size/2])
%cube(Size,center=true);
}
module PolyCyl(Dia,Height,ForceSides=0) { // based on nophead's polyholes
Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2);
FixDia = Dia / cos(180/Sides);
cylinder(r=(FixDia + HoleWindage)/2,
h=Height,
$fn=Sides);
}
// Locating pin hole with glue recess
// Default length is two pin diameters on each side of the split
module LocatingPin(Dia=PinOD,Len=0.0) {
PinLen = (Len != 0.0) ? Len : (4*Dia);
translate([0,0,-ThreadThick])
PolyCyl((Dia + 2*ThreadWidth),2*ThreadThick,4);
translate([0,0,-2*ThreadThick])
PolyCyl((Dia + 1*ThreadWidth),4*ThreadThick,4);
translate([0,0,-(Len/2 + ThreadThick)])
PolyCyl(Dia,(Len + 2*ThreadThick),4);
}
module LocatingPins(Length) {
for (i=[-1,1])
translate([i*PinSpace/2,0,0])
LocatingPin(Len=Length);
}
//-- import a single mold item
module MoldItem() {
import(FileName,convexity=10);
}
//-- Overall frame shape
module Frame() {
translate([0,0,BaseSize[2]/2]) // platform under molds
cube(BaseSize,center=true);
}
//- Build it
ShowPegGrid();
if (Layout == "Pin")
LocatingPin(Len=PinLength);
if (Layout == "Single")
difference() {
MoldItem();
LocatingPins(PinLength);
}
if (Layout == "Frame")
Frame();
if (Layout == "Molds") {
translate([-MoldOC[0]*(Molds[0] - 1)/2,-MoldOC[1]*(Molds[1] - 1)/2,0])
for (i=[0:Molds[0]-1],j=[0:Molds[1]-1])
translate([i*MoldOC[0],j*MoldOC[1],0])
difference() {
MoldItem();
LocatingPins(PinLength);
}
}
if (Layout == "FramePins")
difference() {
Frame();
translate([-MoldOC[0]*(Molds[0] - 1)/2,-MoldOC[1]*(Molds[1] - 1)/2,0])
for (i=[0:Molds[0]-1],j=[0:Molds[1]-1])
translate([i*MoldOC[0],j*MoldOC[1],BaseSize[2]])
LocatingPins(BaseThick);
}
if (Layout == "FrameMolds") {
Frame();
translate([-MoldOC[0]*(Molds[0] - 1)/2,-MoldOC[1]*(Molds[1] - 1)/2,0])
for (i=[0:Molds[0]-1],j=[0:Molds[1]-1])
translate([i*MoldOC[0],j*MoldOC[1],BaseThick - MoldSlab + Protrusion])
MoldItem();
}
Given that you really don’t care about the absolute dimensions, you can generate a positive mold from a height map image and avoid the entire solid modeling process. Having already solved the cookie press problem, this was a quick-and-easy feasibility study…
Start by selecting the logo, growing the selection by a few pixels, and feathering the edges to produce the mold draft. Then apply a square gradient behind the Squidwrench logo to produce the height map for the edge of the mold. This one is scaled at 3.0 pixel/mm and is 100×100 pixel, thus producing a 33 mm square mold:
One could, of course, produce a non-square mold with a different gradient outline shape.
Hand the image to a slightly modified version of the cookie press script (see below) to get an STL file of the mold:
SqWr solid model – oblique view
Feed the STL into Slic3r, hand the G-Code to Pronterface, fire the M2!, and you get a positive mold that looks enough like black chocolate to seem ready-to-eat:
SqWr – mold positive
I have no idea whether that will work as a mold, but I suspect flexy silicone putty won’t reproduce much of the fine plastic filament detail, so the negative mold won’t grab the chocolate. The logo is six threads deep with a little bit of draft, if that makes any difference.
The backing plate is 1 mm thick and the height map is 5 mm stacked atop that. A few iterations suggested using about 0.75 gray for the logo; working backwards says 5 mm = 25 layers @ 0.20 mm/layer, so a depth of 0.25 * 25 is about six threads.
For production use, I’d be tempted to import maybe a dozen copies of the STL into OpenSCAD, mount them on a platform with a gutter and a lip on the outside, and then print the whole positive multi-cavity mold in one shot.
The Bash script that produces the mold strongly resembles my cookie cutter script and contains about as much cruft as you’d expect. Because we need a positive mold, not a negative press, the script doesn’t invert the colors or flop the image left-to-right, nor does it generate the cookie cutter STL around the outside of the press:
The avconv (formerly ffmpeg) image-to-video programs expect sequentially numbered files, with the numbers in a fixed-width part of the file name, thusly: dsc00001.jpg.
ll | head
total 286576
-rwxr-xr-x 1 ed ed 595708 Jan 23 19:14 dsc00940.jpg
-rwxr-xr-x 1 ed ed 515561 Jan 23 19:14 dsc00941.jpg
-rwxr-xr-x 1 ed ed 580190 Jan 23 19:14 dsc00942.jpg
-rwxr-xr-x 1 ed ed 571387 Jan 23 19:14 dsc00943.jpg
-rwxr-xr-x 1 ed ed 573207 Jan 23 19:14 dsc00944.jpg
-rwxr-xr-x 1 ed ed 571086 Jan 23 19:14 dsc00945.jpg
-rwxr-xr-x 1 ed ed 571600 Jan 23 19:14 dsc00946.jpg
-rwxr-xr-x 1 ed ed 571547 Jan 23 19:14 dsc00947.jpg
-rwxr-xr-x 1 ed ed 565706 Jan 23 19:15 dsc00948.jpg
A Bash one-liner loop does the renumbering:
sn=1 ; for f in *jpg ; do printf -v dn 'dsc%05d.jpg' "$(( sn++ ))" ; mv $f $dn ; done
The results look pretty much like you’d expect:
ll | head
total 286556
-rwxr-xr-x 1 ed ed 595708 Jan 23 19:14 dsc00001.jpg
-rwxr-xr-x 1 ed ed 515561 Jan 23 19:14 dsc00002.jpg
-rwxr-xr-x 1 ed ed 580190 Jan 23 19:14 dsc00003.jpg
-rwxr-xr-x 1 ed ed 571387 Jan 23 19:14 dsc00004.jpg
-rwxr-xr-x 1 ed ed 573207 Jan 23 19:14 dsc00005.jpg
-rwxr-xr-x 1 ed ed 571086 Jan 23 19:14 dsc00006.jpg
-rwxr-xr-x 1 ed ed 571600 Jan 23 19:14 dsc00007.jpg
-rwxr-xr-x 1 ed ed 571547 Jan 23 19:14 dsc00008.jpg
-rwxr-xr-x 1 ed ed 565706 Jan 23 19:15 dsc00009.jpg
Because you’re renaming the files anyway, don’t bother to normalize ’em:
sn=1 ; for f in *JPG ; do printf -v dn 'dsc%05d.jpg' "$(( sn++ ))" ; mv $f $dn ; done
And, of course, you can fetch ’em from the camera while doing that:
sn=1 ; for f in /mnt/part/DCIM/100MSDCF/*JPG ; do printf -v dn 'dsc%05d.jpg' "$(( sn++ ))" ; cp -a $f $dn ; done
That leaves the DSC*JPG original files on the camera, where you can delete all of them in one operation when you’re happy with the results.
If you don’t need the full resolution, reserialize and resize each picture on the fly:
sn=1 ; for f in /mnt/part/DCIM/100MSDCF/*JPG ; do printf -v dn 'dsc%05d.jpg' "$(( sn++ ))" ; convert $f -resize 50% $dn ; done
That’s based on combining several hints turned up by the usual Google search.
To assemble a quick-and-simple movie from the images:
avconv -r 30 -i dsc%05d.jpg -q 5 movie.mp4
The image quality certainly isn’t up to what you (well, I) would expect from a 1920×1080 “HD” file, but the Sony HDR-AS30V Zeiss camera lens seems to be a fisheye pinhole (170° view angle, 2.5 mm f/2.8) backed with relentless image compression:
Sony HDR-AS30V Action Camera
Memo to Self: It’s not worth creating and remembering Yet Another Script.
While converting a stop-action series of images from the HDR-AS30V into a movie, I wanted change all the image files on a USB Flash drive from DSC00008.JPG to dsc00008.jpg, so as to simplify typing their names.
Alas, because the camera’s exFAT filesystem cares not one whit about case, the obvious command doesn’t work:
rename 's/JPG/jpg/' /mnt/part/*
/mnt/part/DSC00008.JPG not renamed: /mnt/part/DSC00008.jpg already exists
The Sony HDR-AS30V “action camera” uses NP-BX1 lithium batteries (3.7 V @ 1.24 A·h = 4.6 W·h) that are, of course, a completely different size and shape than any other lithium battery on the planet.
So.
Tweaking a few dimensions in the Canon NB-6L source code, tinkering with the layout of the contact pins, and shazam Yet Another 3D Printed Battery Test Fixture:
NP-BX1 Holder – show layout
It builds nicely, although the contact pin tunnels are a bit too close to the top of the case:
Sony NP-BX1 Holder – on platform
After reaming out the contact pin holes to the proper diameters & depths, then gluing the plugs in place, it works just as you’d expect:
Sony NP-BX1 battery holder
It’s worth noting that the Wasabi charger accepts the batteries upside-down, with the conspicuous chevron against the charger body. It’s definitely not the way all the other chargers work. The keying recesses on the battery (corresponding to the blocks in the solid model) lie along the bottom edge of the contact surface, so flipping the battery over means they’ll hold it in place, but … oh, well.
That grotty Powerpole connector last saw use in some random benchtop lashup. At some point I’ll be forced to start making more of those.
It turns out that the audio-over-HDMI/DisplayPort channel which, for whatever reason, is the only way to get audio out of the Optiplex 980 with the big Dell U2711 monitor starts up AT MAXIMUM VOLUME! regardless of the GUI’s Pulseaudio mixer setting that’s diligently saved-and-restored across sessions. That makes a certain perverse sense, as the digital-to-analog converter & power amp live inside the monitor.
Manually adjusting the GUI mixer by one click, either up or down, forces the new setting out over the digital link to the monitor, after which the audio output corresponds to the mixer; I never remember that until just after some dipshit auto-play video lights up with a fanfare.
Setting the mixer to the same value doesn’t force an update, so the obvious solution (at least to me) of sending a fixed initial value doesn’t work; it’s optimized away. I think that’s why the initial update doesn’t happen: the stored volume is the same as the, ah, stored volume, so there’s no need to tell the monitor.
The automatic solution involves putting two more commands in my ever-growing ~/.config/startup.sh:
That sets a rational level (which might be the same as the existing one from the previous session), then changing it by one tiny click to force the new value out to the monitor.