Foam Cutter Manual
User Manual:
Open the PDF directly: View PDF .
Page Count: 92
Download | |
Open PDF In Browser | View PDF |
UCSD Design/Build/Fly Foamcutter Manual Yuting Huang ythuang96@gmail.com Updated: December 6, 2018 Preface Before operating the foamcutter, please read this manual carefully to avoid injuries and damages to the equipment. All content in the text boxes are extremely important and should be given extra attention. This foamcutter project was completed with funds from University of California San Diego Design/Build/Fly, and is intended to make the manufacturing process of composite planes easier. All SolidWorks files, C code, MatLab scripts and operation manual are available for download at the author’s Github page: https://github.com/ythuang96/FoamCutter. Please email the author at ythuang96@gmail.com to report any bug in code or any improvement suggestions. This project was an improvement on the foamcutter design by Derek Ung and David Cruz. Special thanks to Dr. Mark Anderson, UCSD MAE department for support of the project. And thanks to Geo Lopez for his help during the project. 1 2 Contents Preface 1 1 Preparations 5 1 2 3 4 Laptop Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.1 PuTTY Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.2 WinSCP Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 Assemble the Foamcutter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.1 Connect the two Side Frames . . . . . . . . . . . . . . . . . . . . . . 11 2.2 Connect two Molex Connector . . . . . . . . . . . . . . . . . . . . . . 12 2.3 Install the Work Panel . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.4 Install the Wire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Foamcutter Software Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.1 Connect to the Pi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.2 Calibrate Wire Origin . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.3 Install the Foamcutter Software . . . . . . . . . . . . . . . . . . . . . 21 3.4 Update the Foamcutter Software . . . . . . . . . . . . . . . . . . . . 22 G-Code Generation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 4.1 G-Code for Wing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 4.2 G-Code for General Shapes . . . . . . . . . . . . . . . . . . . . . . . 25 2 Operate the FoamCutter 33 1 Switches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 2 Start a Cut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 3 Secure the Foam . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 4 End a Cut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 3 5 Cleaning Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Raspberry Pi Setup 38 39 1 Install Raspbian on the Raspberry Pi . . . . . . . . . . . . . . . . . . . . . . 39 2 Install WiringPi on the Raspberry Pi . . . . . . . . . . . . . . . . . . . . . . 40 3 Making the Pi into a Wireless Hotspot . . . . . . . . . . . . . . . . . . . . . 41 Appendices 43 A C Code for the Foamcutter Program 44 1 foamcutter.c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 2 foamcutter_setup.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 B MatLab Code for G-code Generation 63 1 DBF_foamcutter_general_shape.m . . . . . . . . . . . . . . . . . . . . . . . 63 2 DBF_foamcutter_wing.m . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 C Drawings 74 D Part List 86 E Purchase History 89 4 Chapter 1 Preparations 1 Laptop Setup The foamcutter is controlled by a Raspberry Pi 3 B+. In order to communicate with the Pi, a Windows laptop has to install PuTTY (or similar) to enable secure shell (SSH) connection to the Pi, and WinSCP (or similar) for graphical file management on the Pi. A Linux laptop have SSH capability built in, therefore, no software required. Note: This setup process is only required once per laptop. formed, skip this section and continue to section 4. 1.1 If already per- PuTTY Setup Please follow the following steps for install and setup of PuTTY: 1. Download PuTTY at https://www.putty.org/ then install. 2. Launch PuTTY and a window similar to fig 1.1 below should show up: 5 Figure 1.1: PuTTY Launch Window 3. As shown in fig 1.2 below, in the “Host Name” field, enter “foamcutter.local", then make sure Connection type is SSH. Next, in “Saved Session field”, enter “foamcutter", and lastly, click save. Figure 1.2: PuTTY Setup 4. The session with name “foamcutter" should show up in Saved Session as shown in 6 fig 1.3 below, and the PuTTY setup is completed. Figure 1.3: Saved PuTTY Session 5. Note on using PuTTY: to copy text from PuTTY, simply select the text, do not press “Control + C”. To paste text to PuTTY, simply right click mouse, do not press “Control + V”. 1.2 WinSCP Setup Please follow the following steps for install and setup of WinSCP: 1. Download WinSCP at https://winscp.net/eng/download.php then install. 2. Launch WinSCP and a window similar to fig 1.4 below should show up: 7 Figure 1.4: WinSCP Launch Window 3. As shown in fig 1.5 below, make sure the protocol is “SFTP", then enter “foamcutter.local" for the host name, “pi" for the user name, and “ucsdaiaadbf" for the password. Figure 1.5: WinSCP Setup 4. Click save, and a window shown in fig 1.6 below should pop up. Both “Save password" and “Create desktop shortcut" are recommended. Click “OK" and the setup for WinSCP is completed. 8 Figure 1.6: WinSCP Setup continued 9 2 Assemble the Foamcutter The entire foamcutter as shown in fig 1.7 below consists of two side frames each having two axis of motion, four 4-feet long connection rods, and a work panel for securing the foam. Figure 1.7: Foamcutter Overview Caution: • Side frames are extremely heavy, lift with caution; • Avoid bumping head onto the connection rods when leaning into and out of the frame. 10 Do: • Always make sure the two side frames are connected with the connection rods; • Always push the two side frames firmly against each other before tightening the thumb screws; • Remember to plug in the two Molex connectors. Don’t: • Never let the side frames stand on their own; • Never over tighten the thumb screws on the work panel; • Never unscrew screws that are not thumb screws unless performing repairs. 2.1 Connect the two Side Frames The two side frames are connected with 4 connection rods, use the 4ft long connection rods for performing cutting and use the 2ft long connection rods (not purchased yet) for storage of the foamcutter. Each of the 4 connection rods are secured with two thumb screws at each end shown in fig 1.8, loosen the thumb screws to remove the connection rod. Figure 1.8: Thumb Screws for Connection Rods 11 During the installation of the connection rods, do not let the side frames stand on their own. The side frames are capable of standing on their own, but are not designed to do so: deformation will occur and degrade the accuracy. When installing the connection rods, push the two side frames firmly against each other to make sure there are no gaps between the side frames and the connection rods. Then tighten the thumb screws. An Allen key is recommended to further tighten the thumb screws. 2.2 Connect two Molex Connector There are two Molex connectors located at the two top corners of the foamcutter as shown in fig 1.9 below. Make sure to connect them before operating the foamcutter. The Molex connectors are foolproof, do not force it when unable to plug in. Figure 1.9: Molex Connectors 2.3 Install the Work Panel The work panel is connected to the two side frames at the four corners of the panel. Each corner is secured with two thumb screws as shown in fig 1.10 below. 12 Figure 1.10: Work Panel Thumb Screws The work panel should be oriented so that the guiding block, which is a long piece of PVC screwed to the work panel (pointed by the red arrows in the fig 1.11 below) should be at the closer side relative to the two horizontal motors (marked by the red circle in the fig 1.11 below). 13 Figure 1.11: Work Panel Guiding Block Orientation To install the work panel, first slide the work panel into the 4 supporting rods at the 4 corners with the correct orientation. Make sure all 4 corners sit securely in the supporting rods, then tighten all 8 thumb screws. 14 2.4 Install the Wire The wire is held to tension of approximately 10 lbs with a constant force spring. To install the wire, follow the steps shown below. (a) Step 1 (b) Step 2 (c) Step 3 (d) Step 4 Figure 1.12: Steps 1-4 1. Feed the wire through the small guiding hole on the side without the pulley. 2. Make a hoop at the end of the wire. 3. Put the hoop around the Nylon bolt and straighten the wire across the entire span of the foamcutter. 4. Cut the wire at about 3ft longer than the span of the foamcutter. Then feed the end of the wire through the small guiding hole on the side with the pulley. 15 (a) Step 5 (b) Step 6 (c) Step 7 (d) Step 8 Figure 1.13: Steps 5-8 5. 6. 7. 8. On the back side of the pulley, feed the wire around the copper sleeve bearing. Feed the end of the wire through one of the small mounting holes on the acrylic pulley. Remove the nut stored on the alligator clip. Wrap the wire around the nut. 16 (a) Step 9 (b) Step 10 (c) Step 11 (d) Step 12 (e) Step 13 (f) Step 14 Figure 1.14: Steps 9-12 9. Twist the wire around the nut. 10. Rotate the acrylic pulley clockwise to tighten the wire. 11. Hold the acrylic pulley by hand. Use a wrench to further tighten the spring by holding 17 onto the mounting screw of the spring and rotate counterclockwise. 12. Further tighten the spring with the wrench. 13. Tighten the spring to the approximate position shown in the figure. 14. Clip the alligator click next to the pulley. On the other end, clip the other alligator clip on the back side of the guiding hole, and make sure the alligator clip does not touch anything other than the hot wire. 3 Foamcutter Software Setup 3.1 Connect to the Pi 1. Perform the Laptop setup guide in Chapter 1 Section 1 if not already completed. 2. Plug in the power cord to the foamcutter to power on the Pi. 3. Connect to the WiFi hotspot named “foamcutter” with password “ucsdaiaadbf ” or connect to the ethernet port to the ethernet port of the laptop with a ethernet cable. 4. SSH into the Pi by opening PuTTY and double click on the saved session named “foamcutter”. Enter the user name “pi” then password “ucsdaiaadbf ”. 5. Open WinSCP, a window similar to fig 1.15 below should show up. The left hand side are folders and files on your computer, while the right hand side is files on the Pi. Make sure the right hand side is in the folder “/home/pi”, if not, navigate to it. To create new folder on the Pi, use the “new directory” button. To upload files from your computer to the Pi, simply drag the file/folder from the left half to the right half. Figure 1.15: WinSCP Window 18 3.2 Calibrate Wire Origin The 4 axes of the foamcutter each have one limit switch installed. Upon starting of the program, the foamcutter moves all axes towards the negative direction until hitting the limit switches. Then the foamcutter moves the 4 axes each a distance specified in the setting file to reach the Origin. This calibration only needs to be performed once in a while to improve accuracy. Please follow the steps to calibrate the wire origin: 1. Download the “User Package.zip” from https://github.com/ythuang96/FoamCutter, and unzip it. 2. Create a new folder on the Pi called “foamcutter” in the directory of “/home/pi”. 3. Upload the files named “foamcutter.c”, “foamcutter_setup.h” and “Makefile” from the ”User Package” just downloaded into the “foamcutter” folder just created on the Pi. 4. Open PuTTY and run the following commands line by line: • cd • cd foamcutter • make foamcutter • sudo ./foamcutter This will launch the foamcutter program. 5. When the program reads “I see the system is not homed yet, please press ENTER to home the system:” press the Enter key. The foamcutter will move all 4 axes towards the negative direction until touching the limit switch. Then all 4 axes will move towards the positive direction to the origin. 6. Place a Aluminum block on the work panel against the work panel guiding block, and observe from top down as shown in fig 1.16 below. Both the far left and far right of the wire should sit right against the aluminum block, measure the amount that needs to be adjusted in millimeters. 19 Figure 1.16: Calibrate Wire Origin in the Horizontal Direction 7. Again, place a Aluminum block on the work panel against the work panel guiding block, but observe horizontally as shown in fig 1.17 below. Both the far left and the far right of the wire should sit at the same height. The height could be arbitrary, but should be higher than the thumb screws on the work panel. Again, measure the amount that needs to be adjusted in millimeters. 20 Figure 1.17: Calibrate Wire Origin in the Vertical Direction 8. Open WinSCP, navigate to “/home/pi/foamcutter” on the Pi (the right hand side of the WinSCP window), and double click on the file “foamcutter_setup.h” to edit it. 9. Find the 5 lines that read as the following: // position of limit switch relative to cutter origin #define LIM2ORIGIN_LX -4100 #define LIM2ORIGIN_RX -3550 #define LIM2ORIGIN_LY -2500 #define LIM2ORIGIN_RY -2400 and adjust the four numbers: if wish to move an axis 1mm further away (or closer) from the limit switch, subtract (or add) 157 from the number corresponding to the axis. All 4 numbers must be negative integers, no decimal, and no equations (please calculated the number yourself and replace the original number). 10. Save the file then repeat the process from step 4 to adjust the 4 numbers until the wire sits at the desired location. Then save the 4 numbers on your computer. 3.3 Install the Foamcutter Software 1. Perform the Wire Origin Calibration guide in Chapter 1 Section 3.2 if not already completed. 2. Open PuTTY and run the following commands line by line: • cd 21 • cd foamcutter • sudo make install foamcutter 3. Delete the folder “/home/pi/foamcutter” on the Pi. 4. You can now use sudo foamcutter to run the foamcutter program. 3.4 Update the Foamcutter Software 1. If a new version is available, download the “User Package.zip” from https://github.com/ythuang96/FoamCutter, and unzip it. 2. Create a new folder on the Pi called “foamcutter” in the directory of “/home/pi”. 3. Upload the files named “foamcutter.c”, “foamcutter_setup.h” and “Makefile” from the ”User Package” just downloaded into the “foamcutter” folder just created on the Pi. 4. • If wish to perform Wire Origin Calibration, proceed to Chapter 1 Section 3.2 step 4 to step 10. Then continue to the steps in Chapter 1 Section 3.3. • If wish to use the previously calibrated parameters, open WinSCP, edit the file “/home/pi/foamcutter_setup.h”, find the 5 lines that read as the following: // position of limit switch relative to cutter origin #define LIM2ORIGIN_LX -4100 #define LIM2ORIGIN_RX -3550 #define LIM2ORIGIN_LY -2500 #define LIM2ORIGIN_RY -2400 and change the 4 numbers to the previously calibrated parameters. Then proceed to Chapter 1 Section 3.3 step 2 to step 4. 4 G-Code Generation 4.1 G-Code for Wing 1. Download the needed airfoil coordinates at http://m-selig.ae.illinois.edu/ads/coord_database.html#S 2. Place the needed airfoil coordinate files in the same folder as the MatLab code “DBF_foamcutter_wing.m” which is provided in the “User Package.zip” from https://github.com/ythuang96/FoamCutter. 3. Enter the parameters into the MatLab script and run the script. A figure tracing the the motion of the two ends of the wire should show up, and the G-code should be saved in the same folder. Note: When cutting extremely tampered parts, figure similar to the fig 1.18 below might show up during the G-code generation process: 22 Figure 1.18: Example Figure During G-code Generation for a Wing The parameters used for this figure is shown below: 23 Figure 1.19: Example Parameters During G-code Generation for a Wing Note that the root and tip cord length differed a lot, and the span is significantly smaller than the cutter width, therefore, this is an extremely tapered cut. As a result, the inverted airfoil shown in fig 1.18 above is expected. As explained in fig 1.20 below, for extremely tapered cuts, the two ends of the wire could move in opposite directions, resulting in the inverted airfoil shown in fig 1.18. Figure 1.20: Sketch of an Extremely Tapered Cut 24 4.2 G-Code for General Shapes Key Points: • Make sure to increase AutoCAD precision; • Make sure to scale the drawing by 25.4 in AutoCAD if the SolidWorks file is in inches; • Make sure to run “Explode” command in AutoCAD; • Make sure there are no overlapping lines; • Make sure to convert spline to polyline in AutoCAD and explode again. 1. Export the projection of the top or side (or both) profiles from SolidWorks into .dxf files, the steps are demonstrated with the payload bay shown in fig 1.21 below. Figure 1.21: Sample Payload Bay • The projection of the side profile can be obtained by slicing the part vertically in the center: go to “Insert → Features → Split” as shown in fig 1.22 below: 25 Figure 1.22: SolidWorks Split Command • In the pop up window, as shown in fig 1.23 below, in “Trim Tools” selected the desired plane (the front plane in this case) then click “Cut Body”. In the “Resulting Bodies” box, select one of the resulting bodies. Lastly, make sure the “Consume cut bodies” check box is selected, and click the top left green check mark. This will delete the selected half of the part (the brown half in fig 1.23). 26 Figure 1.23: SolidWorks Split Menu • Right click on the cut plane and select “Export to DXF/DEG” to export the projection of the side profile as shown in fig 1.24 below: 27 Figure 1.24: Export Side Profile • The top plane of the part is the projection from above, therefore, skip the split command and directly right click on the plane and export: Figure 1.25: Export Top Profile 2. Open AutoCAD, increase the precision by clicking the top left red AutoCAD sign, select “Drawing Utilities → Units” and increase the precision to 4 decimal points, as shown in fig 1.26 below. 28 Figure 1.26: Increase AutoCAD Precision 3. 4. 5. 6. Import the dxf files from SolidWorks. Scale the drawing by 25.4 if the SolidWorks file is in inches. Select all objects and run “Explode” command. Select all Spline (if any), right click and select “Spline → Convert to Polyline” as shown in fig 1.27 below. When ask to enter accuracy, enter 1 and press enter. Figure 1.27: Convert Spline 7. Explode again (explode the polylines converted from spline). 29 8. Rotate and delete overlapping lines if necessary. 9. Select all lines and run “eattext” command, a window similar to fig 1.28 should pop up, select “Edit an existing data extraction” then click the browse button. Figure 1.28: Eattext Page 1 10. Browse for the extraction templates, select the template with or without arc depending on the drawing. (The templates are included in the “User Package.zip” from https://github.com/ythuang96/FoamCutter) Figure 1.29: Browse for Data Extraction Templates 11. On page 2, check “include current drawing”, select the one that is not the current drawing (if any) and click remove. 30 Figure 1.30: Eattext Page 2 Step 1 12. Then select the current drawing and click “Next”. Figure 1.31: Eattext Page 2 Step 2 13. On page 3, make sure the drawing only has lines, or only has lines and arcs, depending on the template used. If not, cancel the process, fix the drawing, and redo the “eattext” command. Figure 1.32: Eattext Page 3 14. Continue clicking “Next” until reaching page 6, name the data point file and save to the desired location. Click “Next” then “Finish”. 31 Figure 1.33: Eattext Page 6 15. Put the .csv file from AutoCAD into the same folder as the MatLAB script “DBF_foamcutter_general_shape.m” (provided in the “User Package.zip” from https://github.com/ythuang96/FoamCutter) and run the MatLAB code. Follow the prompts, and the G-code should be ready. 32 Chapter 2 Operate the FoamCutter 1 Switches The foamcutter has 3 switches installed as shown in fig 2.1 below. Figure 2.1: Three Switches of the Foamcutter • Left most switch: Exit switch Only operational when the foamcutter software is running. Hold the switch for 4 seconds will stop all motor movement, exit the foamcutter software and shutdown the Raspberry Pi. 33 • Middle switch: Pause switch Only operational when the foamcutter software is running. Toggle the switch up to pause the motor movements. Toggle the switch down to resume the motor movements. • Right most switch: Wire Control switch Operational regardless of foamcutter software. Toggle the switch up will enable the wire heating when the wire power supply is turned on and connected. Toggle the switch down will enable the foamcutter software to control the wire heating. The software automatically turns on the wire heating when starting the cut and turns off the wire heating when cut ends. This switch should generally be left at the down position for safety. 2 Start a Cut Please follow the steps below to operate the foamcutter: 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 3 Prepare the G-code following guide from Chapter 1 Section 4. Assemble the foamcutter following guide from Chapter 1 Section 2. Check that the Wire Control switch (right most switch) is toggled to the down position. Plug in the power cord. Plug in the power supply for the hot wire but do not turn it on. On your laptop, connect to the WiFi network named “foamcutter”, with password “ucsdaiaadbf or connect to the ethernet port to the ethernet port of the laptop with a ethernet cable. Open PuTTY and connect to the Pi with user name “pi” and password “ucsdaiaadbf ”. Open WinSCP and connect to the Pi. Upload the G-code file to the Pi folder “/home/pi”. In PuTTY, run: sudo foamcutter The software will first check the Pause switch status and will prompt to toggle the switch down if needed. The software will then ask to perform the homing process. Double check that the Molex connectors are connected, then press enter to perform homing. The software will then enter the main menu, follow the prompts to select desired options to perform cuts or move the wire. Secure the Foam The foam can be secured with acrylic plates with thumb screws on the three sides. Follow the steps below to secure the foam on the work panel: 1. Measure the width of the foam that need to be cut. 34 2. Set one acrylic plate half of the width of the foam so that the foam will be centered on the work panel. Make sure both ruler reads the same so that the acrylic plate is in the vertical direction as shown in fig 2.2 below. Figure 2.2: Secure the Foam Step 2 3. Secure the first plate with 3 thumb screws. Do not over tighten the thumbscrews, or the threads on the work panel might be destroyed. 4. Place the foam on the work panel with one side firmly against the guiding block and another side firmly against the first acrylic plate. 5. Place a second acrylic plate firmly against the foam and secure with 3 thumb screws as shown in fig 2.3 below. 35 Figure 2.3: Secure the Foam Step 5 6. Secure the back of the foam with another acrylic plate and secure with 3 thumb screws as shown in fig 2.4 below. Figure 2.4: Secure the Foam Step 6 7. Place a small piece of aluminum block on top of the foam to prevent vertical movement as shown in fig 2.5 below. 36 Figure 2.5: Secure the Foam Step 7 8. To secure very small pieces of foam, one of the acrylic plates can be flipped as shown in fig 2.6 below. Figure 2.6: Secure the Foam Step 6 4 End a Cut To the cutting operation half way through a G-code: • In the PuTTY session, press “Control + C”. This will stop the cut and exit the program. It will also ask if desired to shutdown the Pi immediately. 37 • Or hold the Exit switch for more than 3 seconds. This will stop the cut and exit the program, but will also immediately shutdown the Pi. To exit the program after the cut is finished, apart form the two options listed above, the user can also select the menu options to return to the main menu, and select the third option to exit the program. It will also ask if desired to shutdown the Pi immediately. 5 Cleaning Up After the Pi is properly turned off by: • Press Enter when seeing “If you would like to shutdown the Pi now. Please press ENTER. Otherwise, press ’n’ then press ENTER:” when exiting the foamcutter software. • Or run “sudo shutdown now” in PuTTY. the green LED should flash a couple of times indicating the operating system is reading/writing to the SD card. After the green LED turns off, unplug the power cord and wrap it around the acrylic electronics box. Also remember to turn off the power supply for the hot wire and wrap the power cables for the hot wire around the two screws. 38 Chapter 3 Raspberry Pi Setup Note: All steps in this chapter are already performed on the currently used Raspberry Pi, and do not need to be performed again unless the Raspberry Pi or the SD card is damaged and needs replacement. 1 Install Raspbian on the Raspberry Pi Raspbian is one of the recommended operating system for the Raspberry Pi. The following steps are used to install Raspbian and setup the Pi. 1. Perform the Laptop setup guide in Chapter 1 Section 1 if not already completed. 2. Download the Raspbian Sketch Lite version at: https://www.raspberrypi.org/downloads/raspbian/ The Lite version does not have desktop support, but should be sufficient for the foamcutter. 3. Download and install Etcher at https://etcher.io/. 4. Plug in the SD card into a laptop and run Etcher. 5. In Etcher, select the raspbian image just downloaded, then select the SD card. Lastly, click flash. 6. Wait for flash to complete, the SD card should now have two partition with one named “boot”. 7. Enable SSH on the Pi by creating a file name “ssh” without any extension in the partition named “boot” on the SD card. 8. Unplug the SD card from the laptop and plug into the Pi. Connect a Ethernet cable from the Pi to same router your laptop is currently connected to. 9. Connect power to the Pi. 10. SSH into the Pi by opening up PuTTY and create a new session with host name: “raspberrypi.local”. Note on using PuTTY: to copy text from PuTTY, simply select the text, do not press “Control + C”. To paste text to PuTTY, simply right click mouse, 39 do not press “Control + V”. 11. Enter the user name “pi” then password “raspberry”. 12. Change the log in password: enter “passwd” on the command line and press Enter. Enter the current password “raspberry”, then enter the new password “ucsdaiaadbf ” twice. 13. Change the name of the Pi by entering “sudo raspi-config”, select “Network Options → Hostname”, then enter the new hostname: “foamcutter”, then press Enter. 14. Select “Finish”, then press Enter. When asked to reboot, select “Yes”. 2 Install WiringPi on the Raspberry Pi WiringPi is a GPIO Interface library written for the Raspberry Pi. A complete guide can be found at: http://wiringpi.com/. To install on the pi, follow the complete guide at: http://wiringpi.com/download-and-install/ or follow the steps below: 1. Power on the Pi and open PuTTY on your laptop. 2. Double click on the saved PuTTY session named “foamcutter” as shown in fig 3.1 below. Figure 3.1: SSH to the Raspberry Pi 3. Enter user name “pi” and password “ucsdaiaadbf ”. 4. Update the operating system using the following two command: • sudo apt-get update • sudo apt-get upgrade 5. Install GIT using: 40 • sudo apt-get install git-core 6. Install WiringPi using: • cd • git clone git://git.drogon.net/wiringPi • cd /wiringPi • ./build 7. Completed when seeing: All Done. NOTE: To compile programs with wiringPi, you need to add: -lwiringPi to your compile line(s) To use the Gertboard, MaxDetect, etc. code (the devLib), you need to also add: -lwiringPiDev to your compile line(s). 3 Making the Pi into a Wireless Hotspot Do not perform the following procedure. This method seems to have unsolved connection problems and is not recommended currently. Connection through an Ethernet cable is recommended. Making the Pi into a Wireless Hotspot will enable operation of the foamcutter even in areas without a wireless network. The following steps are adapted from: https://elinux.org/RPIWireless-Hotspot 1. SSH into the Pi via PuTTY. 2. Run: sudo apt-get install hostapd udhcpd 3. Run: sudo nano /etc/udhcpd.conf This will open a text editor, use keyboard instead of mouse to navigate. • Find the line starting with interface and change to interface wlan0 • Find the line starting with opt router and change to opt router 192.168.42.1 • Press Control + O then Enter then Control + X to save and close file. 4. Run: sudo nano /etc/default/udhcpd • Add # to the front of the line DHCPD_ENABLED="no" • Press Control + O then Enter then Control + X to save and close file. 5. Run: sudo ifconfig wlan0 192.168.42.1 6. Run: sudo nano /etc/network/interfaces • Add the following lines to the bottom of the file if the line iface wlan0 inet dhcp is not present, otherwise replace it: iface wlan0 inet static address 192.168.42.1 41 netmask 255.255.255.0 • Add # to the beginning of each of the following lines if present: allow-hotplug wlan0 wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf iface default inet manual • Press Control + O then Enter then Control + X to save and close file. 7. Run: sudo nano /etc/hostapd/hostapd.conf • Add the following lines to the bottom of the file: interface=wlan0 driver=nl80211 ssid=foamcutter hw_mode=g channel=6 macaddr_acl=0 auth_algs=1 ignore_broadcast_ssid=0 wpa=2 wpa_passphrase=ucsdaiaadbf wpa_key_mgmt=WPA-PSK rsn_pairwise=CCMP 8. 9. 10. 11. 12. channel=1 ieee80211n=1 wmm_enabled=1 ht_capab=[HT40+][SHORT-GI-20][DSSS_CCK-40] • Press Control + O then Enter then Control + X to save and close file. Run: sudo nano /etc/default/hostapd • Change the line from #DAEMON_CONF="" to DAEMON_CONF="/etc/hostapd/hostapd.conf" • Press Control + O then Enter then Control + X to save and close file. Run: sudo nano /etc/default/hostapd • Add net.ipv4.ip_forward=1 to the bottom of the file. • Press Control + O then Enter then Control + X to save and close file. Run: sudo update-rc.d hostapd enable Run: sudo update-rc.d udhcpd enable Run: sudo reboot 42 Appendices 43 Appendix A C Code for the Foamcutter Program 1 foamcutter.c The C code for the foamcutter program, foamcutter.c is attached below. Verison 1.0.0, lasted updated: 6/30/2018. 44 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 /*********************************************************************** * FoamCutter * This code is written for Design/Build/Fly CNC foamcutter. * Written by Yuting Huang (ythuang96@gmail.com). * Please report any bug to my email address. * * Last update: 6/30/2018 * * Current Version: V 1.0.0 ***********************************************************************/ #define VERSION_A 1 #define VERSION_B 0 #define VERSION_C 0 #include "foamcutter_setup.h" typedef enum state_t{ HOMED, GCODE, EXITING } state_t; typedef struct position_t{ int32_t LX, LY, RX, RY; } position_t; typedef struct speed_t{ float LX, LY, RX, RY; } speed_t; typedef struct coord_t{ float LX_old, LY_old, RX_old, RY_old; float LX, LY, RX, RY; } coord_t; typedef struct coord_lim_t{ float LX_max, LY_max, RX_max, RY_max; float LX_min, LY_min, RX_min, RY_min; } coord_lim_t; /*********************************************************************** ************************** GLOBAL VARIABLES **************************** ***********************************************************************/ state_t state_; position_t target_position_; position_t current_position_; position_t reached_position_; position_t stop_; speed_t set_speed_; coord_t coord_; coord_lim_t coord_lim_; float coord_offset_x_; float coord_offset_y_; int gcode_menu_option_; int ETA_; struct timespec start_time_; FILE *ptr_file_; int state_STOP_ = 0; // Threads pthread_t LX_thread; pthread_t LY_thread; pthread_t RX_thread; pthread_t RY_thread; pthread_t printing_thread; pthread_t cut_manager; pthread_t switch_thread; struct sched_param params_motor_thread; struct sched_param params_print_thread; struct sched_param params_cut_manager; struct sched_param params_switch_thread; /*********************************************************************** *********************** FUNCTION DECLARATIONS ************************** ***********************************************************************/ // THREADS void* LX_thread_func(void* ptr); void* LY_thread_func(void* ptr); void* RX_thread_func(void* ptr); void* RY_thread_func(void* ptr); void* cut_manager_func(void* ptr); void* print_func(void* ptr); void* switch_thread_func(void* ptr); // SYSTEM FUNCTIONS void initialize_pin(); void home(); int loadtext(char* filename); int check_cord(char* str); int allreached(); void stop_all(); float cut_length_func(); 45 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 void drive(int pin_pul, int pin_dir, float speed, int32_t delta_pulse, int* ptr_current, int* ptr_stop, int polarity ); void cut_gcode(char* filename); void moveto(float x, float y); // MENU FUNCTIONS void main_menu(); void gcode_menu(); void move_menu(); int menu(int numb_of_options); int menu_enter(); int menu_yes(); int menu_enter_one(float* output, char* string); int menu_enter_two(float* output1, float* output2, char* string); // OTHER FUNCTIONS void nsleep(uint64_t ns); int file_filter(const struct dirent *entry); void removespace(char* str); void SigHandler(int dummy); float max(float a, float b); float min(float a, float b); void print_time(int sec); int str2f(char* str, float* output); /*********************************************************************** ******************************** MAIN ********************************** ***********************************************************************/ int main(){ // Setup GPIO pins if (wiringPiSetupGpio () == -1) { printf("Initialization failed. Most likely you are not root\n"); printf("Please remember to use 'sudo foamcutter'.\n"); return 1 ; } initialize_pin(); digitalWrite(PIN_RELAY, LOW); // Setup signal handler for CTRL+C signal(SIGINT, SigHandler); // Start motor threads params_motor_thread.sched_priority = 90; pthread_setschedparam(LX_thread, SCHED_FIFO, ¶ms_motor_thread); pthread_create(&LX_thread, NULL, LX_thread_func, (void*) NULL); pthread_setschedparam(LY_thread, SCHED_FIFO, ¶ms_motor_thread); pthread_create(&LY_thread, NULL, LY_thread_func, (void*) NULL); pthread_setschedparam(RX_thread, SCHED_FIFO, ¶ms_motor_thread); pthread_create(&RX_thread, NULL, RX_thread_func, (void*) NULL); pthread_setschedparam(RY_thread, SCHED_FIFO, ¶ms_motor_thread); pthread_create(&RY_thread, NULL, RY_thread_func, (void*) NULL); stop_.LX = stop_.LY = stop_.RX = stop_.RY = 0; // Print Header printf("\n"); printf("+---------------------------------------------------------------+\n"); printf("| DBF Foamcutter Program by Yuting Huang |\n"); printf("| Current Version is V%d.%d.%d |\n" \ , VERSION_A, VERSION_B, VERSION_C); printf("| Contact me at ythuang96@gmail.com to report bugs |\n"); printf("| |\n"); printf("|---------------------------------------------------------------|\n"); printf("| Brief User Instructions |\n"); printf("| At any time in the program: |\n"); printf("| 1. Press CTRL+C to exit the prgram |\n"); printf("| 2. Long press EXIT button exit the prgram and shutdown the Pi |\n"); printf("| 3. Toggle PAUSE button to pause/resume all motor momevments |\n"); printf("+---------------------------------------------------------------+\n\n"); // Check Pause Switch int counter1 = 0; if (state_ != EXITING) { for (int i = 1; i<= 21; i++) { if (digitalRead(PIN_PAUSE)) counter1 ++; nsleep(500000); } if (counter1 > 10) printf("Please toggle the PAUSE switch to resume\n\n"); } while (state_ != EXITING && counter1 > 10) { counter1 = 0; for (int i = 1; i<= 21; i++) { if (digitalRead(PIN_PAUSE)) counter1 ++; nsleep(500000); } } // Start Switch Thread 46 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 params_switch_thread.sched_priority = 50; pthread_setschedparam(switch_thread, SCHED_FIFO, ¶ms_switch_thread); pthread_create(&switch_thread, NULL, switch_thread_func, (void*) NULL); // Home the system if (state_ != EXITING) home(); while (state_ != EXITING) main_menu(); // stop all motors stop_all(); // end swtich thread pthread_join(switch_thread, NULL); // end all motor threads pthread_join(LX_thread, NULL); pthread_join(LY_thread, NULL); pthread_join(RX_thread, NULL); pthread_join(RY_thread, NULL); // print exit messages printf("EXIT successful, Thank you for using the FoamCutter program.\n"); if (state_STOP_) { printf("\nShuting down ... ...\n"); system("shutdown -P now"); return 0; } printf("\nIf you would like to shutdown the Pi now. Please press ENTER.\n"); printf("Otherwise, press 'n' then press ENTER: "); fflush(stdout); /****************** SHUTDOWN MENU OPTIONS ****************************/ fd_set input_set; struct timeval timeout; timeout.tv_sec = 10; timeout.tv_usec = 0; // Listening for input stream for any activity FD_ZERO(&input_set); FD_SET(0, &input_set); while (!select(1, &input_set, NULL, NULL, &timeout)) { timeout.tv_sec = 10; FD_ZERO(&input_set ); FD_SET(0, &input_set); } // get input char input_option[256]; fgets(input_option,256,stdin); // determine length of input int i; for(i=0; input_option[i]!='\0'; i++); i --; if (i == 1 && (input_option[0] == 'n' || input_option[0] == 'N')) { // if chose not to shutdown printf("\nOk, Please remember to use 'sudo shutdown now'\n"); printf("to shutdown the Pi before unpluging the power.\n\n"); } else { // chose to shutdown printf("\nShuting down ... ...\n"); system("shutdown -P now"); } return 0; } /*********************************************************************** ****************************** THREADS ********************************* ***********************************************************************/ void* LX_thread_func(void* ptr){ while (state_ != EXITING) { if (set_speed_.LX == 0 || reached_position_.LX == 1 || stop_.LX == 1) { if (current_position_.LX == target_position_.LX){ reached_position_.LX = 1; } digitalWrite(PIN_LX_PUL, LOW); nsleep(1000000); } else { drive(PIN_LX_PUL, PIN_LX_DIR, set_speed_.LX, \ target_position_.LX - current_position_.LX, \ &(current_position_.LX), &(stop_.LX), POLARITY_LX); if (!stop_.LX) reached_position_.LX = 1; } } return NULL; } void* LY_thread_func(void* ptr){ while (state_ != EXITING) { if (set_speed_.LY == 0 || reached_position_.LY == 1 || stop_.LY == 1) { if (current_position_.LY == target_position_.LY){ reached_position_.LY = 1; } digitalWrite(PIN_LY_PUL, LOW); nsleep(1000000); } else { 47 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 drive(PIN_LY_PUL, PIN_LY_DIR, set_speed_.LY, \ target_position_.LY - current_position_.LY, \ &(current_position_.LY), &(stop_.LY), POLARITY_LY); if (!stop_.LY) reached_position_.LY = 1; } } return NULL; } void* RX_thread_func(void* ptr){ while (state_ != EXITING) { if (set_speed_.RX == 0 || reached_position_.RX == 1 || stop_.RX == 1) { if (current_position_.RX == target_position_.RX){ reached_position_.RX = 1; } digitalWrite(PIN_RX_PUL, LOW); nsleep(1000000); } else { drive(PIN_RX_PUL, PIN_RX_DIR, set_speed_.RX, \ target_position_.RX - current_position_.RX, \ &(current_position_.RX), &(stop_.RX), POLARITY_RX); if (!stop_.RX) reached_position_.RX = 1; } } return NULL; } void* RY_thread_func(void* ptr){ while (state_ != EXITING) { if (set_speed_.RY == 0 || reached_position_.RY == 1 || stop_.RY == 1) { if (current_position_.RY == target_position_.RY){ reached_position_.RY = 1; } digitalWrite(PIN_RY_PUL, LOW); nsleep(1000000); } else { drive(PIN_RY_PUL, PIN_RY_DIR, set_speed_.RY, \ target_position_.RY - current_position_.RY, \ &(current_position_.RY), &(stop_.RY), POLARITY_RY); if (!stop_.RY) reached_position_.RY = 1; } } return NULL; } void* cut_manager_func(void* ptr){ char buf[500]; while(state_ == GCODE && fgets(buf,500, ptr_file_)!=NULL){ while (state_ == GCODE && !allreached()) { nsleep(10000000); } // wait till last coord is reached removespace(buf); // remove spaces if (!strncmp(buf,"G4P",3)) { // check if is a pause statement float temp; str2f(buf+3, &temp); nsleep((uint64_t)(floor(temp*1.0E9))); } else if (!strncmp(buf,"G1",2)) { // if a cut statement check_cord(buf); // read coordinates and update global coord_ // Update new target position target_position_.LX = (int32_t)floor((coord_.LX + coord_offset_x_) * MM2PULSE); target_position_.LY = (int32_t)floor((coord_.LY + coord_offset_y_) * MM2PULSE); target_position_.RX = (int32_t)floor((coord_.RX + coord_offset_x_) * MM2PULSE); target_position_.RY = (int32_t)floor((coord_.RY + coord_offset_y_) * MM2PULSE); // Calculate time to move to the next coord float dL = sqrt( pow(target_position_.LX - current_position_.LX,2.0) \ + pow(target_position_.LY - current_position_.LY,2.0)); float dR = sqrt( pow(target_position_.RX - current_position_.RX,2.0) \ + pow(target_position_.RY - current_position_.RY,2.0)); float time = (dL + dR)/2.0/FEEDRATE/MM2PULSE; // Set speed for all 4 axis set_speed_.LX = (target_position_.LX - current_position_.LX)/time/MM2PULSE; set_speed_.LY = (target_position_.LY - current_position_.LY)/time/MM2PULSE; set_speed_.RX = (target_position_.RX - current_position_.RX)/time/MM2PULSE; set_speed_.RY = (target_position_.RY - current_position_.RY)/time/MM2PULSE; if (state_ == GCODE) { // Start the cut by setting reached_position_ to 0 reached_position_.LX = reached_position_.LY = 0; reached_position_.RX = reached_position_.RY = 0; // wait till new coord is reached nsleep((uint64_t)(floor(time*1.0E9))); } } // end while --- read line by line } // if the state_ is still GCODE, but the while loop ended; // means the end of file is reached, and therefore cut is complete 48 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 if (state_ == GCODE) { state_ = HOMED;} return NULL; } void* print_func(void* ptr){ struct timespec current_time; int elapsed_time = 0; int remain_time; while(state_ == GCODE){ clock_gettime( CLOCK_REALTIME, ¤t_time); elapsed_time = current_time.tv_sec - start_time_.tv_sec; remain_time = ETA_ - elapsed_time; printf("\r"); printf("%7.3f/%8.3f|%7.3f/%8.3f|%7.3f/%8.3f|%7.3f/%8.3f| ", \ current_position_.LX/MM2PULSE*MM2IN , current_position_.LX/MM2PULSE, \ current_position_.LY/MM2PULSE*MM2IN , current_position_.LY/MM2PULSE, \ current_position_.RX/MM2PULSE*MM2IN , current_position_.RX/MM2PULSE, \ current_position_.RY/MM2PULSE*MM2IN , current_position_.RY/MM2PULSE); print_time(elapsed_time); printf(" | "); print_time(remain_time); printf(" |"); fflush(stdout); nsleep(1000000000); // run at 1 Hz } return NULL; } void* switch_thread_func(void* ptr){ int state_P = 0; int counter_P, counter_S; int counter_S2 = 0; while (state_ != EXITING) { counter_P = counter_S = 0; for (int i = 1; i<= 41; i++) { if (digitalRead(PIN_PAUSE)) counter_P ++; if (digitalRead(PIN_STOP )) counter_S ++; nsleep(500000); } if (counter_P > 25 && !state_P) { stop_all(); state_P = 1; } else if (counter_P <= 15 && state_P && state_ state_P = 0; stop_.LX = stop_.LY = stop_.RX = stop_.RY digitalWrite(PIN_RELAY, HIGH); } else if (counter_P <= 15 && state_P && state_ state_P = 0; stop_.LX = stop_.LY = stop_.RX = stop_.RY } == GCODE) { = 0; == HOMED) { = 0; if (counter_S > 25) counter_S2 ++; else counter_S2 = 0; if (counter_S2 == 100){ state_ = EXITING; stop_all(); state_STOP_ = 1; printf("\n\n**************************** EXITING PROGRAM ****************************\n"); } } return NULL; } /*********************************************************************** ************************** SYSTEM FUNCTIONS **************************** ***********************************************************************/ void initialize_pin(){ // Setup limit switch pins pinMode(PIN_LX_LIM,INPUT); pullUpDnControl(PIN_LX_LIM,PUD_DOWN); pinMode(PIN_LY_LIM,INPUT); pullUpDnControl(PIN_LY_LIM,PUD_DOWN); pinMode(PIN_RX_LIM,INPUT); pullUpDnControl(PIN_RX_LIM,PUD_DOWN); pinMode(PIN_RY_LIM,INPUT); pullUpDnControl(PIN_RY_LIM,PUD_DOWN); // Setup motor dive pins pinMode(PIN_LX_DIR,OUTPUT); pinMode(PIN_LX_PUL,OUTPUT); pinMode(PIN_LY_DIR,OUTPUT); pinMode(PIN_LY_PUL,OUTPUT); pinMode(PIN_RX_DIR,OUTPUT); pinMode(PIN_RX_PUL,OUTPUT); pinMode(PIN_RY_DIR,OUTPUT); pinMode(PIN_RY_PUL,OUTPUT); // Setup switches pins pinMode(PIN_PAUSE,INPUT); pullUpDnControl(PIN_PAUSE,PUD_DOWN); pinMode(PIN_STOP ,INPUT); pullUpDnControl(PIN_STOP ,PUD_DOWN); // Setup relay control pin pinMode(PIN_RELAY,OUTPUT); return; } void home(){ 49 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 // Print Header printf("I see the system is not homed yet, please press ENTER to home the system: fflush(stdout); if (menu_enter() == -2) return; // if EXITING state, end function printf("Homing ... "); fflush(stdout); "); // Home the system current_position_.LX = current_position_.LY = current_position_.RX = current_position_.RY = 0; reached_position_.LX = reached_position_.LY = reached_position_.RX = reached_position_.RY = 0; target_position_.LX = target_position_.LY = target_position_.RX = target_position_.RY = -640000; // Home X axis set_speed_.LY = set_speed_.RY = 0.0; set_speed_.LX = set_speed_.RX = -1.0; int counter1, counter2; int state1 = 0; int state2 = 0; while (state_ != EXITING && (!state1 || !state2)) { counter1 = counter2 = 0; for (int i = 1; i<= 41; i++) { if (digitalRead(PIN_LX_LIM)) counter1 ++; if (digitalRead(PIN_RX_LIM)) counter2 ++; nsleep(20000); } if (counter1 > 30 && !state1) { state1 = 1; stop_.LX = 1; set_speed_.LX = 0.0; current_position_.LX = LIM2ORIGIN_LX; reached_position_.LX = 1; } if (counter2 > 30 && !state2) { state2 = 1; stop_.RX = 1; set_speed_.RX = 0.0; current_position_.RX = LIM2ORIGIN_RX; reached_position_.RX = 1; } } if (state_ == EXITING) return; nsleep(200000000); target_position_.LX = LIM2ORIGIN_LX + 1000; target_position_.RX = LIM2ORIGIN_RX + 1000; set_speed_.LX = set_speed_.RX = +FEEDRATE; stop_.LX = stop_.RX = 0; reached_position_.LX = reached_position_.RX = 0; while (state_ != EXITING && (!reached_position_.LX || !reached_position_.RX)) { nsleep(1000000);} // Home Y axis if (state_ == EXITING) return; nsleep(500000000); set_speed_.LX = set_speed_.RX = 0.0; set_speed_.LY = set_speed_.RY = -1.0; counter1 = counter2 = state1 = state2 = 0; while (state_ != EXITING && (!state1 || !state2)) { counter1 = counter2 = 0; for (int i = 1; i<= 41; i++) { if (digitalRead(PIN_LY_LIM)) counter1 ++; if (digitalRead(PIN_RY_LIM)) counter2 ++; nsleep(20000); } if (counter1 > 30 && !state1) { state1 = 1; stop_.LY = 1; set_speed_.LY = 0.0; current_position_.LY = LIM2ORIGIN_LY; reached_position_.LY = 1; } if (counter2 > 30 && !state2) { state2 = 1; stop_.RY = 1; set_speed_.RY = 0.0; current_position_.RY = LIM2ORIGIN_RY; reached_position_.RY = 1; } } if (state_ == EXITING) return; nsleep(500000000); // Move to orgin if (state_ == EXITING) return; printf("All limits reached, Moving to Origin ... "); fflush(stdout); target_position_.LX = target_position_.LY = target_position_.RX = target_position_.RY = 0; 50 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 set_speed_.LX = set_speed_.LY = set_speed_.RX stop_.LX = stop_.LY = stop_.RX = stop_.RY = 0; = set_speed_.RY = +FEEDRATE; reached_position_.LY = reached_position_.RY = reached_position_.LX = reached_position_.RX = 0; while (state_ != EXITING && !allreached()) { nsleep(1000000);} if (state_ == EXITING) return; state_ = HOMED; printf("Homing Complete! \n\n"); return; } int loadtext(char* filename){ float pause_time = 0.0; float cut_length = 0.0; int asym_cut = 0; int error = 0; // set error to 1 will return to main menu float span; float coord_x_min; float coord_y_min; float width; float height; coord_lim_.LX_max coord_lim_.LY_max coord_lim_.RX_max coord_lim_.RY_max coord_.LX_old coord_.LY_old coord_.RX_old coord_.RY_old = = = = = = = = coord_lim_.LX_min coord_lim_.LY_min coord_lim_.RX_min coord_lim_.RY_min coord_.LX coord_.LY coord_.RX coord_.RY = = = = = = = = 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; 0.0; coord_offset_x_ = coord_offset_y_ = 0.0; /*************************** READ FILE *******************************/ FILE *ptr_file; char buf[500]; ptr_file =fopen(filename, "r"); int line_numb = 1; while (fgets(buf,500, ptr_file)!=NULL){ // get line by line removespace(buf); // remove spaces if (!strncmp(buf,"G4P",3)) { // check if is a pause statement float temp; if (str2f(buf+3, &temp)) {pause_time += temp;} else { printf("G-code error at line %d. Returning to Main Menu.\n\n",line_numb); error = 1; return 1; } } else if (!strncmp(buf,"G1",2)) { // if a cut statement if (check_cord(buf)) { // check if statement is valid cut_length += cut_length_func(); if (fabs(coord_.LX - coord_.RX) + fabs(coord_.LY - coord_.RY) > 0.0001 ) { asym_cut ++; } // update the max/min coordinates coord_lim_.LX_max = max(coord_lim_.LX_max, coord_.LX); coord_lim_.LX_min = min(coord_lim_.LX_min, coord_.LX); coord_lim_.LY_max = max(coord_lim_.LY_max, coord_.LY); coord_lim_.LY_min = min(coord_lim_.LY_min, coord_.LY); coord_lim_.RX_max = max(coord_lim_.RX_max, coord_.RX); coord_lim_.RX_min = min(coord_lim_.RX_min, coord_.RX); coord_lim_.RY_max = max(coord_lim_.RY_max, coord_.RY); coord_lim_.RY_min = min(coord_lim_.RY_min, coord_.RY); } // if not a valid line, print error message and stop reading else { printf("G-code error at line %d. Returning to Main Menu.\n\n",line_numb); error = 1; return 1; } } line_numb ++; } // end while --- read line by line fclose(ptr_file); // read complete if (!error && state_ != EXITING) { // if no reading error occured coord_x_min = min(coord_lim_.LX_min,coord_lim_.RX_min); coord_y_min = min(coord_lim_.LY_min,coord_lim_.RY_min); } // end if --- check error /**************** FIRST: check if offset is needed *******************/ // check x if (coord_x_min < 0 && state_ != EXITING && !error){ printf("I see you have a min x coordinate of %7.3f in (%8.3f mm)\n", \ coord_x_min*MM2IN, coord_x_min); 51 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 printf("A negative value is not allowed\n"); printf("You can use the minimum offset, or enter one yourself\n"); printf("Would you like to use the minimum offset for x?\n"); if (!menu_yes()){ while(state_ != EXITING) { int temp = menu_enter_one(&coord_offset_x_,"Please enter the x offset"); if (temp == -1) {printf("Invalid input, please enter again.\n\n");} else if (temp == 1) { if (coord_offset_x_ < - coord_x_min) { printf("Insufficient x offset, please enter a bigger x offset\n\n"); } else break; } } } else { coord_offset_x_ = - coord_x_min;} } // check y if (coord_y_min < 0 && state_ != EXITING && !error){ printf("I see you have a min y coordinate of %7.3f in (%8.3f mm)\n", \ coord_y_min*MM2IN, coord_y_min); printf("A negative value is not allowed\n"); printf("You can use the minimum offset, or enter one yourself\n"); printf("If you are cutting part of a 3-piece wing\n"); printf("I would recommend enter the same offset for all 3 pieces.\n"); printf("It will make the vaccum bagging easier\n\n"); printf("Would you like to use the minimum offset for y?\n"); if (!menu_yes()){ while(state_ != EXITING) { int temp = menu_enter_one(&coord_offset_y_,"Please enter the y offset"); if (temp == -1) {printf("Invalid input, please enter again.\n\n");} else if (temp == 1) { if (coord_offset_y_ < - coord_y_min) { printf("Insufficient y offset, please enter a bigger y offset\n\n"); } else break; } } } else { coord_offset_y_ = - coord_y_min;} } coord_y_min += coord_offset_x_; coord_y_min += coord_offset_y_; coord_lim_.LX_max += coord_offset_x_; coord_lim_.LX_min += coord_offset_x_; coord_lim_.LY_max += coord_offset_y_; coord_lim_.LY_min += coord_offset_y_; coord_lim_.RX_max += coord_offset_x_; coord_lim_.RX_min += coord_offset_x_; coord_lim_.RY_max += coord_offset_y_; coord_lim_.RY_min += coord_offset_y_; if (coord_lim_.LX_max > X_MAX || coord_lim_.RX_max > X_MAX) { printf("X axis out of bound, maximum X distance is 29 inches\n"); printf("Returning to G-code Menu\n\n"); error = 1; gcode_menu_option_ = -1; } if (coord_lim_.LY_max > Y_MAX || coord_lim_.RY_max > Y_MAX) { printf("Y axis out of bound, maximum Y distance is 16 inches\n"); printf("Returning to G-code Menu\n\n"); error = 1; gcode_menu_option_ = -1; } /************** SECOND: determine and check foamsize *****************/ if(!asym_cut && state_ != EXITING && !error){ // if not an asymetric cut printf("I see this is a symmetric cut\n"); printf("The minimum require foam size is:\n"); width = coord_lim_.LX_max; height = coord_lim_.LY_max; printf("Width (x-direction): %7.3f in (%8.3f mm)\n", width*MM2IN, width); printf("Thickness (y-direction): %7.3f in (%8.3f mm)\n", height*MM2IN, height); printf("Please leave some extra space.\n"); } else if (asym_cut && state_ != EXITING && !error){ // if an asymetric cut printf("I see this is an asymmetric cut\n"); printf("The minimum require foam size depends on the span of the cut.\n"); printf("Please enter the span size of the cut.\n"); while(state_ != EXITING && menu_enter_one(&span,"Please enter the span size") == -1) { printf("Invalid input, please enter again.\n\n"); } width = min(coord_lim_.LX_max,coord_lim_.RX_max) + \ fabs(coord_lim_.LX_max-coord_lim_.RX_max)*(CUTTERWIDTH + span)/2.0/CUTTERWIDTH; height = min(coord_lim_.LY_max,coord_lim_.RY_max) + \ fabs(coord_lim_.LY_max-coord_lim_.RY_max)*(CUTTERWIDTH + span)/2.0/CUTTERWIDTH; if (state_ != EXITING){ printf("Width (x-direction): %7.3f in (%8.3f mm)\n", width*MM2IN, width); printf("Thickness (y-direction): %7.3f in (%8.3f mm)\n", height*MM2IN, height); printf("Please leave some extra space.\n"); } } if(state_ != EXITING && !error){ printf("Does this look correct and matches your foam size?\n"); fflush(stdout); 52 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 if (!menu_yes()){ printf("Looks like there's something wrong. Returning to G-code Menu.\n\n"); error = 1; gcode_menu_option_ = -1; } } /******************* THIRD: review cut settings **********************/ if (state_ != EXITING && !error){ printf("***************************** CUT SETTINGS ******************************\n"); printf("G-code file: %s.\n", filename); if (asym_cut) { printf("Asymetric Cut of span %7.3f in (%8.3f mm)\n", span*MM2IN, span); } else {printf("Symmetric Cut\n");} printf("Minimum Foam Size:\n"); printf("Width (x-direction): %7.3f in (%8.3f mm)\n", width*MM2IN, width); printf("Thickness (y-direction): %7.3f in (%8.3f mm)\n", height*MM2IN, height); if (coord_offset_x_) { printf("x offset of %7.3f in (%8.3f mm)", coord_offset_x_*MM2IN, coord_offset_x_); } else {printf("No x offset");} printf(" and "); if (coord_offset_y_) { printf("y offset of %7.3f in (%8.3f mm)", coord_offset_y_*MM2IN, coord_offset_y_); } else {printf("No y offset");} printf("\n"); printf("Estimate total time of cut: "); ETA_ = round(pause_time + cut_length/FEEDRATE ); print_time(ETA_); printf("\n"); if (asym_cut) {printf("Please make sure the foam is centered.\n");} printf("\nWould you like to start the cut?\n"); if (!menu_yes()){ printf("OK, setting incorrect. Returning to G-code Menu.\n\n"); error = 1; gcode_menu_option_ = -1; } } return error; } int check_cord(char* str){ coord_.LX_old = coord_.LX; coord_.LY_old = coord_.LY; coord_.RX_old = coord_.RX; coord_.RY_old = coord_.RY; char* ptr_X = strchr(str, 'X'); char* ptr_Y = strchr(str, 'Y'); char* ptr_Z = strchr(str, 'Z'); char* ptr_A = strchr(str, 'A'); if (ptr_X && ptr_Y && ptr_Z && ptr_A && \ (ptr_X < ptr_Y) && (ptr_Y < ptr_Z) &&(ptr_Z < ptr_A) ){ char temp1[20], temp2[20], temp3[20], temp4[20]; float tempf1, tempf2, tempf3, tempf4; strncpy(temp1,ptr_X+1,ptr_Y-ptr_X-1); temp1[(ptr_Y-ptr_X-1)] = '\0'; strncpy(temp2,ptr_Y+1,ptr_Z-ptr_Y-1); temp2[(ptr_Z-ptr_Y-1)] = '\0'; strncpy(temp3,ptr_Z+1,ptr_A-ptr_Z-1); temp3[(ptr_A-ptr_Z-1)] = '\0'; strncpy(temp4,ptr_A+1,10 ); if(str2f(temp1, &tempf1) && str2f(temp2, &tempf2) && \ str2f(temp3, &tempf3) && str2f(temp4, &tempf4)){ coord_.LX = tempf1; coord_.LY = tempf2; coord_.RX = tempf3; coord_.RY = tempf4; return 1; } else return 0; } else return 0; } int allreached() { return reached_position_.LX * reached_position_.LY * reached_position_.RX * reached_position_.RY; } void stop_all() { stop_.LX = stop_.LY = stop_.RX = stop_.RY = 1; digitalWrite(PIN_RELAY,LOW); return; } float cut_length_func(){ float L_length = sqrt(pow((coord_.LX - coord_.LX_old),2.0) + pow((coord_.LY - coord_.LY_old),2.0)); float R_length = sqrt(pow((coord_.RX - coord_.RX_old),2.0) + pow((coord_.RY - coord_.RY_old),2.0)); return (L_length + R_length)/2.0; } void drive(int pin_pul, int pin_dir, float speed, int32_t delta_pulse, int* ptr_current, int* ptr_stop, int polarity ){ int inc; if (speed*polarity > 0) { digitalWrite(pin_dir, HIGH); inc = 1*polarity;} 53 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 else if (speed*polarity < 0) { digitalWrite(pin_dir, LOW ); inc = -1*polarity;} // the time between pulses, calculated from speed // 4000 is the sleep time in the loop; // 100000 is the approximate code execution time; uint64_t sleep_time = floor(1000000000.0/MM2PULSE/fabs(speed)) - 4000 - 100000; nsleep(5000); // ensure dir pin leads by at least 5 microsec for (int i = 0; i < abs(delta_pulse); i++) { // send out desired number of pulses digitalWrite(pin_pul, HIGH); nsleep(4000); // ensure pulse width of at least 2 microsec digitalWrite(pin_pul, LOW ); *ptr_current += inc; if (*ptr_stop) break; // stop the motor if stop is a 1; nsleep(sleep_time); } return; } void cut_gcode(char* filename){ state_ = GCODE; if (state_ == EXITING) return; moveto(-5.0,0.0); while (state_ != EXITING && !allreached()){ nsleep(1000000);} if (state_ == EXITING) return; printf("Please connect and turn on the power supply for the hot wire\n"); printf("Please make sure the voltage is approximately 10V, and press ENTER to continue: fflush(stdout); if (menu_enter() == -2) return; digitalWrite(PIN_RELAY,HIGH); printf("Please now adjust the power supply to the desired current.\n"); printf("Recommend 2.1 to 2.3 Amps, depending on the cut span.\n"); printf("Use higher current for wider cuts.\n"); printf("Increase current if wire bows significantly.\n"); printf("Press ENTER to start cutting: "); fflush(stdout); if (menu_enter() == -2) return; printf("Heating wire ... ..."); fflush(stdout); if (state_ != EXITING) nsleep(5000000000); if (state_ == EXITING) return; printf(" Cut Starting\n"); moveto(0.0,0.0); while (state_ != EXITING && !allreached()) nsleep(1000000); if (state_ == EXITING) return; "); coord_.LX_old = coord_.LX = 0.0; coord_.LY_old = coord_.LY = 0.0; coord_.RX_old = coord_.RX = 0.0; coord_.RY_old = coord_.RY = 0.0; clock_gettime( CLOCK_REALTIME, &start_time_); ptr_file_ =fopen(filename, "r"); printf(" LX | LY printf(" in / mm | in / mm | | in RX / mm | | in RY / mm | | Elapsed params_print_thread.sched_priority = 40; params_cut_manager.sched_priority = 99; pthread_setschedparam(printing_thread, SCHED_FIFO, ¶ms_print_thread); pthread_create(&printing_thread, NULL, print_func, (void*) NULL); pthread_setschedparam(cut_manager, SCHED_FIFO, ¶ms_cut_manager); pthread_create(&cut_manager, NULL, cut_manager_func, (void*) NULL); while (state_ == GCODE) nsleep(100000); fclose(ptr_file_); // read complete if (state_ != EXITING) moveto(-5.0,0.0); while (state_ != EXITING && !allreached()) nsleep(1000000); digitalWrite(PIN_RELAY,LOW); if (state_ != EXITING) moveto( 0.0,0.0); while (state_ != EXITING && !allreached()) nsleep(1000000); if (state_ != EXITING) printf("\nCut Complete, Returning to Main Menu.\n\n"); pthread_join(cut_manager, NULL); pthread_join(printing_thread, NULL); return; } void moveto(float x, float y){ target_position_.LX = target_position_.RX = (int32_t)floor(x * MM2PULSE); target_position_.LY = target_position_.RY = (int32_t)floor(y * MM2PULSE); // Set speed for all 4 axis if (target_position_.LX > current_position_.LX) set_speed_.LX = +FEEDRATE; else if (target_position_.LX < current_position_.LX) set_speed_.LX = -FEEDRATE; else if (target_position_.LX == current_position_.LX) set_speed_.LX = 0.0; if (target_position_.LY > current_position_.LY) set_speed_.LY = +FEEDRATE; else if (target_position_.LY < current_position_.LY) set_speed_.LY = -FEEDRATE; else if (target_position_.LY == current_position_.LY) set_speed_.LY = 0.0; 54 TIME |\n"); | Remaining |\n"); 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 if (target_position_.RX > current_position_.RX) set_speed_.RX = +FEEDRATE; else if (target_position_.RX < current_position_.RX) set_speed_.RX = -FEEDRATE; else if (target_position_.RX == current_position_.RX) set_speed_.RX = 0.0; if (target_position_.RY > current_position_.RY) set_speed_.RY = +FEEDRATE; else if (target_position_.RY < current_position_.RY) set_speed_.RY = -FEEDRATE; else if (target_position_.RY == current_position_.RY) set_speed_.RY = 0.0; if (state_ != EXITING) { // Start the cut by setting reached_position_ to 0 reached_position_.LX = reached_position_.LY = 0; reached_position_.RX = reached_position_.RY = 0; } return; } /*********************************************************************** *************************** MENU FUNCTIONS ***************************** ***********************************************************************/ void main_menu() { printf("******************************* MAIN MENU *******************************\n"); printf("Please choose from the following options:\n"); printf("a. Load and cut from G-Code;\n"); printf("b. Move wire to specified location;\n"); printf("c. Exit Program.\n"); printf("Please enter the corresponding letter and press ENTER key: "); fflush(stdout); switch (menu(3)) { case 0: {gcode_menu(); break; case 1: {move_menu(); break; case 2: {state_ = EXITING; break; case -1: {printf("Invalid option. Let's try again.\n\n"); break; case -2: { break; } return; } } } } } } void gcode_menu() { if (state_ != EXITING && (current_position_.LX || \ current_position_.LY || current_position_.RX || current_position_.RY)){ printf("I see the wire is not at origin. Plesse press ENTER to move wire to origin: fflush(stdout); if (menu_enter() != -2) { moveto(0.0,0.0); while (state_ != EXITING && !allreached()){ nsleep(1000000);} printf("Origin Reached\n\n"); } } gcode_menu_option_ = -1; int n = 10; while (state_ != EXITING && gcode_menu_option_ == -1){ printf("****************************** GCODE MENU *******************************\n"); // keep looping when menu selection is invalid struct dirent **namelist; n = scandir("/home/pi/", &namelist, file_filter, alphasort); // scan for files with .txt extension if (n == 0) { printf("I do not see any txt files in '/home/pi/' directory\n"); printf("Please put the desired gcode file in '/home/pi/' directory.\n"); printf("Returning to Main Menu.\n\n"); break; } else if (n > 9){ printf("Too many txt files in '/home/pi/' directory\n"); printf("Please clean it up.\n"); printf("Returning to Main Menu.\n\n"); break; } else{ printf("I see there are %d txt files listed below:\n\n",n); int i = 0; while (i++ < n){ printf("%d: %s\n", i, namelist[i-1]->d_name); } printf("0: None of the above, or Return to Main Menu\n\n"); printf("Please select one by entering the corresponding number then press ENTER: fflush(stdout); gcode_menu_option_ = menu(n+1); if (gcode_menu_option_ >= 1) { printf("You selected: '%s' switch (menu_yes()){ case 1: "); "); is that correct?\n",namelist[gcode_menu_option_-1]->d_name); 55 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 if (state_ != EXITING && !loadtext(namelist[gcode_menu_option_-1]->d_name) ) { if (state_ != EXITING) cut_gcode(namelist[gcode_menu_option_-1]->d_name); return; } break; case 0: printf("OK, Let try again\n\n"); gcode_menu_option_ = -1; break; } } else if (gcode_menu_option_ == 0 ) { printf("Please put the desired gcode file in the working directory.\n"); printf("Returning to Main Menu.\n\n"); break; } else if (gcode_menu_option_ == -1 ) { // Invalid input printf("Invalid Input, let's try again.\n\n"); } else if (gcode_menu_option_ == -2 ) { // EXITING state break; } // end of if --- menu input check } // end of if --- file number check } // end of while return; } // end of gcode_menu void move_menu(){ float x,y; while (state_ != EXITING){ float current_x = (current_position_.LX + current_position_.RX)/2.0/MM2PULSE; float current_y = (current_position_.LY + current_position_.RY)/2.0/MM2PULSE; printf("******************************* MOVE MENU *******************************\n"); printf("The current wire position is (x,y) = (%.3f,%.3f) in = (%.3f,%.3f) mm\n", \ current_x*MM2IN,current_y*MM2IN,current_x,current_y); printf("Please choose from the following options:\n"); printf("a. Move wire to a specific location relative to origin (Absolute Location);\n"); printf("b. Move wire to a specific location relative to current position (Increment);\n"); printf("c. Move wire to origin;\n"); printf("d. Return to Main Menu;\n"); printf("Please enter the corresponding letter and press ENTER key: "); fflush(stdout); switch (menu(4)) { case 0: { // Move to a specific location while(state_ != EXITING && \ (menu_enter_two(&x,&y,"Please enter the destination x and y coordinates RELATIVE TO ORIGIN") == -1 \ || x < 0 || y <0)) { printf("Invalid input, please enter again. Please note that negative destination is not allowed.\n\n"); } if (state_ != EXITING) { printf("| | Current | Increment | Destination |\n"); printf("| Inch | %7.3f, %7.3f | %7.3f, %7.3f | %7.3f, %7.3f |\n", \ current_x*MM2IN, current_y*MM2IN, (x-current_x)*MM2IN, (y-current_y)*MM2IN, x*MM2IN, y*MM2IN); printf("| MM | %8.3f, %8.3f | %8.3f, %8.3f | %8.3f, %8.3f |\n", \ current_x, current_y, (x-current_x), (y-current_y), x, y); printf("Continue?\n"); switch (menu_yes()){ case 1: if (state_ != EXITING ) { printf("Moving ... ..."); fflush(stdout); moveto(x,y); while (state_ != EXITING && !allreached()){ nsleep(1000000);} if (state_ != EXITING) printf(" Destination Reached\n\n"); } break; case 0: printf("OK, Let try again\n\n"); break; } } break; } // end case --- move to a specific location case 1: { // Move to a specific location while(state_ != EXITING && \ (menu_enter_two(&x,&y,"Please enter the destination x and y coordinates RELATIVE TO CURRENT POSITION") == -1 \ || x+current_x < 0 || y+current_y <0)) { printf("Invalid input, please enter again. Please note that negative destination is not allowed.\n\n"); } if (state_ != EXITING) { printf("| | Current | Increment | Destination |\n"); printf("| Inch | %7.3f, %7.3f | %7.3f, %7.3f | %7.3f, %7.3f |\n", \ current_x*MM2IN, current_y*MM2IN, x*MM2IN, y*MM2IN, (x+current_x)*MM2IN, (y+current_y)*MM2IN); printf("| MM | %8.3f, %8.3f | %8.3f, %8.3f | %8.3f, %8.3f |\n", \ current_x, current_y, x, y, (x+current_x), (y+current_y)); printf("Continue?\n"); switch (menu_yes()){ case 1: 56 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 if (state_ != EXITING ) { printf("Moving ... ..."); fflush(stdout); moveto(x+current_x,y+current_y); while (state_ != EXITING && !allreached()){ nsleep(1000000);} if (state_ != EXITING) printf(" Destination Reached\n\n"); } break; case 0: printf("OK, Let try again\n\n"); break; } } break; } // end case --- move to a specific location case 2: { //case --- move to origin if (!current_position_.LX && !current_position_.LY && \ !current_position_.RX && !current_position_.RY) { printf("Already at Origin\n\n"); } else{ printf("Move to Orignin. Continue?\n"); switch (menu_yes()){ case 1: printf("Moving ... ..."); fflush(stdout); moveto(0.0,0.0); while (state_ != EXITING && !allreached()){ nsleep(1000000);} if (state_ != EXITING) printf(" Origin Reached\n\n"); break; case 0: printf("OK, Let try again\n\n"); break; } } break; } // end case --- move to origin case -1: {printf("Invalid option. Let's try again.\n\n"); break; } default: {return; break; } } // end switch } // end while return; } int menu(int numb_of_options){ // pass in number of menu options, maximum of 10 options // return 0 to (numb_of_options - 1) if input is within the range // return -1 for invalid input // return -2 when state_ is exiting if (state_ == EXITING) return -2; fd_set input_set; struct timeval timeout; timeout.tv_sec = 10; timeout.tv_usec = 0; // Listening for input stream for any activity // If there FD_ZERO(&input_set ); FD_SET(0, &input_set); while (state_ != EXITING && !select(1, &input_set, NULL, NULL, &timeout)) { timeout.tv_sec = 1; FD_ZERO(&input_set ); FD_SET(0, &input_set); } if (state_ == EXITING) return -2; // get input char input_option[256]; fgets(input_option,256,stdin); // determine length of input int i; for(i=0; input_option[i]!='\0'; i++); i --; int result; if (i == 1) { if (input_option[0] >= 48 && input_option[0] <= 57) { result = input_option[0]-48; } else if(input_option[0] >= 65 && input_option[0] <= 74) { result = input_option[0]-65; } else if(input_option[0] >= 97 && input_option[0] <= 106) { result = input_option[0]-97; } else result = -1; } else result = -1; if (result >= numb_of_options) result = -1; printf("\n"); 57 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 return result; } int menu_enter(){ // return 1 if anthing is entered, including ENTER key // return 0 if m is entered // return -2 when state_ is exiting if (state_ == EXITING) return -2; fd_set input_set; struct timeval timeout; timeout.tv_sec = 10; timeout.tv_usec = 0; // Listening for input stream for any activity FD_ZERO(&input_set ); FD_SET(0, &input_set); while (state_ != EXITING && !select(1, &input_set, NULL, NULL, &timeout)) { timeout.tv_sec = 1; FD_ZERO(&input_set ); FD_SET(0, &input_set); } if (state_ == EXITING) return -2; // get input char input_option[256]; fgets(input_option,256,stdin); // determine length of input int i; for(i=0; input_option[i]!='\0'; i++); i --; if (i == 1 && (input_option[0] == 'm' || input_option[0] == 'M')) { printf("\n"); return 0; // return 0 if input is 'm' or 'M' } else {printf("\n"); return 1; } } int menu_yes(){ // return 1 if anthing is entered, including ENTER key // return 0 if n is entered // return -2 when state_ is exiting if (state_ == EXITING) return -2; printf("Press ENTER for YES, or 'n' then ENTER for NO: fd_set input_set; struct timeval timeout; timeout.tv_sec = 10; timeout.tv_usec = 0; "); fflush(stdout); // Listening for input stream for any activity FD_ZERO(&input_set ); FD_SET(0, &input_set); while (state_ != EXITING && !select(1, &input_set, NULL, NULL, &timeout)) { timeout.tv_sec = 1; FD_ZERO(&input_set ); FD_SET(0, &input_set); } if (state_ == EXITING) return -2; // get input char input_option[256]; fgets(input_option,256,stdin); // determine length of input int i; for(i=0; input_option[i]!='\0'; i++); i --; if (i == 1 && (input_option[0] == 'n' || input_option[0] == 'N')) { printf("\n"); return 0; // return 0 if input is 'm' or 'M' } else {printf("\n"); return 1;} } int menu_enter_one(float* output, char* string){ float scale = 1.0; if (state_ == EXITING) return -2; printf("Would you like to enter the coordinate in inches?\n"); switch (menu_yes()) { case 1: scale = 25.4; printf("%s in inches then press ENTER: ", string); break; case 0: scale = 1.0; printf("%s in millimeters then press ENTER: ", string); break; } fflush(stdout); if (state_ == EXITING) return -2; fd_set input_set; struct timeval timeout; timeout.tv_sec = 10; timeout.tv_usec = 0; // Listening for input stream for any activity // If there 58 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 FD_ZERO(&input_set ); FD_SET(0, &input_set); while (state_ != EXITING && !select(1, &input_set, NULL, NULL, &timeout)) { timeout.tv_sec = 1; FD_ZERO(&input_set ); FD_SET(0, &input_set); } if (state_ == EXITING) return -2; // get input char input_option[256]; fgets(input_option,256,stdin); // remove white spaces removespace(input_option); float temp; if (str2f(input_option, &temp)) { *output = temp*scale; printf("\n"); return 1; } else {printf("\n"); return -1;} } int menu_enter_two(float* output1, float* output2, char* string){ float scale = 1.0; if (state_ == EXITING) return -2; printf("Would you like to enter the coordinate in inches?\n"); switch (menu_yes()) { case 1: scale = 25.4; printf("%s in INCHES \nseparated with comma then press ENTER: ", string); break; case 0: scale = 1.0; printf("%s in MM \nseparated with comma then press ENTER: ", string); break; } fflush(stdout); if (state_ == EXITING) return -2; fd_set input_set; struct timeval timeout; timeout.tv_sec = 10; timeout.tv_usec = 0; // Listening for input stream for any activity // If there FD_ZERO(&input_set ); FD_SET(0, &input_set); while (state_ != EXITING && !select(1, &input_set, NULL, NULL, &timeout)) { timeout.tv_sec = 1; FD_ZERO(&input_set ); FD_SET(0, &input_set); } if (state_ == EXITING) return -2; // get input char input_option[256]; fgets(input_option,256,stdin); // remove white spaces removespace(input_option); // get the two char* ptr = strchr(input_option, ','); if (ptr == NULL) {return -1;} char temp1[20]; strncpy(temp1,input_option,ptr-input_option); temp1[(ptr-input_option)] = '\0'; char temp2[20]; strncpy(temp2,ptr+1,20); float temp1f, temp2f; if (str2f(temp1, &temp1f) && str2f(temp2, &temp2f)) { *output1 = temp1f*scale; *output2 = temp2f*scale; printf("\n"); return 1; } else {return -1;} } /*********************************************************************** *************************** OTHER FUNCTIONS **************************** ***********************************************************************/ // Sleep for nanoseconds void nsleep(uint64_t ns){ struct timespec req,rem; req.tv_sec = ns/1000000000; req.tv_nsec = ns%1000000000; // loop untill nanosleep sets an error or finishes successfully errno=0; // reset errno to avoid false detection while(nanosleep(&req, &rem) && errno==EINTR){ req.tv_sec = rem.tv_sec; 59 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 req.tv_nsec = rem.tv_nsec; } return; } // Filter out file without .txt extension int file_filter(const struct dirent *entry){ return !strcmp(entry->d_name + strlen(entry->d_name) -4, ".txt"); } // Remove space in a string void removespace(char* str) { int i,j=0; for(i=0;str[i]!='\0';i++) { if(str[i]!=' ' && str[i] != 10 && str[i] != 13) str[j++]=str[i]; } str[j]='\0'; return; } // Signal Handler for CTRL+C void SigHandler(int dummy) { state_ = EXITING; stop_all(); printf("\n\n**************************** EXITING PROGRAM ****************************\n"); return; } float max(float a, float b) { if(a >= b) return a; else return b; } float min(float a, float b) { if(a >= b) return b; else return a; } void print_time(int sec){ if (sec <= 0) printf("00:00"); else printf("%02d:%02d", (int)floor(sec /60.0),sec % 60); return; } int str2f(char* str, float* output){ removespace(str); float out = 0; int dec_location; int dec_numb = 0; int i; for(i=0; str[i] !='\0'; i++); int length = i; int j =1; for(int i = length-1; i >= 0; i--) { if (str[i]>=48 && str[i]<=57){out = out+j*(str[i]-48); j = j*10;} else if (str[i] == 46) {dec_numb ++; dec_location = i;} else if (i == 0 && str[i] == 43) {out = +out;} else if (i == 0 && str[i] == 45) {out = -out;} else return 0; } if (dec_numb == 0) {out = out;} else if (dec_numb == 1) {out = out/pow(10.0,length-1-dec_location);} else {return 0;} *output = out; return 1; } 60 2 foamcutter_setup.h The setting file, foamcutter_setup.h is attached below. 61 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 /******************************************************************************* * foamcutter_setup.h *******************************************************************************/ #ifndef FOAMCUTTER_SETUP #define FOAMCUTTER_SETUP #include #include #include #include #include #include #include #include #include #include #include #include #include #include// Cutter Width #define CUTTERWIDTH (36.0*IN2MM) // inches // Unit #define #define #define #define #define Conversion IN2MM 25.4 MM2IN (1.0/25.4) IN2REV 10.0 // Lead screw 10 TPI REV2PULSE 400.0 // depend on the switch settings on the motor drive MM2PULSE ( IN2REV / IN2MM * REV2PULSE) // 157.48 PULSE = 1 mm // Settings #define FEEDRATE 0.8 // unit: mm/s #define FEEDRATE_PUL (FEEDRATE*MM2PULSE) // position of limit switch relative to cutter origin #define LIM2ORIGIN_LX -4100 #define LIM2ORIGIN_RX -3550 #define LIM2ORIGIN_LY -2500 #define LIM2ORIGIN_RY -2400 // Motor Drive Pins #define PIN_LX_DIR 14 #define PIN_LX_PUL 15 #define PIN_LY_DIR 18 #define PIN_LY_PUL 23 #define PIN_RX_DIR 24 #define PIN_RX_PUL 25 #define PIN_RY_DIR 8 #define PIN_RY_PUL 7 // Limit Switches #define PIN_LX_LIM #define PIN_LY_LIM #define PIN_RX_LIM #define PIN_RY_LIM 6 13 19 26 // Relay #define PIN_RELAY 12 // Buttons #define PIN_PAUSE 17 #define PIN_STOP 27 // Motor Polarity // set to +1 or -1 // reverse the setting if the wire moves away from the limit switch during homing. #define POLARITY_LX +1 #define POLARITY_LY -1 #define POLARITY_RX +1 #define POLARITY_RY -1 // MAX cut area #define X_MAX (29.0*IN2MM) #define Y_MAX (16.0*IN2MM) #endif // 29 in // 16 in //FOAMCUTTER_SETUP 62 Appendix B MatLab Code for G-code Generation 1 DBF_foamcutter_general_shape.m The MatLab code for generating G-code for general shape, DBF_foamcutter_general_shape.m is attached below. Last updated: 8/26/2018. 63 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 %% DBF Foamcutter for Genearl Shapes % This code is written by Yuting Huang (ythuang96@gmail.com); % Please report all bug to the author's email address. % Last updated: 8/26/2018 % This is written for DBF foamcutting, to generate G-code from general shape % AutoCAD drawings. %% User Manual % 1. Export lines and arcs form AutoCAD, save as csv file. % 2. Copy the csv file to the same folder as this MatLab code. % 3. Run Code and done! % Press CRT+C at anytime to terminate code. %% -----------------------------------------------------------------------%% -----------------------------------------------------------------------%% -----------------------------------------------------------------------%% -----------------------------------------------------------------------clear all; close all; clc; tolerance = 0.0002; accuracy = 2; % length in mm of segments when breaking arc %% Determine Units % GUI stuff UIControl_FontSize_bak = get(0, 'DefaultUIControlFontSize' ); set(0, 'DefaultUIControlFontSize' , 30); unit = menu( 'Is Drawing in millimeters?' ,'Yes','No'); if unit == 1; % if drawing is in mm, continue generation of G-code %% Check the current folder for csv files D = dir( '*.csv'); if ~length(D) % if no .csv file exsist print error message fprintf( 'I could not find any file with .csv' ); fprintf( ' estension in the current folder.\n' ); fprintf( 'Please move the .csv file created by AutoCAD ' ); fprintf( '''eattext'' command into the current ' ); fprintf( 'working folder and try again.\n' ); else % Create a menu to select csv files in the current folder string = [ 'file = menu(''I detected ' num2str(length(D)) ... ' csv files list below, please select one'',' ]; for i = 1:length(D); string = [string '''' D(i).name ''',']; end string = [string '''None of the above'');' ]; eval(string); if file <= length(D); filename = D(file).name(1:end-4); clear string; if file <= length(D) && file; %% Inport File inport = csvread([filename '.csv'],1,2); [m,n] = size(inport); % make changes if there are only lines if n ==4; inport = [zeros(m,5) , inport]; end %% Eliminate 0 length lines k = 1; for i = 1:size(inport,1); if any(inport(i,6:9)64~= [0 0 0 0]) && ... 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 all(inport(i,6:7) == inport(i,8:9)); m = m-1; else temp(k,:) = inport(i,:); k = k+1; end end inport = temp; clear temp k i D file; %% Seperate Arc With line n_arc = 0; n_line = 0; for i = 1:m; if all(inport(i,6:9) == [0 0 0 0]); n_arc = n_arc + 1; arc(n_arc,:) = inport(i,1:5); else n_line = n_line + 1; line(n_line,:) = inport(i,6:9); end end %% Break Arcs into lines alllines = line; for i = 1:n_arc; n_segment = ceil(2*pi*arc(i,3)*arc(i,5)/360/accuracy); dtheta = arc(i,5)/n_segment; arcpoints = zeros(n_segment+1,2); for j = 1:n_segment+1; % break arc into points theta = arc(i,4) + (j-1)*dtheta; arcpoints(j,:) = arc(i,1:2) + ... arc(i,3).*[cosd(theta),sind(theta)]; end % chage the start and end point so that the arc join % the lines for j = 1:n_line; if abs(arcpoints(1,:) - line(j,1:2)) <= 0.01; arcpoints(1,:) = line(j,1:2); elseif abs(arcpoints(1,:) - line(j,3:4)) <= 0.01; arcpoints(1,:) = line(j,3:4); end if abs(arcpoints(end,:) - line(j,1:2)) <= 0.01; arcpoints(end,:) = line(j,1:2); elseif abs(arcpoints(end,:) - line(j,3:4)) <= 0.01; arcpoints(end,:) = line(j,3:4); end end % put all lines with arc points together alllines = [alllines ; arcpoints(1:end-1,:) , ... arcpoints(2:end,:)]; end %% Sort the lines in order sort(1,:) = alllines(1,:); alllines(1,:) = []; for i = 2:size(alllines,1)+1; compare = sort(i-1,3:4); [n2,~] = size(alllines); for j = 1:n2; if all(abs(compare - alllines(j,1:2)) <= tolerance); sort(i,:) = alllines(j,:); alllines(j,:) 65 = []; check = 1; break; 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 elseif all(abs(compare - alllines(j,3:4)) <= tolerance); sort(i,1:2) = alllines(j,3:4); sort(i,3:4) = alllines(j,1:2); alllines(j,:) = []; check = 1; break; end end if ~check % cannot find the another line that connects with % the previous fprintf( 'There is an open countour.\n' ); fprintf( 'This is most likely caused by an ' ); fprintf( 'extra line underneath a long line.\n' ); fprintf( 'Please check your drawing.\n' ); return; end check = 0; end sort2 = [sort(:,1:2); sort(end,3:4)]; %% Shift to positive min_x = min(sort2(:,1)); min_y = min(sort2(:,2)); sort2(:,1) = sort2(:,1) - min_x; sort2(:,2) = sort2(:,2) - min_y; max_x = max(sort2(:,1)); max_y = max(sort2(:,2)); %% Plot Curve figure(1); set(1, 'position',[0 0 1920 1080]); hold on; plotx = sort2(:,1); ploty = sort2(:,2); plot(plotx,ploty ); title( 'Drawing Unit mm' ,'fontsize',30); axis equal; %% Plot number [n,~] = size(sort2); j = 1; index = []; for i = 1:n-1; if sort2(i,1) == 0 || sort2(i,1) == max_x ... || sort2(i,2) == 0 || sort2(i,2) == max_y; text(plotx(i),ploty(i),sprintf( '%d',j),'fontsize',20); j = j+1; index = [index, i]; end end hold off; %% Determine Start Point start = index(input( 'Which point would you like to start? ' )); sort3 = [sort2(start:end-1,:) ; sort2(1:start-1,:); ... sort2(start,:)]; %% Cut Direction Reverse if chosen to direction = menu([ 'The Current Cut Direction is shown in' , ... 'the Figure with Increasing Number,' , ... 'Reverse Cut Direction?' ,'N0','YES']); if direction == 2; final = rot90(sort3',1); elseif direction == 1; final = sort3; end %% Final Plot clf; hold on; finalx = final(:,1); finaly = final(:,2); plot(finalx,finaly); title( 'Final shape on Foam 66 Cutter, Drawing Unit mm' , ... 163 'fontsize',30); 164 j = 1; 165 for i = 1:n 166 if final(i,1) == 0 || final(i,1) == max_x ... 167 || final(i,2) == 0 || final(i,2) == max_y; 168 text(finalx(i),finaly(i),sprintf( '%d',j),'fontsize',20); 169 j = j+1; 170 if j == 4; break; end 171 end 172 end 173 axis equal; hold off; 174 set(0, 'DefaultUIControlFontSize' , UIControl_FontSize_bak); 175 %% Generate G-code 176 fidw = fopen([filename '.txt'],'wt'); 177 fprintf(fidw, 'G21\n'); fprintf(fidw, 'M49\n'); 178 fprintf(fidw, 'F80\n'); fprintf(fidw, 'S80\n'); 179 fprintf(fidw, 'G1 X % 8.3f Y % 8.3f Z % 8.3f A % 8.3f\n' ... 180 ,0,0,0,0); 181 fprintf(fidw, 'G1 X % 8.3f Y % 8.3f Z % 8.3f A % 8.3f\n' ,... 182 finalx(1),finaly(1),finalx(1),finaly(1)); 183 for i = 2:length(finalx)-1 184 fprintf(fidw, 'G1 X % 8.3f Y % 8.3f Z % 8.3f A % 8.3f\n' ... 185 ,finalx(i),finaly(i),finalx(i),finaly(i)); 186 187 % Calculate Length 188 length = sqrt((finalx(i)-finalx(i-1))^2 + ... 189 (finaly(i)-finaly(i-1))^2); 190 if length >= 100; 191 fprintf(fidw,sprintf( 'G4 P%d\n',floor(length/100) ) ); 192 end 193 % Add 1 sec pause per 100 mm cut for long cuts 194 end 195 fprintf(fidw, 'G1 X % 8.3f Y % 8.3f Z % 8.3f A % 8.3f\n' ... 196 ,finalx(end),finaly(end),finalx(end),finaly(end)); 197 fprintf(fidw, 'G1 X % 8.3f Y % 8.3f Z % 8.3f A % 8.3f\n' ... 198 ,0,0,0,0); 199 fprintf(fidw, 'M2'); 200 fclose(fidw); 201 disp([ 'The G-Code is saved as ''' filename ... 202 '.txt'' in this folder. ' ]); 203 set(gcf, 'PaperUnits','inches','PaperPosition',[0 0 16 9]); 204 print(filename, '-dpng','-r240'); 205 else % if 'none of the above selectee' 206 fprintf( 'Please move your desired file to the current' ); 207 fprintf( ' folder and try again.\n' ); 208 end 209 else % if no file selected 210 fprintf( 'Please move your desired file to the current' ); 211 fprintf( ' folder and try again.\n' ); 212 end % end file selection check 213 end % end file exsistence check 214 else % if drawing not in mm, print error message 215 fprintf( 'Please go back to AutoCAD and use the ''Scale''' ); 216 fprintf( ' command to scale the drawing 67 by 25.4. \n' ); 217 fprintf( 'Inches do not provide high enough accuracy\n' ); 218 end % end 'unit' check 68 2 DBF_foamcutter_wing.m The MatLab code for generating G-code for wing, DBF_foamcutter_wing.m is attached below. Last updated: 8/23/2018. 69 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 %% DBF Foamcutter for Wing % This code is modified by Yuting Huang (ythuang96@gmail.com) based on % Dr.Anderson's code written on Scilab. % Please report all bug to the author's email address. % Last updated: 8/23/2018 % This is written for DBF foamcutting, to generate G-code from wing % prameters. clear all; close all; clc; %% Enter Parameters Below % airfoil section file for root root_filename = 'AH-79-100.dat'; % root chord length [in] root_chord = 6; % airfoil section file for tip tip_filename = 'E216.dat'; % tip chord length [in] tip_chord = 3; % root chord has to be greater or equal to tip chord % +1 for right wing, -1 for left wing right_wing = 1; % semi-span [in] semi_span = 3; % leading edge sweep [deg] LE_sweep = 0; % twist [deg] twist = 0.0; % g-code output file name g_filename = 'HTail'; % width of CNC cutter [in] cutter_width = 39; % scale the cord length to accomodaate for broken trailing edge % recommend using 1.2, then cut trailing edge with a blade to desired % length. scale_factor = 1.2; %% -----------------------------------------------------------------------%% -----------------------------------------------------------------------%% -----------------------------------------------------------------------%% -----------------------------------------------------------------------%% Open File fid = fopen(root_filename); temp = textscan(fid, '%f %f','headerLines', 1); fclose(fid); root_pts = [temp{1}, temp{2}]; 70 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 fid = fopen(tip_filename); temp = textscan(fid, '%f %f','headerLines', 1); fclose(fid); tip_pts = [temp{1}, temp{2}]; clear temp fid; %% Deconstruct Airfoil into Upper and Lower Parts root_size = size(root_pts,1); root_turn_point = find(root_pts(:,2)<0,1) - 1; root_upper_x = root_pts(1:root_turn_point, 1); root_upper_y = root_pts(1:root_turn_point, 2); root_lower_x = root_pts(root_turn_point:root_size, 1); root_lower_y = root_pts(root_turn_point:root_size, 2); tip_size = size(tip_pts,1); tip_turn_point = find(tip_pts(:,2)<0,1) - 1; tip_upper_x = tip_pts(1:tip_turn_point, 1); tip_upper_y = tip_pts(1:tip_turn_point, 2); tip_lower_x = tip_pts(tip_turn_point:tip_size, 1); tip_lower_y = tip_pts(tip_turn_point:tip_size, 2); %% Interpolate n = 301; root_upper_yp = root_upper_xp = root_lower_yp = root_lower_xp = tip_upper_yp tip_upper_xp tip_lower_yp tip_lower_xp = = = = interp1(root_upper_x,root_upper_y,linspace(1,0,n)'); linspace(1,0,n)'; interp1(root_lower_x,root_lower_y,linspace(0,1,n)'); linspace(0,1,n)'; interp1(tip_upper_x,tip_upper_y,linspace(1,0,n)'); linspace(1,0,n)'; interp1(tip_lower_x,tip_lower_y,linspace(0,1,n)'); linspace(0,1,n)'; %% Scale to Chord Length % chord length on machine root_chord_ext = root_chord + ... 0.5*(root_chord - tip_chord)*(cutter_width - semi_span)/semi_span; tip_chord_ext = root_chord - ... 0.5*(root_chord - tip_chord)*(cutter_width + semi_span)/semi_span; root_upper_xp = root_chord_ext*root_upper_xp; root_upper_yp = root_chord_ext*root_upper_yp; root_lower_xp = root_chord_ext*root_lower_xp; root_lower_yp = root_chord_ext*root_lower_yp; tip_upper_xp = tip_chord_ext*tip_upper_xp; tip_upper_yp = tip_chord_ext*tip_upper_yp; tip_lower_xp = tip_chord_ext*tip_lower_xp; tip_lower_yp = tip_chord_ext*tip_lower_yp; %% Rotate Tip by Twist Angle c4 = tip_chord/4; 71 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 twist_rad = (pi/180)*twist; tip_upper_xpr = tip_upper_xp*cos(twist_rad) ... + tip_upper_yp*sin(twist_rad) + c4*(1.0-cos(twist_rad)); tip_upper_ypr = -tip_upper_xp*sin(twist_rad) ... + tip_upper_yp*cos(twist_rad) + c4*sin(twist_rad); tip_lower_xpr = tip_lower_xp*cos(twist_rad) ... + tip_lower_yp*sin(twist_rad) + c4*(1.0-cos(twist_rad)); tip_lower_ypr = -tip_lower_xp*sin(twist_rad) ... + tip_lower_yp*cos(twist_rad) + c4*sin(twist_rad); %% Use Sweep Angle to Shift Tip Relative to Root sweep_shift = cutter_width*tan(LE_sweep*pi/180); tip_upper_xpr = tip_upper_xpr + sweep_shift; tip_lower_xpr = tip_lower_xpr + sweep_shift; %% Swap x-axis to Start Cut on root_upper_xp = root_chord_ext root_lower_xp = root_chord_ext tip_upper_xpr = root_chord_ext tip_lower_xpr = root_chord_ext Trailing Edge - root_upper_xp; - root_lower_xp; - tip_upper_xpr; - tip_lower_xpr; %% Combine Upper and Lower Surfaces root_x = [root_upper_xp; root_lower_xp]; root_y = [root_upper_yp; root_lower_yp]; tip_x = [tip_upper_xpr; tip_lower_xpr]; tip_y = [tip_upper_ypr; tip_lower_ypr]; %% Make a Plot of Root and Tip set(0,'defaultlinelinewidth' ,2) set(0,'defaultaxeslinewidth' ,1) set(0,'defaultaxesfontsize' ,20) figure(1); set(1, 'position',[0 0 1920 1080]); hold on; plot(root_x,root_y); plot(tip_x,tip_y); legend1 = legend( 'Root','Tip'); set(legend1, 'interpreter','latex'); set(legend1, 'fontsize',18); title('Wing on FoamCutter' ,'interpreter','latex','fontsize',25); xlabel('X [in]','interpreter','latex','fontsize',25); ylabel('Y [in]','interpreter','latex','fontsize',25); axis equal; grid on; %% Write G-code File root_x = scale_factor*root_x*25.4; root_y = scale_factor*root_y*25.4; tip_x = scale_factor*tip_x*25.4; tip_y = scale_factor*tip_y*25.4; fidw = fopen([g_filename '.txt'],'wt'); 72 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 fprintf(fidw, 'G21\n'); fprintf(fidw, 'M48\n'); fprintf(fidw, 'F80\n'); fprintf(fidw, 'S80\n'); fprintf(fidw, 'G1 X % 8.3f Y % 8.3f Z % 8.3f A % 8.3f\n' ,0,0,0,0); if right_wing > 0 fprintf(fidw, 'G1 X % 8.3f Y % 8.3f Z % 8.3f A % root_x(1),root_y(1),tip_x(1),tip_y(1)); fprintf(fidw, 'G4 P5\n'); for i=length(root_x):-1:1 fprintf(fidw, 'G1 X % 8.3f Y % 8.3f Z % 8.3f root_x(i),root_y(i),tip_x(i),tip_y(i)); end else fprintf(fidw, 'G1 X % 8.3f Y % 8.3f Z % 8.3f A % tip_x(1),tip_y(1),root_x(1),root_y(1)); fprintf(fidw, 'G4 P5\n'); for i=length(root_x):-1:1 fprintf(fidw, 'G1 X % 8.3f Y % 8.3f Z % 8.3f tip_x(i),tip_y(i),root_x(i),root_y(i)); end end fprintf(fidw, 'G1 X % 8.3f fprintf(fidw, 'M2'); Y % 8.3f Z % 8.3f fclose(fidw); 73 8.3f\n' , ... A % 8.3f\n' , ... 8.3f\n' , ... A % 8.3f\n' , ... A % 8.3f\n' ,0,0,0,0); Appendix C Drawings The complete SolidWorks drawings can be found at https://github.com/ythuang96/FoamCutter. The following is the complete assembly in SolidWorks: Drawings attached below are all aluminum pieces that required machining. But the drawings are not drawn in compliance with engineering standards and therefore should only be used as a reference when machining yourself. 74 7.736 8.000 5.965 4.390 3.610 2.035 1 0 0.264 2 X4 0 0.394 B B 1.969 M6 Close Fit X10 Drill B 0 0.610 23.417 24.000 21.843 X2 11.500 12.000 12.500 2.157 75 0 0.583 60 2.362 0 0.80 1.50 1.390 2.000 1/4-20 Close Fit X3 Drill F A M6 Close Fit X8 Drill B TITLE: SIZE A Al Sheet 1 Al Sheet 2 DWG. NO. SCALE: 1:2 2 A 1 1 WEIGHT: REV SHEET 1 OF 11 0 0.394 7.638 8.000 0.591 B 1.162 1.772 1.862 1.969 1/4-20 Close Fit X4 Drill F 76 0 M6 Close Fit X8 Drill B 5.138 60 2.362 0 2.756 B 6.063 5.244 5.441 4.500 4.000 3.500 1.937 1 0 0.362 2 0 1.522 1.772 2.022 4-40 Close Fit X2 Drill #34 A TITLE: SIZE A Al Sheet 3-1 Al Sheet 3-2 DWG. NO. SCALE: 1:2 2 A 1 1 WEIGHT: REV SHEET 2 OF 11 1 X2 1.969 0.394 0 2 0 B B 0.375 0.750 3.500 2.800 0.900 77 0 M6 Close Fit X2 Drill B 0 0.550 1.500 A 1/2-13 Close Fit Drill 33/64 3/4 Ream TITLE: SIZE A Rec Tube 1 DWG. NO. SCALE: 1:2 2 A 1 1 WEIGHT: REV SHEET 3 OF 11 1 0.750 0.500 0.250 0 2 0 B B 1.000 0 78 0.313 0.20 Drill 10 4-40 Close Fit X2 Drill #34 0.375 0 1/4-20 Close Fit Drill F 0.500 A 1.200 TITLE: SIZE A Rec Tube 2 DWG. NO. SCALE: 2:1 2 A 1 1 WEIGHT: REV SHEET 4 OF 11 1 0 1.201 50 1.969 2 0 0.625 Drill 5/8 0.288 B B 0.788 1.288 1.288 40 1.575 0.788 0 79 0.288 4-40 Close Fit X2 Drill #34 0 X2 0.400 1.100 A 50 1.969 TITLE: 1/4-20 Close Fit X3 Drill F L Bracket 50-50-40 Nut Vertical SIZE A DWG. NO. SCALE: 1:1 2 1 1 WEIGHT: REV SHEET 5 OF 11 A 0 1 0.951 50 1.969 2 0 0.625 Drill 5/8 0.288 B B 0.788 1.288 1.288 40 1.575 0.788 0 80 0.288 4-40 Close Fit X2 Drill #34 0 X2 0.400 1.100 A 50 1.969 TITLE: 1/4-20 Close Fit X3 Drill F L Bracket 50-50-40 Nut Horizontal SIZE A DWG. NO. SCALE: 1:1 2 1 1 WEIGHT: REV SHEET 6 OF 11 A 1 0 1.500 75 2.953 2 0 B B 0.594 1.374 4-40 Close Fit X2 Drill #34 0 1.819 0 0.150 81 M6 Close Fit X2 Drill B 0.984 50 1.969 1.082 A TITLE: L Bracket 75-60-50 Vertical Motor 60 2.362 SIZE 1.00 A DWG. NO. SCALE: 1:1 2 1 1 WEIGHT: REV SHEET 7 OF 11 A 1 0 1.900 75 2.953 2 0 B B 0.594 1.374 4-40 Close Fit X2 Drill #34 0 1.819 0 0.150 82 M6 Close Fit X2 Drill B 0.984 50 1.969 0.964 A TITLE: L Bracket 75-60-50 Vertical W/O Motor 60 2.362 SIZE 1.00 A DWG. NO. SCALE: 1:1 2 1 1 WEIGHT: REV SHEET 8 OF 11 A 1 0 1.000 75 2.953 2 0 B B 0.484 1.484 4-40 Close Fit X2 Drill #34 0 1.082 A 1.819 0.984 0 0.150 83 1/4-20 Close Fit X2 Drill F 50 1.969 TITLE: L Bracket 75-60-50 Horizontal Motor 60 2.362 2 SIZE 1.00 A DWG. NO. SCALE: 1:1 1 1 WEIGHT: REV SHEET 9 OF 11 A 1 0 1.000 75 2.953 2 0 B B 0.484 1.484 4-40 Close Fit X2 Drill #34 0 1.819 0 0.150 84 1/4-20 Close Fit X2 Drill F 0.984 50 1.969 0.964 A TITLE: 60 2.362 1.00 L Bracket 75-60-50 Horizontal W/O SIZE DWG. NO. Motor REV 1 A SCALE: 1:1 2 1 WEIGHT: SHEET 10 OF 11 A 1.700 0 1.306 1 0.306 2 0 0.222 B B 0.505 0.597 0.800 1.300 M6 Close Fit Drill B 0.906 0 85 0.306 2-56 Close Fit X4 Drill #43 0 0.222 A 0.505 0.597 0.800 1/4-20 Close Fit Drill F TITLE: Limit Switch Plate SIZE A DWG. NO. SCALE: 2:1 2 1 1 WEIGHT: REV SHEET 11 OF 11 A Appendix D Part List The parts used for this project are listed below. 86 Category Spec Name Screw Size Link 4ft Rail T-Slot Aluminum 87 Electronics Panel Corner Brace Corner Surface Bracket Tee Surface Bracket 3 Way Outside Corner Fasten Plate Single Fasten Plate Dual Dust Cover L Bracket L Bracket Al Sheet Al Sheet Al Rod 75 * 60 * 50 mm 50 * 50 * 40 mm 2 * 24 * 1/8'' 8 * 8 * 1/8'' 1 1/4'' OD 1'' length Manual Switch Toggle - Monentary Toggle - Maintained Limit Switch Standoff Flat cable Signal Flat cable Power 26 awg 18 awg L to R R to L PVC 1/4'' thick https://www.mcmaster.com/#47065t101/=1cbr8ec 2ft 1ft 1'' screw distance Exit Switch Pause and Wire switch 4 cable 4 cable $10.57 https://www.mcmaster.com/#47065t216/=1cbr8mf https://www.mcmaster.com/#47065t267/=1ct8pah https://www.mcmaster.com/#47065t278/=1ct8qy1 https://www.mcmaster.com/#47065t244/=1cbra50 1/4''-20-1/2'' https://www.mcmaster.com/#47065t142/=1cbr7ot 1/4''-20-1/2'' https://www.mcmaster.com/#47065t147/=1cbr7xq https://www.mcmaster.com/#47065t93/=1cbr627 $7.79 $5.84 $4.96 $7.62 $7.95 $9.86 $2.30 $4.29 $4.15 https://www.mcmaster.com/#2313n46/=1cby7fm https://www.mcmaster.com/#2313n43/=1cttttv https://www.mcmaster.com/#89015k231/=1ctty5m https://www.mcmaster.com/#89015k239/=1ctu0cb https://www.mcmaster.com/#1610t12/=1d2s65q $13.60 $8.39 $10.46 $13.07 $2.96 8 4 2 3 1 https://www.mcmaster.com/#7343k29/=1ct8u34 https://www.mcmaster.com/#7343k184/=1ct8uci https://www.mcmaster.com/#7193k3/=1cazszw https://www.mcmaster.com/#90268a203/=1ccj61x https://www.mcmaster.com/#9634t203/=1ctvn73 https://www.mcmaster.com/#9634t603/=1ct11kf $9.14 $4.34 $7.17 $1.66 $2.08 $3.78 1 2 4 10 10 10 $2.31 $2.31 $92.78 5 5 1 1/2'' 1/2'' 2-56 2-56 Comment X2 cut to 34'', X2 use 10 for cutting shorter ones 5 2 38 8 10 8 10 10 3 Hole D 0.25'' Hole Spacing 0.5'' https://www.mcmaster.com/#1910a42/=1ct0b2b https://www.mcmaster.com/#1910a22/=1ct0aul https://www.mcmaster.com/#92985t52/=1ct8ij0 Constant-Force Spring 10.60 lbs Sleeve Bearing Sleeve Bearing Width 1'' Length 1/2'' Length 1 1/2'' ID 1.2'', OD 1.52'' ID 1/4'', OD 3/8'' ID 1/2'', OD 3/4'' https://www.mcmaster.com/#9293k12/=1ctcaxk https://www.mcmaster.com/#6391k136/=1cucsi1 https://www.mcmaster.com/#6391k413/=1ctcfzf $10.83 $0.67 $4.71 1 1 1 Sleeve Bearing Length 3/8'' ID 1/2'', OD 3/4'' https://www.mcmaster.com/#6391k521/=1ctsrf0 $1.50 0 Flanged Sleeve Bearing One-way Bearing Shaft Shaft Collar Rectangular Tube Length 1'' Length 1/2'' Length 9'' ID 1/2'', OD 3/4'' ID 1/2'', OD 3/4'' D 1/2'' Length 12'' 1.5*0.75'' https://www.mcmaster.com/#6338k424/=1ctsg38 https://www.mcmaster.com/#2489k24/=1ctsnot https://www.mcmaster.com/#7398k6/=1cuchny https://www.mcmaster.com/#9946k15/=1ctt7z4 https://www.mcmaster.com/#6546k54/=1cts37f $2.28 $11.17 $19.40 $2.77 $9.12 2 1 1 4 1 Ruler Perforated Sheet Wire Retractor 3ft 1'' Amount for Final Desgin $12.31 4 Unit Price No longer required in final design Category Button Head Screw Screw Socket Head Screw Nut Shaft 2-56 1/4'' 4-40 0.5'' 4-40 1 3/8'' 1/4-20 0.5'' 1/4-20 1'' 1/2-13 3.5'' M6 12mm M6 20mm https://www.mcmaster.com/#92196a077/=1cu00p8 https://www.mcmaster.com/#92949a110/=1ctzdks https://www.mcmaster.com/#92949a813/=1ctzdgv https://www.mcmaster.com/#97763a263/=1ctznzk https://www.mcmaster.com/#97763a267/=1ctze0m https://www.mcmaster.com/#90044a167/=1ctzj8m https://www.mcmaster.com/#91292a134/=1ctzrd6 https://www.mcmaster.com/#91292a137/=1ctzorm $5.97 $3.38 $5.19 $8.35 $6.72 $5.05 $7.44 $4.44 Amount for Final Desgin 1 1 1 1 1 1 1 1 M6 30mm https://www.mcmaster.com/#91292a139/=1ctzfq8 $9.41 0 1/4-20 1/2'' https://www.mcmaster.com/#94567a510/=1d2s6uf https://www.mcmaster.com/#91841a005/=1ctzaex https://www.mcmaster.com/#97149a100/=1ctzgtl https://www.mcmaster.com/#97149a250/=1ctzjtn https://www.mcmaster.com/#91828a251/=1cu0gf7 $2.65 $2.91 $7.24 $6.51 $8.73 24 1 1 1 1 Spec Name Thumb Screw 4-40 Nut 1/4-20 Nut 1/2-13 Nut M6 Nut Shaft Screw Size Link Unit Price 20mm 1000mm https://www.mcmaster.com/#6459k62/=1cagjsi $95.82 20mm 600mm https://www.mcmaster.com/#6459k59/=1cagjxv $57.48 88 Shaft Support Mounting Hole 45mm Linear Bearings 40 mm spacing 1* M5 2*M7 https://www.mcmaster.com/#61815k36/=1cag4oi $25.19 Comment No longer required in final design X2 cut from previous foamcutter X4 cut from previous 4 foamcutter X8 from previous 16 foamcutter 4 https://www.mcmaster.com/#2464k34/=1cbgsmi $68.64 3 ft OD 1.47'' 304 Stainless thinckness 1'' https://www.mcmaster.com/#98980a132/=1cbizut https://www.mcmaster.com/#1343k134/=1cbj0f6 $36.59 $36.81 Collar OD 1.3125'' thinckness 0.5'' https://www.mcmaster.com/#6698k13/=1cbj08y $18.35 Bearing OD 1.125'' thinckness 0.3125'' https://www.mcmaster.com/#60355k704/=1cbrell $9.08 https://www.mcmaster.com/#91182a703/=1cbrlk2 $11.59 X8 from previous foamcutter X4 from previous 4 foamcutter 4 4 X4 from previous 8 foamcutter No longer required in 0 final design 1 Shaft Coupler Lead screw Transmission Nut 10mm to 1/2 inch 1/2''-10 Shim 4* M6 https://www.mcmaster.com/#9338t54/=1cag46d $68.20 12 Acrylic 24'' * 48'' *1/4'' https://www.mcmaster.com/#8589k84/=1cy43x1 $55.76 1 Acrylic 6'' * 12'' * 1/8'' https://www.mcmaster.com/#8560k275/=1cyln3f $5.37 1 Delrin 6'' * 6'' * 1/4'' https://www.mcmaster.com/#8573k121/=1d2s6lb $9.43 1 No longer required in final design Appendix E Purchase History The purchase history of this project are listed below. 89 Category Spec Name Rail T-Slot Aluminum Electronics 90 Panel 4ft 3ft 2ft 1ft 1'' Corner Brace Corner Surface Bracket Tee Surface Bracket 3 Way Outside Corner Fasten Plate Single Fasten Plate Dual Dust Cover $12.31 $10.57 $7.79 $5.84 $4.96 $7.62 $7.95 $9.86 $2.30 $4.29 $4.15 Amount for Final Desgin 4 10 5 2 38 8 10 8 10 10 3 Unit Price 1'' screw distance Constant-Force Spring 10.60 lbs Sleeve Bearing Sleeve Bearing Sleeve Bearing Wire Retractor Flanged Sleeve Bearing One-way Bearing Shaft Shaft Collar Rectangular Tube Width 1'' Length 1/2'' Length 1 1/2'' Length 3/8'' Length 1'' Length 1/2'' Length 9'' Length 12'' ---------------$37.80 1 2 4 10 10 10 $9.14 $8.68 $28.68 $16.60 $20.80 $37.80 ------------------- ------------------- ------------------- ------------------- ---------------- $2.31 $2.31 $92.78 5 5 1 5 5 1 $11.55 $11.55 $92.78 ---------- ---------- ---------- ---------- ---------- ---------- $10.83 $0.67 $4.71 $1.50 $2.28 $11.17 $19.40 $2.77 $9.12 1 1 1 0 2 1 1 4 1 1 1 1 2 2 1 1 4 1 $10.83 $0.67 $4.71 $3.00 $4.56 $11.17 $19.40 $11.08 $9.12 ---------------------------- ---------------------------- ---------------------------- ---------------------------- ---------------------------- ---------------------------- $9.14 $4.34 $7.17 $1.66 $2.08 $3.78 Hole D 0.25'' ---------------- 1 2 4 10 10 10 Toggle - Monentary Toggle - Maintained Perforated Sheet ---------------- ------------------$15.90 ------------- ------------- Manual Switch L to R R to L PVC 1/4'' thick ------------$2.96 $53.70 ---------------- 8 4 2 3 1 Ruler ---------$11.68 ---$60.96 ---------------- Order4 Amount ------------------2 ------------- $148.63 ---------------- $13.60 $8.39 $10.46 $13.07 $2.96 4 cable 4 cable Order3 Amount ---------2 ---8 ---------------- $108.80 $33.56 $20.92 $39.21 ---- 75 * 60 * 50 mm 50 * 50 * 40 mm 2 * 24 * 1/8'' 8 * 8 * 1/8'' 1 1/4'' OD 1'' length 26 awg 18 awg Order2 $1,256.92 Amount ------1 $10.57 1 $7.79 ------6 $29.76 4 $30.48 ------------------------------- 8 4 2 3 L Bracket L Bracket Al Sheet Al Sheet Al Rod Limit Switch Standoff Flat cable Signal Flat cable Power Order1 $1,235.68 Amount 4 $49.24 9 $95.13 5 $38.95 ------32 $158.72 8 $60.96 10 $79.50 8 $78.88 10 $23.00 10 $42.90 3 $12.45 ---- 1 10 Category Button Head Screw Screw Socket Head Screw Nut Shaft Spec Name Thumb Screw 4-40 Nut 1/4-20 Nut 1/2-13 Nut M6 Nut Shaft Shaft Support 91 Linear Bearings Shaft Coupler Lead screw Transmission Nut Collar Bearing Shim Acrylic Acrylic Delrin 2-56 1/4'' 4-40 0.5'' 4-40 1 3/8'' 1/4-20 0.5'' 1/4-20 1'' 1/2-13 3.5'' M6 12mm M6 20mm M6 30mm 1/4-20 1/2'' 20mm 20mm 10mm to 1/2 inch 1/2''-10 24'' * 48'' *1/4'' 6'' * 12'' * 1/8'' 6'' * 6'' * 1/4'' 1000mm 600mm 3 ft OD 1.47'' OD 1.3125'' OD 1.125'' $5.97 $3.38 $5.19 $8.35 $6.72 $5.05 $7.44 $4.44 $9.41 $2.65 $2.91 $7.24 $6.51 $8.73 Amount for Final Desgin 1 1 1 1 1 1 1 1 0 24 1 1 1 1 $95.82 $57.48 $25.19 4 4 16 ---------- ---------- 2 0 8 $191.64 $0.00 $201.52 ---------- ---------- ---------- ---------- $68.20 $68.64 $36.59 $36.81 $18.35 $9.08 $11.59 12 4 4 4 8 0 1 ---------------------- ---------------------- 4 0 4 4 4 8 1 $272.80 $0.00 $146.36 $147.24 $73.40 $72.64 $11.59 ---------------------- ---------------------- ---------------------- ---------------------- $55.76 $5.37 $9.43 1 1 1 ---------- ---------- 1 1 $55.76 $5.37 ---- ------- ------$9.43 ---------- ---------- Unit Price Order1 $1,235.68 Amount 1 $5.97 1 $3.38 1 $5.19 1 $8.35 1 $6.72 1 $5.05 1 $7.44 1 $4.44 1 $9.41 ------1 $2.91 1 $7.24 1 $6.51 1 $8.73 Order2 $1,256.92 Amount ------------------------------------------------------------------------------------- ---- Order3 Amount ---------------------------24 ------------- 1 $148.63 ---------------------------$63.60 ------------- Order4 Amount ------------------------------------------- $53.70 -------------------------------------------
Source Exif Data:
File Type : PDF File Type Extension : pdf MIME Type : application/pdf PDF Version : 1.5 Linearized : No XMP Toolkit : Adobe XMP Core 5.6-c015 84.158975, 2016/02/13-02:40:29 Format : application/pdf Creator : Description : Title : Create Date : 2018:12:06 15:10:41-08:00 Creator Tool : LaTeX with hyperref package Modify Date : 2018:12:09 00:29:20-08:00 Metadata Date : 2018:12:09 00:29:20-08:00 Keywords : Producer : pdfTeX-1.40.17 Trapped : False PTEX Fullbanner : This is MiKTeX-pdfTeX 2.9.5900 (1.40.17) Document ID : uuid:14ca05f2-f180-4a50-84be-66aad41289e9 Instance ID : uuid:00ed5fb7-0314-4e9f-83b3-37339142f1cc Page Mode : UseOutlines Page Count : 92 Author : PTEX Fullbanner : This is MiKTeX-pdfTeX 2.9.5900 (1.40.17) Subject :EXIF Metadata provided by EXIF.tools