Mini Plotter

Hola
He montado un miniplotter con 2 lectores de dvd reciclados + servo y a la hora de imprimir estoy teniendo algun problema... (adjunto foto).
Estoy usando un arduino uno con shield l293d.
He probado a utilizar benbox y no hay forma de que los motores se muevan... en youtube sale pero con otro shield diferente (supongo que el problema vendra por ahi).
Buscando he encontrado otro post (en ingles) de aqui mismo donde a otro forero le pasaba igual... mi ingles es muy malo pero creo que no encontro solucion.
Al no funcionar el benbox, he probado con gctrl y aqui he avanzado algo mas...
Primero he probado con un archivo gcode bajado de internet y al no funcionar, he creado un jpg de un cuadrado de unos 3x3 para comprobar y el fallo es el mismo.
El problema que tengo es que el desplazamiento de los motores es tan pequeño que lo unico que hace es una especie de borron en el papel.
Tengo dos sketch para hacer la prueba de los motores y asi funcionan bien.
Mas abajo adjunto el sketch correcto (bajado de internet) correspondiente pero antes os pongo los datos medidos con el sketch de prueba...
Los pasos totales de un eje son unos 150, los del otro 260
Segun el sketch tengo que calcular el numero de pasos para desplazamiento de un milimetro...
Un eje da 100 pasos y le corresponde 22mm, por lo que da 4,5 pasos para 1 mm
El otro eje da 100 y le corresponde 14mm, por lo que da 7,1 pasos para 1 mm.
El desplazamiento total en un eje es de 33mm y en el otro de 38mm aproximados.
Como os he puesto mas arriba... el problema que tengo es que el desplazamiento que tengo es tan pequeño que lo unico que dibuja es un manchurron de tinta en el papel.

El codigo lo pongo a parte porque excede el limite.

Codigo:

#include <Servo.h>
#include <AFMotor.h>

#define LINE_BUFFER_LENGTH 512

char STEP = MICROSTEP ;

// Servo position for Up and Down 
const int penZUp = 115;
const int penZDown = 83;

// Servo on PWM pin 10
const int penServoPin =10 ;

// Should be right for DVD steppers, but is not too important here
const int stepsPerRevolution = 48; 

// create servo object to control a servo 
Servo penServo;  

// Initialize steppers for X- and Y-axis using this Arduino pins for the L293D H-bridge
AF_Stepper myStepperY(stepsPerRevolution,1);            
AF_Stepper myStepperX(stepsPerRevolution,2);  

/* Structures, global variables    */
struct point { 
  float x; 
  float y; 
  float z; 
};

// Current position of plothead
struct point actuatorPos;

//  Drawing settings, should be OK
float StepInc = 1;
int StepDelay = 0;
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 = 5;
float StepsPerMillimeterY = 5;

// Drawing robot limits, in mm
// OK to start with. Could go up to 50 mm if calibrated well. 
float Xmin = 0;
float Xmax = 33;
float Ymin = 0;
float Ymax = 38;
float Zmin = 0;
float Zmax = 1;

float Xpos = Xmin;
float Ypos = Ymin;
float Zpos = Zmax; 

// Set to true to get debug output.
boolean verbose = false;

//  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 setup() - Initialisations
 ***********************/
void setup() {
  //  Setup
  
  Serial.begin( 9600 );
  
  penServo.attach(penServoPin);
  penServo.write(penZUp);
  delay(100);

  // Decrease if necessary
  myStepperX.setSpeed(600);

  myStepperY.setSpeed(600);  
  

  //  Set & move to initial default position
  // TBD

  //  Notifications!!!
  Serial.println("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
          if (verbose) { 
            Serial.print( "Received : "); 
            Serial.println( line ); 
          }
          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;
          }
        }
      }
    }
  }
}

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 );
        //        Serial.println("ok");
        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 );
      }
    }
  }



}

segunda parte del codigo:

/*********************************
 * Draw a line from (x0;y0) to (x1;y1).
 * int (x1;y1) : Starting coordinates
 * int (x2;y2) : Ending coordinates
 **********************************/
void drawLine(float x1, float y1) {

  if (verbose)
  {
    Serial.print("fx1, fy1: ");
    Serial.print(x1);
    Serial.print(",");
    Serial.print(y1);
    Serial.println("");
  }  

  //  Bring instructions within limits
  if (x1 >= Xmax) { 
    x1 = Xmax; 
  }
  if (x1 <= Xmin) { 
    x1 = Xmin; 
  }
  if (y1 >= Ymax) { 
    y1 = Ymax; 
  }
  if (y1 <= Ymin) { 
    y1 = Ymin; 
  }

  if (verbose)
  {
    Serial.print("Xpos, Ypos: ");
    Serial.print(Xpos);
    Serial.print(",");
    Serial.print(Ypos);
    Serial.println("");
  }

  if (verbose)
  {
    Serial.print("x1, y1: ");
    Serial.print(x1);
    Serial.print(",");
    Serial.print(y1);
    Serial.println("");
  }

  //  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);
      over+=dy;
      if (over>=dx) {
        over-=dx;
        myStepperY.onestep(sy,STEP);
      }
    delay(StepDelay);
    }
  }
  else {
    for (i=0; i<dy; ++i) {
      myStepperY.onestep(sy,STEP);
      over+=dx;
      if (over>=dy) {
        over-=dy;
        myStepperX.onestep(sx,STEP);
      }
      delay(StepDelay);
    }    
  }

  if (verbose)
  {
    Serial.print("dx, dy:");
    Serial.print(dx);
    Serial.print(",");
    Serial.print(dy);
    Serial.println("");
  }

  if (verbose)
  {
    Serial.print("Going to (");
    Serial.print(x0);
    Serial.print(",");
    Serial.print(y0);
    Serial.println(")");
  }

  //  Delay before any next lines are submitted
  delay(LineDelay);
  //  Update the positions
  Xpos = x1;
  Ypos = y1;
}

//  Raises pen
void penUp() { 
  penServo.write(penZUp); 
  delay(penDelay); 
  Zpos=Zmax; 
  digitalWrite(15, LOW);
    digitalWrite(16, HIGH);
  if (verbose) { 
    Serial.println("Pen up!"); 
    
  } 
}
//  Lowers pen
void penDown() { 
  penServo.write(penZDown); 
  delay(penDelay); 
  Zpos=Zmin; 
  digitalWrite(15, HIGH);
    digitalWrite(16, LOW);
  if (verbose) { 
    Serial.println("Pen down."); 
    
    
  } 
}

Hola

He conseguido imprimir algo un poco coherente pero hay ciertos errores que no consigo corregir y no tengo ni idea del motivo.
Adjunto la foto de la impresion y la imagen que segun JViewer deberia de salir... parece que la segunda pasada que hace, la hace descolocada dando la sensacion de ser 3d cuando es 2d.
Comentar que estoy metiendo 7,5V al arduino y no estoy alimentando la shield.

prueba bob esponja.jpg

prueba bob esponja.jpg

Hola
Por favor, alguien podria echarme una mano con este problema?
Ahora tengo mas datos...
He estado haciendo pruebas porque esto me sonaba a tema de posicionamiento de inicio y asi parece ser... si pongo el inicio tanto del eje x como del y en un extremo el error es de solo 2-3 mm en la primera pasada y luego el resto de pasadas las hace correctamente, y si lo posiciono a medias o en el otro extremo se agrava muchisimo mas.
El tema es que el mas correcto de todos esta justo en el tope de los dos ejes...
Creo que el tema esta en esta parte del codigo pero no estoy seguro de sea asi...
Mañana de todas formas probare a hacer alguna modificacion

