Archive for April, 2013

Printed Chain Mail: Subtractive Model

This is a subtractive version of Zomboe’s Chainmail, built by removing chunks from a solid rectangle the size of one link:

Chain Mail Link - Subtractive

Chain Mail Link – Subtractive

Until what’s left is, indeed, a single link:

Chain Mail Link

Chain Mail Link

The pillars in the original model weren’t nearly large enough; Slic3r omitted them from the G-Code. They’re now as wide as the bars and √2 times that width long, which means they actually get a bit of fill.

Then a pair of nested loops replicates that link across the entire fabric:

Chain Mail Sheet

Chain Mail Sheet

That technique didn’t work with Skeinforge (because it sent the nozzle scampering all over each layer, knocking things loose) and it didn’t work with Slic3r 0.9.8 (because it had problems with bridges), but Slic3r 0.9.10, hot from github, produced good results:

Chain Mail - as built

Chain Mail – as built

There were some strings connecting adjacent links, but a few minutes with a flush cutter solved that. Retraction was 1 mm at 80 mm/s = 480 mm/min, which seems to work fine in other contexts, but adjacent links fell inside the 1 mm minimum distance setting I’d been using. That’s now down to 0.5 mm, which should suffice for nearly everything.

The M2 sounded like I was hitting it with a hammer: each of the 480 pillar layers (!) required a quick squirt and a retraction, followed by a 500 mm/s move. Worked fine and didn’t miss a step anywhere along the way.

A view from the bottom shows it really is flexy:

Chain Mail - bottom

Chain Mail – bottom

I used zero perimeter threads on these tiny links, which means you can see the ripply edges of the second layer that was crosswise to the length of the link bars. Next time, I’ll try one perimeter thread, which should smooth that out.

The links stuck to the glass like they were glued, which, indeed, they were: White Rain Unscented Extra Hold Hairspray in a pump bottle (either they didn’t have Maximum Hold pump spray or I couldn’t see it). I’m not a big fan of aerosol anything and decided to try wiping the stuff across the platform glass, rather than filling the air with a fine mist and getting some on the glass. Seems to work, but more examples are needed…

The Slic3r configuration:

; generated by Slic3r 0.9.10-dev on 2013-04-17 at 17:28:11
; layer_height = 0.25
; perimeters = 0
; top_solid_layers = 3
; bottom_solid_layers = 3
; fill_density = 0.10
; perimeter_speed = 100
; infill_speed = 100
; travel_speed = 500
; nozzle_diameter = 0.35
; filament_diameter = 1.73
; extrusion_multiplier = 0.9
; perimeters extrusion width = 0.40mm
; infill extrusion width = 0.40mm
; solid infill extrusion width = 0.40mm
; top infill extrusion width = 0.40mm

The OpenSCAD source code, with the platform marker cubes expanded to cover the M2’s glass plate:

// Chain Mail Sheet
// For Slic3r and M2 printer
// Ed Nisley KE4ZNU - Apr 2013

Layout = "Build";			// Link Build

//-------
//- Extrusion parameters must match reality!
//  Print with +0 shells and 3 solid layers

ThreadThick = 0.25;
ThreadWidth = 0.40;

HoleWindage = 0.2;

Protrusion = 0.1;			// make holes end cleanly

function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);

//-------
// Dimensions

BarWidth = 5 * ThreadWidth;
BarThick = 3 * ThreadThick;

LinkSquare = IntegerMultiple(13.0,ThreadWidth);
LinkHeight = 2*BarThick + 1*BarThick;

LinkOutDiagonal = LinkSquare*sqrt(2) - BarWidth;
LinkInDiagonal = LinkSquare*sqrt(2) - 2*(BarWidth/2 + BarWidth*sqrt(2));

echo("Outside diagonal: ",LinkOutDiagonal);

SheetSizeX = 75;
SheetSizeY = 100;

NumLinksX = 1 + floor(SheetSizeX / LinkOutDiagonal);
NumLinksY = 1 + floor(SheetSizeY / LinkOutDiagonal);

echo("Links X: ",NumLinksX," Y: ",NumLinksY);

LinkSpacing = 0.59 * LinkOutDiagonal;

//-------

module ShowPegGrid(Space = 10.0,Size = 1.0) {

  RangeX = floor(95 / Space);
  RangeY = floor(125 / Space);

	for (x=[-RangeX:RangeX])
	  for (y=[-RangeY:RangeY])
		translate([x*Space,y*Space,Size/2])
		  %cube(Size,center=true);

}

//-------
// Create basic link

module Link() {

