void CardReader::removeFile(char* name) {
  if (!cardOK) return;

  file.close();
  sdprinting = false;

  SdFile myDir;
  curDir = &root;
  char *fname = name;

  char *dirname_start, *dirname_end;
  if (name[0] == '/') {
    dirname_start = strchr(name, '/') + 1;
    while (dirname_start > 0) {
      dirname_end = strchr(dirname_start, '/');
      if (dirname_end > 0 && dirname_end > dirname_start) {
        char subdirname[FILENAME_LENGTH];
        strncpy(subdirname, dirname_start, dirname_end - dirname_start);
        subdirname[dirname_end - dirname_start] = 0;
        ECHO_EV(subdirname);
        if (!myDir.open(curDir, subdirname, O_READ)) {
          ECHO_MV(MSG_SD_OPEN_FILE_FAIL, subdirname);
          ECHO_C('.');
          return;
        }

        curDir = &myDir;
        dirname_start = dirname_end + 1;
      }
      else { // the remainder after all /fsa/fdsa/ is the filename
        fname = dirname_start;
        break;
      }
    }
  }
  else { // relative path
    curDir = &workDir;
  }

  if (file.remove(curDir, fname)) {
    ECHO_EMV(MSG_SD_FILE_DELETED, fname);
    sdpos = 0;
  }
  else {
    ECHO_MV(MSG_SD_FILE_DELETION_ERR, fname);
    ECHO_C('.');
  }
}
/**
 * Dive into a folder and recurse depth-first to perform a pre-set operation lsAction:
 *   LS_Count       - Add +1 to nrFiles for every file within the parent
 *   LS_GetFilename - Get the filename of the file indexed by nrFiles
 *   LS_SerialPrint - Print the full path of each file to serial output
 */
