G-code 3D printer servo control

Hello all, I'm new to Arduino and this is a real doozy...

I want to control 4 servos (3x180 degree hobby servos and 1x360 degree rotational servo) from my arduino Uno using serial output generated by repetier, a popular 3D printing control software. Repetier sends information like so: G1 X12.34 Y23.45 Z34.56 E45.67 F4000. G1 is a movement command, X, Y, and Z are absolute coordinates on the Cartesian plane, E is the extruder movement and F is extruder speed. From my limited knowledge and heavy modification of the sample servo sketch, this is my "code."

#inculde <Servo.h>

Servo servo1; Servo servo2; Servo servo3; Servo servo4;
  //servo1 is X, 2 is Y, 3 is Z and 4 is E

void setup() {
  pinMode(1,OUTPUT);
  servo1.attach(9);
  servo2.attach(10);
  servo3.attach(11);
  servo4.attach(12);
  Serial.begin(19200);
  Serial.println("Ready");
}

void loop() {
  static int v = 0
  if ( Serial.available()) {
    char ch = Serial.read();
    Serial.print(ch);

  case 'X':
    v = v * 10 + ch - '0';
    servo1.write(v);
    v = 0;
    break;
  case 'Y':
    v = v * 10 + ch - '0';
    servo2.write(v);
    v = 0;
    break;
  case 'Z':
    v = v * 10 + ch - '0';
    servo3.write(v);
    v = 0;
    break;
  case 'E':
    v = v * 10 + ch - '0';
    servo4.write(v);
    v = 0;
    break;
  }
}

