
Kệ Gridfinity OpenGrid hỗ trợ Multiconnect gắn nam châm
Remix of Kệ treo tường đa năng tích hợp Gridfinity cho hệ thống openGrid By DavidD
- Downloads
- 0
- Likes
- 1
- Views
- 32
- Remixes
- 0
- Comments
- 0
Files 2 9.1 MB
- opengrid_multiconnect_gridfinity_shelf_magnets_1x2_1783592282.stlSTL · 1.7 MB
- opengrid_multiconnect_gridfinity_shelf_magnets_2x4_1783592283.stlSTL · 7.4 MB
Make a variant with AI
Build another version of this model.
Details
- Model ID
- #410057
- Category
- DIY
- Published
Description
using the openscad designs from https://github.com/AndyLevesque/QuackWorks and https://github.com/kennetek/gridfinity-rebuilt-openscad I added the magnets to this.
LLM was used to add in the new code for the magnet holes and magnet removal methods. It also re-wrote the crush ribs for the magnets
/*Created by BlackjackDuck (Andy)
Credit to
@David D on Printables for Multiboard
Jonathan at Keep Making for Multiconnect
Zach at gridfinity.xyz for the Gridfinity standard
Katie (and her community) at Hands on Katie on Youtube for advice
@Dontic on GitHub for Multiconnect v2 code
Additional credit:
@kennetek on GitHub for the ribbed/crush-fit magnet hole concept, adapted
from the ribbed_cylinder technique in gridfinity-rebuilt-openscad
(https://github.com/kennetek/gridfinity-rebuilt-openscad), used under the
MIT License. Reimplemented here as ribbed_press_circle/ribbed_press_cylinder
rather than copied directly.
Licensed Creative Commons 4.0 Attribution Non-Commercial Share-alike with Attribution
TODO:
- Add ability to tweak rim height
- Ability to trim grid height (some versions lop off a tiny bit off the top)
Change Log:
- 2024-09-01
- Initial release
- 2025-04-08
- New backs (Multipoint, openGrid, GOEWS)
-2025-07-15
- New Multiconnect v2 option added with improved holding (thanks @dontic on GitHub!)
Local modifications:
- Added corner magnet holes, press-fit from the top surface, cut into the
shelf floor slab (see the Magnets section below).
- Optional ribbed (wavy crush-fit) hole style, inspired by the ribbed_cylinder
technique in kennetek/gridfinity-rebuilt-openscad (MIT licensed).
- Optional slot/hole magnet removal assist, slots cut diagonally toward the
nearest open cell corner.
*/
include <BOSL2/std.scad>
/[Slot Types]/
//How do you intend to mount to a surface and which surface?
Connection_Type = "Multiconnect - openGrid"; // [Multipoint, Multiconnect - Multiboard, Multiconnect - openGrid, Multiconnect - Custom Size, GOEWS]
/[Parameters]/
unitsWide = 4;
unitsDeep = 2;
rimThickness = 1.5;
additionalRimHeight = 2;
baseThickness = 4;
overrideBackHeight = false;
customBackHeight = 60;
backHeight = overrideBackHeight ? customBackHeight : 15+15*unitsDeep;
removeBase = false;
bracketDepthPercentage = 50; //[0:1:100]
/[Magnets]/
// Enable magnets in each gridfinity cell corner
Enable_Magnets = true;
//size of magnet, diameter and height. Zack's original used 6.5 and 2.4
Magnet_Size = [6.5, 2.4]; // .1
//solid material left below the magnet so it can't push through the floor
Magnet_Floor_Below = 1.0; // .1
//Press-fit style: "smooth" (plain reduced-diameter hole) or "ribbed" (wavy crush-fit, more tolerant of printer variance)
Magnet_Fit_Style = "ribbed"; // [smooth, ribbed]
//shrinks the hole radius below magnet radius. For "ribbed" this can be small/zero since the ribs do the gripping; for "smooth" this is the entire fit.
Magnet_Press_Fit_Reduction = 0.05; // .05
//Number of crush ribs around the hole (ribbed style only)
Magnet_Rib_Count = 12;
//How far the ribs bulge in/out from the nominal radius (ribbed style only)
Magnet_Rib_Depth = 0.15; // .05
//lead-in chamfer at the top opening so the magnet is easy to start pressing in
Magnet_Chamfer = 0.4; // .1
//Distance magnet hole center sits from each cell edge (gridfinity standard is 8mm)
Magnet_Inset = 8;
//Method to help remove magnets: "none", "slot" (screwdriver pry notch, cut diagonally toward the nearest open corner), "hole" (poke-out hole from underneath), "both"
Magnet_Release_Method = "hole"; //[none, slot, hole, both]
//Width of the pry notch, for a small flathead screwdriver tip
Release_Slot_Width = 3.5; // .1
//How far the pry notch cuts down from the top surface
Release_Slot_Depth = 1.5; // .1
//Diameter of the poke-out hole through the floor
Release_Hole_Diameter = 3; // .1
/[Slot Customization]/
// Version of multiconnect (dimple or snap)
multiConnectVersion = "v2"; // [v1, v2]
onRampHalfOffset = true;
//Distance between Multiconnect slots on the back (25mm is standard for MultiBoard)
customDistanceBetweenSlots = 25;
//Reduce the number of slots
subtractedSlots = 0;
//QuickRelease removes the small indent in the top of the slots that lock the part into place
slotQuickRelease = false;
//Dimple scale tweaks the size of the dimple in the slot for printers that need a larger dimple to print correctly
dimpleScale = 1; //[0.5:.05:1.5]
//Scale the size of slots in the back (1.015 scale is default for a tight fit. Increase if your finding poor fit. )
slotTolerance = 1.00; //[0.925:0.005:1.075]
//Move the slot in (positive) or out (negative)
slotDepthMicroadjustment = 0; //[-.5:0.05:.5]
//enable a slot on-ramp for easy mounting of tall items
onRampEnabled = true;
//frequency of slots for on-ramp. 1 = every slot; 2 = every 2 slots; etc.
On_Ramp_Every_X_Slots = 1;
//Distance from the back of the item holder to where the multiconnect stops (i.e., where the dimple is) (by mm)
Multiconnect_Stop_Distance_From_Back = 13;
onRampEveryXSlots = On_Ramp_Every_X_Slots;
/[GOEWS Customization]/
GOEWS_Cleat_position = "normal"; // [normal, top, bottom, custom]
GOEWS_Cleat_custom_height_from_top_of_back = 11.24;
/[Hidden]/
magnets_active = Enable_Magnets && !removeBase;
magnetStackHeight = Magnet_Size[1] + Magnet_Floor_Below;
effectiveBaseThickness = magnets_active ? max(baseThickness, magnetStackHeight) : baseThickness;
adjustedBaseThickness = removeBase ? 0 : effectiveBaseThickness;
totalWidth = unitsWide42+rimThickness2;
totalDepth = unitsDeep42+rimThickness;
bracketDepth = totalDepthbracketDepthPercentage/100;
totalHeight = backHeight;
distanceBetweenSlots =
Connection_Type == "Multiconnect - openGrid" ? 28 :
Connection_Type == "Multiconnect - Custom Size" ? customDistanceBetweenSlots :
25; //default for multipoint
Connection_Standard =
Connection_Type == "Multipoint" ? "Multipoint" :
Connection_Type == "Multiconnect - Multiboard" ? "Multiconnect" :
Connection_Type == "Multiconnect - openGrid" ? "Multiconnect" :
Connection_Type == "Multiconnect - Custom Size" ? "Multiconnect" :
Connection_Type == "GOEWS" ? "GOEWS" :
"Unknown";
//begin build
translate(v = [-totalWidth/2,-totalDepth/2])
union(){
translate(v = [rimThickness,0,0])
gridfinity_grid(xCount = unitsWide, yCount = unitsDeep);
//BEGIN BACKERS
if(Connection_Standard == "Multipoint"){
translate(v = [0,0,-adjustedBaseThickness])
makebackPlate(
backWidth = totalWidth,
backHeight = totalHeight,
distanceBetweenSlots = distanceBetweenSlots,
backThickness=4.8);
}
if(Connection_Standard == "Multiconnect"){
translate(v = [0,0,-adjustedBaseThickness])
makebackPlate(
backWidth = totalWidth,
backHeight = totalHeight,
distanceBetweenSlots = distanceBetweenSlots,
backThickness=6.5);
}
if(Connection_Standard == "GOEWS"){
translate(v = [0,0,-adjustedBaseThickness])
makebackPlate(
backWidth = totalWidth,
backHeight = totalHeight,
distanceBetweenSlots = 42,
backThickness=7
);
}
//END BACKERS
//shelf
difference(){
translate(v = [0,0,-adjustedBaseThickness+0.01])
cube([totalWidth,totalDepth,4.65+adjustedBaseThickness+additionalRimHeight]);
translate(v = [rimThickness,0,0])
cube([totalWidth-rimThickness2-0.02,totalDepth-rimThickness-0.02,6+additionalRimHeight]);
//magnet press-fit pockets, opening on the top surface of the floor
if (magnets_active)
translate(v = [rimThickness,0,0])
for (cellY = [0:1:unitsDeep-1])
for (cellX = [0:1:unitsWide-1])
translate([cellX42, cellY*42, 0])
gridfinity_magnet_press_pockets(
inset = Magnet_Inset,
magnetSize = Magnet_Size,
floorThickness = adjustedBaseThickness,
pressFitReduction = Magnet_Press_Fit_Reduction,
chamfer = Magnet_Chamfer,
fitStyle = Magnet_Fit_Style,
ribCount = Magnet_Rib_Count,
ribDepth = Magnet_Rib_Depth,
releaseMethod = Magnet_Release_Method,
slotWidth = Release_Slot_Width,
slotDepth = Release_Slot_Depth,
releaseHoleDiameter = Release_Hole_Diameter
);
}
//bracket
exposedBackHeight = backHeight-adjustedBaseThickness-4.65;
translate(v = [rimThickness,0,backHeight-adjustedBaseThickness])
shelfBracket(bracketHeight = exposedBackHeight, bracketDepth = bracketDepth,rimThickness = rimThickness);
translate(v = [totalWidth,0,backHeight-adjustedBaseThickness])
shelfBracket(bracketHeight = exposedBackHeight, bracketDepth = bracketDepth,rimThickness = rimThickness);
}
module gridfinity_grid(xCount, yCount) {
//Gridfinity Grid
for (columnY = [0:1:yCount-1]){
for (rowX = [0:1:xCount-1]) {
translate(v = [rowX42,columnY42,0]) gridfinity_base();
}
}
}
module gridfinity_base() {
difference() {
cube(size = [42,42,4.65]);
/*
baseplate delete tool
This is a delete tool which is the inverse profile of the baseplate intended for Difference.
Using polygon, I sketched the profile of the base edge per gridfinity specs.
I then realized I need rounded corners with 8mm outer diameter, so I increased the x axis enough to have a 4mm total outer length (radius).
I rotate extrude to created the rounded corner
Finally, I used minkowski (thank you Katie from "Hands on Katie") using a cube that is 42mm minus the 8mm of the edges (equalling 34mm)
I also added separate minkowski tools to extend the top and the bottom for proper deleting
*/
union() {
//primary profile
translate(v = [4,38,5.65])
rotate(a = [180,0,0])
minkowski() {
rotate_extrude($fn=50)
polygon(points = [[0,0],[4,0],[3.3,0.7],[3.3,2.5],[1.15,4.65],[0,4.65]]);
cube(size = [34,34,1]);
}
//bottom extension bottom tool
translate(v = [4,4,-2])
minkowski() {
linear_extrude(height = 1) circle(r = 4-2.85, $fn=50);
cube(size = [34,34,6]);
}
//top extension
translate(v = [4,4,5])
minkowski() {
linear_extrude(height = 1) circle(r = 4, $fn=50);
cube(size = [34,34,2]);
}
}
}
}
//BEGIN MODULES
// Wavy crush-fit circle: wraps a sine wave around the nominal radius so the
// hole flexes slightly on insertion instead of relying purely on a fixed
// diameter tolerance. Same underlying idea as gridfinity-rebuilt-openscad's
// ribbed_cylinder (MIT licensed), reimplemented to match this file's style.
module ribbed_press_circle(radius, ribDepth, ribs) {
points = [
for (a = [0 : 360/max($fn, 60) : 360])
[sin(a), cos(a)] * (radius + sin(a * ribs) * ribDepth)
];
polygon(points);
}
module ribbed_press_cylinder(radius, ribDepth, ribs, height) {
linear_extrude(height)
ribbed_press_circle(radius, ribDepth, ribs);
}
// Press-fit magnet pockets, opening on the TOP surface of the floor (z=0, the
// surface bins sit on). Magnet is pushed in by hand after printing.
// fitStyle "smooth": plain reduced-radius hole, friction from pressFitReduction alone.
// fitStyle "ribbed": wavy crush-fit hole, ribs do most of the gripping.
// Solid material below (floorThickness - magnetSize[1]) stops the magnet from
// pushing through to the underside.
// Optional removal assist: a side pry notch ("slot", cut diagonally toward the
// nearest open cell corner), a poke-out hole from underneath ("hole"), or both.
module gridfinity_magnet_press_pockets(inset = 8, magnetSize = [6.5, 2.4], floorThickness = 3.4, pressFitReduction = 0.15, chamfer = 0.4, fitStyle = "smooth", ribCount = 12, ribDepth = 0.15, releaseMethod = "none", slotWidth = 3.5, slotDepth = 1.5, releaseHoleDiameter = 3) {
r = (magnetSize[0] - pressFitReduction) / 2;
h = magnetSize[1];
for (xi = [0, 1]) {
for (yi = [0, 1]) {
x = (xi == 0) ? inset : 42 - inset;
y = (yi == 0) ? inset : 42 - inset;
// direction toward the nearest open corner of the cell, for the pry notch
xSign = (xi == 0) ? -1 : 1;
ySign = (yi == 0) ? -1 : 1;
diagonalAngle = atan2(-ySign, -xSign);
translate([x, y, 0]) {
union() {
// press-fit hole: smooth (plain cylinder) or ribbed (crush-fit)
translate([0, 0, -h])
if (fitStyle == "ribbed")
ribbed_press_cylinder(radius = r, ribDepth = ribDepth, ribs = ribCount, height = h + 0.01);
else
cylinder(h = h + 0.01, r = r, $fn = 48);
// lead-in chamfer right at the opening
if (chamfer > 0)
translate([0, 0, -chamfer])
cylinder(h = chamfer + 0.01, r1 = r, r2 = r + chamfer, $fn = 48);
// pry notch: cut diagonally outward toward the nearest open cell
// corner (not straight along X), so the notch lands where there's
// actually open space to slide a screwdriver in, rather than
// potentially running into a neighboring cell or the tray wall.
if (releaseMethod == "slot" || releaseMethod == "both")
rotate([0, 0, diagonalAngle])
translate([r - 0.5, -slotWidth/2, -slotDepth])
cube([r + 1.5, slotWidth, slotDepth + 0.01]);
// poke-out hole: straight through from the TRUE underside of the
// floor up to the bottom of the magnet pocket.
if ((releaseMethod == "hole" || releaseMethod == "both") && floorThickness > h)
translate([0, 0, -floorThickness - 0.5])
cylinder(h = (floorThickness - h) + 0.5, r = releaseHoleDiameter/2, $fn = 30);
}
}
}
}
}
module shelfBracket(bracketHeight, bracketDepth, rimThickness){
rotate(a = [-90,0,90])
linear_extrude(height = rimThickness)
polygon([[0,0],[0,bracketHeight],[bracketDepth,bracketHeight]]);
}
//Slotted back Module
module makebackPlate(backWidth, backHeight, distanceBetweenSlots, backThickness, slotStopFromBack = 13, edgeRounding = 1)
{
//slot count calculates how many slots can fit on the back. Based on internal width for buffer.
//slot width needs to be at least the distance between slot for at least 1 slot to generate
let (backWidth = max(backWidth,distanceBetweenSlots), backHeight = max(backHeight, 25),slotCount = floor(backWidth/distanceBetweenSlots)- subtractedSlots){
if(Connection_Standard != "GOEWS"){
difference() {
translate(v = [0,-backThickness,0])
cuboid(size = [backWidth,backThickness,backHeight], rounding=edgeRounding, edges=FRONT, except_edges=BOT, anchor=FRONT+LEFT+BOT, $fn = 25);
//Loop through slots and center on the item
//Note: I kept doing math until it looked right. It's possible this can be simplified.
for (slotNum = [0:1:slotCount-1]) {
translate(v = [distanceBetweenSlots/2+(backWidth/distanceBetweenSlots-slotCount)distanceBetweenSlots/2+slotNumdistanceBetweenSlots,-2.35+slotDepthMicroadjustment,backHeight-Multiconnect_Stop_Distance_From_Back]) {
if(Connection_Standard == "Multipoint"){
multiPointSlotTool(totalHeight);
}
if(Connection_Standard == "Multiconnect"){
multiConnectSlotTool(totalHeight);
}
}
}
}
} else {
// GOEWS
GOEWS_Cleat_custom_height_from_top_of_back = (GOEWS_Cleat_position == "normal") ? 11.24 : (GOEWS_Cleat_position == "top") ? 0 : (GOEWS_Cleat_position == "bottom") ? backHeight - 13.15 : GOEWS_Cleat_custom_height_from_top_of_back;
difference() {
union() {
// Back plate
translate(v = [0,-backThickness,0])
cuboid(size = [backWidth,backThickness,backHeight], rounding=edgeRounding, except_edges=BACK, anchor=FRONT+LEFT+BOT);
//Loop through slots and center on the item
//Note: I kept doing math until it looked right. It's possible this can be simplified.
// Add cleats
for (slotNum = [0:1:slotCount-1]) {
translate(v = [distanceBetweenSlots/2+(backWidth/distanceBetweenSlots-slotCount)distanceBetweenSlots/2+slotNumdistanceBetweenSlots,-1 * backThickness,backHeight-GOEWS_Cleat_custom_height_from_top_of_back]) {
GOEWSCleatTool(totalHeight);
}
}
};
// Remove back plate cut outs for screw threads
for (slotNum = [0:1:slotCount-1]) {
translate(v = [distanceBetweenSlots/2+(backWidth/distanceBetweenSlots-slotCount)distanceBetweenSlots/2+slotNumdistanceBetweenSlots, 0, backHeight + 0.46 - GOEWS_Cleat_custom_height_from_top_of_back + 11.24]) {
Print spec
- Formats
- stl
- Model files
- 2
- Total size
- 9.1 MB
- Largest file
- 7.4 MB












