Sunday, 11 November 2012

Mantis-a-like CNC milling machine - update

I've been working on the Mantis CNC build recently to get the X-Y table up and running. Previously, I'd posted the design. I was working on a motor driver board based on an ATMEGA328P running the Arduino bootloader and three L293 motor driver chips. After a few months on-again-off-again (I/O pun) work on the board I gave up trying to get it working :( Instead I bought 3x Easy Driver boards.



These are really easy to use - just two pins are used to step the motor and tell it which direction to turn. They support bi-polar (4,6 and 8 wire steppers) motors and have a trim pot for varying the current supply.


So I wired an Easy Driver running on a PC PSU 12V supply into an Airpax stepper motor that I found a pair of on Ebay. It made a few noises and tick-tocked, but spectacularly failed to spin. I suspect these motors need more voltage/current to run. Oh well. In my box-o-junk I had a stepper from a 99p Ebay printer. Wiring that up and tweaking the step speed gave good results so I fitted this motor to the X-axis drive shaft on my mill. Now we're in business:


video





The green wire on the left is an end-stop switch. When the X-axis reaches the wires it makes a circuit that stops the motor running. I also used this to set a 'zero' for the axis. Some Arduino coding gave me the following functions (so far!):
  • Go to zero (and reset the step counter at the zero postion)
  • Do a relative move (+ve or -ve direction) at a certain speed
  • Move to a set position on the X-axis
The end-stop switch protects the machine from driving itself apart. A 'max X position' function prevents the machine winding itself too far the other way. Both these features aren't failsafe though!
The code can be controlled by serial commands - the intent is to use GRBL eventually, but this is great for getting to grips with hardware/software tuning :)

Now I just need some more stepper motors.

Here's some really bad, hacked code. Don't look at it too hard or it might self-combust...

// CmdMessenger library available from https://github.com/dreamcat4/cmdmessenger
#include <CmdMessenger.h>
// Streaming4 library available from http://arduiniana.org/libraries/streaming/
#include <Streaming.h>
// Mustnt conflict / collide with our message payload data. Fine if we use base64 library ^^ above
char field_separator = ',';
char command_separator = ';';

// Attach a new CmdMessenger object to the default Serial port
CmdMessenger cmdMessenger = CmdMessenger(Serial, field_separator, command_separator);

// ------------------ C M D  L I S T I N G ( T X / R X ) ---------------------

// We can define up to a default of 50 cmds total, including both directions (send + recieve)
// and including also the first 4 default command codes for the generic error handling.
// If you run out of message slots, then just increase the value of MAXCALLBACKS in CmdMessenger.h

// Commands we send from the Arduino to be received on the PC
enum
{
  kCOMM_ERROR    = 000, // Lets Arduino report serial port comm error back to the PC (only works for some comm errors)
  kACK           = 001, // Arduino acknowledges cmd was received
  kARDUINO_READY = 002, // After opening the comm port, send this cmd 02 from PC to check arduino is ready
  kERR           = 003, // Arduino reports badly formatted cmd, or cmd not recognised

  // Now we can define many more 'send' commands, coming from the arduino -> the PC, eg
  // kICE_CREAM_READY,
  // kICE_CREAM_PRICE,
  // For the above commands, we just call cmdMessenger.sendCmd() anywhere we want in our Arduino program.

  kSEND_CMDS_END, // Mustnt delete this line
};

// Commands we send from the PC and want to recieve on the Arduino.
// We must define a callback function in our Arduino program for each entry in the list below vv.
// They start at the address kSEND_CMDS_END defined ^^ above as 004
messengerCallbackFunction messengerCallbacks[] = 
{
  G00,            // 004 Rapid positioning
  G01,            // 005 Linear interpolation
  G09,            // 006 Exact stop check
  RESET_X,        // 007 go to end stop in x
  ABS_X,          // 008 go to absolute location in X - assumes system has been zeroed
  NULL
};
// Its also possible (above ^^) to implement some symetric commands, when both the Arduino and
// PC / host are using each other's same command numbers. However we recommend only to do this if you
// really have the exact same messages going in both directions. Then specify the integers (with '=')

#define DIR_PIN 3
#define STEP_PIN 2
float x_speed = 0.1;
int end_stop_1 = 6;
int end_stop_2 = 7;
long max_x = 31500;
long curr_pos = 0;
float max_x_speed = 0.2;

void setup() 
{
  //setup serial comms
  Serial.begin(9600);

  // cmd messanger setup
  cmdMessenger.print_LF_CR();   // Make output more readable whilst debugging in Arduino Serial Monitor
  // Attach default / generic callback methods
  cmdMessenger.attach(kARDUINO_READY, arduino_ready);
  cmdMessenger.attach(unknownCmd);
  // Attach my application's user-defined callback methods
  attach_callbacks(messengerCallbacks);
  arduino_ready();
  pinMode(DIR_PIN, OUTPUT);
  pinMode(STEP_PIN, OUTPUT);
  pinMode(end_stop_1, INPUT);
  pinMode(end_stop_2, INPUT);
}

void loop() 
{
  // Process incoming serial data, if any
  cmdMessenger.feedinSerialData();


}


// ------------------ C A L L B A C K  M E T H O D S -------------------------