Of course nothing happens when I send some code via Repetier. Let me know if this is even feasible. No, scratch that, let me know how to make this possible, because I know it must be. Thanks for your time.

  if ( Serial.available()) {
    char ch = Serial.read();
    Serial.print(ch);

  case 'X':

Can't have a case without a switch.

It all sounds feasible (when you get your code working), but servos aren't noted for their accuracy, so unless you're looking for all your 3D prints to look like Dali originals, you might want to rethink.

thisguyaves:

    v = v * 10 + ch - '0';

Also, '0' returns as the number 48 I would think...
If you put it in ' ' thingies (I'm dutch, so live with it that i don't know how there called) it's a string...

C-F-K:

thisguyaves:

    v = v * 10 + ch - '0';

Also, '0' returns as the number 48 I would think...
If you put it in ' ' thingies (I'm dutch, so live with it that i don't know how there called) it's a string...

That's a very common way of converting ASCII decimal digits to their decimal value.
e.g. '3 - '0' == 3

The * 10 performs the decimal weighting of the result so far.

AWOL:

C-F-K:

thisguyaves:

    v = v * 10 + ch - '0';

Also, '0' returns as the number 48 I would think...
If you put it in ' ' thingies (I'm dutch, so live with it that i don't know how there called) it's a string...

That's a very common way of converting ASCII decimal digits to their decimal value.
e.g. '3 - '0' == 3

The * 10 performs the decimal weighting of the result so far.

[dramatic voice]
When... does... the... learning... stop...
[/dramatic voice]

AWOL:

  if ( Serial.available()) {

char ch = Serial.read();
    Serial.print(ch);

case 'X':



Can't have a case without a switch.

It all sounds feasible (when you get your code working), but servos aren't noted for their accuracy, so unless you're looking for all your 3D prints to look like Dali originals, you might want to rethink.

I'm shooting for more of a "look mommy, my first 3D printer!" type design. The build area wouldn't be much more than 3" or so. I understand the limitations, but I think if this works then there could be a printer that costs less than $150 or so. So I guess I missed the switch from the demo code... does this make logical sense now?

#include <Servo.h>

Servo servo1; Servo servo2; Servo servo3; Servo servo4;
  //servo1 is X, 2 is Y, 3 is Z and 4 is E

void setup() {
  pinMode(1,OUTPUT);
  servo1.attach(9);
  servo2.attach(10);
  servo3.attach(11);
  servo4.attach(12);
  Serial.begin(19200);
  Serial.println("Ready");
}

void loop() {
  static int v = 0;
  if (Serial.available()) {
    char ch = Serial.read();
    Serial.print(ch);

switch(ch) {
  case 'X':
    v = v * 10 + ch - '0';
    servo1.write(v);
    v = 0;
    break;
}
switch(ch){
  case 'Y':
    v = v * 10 + ch - '0';
    servo2.write(v);
    v = 0;
    break;
  }
switch(ch){
  case 'Z':
    v = v * 10 + ch - '0';
    servo3.write(v);
    v = 0;
    break;
}
switch(ch){
  case 'E':
    v = v * 10 + ch - '0';
    servo4.write(v);
    v = 0;
    break;
    }
  }
}

Edit: Tried this, and X and Y servos are separate and distinct, but no matter what X or Y number I send manually in serial, the servo (X or Y) moves to approximately 30 degrees after the first number I enter and will not respond again. What am I missing? I figure I need to be able to get all servos to move correctly before I move forward.

There's nothing in your code that does anything with the digits you're sending. Only X,Y,Z and E are processed. This:

switch(ch) {
  case 'X':
    v = v * 10 + ch - '0';

is equivalent to

switch(ch) {
  case 'X':
    v = v * 10 + 'X' - '0';

So you always get a fixed servo angle.

Your switch statements are odd too - there should be one, with four cases in it. Not four with one each.

wildbill:
There's nothing in your code that does anything with the digits you're sending. Only X,Y,Z and E are processed. This:

switch(ch) {

case 'X':
    v = v * 10 + ch - '0';



is equivalent to 


switch(ch) {
  case 'X':
    v = v * 10 + 'X' - '0';



So you always get a fixed servo angle.

Your switch statements are odd too - there should be one, with four cases in it. Not four with one each.

what would that switch statement look like?

switch(x,y,z,e) {

^?

I'm still trying to wrap my mind around servo control. I just need the X,Y,Z,and E values separated then executed all at once... does that make sense?

Edit: tinkering produced the following. I can control individual servos! :smiley: that's a start... But the angle entered lags 1 command behind, i.e. X90 no movement, X0 moves to X90, X180 moves to X0, X45 moves to X180, etc.

#include <Servo.h>

Servo servo1; Servo servo2; Servo servo3; Servo servo4;
  //servo1 is X, 2 is Y, 3 is Z and 4 is E

void setup() {
  pinMode(1,OUTPUT);
  servo1.attach(9);
  servo2.attach(10);
  servo3.attach(11);
  servo4.attach(12);
  Serial.begin(19200);
  Serial.println("Ready");
}

void loop() {
  static int v = 0;
  static int w = 0;
  if (Serial.available()) {
    char ch = Serial.read();
    Serial.print(ch);

switch(ch) {
    case '0'...'9':
      v = v * 10 + ch - '0';
      w = w * 10 + ch - '0';
      break;
    case 'X':
      servo1.write(v);
      v = 0;
      break;
    case 'Y':
      servo2.write(w);
      w = 0;
      break;
  }
}
}

thisguyaves:
Repetier sends information like so: G1 X12.34 Y23.45 Z34.56 E45.67 F4000.

Assuming you are happy with the fact that servos will be very inadequate for what you want ...

I am interested in the concept of controlling stepper motors using an Arduino and with the instructions coming from a GCode file.

Without meaning any offense you have chosen a very mickey-mouse way of going about the problem. Your Arduino needs to read in the whole line of GCode and only then try to figure it out. You might like to look at the demo here and here for ways of sending data to an Arduino.

Interpretiing GCode is not a simple process and my suggestion is that you do that on your PC and just send the values needed by the servos to the Arduino. That will reduce the Arduino code to something very simple - almost as simple as

for (n = 0; n < 4; n++) {
  servo[n].write(servoValue[n]);
}

...R

Robin2:

thisguyaves:
Repetier sends information like so: G1 X12.34 Y23.45 Z34.56 E45.67 F4000.

Assuming you are happy with the fact that servos will be very inadequate for what you want ...

I am interested in the concept of controlling stepper motors using an Arduino and with the instructions coming from a GCode file.

Without meaning any offense you have chosen a very mickey-mouse way of going about the problem. Your Arduino needs to read in the whole line of GCode and only then try to figure it out. You might like to look at the demo here and here for ways of sending data to an Arduino.

Interpretiing GCode is not a simple process and my suggestion is that you do that on your PC and just send the values needed by the servos to the Arduino. That will reduce the Arduino code to something very simple - almost as simple as

for (n = 0; n < 4; n++) {

servo[n].write(servoValue[n]);
}




...R

I understand how ridiculous a servo printer is, I actually have a "normal" Printrbot, so I totally get that. My only coding experience is TI-BASIC on a TI-84 calculator, so I take absolutely no offense. I vaguely comprehend the idea of sending just the values to arduino, but not nearly to the magnitude I need to, so I'll sacrifice arduino performance for my comprehension if that's ok. Surely there is some way to pluck apart the 4 axis from the Gcode. It might not be the most practical, but if I can understand it I'll feel much better. I know it's a lot, but if someone could spare the time to write the bit that would parse, say, the X axis alone then maybe I could wrap my mind around it and duplicate it for the other axis. Thanks in advance.

thisguyaves:
but if someone could spare the time to write the bit that would parse, say, the X axis alone then maybe I could wrap my mind around it and duplicate it for the other axis.

The logic of the following applies whether on an Arduino or in a PC program.

Assume your Arduino has received this line of GCode.

G1 X12.34 Y23.45 Z34.56 E45.67 F4000

First step is to have a list of ALL possible letter codes that you want to interpret - for this line they will be G, X, Y, Z, E and F

Next complication is whether the letters always come in the same order and whether the line is invalid in some circumstances.

If the line always comes in the same order (and obviously different lines will have a different letter sequence. So maybe you need one treatment for a G1 line and another treatment for a different type of line.

For the example line you can work through it searching for the letters in order and saving the number that follows into the appropriate variable.

I am attaching a .txt file which is actually a JRuby (PC) program (should have a .rb filetype) that I wrote to interpret GCode. That program converts the GCode into timing for driving stepper motors. You may not be interested in that part - but I think the way I have parsed the GCode may be of interest. I am in the middle of converting the JRuby code to Python. By the way I have not proved that this program works correctly - but it should be pretty close.

One of the reasons why I advocate doing this stuff on a PC is because it is child's play to do floating point maths on a PC.

...R

Gcode2.txt (6.99 KB)

Robin2:

thisguyaves:
but if someone could spare the time to write the bit that would parse, say, the X axis alone then maybe I could wrap my mind around it and duplicate it for the other axis.

The logic of the following applies whether on an Arduino or in a PC program.

Assume your Arduino has received this line of GCode.

G1 X12.34 Y23.45 Z34.56 E45.67 F4000

First step is to have a list of ALL possible letter codes that you want to interpret - for this line they will be G, X, Y, Z, E and F

Next complication is whether the letters always come in the same order and whether the line is invalid in some circumstances.

If the line always comes in the same order (and obviously different lines will have a different letter sequence. So maybe you need one treatment for a G1 line and another treatment for a different type of line.

For the example line you can work through it searching for the letters in order and saving the number that follows into the appropriate variable.

I am attaching a .txt file which is actually a JRuby (PC) program (should have a .rb filetype) that I wrote to interpret GCode. That program converts the GCode into timing for driving stepper motors. You may not be interested in that part - but I think the way I have parsed the GCode may be of interest. I am in the middle of converting the JRuby code to Python. By the way I have not proved that this program works correctly - but it should be pretty close.

One of the reasons why I advocate doing this stuff on a PC is because it is child's play to do floating point maths on a PC.

...R

Thanks a lot! I'll look into changing the steps into degrees. I understand why the computer should do most of the heavy lifting, I'm just not affluent with it since I got the arduino yesterday.

Edit, after looking at your code and comments in said code, I'll share some of my printer's settings so you can get a better idea of speed and range. Also, you'll want to include support for endstops, essentially physical switches/push buttons so the printer knows its max and minimum positions. (this is how most 3D printers "zero themselves" aka homing.) I also didn't spot any temperature commands, but I'm an untrained eye. Temperature heat up should be the first thing before any movement is accomplished, and a thermistor temp reading should happen every second or two idealy. Default PLA is about 205 C.

Extruder feed rate: 20 mm/s
Travel rate (non-extrusion): 130 mm/s
Travel rate (extruding): 30-60 mm/s
Z travel rate: 100 mm/s

thisguyaves:
Edit, after looking at your code and comments in said code, I'll share some of my printer's settings so you can get a better idea of speed and range. Also, you'll want to include support for endstops, essentially physical switches/push buttons so the printer knows its max and minimum positions

My stuff is old code - just intended as an experiment at interpreting GCode. I had been thinking of building a 3D printer but that idea has gone away. And I had been thinking of using a small lathe for the axes for the printer - so it would have been very slow. Even the fast DIY ones are too slow - now that I have actually seen one working at an exhibition.

...R