Spotted in an arboretum:

How about good old “Keep Out”?
I’m not sure what the “do not touch” icon is supposed to mean, other than a lack of “no entry” icons.
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.
Growing and sometimes fixing
Spotted in an arboretum:

How about good old “Keep Out”?
I’m not sure what the “do not touch” icon is supposed to mean, other than a lack of “no entry” icons.
An outlet thread failed on yet another garden hose Y valve:

Out of an abundance of curiosity, I battered the remaining parts out of the carcass:

One of these days, we must buy an assortment of new fittings …

A pair of Step2 rolling garden seats (they have a new version) served in Mary’s gardens long enough to give their seat panels precarious cracks:
The underside was giving way, too:
We agreed the new seat could be much simpler, although it must still hinge upward, so I conjured a pair of hinges from the vasty digital deep:
The woodpile disgorged a slab of 1/4 inch = 6 mm plywood (used in a defunct project) of just about the right size and we agreed a few holes wouldn’t be a problem for its projected ahem use case:
The screw holes on the hinge tops will let me run machine screws all the way through, should that be necessary. So far, a quartet of self-tapping sheet metal (!) screws are holding firm.
A closer look at the hinges in real life:
The solid model now caps the holes; I can drill them out should the need arise.
From the bottom:
Three coats of white exterior paint make it blindingly bright in the sun, although we expect a week or two in the garden will knock the shine right off:
After the first coat, I conjured a drying rack from a bamboo skewer, a cardboard flap, and some hot-melt glue:

