Main > Project Announcements
3d printed or CNC milled mounting plate for Ultimarc ServoStik
(1/3) > >>
PL1:
Like the title says, this is the thread for developing and testing a 3d printed or CNC milled ServoStik mounting plate and spacers for use with the "Under mount (support blocks)" method described here in the wiki.

Anyone who wants to join in, grab a copy of OpenSCAD and bookmark the OpenSCAD cheatsheet to help you with the commands and syntax.   ;D

---------------------
Gilrock came up with the original plate design and measurements.

He fabricated some here in his build thread.

 

DXF file: http://www.threebuttes.com/Extras/mame/JoystickPlate.dxf





The original design used this hardware.



I made parametric 3d models for the plate and spacers based on Gilrock's measurements.
- Your choice whether to use threaded spacers like the original or 3d printed spacers with four #8 screws and locknuts.
- In addition to generating the models, OpenSCAD uses echo commands to show key measurements that you may find useful.



OpenSCAD code for the plate:

--- Code: ---// ServoStik Mount Plate v1

// Original plate design and measurements by Gilrock

// The diameter values may need to be *very slightly* larger to account for the 180-sided polygon used to render circles -- see "undersized holes" at https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Primitive_Solids#cylinder

////////////////////////////////////////////
//
// Variables:
//
////////////////////////////////////////////
PlateThick = 2.4;       // Mount plate thickness

PlateX = 102;           // Plate body width
PlateY = 85.5;          // Plate body length

TabCornerDia = 3;       // Tab corner rounding diameter
InsideCornerDia = 6;    // Inside corner rounding diameter or bit diameter for CNC milling

TTabX = 15.92;          // T-Tab width
TTabY = 13.61;          // T-Tab length

HTabX = 15.92;          // H-Tab width
HTabY = 13.61;          // H-Tab length

ServoStikBodyX = 68.5;  // ServoStik body width
ServoStikBodyY = 68.5;  // ServoStik body length

PlateScrewHoleDia = 5;  // Plate screw hole diameter
PlateScrewHoleX = 77.5; // X-axis distance between plate screw holes
PlateScrewHoleY = 68.5; // Y-axis distance between plate screw holes

MountScrewHoleDia = 5;  // Mount tab screw hole diameter
TTabScrewXOffset = 0.965;   // Screw hole X-axis offset from center of T-Tab, increase to move away from center, decrease to move toward center
HTabScrewYOffset = 0.445;   // Screw hole Y-axis offset from center of H-Tab, increase to move away from center, decrease to move toward center

// Number of fragments (polygon sides) used to render a full circle.
    $fn = 180; // Default = 180  Typical range = 6 - 360
    // 6 will render a circular hole as a hexagon, 8 will render a circular hole as an octagon.
    // Lower the number for faster rendering, raise the number for smoother rendering.

////////////////////////////////////////////
//
// Automatically calculated variables:
//
////////////////////////////////////////////
TTabScrewX = PlateX/2 + TTabX/2 + TTabScrewXOffset; // X-axis position of T-Tab screw hole
TTabScrewY = PlateY/2 - TTabY/2; // Y-axis position of T-Tab screw hole

HTabScrewX = PlateX/2 - HTabX/2; // X-axis position of H-Tab screw hole
HTabScrewY = - PlateY/2 - HTabY/2 - HTabScrewYOffset; // Y-axis position of H-Tab screw hole