void CardReader::lsDive(const char *prepend, SdFile parent, const char * const match/*=NULL*/) {
  dir_t p;
  uint8_t cnt = 0;

  // Read the next entry from a directory
  while (parent.readDir(p, longFilename) > 0) {

    // If the entry is a directory and the action is LS_SerialPrint
    if (DIR_IS_SUBDIR(&p) && lsAction != LS_Count && lsAction != LS_GetFilename) {

      // Get the short name for the item, which we know is a folder
      char lfilename[FILENAME_LENGTH];
      createFilename(lfilename, p);

      // Allocate enough stack space for the full path to a folder, trailing slash, and nul
      boolean prepend_is_empty = (prepend[0] == '\0');
      int len = (prepend_is_empty ? 1 : strlen(prepend)) + strlen(lfilename) + 1 + 1;
      char path[len];

      // Append the FOLDERNAME12/ to the passed string.
      // It contains the full path to the "parent" argument.
      // We now have the full path to the item in this folder.
      strcpy(path, prepend_is_empty ? "/" : prepend); // root slash if prepend is empty
      strcat(path, lfilename); // FILENAME_LENGTH-1 characters maximum
      strcat(path, "/");       // 1 character

      // Serial.print(path);

      // Get a new directory object using the full path
      // and dive recursively into it.
      SdFile dir;
      if (!dir.open(parent, lfilename, O_READ)) {
        if (lsAction == LS_SerialPrint) {
          ECHO_LMV(ER, MSG_SD_CANT_OPEN_SUBDIR, lfilename);
        }
      }
      lsDive(path, dir);
      // close() is done automatically by destructor of SdFile
    }
    else {
      char pn0 = p.name[0];
      if (pn0 == DIR_NAME_FREE) break;
      if (pn0 == DIR_NAME_DELETED || pn0 == '.') continue;
      if (longFilename[0] == '.') continue;

      if (!DIR_IS_FILE_OR_SUBDIR(&p)) continue;

      filenameIsDir = DIR_IS_SUBDIR(&p);

      if (!filenameIsDir && (p.name[8] != 'G' || p.name[9] == '~')) continue;

      switch (lsAction) {
        case LS_Count:
          nrFiles++;
          break;
        case LS_SerialPrint:
          createFilename(filename, p);
          ECHO_V(prepend);
          ECHO_EV(filename);
          break;
        case LS_GetFilename:
          createFilename(filename, p);
          if (match != NULL) {
            if (strcasecmp(match, filename) == 0) return;
          }
          else if (cnt == nrFiles) return;
          cnt++;
          break;
      }
    }
  } // while readDir
}
void CardReader::openFile(char* name, bool read, bool replace_current/*=true*/, bool lcd_status/*=true*/) {
  if (!cardOK) return;
  if (file.isOpen()) { //replacing current file by new file, or subfile call
    if (!replace_current) {
      if (file_subcall_ctr > SD_PROCEDURE_DEPTH - 1) {
        ECHO_LMV(ER, MSG_SD_MAX_DEPTH, SD_PROCEDURE_DEPTH);
        kill(PSTR(MSG_KILLED));
        return;
      }

      ECHO_SMV(DB, "SUBROUTINE CALL target:\"", name);
      ECHO_M("\" parent:\"");

      //store current filename and position
      getAbsFilename(filenames[file_subcall_ctr]);

      ECHO_V(filenames[file_subcall_ctr]);
      ECHO_EMV("\" pos", sdpos);
      filespos[file_subcall_ctr] = sdpos;
      file_subcall_ctr++;
    }
    else {
     ECHO_LMV(DB, "Now doing file: ", name);
    }
    file.close();
  }
  else { // opening fresh file
    file_subcall_ctr = 0; // resetting procedure depth in case user cancels print while in procedure
    ECHO_LMV(DB, "Now fresh file: ", name);
  }
  sdprinting = false;

  SdFile myDir;
  curDir = &root;
  char *fname = name;

  char *dirname_start, *dirname_end;
  if (name[0] == '/') {
    dirname_start = &name[1];
    while (dirname_start > 0) {
      dirname_end = strchr(dirname_start, '/');
      if (dirname_end > 0 && dirname_end > dirname_start) {
        char subdirname[FILENAME_LENGTH];
        strncpy(subdirname, dirname_start, dirname_end - dirname_start);
        subdirname[dirname_end - dirname_start] = 0;
        ECHO_EV(subdirname);
        if (!myDir.open(curDir, subdirname, O_READ)) {
          ECHO_MV(MSG_SD_OPEN_FILE_FAIL, subdirname);
          ECHO_C('.');
          return;
        }
        else {
          //ECHO_EM("dive ok");
        }

        curDir = &myDir;
        dirname_start = dirname_end + 1;
      }
      else { // the remainder after all /fsa/fdsa/ is the filename
        fname = dirname_start;
        //ECHO_EM("remainder");
        //ECHO_EV(fname);
        break;
      }
    }
  }
  else { //relative path
    curDir = &workDir;
  }

  if (read) {
    if (file.open(curDir, fname, O_READ)) {
      filesize = file.fileSize();
      ECHO_MV(MSG_SD_FILE_OPENED, fname);
      ECHO_EMV(MSG_SD_SIZE, filesize);
      sdpos = 0;

      ECHO_EM(MSG_SD_FILE_SELECTED);
      getfilename(0, fname);
      if(lcd_status) lcd_setstatus(longFilename[0] ? longFilename : fname);
    }
    else {
      ECHO_MV(MSG_SD_OPEN_FILE_FAIL, fname);
      ECHO_PGM(".\n");
    }
  }
  else { //write
    if (!file.open(curDir, fname, O_CREAT | O_APPEND | O_WRITE | O_TRUNC)) {
      ECHO_MV(MSG_SD_OPEN_FILE_FAIL, fname);
      ECHO_PGM(".\n");
    }
    else {
      saving = true;
      ECHO_EMV(MSG_SD_WRITE_TO_FILE, name);
      if(lcd_status) lcd_setstatus(fname);
    }
  }
}
void FirmwareTest() {
  ECHO_EM("---------- FIRMWARE TEST --------------");
  ECHO_EM("--------- by MarlinKimbra -------------");
  ECHO_EV(MSG_FWTEST_01);
  ECHO_EV(MSG_FWTEST_02);
  ECHO_EV(MSG_FWTEST_YES_NO);
  serial_answer = ' ';
  while (serial_answer!='y' && serial_answer!='Y' && serial_answer!='n' && serial_answer!='N') {
    serial_answer = MYSERIAL.read();
  }
  if (serial_answer=='y' || serial_answer=='Y') {
    ECHO_EV(MSG_FWTEST_03);

    ECHO_EM(" ");
    ECHO_EM("***** ENDSTOP X *****");
    #if PIN_EXISTS(X_MIN) && (X_HOME_DIR == -1)
      if (!READ(X_MIN_PIN)^X_MIN_ENDSTOP_INVERTING) {
        ECHO_M("MIN ENDSTOP X: ");
        ECHO_EV(MSG_ENDSTOP_OPEN);
      }
      else {
        ECHO_M("X ENDSTOP ");
        ECHO_EM(MSG_FWTEST_ERROR);
        ECHO_M(MSG_FWTEST_INVERT);
        ECHO_M("#define X_MIN_ENDSTOP_LOGIC ");
        ECHO_M(MSG_FWTEST_INTO);
        #if MECH(CARTESIAN)
          ECHO_EM("Configuration_Cartesian.h");
        #elif MECH(COREXY)
          ECHO_EM("Configuration_Core.h");
        #elif MECH(COREXZ)
          ECHO_EM("Configuration_Core.h");
        #elif MECH(DELTA)
          ECHO_EM("Configuration_Delta.h");
        #elif MECH(SCARA)
          ECHO_EM("Configuration_Scara.h");
        #endif
        return;
      }
      ECHO_V(MSG_FWTEST_PRESS);
      ECHO_EM("X");
      ECHO_EV(MSG_FWTEST_YES);
      serial_answer = ' ';
      while (serial_answer!='y' && serial_answer!='Y' && !(READ(X_MIN_PIN)^X_MIN_ENDSTOP_INVERTING)) {
        serial_answer = MYSERIAL.read();
      }
      if (READ(X_MIN_PIN)^X_MIN_ENDSTOP_INVERTING) {
        ECHO_M("MIN ENDSTOP X: ");
        ECHO_EV(MSG_ENDSTOP_HIT);
      }
      else {
        ECHO_M("X ");
        ECHO_EV(MSG_FWTEST_ENDSTOP_ERR);
        return;
      }
    #elif PIN_EXISTS(X_MAX) && X_HOME_DIR == 1
      if (!READ(X_MAX_PIN)^X_MAX_ENDSTOP_INVERTING) {
        ECHO_M("MAX ENDSTOP X: ");
        ECHO_EV(MSG_ENDSTOP_OPEN);
      }
      else {
        ECHO_M("X ENDSTOP ");
        ECHO_EM(MSG_FWTEST_ERROR);
        ECHO_M(MSG_FWTEST_INVERT);
        ECHO_M("#define X_MAX_ENDSTOP_LOGIC ");
        ECHO_M(MSG_FWTEST_INTO);
        #if MECH(CARTESIAN)
          ECHO_EM("Configuration_Cartesian.h");
        #elif MECH(COREXY)
          ECHO_EM("Configuration_Core.h");
        #elif MECH(COREXZ)
          ECHO_EM("Configuration_Core.h");
        #elif MECH(DELTA)
          ECHO_EM("Configuration_Delta.h");
        #elif MECH(SCARA)
          ECHO_EM("Configuration_Scara.h");
        #endif
        return;
      }
      ECHO_V(MSG_FWTEST_PRESS);
      ECHO_EM("X");
      ECHO_EV(MSG_FWTEST_YES);
      serial_answer = ' ';
      while (serial_answer!='y' && serial_answer!='Y' && !(READ(X_MAX_PIN)^X_MAX_ENDSTOP_INVERTING)) {
        serial_answer = MYSERIAL.read();
      }
      if (READ(X_MAX_PIN)^X_MAX_ENDSTOP_INVERTING) {
        ECHO_M("MAX ENDSTOP X: ");
        ECHO_EV(MSG_ENDSTOP_HIT);
      }
      else {
        ECHO_M("X ");
        ECHO_EV(MSG_FWTEST_ENDSTOP_ERR);
        return;
      }
    #elif X_HOME_DIR == -1
      ECHO_M(MSG_FWTEST_ERROR);
      ECHO_M("!!! X_MIN_PIN ");
      ECHO_EM(MSG_FWTEST_NDEF);
      return;
    #elif X_HOME_DIR == 1
      ECHO_M(MSG_FWTEST_ERROR);
      ECHO_M("!!! X_MAX_PIN ");
      ECHO_EM(MSG_FWTEST_NDEF);
      return;
    #endif

    ECHO_EM(" ");
    ECHO_EM("***** ENDSTOP Y *****");
    #if PIN_EXISTS(Y_MIN) && Y_HOME_DIR == -1
      if (!READ(Y_MIN_PIN)^Y_MIN_ENDSTOP_INVERTING) {
        ECHO_M("MIN ENDSTOP Y: ");
        ECHO_EV(MSG_ENDSTOP_OPEN);
      }
      else {
        ECHO_M("Y ENDSTOP ");
        ECHO_EM(MSG_FWTEST_ERROR);
        ECHO_M(MSG_FWTEST_INVERT);
        ECHO_M("#define Y_MIN_ENDSTOP_LOGIC ");
        ECHO_M(MSG_FWTEST_INTO);
        #if MECH(CARTESIAN)
          ECHO_EM("Configuration_Cartesian.h");
        #elif MECH(COREXY)
          ECHO_EM("Configuration_Core.h");
        #elif MECH(COREXZ)
          ECHO_EM("Configuration_Core.h");
        #elif MECH(DELTA)
          ECHO_EM("Configuration_Delta.h");
        #elif MECH(SCARA)
          ECHO_EM("Configuration_Scara.h");
        #endif
        return;
      }
      ECHO_V(MSG_FWTEST_PRESS);
      ECHO_EM("Y");
      ECHO_EV(MSG_FWTEST_YES);
      serial_answer = ' ';
      while (serial_answer!='y' && serial_answer!='Y' && !(READ(Y_MIN_PIN)^Y_MIN_ENDSTOP_INVERTING)) {
        serial_answer = MYSERIAL.read();
      }
      if (READ(Y_MIN_PIN)^Y_MIN_ENDSTOP_INVERTING) {
        ECHO_M("MIN ENDSTOP Y: ");
        ECHO_EV(MSG_ENDSTOP_HIT);
      }
      else {
        ECHO_M("Y ");
        ECHO_EV(MSG_FWTEST_ENDSTOP_ERR);
        return;
      }
    #elif PIN_EXISTS(Y_MAX) && Y_HOME_DIR == 1
      if (!READ(Y_MAX_PIN)^Y_MAX_ENDSTOP_INVERTING) {
        ECHO_M("MAX ENDSTOP Y: ");
        ECHO_EV(MSG_ENDSTOP_OPEN);
      }
      else {
        ECHO_M("Y ENDSTOP ");
        ECHO_EM(MSG_FWTEST_ERROR);
        ECHO_M(MSG_FWTEST_INVERT);
        ECHO_M("#define Y_MAX_ENDSTOP_LOGIC ");
        ECHO_M(MSG_FWTEST_INTO);
        #if MECH(CARTESIAN)
          ECHO_EM("Configuration_Cartesian.h");
        #elif MECH(COREXY)
          ECHO_EM("Configuration_Core.h");
        #elif MECH(COREXZ)
          ECHO_EM("Configuration_Core.h");
        #elif MECH(DELTA)
          ECHO_EM("Configuration_Delta.h");
        #elif MECH(SCARA)
          ECHO_EM("Configuration_Scara.h");
        #endif
        return;
      }
      ECHO_V(MSG_FWTEST_PRESS);
      ECHO_EM("Y");
      ECHO_EV(MSG_FWTEST_YES);
      serial_answer = ' ';
      while (serial_answer!='y' && serial_answer!='Y' && !(READ(Y_MAX_PIN)^Y_MAX_ENDSTOP_INVERTING)) {
        serial_answer = MYSERIAL.read();
      }
      if (READ(Y_MAX_PIN)^Y_MAX_ENDSTOP_INVERTING) {
        ECHO_M("MAX ENDSTOP Y: ");
        ECHO_EV(MSG_ENDSTOP_HIT);
      }
      else {
        ECHO_M("Y ");
        ECHO_EV(MSG_FWTEST_ENDSTOP_ERR);
        return;
      }
    #elif Y_HOME_DIR == -1
      ECHO_M(MSG_FWTEST_ERROR);
      ECHO_M("!!! Y_MIN_PIN ");
      ECHO_EM(MSG_FWTEST_NDEF);
      return;
    #elif Y_HOME_DIR == 1
      ECHO_M(MSG_FWTEST_ERROR);
      ECHO_M("!!! Y_MAX_PIN ");
      ECHO_EM(MSG_FWTEST_NDEF);
      return;
    #endif

    ECHO_EM(" ");
    ECHO_EM("***** ENDSTOP Z *****");
    #if PIN_EXISTS(Z_MIN) && Z_HOME_DIR == -1
      if (!READ(Z_MIN_PIN)^Z_MIN_ENDSTOP_INVERTING) {
        ECHO_M("MIN ENDSTOP Z: ");
        ECHO_EV(MSG_ENDSTOP_OPEN);
      }
      else {
        ECHO_M("Z ENDSTOP ");
        ECHO_EM(MSG_FWTEST_ERROR);
        ECHO_M(MSG_FWTEST_INVERT);
        ECHO_M("#define Z_MIN_ENDSTOP_LOGIC ");
        ECHO_M(MSG_FWTEST_INTO);
        #if MECH(CARTESIAN)
          ECHO_EM("Configuration_Cartesian.h");
        #elif MECH(COREXY)
          ECHO_EM("Configuration_Core.h");
        #elif MECH(COREXZ)
          ECHO_EM("Configuration_Core.h");
        #elif MECH(DELTA)
          ECHO_EM("Configuration_Delta.h");
        #elif MECH(SCARA)
          ECHO_EM("Configuration_Scara.h");
        #endif
        return;
      }
      ECHO_V(MSG_FWTEST_PRESS);
      ECHO_EM("Z");
      ECHO_EV(MSG_FWTEST_YES);
      serial_answer = ' ';
      while (serial_answer!='y' && serial_answer!='Y' && !(READ(Z_MIN_PIN)^Z_MIN_ENDSTOP_INVERTING)) {
        serial_answer = MYSERIAL.read();
      }
      if (READ(Z_MIN_PIN)^Z_MIN_ENDSTOP_INVERTING) {
        ECHO_M("MIN ENDSTOP Z: ");
        ECHO_EV(MSG_ENDSTOP_HIT);
      }
      else {
        ECHO_M("Z ");
        ECHO_EV(MSG_FWTEST_ENDSTOP_ERR);
        return;
      }
    #elif PIN_EXISTS(Z_MAX) && Z_HOME_DIR == 1
      if (!READ(Z_MAX_PIN)^Z_MAX_ENDSTOP_INVERTING) {
        ECHO_M("MAX ENDSTOP Z: ");
        ECHO_EV(MSG_ENDSTOP_OPEN);
      }
      else {
        ECHO_M("Z ENDSTOP ");
        ECHO_EM(MSG_FWTEST_ERROR);
        ECHO_M(MSG_FWTEST_INVERT);
        ECHO_M("#define Z_MAX_ENDSTOP_LOGIC ");
        ECHO_M(MSG_FWTEST_INTO);
        #if MECH(CARTESIAN)
          ECHO_EM("Configuration_Cartesian.h");
        #elif MECH(COREXY)
          ECHO_EM("Configuration_Core.h");
        #elif MECH(COREXZ)
          ECHO_EM("Configuration_Core.h");
        #elif MECH(DELTA)
          ECHO_EM("Configuration_Delta.h");
        #elif MECH(SCARA)
          ECHO_EM("Configuration_Scara.h");
        #endif
        return;
      }
      ECHO_V(MSG_FWTEST_PRESS);
      ECHO_EM("Z");
      ECHO_EV(MSG_FWTEST_YES);
      serial_answer = ' ';
      while (serial_answer!='y' && serial_answer!='Y' && !(READ(Z_MAX_PIN)^Z_MAX_ENDSTOP_INVERTING)) {
        serial_answer = MYSERIAL.read();
      }
      if (READ(Z_MAX_PIN)^Z_MAX_ENDSTOP_INVERTING) {
        ECHO_M("MAX ENDSTOP Z: ");
        ECHO_EV(MSG_ENDSTOP_HIT);
      }
      else {
        ECHO_M("Z ");
        ECHO_EV(MSG_FWTEST_ENDSTOP_ERR);
        return;
      }
    #elif Z_HOME_DIR == -1
      ECHO_M(MSG_FWTEST_ERROR);
      ECHO_M("!!! Z_MIN_PIN ");
      ECHO_EM(MSG_FWTEST_NDEF);
      return;
    #elif Z_HOME_DIR == 1
      ECHO_M(MSG_FWTEST_ERROR);
      ECHO_M("!!! Z_MAX_PIN ");
      ECHO_EM(MSG_FWTEST_NDEF);
      return;
    #endif

    ECHO_EM("ENDSTOP ");
    ECHO_M(MSG_FWTEST_OK);
    ECHO_EM(" ");
  }

  #if HAS(POWER_SWITCH)
    SET_OUTPUT(PS_ON_PIN);
    WRITE(PS_ON_PIN, PS_ON_AWAKE);
  #endif

  // Reset position to 0
  st_synchronize();
  for (int8_t i = 0; i < NUM_AXIS; i++) current_position[i] = 0;
  plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);

  ECHO_EM("***** TEST MOTOR  *****");
  ECHO_EV(MSG_FWTEST_ATTENTION);
  ECHO_EV(MSG_FWTEST_YES);
  serial_answer = ' ';
  while (serial_answer!='y' && serial_answer!='Y') {
    serial_answer = MYSERIAL.read();
  }
  ECHO_EV(MSG_FWTEST_04);
  ECHO_EM(" ");
  ECHO_EM("***** MOTOR X *****");
  destination[X_AXIS] = 10;
  prepare_move();
  st_synchronize();

  ECHO_EV(MSG_FWTEST_XAXIS);
  ECHO_EV(MSG_FWTEST_YES_NO);
  serial_answer = ' ';
  while (serial_answer!='y' && serial_answer!='Y' && serial_answer!='n' && serial_answer!='N') {
    serial_answer = MYSERIAL.read();
  }
  if (serial_answer=='y' || serial_answer=='Y') {
    ECHO_EM("MOTOR X ");
    ECHO_M(MSG_FWTEST_OK);
  }
  else {
    ECHO_M(MSG_FWTEST_INVERT);
    ECHO_M("#define INVERT_X_DIR ");
    ECHO_M(MSG_FWTEST_INTO);
    #if MECH(CARTESIAN)
      ECHO_EM("Configuration_Cartesian.h");
    #elif MECH(COREXY)
      ECHO_EM("Configuration_Core.h");
    #elif MECH(COREXZ)
      ECHO_EM("Configuration_Core.h");
    #elif MECH(DELTA)
      ECHO_EM("Configuration_Delta.h");
    #elif MECH(SCARA)
      ECHO_EM("Configuration_Scara.h");
    #endif
    return;
  }
  ECHO_EM(" ");
  ECHO_EM("***** MOTOR Y *****");
  destination[Y_AXIS] = 10;
  prepare_move();
  st_synchronize();
  ECHO_EV(MSG_FWTEST_YAXIS);
  ECHO_EV(MSG_FWTEST_YES_NO);
  serial_answer = ' ';
  while (serial_answer!='y' && serial_answer!='Y' && serial_answer!='n' && serial_answer!='N') {
    serial_answer = MYSERIAL.read();
  }
  if (serial_answer=='y' || serial_answer=='Y') {
    ECHO_EM("MOTOR Y ");
    ECHO_M(MSG_FWTEST_OK);
  }
  else {
    ECHO_M(MSG_FWTEST_INVERT);
    ECHO_M("#define INVERT_Y_DIR ");
    ECHO_M(MSG_FWTEST_INTO);
    #if MECH(CARTESIAN)
      ECHO_EM("Configuration_Cartesian.h");
    #elif MECH(COREXY)
      ECHO_EM("Configuration_Core.h");
    #elif MECH(COREXZ)
      ECHO_EM("Configuration_Core.h");
    #elif MECH(DELTA)
      ECHO_EM("Configuration_Delta.h");
    #elif MECH(SCARA)
      ECHO_EM("Configuration_Scara.h");
    #endif
    return;
  }
  ECHO_EM(" ");
  ECHO_EM("***** MOTOR Z *****");
  destination[Z_AXIS] = 10;
  prepare_move();
  st_synchronize();
  ECHO_EV(MSG_FWTEST_ZAXIS);
  ECHO_EV(MSG_FWTEST_YES_NO);
  serial_answer = ' ';
  while (serial_answer!='y' && serial_answer!='Y' && serial_answer!='n' && serial_answer!='N') {
    serial_answer = MYSERIAL.read();
  }
  if (serial_answer=='y' || serial_answer=='Y') {
    ECHO_EM("MOTOR Z ");
    ECHO_M(MSG_FWTEST_OK);
  }
  else {
    ECHO_M(MSG_FWTEST_INVERT);
    ECHO_M("#define INVERT_Z_DIR ");
    ECHO_M(MSG_FWTEST_INTO);
    #if MECH(CARTESIAN)
      ECHO_EM("Configuration_Cartesian.h");
    #elif MECH(COREXY)
      ECHO_EM("Configuration_Core.h");
    #elif MECH(COREXZ)
      ECHO_EM("Configuration_Core.h");
    #elif MECH(DELTA)
      ECHO_EM("Configuration_Delta.h");
    #elif MECH(SCARA)
      ECHO_EM("Configuration_Scara.h");
    #endif
    return;
  }
  ECHO_EM("MOTOR ");
  ECHO_M(MSG_FWTEST_OK);
  ECHO_EM(" ");
  ECHO_V(MSG_FWTEST_END);
}