// Drawing robot limits, in mm
// OK to start with. Could go up to 50 mm if calibrated well. 
float Xmin = 0;
float Xmax = 40;
float Ymin = 0;
float Ymax = 40;
float Zmin = 0;
float Zmax = 1;

float Xpos = Xmin;
float Ypos = Ymin;
float Zpos = Zmax;

Si pusiese Xmin =2, solucionaria algo?
o tendria que modificar los max?

Comentar que estoy metiendo 7,5V al arduino y no estoy alimentando la shield.

Yo empezaria por alimentar adecuadamente los motores y el servo con una fuente externa del voltaje y amperaje adecuado para descartar problemas de perdidas de pasos o que fundas el regulador del arduino.Averigua a que voltaje funcionan esos motores ,porque alomejor son de 12v.Los servos de ese tamaño suelen funcionar a 5-6v.

Muchas gracias por tu respuesta.
El servo se alimenta por el Shield, directamente de arduino, vamos a 5v.
Los motores los estoy alimentando con 12v con alimentador externo aunque solo saca 500Ma....
Podrían venir los tiros por ahí?

Pues los tiros no van por ahi...
Le he puesto un alimentador de 12v y 2,5A y lo único que he conseguido es recalentar al menos uno de los motores... y el error sigue estando...

Se me están acabando las ideas

Viendo los dibujos ,da la sensacion que el servo no eleva la pieza que sujeta el boligrafo o lo que uses para dibujar ,y esta pintando tambien los desplazamientos en vacio.

Si que lo levanta, si...
Las cejas de bob es que las repasa un monton de veces al igual que los pantalones... y por eso da esa sensación al pintar en sitios diferentes.
Ojalá fuera eso.
De todas formas cómo hice un montón de pruebas podría haberme equivocado al fotografiar pero el problema es el comentado... la primera pasada la realiza en distinto sitio al resto...
Y peor aún... no es constante... si imprimes varias veces en cada una de ellas hay diferencias...

Segun el sketch tengo que calcular el numero de pasos para desplazamiento de un milimetro...
Un eje da 100 pasos y le corresponde 22mm, por lo que da 4,5 pasos para 1 mm
El otro eje da 100 y le corresponde 14mm, por lo que da 7,1 pasos para 1 mm.

Esto no es asi,los pasos por milimetro te los da la combinacion de los pasos por vuelta que tenga el motor junto con el paso de la rosca del eje del motor y si los dos ejes x e y tienen el mismo motor ,los pasos por milimetro tendrian que ser iguales en los dos ejes.otra cosa es el recorrido que tengas ,que lo podras calcular posicionando la maquina en el punto x0 y0 ,esto suele hacerse con microinterruptores o finales de carrera .Antes de comenzar el trabajo la maquina hace un homing (se desplaza hasta accionar los finales de carrera de x y ) y queda posicionada en x0 y0 ,entonces con el programa que uses para comunicarte con la maquina ,la mueves manualmente hasta los limites mecanicos ,el programa te ira marcando la posicion ,si por ejemplo el eje x hace tope en la posicion x100 ,pues ese dato lo tienes que poner en el fichero configuration.h:

// @section machine

// The size of the print bed
#define X_BED_SIZE 200
#define Y_BED_SIZE 200

Tambien puedes hacerlo por el firmware hasta dar con los pasos por mm exactos y luego modificar el fichero ,para no estar cargando el firm muchas veces:

Subido todo ya no utilizaremos mas ni el Xloader ni el firmware, a partir de ahora lo único que utilizaremos será el GRBL Controller.

Abrimos el GRBL Controller y le indicamos el puerto del arduino, en baud rate le pondremos 9600 y pulsamos el botón Open, si todo va bien obtendremos una respuesta del arduino con OK.

Para configurar la CNC ponemos en command $$ y pulsamos enter y nos mostrara toda la configuración, lo más importante son:

$0 (pasos por mm del eje X)

