/** * M900: Get or Set Linear Advance K-factor * * K<factor> Set advance K factor */ void GcodeSuite::M900() { #if EXTRUDERS < 2 constexpr uint8_t tmp_extruder = 0; #else const uint8_t tmp_extruder = parser.seenval('T') ? parser.value_int() : active_extruder; if (tmp_extruder >= EXTRUDERS) { SERIAL_ECHOLNPGM("?T value out of range."); return; } #endif if (parser.seenval('K')) { const float newK = parser.floatval('K'); if (WITHIN(newK, 0, 10)) { planner.synchronize(); planner.extruder_advance_K[tmp_extruder] = newK; } else SERIAL_ECHOLNPGM("?K value out of range (0-10)."); } else { SERIAL_ECHO_START(); #if EXTRUDERS < 2 SERIAL_ECHOLNPAIR("Advance K=", planner.extruder_advance_K[0]); #else SERIAL_ECHOPGM("Advance K"); LOOP_L_N(i, EXTRUDERS) { SERIAL_CHAR(' '); SERIAL_ECHO(int(i)); SERIAL_CHAR('='); SERIAL_ECHO(planner.extruder_advance_K[i]); } SERIAL_EOL(); #endif }
void prt_hex_nibble(unsigned int n) { if (n <= 9) SERIAL_ECHO(n); else SERIAL_ECHO((char)('A' + n - 10)); delay(2); }
static void serial_echo_xy(const int16_t x, const int16_t y) { SERIAL_CHAR('('); SERIAL_ECHO(x); SERIAL_CHAR(','); SERIAL_ECHO(y); SERIAL_CHAR(')'); safe_delay(10); }
void PrintJobRecovery::debug(PGM_P const prefix) { serialprintPGM(prefix); SERIAL_ECHOLNPAIR(" Job Recovery Info...\nvalid_head:", int(info.valid_head), " valid_foot:", int(info.valid_foot)); if (info.valid_head) { if (info.valid_head == info.valid_foot) { SERIAL_ECHOPGM("current_position: "); LOOP_XYZE(i) { SERIAL_ECHO(info.current_position[i]); if (i < E_AXIS) SERIAL_CHAR(','); } SERIAL_EOL(); SERIAL_ECHOLNPAIR("feedrate: ", info.feedrate); #if HOTENDS > 1 SERIAL_ECHOLNPAIR("active_hotend: ", int(info.active_hotend)); #endif SERIAL_ECHOPGM("target_temperature: "); HOTEND_LOOP() { SERIAL_ECHO(info.target_temperature[e]); if (e < HOTENDS - 1) SERIAL_CHAR(','); } SERIAL_EOL(); #if HAS_HEATED_BED SERIAL_ECHOLNPAIR("target_temperature_bed: ", info.target_temperature_bed); #endif #if FAN_COUNT SERIAL_ECHOPGM("fan_speed: "); FANS_LOOP(i) { SERIAL_ECHO(int(info.fan_speed[i])); if (i < FAN_COUNT - 1) SERIAL_CHAR(','); } SERIAL_EOL(); #endif #if HAS_LEVELING SERIAL_ECHOLNPAIR("leveling: ", int(info.leveling), "\n fade: ", int(info.fade)); #endif #if ENABLED(FWRETRACT) SERIAL_ECHOPGM("retract: "); for (int8_t e = 0; e < EXTRUDERS; e++) { SERIAL_ECHO(info.retract[e]); if (e < EXTRUDERS - 1) SERIAL_CHAR(','); } SERIAL_EOL(); SERIAL_ECHOLNPAIR("retract_hop: ", info.retract_hop); #endif SERIAL_ECHOLNPAIR("cmd_queue_index_r: ", int(info.cmd_queue_index_r)); SERIAL_ECHOLNPAIR("commands_in_queue: ", int(info.commands_in_queue)); for (uint8_t i = 0; i < info.commands_in_queue; i++) SERIAL_ECHOLNPAIR("> ", info.command_queue[i]); SERIAL_ECHOLNPAIR("sd_filename: ", info.sd_filename); SERIAL_ECHOLNPAIR("sdpos: ", info.sdpos); SERIAL_ECHOLNPAIR("print_job_elapsed: ", info.print_job_elapsed); } else
static void serial_echo_xy(const uint8_t sp, const int16_t x, const int16_t y) { SERIAL_ECHO_SP(sp); SERIAL_CHAR('('); if (x < 100) { SERIAL_CHAR(' '); if (x < 10) SERIAL_CHAR(' '); } SERIAL_ECHO(x); SERIAL_CHAR(','); if (y < 100) { SERIAL_CHAR(' '); if (y < 10) SERIAL_CHAR(' '); } SERIAL_ECHO(y); SERIAL_CHAR(')'); serial_delay(5); }
void automatic_current_control(TMC2130Stepper &st, String axisID) { // Check otpw even if we don't use automatic control. Allows for flag inspection. const bool is_otpw = st.checkOT(); // Report if a warning was triggered static bool previous_otpw = false; if (is_otpw && !previous_otpw) { char timestamp[10]; duration_t elapsed = print_job_timer.duration(); const bool has_days = (elapsed.value > 60*60*24L); (void)elapsed.toDigital(timestamp, has_days); SERIAL_ECHO(timestamp); SERIAL_ECHOPGM(": "); SERIAL_ECHO(axisID); SERIAL_ECHOLNPGM(" driver overtemperature warning!"); } previous_otpw = is_otpw; #if ENABLED(AUTOMATIC_CURRENT_CONTROL) && CURRENT_STEP > 0 // Return if user has not enabled current control start with M906 S1. if (!auto_current_control) return; /** * Decrease current if is_otpw is true. * Bail out if driver is disabled. * Increase current if OTPW has not been triggered yet. */ uint16_t current = st.getCurrent(); if (is_otpw) { st.setCurrent(current - CURRENT_STEP, R_SENSE, HOLD_MULTIPLIER); #if ENABLED(REPORT_CURRENT_CHANGE) SERIAL_ECHO(axisID); SERIAL_ECHOPAIR(" current decreased to ", st.getCurrent()); #endif } else if (!st.isEnabled()) return; else if (!is_otpw && !st.getOTPW()) { current += CURRENT_STEP; if (current <= AUTO_ADJUST_MAX) { st.setCurrent(current, R_SENSE, HOLD_MULTIPLIER); #if ENABLED(REPORT_CURRENT_CHANGE) SERIAL_ECHO(axisID); SERIAL_ECHOPAIR(" current increased to ", st.getCurrent()); #endif } } SERIAL_EOL(); #endif }
int check_for_free_memory_corruption(const char * const title) { SERIAL_ECHO(title); char *ptr = END_OF_HEAP(), *sp = top_of_stack(); int n = sp - ptr; SERIAL_ECHOPAIR("\nfmc() n=", n); SERIAL_ECHOPAIR("\n&__brkval: ", hex_address(&__brkval)); SERIAL_ECHOPAIR("=", hex_address(__brkval)); SERIAL_ECHOPAIR("\n__bss_end: ", hex_address(&__bss_end)); SERIAL_ECHOPAIR(" sp=", hex_address(sp)); if (sp < ptr) { SERIAL_ECHOPGM(" sp < Heap "); // SET_INPUT_PULLUP(63); // if the developer has a switch wired up to their controller board // safe_delay(5); // this code can be enabled to pause the display as soon as the // while ( READ(63)) // malfunction is detected. It is currently defaulting to a switch // idle(); // being on pin-63 which is unassigend and available on most controller // safe_delay(20); // boards. // while ( !READ(63)) // idle(); safe_delay(20); #ifdef M100_FREE_MEMORY_DUMPER M100_dump_routine(" Memory corruption detected with sp<Heap\n", (char*)0x1B80, (char*)0x21FF); #endif } // Scan through the range looking for the biggest block of 0xE5's we can find int block_cnt = 0; for (int i = 0; i < n; i++) { if (ptr[i] == TEST_BYTE) { int16_t j = count_test_bytes(ptr + i); if (j > 8) { // SERIAL_ECHOPAIR("Found ", j); // SERIAL_ECHOLNPAIR(" bytes free at ", hex_address(ptr + i)); i += j; block_cnt++; SERIAL_ECHOPAIR(" (", block_cnt); SERIAL_ECHOPAIR(") found=", j); SERIAL_ECHOPGM(" "); } } } SERIAL_ECHOPAIR(" block_found=", block_cnt); if (block_cnt != 1 || __brkval != 0x0000) SERIAL_ECHOLNPGM("\nMemory Corruption detected in free memory area."); if (block_cnt == 0) // Make sure the special case of no free blocks shows up as an block_cnt = -1; // error to the calling code! SERIAL_ECHOPGM(" return="); if (block_cnt == 1) { SERIAL_CHAR('0'); // if the block_cnt is 1, nothing has broken up the free memory SERIAL_EOL(); // area and it is appropriate to say 'no corruption'. return 0; } SERIAL_ECHOLNPGM("true"); return block_cnt; }
void checkHitEndstops() { if( endstop_x_hit || endstop_y_hit || endstop_z_hit) { SERIAL_ECHO_START; SERIAL_ECHO(MSG_ENDSTOPS_HIT); if(endstop_x_hit) { SERIAL_ECHOPAIR(" X:",(float)endstops_trigsteps[X_AXIS]/axis_steps_per_unit[X_AXIS]); } if(endstop_y_hit) { SERIAL_ECHOPAIR(" Y:",(float)endstops_trigsteps[Y_AXIS]/axis_steps_per_unit[Y_AXIS]); } if(endstop_z_hit) { SERIAL_ECHOPAIR(" Z:",(float)endstops_trigsteps[Z_AXIS]/axis_steps_per_unit[Z_AXIS]); } SERIAL_ECHOLN(""); endstop_x_hit=false; endstop_y_hit=false; endstop_z_hit=false; if (abort_on_endstop_hit){ quickStop(); Jawsboard::getBoard().setTargetDegExtruder(0,0); Jawsboard::getBoard().setTargetDegExtruder(0,1); Jawsboard::getBoard().setTargetDegExtruder(0,2); } } }
/*Crash1 - G29 to Probe and stop on Bed G29 will probe bed at least twice at 3 points and take an average. G30 will probe bed at it's current location. Z stop should be set slightly below bed height. Solder stub wire to each hole in huxley bed and attach a ring terminal under spring. Wire bed probe to A2 on Melzi and duplicate cap/resistor circuit in schematic. Use something like this in the start.gcode file: G29 ;Probe bed for Z height G92 Z0 ;Set Z to Probed Depth G1 Z5 F200 ;Lift Z out of way */ void probe_3points() { float Probe_Avg, Point1, Point2, Point3; Point1 = Probe_Bed(15,15,PROBE_N); Point2 = Probe_Bed(X_MAX_LENGTH - 20,15,PROBE_N) ; Point3 = Probe_Bed(X_MAX_LENGTH/2,Y_MAX_LENGTH - 5,PROBE_N); Probe_Avg = (Point1 + Point2 + Point3) / 3; //destination[2] = Probe_Avg; //feedrate = homing_feedrate[Z_AXIS]; //prepare_move(); SERIAL_ECHOLN("**************************************"); SERIAL_ECHO("Point1 ="); SERIAL_ECHOLN(Point1); SERIAL_ECHO("Point2 ="); SERIAL_ECHOLN(Point2); SERIAL_ECHO("Point3 ="); SERIAL_ECHOLN(Point3); SERIAL_ECHO("Probed Average="); SERIAL_ECHOLN(Probe_Avg); SERIAL_ECHOLN("**************************************"); }
/** * Extrapolate a single point from its neighbors */ static void extrapolate_one_point(const uint8_t x, const uint8_t y, const int8_t xdir, const int8_t ydir) { #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) { SERIAL_ECHOPGM("Extrapolate ["); if (x < 10) SERIAL_CHAR(' '); SERIAL_ECHO((int)x); SERIAL_CHAR(xdir ? (xdir > 0 ? '+' : '-') : ' '); SERIAL_CHAR(' '); if (y < 10) SERIAL_CHAR(' '); SERIAL_ECHO((int)y); SERIAL_CHAR(ydir ? (ydir > 0 ? '+' : '-') : ' '); SERIAL_CHAR(']'); } #endif if (!isnan(z_values[x][y])) { #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM(" (done)"); #endif return; // Don't overwrite good values. } SERIAL_EOL(); // Get X neighbors, Y neighbors, and XY neighbors const uint8_t x1 = x + xdir, y1 = y + ydir, x2 = x1 + xdir, y2 = y1 + ydir; float a1 = z_values[x1][y ], a2 = z_values[x2][y ], b1 = z_values[x ][y1], b2 = z_values[x ][y2], c1 = z_values[x1][y1], c2 = z_values[x2][y2]; // Treat far unprobed points as zero, near as equal to far if (isnan(a2)) a2 = 0.0; if (isnan(a1)) a1 = a2; if (isnan(b2)) b2 = 0.0; if (isnan(b1)) b1 = b2; if (isnan(c2)) c2 = 0.0; if (isnan(c1)) c1 = c2; const float a = 2 * a1 - a2, b = 2 * b1 - b2, c = 2 * c1 - c2; // Take the average instead of the median z_values[x][y] = (a + b + c) / 3.0; // Median is robust (ignores outliers). // z_values[x][y] = (a < b) ? ((b < c) ? b : (c < a) ? a : c) // : ((c < b) ? b : (a < c) ? a : c); }
static void serial_echo_column_labels(const uint8_t sp) { SERIAL_ECHO_SP(7); for (int8_t i = 0; i < GRID_MAX_POINTS_X; i++) { if (i < 10) SERIAL_CHAR(' '); SERIAL_ECHO(i); SERIAL_ECHO_SP(sp); } serial_delay(10); }
void print_xyz(PGM_P const prefix, PGM_P const suffix, const float x, const float y, const float z) { serialprintPGM(prefix); SERIAL_CHAR('('); SERIAL_ECHO(x); SERIAL_ECHOPAIR(", ", y); SERIAL_ECHOPAIR(", ", z); SERIAL_CHAR(')'); if (suffix) serialprintPGM(suffix); else SERIAL_EOL(); }
void probe_1point() { float Point; Point = Probe_Bed(-1,-1,PROBE_N); //destination[2] = Point +1; //feedrate = homing_feedrate[Z_AXIS]; //prepare_move(); SERIAL_ECHOLN("**************************************"); SERIAL_ECHO("Probed Z="); SERIAL_ECHOLN(Point); }
bool lcd_material_verify_material_settings() { SERIAL_ECHO_START; uint8_t max_mats = eeprom_read_byte(EEPROM_MATERIAL_COUNT_OFFSET()); SERIAL_ECHOPAIR("Checking ", (unsigned long) max_mats); SERIAL_ECHOPAIR(" presets and ", (unsigned long) EXTRUDERS); SERIAL_ECHOLNPGM (" extruder settings:"); if (max_mats < 2 || max_mats > EEPROM_MATERIAL_SETTINGS_MAX_COUNT) return false; byte cnt =0; while(cnt < max_mats+EXTRUDERS) { SERIAL_ECHOPAIR("Checking preset # ",(unsigned long) cnt); if (eeprom_read_word(EEPROM_MATERIAL_TEMPERATURE_OFFSET(cnt)) > HEATER_0_MAXTEMP) return false; if (eeprom_read_word(EEPROM_MATERIAL_BED_TEMPERATURE_OFFSET(cnt)) > BED_MAXTEMP) return false; if (eeprom_read_byte(EEPROM_MATERIAL_FAN_SPEED_OFFSET(cnt)) > 100) return false; if (eeprom_read_word(EEPROM_MATERIAL_FLOW_OFFSET(cnt)) > 1000) return false; if (eeprom_read_float(EEPROM_MATERIAL_DIAMETER_OFFSET(cnt)) > 10.0) return false; if (eeprom_read_float(EEPROM_MATERIAL_DIAMETER_OFFSET(cnt)) < 0.1) return false; strcpy_P (material_name_buf,"???"); eeprom_read_block(material_name_buf, EEPROM_MATERIAL_NAME_OFFSET(cnt), MATERIAL_NAME_LENGTH); material_name_buf[MATERIAL_NAME_LENGTH] = '\0'; if (cnt >= max_mats ) { SERIAL_ECHOPAIR(".....verified extruder setting # ",(unsigned long) cnt-max_mats);} else { SERIAL_ECHOPAIR(".....verified preset # ",(unsigned long) cnt);} SERIAL_ECHO(" ("); SERIAL_ECHO(material_name_buf); SERIAL_ECHO(")"); SERIAL_ECHOLN(""); cnt++; } return true; }
/** * Get a long pretty path based on a DOS 8.3 path */ void CardReader::printLongPath(char *path) { lsAction = LS_GetFilename; int i, pathLen = strlen(path); // SERIAL_ECHOPGM("Full Path: "); SERIAL_ECHOLN(path); // Zero out slashes to make segments for (i = 0; i < pathLen; i++) if (path[i] == '/') path[i] = '\0'; SdFile diveDir = root; // start from the root for segment 1 for (i = 0; i < pathLen;) { if (path[i] == '\0') i++; // move past a single nul char *segment = &path[i]; // The segment after most slashes // If a segment is empty (extra-slash) then exit if (!*segment) break; // Go to the next segment while (path[++i]) { } // SERIAL_ECHOPGM("Looking for segment: "); SERIAL_ECHOLN(segment); // Find the item, setting the long filename diveDir.rewind(); lsDive("", diveDir, segment); // Print /LongNamePart to serial output SERIAL_PROTOCOLCHAR('/'); SERIAL_PROTOCOL(longFilename[0] ? longFilename : "???"); // If the filename was printed then that's it if (!filenameIsDir) break; // SERIAL_ECHOPGM("Opening dir: "); SERIAL_ECHOLN(segment); // Open the sub-item as the new dive parent SdFile dir; if (!dir.open(diveDir, segment, O_READ)) { SERIAL_EOL; SERIAL_ECHO_START; SERIAL_ECHOPGM(MSG_SD_CANT_OPEN_SUBDIR); SERIAL_ECHO(segment); break; } diveDir.close(); diveDir = dir; } // while i<pathLen SERIAL_EOL; }
void PrintCounter::showStats() { char buffer[21]; duration_t elapsed; SERIAL_PROTOCOLPGM(MSG_STATS); SERIAL_ECHOPGM("Prints: "); SERIAL_ECHO(this->data.totalPrints); SERIAL_ECHOPGM(", Finished: "); SERIAL_ECHO(this->data.finishedPrints); SERIAL_ECHOPGM(", Failed: "); // Note: Removes 1 from failures with an active counter SERIAL_ECHO(this->data.totalPrints - this->data.finishedPrints - ((this->isRunning() || this->isPaused()) ? 1 : 0)); SERIAL_EOL; SERIAL_PROTOCOLPGM(MSG_STATS); elapsed = this->data.printTime; elapsed.toString(buffer); SERIAL_ECHOPGM("Total time: "); SERIAL_ECHO(buffer); #if ENABLED(DEBUG_PRINTCOUNTER) SERIAL_ECHOPGM(" ("); SERIAL_ECHO(this->data.printTime); SERIAL_ECHOPGM(")"); #endif elapsed = this->data.longestPrint; elapsed.toString(buffer); SERIAL_ECHOPGM(", Longest job: "); SERIAL_ECHO(buffer); #if ENABLED(DEBUG_PRINTCOUNTER) SERIAL_ECHOPGM(" ("); SERIAL_ECHO(this->data.longestPrint); SERIAL_ECHOPGM(")"); #endif SERIAL_EOL; SERIAL_PROTOCOLPGM(MSG_STATS); SERIAL_ECHOPGM("Filament used: "); SERIAL_ECHO(this->data.filamentUsed / 1000); SERIAL_ECHOPGM("m"); SERIAL_EOL; }
void GCodeParser::debug() { SERIAL_ECHOPAIR("Command: ", command_ptr); SERIAL_ECHOPAIR(" (", command_letter); SERIAL_ECHO(codenum); SERIAL_ECHOLNPGM(")"); #if ENABLED(FASTER_GCODE_PARSER) SERIAL_ECHO(" args: \""); for (char c = 'A'; c <= 'Z'; ++c) if (seen(c)) { SERIAL_CHAR(c); SERIAL_CHAR(' '); } #else SERIAL_ECHOPAIR(" args: \"", command_args); #endif SERIAL_ECHOPGM("\""); if (string_arg) { SERIAL_ECHOPGM(" string: \""); SERIAL_ECHO(string_arg); SERIAL_CHAR('"'); } SERIAL_ECHOPGM("\n\n"); for (char c = 'A'; c <= 'Z'; ++c) { if (seen(c)) { SERIAL_ECHOPAIR("Code '", c); SERIAL_ECHOPGM("':"); if (has_value()) { SERIAL_ECHOPAIR("\n float: ", value_float()); SERIAL_ECHOPAIR("\n long: ", value_long()); SERIAL_ECHOPAIR("\n ulong: ", value_ulong()); SERIAL_ECHOPAIR("\n millis: ", value_millis()); SERIAL_ECHOPAIR("\n sec-ms: ", value_millis_from_seconds()); SERIAL_ECHOPAIR("\n int: ", value_int()); SERIAL_ECHOPAIR("\n ushort: ", value_ushort()); SERIAL_ECHOPAIR("\n byte: ", (int)value_byte()); SERIAL_ECHOPAIR("\n bool: ", (int)value_bool()); SERIAL_ECHOPAIR("\n linear: ", value_linear_units()); SERIAL_ECHOPAIR("\n celsius: ", value_celsius()); } else SERIAL_ECHOPGM(" (no value)"); SERIAL_ECHOPGM("\n\n"); } } }
void Mixer::normalize(const uint8_t tool_index) { float cmax = 0; #ifdef MIXER_NORMALIZER_DEBUG float csum = 0; #endif MIXER_STEPPER_LOOP(i) { const float v = collector[i]; NOLESS(cmax, v); #ifdef MIXER_NORMALIZER_DEBUG csum += v; #endif } #ifdef MIXER_NORMALIZER_DEBUG SERIAL_ECHOPGM("Mixer: Old relation : [ "); MIXER_STEPPER_LOOP(i) { SERIAL_ECHO_F(collector[i] / csum, 3); SERIAL_CHAR(' '); } SERIAL_ECHOLNPGM("]"); #endif // Scale all values so their maximum is COLOR_A_MASK const float scale = float(COLOR_A_MASK) / cmax; MIXER_STEPPER_LOOP(i) color[tool_index][i] = collector[i] * scale; #ifdef MIXER_NORMALIZER_DEBUG csum = 0; SERIAL_ECHOPGM("Mixer: Normalize to : [ "); MIXER_STEPPER_LOOP(i) { SERIAL_ECHO(uint16_t(color[tool_index][i])); SERIAL_CHAR(' '); csum += color[tool_index][i]; } SERIAL_ECHOLNPGM("]"); SERIAL_ECHOPGM("Mixer: New relation : [ "); MIXER_STEPPER_LOOP(i) { SERIAL_ECHO_F(uint16_t(color[tool_index][i]) / csum, 3); SERIAL_CHAR(' '); } SERIAL_ECHOLNPGM("]"); #endif #if ENABLED(GRADIENT_MIX) refresh_gradient(); #endif }
void debug_current_and_destination(const char *title) { // if the title message starts with a '!' it is so important, we are going to // ignore the status of the g26_debug_flag if (*title != '!' && !g26_debug_flag) return; const float de = destination[E_AXIS] - current_position[E_AXIS]; if (de == 0.0) return; // Printing moves only const float dx = destination[X_AXIS] - current_position[X_AXIS], dy = destination[Y_AXIS] - current_position[Y_AXIS], xy_dist = HYPOT(dx, dy); if (xy_dist == 0.0) return; SERIAL_ECHOPGM(" fpmm="); const float fpmm = de / xy_dist; SERIAL_ECHO_F(fpmm, 6); SERIAL_ECHOPGM(" current=( "); SERIAL_ECHO_F(current_position[X_AXIS], 6); SERIAL_ECHOPGM(", "); SERIAL_ECHO_F(current_position[Y_AXIS], 6); SERIAL_ECHOPGM(", "); SERIAL_ECHO_F(current_position[Z_AXIS], 6); SERIAL_ECHOPGM(", "); SERIAL_ECHO_F(current_position[E_AXIS], 6); SERIAL_ECHOPGM(" ) destination=( "); debug_echo_axis(X_AXIS); SERIAL_ECHOPGM(", "); debug_echo_axis(Y_AXIS); SERIAL_ECHOPGM(", "); debug_echo_axis(Z_AXIS); SERIAL_ECHOPGM(", "); debug_echo_axis(E_AXIS); SERIAL_ECHOPGM(" ) "); SERIAL_ECHO(title); SERIAL_EOL(); }
/** * M100 I * Init memory for the M100 tests. (Automatically applied on the first M100.) */ void init_free_memory(char *ptr, int16_t size) { SERIAL_ECHOLNPGM("Initializing free memory block.\n\n"); size -= 250; // -250 to avoid interrupt activity that's altered the stack. if (size < 0) { SERIAL_ECHOLNPGM("Unable to initialize.\n"); return; } ptr += 8; // move a few bytes away from the heap just because we don't want // to be altering memory that close to it. memset(ptr, TEST_BYTE, size); SERIAL_ECHO(size); SERIAL_ECHOLNPGM(" bytes of memory initialized.\n"); for (int16_t i = 0; i < size; i++) { if (ptr[i] != TEST_BYTE) { SERIAL_ECHOPAIR("? address : ", hex_address(ptr + i)); SERIAL_ECHOLNPAIR("=", hex_byte(ptr[i])); SERIAL_EOL(); } } }
void CardReader::openFile(char* name, bool read, bool replace_current/*=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) { SERIAL_ERROR_START; SERIAL_ERRORPGM("trying to call sub-gcode files with too many levels. MAX level is:"); SERIAL_ERRORLN(SD_PROCEDURE_DEPTH); kill(); return; } SERIAL_ECHO_START; SERIAL_ECHOPGM("SUBROUTINE CALL target:\""); SERIAL_ECHO(name); SERIAL_ECHOPGM("\" parent:\""); //store current filename and position getAbsFilename(filenames[file_subcall_ctr]); SERIAL_ECHO(filenames[file_subcall_ctr]); SERIAL_ECHOPGM("\" pos"); SERIAL_ECHOLN(sdpos); filespos[file_subcall_ctr] = sdpos; file_subcall_ctr++; } else { SERIAL_ECHO_START; SERIAL_ECHOPGM("Now doing file: "); SERIAL_ECHOLN(name); } file.close(); } else { //opening fresh file file_subcall_ctr = 0; //resetting procedure depth in case user cancels print while in procedure SERIAL_ECHO_START; SERIAL_ECHOPGM("Now fresh file: "); SERIAL_ECHOLN(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, '/'); //SERIAL_ECHO("start:");SERIAL_ECHOLN((int)(dirname_start - name)); //SERIAL_ECHO("end :");SERIAL_ECHOLN((int)(dirname_end - name)); 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; SERIAL_ECHOLN(subdirname); if (!myDir.open(curDir, subdirname, O_READ)) { SERIAL_PROTOCOLPGM(MSG_SD_OPEN_FILE_FAIL); SERIAL_PROTOCOL(subdirname); SERIAL_PROTOCOLCHAR('.'); return; } else { //SERIAL_ECHOLN("dive ok"); } curDir = &myDir; dirname_start = dirname_end + 1; } else { // the remainder after all /fsa/fdsa/ is the filename fname = dirname_start; //SERIAL_ECHOLN("remainder"); //SERIAL_ECHOLN(fname); break; } } } else { //relative path curDir = &workDir; } if (read) { if (file.open(curDir, fname, O_READ)) { filesize = file.fileSize(); SERIAL_PROTOCOLPGM(MSG_SD_FILE_OPENED); SERIAL_PROTOCOL(fname); SERIAL_PROTOCOLPGM(MSG_SD_SIZE); SERIAL_PROTOCOLLN(filesize); sdpos = 0; SERIAL_PROTOCOLLNPGM(MSG_SD_FILE_SELECTED); getfilename(0, fname); lcd_setstatus(longFilename[0] ? longFilename : fname); } else { SERIAL_PROTOCOLPGM(MSG_SD_OPEN_FILE_FAIL); SERIAL_PROTOCOL(fname); SERIAL_PROTOCOLCHAR('.'); } } else { //write if (!file.open(curDir, fname, O_CREAT | O_APPEND | O_WRITE | O_TRUNC)) { SERIAL_PROTOCOLPGM(MSG_SD_OPEN_FILE_FAIL); SERIAL_PROTOCOL(fname); SERIAL_PROTOCOLCHAR('.'); } else { saving = true; SERIAL_PROTOCOLPGM(MSG_SD_WRITE_TO_FILE); SERIAL_PROTOCOLLN(name); lcd_setstatus(fname); } } }
void Config_RetrieveSettings() { int i = EEPROM_OFFSET; char stored_ver[4]; char ver[4] = EEPROM_VERSION; EEPROM_READ_VAR(i, stored_ver); //read stored version // SERIAL_ECHOLN("Version: [" << ver << "] Stored version: [" << stored_ver << "]"); if (strncmp(ver, stored_ver, 3) != 0) { Config_ResetDefault(); } else { float dummy = 0; // version number match EEPROM_READ_VAR(i, axis_steps_per_unit); EEPROM_READ_VAR(i, max_feedrate); EEPROM_READ_VAR(i, max_acceleration_units_per_sq_second); // steps per sq second need to be updated to agree with the units per sq second (as they are what is used in the planner) reset_acceleration_rates(); EEPROM_READ_VAR(i, acceleration); EEPROM_READ_VAR(i, retract_acceleration); EEPROM_READ_VAR(i, travel_acceleration); EEPROM_READ_VAR(i, minimumfeedrate); EEPROM_READ_VAR(i, mintravelfeedrate); EEPROM_READ_VAR(i, minsegmenttime); EEPROM_READ_VAR(i, max_xy_jerk); EEPROM_READ_VAR(i, max_z_jerk); EEPROM_READ_VAR(i, max_e_jerk); EEPROM_READ_VAR(i, home_offset); uint8_t dummy_uint8 = 0, mesh_num_x = 0, mesh_num_y = 0; EEPROM_READ_VAR(i, dummy_uint8); EEPROM_READ_VAR(i, mesh_num_x); EEPROM_READ_VAR(i, mesh_num_y); #if ENABLED(MESH_BED_LEVELING) mbl.active = dummy_uint8; if (mesh_num_x == MESH_NUM_X_POINTS && mesh_num_y == MESH_NUM_Y_POINTS) { EEPROM_READ_VAR(i, mbl.z_values); } else { mbl.reset(); for (int q = 0; q < mesh_num_x * mesh_num_y; q++) EEPROM_READ_VAR(i, dummy); } #else for (int q = 0; q < mesh_num_x * mesh_num_y; q++) EEPROM_READ_VAR(i, dummy); #endif // MESH_BED_LEVELING #if DISABLED(AUTO_BED_LEVELING_FEATURE) float zprobe_zoffset = 0; #endif EEPROM_READ_VAR(i, zprobe_zoffset); #if ENABLED(DELTA) EEPROM_READ_VAR(i, endstop_adj); // 3 floats EEPROM_READ_VAR(i, delta_radius); // 1 float EEPROM_READ_VAR(i, delta_diagonal_rod); // 1 float EEPROM_READ_VAR(i, delta_segments_per_second); // 1 float #elif ENABLED(Z_DUAL_ENDSTOPS) EEPROM_READ_VAR(i, z_endstop_adj); dummy = 0.0f; for (int q=5; q--;) EEPROM_READ_VAR(i, dummy); #else dummy = 0.0f; for (int q=6; q--;) EEPROM_READ_VAR(i, dummy); #endif #if DISABLED(ULTIPANEL) int plaPreheatHotendTemp, plaPreheatHPBTemp, plaPreheatFanSpeed, absPreheatHotendTemp, absPreheatHPBTemp, absPreheatFanSpeed; #endif EEPROM_READ_VAR(i, plaPreheatHotendTemp); EEPROM_READ_VAR(i, plaPreheatHPBTemp); EEPROM_READ_VAR(i, plaPreheatFanSpeed); EEPROM_READ_VAR(i, absPreheatHotendTemp); EEPROM_READ_VAR(i, absPreheatHPBTemp); EEPROM_READ_VAR(i, absPreheatFanSpeed); #if ENABLED(PIDTEMP) for (int e = 0; e < 4; e++) { // 4 = max extruders currently supported by Marlin EEPROM_READ_VAR(i, dummy); // Kp if (e < EXTRUDERS && dummy != DUMMY_PID_VALUE) { // do not need to scale PID values as the values in EEPROM are already scaled PID_PARAM(Kp, e) = dummy; EEPROM_READ_VAR(i, PID_PARAM(Ki, e)); EEPROM_READ_VAR(i, PID_PARAM(Kd, e)); #if ENABLED(PID_ADD_EXTRUSION_RATE) EEPROM_READ_VAR(i, PID_PARAM(Kc, e)); #else EEPROM_READ_VAR(i, dummy); #endif } else { for (int q=3; q--;) EEPROM_READ_VAR(i, dummy); // Ki, Kd, Kc } } #else // !PIDTEMP // 4 x 4 = 16 slots for PID parameters for (int q=16; q--;) EEPROM_READ_VAR(i, dummy); // 4x Kp, Ki, Kd, Kc #endif // !PIDTEMP #if DISABLED(PID_ADD_EXTRUSION_RATE) int lpq_len; #endif EEPROM_READ_VAR(i, lpq_len); #if DISABLED(PIDTEMPBED) float bedKp, bedKi, bedKd; #endif EEPROM_READ_VAR(i, dummy); // bedKp if (dummy != DUMMY_PID_VALUE) { bedKp = dummy; UNUSED(bedKp); EEPROM_READ_VAR(i, bedKi); EEPROM_READ_VAR(i, bedKd); } else { for (int q=2; q--;) EEPROM_READ_VAR(i, dummy); // bedKi, bedKd } #if DISABLED(HAS_LCD_CONTRAST) int lcd_contrast; #endif EEPROM_READ_VAR(i, lcd_contrast); #if ENABLED(SCARA) EEPROM_READ_VAR(i, axis_scaling); // 3 floats #else EEPROM_READ_VAR(i, dummy); #endif #if ENABLED(FWRETRACT) EEPROM_READ_VAR(i, autoretract_enabled); EEPROM_READ_VAR(i, retract_length); #if EXTRUDERS > 1 EEPROM_READ_VAR(i, retract_length_swap); #else EEPROM_READ_VAR(i, dummy); #endif EEPROM_READ_VAR(i, retract_feedrate); EEPROM_READ_VAR(i, retract_zlift); EEPROM_READ_VAR(i, retract_recover_length); #if EXTRUDERS > 1 EEPROM_READ_VAR(i, retract_recover_length_swap); #else EEPROM_READ_VAR(i, dummy); #endif EEPROM_READ_VAR(i, retract_recover_feedrate); #endif // FWRETRACT EEPROM_READ_VAR(i, volumetric_enabled); for (int q = 0; q < 4; q++) { EEPROM_READ_VAR(i, dummy); if (q < EXTRUDERS) filament_size[q] = dummy; } calculate_volumetric_multipliers(); // Call updatePID (similar to when we have processed M301) updatePID(); // Report settings retrieved and length SERIAL_ECHO_START; SERIAL_ECHO(ver); SERIAL_ECHOPAIR(" stored settings retrieved (", (unsigned long)i); SERIAL_ECHOLNPGM(" bytes)"); } #if ENABLED(EEPROM_CHITCHAT) Config_PrintSettings(); #endif }
void gcode_M100() { static int m100_not_initialized = 1; unsigned char* sp, *ptr; int i, j, n; // // M100 D dumps the free memory block from __brkval to the stack pointer. // malloc() eats memory from the start of the block and the stack grows // up from the bottom of the block. Solid 0xE5's indicate nothing has // used that memory yet. There should not be anything but 0xE5's within // the block of 0xE5's. If there is, that would indicate memory corruption // probably caused by bad pointers. Any unexpected values will be flagged in // the right hand column to help spotting them. // #if ENABLED(M100_FREE_MEMORY_DUMPER) // Disable to remove Dump sub-command if (code_seen('D')) { ptr = (unsigned char*) __brkval; // // We want to start and end the dump on a nice 16 byte boundry even though // the values we are using are not 16 byte aligned. // SERIAL_ECHOPGM("\n__brkval : "); prt_hex_word((unsigned int) ptr); ptr = (unsigned char*)((unsigned long) ptr & 0xfff0); sp = top_of_stack(); SERIAL_ECHOPGM("\nStack Pointer : "); prt_hex_word((unsigned int) sp); SERIAL_ECHOPGM("\n"); sp = (unsigned char*)((unsigned long) sp | 0x000f); n = sp - ptr; // // This is the main loop of the Dump command. // while (ptr < sp) { prt_hex_word((unsigned int) ptr); // Print the address SERIAL_ECHOPGM(":"); for (i = 0; i < 16; i++) { // and 16 data bytes prt_hex_byte(*(ptr + i)); SERIAL_ECHOPGM(" "); delay(2); } SERIAL_ECHO("|"); // now show where non 0xE5's are for (i = 0; i < 16; i++) { delay(2); if (*(ptr + i) == 0xe5) SERIAL_ECHOPGM(" "); else SERIAL_ECHOPGM("?"); } SERIAL_ECHO("\n"); ptr += 16; delay(2); } SERIAL_ECHOLNPGM("Done.\n"); return; } #endif // // M100 F requests the code to return the number of free bytes in the memory pool along with // other vital statistics that define the memory pool. // if (code_seen('F')) { int max_addr = (int) __brkval; int max_cnt = 0; int block_cnt = 0; ptr = (unsigned char*) __brkval; sp = top_of_stack(); n = sp - ptr; // Scan through the range looking for the biggest block of 0xE5's we can find for (i = 0; i < n; i++) { if (*(ptr + i) == (unsigned char) 0xe5) { j = how_many_E5s_are_here((unsigned char*) ptr + i); if (j > 8) { SERIAL_ECHOPAIR("Found ", j); SERIAL_ECHOPGM(" bytes free at 0x"); prt_hex_word((int) ptr + i); SERIAL_ECHOPGM("\n"); i += j; block_cnt++; } if (j > max_cnt) { // We don't do anything with this information yet max_cnt = j; // but we do know where the biggest free memory block is. max_addr = (int) ptr + i; } } } if (block_cnt > 1) SERIAL_ECHOLNPGM("\nMemory Corruption detected in free memory area.\n"); SERIAL_ECHO("\nDone.\n"); return; } // // M100 C x Corrupts x locations in the free memory pool and reports the locations of the corruption. // This is useful to check the correctness of the M100 D and the M100 F commands. // #if ENABLED(M100_FREE_MEMORY_CORRUPTOR) if (code_seen('C')) { int x; // x gets the # of locations to corrupt within the memory pool x = code_value(); SERIAL_ECHOLNPGM("Corrupting free memory block.\n"); ptr = (unsigned char*) __brkval; SERIAL_ECHOPAIR("\n__brkval : ", (long) ptr); ptr += 8; sp = top_of_stack(); SERIAL_ECHOPAIR("\nStack Pointer : ", (long) sp); SERIAL_ECHOLNPGM("\n"); n = sp - ptr - 64; // -64 just to keep us from finding interrupt activity that // has altered the stack. j = n / (x + 1); for (i = 1; i <= x; i++) { *(ptr + (i * j)) = i; SERIAL_ECHO("\nCorrupting address: 0x"); prt_hex_word((unsigned int)(ptr + (i * j))); } SERIAL_ECHOLNPGM("\n"); return; } #endif // // M100 I Initializes the free memory pool so it can be watched and prints vital // statistics that define the free memory pool. // if (m100_not_initialized || code_seen('I')) { // If no sub-command is specified, the first time SERIAL_ECHOLNPGM("Initializing free memory block.\n"); // this happens, it will Initialize. ptr = (unsigned char*) __brkval; // Repeated M100 with no sub-command will not destroy the SERIAL_ECHOPAIR("\n__brkval : ", (long) ptr); // state of the initialized free memory pool. ptr += 8; sp = top_of_stack(); SERIAL_ECHOPAIR("\nStack Pointer : ", (long) sp); SERIAL_ECHOLNPGM("\n"); n = sp - ptr - 64; // -64 just to keep us from finding interrupt activity that // has altered the stack. SERIAL_ECHO(n); SERIAL_ECHOLNPGM(" bytes of memory initialized.\n"); for (i = 0; i < n; i++) *(ptr + i) = (unsigned char) 0xe5; for (i = 0; i < n; i++) { if (*(ptr + i) != (unsigned char) 0xe5) { SERIAL_ECHOPAIR("? address : ", (unsigned long) ptr + i); SERIAL_ECHOPAIR("=", *(ptr + i)); SERIAL_ECHOLNPGM("\n"); } } m100_not_initialized = 0; SERIAL_ECHOLNPGM("Done.\n"); return; } return; }
/** * M420: Enable/Disable Bed Leveling and/or set the Z fade height. * * S[bool] Turns leveling on or off * Z[height] Sets the Z fade height (0 or none to disable) * V[bool] Verbose - Print the leveling grid * * With AUTO_BED_LEVELING_UBL only: * * L[index] Load UBL mesh from index (0 is default) * T[map] 0:Human-readable 1:CSV 2:"LCD" 4:Compact * * With mesh-based leveling only: * * C Center mesh on the mean of the lowest and highest * * With MARLIN_DEV_MODE: * S2 Create a simple random mesh and enable */ void GcodeSuite::M420() { const bool seen_S = parser.seen('S'), to_enable = seen_S ? parser.value_bool() : planner.leveling_active; #if ENABLED(MARLIN_DEV_MODE) if (parser.intval('S') == 2) { #if ENABLED(AUTO_BED_LEVELING_BILINEAR) bilinear_start[X_AXIS] = MIN_PROBE_X; bilinear_start[Y_AXIS] = MIN_PROBE_Y; bilinear_grid_spacing[X_AXIS] = (MAX_PROBE_X - (MIN_PROBE_X)) / (GRID_MAX_POINTS_X - 1); bilinear_grid_spacing[Y_AXIS] = (MAX_PROBE_Y - (MIN_PROBE_Y)) / (GRID_MAX_POINTS_Y - 1); #endif for (uint8_t x = 0; x < GRID_MAX_POINTS_X; x++) for (uint8_t y = 0; y < GRID_MAX_POINTS_Y; y++) Z_VALUES(x, y) = 0.001 * random(-200, 200); SERIAL_ECHOPGM("Simulated " STRINGIFY(GRID_MAX_POINTS_X) "x" STRINGIFY(GRID_MAX_POINTS_X) " mesh "); SERIAL_ECHOPAIR(" (", MIN_PROBE_X); SERIAL_CHAR(','); SERIAL_ECHO(MIN_PROBE_Y); SERIAL_ECHOPAIR(")-(", MAX_PROBE_X); SERIAL_CHAR(','); SERIAL_ECHO(MAX_PROBE_Y); SERIAL_ECHOLNPGM(")"); } #endif // If disabling leveling do it right away // (Don't disable for just M420 or M420 V) if (seen_S && !to_enable) set_bed_leveling_enabled(false); const float oldpos[] = { current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS] }; #if ENABLED(AUTO_BED_LEVELING_UBL) // L to load a mesh from the EEPROM if (parser.seen('L')) { set_bed_leveling_enabled(false); #if ENABLED(EEPROM_SETTINGS) const int8_t storage_slot = parser.has_value() ? parser.value_int() : ubl.storage_slot; const int16_t a = settings.calc_num_meshes(); if (!a) { SERIAL_ECHOLNPGM("?EEPROM storage not available."); return; } if (!WITHIN(storage_slot, 0, a - 1)) { SERIAL_ECHOLNPGM("?Invalid storage slot."); SERIAL_ECHOLNPAIR("?Use 0 to ", a - 1); return; } settings.load_mesh(storage_slot); ubl.storage_slot = storage_slot; #else SERIAL_ECHOLNPGM("?EEPROM storage not available."); return; #endif } // L or V display the map info if (parser.seen('L') || parser.seen('V')) { ubl.display_map(parser.byteval('T')); SERIAL_ECHOPGM("Mesh is "); if (!ubl.mesh_is_valid()) SERIAL_ECHOPGM("in"); SERIAL_ECHOLNPAIR("valid\nStorage slot: ", ubl.storage_slot); } #endif // AUTO_BED_LEVELING_UBL const bool seenV = parser.seen('V'); #if HAS_MESH if (leveling_is_valid()) { // Subtract the given value or the mean from all mesh values if (parser.seen('C')) { const float cval = parser.value_float(); #if ENABLED(AUTO_BED_LEVELING_UBL) set_bed_leveling_enabled(false); ubl.adjust_mesh_to_mean(true, cval); #else #if ENABLED(M420_C_USE_MEAN) // Get the sum and average of all mesh values float mesh_sum = 0; for (uint8_t x = GRID_MAX_POINTS_X; x--;) for (uint8_t y = GRID_MAX_POINTS_Y; y--;) mesh_sum += Z_VALUES(x, y); const float zmean = mesh_sum / float(GRID_MAX_POINTS); #else // Find the low and high mesh values float lo_val = 100, hi_val = -100; for (uint8_t x = GRID_MAX_POINTS_X; x--;) for (uint8_t y = GRID_MAX_POINTS_Y; y--;) { const float z = Z_VALUES(x, y); NOMORE(lo_val, z); NOLESS(hi_val, z); } // Take the mean of the lowest and highest const float zmean = (lo_val + hi_val) / 2.0 + cval; #endif // If not very close to 0, adjust the mesh if (!NEAR_ZERO(zmean)) { set_bed_leveling_enabled(false); // Subtract the mean from all values for (uint8_t x = GRID_MAX_POINTS_X; x--;) for (uint8_t y = GRID_MAX_POINTS_Y; y--;) Z_VALUES(x, y) -= zmean; #if ENABLED(ABL_BILINEAR_SUBDIVISION) bed_level_virt_interpolate(); #endif } #endif } } else if (to_enable || seenV) { SERIAL_ECHO_MSG("Invalid mesh."); goto EXIT_M420; } #endif // HAS_MESH // V to print the matrix or mesh if (seenV) { #if ABL_PLANAR planner.bed_level_matrix.debug(PSTR("Bed Level Correction Matrix:")); #else if (leveling_is_valid()) { #if ENABLED(AUTO_BED_LEVELING_BILINEAR) print_bilinear_leveling_grid(); #if ENABLED(ABL_BILINEAR_SUBDIVISION) print_bilinear_leveling_grid_virt(); #endif #elif ENABLED(MESH_BED_LEVELING) SERIAL_ECHOLNPGM("Mesh Bed Level data:"); mbl.report_mesh(); #endif } #endif } #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) if (parser.seen('Z')) set_z_fade_height(parser.value_linear_units(), false); #endif // Enable leveling if specified, or if previously active set_bed_leveling_enabled(to_enable); #if HAS_MESH EXIT_M420: #endif // Error if leveling failed to enable or reenable if (to_enable && !planner.leveling_active) SERIAL_ERROR_MSG(MSG_ERR_M420_FAILED); SERIAL_ECHO_START(); SERIAL_ECHOPGM("Bed Leveling "); serialprintln_onoff(planner.leveling_active); #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) SERIAL_ECHO_START(); SERIAL_ECHOPGM("Fade Height "); if (planner.z_fade_height > 0.0) SERIAL_ECHOLN(planner.z_fade_height); else SERIAL_ECHOLNPGM(MSG_OFF); #endif // Report change in position if (memcmp(oldpos, current_position, sizeof(oldpos))) report_current_position(); }
inline void invalid_extruder_error(const uint8_t e) { SERIAL_ECHO_START(); SERIAL_CHAR('T'); SERIAL_ECHO(int(e)); SERIAL_CHAR(' '); SERIAL_ECHOLNPGM(MSG_INVALID_EXTRUDER); }
/** * M48: Z probe repeatability measurement function. * * Usage: * M48 <P#> <X#> <Y#> <V#> <E> <L#> <S> * P = Number of sampled points (4-50, default 10) * X = Sample X position * Y = Sample Y position * V = Verbose level (0-4, default=1) * E = Engage Z probe for each reading * L = Number of legs of movement before probe * S = Schizoid (Or Star if you prefer) * * This function requires the machine to be homed before invocation. */ void GcodeSuite::M48() { if (axis_unhomed_error()) return; const int8_t verbose_level = parser.byteval('V', 1); if (!WITHIN(verbose_level, 0, 4)) { SERIAL_ECHOLNPGM("?(V)erbose level is implausible (0-4)."); return; } if (verbose_level > 0) SERIAL_ECHOLNPGM("M48 Z-Probe Repeatability Test"); const int8_t n_samples = parser.byteval('P', 10); if (!WITHIN(n_samples, 4, 50)) { SERIAL_ECHOLNPGM("?Sample size not plausible (4-50)."); return; } const ProbePtRaise raise_after = parser.boolval('E') ? PROBE_PT_STOW : PROBE_PT_RAISE; float X_current = current_position[X_AXIS], Y_current = current_position[Y_AXIS]; const float X_probe_location = parser.linearval('X', X_current + X_PROBE_OFFSET_FROM_EXTRUDER), Y_probe_location = parser.linearval('Y', Y_current + Y_PROBE_OFFSET_FROM_EXTRUDER); if (!position_is_reachable_by_probe(X_probe_location, Y_probe_location)) { SERIAL_ECHOLNPGM("? (X,Y) out of bounds."); return; } bool seen_L = parser.seen('L'); uint8_t n_legs = seen_L ? parser.value_byte() : 0; if (n_legs > 15) { SERIAL_ECHOLNPGM("?Number of legs in movement not plausible (0-15)."); return; } if (n_legs == 1) n_legs = 2; const bool schizoid_flag = parser.boolval('S'); if (schizoid_flag && !seen_L) n_legs = 7; /** * Now get everything to the specified probe point So we can safely do a * probe to get us close to the bed. If the Z-Axis is far from the bed, * we don't want to use that as a starting point for each probe. */ if (verbose_level > 2) SERIAL_ECHOLNPGM("Positioning the probe..."); // Disable bed level correction in M48 because we want the raw data when we probe #if HAS_LEVELING const bool was_enabled = planner.leveling_active; set_bed_leveling_enabled(false); #endif setup_for_endstop_or_probe_move(); float mean = 0.0, sigma = 0.0, min = 99999.9, max = -99999.9, sample_set[n_samples]; // Move to the first point, deploy, and probe const float t = probe_pt(X_probe_location, Y_probe_location, raise_after, verbose_level); bool probing_good = !isnan(t); if (probing_good) { randomSeed(millis()); for (uint8_t n = 0; n < n_samples; n++) { if (n_legs) { const int dir = (random(0, 10) > 5.0) ? -1 : 1; // clockwise or counter clockwise float angle = random(0, 360); const float radius = random( #if ENABLED(DELTA) (int) (0.1250000000 * (DELTA_PRINTABLE_RADIUS)), (int) (0.3333333333 * (DELTA_PRINTABLE_RADIUS)) #else (int) 5.0, (int) (0.125 * MIN(X_BED_SIZE, Y_BED_SIZE)) #endif ); if (verbose_level > 3) { SERIAL_ECHOPAIR("Starting radius: ", radius); SERIAL_ECHOPAIR(" angle: ", angle); SERIAL_ECHOPGM(" Direction: "); if (dir > 0) SERIAL_ECHOPGM("Counter-"); SERIAL_ECHOLNPGM("Clockwise"); } for (uint8_t l = 0; l < n_legs - 1; l++) { float delta_angle; if (schizoid_flag) // The points of a 5 point star are 72 degrees apart. We need to // skip a point and go to the next one on the star. delta_angle = dir * 2.0 * 72.0; else // If we do this line, we are just trying to move further // around the circle. delta_angle = dir * (float) random(25, 45); angle += delta_angle; while (angle > 360.0) // We probably do not need to keep the angle between 0 and 2*PI, but the angle -= 360.0; // Arduino documentation says the trig functions should not be given values while (angle < 0.0) // outside of this range. It looks like they behave correctly with angle += 360.0; // numbers outside of the range, but just to be safe we clamp them. X_current = X_probe_location - (X_PROBE_OFFSET_FROM_EXTRUDER) + cos(RADIANS(angle)) * radius; Y_current = Y_probe_location - (Y_PROBE_OFFSET_FROM_EXTRUDER) + sin(RADIANS(angle)) * radius; #if DISABLED(DELTA) X_current = constrain(X_current, X_MIN_POS, X_MAX_POS); Y_current = constrain(Y_current, Y_MIN_POS, Y_MAX_POS); #else // If we have gone out too far, we can do a simple fix and scale the numbers // back in closer to the origin. while (!position_is_reachable_by_probe(X_current, Y_current)) { X_current *= 0.8; Y_current *= 0.8; if (verbose_level > 3) { SERIAL_ECHOPAIR("Pulling point towards center:", X_current); SERIAL_ECHOLNPAIR(", ", Y_current); } } #endif if (verbose_level > 3) { SERIAL_ECHOPGM("Going to:"); SERIAL_ECHOPAIR(" X", X_current); SERIAL_ECHOPAIR(" Y", Y_current); SERIAL_ECHOLNPAIR(" Z", current_position[Z_AXIS]); } do_blocking_move_to_xy(X_current, Y_current); } // n_legs loop } // n_legs // Probe a single point sample_set[n] = probe_pt(X_probe_location, Y_probe_location, raise_after, 0); // Break the loop if the probe fails probing_good = !isnan(sample_set[n]); if (!probing_good) break; /** * Get the current mean for the data points we have so far */ float sum = 0.0; for (uint8_t j = 0; j <= n; j++) sum += sample_set[j]; mean = sum / (n + 1); NOMORE(min, sample_set[n]); NOLESS(max, sample_set[n]); /** * Now, use that mean to calculate the standard deviation for the * data points we have so far */ sum = 0.0; for (uint8_t j = 0; j <= n; j++) sum += sq(sample_set[j] - mean); sigma = SQRT(sum / (n + 1)); if (verbose_level > 0) { if (verbose_level > 1) { SERIAL_ECHO(n + 1); SERIAL_ECHOPAIR(" of ", (int)n_samples); SERIAL_ECHOPAIR_F(": z: ", sample_set[n], 3); if (verbose_level > 2) { SERIAL_ECHOPAIR_F(" mean: ", mean, 4); SERIAL_ECHOPAIR_F(" sigma: ", sigma, 6); SERIAL_ECHOPAIR_F(" min: ", min, 3); SERIAL_ECHOPAIR_F(" max: ", max, 3); SERIAL_ECHOPAIR_F(" range: ", max-min, 3); } SERIAL_EOL(); } } } // n_samples loop } STOW_PROBE(); if (probing_good) { SERIAL_ECHOLNPGM("Finished!"); if (verbose_level > 0) { SERIAL_ECHOPAIR_F("Mean: ", mean, 6); SERIAL_ECHOPAIR_F(" Min: ", min, 3); SERIAL_ECHOPAIR_F(" Max: ", max, 3); SERIAL_ECHOLNPAIR_F(" Range: ", max-min, 3); } SERIAL_ECHOLNPAIR_F("Standard Deviation: ", sigma, 6); SERIAL_EOL(); } clean_up_after_endstop_or_probe_move(); // Re-enable bed level correction if it had been on #if HAS_LEVELING set_bed_leveling_enabled(was_enabled); #endif report_current_position(); }
void serial_echopair_PGM(PGM_P const s_P, unsigned long v) { serialprintPGM(s_P); SERIAL_ECHO(v); }
void serial_echopair_PGM(PGM_P const s_P, double v) { serialprintPGM(s_P); SERIAL_ECHO(v); }
void serial_echopair_PGM(PGM_P const s_P, const char *v) { serialprintPGM(s_P); SERIAL_ECHO(v); }
void Config_StoreSettings() { float dummy = 0.0f; char ver[4] = "000"; int i = EEPROM_OFFSET; EEPROM_WRITE_VAR(i, ver); // invalidate data first EEPROM_WRITE_VAR(i, axis_steps_per_unit); EEPROM_WRITE_VAR(i, max_feedrate); EEPROM_WRITE_VAR(i, max_acceleration_units_per_sq_second); EEPROM_WRITE_VAR(i, acceleration); EEPROM_WRITE_VAR(i, retract_acceleration); EEPROM_WRITE_VAR(i, minimumfeedrate); EEPROM_WRITE_VAR(i, mintravelfeedrate); EEPROM_WRITE_VAR(i, minsegmenttime); EEPROM_WRITE_VAR(i, max_xy_jerk); EEPROM_WRITE_VAR(i, max_z_jerk); EEPROM_WRITE_VAR(i, max_e_jerk); EEPROM_WRITE_VAR(i, add_homing); #ifdef DELTA EEPROM_WRITE_VAR(i, endstop_adj); // 3 floats EEPROM_WRITE_VAR(i, delta_radius); // 1 float EEPROM_WRITE_VAR(i, delta_diagonal_rod); // 1 float EEPROM_WRITE_VAR(i, delta_segments_per_second); // 1 float #else dummy = 0.0f; for (int q=6; q--;) EEPROM_WRITE_VAR(i, dummy); #endif #ifndef ULTIPANEL int plaPreheatHotendTemp = PLA_PREHEAT_HOTEND_TEMP, plaPreheatHPBTemp = PLA_PREHEAT_HPB_TEMP, plaPreheatFanSpeed = PLA_PREHEAT_FAN_SPEED, absPreheatHotendTemp = ABS_PREHEAT_HOTEND_TEMP, absPreheatHPBTemp = ABS_PREHEAT_HPB_TEMP, absPreheatFanSpeed = ABS_PREHEAT_FAN_SPEED; #endif // !ULTIPANEL EEPROM_WRITE_VAR(i, plaPreheatHotendTemp); EEPROM_WRITE_VAR(i, plaPreheatHPBTemp); EEPROM_WRITE_VAR(i, plaPreheatFanSpeed); EEPROM_WRITE_VAR(i, absPreheatHotendTemp); EEPROM_WRITE_VAR(i, absPreheatHPBTemp); EEPROM_WRITE_VAR(i, absPreheatFanSpeed); EEPROM_WRITE_VAR(i, zprobe_zoffset); for (int e = 0; e < 4; e++) { #ifdef PIDTEMP if (e < EXTRUDERS) { EEPROM_WRITE_VAR(i, PID_PARAM(Kp, e)); EEPROM_WRITE_VAR(i, PID_PARAM(Ki, e)); EEPROM_WRITE_VAR(i, PID_PARAM(Kd, e)); #ifdef PID_ADD_EXTRUSION_RATE EEPROM_WRITE_VAR(i, PID_PARAM(Kc, e)); #else dummy = 1.0f; // 1.0 = default kc EEPROM_WRITE_VAR(i, dummy); #endif } else { #else // !PIDTEMP { #endif // !PIDTEMP dummy = DUMMY_PID_VALUE; EEPROM_WRITE_VAR(i, dummy); dummy = 0.0f; for (int q = 3; q--;) EEPROM_WRITE_VAR(i, dummy); } } // Extruders Loop #ifndef DOGLCD int lcd_contrast = 32; #endif EEPROM_WRITE_VAR(i, lcd_contrast); #ifdef SCARA EEPROM_WRITE_VAR(i, axis_scaling); // 3 floats #else dummy = 1.0f; EEPROM_WRITE_VAR(i, dummy); #endif #ifdef FWRETRACT EEPROM_WRITE_VAR(i, autoretract_enabled); EEPROM_WRITE_VAR(i, retract_length); #if EXTRUDERS > 1 EEPROM_WRITE_VAR(i, retract_length_swap); #else dummy = 0.0f; EEPROM_WRITE_VAR(i, dummy); #endif EEPROM_WRITE_VAR(i, retract_feedrate); EEPROM_WRITE_VAR(i, retract_zlift); EEPROM_WRITE_VAR(i, retract_recover_length); #if EXTRUDERS > 1 EEPROM_WRITE_VAR(i, retract_recover_length_swap); #else dummy = 0.0f; EEPROM_WRITE_VAR(i, dummy); #endif EEPROM_WRITE_VAR(i, retract_recover_feedrate); #endif // FWRETRACT EEPROM_WRITE_VAR(i, volumetric_enabled); // Save filament sizes for (int q = 0; q < 4; q++) { if (q < EXTRUDERS) dummy = filament_size[q]; EEPROM_WRITE_VAR(i, dummy); } int storageSize = i; char ver2[4] = EEPROM_VERSION; int j = EEPROM_OFFSET; EEPROM_WRITE_VAR(j, ver2); // validate data // Report storage size SERIAL_ECHO_START; SERIAL_ECHOPAIR("Settings Stored (", (unsigned long)i); SERIAL_ECHOLNPGM(" bytes)"); } void Config_RetrieveSettings() { int i = EEPROM_OFFSET; char stored_ver[4]; char ver[4] = EEPROM_VERSION; EEPROM_READ_VAR(i, stored_ver); //read stored version // SERIAL_ECHOLN("Version: [" << ver << "] Stored version: [" << stored_ver << "]"); if (strncmp(ver, stored_ver, 3) != 0) { Config_ResetDefault(); } else { float dummy = 0; // version number match EEPROM_READ_VAR(i, axis_steps_per_unit); EEPROM_READ_VAR(i, max_feedrate); EEPROM_READ_VAR(i, max_acceleration_units_per_sq_second); // steps per sq second need to be updated to agree with the units per sq second (as they are what is used in the planner) reset_acceleration_rates(); EEPROM_READ_VAR(i, acceleration); EEPROM_READ_VAR(i, retract_acceleration); EEPROM_READ_VAR(i, minimumfeedrate); EEPROM_READ_VAR(i, mintravelfeedrate); EEPROM_READ_VAR(i, minsegmenttime); EEPROM_READ_VAR(i, max_xy_jerk); EEPROM_READ_VAR(i, max_z_jerk); EEPROM_READ_VAR(i, max_e_jerk); EEPROM_READ_VAR(i, add_homing); #ifdef DELTA EEPROM_READ_VAR(i, endstop_adj); // 3 floats EEPROM_READ_VAR(i, delta_radius); // 1 float EEPROM_READ_VAR(i, delta_diagonal_rod); // 1 float EEPROM_READ_VAR(i, delta_segments_per_second); // 1 float #else for (int q=6; q--;) EEPROM_READ_VAR(i, dummy); #endif #ifndef ULTIPANEL int plaPreheatHotendTemp, plaPreheatHPBTemp, plaPreheatFanSpeed, absPreheatHotendTemp, absPreheatHPBTemp, absPreheatFanSpeed; #endif EEPROM_READ_VAR(i, plaPreheatHotendTemp); EEPROM_READ_VAR(i, plaPreheatHPBTemp); EEPROM_READ_VAR(i, plaPreheatFanSpeed); EEPROM_READ_VAR(i, absPreheatHotendTemp); EEPROM_READ_VAR(i, absPreheatHPBTemp); EEPROM_READ_VAR(i, absPreheatFanSpeed); EEPROM_READ_VAR(i, zprobe_zoffset); #ifdef PIDTEMP for (int e = 0; e < 4; e++) { // 4 = max extruders currently supported by Marlin EEPROM_READ_VAR(i, dummy); if (e < EXTRUDERS && dummy != DUMMY_PID_VALUE) { // do not need to scale PID values as the values in EEPROM are already scaled PID_PARAM(Kp, e) = dummy; EEPROM_READ_VAR(i, PID_PARAM(Ki, e)); EEPROM_READ_VAR(i, PID_PARAM(Kd, e)); #ifdef PID_ADD_EXTRUSION_RATE EEPROM_READ_VAR(i, PID_PARAM(Kc, e)); #else EEPROM_READ_VAR(i, dummy); #endif } else { for (int q=3; q--;) EEPROM_READ_VAR(i, dummy); // Ki, Kd, Kc } } #else // !PIDTEMP // 4 x 4 = 16 slots for PID parameters for (int q=16; q--;) EEPROM_READ_VAR(i, dummy); // 4x Kp, Ki, Kd, Kc #endif // !PIDTEMP #ifndef DOGLCD int lcd_contrast; #endif EEPROM_READ_VAR(i, lcd_contrast); #ifdef SCARA EEPROM_READ_VAR(i, axis_scaling); // 3 floats #else EEPROM_READ_VAR(i, dummy); #endif #ifdef FWRETRACT EEPROM_READ_VAR(i, autoretract_enabled); EEPROM_READ_VAR(i, retract_length); #if EXTRUDERS > 1 EEPROM_READ_VAR(i, retract_length_swap); #else EEPROM_READ_VAR(i, dummy); #endif EEPROM_READ_VAR(i, retract_feedrate); EEPROM_READ_VAR(i, retract_zlift); EEPROM_READ_VAR(i, retract_recover_length); #if EXTRUDERS > 1 EEPROM_READ_VAR(i, retract_recover_length_swap); #else EEPROM_READ_VAR(i, dummy); #endif EEPROM_READ_VAR(i, retract_recover_feedrate); #endif // FWRETRACT EEPROM_READ_VAR(i, volumetric_enabled); for (int q = 0; q < 4; q++) { EEPROM_READ_VAR(i, dummy); if (q < EXTRUDERS) filament_size[q] = dummy; } calculate_volumetric_multipliers(); // Call updatePID (similar to when we have processed M301) updatePID(); // Report settings retrieved and length SERIAL_ECHO_START; SERIAL_ECHO(ver); SERIAL_ECHOPAIR(" stored settings retrieved (", (unsigned long)i); SERIAL_ECHOLNPGM(" bytes)"); } #ifdef EEPROM_CHITCHAT Config_PrintSettings(); #endif }