	rotate(45)
		difference(convexity=2) {
			translate([0,0,LinkHeight/2]) {
				difference(convexity=2) {
					intersection() {		// outside shape
						cube([LinkSquare,LinkSquare,LinkHeight],center=true);
						rotate(45)
							cube([LinkOutDiagonal,LinkOutDiagonal,LinkHeight],center=true);
					}
					intersection() {		// inside shape
						cube([(LinkSquare - 2*BarWidth),(LinkSquare - 2*BarWidth),(LinkHeight + 2*Protrusion)],center=true);
						rotate(45)
							cube([LinkInDiagonal,LinkInDiagonal,(LinkHeight +2*Protrusion)],center=true);
					}
				}
			}
			for (i=[-1,1]) {				// create bars
				translate([0,-i*(sqrt(2)*BarWidth/2),BarThick])
					rotate(45 + 180*(i+1)/2)
						cube([LinkOutDiagonal,LinkOutDiagonal,LinkHeight]);
				translate([i*(sqrt(2)*BarWidth/2),0,-BarThick])
					rotate(135 + 180*(i+1)/2)
						cube([LinkOutDiagonal,LinkOutDiagonal,LinkHeight]);
			}
		}
}

//-------
// Build it!

ShowPegGrid();

if (Layout == "Link") {
  Link();
}

if (Layout == "Build") {
	for (ix=[-(NumLinksX/2 - 0):(NumLinksX/2 - 1)])
		for (iy=[-(NumLinksY/2 - 0):(NumLinksY/2 - 1)])
			translate([ix*LinkSpacing + LinkSpacing/2,iy*LinkSpacing + LinkSpacing/2,0])
				Link();
}

[Update: The original doodles, in case I ever need the background info:]

Chain Mail - Link Dimension Doodles

Chain Mail – Link Dimension Doodles

6 Comments

Makergear M2: Z-minimum Switch

The best orientation for the Z-minimum switch seems to be slightly angled back:

M2 - Z min limit switch

M2 – Z min limit switch

I used an M4x0.7 socket head cap screw for the height adjustment, with a Nylock nut below the stage:

M2 - Z min limit screw

M2 – Z min limit screw

The assembly instructions show a hex head screw, but the item numbers don’t match the BOM listings. The SHCS lets me hold it firmly in position with the ball-end driver provided in the M2 tool kit while adjusting it:

  • 1/4 turn (the handle is square-ish) = 0.7/4 = 0.175 mm
  • 1/6 turn (the shaft is hex) = 0.12 mm
  • 1/12 turn (you can do it!) = 0.06mm
  • less than that is probably fooling yourself.

I printed a pair of tomlombardi’s  7 mm wrenches, which work well for adjusting the Nylock nut underneath the Z axis stage:

M2 - 7 mm wrenches

M2 – 7 mm wrenches

The left end of the top wrench didn’t adhere to the glass plate, but the business end of the wrench came out OK.

I adjusted the screw to trip the switch with the nozzle 1.0 mm above the platform, then feed that offset in using a G92 Z1.0 instruction in my customized Start G-Code.

However, the most accurate way to set the switch height involves measuring the as-printed thickness of the skirt extrusion around the object. The average value should be 0.25 mm (for my current slic3r settings, anyhow) and all sides should be equally thick: adjust the screw to change the average and adjust the platform screws to remove any tilt. You’ll quickly accumulate a pile of skirt threads, but they make good tchotchkes when you give a presentation on your new toy:

M2 skirt extrusions

M2 skirt extrusions

You could fiddle with the G92 value to make the average thickness come out right, but I favor making the machine as accurate as possible, so that the software begins from a known-good mechanical setting.

,

4 Comments

CO2 Capsule Fins: 16 Gram Threaded Edition

I redesigned those fins to fit 16 gram threaded CO2 cartridges in PLA on the M2:

M2 CO2 capsule fins

M2 CO2 capsule fins

The original intent was to have both the square box and the internal X struts be exactly two threads wide, but the two fins on the sides show slic3r had some trouble doing that. I finally made them wide enough for a little fill, which produced the rather chunky version attached to the capsule.

A closer look while printing shows the fin width:

M2 - CO2 Capsule Fins - on platform

M2 – CO2 Capsule Fins – on platform

It was actually a present to go along with a box of the capsules, so I printed just one in a bit of a hurry. He probably couldn’t get them back across the border, but it’s the thought that counts, right?

The slic3r header:

; generated by Slic3r 0.9.8 on 2013-04-04 at 20:53:07

