4. Achse-Welche Steuerung?

Kaischu

Erfahrener Benutzer
#42
Ach herrje, es sagt,
unrecogized Option: -duser.language=en
Error: could not create the Java virtual machine.
Error: a fatal exception has occurred. Program will exit.
Da soll man doch die Lust verlieren 😤
 

katana

Erfahrener Benutzer
#43
Was hast du genau für ein Windows laufen, win8 win8.1 64 32 bit?
War bei dir mal Java 8 drauf?
Oder ist Java 8 evtl. noch parallel zum Java 7 drauf?
Welche Java 7 Versionen hast du drauf, 32 oder 64 bit?
 

Kaischu

Erfahrener Benutzer
#44
Nein Java ist nur die 7, auf Win 8.1 32bit. Ich werde das tgFX erstmal zurück lassen und etwas abwarten bis die aus dem Beta-Stadium raus sind und später noch einmal testen. Jetzt muss die fräse erstmal laufen, damit ich meine Bestellungen an advensschmuck für die Verwandtschaft fertig bekomme, was ja über chillipeppr ganz gut klappt.
Ich danke dir trotzdem erstmal für deine Hilfe und komme später noch einmal auf das Thema tgFX zurück.
 
#45
Nach kurzer Pause melde ich mich auch mal wieder, da ich mich mal einlesen musste.

Folgende Komponenten werde ich verwenden:
1)Arduino mega2560
2)Protoneer CNC-Shield (20.- inkl.Versand): http://blog.protoneer.co.nz/arduino-cnc-shield/
3)Treiber
4)GRBL-4Achsen: https://github.com/LETARTARE/Grbl-xx_with_Arduino

Wenn man China-Klon Teile verwendet sollte man unter 40.- bleiben. Bin gespannt ob das auch funktioniert das diese Materie volkommen neu für mich ist.

Dies hier finde ich auch interessant:
https://www.marginallyclever.com/bl...d-a-4-axis-cnc-gcode-interpreter-for-arduino/
Hier wird ein Adafruit Motorshield verwendet. Pro Motorshield können 2 Schrittmotoren angeschlossen, die Shield´s werden türmchenhaft übereinandergestapelt und können nun bis zu 32 Schrittmotoren ansteuern.

Ne andere Frage: Welche max. Baugröße von Motoren kann ich verwenden? Ist wohl vom Treiber abhängig? Ich habe dabei immer die dünnen Beinchen auf der Platine im Kopf.

Grüße Erwin
 
#46
Das Protoneer Shield verträgt mit den Treibern NEMA17 Motoren (die für die 3D Drucker), diese kannst mit den 2A aber nicht ganz auslasten. Reicht aber bei mir auf der Eigenbau Fräse und für den Styroschneider sicher auch.

Mega udn n bestücktes Protoneer Shield hab ich noch hier liegen, da könnte ich mal den 4-achs grbl drauf versuchen...wenn da nicht so viele andere Projekte wären.

Der 4-achs Code fürs Adafruid shield is nur bedingt interessant da dort die Motoren ganz anders angesteuert werden.
 

tjwan

Neuer Benutzer
#47
Hallo,

ich klinke mich auch mal hier ein. Ich habe mir auch eine 4-Achs-Mechanik gebaut mit dem Ziel, damit Tragflächen zu schneiden. Als Anregung hatte ich den ct-Artikel von Ralf Stoffels (surasto.de), der die Mechanik aus der Restekiste gebaut hat und sich die Steuerung und Programmierung komplett selbst entwickelt hat.
Meine Mechanik aus MDF-Platten, M6-Gewindestangen und Nema17-Motoren ist etwas wuchtig geworden, aber wird wohl funktionieren. Bauplan hatte ich irgendwo bei Flitetest gesehen.