////////////////////////////////////////////
//
// Make the part:
//
////////////////////////////////////////////
difference(){ // Mount plate body union minus holes

    union(){ // Plate body union

        cube ([PlateX, PlateY, PlateThick], center=true); // Center plate body

        hull() { // T-tab body hull

        translate ([PlateX/2 + TTabX - TabCornerDia/2, PlateY/2 - TabCornerDia/2, 0])
        cylinder (PlateThick, d = TabCornerDia, center=true); // Upper right corner

        translate ([PlateX/2 + TTabX - TabCornerDia/2, PlateY/2 - TTabY + TabCornerDia/2, 0])
        cylinder (PlateThick, d = TabCornerDia, center=true); // Lower right corner

        translate ([-PlateX/2 - TTabX + TabCornerDia/2, PlateY/2 - TTabY + TabCornerDia/2, 0])
        cylinder (PlateThick, d = TabCornerDia, center=true); // Lower left corner

        translate ([-PlateX/2 - TTabX + TabCornerDia/2, PlateY/2 - TabCornerDia/2, 0])
        cylinder (PlateThick, d = TabCornerDia, center=true); // Upper left corner

        } // End T-tab body hull
//

        hull() { // Right H-tab body hull

        translate ([PlateX/2 - TabCornerDia/2, - PlateY/2 + TabCornerDia/2, 0])
        cylinder (PlateThick, d = TabCornerDia, center=true); // Upper right corner

        translate ([PlateX/2 - TabCornerDia/2, - PlateY/2 - HTabY + TabCornerDia/2, 0])
        cylinder (PlateThick, d = TabCornerDia, center=true); // Lower right corner

        translate ([PlateX/2 - HTabX + TabCornerDia/2, - PlateY/2 - HTabY + TabCornerDia/2, 0])
        cylinder (PlateThick, d = TabCornerDia, center=true); // Lower left corner

        translate ([PlateX/2 - HTabX + TabCornerDia/2, - PlateY/2 + TabCornerDia/2, 0])
        cylinder (PlateThick, d = TabCornerDia, center=true); // Upper left corner

        } // End right H-tab body hull
//

        hull() { // Left H-tab body hull

        translate ([-PlateX/2 + TabCornerDia/2, - PlateY/2 + TabCornerDia/2, 0])
        cylinder (PlateThick, d = TabCornerDia, center=true); // Upper left corner

        translate ([-PlateX/2 + TabCornerDia/2, - PlateY/2 - HTabY + TabCornerDia/2, 0])
        cylinder (PlateThick, d = TabCornerDia, center=true); // Lower left corner

        translate ([-PlateX/2 + HTabX - TabCornerDia/2, - PlateY/2 - HTabY + TabCornerDia/2, 0])
        cylinder (PlateThick, d = TabCornerDia, center=true); // Lower right corner

        translate ([-PlateX/2 + HTabX - TabCornerDia/2, - PlateY/2 + TabCornerDia/2, 0])
        cylinder (PlateThick, d = TabCornerDia, center=true); // Upper right corner

        } // End left H-tab body hull
//
       
    } // End plate body union
//

// Holes to remove from plate body union

    cube ([ServoStikBodyX, ServoStikBodyY, PlateThick + 1], center=true); // ServoStik body hole

    translate ([ServoStikBodyX/2, ServoStikBodyY/2, 0])
    rotate ([0, 0, 45])
    translate ([-InsideCornerDia/2, 0, 0])
    cylinder (PlateThick + 1, d = InsideCornerDia, center=true); // Upper right corner rounding for Servostik body hole

    translate ([ServoStikBodyX/2, -ServoStikBodyY/2, 0])
    rotate ([0, 0, 315])
    translate ([-InsideCornerDia/2, 0, 0])
    cylinder (PlateThick + 1, d = InsideCornerDia, center=true); // Lower right corner rounding for Servostik body hole

    translate ([-ServoStikBodyX/2, -ServoStikBodyY/2, 0])
    rotate ([0, 0, 225])
    translate ([-InsideCornerDia/2, 0, 0])
    cylinder (PlateThick + 1, d = InsideCornerDia, center=true); // Lower left corner rounding for Servostik body hole

    translate ([-ServoStikBodyX/2, ServoStikBodyY/2, 0])
    rotate ([0, 0, 135])
    translate ([-InsideCornerDia/2, 0, 0])
    cylinder (PlateThick + 1, d = InsideCornerDia, center=true); // Upper left corner rounding for Servostik body hole

    translate ([PlateScrewHoleX/2, PlateScrewHoleY/2, 0])
    cylinder (PlateThick + 1, d = PlateScrewHoleDia, center=true); // Upper right plate screw hole

    translate ([PlateScrewHoleX/2, -PlateScrewHoleY/2, 0])
    cylinder (PlateThick + 1, d = PlateScrewHoleDia, center=true); // Lower right plate screw hole

    translate ([-PlateScrewHoleX/2, -PlateScrewHoleY/2, 0])
    cylinder (PlateThick + 1, d = PlateScrewHoleDia, center=true); // Lower left plate screw hole

    translate ([-PlateScrewHoleX/2, PlateScrewHoleY/2, 0])
    cylinder (PlateThick + 1, d = PlateScrewHoleDia, center=true); // Upper left plate screw hole

    translate ([TTabScrewX, TTabScrewY, 0])
    cylinder (PlateThick + 1, d = MountScrewHoleDia, center=true); // Upper right T-Tab screw hole

    translate ([-TTabScrewX, TTabScrewY, 0])
    cylinder (PlateThick + 1, d = MountScrewHoleDia, center=true); // Upper left T-Tab screw hole

    translate ([HTabScrewX, HTabScrewY, 0])
    cylinder (PlateThick + 1, d = MountScrewHoleDia, center=true); // Lower right H-Tab screw hole

    translate ([-HTabScrewX, HTabScrewY, 0])
    cylinder (PlateThick + 1, d = MountScrewHoleDia, center=true); // Lower left H-Tab screw hole

} // End mount plate body union minus holes
//

