Project tutorial

CNC Part Picking Machine © GPL3+

Use PHP and MySQL to control a machine that will retrieve parts so you no longer have to dig for that 330 ohm resistor!

  • 2,740 views
  • 0 comments
  • 5 respects

Components and supplies

Necessary tools and machines

3drag
3D Printer (generic)
CNC Router
Cordless Drill
A real lifesaver

Apps and online services

Ide web
Arduino IDE
D94d qxu
Autodesk Fusion 360
Brackets IDE
MySQL Server
Apache24 Webserver

About this project

If you're a serious maker like I am, then you'll most likely have countless resistors, capacitors, and various other electronic components lying around. But there is a major problem: How does one keep track of what or how many of something they have? For this issue I created a CNC machine that gets information from a MySQL database that then goes and retrieves the item that was requested. In addition to the database back-end, I made a front-end webpage that allows for users to login and then create categories of parts, add new parts, and change the quantities of parts. This way every single item can be accounted for, just like a stock management system.

Theory

The basis of this system is to keep track of inventory. For example, if someone buys 20 Arduino Uno boards they could easily add that amount to a database table. The category would be "Arduino", name of "Uno", and a quantity of 20. For multiple people, the owner of that part would be the username of the person who added it. The part would also include data about the it's location on a grid. Whenever the part amount changes the CNC machine would then select that part and give it to the user.

Database

I needed a ubiquitous database that could be accessed by both Python and PHP. It also had to be easy to use with plenty of support, making MySQL the perfect database server. I began by downloading the mysql installer from https://dev.mysql.com/downloads/windows/installer/ and then ran it. I chose to install the server (of course), and also the workbench, shell, and utilities. When you choose a username and password make sure to remember it, as those same credentials are needed in all of the PHP files and the Python script. After starting the server enable it to run as a background process so it will always be active.

From here on in everything must be spelled and in the exact same order as I have it.

Next, create a new database (schema) called "components". Then add the following tables: "categories", "parts", and "users".

In the categories table add the following columns in this exact order: "id" -int(11), PK, AI; "name" -varchar(45); "owner" - varchar(45).

In the parts table add the following columns in this exact order: "id" -int(11), AI, PK; "category" -varchar(45); "name" -varchar(45); "quantity" -int(11); "owner" -varchar(45); "locationX" -int(11); "locationY" -int(11);

In the users table add the following columns in this exact order: "id" -int(11), AI, PK; "username" -varchar(45); "password" -varchar(128);

Setting Up Apache

The webpages I have created utilize HTML, CSS, Javascript, and PHP. Start by downloading the latest apache version from http://www.apachelounge.com/download/ and unzip it, moving the folder to the C:\ directory. Next, download PHP from http://windows.php.net/download#php-7.2 and make sure it is the Thread Safe version. Unzip it, rename it to "PHP", and move it to the C:\ directory. Then go into C:\Apache24\conf\httpd.conf and edit it. Add the following lines right below the <IfModule headers_module> section:

LoadModule php7_module C:/PHP/php7apache2_4.dll
<IfModule php7_module>
   DirectoryIndex index.html index.php
   AddHandler application/x-httpd-php .php
   PHPIniDir "C:/PHP"
</IfModule>

Then test your server by running httpd.exe located in the bin folder. Head to "localhost/" in your browser and see if the hello world page comes up. If it does, hurray, you now have a local webserver.

Setting Up PHP

In order to set up MySQL for PHP several things must be done. First, rename "php.ini-recommended" to "php.ini" and then open it in notepad.

Head to the extensions section and add or uncomment "extension=php_mysqli.dll" which will let PHP communicate with the MySQL server.

Now restart httpd.exe and create a new file called "phptest.php" and put <?php phpinfo(); ?> into the file. Now go to localhost/phptest.php and see if your browser information comes up.

Designing the Machine

I began by creating some basic parts in Fusion 360: a 6mm rod, linear bearing, and a stepper motor. Then I spanned two rods across to form the y axis, and also put a timing belt around the stepper motor and bearing. I also added an x axis, as well.

I then started to 3D print various parts.

And also CNC routed two side panels:

Making the Machine

