My Homemade CNC version 2 with USB Driven PIC16F877A Driver

This is the second version of my CNC machine that is capable of cutting plywood. (see version 1)

DSCF1099

DSCF1590

Skate bearings on L-Iron bars

333

Home made mechanical encoders. I later removed these and made the system open loop.

555

The controller software.

DSCF1589

PSU, Stepper Drivers and PIC

DSCF1588 DSCF1587 DSCF1586

Sketchup Models

1

X-Axis (bed)

2 3

Y-axis with Z-axis motor attached

4

Z-axis and router

Components

* 3A Stepper motors (280 oz-in I guess)
* Cheap TB6560 stepper drivers 24V-3A
* Thread bars and double nuts
* Skate bearings on rails
* 24V 10A SMPS
* CP210x USB-TTL module.
* Homemade Firmware (CCS-C) and driver program (VC++)
* PIC16F877A (more than enough)
* 1/4 inch Trim router + 2mm HSS end mill + V-bit

PIC Program (CCS-C)

core of the code

void main()
{
    int8 c;

    Setup();

    output_high(RED_LED);

    output_low(PIN_X_EN); // disable
    output_high(PIN_Y_EN);
    output_high(PIN_Z_EN);

    delay_ms(1000);
    output_low(RED_LED);
    output_low(GREEN_LED);

    while (TRUE)
    {
       output_high(LED_READY); // RX indicator
       c = my_getc();
       output_low(LED_READY); // RX indicator
       if (c == 0xff) // reset comm
       {
          gi_CmdCount = 0;
          gi_NextCmd = 0;
          continue;
       }
       else if (c == 0xfe) // end of transmission 
       {
          while (gi_CmdCount > 0)
          {
              output_high(LED_WORKING);
              DoNext();
              output_low(LED_WORKING);
          } 
      }

      if (gi_CmdCount >= MAX_CMDS)
      {
          SendNack(ERR_MSG_LIMIT);
          gi_CmdCount = 0;
          gi_NextCmd = 0;
          continue;
      }
      set_cmd(gi_CmdCount, c);
      gi_CmdCount++;
   }
}
//------------------------------------------------------------------------------
void ConfigureDevice(int8 iScale, int8 iActive, int8 iMaxActive)
{
   gi_Active = iActive;
   gi_Active *= 100;
   gi_MaxActive = iMaxActive;
   gi_MaxActive *= 100;
   gi_Scale = iScale;

   output_low(PIN_X_EN); // disable
   output_high(PIN_Y_EN);
   output_high(PIN_Z_EN);
}
//------------------------------------------------------------------------------
void UpdateDelay()
{
   if (gi_CurActive > gi_Active)
   {
      if (gi_CurActive > 3000) 
         gi_CurActive -= 1000;
      else if (gi_CurActive > 1000) 
        gi_CurActive -= 200;
      else
        gi_CurActive -= 50;
   }
}
//------------------------------------------------------------------------------
void Move(signed int iDir, int iAxis)
{
   // Y dir: Reversed to match hardware

   switch (iAxis)
   {
   case AXIS_X:
   {
     output_bit(PIN_X_DIR, iDir == FWD ? 0 : 1);
     output_high(PIN_X_CLK);
     output_high(LED_STEPPING);
     UpdateDelay();
     delay_us(gi_CurActive);
     output_low(PIN_X_CLK);
     output_low(LED_STEPPING);
     break;
   }
   case AXIS_Y:
   {
     output_bit(PIN_Y_DIR, iDir == FWD ? 0 : 1);
     output_high(PIN_Y_CLK);
     output_high(LED_STEPPING);
     UpdateDelay();
     delay_us(gi_CurActive);
     output_low(PIN_Y_CLK);
     output_low(LED_STEPPING);

     break;
  }
  case AXIS_Z:
  {
     output_bit(PIN_Z_DIR, iDir == FWD ? 1 : 0);
     output_high(PIN_Z_CLK);
     output_high(LED_STEPPING);
     UpdateDelay();
     delay_us(gi_CurActive);
     output_low(PIN_Z_CLK);
     output_low(LED_STEPPING);

     break;
  }
 }
}
//------------------------------------------------------------------------------
void RunManual()
{
 int16 i;

 output_low(LED_READY); 

 while (input_state(PIN_MANUAL) == 1)
 {
   if (input_state(PIN_Z_UP) == 1)
   {
      delay_ms(1000);
      gi_CurActive = gi_MaxActive;

      for (i = 0; i < 32000; i++)
         Move(FWD, AXIS_Z);
   }
   else if (input_state(PIN_Z_DOWN) == 1)
   {
       delay_ms(1000);
       gi_CurActive = gi_MaxActive;

       for (i = 0; i < 32000; i++)
           Move(BWD, AXIS_Z);
   }

   output_high(LED_BUSY);
   delay_ms(50);
   output_low(LED_BUSY);
   delay_ms(50);
 }

 delay_ms(1000);
}
//------------------------------------------------------------------------------
void DoNext()
{
   int8 cmd;
   sint16 a;
   int8 i;
   signed int m;
   signed int n;
   signed int k;

   cmd = get_cmd(gi_NextCmd);
   gi_NextCmd++;
   gi_CmdCount--;

   if (cmd == MOVE_XYZ)
   {
      if (gi_CmdCount < 2)
      {
         SendNack(ERR_ARGC);
         gi_CmdCount = 0;
         gi_NextCmd = 0;
         return;
      }
      m = get_cmd(gi_NextCmd) - 125; // distance
      gi_NextCmd++;
      gi_CmdCount--;
      n = get_cmd(gi_NextCmd) - 125; // axis
      gi_NextCmd++;
      gi_CmdCount--;
      gi_CurActive = gi_MaxActive;
      k = (m < 0 ? BWD : FWD);
      a = gi_Scale * abs(m);
     output_high(PIN_X_EN); // enable
     output_low(PIN_Y_EN);
     output_low(PIN_Z_EN);

     while (a > 0)
     {
        for (i = 0; i < 16; i++)
           Move(k, n);
        a--;

        /*if (input_state(PIN_MANUAL) == 1)
          {
              delay_ms(1000);
              RunManual();
              gi_CurActive = gi_MaxActive;
           }*/
      }

       output_low(PIN_X_EN); // disable
       output_high(PIN_Y_EN);
       output_high(PIN_Z_EN);

        gi_CurActive = gi_MaxActive;
    }
    else if (cmd == CONFIGURE_DEVICE)
    {
        if (gi_CmdCount < 3)
        {
             SendNack(ERR_ARGC);
             return;
        }
        m = get_cmd(gi_NextCmd); // scale
        gi_NextCmd++;
        gi_CmdCount--;
        k = get_cmd(gi_NextCmd); // active 
        gi_NextCmd++;
        gi_CmdCount--;

        i = get_cmd(gi_NextCmd); // max active 
        gi_NextCmd++;
        gi_CmdCount--;

        ConfigureDevice(m, k, i);
   }
   else
   {
      SendNack(ERR_NO_SUCH_CMD);
      gi_CmdCount = 0;
      gi_NextCmd = 0;
      return;
   }
   if (gi_CmdCount == 0) // All cmds executed 
   {
       gi_NextCmd = 0;
       SendAck();
   }
}

