Files
SyncHome/trunk/Arduino/sketch_uno_Plotter-mini/sketch_uno_plotter-mini.ino
2023-03-13 09:05:51 +00:00

429 lines
10 KiB
C++

/*
Mini CNC Plotter firmware, based in TinyCNC https://github.com/MakerBlock/TinyCNC-Sketches
Send GCODE to this Sketch using gctrl.pde https://github.com/damellis/gctrl
Convert SVG to GCODE with MakerBot Unicorn plugin for Inkscape available here https://github.com/martymcguire/inkscape-unicorn
More information about the Mini CNC Plotter here (german, sorry): http://www.makerblog.at/2015/02/projekt-mini-cnc-plotter-aus-alten-cddvd-laufwerken/
*/
#include <Servo.h>
#include <AFMotor.h>
/* Structures, global variables */
struct point {
float x;
float y;
float z;
} actuatorPos;
// Current position of plothead
//struct point actuatorPos;
/*****************
* *** Defines ***
* ***************/
//#define debug // Debug
#define LINE_BUFFER_LENGTH 1024
#define LED 13 // LED pin on Arduino Uno
#define zeroX 14 // pin finecorsa X
#define zeroY 15 // pin finecorsa Y
#define ena 2 // enable
/***************
* *** Servo ***
* *************/
#define penZUp 70 // Servo position for Up
#define penZDown 85 // Servo position for Down
#define penServoPin 10 // Servo on PWM pin 10
Servo penServo; // create servo object to control a servo
/*****************
* *** Stepper ***
* ***************/
#define stepsPerRevolution_X 20
#define stepsPerRevolution_Y 48
#define rpm_X 500 // Speed
#define rpm_Y 200 // Speed
#define STEP_Y MICROSTEP // Stepper Command mode (SINGLE, DOUBLE, INTERLEAVE, MICROSTEP)
#define STEP_X DOUBLE
AF_Stepper myStepperY(stepsPerRevolution_Y,1); // Initialize steppers for Y-axis
AF_Stepper myStepperX(stepsPerRevolution_X,2); // Initialize steppers for X-axis
/****************************************
* *** Drawing settings, should be OK ***
* **************************************/
float StepInc = 1;
int StepDelay = 5;
int LineDelay = 0;
int penDelay = 50;
/*****************************************
* *** Motor steps to go 1 millimeter. ***
* ***************************************/
// Use test sketch to go 100 steps. Measure the length of line.
// Calculate steps per mm. Enter here.
float StepsPerMillimeterX = 40.8163; //100.0; 4.7619*2
float StepsPerMillimeterY = 108.1081; //6.6667*2; //100.0;
/*************************************
* *** Drawing robot limits, in mm ***
* ***********************************/
// OK to start with. Could go up to 50 mm if calibrated well.
float Xmin = 0;
float Xmax = 48;
float Ymin = 0;
float Ymax = 35; //40;
float Zmin = 0;
float Zmax = 1;
float Xpos = Xmin;
float Ypos = Ymin;
float Zpos = Zmax;
// Needs to interpret
// G1 for moving
// G4 P300 (wait 150ms)
// M300 S30 (pen down)
// M300 S50 (pen up)
// Discard anything with a (
// Discard any other command!
void Home() {
while (digitalRead(zeroX) == HIGH) {
myStepperX.step(1, BACKWARD, STEP_X); //go to home X
}
while (digitalRead(zeroY) == HIGH) {
myStepperY.step(1, BACKWARD, STEP_Y); //go to home Y
}
digitalWrite(LED, HIGH);
}
/*********************************
* Draw a line from (x0;y0) to (x1;y1).
* Bresenham algo from https://www.marginallyclever.com/blog/2013/08/how-to-build-an-2-axis-arduino-cnc-gcode-interpreter/
* int (x1;y1) : Starting coordinates
* int (x2;y2) : Ending coordinates
**********************************/
void drawLine(float x1, float y1) {
#ifdef debug
Serial.print("fx1, fy1: ");
Serial.print(x1);
Serial.print(",");
Serial.print(y1);
Serial.println("");
#endif
// Bring instructions within limits
if (x1 >= Xmax) {
x1 = Xmax;
}
if (x1 <= Xmin) {
x1 = Xmin;
}
if (y1 >= Ymax) {
y1 = Ymax;
}
if (y1 <= Ymin) {
y1 = Ymin;
}
#ifdef debug
Serial.print("Xpos, Ypos: ");
Serial.print(Xpos);
Serial.print(",");
Serial.print(Ypos);
Serial.println("");
Serial.print("x1, y1: ");
Serial.print(x1);
Serial.print(",");
Serial.print(y1);
Serial.println("");
#endif
// Convert coordinates to steps
x1 = (int)(x1*StepsPerMillimeterX);
y1 = (int)(y1*StepsPerMillimeterY);
float x0 = Xpos;
float y0 = Ypos;
// Let's find out the change for the coordinates
long dx = abs(x1-x0);
long dy = abs(y1-y0);
int sx = x0<x1 ? StepInc : -StepInc;
int sy = y0<y1 ? StepInc : -StepInc;
long i;
long over = 0;
if (dx > dy) {
for (i=0; i<dx; ++i) {
myStepperX.onestep(sx,STEP_X);
//myStepperX.step(1,sx,STEP);
over+=dy;
if (over>=dx) {
over-=dx;
myStepperY.onestep(sy,STEP_Y);
//myStepperY.step(1,sy,STEP);
}
delay(StepDelay);
}
}
else {
for (i=0; i<dy; ++i) {
myStepperY.onestep(sy,STEP_Y);
//myStepperY.step(1,sy,STEP);
over+=dx;
if (over>=dy) {
over-=dy;
myStepperX.onestep(sx,STEP_X);
//myStepperX.step(1,sx,STEP);
}
delay(StepDelay);
}
}
#ifdef debug
Serial.print("dx, dy:");
Serial.print(dx);
Serial.print(",");
Serial.print(dy);
Serial.println("");
Serial.print("Going to (");
Serial.print(x0);
Serial.print(",");
Serial.print(y0);
Serial.println(")");
#endif
// Delay before any next lines are submitted
delay(LineDelay);
// Update the positions
Xpos = x1;
Ypos = y1;
if ((Xpos==0)&&(Ypos==0)){
digitalWrite(LED, HIGH);
}
else {
digitalWrite(LED, LOW);
}
}
// Raises pen
void penUp() {
penServo.write(penZUp);
delay(penDelay);
Zpos=Zmax;
//digitalWrite(15, LOW);
//digitalWrite(16, HIGH);
#ifdef debug
Serial.println("Pen up!");
#endif
}
// Lowers pen
void penDown() {
penServo.write(penZDown);
delay(penDelay);
Zpos=Zmin;
//digitalWrite(15, HIGH);
//digitalWrite(16, LOW);
#ifdef debug
Serial.println("Pen down.");
#endif
}
void processIncomingLine( char* line, int charNB ) {
int currentIndex = 0;
char buffer[ 64 ]; // Hope that 64 is enough for 1 parameter
struct point newPos;
newPos.x = 0.0;
newPos.y = 0.0;
// Needs to interpret
// G1 for moving
// G4 P300 (wait 150ms)
// G1 X60 Y30
// G1 X30 Y50
// M300 S30 (pen down)
// M300 S50 (pen up)
// Discard anything with a (
// Discard any other command!
while( currentIndex < charNB ) {
switch ( line[ currentIndex++ ] ) { // Select command, if any
case 'U':
penUp();
break;
case 'D':
penDown();
break;
case 'G':
buffer[0] = line[ currentIndex++ ]; // /!\ Dirty - Only works with 2 digit commands
//buffer[1] = line[ currentIndex++ ];
//buffer[2] = '\0';
buffer[1] = '\0';
switch ( atoi( buffer ) ){ // Select G command
case 0: // G00 & G01 - Movement or fast movement. Same here
case 1:
// /!\ Dirty - Suppose that X is before Y
char* indexX = strchr( line+currentIndex, 'X' ); // Get X/Y position in the string (if any)
char* indexY = strchr( line+currentIndex, 'Y' );
if ( indexY <= 0 ) {
newPos.x = atof( indexX + 1);
newPos.y = actuatorPos.y;
}
else if ( indexX <= 0 ) {
newPos.y = atof( indexY + 1);
newPos.x = actuatorPos.x;
}
else {
newPos.y = atof( indexY + 1);
indexY = '\0';
newPos.x = atof( indexX + 1);
}
drawLine(newPos.x, newPos.y );
actuatorPos.x = newPos.x;
actuatorPos.y = newPos.y;
break;
}
break;
case 'M':
buffer[0] = line[ currentIndex++ ]; // /!\ Dirty - Only works with 3 digit commands
buffer[1] = line[ currentIndex++ ];
buffer[2] = line[ currentIndex++ ];
buffer[3] = '\0';
switch ( atoi( buffer ) ){
case 300: {
char* indexS = strchr( line+currentIndex, 'S' );
float Spos = atof( indexS + 1);
// Serial.println("ok");
if (Spos == 30) {
penDown();
}
if (Spos == 50) {
penUp();
}
break;
}
case 114: // M114 - Repport position
Serial.print( "Absolute position : X = " );
Serial.print( actuatorPos.x );
Serial.print( " - Y = " );
Serial.println( actuatorPos.y );
break;
default:
Serial.print( "Command not recognized : M");
Serial.println( buffer );
}
}
}
}
/**********************************
* void setup() - Initialisations
**********************************/
void setup() {
// Setup
pinMode(LED, OUTPUT);
pinMode(zeroX,INPUT_PULLUP);
pinMode(zeroY,INPUT_PULLUP);
pinMode(ena,INPUT_PULLUP);
// Decrease if necessary
myStepperX.setSpeed(rpm_X);
myStepperY.setSpeed(rpm_Y);
// Set & move to initial default position
penServo.attach(penServoPin);
penServo.write(penZUp);
Home();
// Notifications!!!
Serial.begin( 9600 );
Serial.println("Paolo's Mini CNC Plotter alive and kicking!");
Serial.print("X range is from ");
Serial.print(Xmin);
Serial.print(" to ");
Serial.print(Xmax);
Serial.println(" mm.");
Serial.print("Y range is from ");
Serial.print(Ymin);
Serial.print(" to ");
Serial.print(Ymax);
Serial.println(" mm.");
}
/**********************
* void loop() - Main loop
***********************/
void loop()
{
delay(100);
char line[ LINE_BUFFER_LENGTH ];
char c;
int lineIndex;
bool lineIsComment, lineSemiColon;
lineIndex = 0;
lineSemiColon = false;
lineIsComment = false;
while (1) {
// Serial reception - Mostly from Grbl, added semicolon support
while ( Serial.available()>0 ) {
c = Serial.read();
if (( c == '\n') || (c == '\r') ) { // End of line reached
if ( lineIndex > 0 ) { // Line is complete. Then execute!
line[ lineIndex ] = '\0'; // Terminate string
#ifdef debug
Serial.print( "Received : ");
Serial.println( line );
#endif
processIncomingLine( line, lineIndex );
lineIndex = 0;
}
else {
// Empty or comment line. Skip block.
}
lineIsComment = false;
lineSemiColon = false;
Serial.println("ok");
}
else {
if ( (lineIsComment) || (lineSemiColon) ) { // Throw away all comment characters
if ( c == ')' ) lineIsComment = false; // End of comment. Resume line.
}
else {
if ( c <= ' ' ) { // Throw away whitepace and control characters
}
else if ( c == '/' ) { // Block delete not supported. Ignore character.
}
else if ( c == '(' ) { // Enable comments flag and ignore all characters until ')' or EOL.
lineIsComment = true;
}
else if ( c == ';' ) {
lineSemiColon = true;
}
else if ( lineIndex >= LINE_BUFFER_LENGTH-1 ) {
Serial.println( "ERROR - lineBuffer overflow" );
lineIsComment = false;
lineSemiColon = false;
}
else if ( c >= 'a' && c <= 'z' ) { // Upcase lowercase
line[ lineIndex++ ] = c-'a'+'A';
}
else {
line[ lineIndex++ ] = c;
}
}
}
}
}
}