; layer_height = 0.25
; perimeters = 1
; top_solid_layers = 3
; bottom_solid_layers = 3
; fill_density = 0.10
; perimeter_speed = 100
; infill_speed = 200
; travel_speed = 500
; scale = 1
; nozzle_diameter = 0.35
; filament_diameter = 1.70
; extrusion_multiplier = 0.9
; perimeters extrusion width = 0.40mm
; infill extrusion width = 0.40mm
; first layer extrusion width = 0.39mm

The OpenSCAD source has dimensions for various capsules as commented-out cruft, so your mileage may vary:

// CO2 capsule tail fins
// Ed Nisley KE4ZNU - Apr 2013

Layout = "Build";            // Show Build FinBlock Cartridge Fit

//-------
//- Extrusion parameters must match reality!
//  Print with +0 shells and 3 solid layers

ThreadThick = 0.25;
ThreadWidth = 0.40;

HoleWindage = 0.2;

Protrusion = 0.1;            // make holes end cleanly

function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);

//-------
// Capsule dimensions

//- 12 gram capsule

/*
BodyDia = 18.70;
BodyRad = BodyDia/2;

BodyLength = 53.0;                        // between hemispherical endcap centers
BodyBaseLength = 21;                    // tip to endcap center

TipDia = 7.40;
TipRad = TipDia/2;
TipLength = IntegerMultiple(4.0,ThreadThick);

FilletLength = 5.0;                        // fillet between tip and cone
FilletTop = TipLength + FilletLength;

FilletBaseDia = 8.60;
FilletBaseRad= FilletBaseDia/2;
FilletTopDia = 9.5;
FilletTopRad = FilletTopDia/2;

ConeTop = 16.0;                            // tip to tangent with endcap
ConeLength = ConeTop - FilletTop;
*/

//- 16 gram capsule

/*-- unthreaded
BodyDia = 22.0;
BodyRad = BodyDia/2;

BodyLength = 53.0;                        // between hemispherical endcap centers
BodyBaseLength = 27;                    // tip to endcap center

TipDia = 8.30;
TipRad = TipDia/2;
TipLength = IntegerMultiple(7.4,ThreadThick);

FilletLength = 8.3;                        // fillet between tip and cone
FilletTop = TipLength + FilletLength;

FilletBaseDia = 8.6;
FilletBaseRad= FilletBaseDia/2;
FilletTopDia = 18.1;
FilletTopRad = FilletTopDia/2;

ConeTop = 23.0;                            // tip to tangent with endcap
ConeLength = ConeTop - FilletTop;
*/

/*-- threaded */
BodyDia = 22.0;
BodyRad = BodyDia/2;

BodyLength = 53.0;                        // between hemispherical endcap centers
BodyBaseLength = 27;                    // tip to endcap center

TipDia = 9.4;
TipRad = TipDia/2;
TipLength = IntegerMultiple(12.0,ThreadThick);

FilletLength = 5.0;                        // fillet between tip and cone
FilletTop = TipLength + FilletLength;

FilletBaseDia = TipDia;
FilletBaseRad= FilletBaseDia/2;
FilletTopDia = 15.1;
FilletTopRad = FilletTopDia/2;

ConeTop = 23.0;                            // tip to tangent with endcap
ConeLength = ConeTop - FilletTop;

echo(str("Cone Length: ",ConeLength));

IntersectZ = ConeTop;                    // coordinates of intersect tangent
IntersectX = sqrt(pow(BodyRad,2) - pow(BodyBaseLength - ConeTop,2));

echo(str("IntersectZ: ",IntersectZ));
echo(str("IntersectX: ",IntersectX," dia: ",2*IntersectX));

/* */

//-------
// Fin dimensions

FinThick = 2*ThreadWidth;            // outer square
StrutThick = 3*ThreadWidth;            // diagonal struts

FinSquare = 1.25*BodyDia;
FinTaperLength = sqrt(2)*FinSquare/2 - sqrt(2)*FinThick - ThreadWidth;

FinBaseLength = 1.5*TipLength;
FinFlatTop = ConeTop;

//-------

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);
}

module ShowPegGrid(Space = 10.0,Size = 1.0) {

Range = floor(50 / Space);

for (x=[-Range:Range])
for (y=[-Range:Range])
translate([x*Space,y*Space,Size/2])
%cube(Size,center=true);

}

//-------
// CO2 cartridge outline