I ended up going through multiple iterations of each part, so if any are different that is why. I started by sanding each part and then drilling out each hole in the 3D printed parts. Then I put linear bearings into the holes and ran the 6mm rods through them.

I also mounted the stepper motors into their respective locations after attaching the pulleys to their shafts.

The timing belt got looped around each of the two sides for both axes.

Eventually I realized that the gripper would be too cumbersome, so I opted for an electromagnet instead. I also had some help while building it, in the form of a cat.

Arduino Code

My basis for this machine was GRBL. The start of the code lists various parameters, such as distance per rotation, offsets, and extents. I used the BasicStepperDriver library to control the DRV8825 stepper motor drivers. The stepper drivers are set to use 1/32 micro-stepping, increasing the resolution. Whenever the machine "boots up" it goes through a homing sequence where each axis steps until it hits a limit switch. Then it moves based off of the offset to a set location and sets the location to 0, 0. Now whenever it receives a move command via serial it moves to that grid location.

Python Program

I chose to use Flask as a webserver that would receive GET requests from the main website. The requests consist of the name and category of the part. After Flask handles it the data gets parsed, then the MySQL server gets queried to find out the location of the part. Then the python script sends a command to the Arduino, specifying where part is.

Using the Part Picker

I have provided the website files in my github repository: https://github.com/having11/cnc_part_picker_webpages Replace the missing parameters in the PHP files for your specific MySQL server. Put the files into the htdocs folder in the Apache folder. Simply run the python script and then whenever the part amount gets changed the machine will go to that location and get it.

Code

Arduino Uno CodeC/C++
#include <Arduino.h>
#include <Servo.h>
#include "BasicStepperDriver.h"

// Motor steps per revolution. Most steppers are 200 steps or 1.8 degrees/step
#define MOTOR_STEPS 200
#define RPM 60

//1:32 step ratio
#define MICROSTEPS 32

#define MM_REV_RATIO 44.5 //MM moved per one rotation
#define GRID_COEFFICIENT 40 //for x,y grid location to be converted into mm

#define X_OFFSET 20
#define Y_OFFSET 10

#define Y_LIM 2
#define X_LIM 3

//y axis stepper DIR, y axis stepper STEP, x axis stepper DIR, x axis stepper STEP, magnet, z axis servo
int8_t pins[6] = {8,9,10,11,6,5};

//X axis pos, Y axis pos
float current_positions[2] = {0.0, 0.0};
int servo_limits[2] = {60,30}; //z axis up, z axis down

BasicStepperDriver stepperY(MOTOR_STEPS, pins[0], pins[1]);
BasicStepperDriver stepperX(MOTOR_STEPS, pins[2], pins[3]);
Servo z_axis_servo;

void moveDistance(float distance, BasicStepperDriver& stepper){
  float stepsPerRev = MOTOR_STEPS * MICROSTEPS;
  float distancePerStep = MM_REV_RATIO / stepsPerRev;
  int steps_needed = round(distance / distancePerStep);
  Serial.print("Moving: ");
  Serial.println(steps_needed);
  stepper.move(-steps_needed);
}

void homeAxes(){
  if(!digitalRead(Y_LIM)){
    moveDistance(10, stepperY);
  }
  if(!digitalRead(X_LIM)){
    moveDistance(10, stepperX);
  }
  bool Y_lim_state = digitalRead(Y_LIM);
  bool X_lim_state = digitalRead(X_LIM);
  while(Y_lim_state){
    stepperY.move(1);
    delay(2);
    Y_lim_state = digitalRead(Y_LIM);
  }
  while(X_lim_state){
    stepperX.move(1);
    delay(2);
    X_lim_state = digitalRead(X_LIM);
  }

  moveDistance(X_OFFSET,stepperX);
  moveDistance(Y_OFFSET,stepperY);
  
  current_positions[0] = 0.0;
  current_positions[1] = 0.0;
  raise_gripper();
  delay(1000);
  magnet(false);
}

void raise_gripper(){
  z_axis_servo.write(servo_limits[0]);
}

void lower_gripper(){
  z_axis_servo.write(servo_limits[1]);
}

void magnet(bool state){
  digitalWrite(pins[4],state);
}

