From 70871715e46770637623d2ab9488694c5e63f434 Mon Sep 17 00:00:00 2001
From: Denis B <den_at_okob_net>
Date: Mon, 4 Feb 2013 23:05:45 -0500
Subject: [PATCH] Added support for extruder offset handling

The extruder offset can be specified in the configuration
file or adjusted on the fly using the "M218 T# X# Y#" command.
The EEPROM support is not yet merged in.
The "T#" command can take option "F#" that specifies the feedrate
at which the printing head should be re-positioned. If not
specified the re-positioning move is not preformed immediately,
but the coordinates are adjusted for the printer to properly
position the head when the next movement happens.
---
 Marlin/Configuration.h |  8 ++++-
 Marlin/Marlin_main.cpp | 78 ++++++++++++++++++++++++++++++++++++------
 Marlin/language.h      | 18 ++++++++++
 3 files changed, 93 insertions(+), 11 deletions(-)

diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h
index 9d1027a0fe..0ba908b06e 100644
--- a/Marlin/Configuration.h
+++ b/Marlin/Configuration.h
@@ -286,7 +286,13 @@ const bool Z_ENDSTOPS_INVERTING = true; // set to true to invert the logic of th
 #define DEFAULT_ACCELERATION          3000    // X, Y, Z and E max acceleration in mm/s^2 for printing moves 
 #define DEFAULT_RETRACT_ACCELERATION  3000   // X, Y, Z and E max acceleration in mm/s^2 for r retracts
 
-// 
+// Offset of the extruders (uncomment if using more than one and relying on firmware to position when changing).
+// The offset has to be X=0, Y=0 for the extruder 0 hotend (default extruder).
+// For the other hotends it is their distance from the extruder 0 hotend.
+// #define EXTRUDER_OFFSET_X {0.0, 20.00} // (in mm) for each extruder, offset of the hotend on the X axis
+// #define EXTRUDER_OFFSET_Y {0.0, 5.00}  // (in mm) for each extruder, offset of the hotend on the Y axis
+
+// The speed change that does not require acceleration (i.e. the software might assume it can be done instanteneously)
 #define DEFAULT_XYJERK                20.0    // (mm/sec)
 #define DEFAULT_ZJERK                 0.4     // (mm/sec)
 #define DEFAULT_EJERK                 5.0    // (mm/sec)
diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp
index 355e08b9fe..0ea6ccdc91 100644
--- a/Marlin/Marlin_main.cpp
+++ b/Marlin/Marlin_main.cpp
@@ -113,6 +113,7 @@
 // M207 - set retract length S[positive mm] F[feedrate mm/sec] Z[additional zlift/hop]
 // M208 - set recover=unretract length S[positive mm surplus to the M207 S*] F[feedrate mm/sec]
 // M209 - S<1=true/0=false> enable automatic retract detect if the slicer did not support G10/11: every normal extrude-only move will be classified as retract depending on the direction.
+// M218 - set hotend offset (in mm): T<extruder_number> X<offset_on_X> Y<offset_on_Y>
 // M220 S<factor in percent>- set speed factor override percentage
 // M221 S<factor in percent>- set extrude factor override percentage
 // M240 - Trigger a camera to take a photograph
@@ -155,6 +156,12 @@ float current_position[NUM_AXIS] = { 0.0, 0.0, 0.0, 0.0 };
 float add_homeing[3]={0,0,0};
 float min_pos[3] = { X_MIN_POS, Y_MIN_POS, Z_MIN_POS };
 float max_pos[3] = { X_MAX_POS, Y_MAX_POS, Z_MAX_POS };
+// Extruder offset, only in XY plane
+float extruder_offset[2][EXTRUDERS] = { 
+#if defined(EXTRUDER_OFFSET_X) && defined(EXTRUDER_OFFSET_Y)
+  EXTRUDER_OFFSET_X, EXTRUDER_OFFSET_Y 
+#endif
+}; 
 uint8_t active_extruder = 0;
 int fanSpeed=0;
 
@@ -1353,7 +1360,6 @@ void process_commands()
         retract_recover_feedrate = code_value() ;
       }
     }break;
-    
     case 209: // M209 - S<1=true/0=false> enable automatic retract detect if the slicer did not support G10/11: every normal extrude-only move will be classified as retract depending on the direction.
     {
       if(code_seen('S')) 
@@ -1372,7 +1378,31 @@ void process_commands()
       }
       
     }break;
