Tutorials

How to build an 2-axis Arduino CNC Gcode Interpreter

Computer model of where the 3-axis CNC router will move to cut the shape

Purpose

3D printers, laser cutters, water jet cutters, robot arms, delta robots, stewart platforms, the Makelangelo: all of these are examples of computer numerical control (CNC) machines. CNC machines need to move accurately and on command. Stepper motors are a great way to move accurately – they move a predictable amount and then stay where you put them. To command the stepper motors we need a way to easily turn our human desires into machine instructions into stepper motor steps. In effect we need our robot brain to be an interpreter. I’m going to show you a simple interpreter written for Arduino that lets you move stepper motors for your robots.

read the explanation and see a video

Tutorials

How to Build a Amazon Warehouse KIVA style robot with Mecanum Wheels

Amazon warehouse KIVA robots can roll forward, sideways, and turn on the spot thanks to special Mecanum wheels. A single wheel has many rollers on the outside edge, turned at a 45 degree angle. Here’s a short video example of what I mean.

What’s really odd about Mecanum wheels is that half turn one way and half turn the other to go in a straight line.

Each wheel is driven by a DC motor. Pairs of motors are connected to 2 channel motor drivers. The two 2-channel drivers are connected to an Arduino that listens for instructions from the pilot (me, in this case).

In 2015 I made an update to this model and put it on a laser cut frame.

ask me and I’ll draw a wiring diagram

Tutorials

How to Control Arduino through the Serial Monitor

When I’m developing Arduino code, I often have variables that need to be tweaked during testing & calibration.
Rather than recompile and upload the code (a time consuming process) I wrote a few lines to let me open the Serial Monitor and send commands directly.

I hope you find this useful – I use it in all my robots!

#define DELAY   (5)
#define BAUD    (57600)
#define MAX_BUF (64)

char buffer[MAX_BUF];
int sofar;

char test_on=0;  // only used in processCommand as an example.
float pos_x;
float pos_y;
float pos_z;


void jog(float x, float y, float z) {
  // do some tests here to validate x, y, and z.
  pos_x=x;
  pos_y=y;
  pos_z=z;
}


void processCommand() {
  if(!strncmp(buffer,"help",4)) {
    Serial.println("commands: where; test (1/0); jog [x(float)] [y(float)] [z(float)];");
  } else if(!strncmp(buffer,"where",5)) {
    // no parameters
    Serial.print(pos_x);
    Serial.print(", ");
    Serial.print(pos_y);
    Serial.print(", ");
    Serial.println(pos_z);
  } else if(!strncmp(buffer,"test",4)) {
    // one whole number parameter
    char *state=strchr(buffer,' ')+1;
    Serial.println(state);
    if(state[0]=='0') {
      Serial.println("End test");
      test_on=0;
    } else {
      Serial.println("Start test");
      test_on=1;
    }
  } else if(!strncmp(buffer,"jog",3)) {
    // several optional float parameters.
    // then calls a method to do something with those parameters.
    float xx=pos_x;
    float yy=pos_y;
    float zz=pos_z;

    char *ptr=buffer;
    while(ptr && ptr < buffer+sofar) {
      ptr=strchr(ptr,' ')+1;
      switch(*ptr) {
      case 'x': case 'X': xx=atof(ptr+1);  Serial.print('x'); Serial.println(xx); break;
      case 'y': case 'Y': yy=atof(ptr+1);  Serial.print('y'); Serial.println(yy); break;
      case 'z': case 'Z': zz=atof(ptr+1);  Serial.print('z'); Serial.println(zz); break;
      default: ptr=0; break;
      }
    }
    
    jog(xx,yy,zz);
  }
}


void setup() {
  Serial.begin(BAUD);
  Serial.println("Init...");
  Serial.println("Stretching...");

  sofar=0;
  Serial.println(F("** AWAKE **"));
  Serial.print(F("\n> "));
}


void loop() {
  // listen for serial commands
  while(Serial.available() > 0) {
    char c = Serial.read();
    if(sofar < MAX_BUF-1)
      buffer[sofar++]=c;
    if(c=='\n') {
      // echo confirmation
      buffer[sofar]=0;
      Serial.println(buffer);

      // do something with the command
      processCommand();
      // reset the buffer
      sofar=0;
      // ready for more
      Serial.print(F("\n> "));
    }
  }
}

Edit 2016-07-11: closed a possible overflow in buffer[].

Tutorials

How to Use a Rotary Encoder with Arduino

Arduino / rotary encoder example

A rotary encoder is essential in most analog to digital measurements. Everything from volume control to robot movement can be tracked with a good rotatry encoder. Conveniently, everything you need to know is in Wikipedia. This code is a variation on Metsfan’s contribution to the Sparkfun forums. This version avoids unnecessary if statements. SO NERDY.

#define PIN_HIGHBIT (7)
#define PIN_LOWBIT  (5)
#define PIN_PWR     (3)
#define BAUDRATE    (9600)
#define DEBUG         (1)

// globals
int state, prevState = 0, count = 0;
/* old state, new state, change (+ means clockwise)
 * 0 2 +
 * 1 0 +
 * 2 3 +
 * 3 1 +
 * 0 1 -
 * 1 3 -
 * 2 0 -
 * 3 2 -
 */
int encoderStates[4][4] = {
 {  0, -1,  1,  0 }, 
 {  1,  0,  0, -1 }, 
 { -1,  0,  0,  1 }, 
 {  0,  1, -1,  0 }, 
};

void setup() {
  pinMode(PIN_HIGHBIT, INPUT);
  pinMode(PIN_LOWBIT, INPUT);
  pinMode(PIN_PWR, OUTPUT);
  digitalWrite(PIN_PWR, LOW);
  digitalWrite(PIN_LOWBIT, HIGH);
  digitalWrite(PIN_HIGHBIT, HIGH);
  Serial.begin(BAUDRATE); 
}

void loop() {
  state = (digitalRead(PIN_HIGHBIT) << 1) | digitalRead(PIN_LOWBIT);
  count += encoderStates[prevState][state];

#ifdef DEBUG
  if (state != prevState) {
    Serial.print(state, DEC);
    Serial.print(' ');
    Serial.print(prevState, DEC);
    Serial.print(' ');
    Serial.println(count, DEC);
  }
#endif

  prevState = state;
}