Three small scars on the seat bottom were deemed acceptable.
The OpenSCAD source code as a GitHub Gist:
| // Hinge brackets for rolling garden stool | |
| // Ed Nisley – KE4ZNU – 2019-06 | |
| Layout = "Build"; // [Block,Build,Show] | |
| Support = true; | |
| /* [Hidden] */ | |
| ThreadThick = 0.20; | |
| ThreadWidth = 0.40; | |
| HoleWindage = 0.2; | |
| Protrusion = 0.1; // make holes end cleanly | |
| ID = 0; | |
| OD = 1; | |
| LENGTH = 2; | |
| //———————- | |
| // Dimensions | |
| SeatThick = 6.0; // seat panel above cart body | |
| HingePin = [11.5,12.0,7.0]; // ID = tip OD = base | |
| HingeOffset = 8.0; // hinge axis above cart body (larger than radius!) | |
| HingeBolster = [5.0,24.0,SeatThick]; // backing block below hinge | |
| Block = [25.0,HingeOffset + 30.0,23.0]; // Z = above cart body | |
| Screw = [3.8,11.0,2.5]; // self-tapping #8 OD=head LENGTH=head thickness | |
| ScrewOC = 15.0; // spacing > greater than head OD | |
| ScrewOffset = Block.y/2 – (ScrewOC/2 + Screw[OD]/2 + HingeOffset); // space for head behind hinge | |
| BlockRadius = 7.0; // corner rounding | |
| //———————- | |
| // Useful routines | |
| 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); | |
| } | |
| // Basic block shape | |
| // X axis collinear with hinge axes, hinge base at X=0 | |
| module HingeBlock() { | |
| PinSides = 3*4; | |
| PinSupport = [HingePin[LENGTH] – 2*ThreadWidth,0.6*HingeOffset,HingePin[OD]]; // pre-rotated | |
| union() { | |
| translate([Protrusion,Block.y/2 – HingeOffset,HingeOffset]) | |
| rotate([0,-90,0]) | |
| rotate(180/PinSides) | |
| cylinder(d=HingePin[OD],h=HingePin[LENGTH] + Protrusion,$fn=PinSides); | |
| difference() { | |
| hull() { | |
| translate([Block.x – BlockRadius,-(Block.y/2 – BlockRadius),Block.z – BlockRadius]) | |
| rotate(180/PinSides) | |
| sphere(r=BlockRadius/cos(180/PinSides),$fn=PinSides); | |
| translate([0,-(Block.y/2 – BlockRadius),Block.z – BlockRadius]) | |
| rotate([0,90,0]) rotate(180/PinSides) | |
| cylinder(r=BlockRadius/cos(180/PinSides),h=Block.x/2,$fn=PinSides); | |
| translate([Block.x – BlockRadius,(Block.y/2 – BlockRadius),Block.z – BlockRadius]) | |
| sphere(r=BlockRadius/cos(180/PinSides),$fn=PinSides); | |
| translate([0,(Block.y/2 – BlockRadius),Block.z – BlockRadius]) | |
| rotate([0,90,0]) rotate(180/PinSides) | |
| cylinder(r=BlockRadius/cos(180/PinSides),h=Block.x/2,$fn=PinSides); | |
| translate([0,-Block.y/2,0]) | |
| cube([Block.x,Block.y – HingeOffset,Block.z/2],center=false); | |
| translate([0,Block.y/2 – HingeOffset,HingeOffset]) | |
| rotate([0,90,0]) rotate(180/PinSides) | |
| cylinder(r=HingeOffset/cos(180/PinSides),h=Block.x,$fn=PinSides); | |
| } | |
| translate([Block.x/2 + HingeBolster.x,0,(SeatThick – Protrusion)/2]) | |
| cube([Block.x,2*Block.y,SeatThick + Protrusion],center=true); | |
| translate([0,-HingeBolster.y,(SeatThick – Protrusion)/2]) | |
| cube([3*Block.x,Block.y,SeatThick + Protrusion],center=true); | |
| for (j=[-1,1]) | |
| translate([Block.x/2,j*ScrewOC/2 + ScrewOffset,-4*ThreadThick]) | |
| rotate(180/8) | |
| PolyCyl(Screw[ID],Block.z,8); | |
| } | |
| } | |
| if (Support) { // totally ad-hoc | |
| color("Yellow") render(convexity=4) | |
| difference() { | |
| translate([-(PinSupport.x/2 + 2*ThreadWidth),Block.y/2 – PinSupport.y/2,HingeOffset]) | |
| cube(PinSupport,center=true); | |
| translate([Protrusion,Block.y/2 – HingeOffset,HingeOffset]) | |
| rotate([0,-90,0]) | |
| rotate(180/PinSides) | |
| cylinder(d=HingePin[OD] + 2*ThreadThick,h=2*HingePin[LENGTH],$fn=PinSides); | |
| for (i=[-1:1]) | |
| translate([i*4*ThreadWidth – HingePin[LENGTH]/2, | |
| Block.y/2 – (PinSupport.y + 1*ThreadThick), | |
| HingeOffset]) | |
| cube([2*ThreadWidth,2*PinSupport.y,2*PinSupport.z],center=true); | |
| } | |
| } | |
| } | |
| module Blocks(Hand = "Left") { | |
| if (Hand == "Left") | |
| HingeBlock(); | |
| else | |
| mirror([1,0,0]) | |
| HingeBlock(); | |
| } | |
| //- Build it | |
| if (Layout == "Block") | |
| HingeBlock(); | |
| if (Layout == "Show") { | |
| translate([1.5*HingePin[LENGTH],0,0]) | |
| Blocks("Left"); | |
| translate([-1.5*HingePin[LENGTH],0,0]) | |
| Blocks("Right"); | |
| } | |
| if (Layout == "Build") { | |
| translate([0,-Block.z/2,Block.y/2]) | |
| rotate([-90,0,0]) { | |
| translate([1.5*HingePin[LENGTH],0,0]) | |
| Blocks("Left"); | |
| translate([-1.5*HingePin[LENGTH],0,0]) | |
| Blocks("Right"); | |
| } | |
| } |
This original doodle gives the key dimensions, apart from the rounded rear edge required so the seat can pivot vertically upward:
The second seat looks just like this one, so life is good …
Two of Mary’s garden soaker hoses failed their pre-installation checks with leaks from around their connectors. The problem seemed to be a break in the hose inside the connector, with water spewing out of the connector around the hose. Having previously fixed a gash in another hose, I figured I might have some success at fixing these leaks.
The general idea is to squish enough silicone rubber inside the connector to seal around the hose, then clamp the hose and connector snugly enough to hold the rubber in place:

The enlarged recess fits around the brass connector shell, which is squashed loosely around the hose and from which the leaking water emerges. Of course, because this is a different hose, the previous model didn’t quite fit and I had to doodle up new geometry:
As before, I bandsawed aluminum backing plates to ensure the plastic didn’t get all bendy in the middle:
The hose clamp (!) around the connector on the far right ensures a split in the brass shell doesn’t get any larger.
They’ll spend the rest of their lives under the garden mulch, where nobody will ever see those bulky lumps. Life is good!
The OpenSCAD source code as a GitHub Gist:
| // Rubber Soaker Hose End Connector Clamp | |
| // Helps hold silicone rubber in connector | |
| // Ed Nisley KE4ZNU June 2019 | |
| Layout = "Build"; // [Hose,Connector,Block,Show,Build] | |
| //- Extrusion parameters must match reality! | |
| /* [Hidden] */ | |
| ThreadThick = 0.25; | |
| ThreadWidth = 0.40; | |
| HoleWindage = 0.2; | |
| Protrusion = 0.1; // make holes end cleanly | |
| inch = 25.4; | |
| function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit); | |
| //———- | |
| // Dimensions | |
| // Hose lies along X axis | |
| Hose = [200,26.5,11.6]; // X=very long, Y=width, Z=overall height | |
| RimThick = 10.3; // outer sections | |
| RimOD = RimThick; | |
| RimFlatRecess = 1.0; // recess to front flat surface | |
| OuterOC = Hose.y – RimOD; // outer tube centers | |
| RecessM = 0.8; // back recess chord | |
| RecessC = OuterOC; | |
| RecessR = (pow(RecessM,2) + pow(RecessC,2)/4) / (2*RecessM); | |
| RidgeM = 1.6; // front ridge chord | |
| RidgeC = 7.5; | |
| RidgeR = (pow(RidgeM,2) + pow(RidgeC,2)/4) / (2*RidgeM); | |
| HoseSides = 12*4; | |
| Connector = [5.0,33.0,13.0]; // oval brass: X=snout Y=width Z=dia | |
| Block = [20.0,50.0,4.0 + Hose.z]; // overall splice block size | |
| echo(str("Block: ",Block)); | |
| Kerf = 0.5; // cut through middle to apply compression | |
| ID = 0; | |
| OD = 1; | |
| LENGTH = 2; | |
| // 8-32 stainless screws | |
| Screw = [4.1,8.0,3.0]; // OD = head LENGTH = head thickness | |
| Washer = [4.4,9.5,1.0]; | |
| Nut = [4.1,9.7,6.0]; | |
| CornerRadius = Washer[OD]/2; | |
| ScrewOC = Block.y – 2*CornerRadius; | |
| echo(str("Screw OC: x=",ScrewOC.x," y=",ScrewOC.y)); | |
| //———————- | |
| // Useful routines | |
| module PolyCyl(Dia,Height,ForceSides=0) { // based on nophead's polyholes | |
| Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2); | |
| FixDia = Dia / cos(180/Sides); | |
| cylinder(d=(FixDia + HoleWindage),h=Height,$fn=Sides); | |
| } | |
| // Hose shape | |
| // This includes magic numbers measured from reality | |
| module HoseProfile() { | |
| rotate([0,-90,0]) | |
| translate([0,0,-Hose.x/2]) | |
| linear_extrude(height=Hose.x,convexity=4) | |
| difference() { | |
| union() { | |
| for (j=[-1,1]) // outer channels | |
| translate([0,j*OuterOC/2]) | |
| circle(d=RimOD,$fn=HoseSides); | |
| translate([-RimOD/4,0]) // rear flat fill | |
| square([RimOD/2,OuterOC],center=true); | |
| translate([(RimOD/4 – RimFlatRecess),0]) // front flat fill | |
| square([RimOD/2,OuterOC],center=true); | |
| intersection() { | |
| translate([Hose.z/2,0]) | |
| square([Hose.z,OuterOC],center=true); | |
| translate([-RidgeR + RimOD/2 – RimFlatRecess + RidgeM,0]) | |
| circle(r=RidgeR,$fn=HoseSides); | |
| } | |
| } | |
| translate([-(RecessR + RimOD/2 – RecessM),0]) | |
| circle(r=RecessR,$fn=2*HoseSides); | |
| } | |
| } | |
| // Outside shape of splice Block | |
| // Z centered on hose rim circles, not overall thickness through center ridge | |
| module SpliceBlock() { | |
| difference() { | |
| hull() | |
| for (i=[-1,1], j=[-1,1]) // rounded block | |
| translate([i*(Block.x/2 – CornerRadius),j*(Block.y/2 – CornerRadius),-Block.z/2]) | |
| cylinder(r=CornerRadius,h=Block.z,$fn=4*8); | |
| for (j=[-1,1]) // screw holes | |
| translate([0, | |
| j*ScrewOC/2, | |
| -(Block.z/2 + Protrusion)]) | |
| PolyCyl(Screw[ID],Block.z + 2*Protrusion,6); | |
| cube([2*Block.x,2*Block.y,Kerf],center=true); // slice through center | |
| } | |
| } | |
| // Splice block less hose | |
| module ShapedBlock() { | |
| difference() { | |
| SpliceBlock(); | |
| HoseProfile(); | |
| Connector(); | |
| } | |
| } | |
| // Brass connector end | |
| module Connector() { | |
| translate([-(Block.x/2 + Protrusion),0,0]) | |
| rotate([0,90,0]) | |
| linear_extrude(height=Connector.x + Protrusion) | |
| hull() | |
| for (i = [-1,1]) | |
| translate([0,i*(Connector.y – Connector.z)/2]) | |
| circle(d=Connector.z); | |
| } | |
| //———- | |
| // Build them | |
| if (Layout == "Hose") | |
| HoseProfile(); | |
| if (Layout == "Block") | |
| SpliceBlock(); | |
| if (Layout == "Connector") | |
| Connector(); | |
| if (Layout == "Show") { | |
| ShapedBlock(); | |
| color("Green",0.25) | |
| HoseProfile(); | |
| } | |
| if (Layout == "Build") { | |
| SliceOffset = 0; | |
| intersection() { | |
| translate([SliceOffset,0,Block.z/4]) | |
| cube([4*Block.x,4*Block.y,Block.z/2],center=true); | |
| union() { | |
| translate([0,0.6*Block.y,Block.z/2]) | |
| ShapedBlock(); | |
| translate([0,-0.6*Block.y,Block.z/2]) | |
| rotate([0,180,0]) | |
| ShapedBlock(); | |
| } | |
| } | |
| } |
One of the handles snapped off a Y valve at the garden and I finally got around to an autopsy:

That’s using a 24 tpi bandsaw blade, which doesn’t cut nearly as smoothly as a fancy diamond saw, but seems good enough for the purpose. Most of the ripply shading on the cut plane comes from specular reflections; it’s pot metal all the way through and cuts to a high shine.
A closeup shows more detail around the (now hemispheric) ball valve:

You can see faint straight lines just inside the hose threads, which gives a hint of what’s to come.
Pry out the sectioned ball and dislodge the O-ring from the now-obvious insert:
Gently squish the threads in the bench vise to pop out the insert:
If lives depended on it, one could dismantle and repair the valve without recourse to a bandsaw, but …