Homemade CNC v1

This is the first version of my homemade CNC machine (see version 2). It does not have enough power to cut anything but has a reasonable precision in drawing.

Image

It does not have any bearings (other than those inside steppers actually), lead screws run each axis and bear the weight at the same time, reasonably noisy and slow. Each axis (X,Y) is driven by two steppers working synchronously.

Image

Z axis is the head moving mechanism of an old CD ROM drive.

Image

In the above picture Y axis steppers are visible at the top. X axis steppers are in the box at the right hand side. Stepper drivers are based on L298 H-bridges and stays at the same box. This was the first time to work with power electronics. Mani chips left their life.

The base is made of MDF. The working area is about 8″ x 8″. Z axis travels about 1″.

Image

Motor couplers are cheap flexible tubes clamped with hose clips.

Image

Image

This machine is driven by a laptop -> USB to serial TTL converter -> PIC micro -> L298 drivers powered by an ATX PC PSU. As there are no opensource or free CAM softwares that support my configuration, I happened to write my own.

Image

The software accepts images, detect edges, generates tool paths and communicates with the PIC.

If you are trying to make your own homemade CNC, create the mechanical implementation your self and buy some stepper drivers. Do not try to build stepper drivers unless you are a skilled electronics guy. Power electronics is a separate subject that can generate lot of headache. Even a cheap poor quality TB6560 based stepper driver easily defeats most of the H-Bridges. Though steppers are supposed to rotate in steps, they do not. In the natural frequency they just vibrate not rotate (happened to me at 200Hz or 5 ms steps). This is a very sad situation. You have to accelerate the stepper rapidly across this area or start at a larger frequency and lose some steps!

Electromagnetic interference generated by steppers and router is non trivial, that can reset a PIC and stop your work unfinished at the middle. Do not forget the filter capacitors at the power supply and PIC MCLR pin. Do not use the USB power to drive signaling circuit. If your laptop is not intelligent enough to shut down the ports at the correct time then you are very likely to have a laptop with 2/3 ports burnt at the end. So use a separate power supply for the signaling circuit.