marlin bugfix + SKR + BLTOUCH
This commit is contained in:
190
Marlin-bugfix-2.0.x/Marlin/src/gcode/bedlevel/G35.cpp
Normal file
190
Marlin-bugfix-2.0.x/Marlin/src/gcode/bedlevel/G35.cpp
Normal file
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(ASSISTED_TRAMMING)
|
||||
|
||||
#include "../gcode.h"
|
||||
#include "../../module/planner.h"
|
||||
#include "../../module/probe.h"
|
||||
#include "../../feature/bedlevel/bedlevel.h"
|
||||
|
||||
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
#include "../../core/debug_out.h"
|
||||
|
||||
constexpr xy_pos_t screws_tilt_adjust_pos[] = TRAMMING_POINT_XY;
|
||||
|
||||
static PGMSTR(point_name_1, TRAMMING_POINT_NAME_1);
|
||||
static PGMSTR(point_name_2, TRAMMING_POINT_NAME_2);
|
||||
static PGMSTR(point_name_3, TRAMMING_POINT_NAME_3);
|
||||
#ifdef TRAMMING_POINT_NAME_4
|
||||
static PGMSTR(point_name_4, TRAMMING_POINT_NAME_4);
|
||||
#ifdef TRAMMING_POINT_NAME_5
|
||||
static PGMSTR(point_name_5, TRAMMING_POINT_NAME_5);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static PGM_P const tramming_point_name[] PROGMEM = {
|
||||
point_name_1, point_name_2, point_name_3
|
||||
#ifdef TRAMMING_POINT_NAME_4
|
||||
, point_name_4
|
||||
#ifdef TRAMMING_POINT_NAME_5
|
||||
, point_name_5
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
#define G35_PROBE_COUNT COUNT(screws_tilt_adjust_pos)
|
||||
|
||||
#if !WITHIN(TRAMMING_SCREW_THREAD, 30, 51) || TRAMMING_SCREW_THREAD % 10 > 1
|
||||
#error "TRAMMING_SCREW_THREAD must be equal to 30, 31, 40, 41, 50, or 51."
|
||||
#endif
|
||||
|
||||
static_assert(G35_PROBE_COUNT > 2, "TRAMMING_POINT_XY requires at least 3 XY positions.");
|
||||
|
||||
/**
|
||||
* G35: Read bed corners to help adjust bed screws
|
||||
*
|
||||
* S<screw_thread>
|
||||
*
|
||||
* Screw thread: 30 - Clockwise M3
|
||||
* 31 - Counter-Clockwise M3
|
||||
* 40 - Clockwise M4
|
||||
* 41 - Counter-Clockwise M4
|
||||
* 50 - Clockwise M5
|
||||
* 51 - Counter-Clockwise M5
|
||||
**/
|
||||
void GcodeSuite::G35() {
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
DEBUG_ECHOLNPGM(">>> G35");
|
||||
log_machine_info();
|
||||
}
|
||||
|
||||
float z_measured[G35_PROBE_COUNT] = { 0 };
|
||||
|
||||
const uint8_t screw_thread = parser.byteval('S', TRAMMING_SCREW_THREAD);
|
||||
if (!WITHIN(screw_thread, 30, 51) || screw_thread % 10 > 1) {
|
||||
SERIAL_ECHOLNPGM("?(S)crew thread must be 30, 31, 40, 41, 50, or 51.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for planner moves to finish!
|
||||
planner.synchronize();
|
||||
|
||||
// Disable the leveling matrix before auto-aligning
|
||||
#if HAS_LEVELING
|
||||
TERN_(RESTORE_LEVELING_AFTER_G35, const bool leveling_was_active = planner.leveling_active);
|
||||
set_bed_leveling_enabled(false);
|
||||
#endif
|
||||
|
||||
#if ENABLED(CNC_WORKSPACE_PLANES)
|
||||
workspace_plane = PLANE_XY;
|
||||
#endif
|
||||
|
||||
// Always home with tool 0 active
|
||||
#if HAS_MULTI_HOTEND
|
||||
const uint8_t old_tool_index = active_extruder;
|
||||
tool_change(0, true);
|
||||
#endif
|
||||
|
||||
#if HAS_DUPLICATION_MODE
|
||||
extruder_duplication_enabled = false;
|
||||
#endif
|
||||
|
||||
// Home all before this procedure
|
||||
home_all_axes();
|
||||
|
||||
bool err_break = false;
|
||||
|
||||
// Probe all positions
|
||||
LOOP_L_N(i, G35_PROBE_COUNT) {
|
||||
|
||||
// In BLTOUCH HS mode, the probe travels in a deployed state.
|
||||
// Users of G35 might have a badly misaligned bed, so raise Z by the
|
||||
// length of the deployed pin (BLTOUCH stroke < 7mm)
|
||||
current_position.z = (Z_CLEARANCE_BETWEEN_PROBES) + (7 * ENABLED(BLTOUCH_HS_MODE));
|
||||
|
||||
const float z_probed_height = probe.probe_at_point(screws_tilt_adjust_pos[i], PROBE_PT_RAISE, 0, true);
|
||||
|
||||
if (isnan(z_probed_height)) {
|
||||
SERIAL_ECHOLNPAIR("G35 failed at point ", int(i), " (", tramming_point_name[i], ")"
|
||||
" X", screws_tilt_adjust_pos[i].x,
|
||||
" Y", screws_tilt_adjust_pos[i].y);
|
||||
err_break = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (DEBUGGING(LEVELING))
|
||||
DEBUG_ECHOLNPAIR("Probing point ", int(i), " (", tramming_point_name[i], ")"
|
||||
" X", screws_tilt_adjust_pos[i].x,
|
||||
" Y", screws_tilt_adjust_pos[i].y,
|
||||
" Z", z_probed_height);
|
||||
|
||||
z_measured[i] = z_probed_height;
|
||||
}
|
||||
|
||||
if (!err_break) {
|
||||
const float threads_factor[] = { 0.5, 0.7, 0.8 };
|
||||
|
||||
// Calculate adjusts
|
||||
LOOP_S_L_N(i, 1, G35_PROBE_COUNT) {
|
||||
const float diff = z_measured[0] - z_measured[i],
|
||||
adjust = abs(diff) < 0.001f ? 0 : diff / threads_factor[(screw_thread - 30) / 10];
|
||||
|
||||
const int full_turns = trunc(adjust);
|
||||
const float decimal_part = adjust - float(full_turns);
|
||||
const int minutes = trunc(decimal_part * 60.0f);
|
||||
|
||||
SERIAL_ECHOPAIR("Turn ", tramming_point_name[i],
|
||||
" ", (screw_thread & 1) == (adjust > 0) ? "Counter-Clockwise" : "Clockwise",
|
||||
"by ", abs(full_turns), " turns");
|
||||
if (minutes) SERIAL_ECHOPAIR(" and ", abs(minutes), " minutes");
|
||||
SERIAL_EOL();
|
||||
}
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOLNPGM("G35 aborted.");
|
||||
|
||||
// Restore the active tool after homing
|
||||
#if HAS_MULTI_HOTEND
|
||||
tool_change(old_tool_index, DISABLED(PARKING_EXTRUDER)); // Fetch previous toolhead if not PARKING_EXTRUDER
|
||||
#endif
|
||||
|
||||
#if BOTH(HAS_LEVELING, RESTORE_LEVELING_AFTER_G35)
|
||||
set_bed_leveling_enabled(leveling_was_active);
|
||||
#endif
|
||||
|
||||
// Stow the probe, as the last call to probe.probe_at_point(...) left
|
||||
// the probe deployed if it was successful.
|
||||
probe.stow();
|
||||
|
||||
// After this operation the Z position needs correction
|
||||
set_axis_not_trusted(Z_AXIS);
|
||||
|
||||
// Home Z after the alignment procedure
|
||||
process_subcommands_now_P(PSTR("G28Z"));
|
||||
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< G35");
|
||||
}
|
||||
|
||||
#endif // ASSISTED_TRAMMING
|
||||
@@ -60,6 +60,10 @@
|
||||
#include "../../../lcd/extui/ui_api.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(DWIN_CREALITY_LCD)
|
||||
#include "../../../lcd/dwin/dwin.h"
|
||||
#endif
|
||||
|
||||
#if HAS_MULTI_HOTEND
|
||||
#include "../../../module/tool_change.h"
|
||||
#endif
|
||||
@@ -888,6 +892,10 @@ G29_TYPE GcodeSuite::G29() {
|
||||
process_subcommands_now_P(PSTR(Z_PROBE_END_SCRIPT));
|
||||
#endif
|
||||
|
||||
#if ENABLED(DWIN_CREALITY_LCD)
|
||||
DWIN_CompletedLeveling();
|
||||
#endif
|
||||
|
||||
report_current_position();
|
||||
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< G29");
|
||||
|
||||
@@ -46,6 +46,9 @@
|
||||
#endif
|
||||
|
||||
#include "../../lcd/ultralcd.h"
|
||||
#if ENABLED(DWIN_CREALITY_LCD)
|
||||
#include "../../lcd/dwin/dwin.h"
|
||||
#endif
|
||||
|
||||
#if HAS_L64XX // set L6470 absolute position registers to counts
|
||||
#include "../../libs/L64XX/L64XX_Marlin.h"
|
||||
@@ -209,6 +212,8 @@ void GcodeSuite::G28() {
|
||||
log_machine_info();
|
||||
}
|
||||
|
||||
TERN_(DWIN_CREALITY_LCD, HMI_flag.home_flag = true);
|
||||
|
||||
#if ENABLED(DUAL_X_CARRIAGE)
|
||||
bool IDEX_saved_duplication_state = extruder_duplication_enabled;
|
||||
DualXMode IDEX_saved_mode = dual_x_carriage_mode;
|
||||
@@ -475,6 +480,8 @@ void GcodeSuite::G28() {
|
||||
|
||||
ui.refresh();
|
||||
|
||||
TERN_(DWIN_CREALITY_LCD, DWIN_CompletedHoming());
|
||||
|
||||
report_current_position();
|
||||
|
||||
if (ENABLED(NANODLP_Z_SYNC) && (doZ || ENABLED(NANODLP_ALL_AXIS)))
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
SERIAL_ECHOLNPGM("?L value out of range (0-20).");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
planner.calculate_volumetric_multipliers();
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ void GcodeSuite::M92() {
|
||||
if (wanted) {
|
||||
const float best = uint16_t(wanted / z_full_step_mm) * z_full_step_mm;
|
||||
SERIAL_ECHOPAIR(", best:[", best);
|
||||
if (best != wanted) { SERIAL_CHAR(','); SERIAL_ECHO(best + z_full_step_mm); }
|
||||
if (best != wanted) { SERIAL_CHAR(','); SERIAL_DECIMAL(best + z_full_step_mm); }
|
||||
SERIAL_CHAR(']');
|
||||
}
|
||||
SERIAL_ECHOLNPGM(" }");
|
||||
|
||||
@@ -67,8 +67,12 @@
|
||||
*/
|
||||
void GcodeSuite::M3_M4(const bool is_M4) {
|
||||
auto get_s_power = [] {
|
||||
if (parser.seen('S'))
|
||||
cutter.unitPower = cutter.power_to_range(cutter_power_t(round(parser.value_float())));
|
||||
if (parser.seenval('S')) {
|
||||
const float spwr = parser.value_float();
|
||||
cutter.unitPower = TERN(SPINDLE_LASER_PWM,
|
||||
cutter.power_to_range(cutter_power_t(round(spwr))),
|
||||
spwr > 0 ? 255 : 0);
|
||||
}
|
||||
else
|
||||
cutter.unitPower = cutter.cpwr_to_upwr(SPEED_POWER_STARTUP);
|
||||
return cutter.unitPower;
|
||||
@@ -86,7 +90,7 @@ void GcodeSuite::M3_M4(const bool is_M4) {
|
||||
else
|
||||
cutter.inline_power(cutter.upower_to_ocr(get_s_power()));
|
||||
#else
|
||||
cutter.inline_enabled(true);
|
||||
cutter.set_inline_enabled(true);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ void GcodeSuite::M900() {
|
||||
SERIAL_ECHOPGM("Advance K");
|
||||
LOOP_L_N(i, EXTRUDERS) {
|
||||
SERIAL_CHAR(' ', '0' + i, ':');
|
||||
SERIAL_ECHO(planner.extruder_advance_K[i]);
|
||||
SERIAL_DECIMAL(planner.extruder_advance_K[i]);
|
||||
}
|
||||
SERIAL_EOL();
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2019 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../../inc/MarlinConfig.h"
|
||||
|
||||
#if HAS_POWER_MONITOR
|
||||
|
||||
#include "../../../feature/power_monitor.h"
|
||||
#include "../../../Marlin.h"
|
||||
#include "../../gcode.h"
|
||||
|
||||
/**
|
||||
* M430: Enable/disable current LCD display
|
||||
* With no parameters report the system current draw (in Amps)
|
||||
*
|
||||
* I[bool] - Set Display of current on the LCD
|
||||
* V[bool] - Set Display of voltage on the LCD
|
||||
* W[bool] - Set Display of power on the LCD
|
||||
*/
|
||||
void GcodeSuite::M430() {
|
||||
bool do_report = true;
|
||||
#if HAS_SPI_LCD
|
||||
#if ENABLED(POWER_MONITOR_CURRENT)
|
||||
if (parser.seen('I')) { power_monitor.set_current_display(parser.value_bool()); do_report = false; }
|
||||
#endif
|
||||
#if HAS_POWER_MONITOR_VREF
|
||||
if (parser.seen('V')) { power_monitor.set_voltage_display(parser.value_bool()); do_report = false; }
|
||||
#endif
|
||||
#if HAS_POWER_MONITOR_WATTS
|
||||
if (parser.seen('W')) { power_monitor.set_power_display(parser.value_bool()); do_report = false; }
|
||||
#endif
|
||||
#endif
|
||||
if (do_report) {
|
||||
SERIAL_ECHOLNPAIR(
|
||||
#if ENABLED(POWER_MONITOR_CURRENT)
|
||||
"Current: ", power_monitor.getAmps(), "A"
|
||||
#if HAS_POWER_MONITOR_VREF
|
||||
" "
|
||||
#endif
|
||||
#endif
|
||||
#if HAS_POWER_MONITOR_VREF
|
||||
"Voltage: ", power_monitor.getVolts(), "V"
|
||||
#endif
|
||||
#if HAS_POWER_MONITOR_WATTS
|
||||
" Power: ", power_monitor.getPower(), "W"
|
||||
#endif
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAS_POWER_MONITOR
|
||||
@@ -179,8 +179,10 @@ void GcodeSuite::get_destination_from_command() {
|
||||
|
||||
#if ENABLED(LASER_MOVE_POWER)
|
||||
// Set the laser power in the planner to configure this move
|
||||
if (parser.seen('S'))
|
||||
cutter.inline_power(cutter.power_to_range(cutter_power_t(round(parser.value_float()))));
|
||||
if (parser.seen('S')) {
|
||||
const float spwr = parser.value_float();
|
||||
cutter.inline_power(TERN(SPINDLE_LASER_PWM, cutter.power_to_range(cutter_power_t(round(spwr))), spwr > 0 ? 255 : 0));
|
||||
}
|
||||
else if (ENABLED(LASER_MOVE_G0_OFF) && parser.codenum == 0) // G0
|
||||
cutter.set_inline_enabled(false);
|
||||
#endif
|
||||
@@ -319,6 +321,10 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {
|
||||
case 34: G34(); break; // G34: Z Stepper automatic alignment using probe
|
||||
#endif
|
||||
|
||||
#if ENABLED(ASSISTED_TRAMMING)
|
||||
case 35: G35(); break; // G35: Read four bed corners to help adjust bed screws
|
||||
#endif
|
||||
|
||||
#if ENABLED(G38_PROBE_TARGET)
|
||||
case 38: // G38.2, G38.3: Probe towards target
|
||||
if (WITHIN(parser.subcode, 2,
|
||||
@@ -718,6 +724,10 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {
|
||||
case 428: M428(); break; // M428: Apply current_position to home_offset
|
||||
#endif
|
||||
|
||||
#if HAS_POWER_MONITOR
|
||||
case 430: M430(); break; // M430: Read the system current (A), voltage (V), and power (W)
|
||||
#endif
|
||||
|
||||
#if ENABLED(CANCEL_OBJECTS)
|
||||
case 486: M486(); break; // M486: Identify and cancel objects
|
||||
#endif
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
* G32 - Undock sled (Z_PROBE_SLED only)
|
||||
* G33 - Delta Auto-Calibration (Requires DELTA_AUTO_CALIBRATION)
|
||||
* G34 - Z Stepper automatic alignment using probe: I<iterations> T<accuracy> A<amplification> (Requires Z_STEPPER_AUTO_ALIGN)
|
||||
* G35 - Read bed corners to help adjust bed screws: T<screw_thread> (Requires ASSISTED_TRAMMING)
|
||||
* G38 - Probe in any direction using the Z_MIN_PROBE (Requires G38_PROBE_TARGET)
|
||||
* G42 - Coordinated move to a mesh point (Requires MESH_BED_LEVELING, AUTO_BED_LEVELING_BLINEAR, or AUTO_BED_LEVELING_UBL)
|
||||
* G60 - Save current position. (Requires SAVED_POSITIONS)
|
||||
@@ -217,6 +218,7 @@
|
||||
* M422 - Set Z Stepper automatic alignment position using probe. X<units> Y<units> A<axis> (Requires Z_STEPPER_AUTO_ALIGN)
|
||||
* M425 - Enable/Disable and tune backlash correction. (Requires BACKLASH_COMPENSATION and BACKLASH_GCODE)
|
||||
* M428 - Set the home_offset based on the current_position. Nearest edge applies. (Disabled by NO_WORKSPACE_OFFSETS or DELTA)
|
||||
* M430 - Read the system current, voltage, and power (Requires POWER_MONITOR_CURRENT, POWER_MONITOR_VOLTAGE, or POWER_MONITOR_FIXED_VOLTAGE)
|
||||
* M486 - Identify and cancel objects. (Requires CANCEL_OBJECTS)
|
||||
* M500 - Store parameters in EEPROM. (Requires EEPROM_SETTINGS)
|
||||
* M501 - Restore parameters from EEPROM. (Requires EEPROM_SETTINGS)
|
||||
@@ -453,6 +455,8 @@ private:
|
||||
static void M422();
|
||||
#endif
|
||||
|
||||
TERN_(ASSISTED_TRAMMING, static void G35());
|
||||
|
||||
TERN_(G38_PROBE_TARGET, static void G38(const int8_t subcode));
|
||||
|
||||
TERN_(HAS_MESH, static void G42());
|
||||
@@ -735,6 +739,8 @@ private:
|
||||
|
||||
TERN_(HAS_M206_COMMAND, static void M428());
|
||||
|
||||
TERN_(HAS_POWER_MONITOR, static void M430());
|
||||
|
||||
TERN_(CANCEL_OBJECTS, static void M486());
|
||||
|
||||
static void M500();
|
||||
|
||||
@@ -115,6 +115,9 @@ void GcodeSuite::M115() {
|
||||
// MOTION_MODES (M80-M89)
|
||||
cap_line(PSTR("MOTION_MODES"), ENABLED(GCODE_MOTION_MODES));
|
||||
|
||||
// ARC_SUPPORT (G2-G3)
|
||||
cap_line(PSTR("ARCS"), ENABLED(ARC_SUPPORT));
|
||||
|
||||
// BABYSTEPPING (M290)
|
||||
cap_line(PSTR("BABYSTEPPING"), ENABLED(BABYSTEPPING));
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ void GcodeSuite::G30() {
|
||||
const ProbePtRaise raise_after = parser.boolval('E', true) ? PROBE_PT_STOW : PROBE_PT_NONE;
|
||||
const float measured_z = probe.probe_at_point(pos, raise_after, 1);
|
||||
if (!isnan(measured_z))
|
||||
SERIAL_ECHOLNPAIR("Bed X: ", FIXFLOAT(pos.x), " Y: ", FIXFLOAT(pos.y), " Z: ", FIXFLOAT(measured_z));
|
||||
SERIAL_ECHOLNPAIR("Bed X: ", pos.x, " Y: ", pos.y, " Z: ", measured_z);
|
||||
|
||||
restore_feedrate_and_scaling();
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
|
||||
#if ENABLED(PARK_HEAD_ON_PAUSE)
|
||||
#include "../../feature/pause.h"
|
||||
#include "../queue.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(HOST_ACTION_COMMANDS)
|
||||
@@ -98,7 +97,8 @@ void GcodeSuite::M25() {
|
||||
#endif
|
||||
|
||||
print_job_timer.pause();
|
||||
ui.reset_status();
|
||||
|
||||
TERN(DWIN_CREALITY_LCD,,ui.reset_status());
|
||||
|
||||
#if ENABLED(HOST_ACTION_COMMANDS)
|
||||
TERN_(HOST_PROMPT_SUPPORT, host_prompt_open(PROMPT_PAUSE_RESUME, PSTR("Pause SD"), PSTR("Resume")));
|
||||
|
||||
Reference in New Issue
Block a user