void init_servos(){
  z_axis_servo.attach(pins[5]);
  raise_gripper();
  magnet(false);
}

void move_machine(float x, float y){
  float distanceToMove = (x * GRID_COEFFICIENT) - current_positions[0];
  moveDistance(distanceToMove, stepperX);
  current_positions[0] = x * GRID_COEFFICIENT;
  distanceToMove = (y * GRID_COEFFICIENT) - current_positions[1];
  moveDistance(distanceToMove, stepperY);
  current_positions[1] = y * GRID_COEFFICIENT;
}

void setup() {
  Serial.begin(115200);
  stepperY.begin(RPM, MICROSTEPS);
  stepperX.begin(RPM, MICROSTEPS);
  pinMode(Y_LIM, INPUT_PULLUP);
  pinMode(X_LIM, INPUT_PULLUP);
  pinMode(pins[4], OUTPUT);
  magnet(false);
  init_servos();
  homeAxes();
}

void loop() {
  if(Serial.available()>0){
    String command = Serial.readStringUntil(',');
    if(command=="MOVE"){
      float locationX = Serial.parseFloat();
      float locationY = Serial.parseFloat();
      locationX --;
      locationY --;
      magnet(true);
      delay(1000);
      raise_gripper();
      delay(1000);
      move_machine(locationX, locationY);
      magnet(false);
      delay(1000);
      lower_gripper();
      delay(1000);
      magnet(true);
      delay(1000);
      raise_gripper();
      move_machine(0, 0);
      lower_gripper();
      delay(1000);
      magnet(false);
      delay(10000);
      magnet(true);
      delay(1000);
      raise_gripper();
      move_machine(locationX, locationY);
      lower_gripper();
      delay(1000);
      magnet(false);
      delay(1000);
      raise_gripper();
      delay(1000);
      move_machine(0, 0);
    }
    else if(command=="HOME"){
      homeAxes();
    }
  }

}
Python CodePython
import MySQLdb
from flask import Flask, abort, request
from serial import Serial

app = Flask(__name__)

ser = Serial("COM3",baudrate=115200)

name = ""
category = ""
locationX, locationY = 0, 0

@app.route('/',methods=['GET','POST'])
def retrieve():
    name = str(request.args.get('name'))
    if "%20" in name:
        name = name.replace("%20"," ")
    category = str(request.args.get('category'))
    if "%20" in category:
        category = category.replace("%20"," ")
    print(name+category)
    cur.execute("SELECT * FROM parts WHERE name LIKE '"+name+"' AND category LIKE '"+category+"'")

    for row in cur.fetchall():
        locationX = int(row[5])
        locationY = int(row[6])
        print("id=%d, category=%s, name=%s, quantity=%d, x=%d, y=%d" % (row[0], row[1], row[2], row[3], row[5], row[6]))
        
    ser.write(str.encode("MOVE,"+str(locationX)+","+str(locationY)+"\n"))
    return "done"

db = MySQLdb.connect(host="IP of mysql server",user="username",passwd="password",db="components")

cur = db.cursor()
    
if __name__=='__main__':
    try:
        app.run(host='0.0.0.0',port=5000)
    except:
        db.close()
        ser.close()

Custom parts and enclosures

Thingiverse Link
A link to the thingiverse repo

Comments

Similar projects you might like

IR Proximity Sensors with Simulink and Arduino (Part 1)

Project tutorial by MATLAB Makers

  • 499 views
  • 3 comments
  • 2 respects

Home Automation System for a Camp with Cellular Internet

Project tutorial by Dana Mah

  • 7,970 views
  • 4 comments
  • 27 respects

Morse Code Transceiver

Project tutorial by Achindra Bhatnagar

  • 5,459 views
  • 8 comments
  • 27 respects

R/C Controller for Arduino and Simulink

Project tutorial by MATLAB Makers

  • 460 views
  • 2 comments
  • 3 respects

Amazon Alexa / Arduino YÚN Smart Home Light Sample

Project showcase by Noctuvigilus

  • 1,243 views
  • 0 comments
  • 4 respects

Crazy Arduino Hose Display

Project showcase by hwhardsoft

  • 195 views
  • 0 comments
  • 3 respects
Add projectSign up / Login