// Add the inside corners next to the tabs

difference(){ // Inside corner by upper right tab
    translate ([PlateX/2 + InsideCornerDia/4, PlateY/2 - TTabY - InsideCornerDia/4, 0])
    cube ([InsideCornerDia/2, InsideCornerDia/2, PlateThick], center=true); // Cube

    translate ([PlateX/2 + InsideCornerDia/2, PlateY/2 - TTabY - InsideCornerDia/2, 0])
    cylinder (PlateThick + 1, d = InsideCornerDia, center=true); // Minus cylnder
} // End inside corner by upper right tab
//

difference(){ // Inside corner by lower right tab
    translate ([PlateX/2 - HTabX - InsideCornerDia/4, - PlateY/2 - InsideCornerDia/4, 0])
    cube ([InsideCornerDia/2, InsideCornerDia/2, PlateThick], center=true); // Cube

    translate ([PlateX/2 - HTabX - InsideCornerDia/2, - PlateY/2 - InsideCornerDia/2, 0])
    cylinder (PlateThick + 1, d = InsideCornerDia, center=true); // Minus cylnder
} // End inside corner by lower right tab
//

difference(){ // Inside corner by lower left tab
    translate ([-PlateX/2 + HTabX + InsideCornerDia/4, - PlateY/2 - InsideCornerDia/4, 0])
    cube ([InsideCornerDia/2, InsideCornerDia/2, PlateThick], center=true); // Cube

    translate ([-PlateX/2 + HTabX + InsideCornerDia/2, - PlateY/2 - InsideCornerDia/2, 0])
    cylinder (PlateThick + 1, d = InsideCornerDia, center=true); // Minus cylnder
} // End inside corner by lower left tab
//

difference(){ // Inside corner by upper left tab
    translate ([-PlateX/2 - InsideCornerDia/4, PlateY/2 - TTabY - InsideCornerDia/4, 0])
    cube ([InsideCornerDia/2, InsideCornerDia/2, PlateThick], center=true); // Cube

    translate ([-PlateX/2 - InsideCornerDia/2, PlateY/2 - TTabY - InsideCornerDia/2, 0])
    cylinder (PlateThick + 1, d = InsideCornerDia, center=true); // Minus cylnder
} // End inside corner by upper left tab
//

// Echo statements with X/Y position of T-Tab and H-Tab screw hole positions and center-to-center distances
echo("--------------------------------------------------------------------------------------------------");
echo("T-Tab screw hole position measurements");
echo(str("X-axis distance = ", TTabScrewX, " and Y-axis distance = ", TTabScrewY, " from center"));
echo(str("X-axis center-to-center distance = ", TTabScrewX*2));
echo();
echo("H-Tab screw hole position measurements");
echo(str("X-axis distance = ", HTabScrewX, " and Y-axis distance = ", HTabScrewY, " from center"));
echo(str("X-axis center-to-center distance = ", HTabScrewX*2));
echo("--------------------------------------------------------------------------------------------------");
//