$1 (Pasos por mm del eje Y)

$8 (Aceleración por mm) poner a 100

$17 poner a 1

Para modificar cualquier parámetro pondremos $0= valor y pulsamos Enter luego volvemos a poner $$ y enter para ver los cambios reflejados.

El valor de $0 y $1 dependerá tanto de tu motor como de la varilla roscada, por ejemplo los motores de las unidades de CD-DVD son motores de 20 pasos y un giro completo del motor avanza 3,333 mm, otro ejemplo seria si tenemos un motor nema 17 con 200 pasos y una varilla roscada de 1 mm nuestro motor giraría 200 pasos por mm.

esto esta sacado de aqui:
http://servercanary.sytes.net/index.php?option=com_content&view=article&id=62:mini-cnc-casera&catid=8&Itemid=102
Otro problema puede ser el software que uses para generar el gcode.Mucha gente usa la aplicacion
Inkscape junto con plugin para generar gcode.Busca Inkscape plugin gcode en google.

Hola
Gracias, miraré todo lo que me has puesto con paciencia porque para un novato aún tiene miga la cosa.
Los motores no son iguales (son de de diferentes marcas de dvd) aunque lo cierto es que físicamente se parecen.
Para probarlo puedo hacer un sketch en que solo mueva 100 pasos tanto uno como otro y medir desplazamiento?
Una duda que me ha surgido dando vueltas al tema...
Si al montar la impresora no queda totalmente perpendicular un eje con el otro afecta o no a la impresión.
Al montarlo pensé que no pero ya dudo...
Por otro lado la impresora nunca hace un homing, empieza donde se haya quedado y el programa que yo sepa no me marca posiciones.
Intente con algún otro que si que los marcaba por lo que entendí, pero no movían ni un mm los motores.
Tengo el inkscape pero para probarlo use uno ya creado que me baje.
Por cierto, uso processing para enviar a la impresora.

Hombre ,si por ejemplo el eje x esta descuadrado respecto al y ,dificilmente vas a poder dibujar un cuadrado perfecto.En cuanto al movimiento,mas que mover pasos ,mueves una distancia.Lo gcode en este tipo de maquinas funcionan con coordenadas en milimetros,el problema es que tu usas processing y lo desconozco,yo uso pronterface con el cual puedo introducir gcodes manualmente,cargar el fichero resultante de "filetear" un archivo con cura o slic3r,mover la maquina manualmente,etc.Entonces si no tengo finales de carrera ,puedo mover manualmente la maquina al punto que quiera y enviar el comando G92 X0 Y0 para decirle que esta en esas coordenadas.Si no tienes los datos concretos de ese motor (pasos por vuelta y avance del eje) puedes hacer una regla de 3 e ir modificando los pasos por mm.Por ejemplo ,estoy en X0 ,me desplazo 20mm con G0 X20 y mido el desplazamiento,pongamos que se ha desplazado 23mm y yo he puesto en el ajuste de pasos por mm 4,5

Si con 4,5 avanzo 23mm
con x avanzo 20mm que es lo que busco.
23x = 90 -> x = 90/23 = 3,9 pasos ,pruebo con esto y vuelvo a calcular hasta que el desplazamiento sea perfecto

Muchas gracias
Ire haciendo pruebas con lo que me has dicho.
Y supongo que modificaré la impresora para intentar cuadrarla al máximo.
Había oído algo del pronterface y lo intenté pero se me quedaba en conectando... sin llegar a conectar...
Probare con otro pc y con otro arduino.
Es que es curioso, solo he conseguido mover motores con el processing... ni el pronterface ni el benbox mueven un mm los motores.
Supongo que el sketch será el mismo uses un programa u otro, verdad?

El unico truco si tienes grbl cargado en la placa es elegir correctamente el puerto com al que lo tienes conectado y la velocidad en baudios ,que suele ser 250000 por defecto en marlin