-    #endif
+    #endif // FWRETRACT
+    case 218: // M218 - set hotend offset (in mm), T<extruder_number> X<offset_on_X> Y<offset_on_Y>
+    {
+      if(setTargetedHotend(218)){
+        break;
+      }
+      if(code_seen('X')) 
+      {
+        extruder_offset[X_AXIS][tmp_extruder] = code_value();
+      }
+      if(code_seen('Y'))
+      {
+        extruder_offset[Y_AXIS][tmp_extruder] = code_value();
+      }
+      SERIAL_ECHO_START;
+      SERIAL_ECHOPGM(MSG_HOTEND_OFFSET);
+      for(tmp_extruder = 0; tmp_extruder < EXTRUDERS; tmp_extruder++) 
+      {
+         SERIAL_ECHO(" ");
+         SERIAL_ECHO(extruder_offset[X_AXIS][tmp_extruder]);
+         SERIAL_ECHO(",");
+         SERIAL_ECHO(extruder_offset[Y_AXIS][tmp_extruder]);
+      }
+      SERIAL_ECHOLN("");
+    }break;
     case 220: // M220 S<factor in percent>- set speed factor override percentage
     {
       if(code_seen('S')) 
@@ -1499,13 +1529,13 @@ void process_commands()
     {
         Config_PrintSettings();
     }
-    break;
-    #ifdef ABORT_ON_ENDSTOP_HIT_FEATURE_ENABLED
-    case 540:
-    {
-        if(code_seen('S')) abort_on_endstop_hit = code_value() > 0;
-    }
-    break;
+    break;
+    #ifdef ABORT_ON_ENDSTOP_HIT_FEATURE_ENABLED
+    case 540:
+    {
+        if(code_seen('S')) abort_on_endstop_hit = code_value() > 0;
+    }
+    break;
     #endif
     #ifdef FILAMENTCHANGEENABLE
     case 600: //Pause for filament change X[pos] Y[pos] Z[relative lift] E[initial retract] L[later retract distance for removal]
@@ -1696,7 +1726,32 @@ void process_commands()
       SERIAL_ECHOLN(MSG_INVALID_EXTRUDER);
     }
     else {
-      active_extruder = tmp_extruder;
+      boolean make_move = false;
+      if(code_seen('F')) {
+        make_move = true;
+        next_feedrate = code_value();
+        if(next_feedrate > 0.0) {
+          feedrate = next_feedrate;
+        }
+      }
+      if(tmp_extruder != active_extruder) {
+        // Save current position to return to after applying extruder offset
+        memcpy(destination, current_position, sizeof(destination));
+        // Offset extruder (only by XY)
+        int i;
+        for(i = 0; i < 2; i++) {
+           current_position[i] = current_position[i] - 
+                                 extruder_offset[i][active_extruder] +
+                                 extruder_offset[i][tmp_extruder];
+        }
+        // Set the new active extruder and position
+        active_extruder = tmp_extruder;
+        plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
+        // Move to the old position if 'F' was in the parameters
+        if(make_move && Stopped == false) {
+           prepare_move();
+        }
+      }
       SERIAL_ECHO_START;
       SERIAL_ECHO(MSG_ACTIVE_EXTRUDER);
       SERIAL_PROTOCOLLN((int)active_extruder);
@@ -2059,6 +2114,9 @@ bool setTargetedHotend(int code){
         case 109:
           SERIAL_ECHO(MSG_M109_INVALID_EXTRUDER);
           break;
+        case 218:
+          SERIAL_ECHO(MSG_M218_INVALID_EXTRUDER);
+          break;
       }
       SERIAL_ECHOLN(tmp_extruder);
       return true;
diff --git a/Marlin/language.h b/Marlin/language.h
index 781268aa4e..3268971ff1 100644
--- a/Marlin/language.h
+++ b/Marlin/language.h
@@ -139,6 +139,7 @@
 	#define MSG_END_FILE_LIST "End file list"
 	#define MSG_M104_INVALID_EXTRUDER "M104 Invalid extruder "
 	#define MSG_M105_INVALID_EXTRUDER "M105 Invalid extruder "
+	#define MSG_M218_INVALID_EXTRUDER "M218 Invalid extruder "
 	#define MSG_ERR_NO_THERMISTORS "No thermistors - no temperature"
 	#define MSG_M109_INVALID_EXTRUDER "M109 Invalid extruder "
 	#define MSG_HEATING "Heating..."
@@ -162,6 +163,7 @@
 	#define MSG_M119_REPORT "Reporting endstop status"
 	#define MSG_ENDSTOP_HIT "TRIGGERED"
 	#define MSG_ENDSTOP_OPEN "open"
+	#define MSG_HOTEND_OFFSET "Hotend offsets:"
 
 	#define MSG_SD_CANT_OPEN_SUBDIR "Cannot open subdir"
 	#define MSG_SD_INIT_FAIL "SD init fail"
@@ -294,6 +296,7 @@
 	#define MSG_END_FILE_LIST "Koniec listy plikow"
 	#define MSG_M104_INVALID_EXTRUDER "M104 Niepoprawny ekstruder "
 	#define MSG_M105_INVALID_EXTRUDER "M105 Niepoprawny ekstruder "
+	#define MSG_M218_INVALID_EXTRUDER "M218 Niepoprawny ekstruder "
 	#define MSG_ERR_NO_THERMISTORS "Brak termistorow - brak temperatury :("
 	#define MSG_M109_INVALID_EXTRUDER "M109 Niepoprawny ekstruder "
 	#define MSG_HEATING "Nagrzewanie ekstrudera..."
@@ -317,6 +320,7 @@
 	#define MSG_M119_REPORT "Zgloszenie statusu wylacznikow krancowych"
 	#define MSG_ENDSTOP_HIT "WYZWOLONY"
 	#define MSG_ENDSTOP_OPEN "otwarty"
+	#define MSG_HOTEND_OFFSET "Hotend offsets:"
 
 	#define MSG_SD_CANT_OPEN_SUBDIR "Nie mozna otworzyc podkatalogu"
 	#define MSG_SD_INIT_FAIL "Blad inicjalizacji karty SD"
@@ -454,6 +458,7 @@
 #define MSG_END_FILE_LIST "Fin de la liste de fichiers"
 #define MSG_M104_INVALID_EXTRUDER "M104 Extruder invalide"
 #define MSG_M105_INVALID_EXTRUDER "M105 Extruder invalide"
+#define MSG_M218_INVALID_EXTRUDER "M218 Extruder invalide"
 #define MSG_ERR_NO_THERMISTORS "Pas de thermistor, pas de temperature"
 #define MSG_M109_INVALID_EXTRUDER "M109 Extruder invalide "
 #define MSG_HEATING "En chauffe..."
@@ -477,6 +482,7 @@
 #define MSG_M119_REPORT "Affichage du status des fin de course"
 #define MSG_ENDSTOP_HIT "DECLENCHE"
 #define MSG_ENDSTOP_OPEN "OUVERT"
+#define MSG_HOTEND_OFFSET "Hotend offsets:"
 
 #define MSG_SD_CANT_OPEN_SUBDIR "Impossible d'ouvrir le sous-repertoire"
 #define MSG_SD_INIT_FAIL "Echec de l'initialisation de la SD"
@@ -612,6 +618,7 @@
 	#define MSG_END_FILE_LIST "End file list"
 	#define MSG_M104_INVALID_EXTRUDER "M104 Invalid extruder "
 	#define MSG_M105_INVALID_EXTRUDER "M105 Invalid extruder "
+	#define MSG_M218_INVALID_EXTRUDER "M218 Invalid extruder "
 	#define MSG_ERR_NO_THERMISTORS "No thermistors - no temp"
 	#define MSG_M109_INVALID_EXTRUDER "M109 Invalid extruder "
 	#define MSG_HEATING "Heating..."
@@ -635,6 +642,7 @@
 	#define MSG_M119_REPORT "Reporting endstop status"
 	#define MSG_ENDSTOP_HIT "TRIGGERED"
 	#define MSG_ENDSTOP_OPEN "open"
+	#define MSG_HOTEND_OFFSET "Hotend offsets:"
 
 	#define MSG_SD_CANT_OPEN_SUBDIR "Cannot open subdir"
 	#define MSG_SD_INIT_FAIL "SD init fail"
@@ -770,6 +778,7 @@
 #define MSG_END_FILE_LIST "Fin de la lista de archivos"
 #define MSG_M104_INVALID_EXTRUDER "M104 Extrusor Invalido "
 #define MSG_M105_INVALID_EXTRUDER "M105 Extrusor Invalido "
+#define MSG_M218_INVALID_EXTRUDER "M218 Extrusor Invalido "
 #define MSG_ERR_NO_THERMISTORS "No hay termistores - no temp"
 #define MSG_M109_INVALID_EXTRUDER "M109 Extrusor Invalido "
 #define MSG_HEATING "Calentando..."
@@ -792,6 +801,7 @@
 #define MSG_M119_REPORT "Comprobando fines de carrera."
 #define MSG_ENDSTOP_HIT "PULSADO"
 #define MSG_ENDSTOP_OPEN "abierto"
+#define MSG_HOTEND_OFFSET "Hotend offsets:"
         
 #define MSG_SD_CANT_OPEN_SUBDIR "No se pudo abrir la subcarpeta."
 #define MSG_SD_INIT_FAIL "Fallo al iniciar la SD"
@@ -921,6 +931,7 @@
 #define MSG_END_FILE_LIST					"Конец списка файлов"
 #define MSG_M104_INVALID_EXTRUDER			"M104 ошибка экструдера "
 #define MSG_M105_INVALID_EXTRUDER			"M105 ошибка экструдера "
+#define MSG_M218_INVALID_EXTRUDER			"M218 ошибка экструдера "
 #define MSG_ERR_NO_THERMISTORS				"Нет термистра - нет температуры"
 #define MSG_M109_INVALID_EXTRUDER			"M109 ошибка экструдера "
 #define MSG_HEATING							"Нагрев...  "
@@ -944,6 +955,7 @@
 #define MSG_M119_REPORT						"Статус концевиков"
 #define MSG_ENDSTOP_HIT						"Срабатывание концевика"
 #define MSG_ENDSTOP_OPEN					"Концевик освобожден"
+#define MSG_HOTEND_OFFSET					"Hotend offsets:"
 #define MSG_SD_CANT_OPEN_SUBDIR				"Не открыть папку"
 #define MSG_SD_INIT_FAIL					"Ошибка инициализации SD"
 #define MSG_SD_VOL_INIT_FAIL				"Ошибка инициализации раздела"
@@ -1080,6 +1092,7 @@
 	#define MSG_END_FILE_LIST        "Fine Lista File"
 	#define MSG_M104_INVALID_EXTRUDER "M104 Estrusore non valido "
 	#define MSG_M105_INVALID_EXTRUDER "M105 Estrusore non valido "
+	#define MSG_M218_INVALID_EXTRUDER "M218 Estrusore non valido "
 	#define MSG_ERR_NO_THERMISTORS   "Nessun Termistore - nessuna temperatura"
 	#define MSG_M109_INVALID_EXTRUDER "M109 Estrusore non valido "
 	#define MSG_HEATING              "Riscaldamento..."
@@ -1103,6 +1116,7 @@
 	#define MSG_M119_REPORT          "Segnalazione stato degli endstop"
 	#define MSG_ENDSTOP_HIT          "INNESCATO"
 	#define MSG_ENDSTOP_OPEN         "aperto"
+	#define MSG_HOTEND_OFFSET        "Hotend offsets:"
 
 	#define MSG_SD_CANT_OPEN_SUBDIR  "Impossibile aprire sottocartella"
 	#define MSG_SD_INIT_FAIL         "Fallita Inizializzazione SD"
@@ -1242,6 +1256,7 @@
 	#define MSG_END_FILE_LIST "Fim da lista de arquivos"
 	#define MSG_M104_INVALID_EXTRUDER "M104 Extrusor inválido "
 	#define MSG_M105_INVALID_EXTRUDER "M105 Extrusor inválido "
+	#define MSG_M218_INVALID_EXTRUDER "M218 Extrusor inválido "
 	#define MSG_ERR_NO_THERMISTORS "Nao ha termistor - no temp"
 	#define MSG_M109_INVALID_EXTRUDER "M109 Extrusor inválido "
 	#define MSG_HEATING "Aquecendo..."
@@ -1265,6 +1280,7 @@
 	#define MSG_M119_REPORT "Relatando estado do ponto final"
 	#define MSG_ENDSTOP_HIT "PULSADO"
 	#define MSG_ENDSTOP_OPEN "Aberto"
+	#define MSG_HOTEND_OFFSET "Hotend offsets:"
 
 	#define MSG_SD_CANT_OPEN_SUBDIR "Nao pode abrir sub diretorio"
 	#define MSG_SD_INIT_FAIL "Falha ao iniciar SD"
@@ -1399,6 +1415,7 @@
 	#define MSG_END_FILE_LIST "Tiedostolistauksen loppu"
 	#define MSG_M104_INVALID_EXTRUDER "M104 Virheellinen suutin "
 	#define MSG_M105_INVALID_EXTRUDER "M105 Virheellinen suutin "
+	#define MSG_M218_INVALID_EXTRUDER "M218 Virheellinen suutin "
 	#define MSG_ERR_NO_THERMISTORS "Ei termistoreja - ei lampotiloja"
 	#define MSG_M109_INVALID_EXTRUDER "M109 Virheellinen suutin "
 	#define MSG_HEATING "Lammitan..."
@@ -1422,6 +1439,7 @@
 	#define MSG_M119_REPORT "Rajakytkimien tilaraportti"
 	#define MSG_ENDSTOP_HIT "AKTIIVISENA"
 	#define MSG_ENDSTOP_OPEN "avoinna"
+	#define MSG_HOTEND_OFFSET "Hotend offsets:"
 
 	#define MSG_SD_CANT_OPEN_SUBDIR "Alihakemistoa ei voitu avata"
 	#define MSG_SD_INIT_FAIL "SD alustus epaonnistui"