--- End code ---

OpenSCAD code for the spacers:

--- Code: ---// ServoStik Mount Spacers v1

// Original plate design and measurements by Gilrock

// Inner diameter of 4.5 should work with #8 screws

// The diameter values may need to be *very slightly* larger to account for the 180-sided polygon used to render circles -- see "undersized holes" at https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Primitive_Solids#cylinder

////////////////////////////////////////////
//
// Variables:
//
////////////////////////////////////////////
OuterDiameter = 6.35; // Spacer outer diameter
InnerDiameter = 4.5;  // Spacer inner diameter
TallHeight = 8.25;    // Tall spacer height

// Number of fragments (polygon sides) used to render a full circle.
    $fn = 180; // Default = 180  Typical range = 6 - 360
    // 6 will render a circular hole as a hexagon, 8 will render a circular hole as an octagon.
    // Lower the number for faster rendering, raise the number for smoother rendering.

////////////////////////////////////////////
//
// Automatically calculated variables:
//
////////////////////////////////////////////
ShortHeight = TallHeight - 1.9; // Short spacer height

////////////////////////////////////////////
//
// Make the part:
//
////////////////////////////////////////////

// Tall spacer 1
difference(){ // Outer cylinder minus inner cylinder

        translate ([- OuterDiameter * 0.75, OuterDiameter * 0.75, TallHeight/2])
        cylinder (TallHeight, d = OuterDiameter, center=true); // Outer cylinder

        translate ([- OuterDiameter * 0.75, OuterDiameter * 0.75, TallHeight/2])
        cylinder (TallHeight + 1, d = InnerDiameter, center=true); // Inner cylinder

} // End outer cylinder minus inner cylinder
//

// Tall spacer 2
difference(){ // Outer cylinder minus inner cylinder

        translate ([OuterDiameter * 0.75, OuterDiameter * 0.75, TallHeight/2])
        cylinder (TallHeight, d = OuterDiameter, center=true); // Outer cylinder

        translate ([OuterDiameter * 0.75, OuterDiameter * 0.75, TallHeight/2])
        cylinder (TallHeight + 1, d = InnerDiameter, center=true); // Inner cylinder

} // End outer cylinder minus inner cylinder
//

// Short spacer 1
difference(){ // Outer cylinder minus inner cylinder

        translate ([- OuterDiameter * 0.75, - OuterDiameter * 0.75, ShortHeight/2])
        cylinder (ShortHeight, d = OuterDiameter, center=true); // Outer cylinder

        translate ([- OuterDiameter * 0.75, - OuterDiameter * 0.75, ShortHeight/2])
        cylinder (ShortHeight + 1, d = InnerDiameter, center=true); // Inner cylinder

} // End outer cylinder minus inner cylinder
//

// Short spacer 2
difference(){ // Outer cylinder minus inner cylinder

        translate ([OuterDiameter * 0.75, - OuterDiameter * 0.75, ShortHeight/2])
        cylinder (ShortHeight, d = OuterDiameter, center=true); // Outer cylinder

        translate ([OuterDiameter * 0.75, - OuterDiameter * 0.75, ShortHeight/2])
        cylinder (ShortHeight + 1, d = InnerDiameter, center=true); // Inner cylinder

} // End outer cylinder minus inner cylinder
//

// Echo statements with tall and short spacer heights and mount plate screw length
echo("--------------------------------------------------------------------------------------------------");
echo(str("Tall spacer height = ", TallHeight, " and short spacer height = ", ShortHeight));
echo(str("The length of the plate mount screws must be at least ", TallHeight + 1.65, "mm plus the thickness of the mount plate, washers, and nut."));
echo("--------------------------------------------------------------------------------------------------");
//

--- End code ---

***  Click here to download a .zip file containing the v1 .STL files and OpenSCAD files.  ***

***  Click here to download a .zip file containing the v1 .DXF file.  ***

***  Click here to download the v1 .SVG file.  ***