void G00()
{
  int i=0;
  int j=0;

  long go_x = 0;


  char str_temp[10];

  while ( cmdMessenger.available() )
  {
    char buf[350] = { 
      '\0'     };
    cmdMessenger.copyString(buf, 350);
    if(buf[0])
    {
      //cmdMessenger.sendCmd(kACK, buf);

      for(j=0;j<10;j++)
      {
        str_temp[j] = buf[j];
      }

      switch(i){

      case 0:
        //cmdMessenger.sendCmd(kACK, "Channel");
        go_x = atol(str_temp);
        break;
      case 1:
        //cmdMessenger.sendCmd(kACK, "Channel");
        x_speed = atof(str_temp);
        break;
      }
      i++;
    }
  }

  Serial.print("moving head in x, d=");
  Serial.print(go_x);
  Serial.print(", speed= ");
  Serial.println(x_speed);  
  rotate(go_x, x_speed);
  Serial.print("Stoped at pos=");
  Serial.println(curr_pos);



  //set_rate(go_x, 0, 0);

}


void G01()
{

  // All stop
  //set_rate(0, 0, 0);

}

void G09()
{

  // All stop
  //set_stop();

}

void RESET_X()
{

  rotate(40000,0.1);
  curr_pos = 0;
  Serial.println("  X axis at origin");
}

void ABS_X()
{
  int i=0;
  int j=0;
  long go_x = 0;
  char str_temp[10];

  while ( cmdMessenger.available() )
  {
    char buf[350] = { 
      '\0'     };
    cmdMessenger.copyString(buf, 350);
    if(buf[0])
    {
      //cmdMessenger.sendCmd(kACK, buf);

      for(j=0;j<10;j++)
      {
        str_temp[j] = buf[j];
      }

      switch(i){

      case 0:
        //cmdMessenger.sendCmd(kACK, "Channel");
        go_x = atol(str_temp);
        break;
      case 1:
        //cmdMessenger.sendCmd(kACK, "Channel");
        x_speed = atof(str_temp);
        break;
      }
      i++;
    }
  }

  long del_pos = curr_pos - go_x; 

  Serial.print("moving head in x, pos=");
  Serial.print(go_x);
  Serial.print(", delta pos= ");
  Serial.print(del_pos);
  Serial.print(", speed= ");
  Serial.println(x_speed);  
  rotate(del_pos, x_speed);
  Serial.print("Stoped at pos=");
  Serial.println(curr_pos);
  
}
// ------------------ D E F A U L T  C A L L B A C K S -----------------------

void arduino_ready()
{
  // In response to ping. We just send a throw-away Acknowledgement to say "im alive"
  cmdMessenger.sendCmd(kACK,"Arduino ready");
}

void unknownCmd()
{
  // Default response for unknown commands and corrupt messages
  cmdMessenger.sendCmd(kERR,"Unknown command");
}

// ------------------ E N D  C A L L B A C K  M E T H O D S ------------------

void attach_callbacks(messengerCallbackFunction* callbacks)
{
  int i = 0;
  int offset = kSEND_CMDS_END;
  while(callbacks[i])
  {
    cmdMessenger.attach(offset+i, callbacks[i]);
    i++;
  }
}






void rotate(long steps, float speed){
  //rotate a specific number of microsteps (8 microsteps per step) - (negitive for reverse movement)
  //speed is any number from .01 -> 1 with 1 being fastest - Slower is stronger
  int dir = (steps > 0)? HIGH:LOW;
  boolean step_dir;
  if (steps > 0)
    step_dir = true;
  else
    step_dir = false;
    
  steps = abs(steps);

  digitalWrite(DIR_PIN,dir); 

  float usDelay;
  if(speed < max_x_speed) 
    usDelay = (1/speed) * 70;
  else
    usDelay = (1/max_x_speed) * 70;
    
  for(long i=0; i < steps; i++){
    
    if((curr_pos >= max_x) && !step_dir)
    {
      Serial.println("Max X reached - halting ");
      break;
    }
    
    digitalWrite(STEP_PIN, HIGH);
    delayMicroseconds(usDelay); 

    digitalWrite(STEP_PIN, LOW);
    delayMicroseconds(usDelay);
    
    if(step_dir)
      curr_pos--;
    else
      curr_pos++;
    
    if(check_end_stops() && step_dir)
    {
      Serial.print("End STOP: Max steps achieved: ");
      Serial.println(i);
      break;
    }
  }
} 





void rotateDeg(float deg, float speed){
  //rotate a specific number of degrees (negitive for reverse movement)
  //speed is any number from .01 -> 1 with 1 being fastest - Slower is stronger
  int dir = (deg > 0)? HIGH:LOW;
  digitalWrite(DIR_PIN,dir); 

  int steps = abs(deg)*(1/0.225);
  float usDelay = (1/speed) * 70;

  for(int i=0; i < steps; i++){
    
    digitalWrite(STEP_PIN, HIGH);
    delayMicroseconds(usDelay); 

    digitalWrite(STEP_PIN, LOW);
    delayMicroseconds(usDelay);
  }
}

boolean check_end_stops()
{
  if(digitalRead(end_stop_1) == false)
    return true;
  else
    return false;
    
}

No comments:

Post a Comment