module Cartridge() {

$fn = 48;

union() {
translate([0,0,BodyBaseLength]) {
cylinder(r=BodyDia/2,h=BodyLength);
translate([0,0,BodyLength])
sphere(r=BodyRad);
}

intersection() {
translate([0,0,BodyBaseLength])
sphere(r=BodyRad);

union() {
translate([0,0,(TipLength + FilletLength+ConeLength)])
cylinder(r=BodyRad,h=(BodyBaseLength - ConeLength));
translate([0,0,(TipLength + FilletLength)])
cylinder(r1=FilletTopRad,r2=IntersectX,h=(ConeLength + Protrusion));
translate([0,0,TipLength])
cylinder(r1=FilletBaseRad,r2=FilletTopRad,h=(FilletLength + Protrusion));
}

}

translate([0,0,FilletTop])
cylinder(r1=FilletTopRad,r2=IntersectX,h=ConeLength);

translate([0,0,TipLength])
cylinder(r1=FilletBaseRad,r2=FilletTopRad,h=(FilletLength + Protrusion));

translate([0,0,-Protrusion])
PolyCyl(TipDia,(TipLength + 2*Protrusion));

}

}

//-------
// Diagonal fin strut

module FinStrut() {
intersection() {
rotate([90,0,45])
translate([0,0,-StrutThick/2])
linear_extrude(height=StrutThick)
polygon(points=[
[0,0],
[FinTaperLength,0],
[FinTaperLength,FinBaseLength],
[0,(FinBaseLength + FinTaperLength)]
]);
translate([0,0,FinFlatTop/2])
cube([2*FinSquare,2*FinSquare,FinFlatTop], center=true);
}
}

//-------
// Fin outline

module FinBlock() {
$fn=12;
union() {
translate([0,0,FinBaseLength/2])
difference() {
intersection() {
minkowski() {
cube([FinSquare - 2*ThreadWidth,
FinSquare - 2*ThreadWidth,
FinBaseLength],center=true);
cylinder(r=FinThick,h=Protrusion,$fn=8);
}
cube([2*FinSquare,2*FinSquare,FinBaseLength],center=true);
}
difference() {
cube([(FinSquare - 2*FinThick),
(FinSquare - 2*FinThick),
(FinBaseLength + 2*Protrusion)],center=true);
for (Index = [0:3])
rotate(Index*90)
translate([(FinSquare/2 - FinThick),(FinSquare/2 - FinThick),0])
cylinder(r=2*StrutThick,h=(FinBaseLength + 2*Protrusion),center=true,$fn=16);
}
}
for (Index = [0:3])
rotate(Index*90)
FinStrut();
cylinder(r=IntegerMultiple(TipDia/2 + 4*ThreadWidth,ThreadWidth),h=TipLength);
}
}

//-------
// Fins

module FinAssembly() {

difference() {
FinBlock();
translate([0,0,2*ThreadThick])                // add two layers to close base cylinder
Cartridge();
}

}

module FinFit() {

translate([0,0.75*BodyBaseLength,2*ThreadThick])
rotate([90,0,0])
difference() {
translate([-FinSquare/2,-2*ThreadThick,0])
cube([IntegerMultiple(FinSquare,ThreadWidth),
4*ThreadThick,
1.5*BodyBaseLength]);
translate([0,0,5*ThreadWidth])
Cartridge();
}

}

//-------
// Build it!

ShowPegGrid();

if (Layout == "FinStrut")
FinStrut();

if (Layout == "FinBlock")
FinBlock();

if (Layout == "Cartridge")
Cartridge();

if (Layout == "Show") {
FinAssembly();
color("LightYellow") Cartridge();
}

if (Layout == "Fit")
FinFit();

if (Layout == "Build")
FinAssembly()

8 Comments

Dual Monitors: devilspie2 FTW!

Because it seems there’s no good support for separate X sessions with dual monitors these days, the landscape and portrait monitors on my desk represent viewports into a larger pixel array within a single X session. As a consequence, it’s entirely possible to slide windows across the gutter between the two displays (generally producing an essentially unusable result), but one cannot flip through workspaces on only one monitor.

Worse, some programs seem to have trouble remembering that they were last seen on the portrait monitor, so I must rearrange the windows at the start of every session. First world problem, yeah, but still annoying.

I’d previously used devilspie to force windows to their proper places across monitors, sessions, and workspaces, but its s-expression syntax was impenetrable and I eventually gave up using it.

A fork (or continuation or something) called devilspie2 uses lua scripts that I can both read and write. It’s an Ubuntu package and easy to set up.

A typical script in ~/.config/devilspie2 looks like this:

if (get_application_name()=="Firefox") then
   set_window_geometry(0,0,1300,1200);
   maximize_vertically();
end

Putting Adobe Reader on the portrait monitor looks about the same:

if (get_application_name()=="acroread") then
   unmaximize();
   set_window_geometry(2561,0,1000,100);
   maximize();