--------------------
Next steps:
Get someone with a 3d printer (or CNC machine) and a ServoStik to test print (or mill) the plate and see if everything fits properly.  Are there any measurements that are off?

Get feedback from CNC users on whether .STL files are a useful format or if there is a better/preferred format that OpenSCAD can export to.

Are there any features you'd like to see added or changed?


Scott
PL1:
Found this interesting thread about converting OpenSCAD files to a CNC-friendly format.
https://forum.openscad.org/Is-OpenSCAD-to-CNC-possible-td22175.html

One way is to use PyCAM to generate toolpaths (GCode) from .STL files.

Another way is to import the OpenSCAD file into FreeCAD.
- There have been issues importing some files as mentioned here.

Have any of you with CNC experience tried one of these methods?


Scott
Gilrock:
I've never ventured past the one way I learned to create GCode for my CNC.  I bought Aspire when I bought the CNC kit and so I've always figured out a way to get my designs into that program and then use it to create GCode.  I figured out how to set things up several years ago and I'd be started from scratch if I had to do it again.
Mike A:
Damn. Aspire?

I can only afford VCarve Pro.

Vectric makes good software.

If I did more 3 dimensional work I would try to find a way to buy Aspire.
PL1:
So far, sounds like PyCAM, eSTLcam, FreeCAD, Aspire, and VCarve Pro are good software packages to consider/test.

PyCAM - STL files
- Not familiar with the software so not sure what the work flow steps are.

eSTLcam - STL files
- Commonly used and inexpensive software.
https://www.estlcam.de/

FreeCAD - Opens most OpenSCAD files, mount plate files haven't been tested yet
- Requires a plugin.
- Not familiar with the software so not sure what the work flow steps are.

Aspire - STL and DXF(?) files
https://www.vectric.com/products/aspire

--- Quote ---Import 3D Models & Clip Art (STL, OBJ, 3DM etc.)

Any model you create in Aspire can be saved as a piece of clipart for use in other projects.

Import 3D mesh files created in other CAD software such as,

    Rhino 5, SolidWorks, AutoCAD, Silo, MOI, Blender, SketchUp etc.
    STL, 3D DXF, OBJ, 3DS, VRML, 3DM, SKP file formats

--- End quote ---
- Not familiar with the software so not sure what the work flow steps are.
- OpenSCAD can only export 2D DXF files, not 3D DXF files.  https://en.m.wikibooks.org/wiki/OpenSCAD_User_Manual/DXF_Export
- That might be good enough for a mount plate if you can convert a 2D DXF into a 3D shape in Aspire.

VCarve Pro - STL and DXF(?) files
https://www.vectric.com/products/vcarve-pro

--- Quote ---Import 3D Models (STL, OBJ, 3DM etc.) & Clipart (V3M)

Import a single 3D mesh file created in other CAD software such as,

    Rhino 5, SolidWorks, AutoCAD, Silo, MOI, Blender, SketchUp etc.
    STL, 3D DXF, OBJ, 3DS, VRML, 3DM, SKP file formats

--- End quote ---
- Not familiar with the software so not sure what the work flow steps are.
- OpenSCAD can only export 2D DXF files, not 3D DXF files.  https://en.m.wikibooks.org/wiki/OpenSCAD_User_Manual/DXF_Export
- That might be good enough for a mount plate if you can convert a 2D DXF into a 3D shape in VCarve Pro.

The OpenSCAD code for converting the mount plate STL to a DXF file is very simple -- directions are in the comments.

--- Code: ---// ServoStik Mount Plate DXF projection

// Place this file in the same directory as the "ServoStik Mount Plate.stl" file.

// Preview shows the imported STL, render shows the 2D shape.

// Click on File -- Export -- Export as DXF.

projection(cut = true) import("ServoStik Mount Plate.stl");

--- End code ---

If you have one or more of these software packages installed and working, please test importing the file and let everyone know if it worked or not.   :cheers:
- If anyone wants to test importing the DXF file from OpenSCAD, you can download it here.


Scott
EDIT: Added eSTLcam -- recommended below by 601 Arcades.
Navigation
Message Index
Next page

Go to full version