PCHotwire von surasto.de arbeitet zwar nicht mit G-Code, kann aber dafür die üblichen Profildaten direkt verarbeiten.
Als Steuerung habe ich auch einen Arduino-UNO mit CNC-Shield 3.0, der auch 4 Achsen ansteuern kann und glaube ich unschlagbar günstig ist. GRBL wäre da eigentlich ideal, kann aber nur 3 Achsen. Den Fork von zapmaker hatte ich auch gefunden, hatte aber den Eindruck, dass der einen Mega2560 benötigt. Auf den passt wieder der Shield nicht direkt drauf,
d.h. man kann den Shield nicht auf die Arduino-Platine draufstecken, sondern muss die Pins manuell verbinden :-(.
Hat jemand diesen Fork mal ausprobiert oder möglicherweise sogar herausgefunden, dass der auch mit dem UNO funktioniert ?

Ich habe jetzt erstmal einen anderen Weg eingeschlagen:
Ich passe PCHotwire und den dazugehörigen Arduino-Treiber gerade an, da die Ansteuerung der Schrittmotoren mit den A4988 Treibern etwas anders erfolgt, als bei dem Originalentwurf.
An der Stelle könnte man natürlich aus dem in processing geschriebenen PCHotwire auch einen Controller mit G-Code ansteuern. Die Profile werden bereits in Vektoren zerlegt, die dann von dem Arduino-Controller abgearbeitet werden.
Da stecken bereits eine Menge meiner Ansicht nach sehr tragflächenspezifische Überlegungen drin.
http://www.heise.de/ct/artikel/CNC-Tragflaechenschneidemaschine-fuer-Modellflugzeuge-1519373.html

Aktuell habe ich noch etwas Optimierungsbedarf mit der Geschindigkeit, da ich mit meinen 0815 M6-Gewindestangen 200 Pulse je mm benötige, aber erste Tests mit Filzstift und Papier waren schon erfolgreich. Joggen auf allen 4 Achsen geht auch schon.

PCHotwire sollte man übrigens nicht aus dem ct-Archiv herunterladen, sondern direkt vom Autor von www.surasto.de. Die SW wurde hier noch weitergepflegt.

Gruß Tjwan
 
#48
Hallo,

das habe ich befürchtet das das cnc-shield nicht auf den mega passt (habe aber schon bestellt).
Die 4Achsen-Letartare-GRBL-Version benötigt aber soweit mir bekannt einen Mega.(?)

In wenigen Wochen wenn mal alle Teile das sind kann ich weiteres berichten.

Grüße Erwin
 

tjwan

Neuer Benutzer
#49
Hallo,

1. Das CNC-Shield passt von der Pinbelegung nicht auf den Mega, aber man kann die Pins wohl verdrahten. Ist vom Aufbau dann nicht optimal aber immer noch möglich.

2. Ich hatte in die Sourcen von Letartare mal reingesehen und da gab es zumindest ein paar Swiches für den UNO.
Aber möglicherweise sind das auch nur "Reste" aus dem originalen GRBL Code.
Man müsste mal mit dem Autor in Kontakt treten.
Die A4988 funktionieren ja grundsätzlich super und sind sehr günstig. Ich hatte auch schon nach einem anderen Shield gesucht, das denselben Zweck erfüllt wie das CNC Shield für den UNO, nämlich einfach nur die PINS auf die 4 A4988 führen.
Kann man sich natürlich notfalls auch mit einem Breadboard aufbauen.
Meine Versuche mit PCHotwire machen Fortschritte, aber an dem GRBL-Thema bin ich immer noch interessiert.

Gruß Tjwan
 
#50
Ich hatte auch vor langer Zeit mal angefangen die Software der CT auf Accelstepper lib umzuschreiben um sie fürs A4988 Shield anzupassen.
Aber ne GCode Lösung hat mir doch besser gefallen, hatte dann mal angefangen die 4-Achs CNC Demo von Adafruit Motoshield nach A4988 umzubauen, allerdings mit PIN-Defines für n 3D Printer Board.

Da der Styroschneider momentan hinten ansteht geht aber nicht weiter damit. Wenn ihr wollt kann ich die Beiden angefangenen Sketches mal hier reinstellen, is aber nix fertig und nix getestet. Evtl hilfts als Anregung.

Persönlich würde ich mittlerweile wohl eher zur GRBL Variante tendieren, ich mag GRBL einfach - kommt jetzt auch wieder auf den größeren Lasergravierer.
 

tjwan

Neuer Benutzer
#51
Hallo,

mein PCHotwire-Aufbau läuft jetzt, aber die Implementation der Übertragung der Daten vom PC zum Arduino sorgt leider noch für leichtes ruckeln, was sich leider auch in den Oberflächen beim Schneiden niederschlägt. Versuche noch, das durch die Leistung auf dem Draht und die Geschwindigkeit hinzubekommen und / oder durch Anpassung der SW. Grundsätzlich funktionert das nämlich mit UNO / CNC-Shield und den 4 Achsen und die Ergebnisse waren immerhin besser als neulich ganz klassisch mit Schneiderippen.

Ich hätte auf jeden Fall Interesse an den Sketches, da ich aktuell auch absolut Low-Level unterwegs bin.

Gruß Tjwan.
 
#52
OK, hier schon mal der Code für den CT Styroschneider, ich hatte versucht es auf die Arduino AccelStepper Lib und Pinbelebung vom GRBL Shield umzubauen. Die meisten Änderungen sind in der oneStep Funktion.
Wie gesagt aber nur mal aus dem Kopf raus geschrieben, nie ausprobiert.
Vieleicht hilft es dir ja.

Code:
// ArduinoCNC
// Styroporschneidemaschine über serielle Schnittstelle gesteuert 
// Version: 1.3
// Datum: 5-Jan-12 
// Versionskommentar: Standardfunktion für Datenempfang eingeführt
// -----------------------------------------------------------------------------------------
// Hinweis: Arduino IDE muss auf Board "Diecimila" eingestellt sein
// -----------------------------------------------------------------------------------------
// Befehle, die über die Serielle Schnittstelle empfangen werden:
//  '1' - Rechts Y-Achse nach oben
//  '2' - Rechts Y-Achse nach unten
//  '3' - Rechts X-Achse nach vorne
//  '4' - Rechts X-Achse nach hinten
//  '5' - Links Y-Achse nach oben
//  '6' - Links Y-Achse nach unten
//  '7' - Links X-Achse nach vorne
//  '8' - Links X-Achse nach hinten
//  '0' - Stoppt die Bewegung aller Achsen
//  'S' - 
//  "<DIR><hex>" - bewegt beide Seiten um <hex> Schritte
//                 <DIR> = 'U'  Aufwärts
//                 <DIR> = 'D'  Abwärts
//                 <DIR> = 'F'  Vorwärts
//                 <DIR> = 'B'  Rückwärts
//                 Beispiel: F00F3
//  "<sign><StepHex><sign><XaspectHex><sign>YaspectHex>"
//                 - Übergibt einen Vector an den Arduino. Der Arduino weiß, ob für die rechte
//                   oder linke Seite, weil er den Vector vorher entsprechend angefragt hat. 
//                   Die drei Vorzeichen geben die Richtung an.
//                   Die drei Werte sind unsigned Hex (4 Characters).
//                   StepHex - Anzahl der Schritte in X-Richtung
//                   XAspectHex - Alle wieviel Clocks wird ein X-SPINDLE_ENABLEschritt gemacht.
//                   YAspectHex - Alle wieviel Clocks wird ein Y-SPINDLE_ENABLEschritt gemacht.
//                   Beispiel: +024E+0041-003D
// "#----#----#----" - Keine weiteren Vectoren mehr d.h. Arduino hört auf danach zu fragen.
//
// Befehle, die gesendet werden:
// 'L' - Arduino fordert nächsten Vector für linke Seite an
// 'R' - Arduino fordert nächsten Vector für rechte Seite an
// 'A' - Rechter Endschalter in X-Richtung erreicht
// 'B' - Linker Endschalter in X-Richtung erreicht
// 'a' - Rechter Endschalter in Y-Richtung erreicht
// 'b' - Linker Endschalter in Y-Richtung erreicht

#include <AccelStepper.h>

#define ENABLE 8 // grbl=8
#define SPINDLE_ENABLE 11 // grbl=12

#define X_STEP 2 // grbl=2
#define Y_STEP 3 // grbl=3
#define Z_STEP 4 // grbl=4
#define A_STEP 12 // grbl spindle enable  

#define X_DIR 5 // grbl=5
#define Y_DIR 6 // grbl=6
#define Z_DIR 7 // grbl=7
#define A_DIR 13  // grbl spindle dir


#define LIMIT_X 9 // grbl=9
#define LIMIT_Y 10 // grbl=10
#define LIMIT_Z A4 // grbl=11
#define LIMIT_A A5 

AccelStepper stepperX(AccelStepper::DRIVER, X_STEP, X_DIR);
AccelStepper stepperY(AccelStepper::DRIVER, Y_STEP, Y_DIR);
AccelStepper stepperZ(AccelStepper::DRIVER, Z_STEP, Z_DIR);
AccelStepper stepperA(AccelStepper::DRIVER, A_STEP, A_DIR);

int xrDir=0, xlDir=0, yrDir=0, ylDir=0;
boolean bitXR0, bitXR1, bitYR0, bitYR1;
boolean bitXL0, bitXL1, bitYL0, bitYL1;
int inByte='0';
int heatSetting=0;
boolean cuttingInProgress = false;
int aspXL, aspXR, aspYL, aspYR;   // Anzahl der Clockticks, die bis zum nächsten Schritt jeweils übersprungen werden 
int countLeft=0, countRight=0;  // Anzahl der X-Schritte auf der jeweiligen Seite des aktuellen Vektors
unsigned long masterClock;
unsigned long clockXL, clockXR, clockYL, clockYR;  // Schrittzähler für die jeweiligen Achsen
int xrSign, xlSign, yrSign, ylSign;  // Richtungen (werden von requestVector erzeugt)
boolean validVectorL, validVectorR;    // Erkennung des Endes der Vector-Daten



// ============= Initialisierung ==================================
void setup() {                
  // Output-Pins initialisieren
  // X-Links
  pinMode(X_DIR, OUTPUT);   
  pinMode(X_STEP, OUTPUT); 
  
  // Y-Links
  pinMode(Y_DIR, OUTPUT);   
  pinMode(Y_STEP, OUTPUT); 
  
  // X-Rechts
  pinMode(Z_DIR, OUTPUT);   
  pinMode(Z_STEP, OUTPUT); 
  
  // Y-Rechts
  pinMode(A_DIR, OUTPUT);   
  pinMode(A_STEP, OUTPUT); 
  
  // Heizung für den Draht (PWM Output)
  pinMode(SPINDLE_ENABLE, OUTPUT);
  analogWrite(SPINDLE_ENABLE,0);   // Heizung aus
  
  //Initialisiere digitale Inputs für Endschalter
  pinMode(LIMIT_X, INPUT);   // X-Rechts
  pinMode(LIMIT_Y, INPUT);   // Y-Rechts
  pinMode(LIMIT_Z, INPUT);   // X-Links
  pinMode(LIMIT_A, INPUT);   // Y-Links
  
  // serielle Schnittstelle initalisieren
  Serial.begin(9600);
}


// ====================== Hauptschleife ==========================
void loop() {

  if (cuttingInProgress) {  // Maschine im Schneidemodus - Vectoren werden angefordert

     if (countLeft==0) validVectorL = requestVector('L');
     if (countRight==0) validVectorR = requestVector('R');
     
     if (validVectorL || validVectorR) {     // Erst wenn beide Seiten keine validen Vectoren mehr haben, wird beendet
        xlDir=0; xrDir=0; ylDir=0; yrDir=0;
        if (masterClock>clockXL) {
            xlDir=xlSign; 
            clockXL+=aspXL;   // das ist die zentrale Stelle, die sicherstellt, dass alle <aspXL> Durchläufe
                              // ein Schritt ausgeführt wird. Da hier die Richtung keine Rolle spielt,  
                              // wird einfach immer weitergezählt.
            countLeft--;      // Es werden nur die Schritte in X-Richtung heruntergezählt, da sich die in Y-Richtung daraus ergeben.
        }         
        if (masterClock>clockXR) { // analog für die anderen Achsen
            xrDir=xrSign; 
            clockXR+=aspXR; 
            countRight--;
        }         
        if (masterClock>clockYL) {
            ylDir=ylSign; 
            clockYL+=aspYL; 
        }         
        if (masterClock>clockYR) {
            yrDir=yrSign; 
            clockYR+=aspYR; 
        }         
     
        oneStep(xrDir, yrDir, xlDir, ylDir);
        masterClock++;
        delayMicroseconds(500);
        
     } else {
        cuttingInProgress=false;   // wenn keine gültiger Vector mehr vorhanden, in manuelle Arbeitsweise umschalten
        xlDir=0; xrDir=0; ylDir=0; yrDir=0;
        delay(1500);
        analogWrite(3,0); // Heizung ausschalten
     } 
  } 
  else
  {   // Maschine im manuellen Modus - Tasteneingaben abfragen.     
    if (Serial.available() > 0) {
       inByte = Serial.read();  // Byte lesen
       if (inByte=='1') yrDir=-1; else 
       if (inByte=='2') yrDir=1; else
       if (inByte=='3') xrDir=-1; else 
       if (inByte=='4') xrDir=1; else     
       if (inByte=='5') ylDir=-1; else 
       if (inByte=='6') ylDir=1; else     
       if (inByte=='7') xlDir=-1; else 
       if (inByte=='8') xlDir=1; else
       if (inByte=='0') {yrDir=0; xrDir=0; xlDir=0; ylDir=0; analogWrite(3,0);}
       if (inByte=='H') readSetHeat();
       if ((inByte=='D')||(inByte=='U')||(inByte=='B')||(inByte=='F')) readMove(inByte);
       if (inByte=='S') {
           cuttingInProgress = true;   // schalte die Maschine in den Schneidemodus
           validVectorL = true;
           validVectorR = true;
           countLeft=0;    // stellt sicher, dass beim Umschalten in den Schneidemodus alle Zähler auf Null stehen
           countRight=0;   // und der erste Vector angefordert wird
           clockXL=0; clockXR=0; clockYL=0; clockYR=0;
           masterClock=0;
       }
     }  
     oneStep(xrDir, yrDir, xlDir, ylDir);
     if (digitalRead(LIMIT_X)==LOW && xrDir==1) {xrDir=0; Serial.print('A'); delay(20);} 
     if (digitalRead(LIMIT_Y)==LOW && yrDir==1) {yrDir=0; Serial.print('a'); delay(20);}    
     if (digitalRead(LIMIT_Z)==LOW && xlDir==1) {xlDir=0; Serial.print('B'); delay(20);}    
     if (digitalRead(LIMIT_A)==LOW && ylDir==1) {ylDir=0; Serial.print('b'); delay(20);}    
     delay(10);              
   }   
}

// Wert für den eingestellten Heizstrom lesen und PWM entsprechend programmieren
// Format auf der seriellen Schnittstelle: Hxx mit xx = 2-Byte Hex-Zahl 
void readSetHeat() {
    int hexNum;
  
    heatSetting = readNumSerial(2);   // 2 Byte lesen
    analogWrite(SPINDLE_ENABLE,heatSetting);
}    

// Rechte und linke Seite um <steps> Schritte verfahren
// dir: U = Up, D = Down, B = Back, F = Forward
void readMove(char dir) {
  int i, steps, hexNum, digit;
  
  steps = readNumSerial(4);
  
  for (i=0; i<steps; i++) {
    if (dir=='U') oneStep(0,-1,0,-1);
    if (dir=='D') oneStep(0,1,0,1);
    if (dir=='B') oneStep(1,0,1,0);
    if (dir=='F') oneStep(-1,0,-1,0);
    delay(10);
  }
}  
    
boolean requestVector(char side) {
   char stpSign, aspXSign, aspYSign;
   int i;
   int hexNum, steps=0, aspX=0, aspY=0, digit;
   boolean exitVal;
   String text;
   
   Serial.print(side);    // sende request
   
   while (Serial.available() == 0) {}   // lese Vorzeichen der Schritte
   stpSign=Serial.read();               
   
   exitVal=true;
   if (stpSign=='#') exitVal=false;    // letzter Vector gelesen - beenden;
   
   steps = readNumSerial(4); 

   while (Serial.available() == 0) {}   // lese Vorzeichen von aspX
   aspXSign=Serial.read();               

   aspX = readNumSerial(4); 

   while (Serial.available() == 0) {}   // lese Vorzeichen von aspY
   aspYSign=Serial.read();               

   aspY = readNumSerial(4); 
         
   if (side=='L') {
     if (aspXSign=='+') xlSign=-1; else xlSign=1;
     if (aspYSign=='+') ylSign=-1; else ylSign=1;
     aspXL = aspX;
     aspYL = aspY;
     countLeft = steps;
   }  
   if (side=='R') {
     if (aspXSign=='+') xrSign=-1; else xrSign=1;
     if (aspYSign=='+') yrSign=-1; else yrSign=1;
     aspXR = aspX;
     aspYR = aspY;
     countRight = steps;
   }  
   
   return(exitVal);
}

// Liest eine Hex-Zahl von der seriellen Schnittstelle.
// numberofBytes gibt die Anzahl der Hex-Characters an, die erwartet werden.
// Der Return-Wert der Funktion enthält die gelesene Zahl. 
long readNumSerial(int numberOfBytes) {
  int i, digit, hexNum, returnValue;
  
  digit=1; returnValue=0;
  for (i=0; i<numberOfBytes-1; i++) digit*=16;
  for (i=0; i<numberOfBytes; i++) {     
     while (Serial.available() == 0) {}
     hexNum = unHex(Serial.read());
     returnValue += digit*hexNum;
     digit=digit/16;
  }   
  return(returnValue);
}  

// Verwandelt eine Hex-Ziffer in eine Dezimalzahl 
int unHex(char hexChar) {
  int (outByte);
  
  if (hexChar>='A') outByte=hexChar-'A'+10; else outByte=hexChar-'0';
  return(outByte);
}

// Führt einen Schritt auf einer oder mehreren Achsen aus
// -1 rückwärts
// 1  vorwärts
// 0  kein Schritt
void oneStep(int xr, int yr, int xl, int yl) {
//  boolean b0xr, b0yr, b0xl, b0yl;
//
//  b0xr=bitXR0;   // alten Zustand zwischenspeichern
//  b0yr=bitYR0;
//  b0xl=bitXL0;
//  b0yl=bitYL0;
//
//  if (xr==1) {bitXR0 = !bitXR1; bitXR1 = b0xr;};
//  if (xr==-1) {bitXR0 = bitXR1; bitXR1 = !b0xr;};
//  if (xl==1) {bitXL0 = !bitXL1; bitXL1 = b0xl;};
//  if (xl==-1) {bitXL0 = bitXL1; bitXL1 = !b0xl;};
//  if (yr==1) {bitYR0 = !bitYR1; bitYR1 = b0yr;}; 
//  if (yr==-1) {bitYR0 = bitYR1; bitYR1 = !b0yr;};
//  if (yl==1) {bitYL0 = !bitYL1; bitYL1 = b0yl;};
//  if (yl==-1) {bitYL0 = bitYL1; bitYL1 = !b0yl;};
//  
//  digitalWrite(8, bitXL1);   
//  digitalWrite(9, bitXL0);   
//  digitalWrite(10, bitYL0);    // Hier sind die Bits auf der Platine vertauscht 
//  digitalWrite(11, bitYL1);   
//  digitalWrite(A0, bitXR1);   
//  digitalWrite(A1, bitXR0);
//  digitalWrite(A2, bitYR1);   
//  digitalWrite(A3, bitYR0);

    stepper1.move(xr);
    stepper2.move(yr);
    stepper3.move(xl);
    stepper4.move(yl);
            
    stepper1.run();
    stepper2.run();
    stepper3.run();
    stepper4.run();
    
}
 
#53
Und hier noch der Code aufbauend auf dem 4-Achs Demo von http://www.github.com/MarginallyClever/GcodeCNCDemo, aber angefangen es fürs Melzi-Board mit A4982 Treibern umzubauen.
http://www.banggood.com/3D-Printer-Reprap-Melzi-Ardentissimo-Control-Board-Motherboard-p-917889.html
Auch nur mal runtergeschrieben und nix getestet.

Code:
//------------------------------------------------------------------------------
// 4 Axis CNC Demo v2 - supports Adafruit motor shield v2
// [email protected] 2013-08-30
//------------------------------------------------------------------------------
// Copyright at end of file.
// please see http://www.github.com/MarginallyClever/GcodeCNCDemo for more information.


//------------------------------------------------------------------------------
// CONSTANTS
//------------------------------------------------------------------------------
//#define VERBOSE (1) // add to get a lot more serial output.

#define VERSION (2) // firmware version
#define BAUD (57600) // How fast is the Arduino talking?
#define MAX_BUF (64) // What is the longest message Arduino can store?
//#define STEPS_PER_TURN (200) // depends on your stepper motor. most are 200.
#define MIN_STEP_DELAY (50)
#define MAX_FEEDRATE (1000000/MIN_STEP_DELAY)
#define MIN_FEEDRATE (0.01)
#define NUM_AXIES (4)

//------------------------------------------------------------------------------
// PINOUT
//------------------------------------------------------------------------------
#define X_STEP_PIN         15
#define X_DIR_PIN          21
#define X_MIN_PIN          18
#define X_ENABLE_PIN       14
#define X_STEPSPERMM       1

#define Y_STEP_PIN         22
#define Y_DIR_PIN          23
#define Y_MIN_PIN          19
#define Y_ENABLE_PIN       14
#define Y_STEPSPERMM       1

#define Z_STEP_PIN         3
#define Z_DIR_PIN          2
#define Z_MIN_PIN          20
#define Z_ENABLE_PIN       26
#define Z_STEPSPERMM       1

#define E_STEP_PIN         1
#define E_DIR_PIN          0
#define E_ENABLE_PIN       14
#define E_STEPSPERMM       1

#define LED_PIN            27

#define FAN_PIN            4 
#define HEATER_PIN         13 // extruder
#define HEATER_BED_PIN     10 // bed (change to 12 for breakout pin on header)

#define TEMP_0_PIN         A7   // Analogue pin
#define TEMP_BED_PIN       A6   // Analogue pin
#define SDSS               31

#define SLAVE_CLOCK	   16

//------------------------------------------------------------------------------
// INCLUDES
//------------------------------------------------------------------------------
//#include <AccelStepper.h>

//------------------------------------------------------------------------------
// STRUCTS
//------------------------------------------------------------------------------
// for line()
typedef struct {
  long deltaSteps;
  long absdeltaSteps;
  int dir;
  long overSteps;
} Axis;


//------------------------------------------------------------------------------
// GLOBALS
//------------------------------------------------------------------------------
Axis a[4]; // for line()
Axis atemp; // for line()

char buffer[MAX_BUF]; // where we store the message until we get a ';'
int sofar; // how much is in the buffer

float px, py, pz, pe; // location in mm

// speeds
float fr=0; // Feedrate human version
long step_delay; // Feedrate machine version

// settings
char mode_abs=1; // absolute mode?


//------------------------------------------------------------------------------
// METHODS
//------------------------------------------------------------------------------


/**
* delay for the appropriate number of microseconds
* @input ms how many milliseconds to wait
*/
void pause(long ms) {
  delay(ms/1000);
  delayMicroseconds(ms%1000); // delayMicroseconds doesn't work for values > ~16k.
}


/**
* Set the feedrate (speed motors will move)
* @input nfr the new speed in steps/second
*/
void feedrate(float nfr) {
  if(fr==nfr) return; // same as last time? quit now.

  if(nfr>MAX_FEEDRATE || nfr<MIN_FEEDRATE) { // don't allow crazy feed rates
    Serial.print(F("New feedrate must be greater than "));
    Serial.print(MIN_FEEDRATE);
    Serial.print(F("steps/s and less than "));
    Serial.print(MAX_FEEDRATE);
    Serial.println(F("steps/s."));
    return;
  }
  step_delay = 1000000.0/nfr;
  fr=nfr;
}


/**
* Set the logical position
* @input npx new position x
* @input npy new position y
*/
void position(float npx,float npy,float npz,float npe) {
  // here is a good place to add sanity tests
  px=npx;
  py=npy;
  pz=npz;
  pe=npe;
}


void onstep(int motor,int direction) {
#ifdef VERBOSE
  char *letter="XYZE";
  Serial.print(letter[motor]);
#endif
  switch (motor) {
    case 0:
      digitalWrite(X_DIR_PIN, direction);
      digitalWrite(X_STEP_PIN, HIGH);
      break;
    case 1:
      digitalWrite(Y_DIR_PIN, direction);
      digitalWrite(Y_STEP_PIN, HIGH);
      break;
    case 2:
      digitalWrite(Z_DIR_PIN, direction);
      digitalWrite(Z_STEP_PIN, HIGH);
      break;
    case 3:
      digitalWrite(E_DIR_PIN, direction);
      digitalWrite(E_STEP_PIN, HIGH);
      break;
  }
}

void offStep() {
  digitalWrite(X_STEP_PIN, LOW);
  digitalWrite(Y_STEP_PIN, LOW);
  digitalWrite(Z_STEP_PIN, LOW);
  digitalWrite(E_STEP_PIN, LOW); 
}


void release() {
  digitalWrite(X_ENABLE_PIN, LOW);
  digitalWrite(Y_ENABLE_PIN, LOW);
  digitalWrite(Z_ENABLE_PIN, LOW);
  digitalWrite(E_ENABLE_PIN, LOW);
}


/**
* Uses bresenham's line algorithm to move both motors
* @input newx the destination x position
* @input newy the destination y position
**/
void line(float newx,float newy,float newz,float newe) {
  a[0].deltaSteps = (newx-px) * X_STEPSPERMM;
  a[1].deltaSteps = (newy-py) * Y_STEPSPERMM;
  a[2].deltaSteps = (newz-pz) * Z_STEPSPERMM;
  a[3].deltaSteps = (newe-pe) * E_STEPSPERMM;
  
  long i,j,maxsteps=0;

  // run over axis
  for(i=0;i<NUM_AXIES;++i) {
    // pos delta
    a[i].absdeltaSteps = abs(a[i].deltaSteps);
    // direction
    a[i].dir = a[i].deltaSteps > 0 ? 1:0;
    // find axis with most steps to do
    if( maxsteps < a[i].absdeltaSteps ) 
      maxsteps = a[i].absdeltaSteps;
    // reset over
    a[i].overSteps=0;
  }

#ifdef VERBOSE
  Serial.println(F("Start >"));
#endif
  
  // for maxsteps
  for( i=0; i<maxsteps; ++i ) {
    // for each Axis
    for(j=0;j<NUM_AXIES;++j) {
      a[j].overSteps += a[j].absdeltaSteps;
      if(a[j].overSteps >= maxsteps) {
        a[j].overSteps -= maxsteps;
        // ENABLE STEP for MOTOR HERE
        onstep(j,a[j].dir);
      }
    }
    delayMicroseconds(50); 
    // disable all Steps
    offStep();
    pause(step_delay);
  }

#ifdef VERBOSE
  Serial.println(F("< Done."));
#endif

  position(newx,newy,newz,newe);
}


/**
* Look for character /code/ in the buffer and read the float that immediately follows it.
* @return the value found. If nothing is found, /val/ is returned.
* @input code the character to look for.
* @input val the return value if /code/ is not found.
**/
float parsenumber(char code,float val) {
  char *ptr=buffer;
  while(ptr && *ptr && ptr<buffer+sofar) {
    if(*ptr==code) {
      return atof(ptr+1);
    }
    ptr=strchr(ptr,' ')+1;
  }
  return val;
}


/**
* write a string followed by a float to the serial line. Convenient for debugging.
* @input code the string.
* @input val the float.
*/
void output(char *code,float val) {
  Serial.print(code);
  Serial.println(val);
}


/**
* print the current position, feedrate, and absolute mode.
*/
void where() {
  output("X",px);
  output("Y",py);
  output("Z",pz);
  output("E",pe);
  output("F",fr);
  Serial.println(mode_abs?"ABS":"REL");
}


/**
* display helpful information
*/
void help() {
  Serial.print(F("GcodeCNCDemo4AxisV2 "));
  Serial.println(VERSION);
  Serial.println(F("Commands:"));
  Serial.println(F("G00/G01 [X(mm)] [Y(mm)] [Z(mm)] [E(mm)] [F(feedrate)]; - linear move"));
  Serial.println(F("G04 P[seconds]; - delay"));
  Serial.println(F("G90; - absolute mode"));
  Serial.println(F("G91; - relative mode"));
  Serial.println(F("G92 [X(mm)] [Y(mm)] [Zmm] [E(mm)]; - change logical position"));
  Serial.println(F("M18; - disable motors"));
  Serial.println(F("M100; - this help message"));
  Serial.println(F("M114; - report position and feedrate"));
}


/**
* Read the input buffer and find any recognized commands. One G or M command per line.
*/
void processCommand() {
  int cmd = parsenumber('G',-1);
  switch(cmd) {
  case 0: // move linear
  case 1: // move linear
    feedrate(parsenumber('F',fr));
    line( parsenumber('X',(mode_abs?px:0)) + (mode_abs?0:px),
          parsenumber('Y',(mode_abs?py:0)) + (mode_abs?0:py),
          parsenumber('Z',(mode_abs?pz:0)) + (mode_abs?0:pz),
          parsenumber('E',(mode_abs?pe:0)) + (mode_abs?0:pe) );
    break;
  case 4: pause(parsenumber('P',0)*1000); break; // dwell
  case 90: mode_abs=1; break; // absolute mode
  case 91: mode_abs=0; break; // relative mode
  case 92: // set logical position
    position( parsenumber('X',0),
              parsenumber('Y',0),
              parsenumber('Z',0),
              parsenumber('E',0) );
    break;
  default: break;
  }

  cmd = parsenumber('M',-1);
  switch(cmd) {
  case 18: // disable motors
    release();
    break;
  case 100: help(); break;
  case 114: where(); break;
  default: break;
  }
}


/**
* prepares the input buffer to receive a new message and tells the serial connected device it is ready for more.
*/
void ready() {
  sofar=0; // clear input buffer
  Serial.print(F(">")); // signal ready to receive input
}


/**
* First thing this machine does on startup. Runs only once.
*/
void setup() {
  Serial.begin(BAUD); // open coms
  
  pinMode(X_STEP_PIN, OUTPUT);
  pinMode(X_DIR_PIN, OUTPUT);
  pinMode(X_ENABLE_PIN, OUTPUT);
  pinMode(X_MIN_PIN, INPUT);
  
  pinMode(Y_STEP_PIN, OUTPUT);
  pinMode(Y_DIR_PIN, OUTPUT);
  pinMode(Y_ENABLE_PIN, OUTPUT);
  pinMode(Y_MIN_PIN, INPUT);
  
  pinMode(Z_STEP_PIN, OUTPUT);
  pinMode(Z_DIR_PIN, OUTPUT);
  pinMode(Z_ENABLE_PIN, OUTPUT);
  pinMode(Z_MIN_PIN, INPUT);
  
  pinMode(E_STEP_PIN, OUTPUT);
  pinMode(E_DIR_PIN, OUTPUT);
  pinMode(E_ENABLE_PIN, OUTPUT);
  
  digitalWrite(X_ENABLE_PIN, HIGH);
  digitalWrite(Y_ENABLE_PIN, HIGH);
  digitalWrite(Z_ENABLE_PIN, HIGH);
  digitalWrite(E_ENABLE_PIN, HIGH);

  help(); // say hello
  position(0,0,0,0); // set staring position
  feedrate(200); // set default speed
  ready();
}


/**
* After setup() this machine will repeat loop() forever.
*/
void loop() {
  // listen for serial commands
  while(Serial.available() > 0) { // if something is available
    char c=Serial.read(); // get it
    Serial.print(c); // repeat it back so I know you got the message
    if(sofar<MAX_BUF) buffer[sofar++]=c; // store it
    if(buffer[sofar-1]==';') break; // entire message received
  }

  if(sofar>0 && buffer[sofar-1]==';') {
    // we got a message and it ends with a semicolon
    buffer[sofar]=0; // end the buffer so string functions work right
    Serial.print(F("\r\n")); // echo a return character for humans
    processCommand(); // do something with the command
    ready();
  }
}


/**
* This file is part of GcodeCNCDemo.
*
* GcodeCNCDemo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GcodeCNCDemo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Foobar. If not, see <http://www.gnu.org/licenses/>.
*/
 

tjwan

Neuer Benutzer
#54
Hallo,

vielen Dank schonmal für den Code.
PCHotwire hatte ich selbst auch schon an den CNC-Shield angepasst, allerdings ohne Stepper-Library sondern mit direkter Ansteuerung der PINs. Kann ich bei Interesse auch gerne hier posten. Habe gestern ein paar Probeschnitte gemacht und das klappt auch grundsätzlich schon ganz gut. Allerdings zuckelt es leider etwas und das liegt denke ich am Aufbau von PCHotwire und der sequentiellen Übertragung der Vektoren. Man glaubt gar nicht, wie langsam so eine serielle Kommunikaton sein kann. Ich habe auch nach ein paar Optimierungen immer noch Pausen von 5-15 ms und die sind erstaunlicherweise noch direkt erkennen.

Weil sich ja beim Tragflächenschneiden ein heisser Draht durchs Material frisst ist es wichtig, dass das möglichst kontinurierlich erfolgt. Anders als vielleicht bei einem Fräser, gibt es ein Problem, wenn der Draht "mal kurz stehen bleibt". Dann gibt es nämlich an der Stelle einen stärkeren Abbrand und eine Delle. Meine Versuche die Geschwindigkeit zu reduzieren und die Heizleistung gleichzeitig zu verringern, waren bisher auch noch nicht so erfolgreich, da der Durchhang des Schneide-Drahtes in der Mitte dann zu gross wurde.
Beim Tragfächenschneiden hat man ja meist eine sequentielle Folge von vielen sehr kurzen Vektoren (bei meinen Zielprofilen 170 Vektoren für das ganze Profil bei ~130 mm Tragflächentiefe).
Bei PCHotwire und auch bei den den beiden Ansätzen vom Frickler erfolgen die Ansteuerung der Stepper und das Abholen neuer Kommandos über die serielle Schnittstelle immer sequentiell und nicht überlappt, so dass es auch dabei immer sehr kleine Unterbrechungen gibt.
Im Gegensatz dazu arbeitet GRBL mit Interrupts für die Stepper-Steuerung. Und es wird auch davon gesprochen, dass hier auch bei zusammengesetzten GRBL-Programmen ein kontinierlicher Flow von Pulsen an die Stepper gesendet wird. Ich nehme an beim 3d-Druck sind Pausen ähnlichen störend wie beim Heissdrahtschneiden.
Beim GRBL-Code glaube ich verstanden zu haben, dass eine allgemeine Erweiterung auf 4 Achsen noch gar nicht die Lösung darstellt. Bei unserem Tragflächenschneiden haben wir ja eigentlich zwei nahezu unabhängige xy Achsen.
Die Wegeplanung per Bresenham-Algorithmus, wie bei GRBL verwendet ist da für X,Y genau richtig, aber für Z,A wahrscheinlich nicht vorhanden. Es kommt also schon auf die Relation der Achsen zueinander an.
Die Lösung wäre eigentlich zwei UNO / CNC-Shield Controller zu verwenden und damit jeweils XY für eine Seite anzutreiben.
Schön wäre es natürlich, wenn man dass dann doch auf einer UNO / CNC-Shield zusammenkriegen könnte, denn die Hardware müste dass ja eigentlich hergeben. Ganz unabhängig sind die beiden XY-Ebenen auch nicht, weil der Draht ja jeweils an den Enden gleichzeitig ankommen muss.

Habe nochmal bei TinyG nachgelesen, der Controller scheint das ja alles zu Unterstützen und auch entsprechende Software für GCode-Abarbeitung zu haben, aber jetzt ist der Ehrgeiz, das mit dem Protoneer Shield hinzubekommen natürlich geweckt.

So, vielleicht war das auch alles nur Geschwafel, die Pausen stören gar nicht weil zu klein und ich muss nur nochmal an meiner Geschwindigkeit / Heizleistung optimieren...

Nochmal eine Frage: Ich stelle mir vor, dass man, wenn man die Profile anders als bei dem Direktansatz mit PCHotwire als GCode verarbeitet auch noch leichter nette Anpassungen wie Aussparungen für Holme etc einbauen kann. Daher finde ich den GCode-Ansatz auch sehr interessant. Da ich mir den Weg vom Profil zum GCode noch nicht erschlossen habe: Kann mir jemand einen Beispiel GCode für ein Tragflächenprofil geben, damit ich das mal testen kann ?


Gruß
Tjwan
 
#55
Ja, wenn ich das alles so lese dann sind das genau meine Gedanken die ich damals auch hatte als ich den Bau geplant hab.
Ich wollte halt zur GCode Basierten Machine weil ich denke das man dann auch einfacher andere Sachen als nur Profile schneiden kann.

Das mit dem Bresenham hatte ich mir fast gedacht bei GRBL, vor allem da Z dort meines Wissens ja auch mit reinspielt fürs 3D Fräsen. Deshalt und wegen dem vorhandenen Shield bzw der Druckerplatine kam dann die Idee mit der 4-Achs Demo als GCode interpreter.

ein GCode Beispiel hab ich jetzt gerade nicht da, aber generell ist es ja so das du den GCode per CAM aus einer Zeichnung (meist DXF) erstellst. Profile als DXF gibts z.B. hier: http://www.verein.rc-network.de/fraesdaten/2010/Profile/, nur ein passendes 4-Achs CAM ist nicht so einfach zu finden.

Es gab da bei RC network mal n schönes Projekt, aber leider ist die Software nirgens mehr zu finden:
http://www.rc-network.de/forum/show...oschneiden-nach-Konzept-Hans23-mit-SchnApps-)
Da ist auch ein Beispiel wie der GCode später ausschaut.

Wie gesagt, mein Bau is verschoben, lese hier aber gerne mit und helfe wenn ich kann.
 

tjwan

Neuer Benutzer
#56
Hallo,

Vielen Dank für die Links. Hans23 fand ich auch mal kurz spannend. Vor allem nett, wieviele Leute sich das nachgebaut haben.
War mir dann aber doch zu exotisch.

Die Lowlevel Algorithmen ala GRBL selber zu schreiben scheint mir doch eine Nummer zu heftig.
Ich bin daher jetzt etwas tiefer in den 4-Achsen Code von https://github.com/LETARTARE/Grbl-xx_with_Arduino eingestiegen.
Man kann die 4te Achse konfigurieren als linear, das könnte schonmal für den Planner helfen.

Der UNO hat leider keinen Port mit 8 parallel zu nutzenden Pins. Wenn man die Achsen mit XY und UV bezeichnet dann ist die V-Achse leider auf einem anderen Port (PORTB), der auch noch für die Spindle verwendet wird.
Ausserdem gibt es für diese Achse keinen Pin für einen Endschalter und es ist auch kein digitaler Pin mehr frei.
Vielleicht kann man dafür noch einen der analogen PINS verwenden. Z.Bsp Coolant wird ja auch nicht benötigt.
Mit dem Mega ist das kein Problem, der hat ausreichend Pins und Ports.
Aber das passt der Shield nicht so schön...

Wie auch immer, wenn man die Spindle-Control totlegt und die ~10 Stellen im Code anpasst, an denen der Port für die Stepper geschrieben wird, könnte das auch mit dem UNO klappen.
Vorrausgesetzt der LETARTARE-Code funktioniert überhaupt...

Ich habe jetzt ein Image, dass sich für den UNO compilieren lässt und bei dem ich Low-Level die Pins und Ports so angesteuert habe, dass alle 4 Stepper richtig angesteuert werden sollten.
Natürlich alles, ohne von dem ganzen Rest allzu viel zu verstehen.
Morgen wird getestet...

Gruß Tjwan
 

tjwan

Neuer Benutzer
#57
Kleines Update:
Das mit den Pins und Ports hat schonmal geklappt und ich kann jetzt mit Uno, Protoneer-Shield und angepasstem LETARTARE-Grbl-Fork alle 4 Achsen per sehr simplem GCode ansteuern etwas
G0 X1
G0 Y1
G0 Z1
G0 U1
oder auch mehrere Achsen auf einmal etwa
G0 X1 Y1
G0 Z1 U1

Probleme

1. Auf der 4ten Achse gibt's noch Probleme mit nicht ganzzahligen Koordinaten, da muss noch irgendwo ein Fehler stecken.
G0 X0.7
klappt wunderbar
G0 U0.7
lässt den Motor nur brummen. Ganzzahlig geht es sofort wieder.

2. Die Feedraten-Berechnung muss nach meinem Verständnis noch entkoppelt werden.
Ich habe jetzt die Achsen XY / ZU und eigentlich will ich ja XY UV
Die 4te Achse T kann als A,B,C oder U,V,W verwendet werden.
Ist aber vielleicht auch nicht so wichtig, wenn die beiden Profile nicht zu unterschiedlich aussehen.
Steckt alles in planner.c, dort wird die absolute Entfernung ermittelt und die feedrate begrenzt.
 

Heino

Neuer Benutzer
#59
Zuletzt bearbeitet:

tjwan

Neuer Benutzer
#60
Moin,

Danke für den Link zu JHW! Werde ich mir ansehen. Was da immer noch wieder für Schätze ans Licht kommen... Den Link kannte ich noch nicht. Gestern hatte ich auch noch eine wissenschaftliche Arbeit über die Optimierung von Heizleistung und Vorschubgeschwindigkeit gefunden...
Das Prinzip Seilwindensteuerung (wie bei Hans23?) hatte ich mir auch schonmal angesehen, aber ich habe mir aus Platzgründen jetzt eine Fräse wie hier: http://flitetest.com/articles/Hot_Wire_CNC_foam_cutter gebaut. Die beiden "Türme" kann ich einfach wegstellen oder wieder auf Markierungen am Boden stellen.

Gestern abend habe ich ein paar Testschnitte gemacht. Die beiden Ebenen XY und UV habe ich in der GRBL-Software noch entkoppelt bekommen und es werden jetzt auch alle Axen berücksichtigt. Nur bei der Beschleunigungsanalyse noch nicht. Das fällt aber wie vermutet bei den langsamen Geschwindigkeiten von ~3mm/s bisher nicht auf. Der Draht geht schön gleichmäßig durch den Schaum.

Für den GCode habe ich auf die Schnelle diesen Generator gefunden: http://swarfer.co.za/rc/wire/
Zum Testen der Maschine war das erstmal völlig ausreichend. Im Focus stand ja erstmal die Machbarkeitsanalyse für den gepatchten GRBL Code.

Gruß Tjwan
 
FPV1

Banggood

Oben Unten