end

Set /usr/bin/devilspie2 as an auto-started program and it Just Works…

6 Comments

Dual Monitors: Dual-Link DVI + HDMI

Finally, this Just Worked…

The problem was to get both a landscape and a portrait monitor working. The complexity came from the landscape monitor, a 2560×1440 Dell U2711, which requires either a Dual-Link DVI or HDMI input to reach full resolution. The portrait monitor, an old 1608×1050 Dell 2005FPW, uses Single-Link DVI.

The previous attempt with an nVidia GT430 card failed: both DVI outputs were Single-Link, despite their connector’s appearance. It had worked OK with the previous landscape monitor, a 1600×1200 Dell 2001FP.

So I picked up a low-end nVidia GT610 card that advertised both Dual-Link DVI and HDMI. I plugged the DVI cable into the U2711 and it lit up at full resolution, then I plugged the HDMI into the 2005FPW and it lit right up. By some miracle that didn’t involve tweaking an xorg.conf file, the 2005FPW immediately displayed the pixels lying to the right of the U2711, exactly as I wanted; all I had to do was rotate the image using xrandr.

Not only that, but the version of xsetwacom in Xubuntu 12.10 once again respects the MapToOutput "HEAD-0" option that restricts the stylus to the U2711 monitor. That means I can’t put the GIMP toolbars and suchlike on the portrait monitor, but the U2711 has enough dots to render that moot.

Although the Dell doc strongly suggests that the on-board VGA and Displayport outputs don’t work with a PCI-E video card plugged in, xrandr reports:

Screen 0: minimum 8 x 8, current 3610 x 1680, maximum 16384 x 16384
DVI-I-0 disconnected (normal left inverted right x axis y axis)
VGA-0 disconnected (normal left inverted right x axis y axis)
DVI-I-1 connected 2560x1440+0+0 (normal left inverted right x axis y axis) 597mm x 336mm
   2560x1440      60.0*+
   1920x1200      59.9
   1680x1050      60.0
   1600x1200      60.0
   1280x1024      75.0     60.0
   1280x800       59.8
   1152x864       75.0
   1024x768       75.0     60.0
   800x600        75.0     60.3
   640x480        75.0     59.9
HDMI-0 connected 1050x1680+2560+0 left (normal left inverted right x axis y axis) 434mm x 270mm
   1680x1050      59.9*+
   1280x1024      75.0     60.0
   1152x864       75.0
   1024x768       75.0     60.0
   800x600        75.0     60.3
   640x480        75.0     59.9

I’m not going to connect the DVI-I-0 and VGA-0 outputs; my physical desk has no room for more pixels…

The ~/.xprofile file now looks like:

setxkbmap -option terminate:ctrl_alt_bksp
xrandr --output HDMI-0 --rotate left
xrandr --dpi 100x100
xsetwacom --verbose set "Wacom Graphire3 6x8 stylus" MapToOutput "HEAD-0"
xsetwacom --verbose set "Wacom Graphire3 6x8 eraser" MapToOutput "HEAD-0"

Phew…

4 Comments

Captchas Gone Wild

This is probably a browser compatibility issue:

Captcha 1

Captcha 1

Let’s refresh the page to see if a better combination comes up:

Captcha 2

Captcha 2

I took that to mean I’m not sufficiently human to post a comment…

8 Comments

Makergear M2: Filament Guide Tube Friction

While changing to black filament, I measured the force required to pull the (natural PLA) filament through the translucent guide tube arching over the M2’s chassis from the spool to the extruder:

M2 Electronics Case on chassis

Makergear M2 3D Printer with cardboard on build platform

A strike-anywhere kitchen match (bet you can’t buy those any more!) provided more than enough heat to bend the end of the filament into a loop suitable for the pull scale:

M2 - Filament loop for pull test

M2 – Filament loop for pull test

The results:

  • Tube reasonably straight: 0.5 lb = 2.2 N
  • Tube arched to middle of X axis: 1 lb = 4.5 N
  • Tube sharply bent to X axis nearest spool: 1.5 lb = 6.7 N

The force increases slightly while tugging filament off the spool, as the spool does not rotate freely on the printed arm jutting out from the frame, but those numbers are in the right ballpark.

The effective diameter of the extruder drive gear is about 11.5 mm, so overcoming the tube friction requires somewhere between 10 and 40 mN·m of torque. That’s applied at the one point in the whole system most likely to show the result of uneven loading, because it directly affects the pressure of the molten plastic behind the nozzle.

That’s considerable motivation to get rid of the filament guide tube…

4 Comments