Skip to content

Commit 43d21d5

Browse files
committed
Keep smaller option
1 parent 70ecf6e commit 43d21d5

File tree

6 files changed

+284
-23
lines changed

6 files changed

+284
-23
lines changed

Marlin/Configuration.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,8 @@
686686
* @section mpctemp
687687
*/
688688
#if ENABLED(MPCTEMP)
689-
//#define MPC_AUTOTUNE // Include a method to do MPC auto-tuning (~7120 bytes of flash)
689+
//#define MPC_AUTOTUNE // Include a method to do MPC auto-tuning (~5664-5882 bytes of flash)
690+
//#define MPC_AUTOTUNE_FANCY // Include a fancier method to do MPC auto-tuning (~7120 bytes of flash)
690691
//#define MPC_EDIT_MENU // Add MPC editing to the "Advanced Settings" menu. (~1300 bytes of flash)
691692
//#define MPC_AUTOTUNE_MENU // Add MPC auto-tuning to the "Advanced Settings" menu. (~350 bytes of flash)
692693

Marlin/src/gcode/temp/M306.cpp

+21-16
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
*
4444
* With MPC_AUTOTUNE:
4545
* T Autotune the extruder specified with 'E' or the active extruder.
46+
*
47+
* With MPC_AUTOTUNE_FANCY:
4648
* S0 : Autotuning method AUTO (default)
4749
* S1 : Autotuning method DIFFERENTIAL
4850
* S2 : Autotuning method ASYMPTOTIC
@@ -55,23 +57,26 @@ void GcodeSuite::M306() {
5557
return;
5658
}
5759

58-
if (parser.seen_test('T')) {
59-
#if ENABLED(MPC_AUTOTUNE)
60-
Temperature::MPCTuningType tuning_type;
61-
const uint8_t type = parser.byteval('S', 0);
62-
switch (type) {
63-
case 1: tuning_type = Temperature::MPCTuningType::FORCE_DIFFERENTIAL; break;
64-
case 2: tuning_type = Temperature::MPCTuningType::FORCE_ASYMPTOTIC; break;
65-
default: tuning_type = Temperature::MPCTuningType::AUTO; break;
66-
}
67-
LCD_MESSAGE(MSG_MPC_AUTOTUNE);
68-
thermalManager.MPC_autotune(e, tuning_type);
60+
#if EITHER(MPC_AUTOTUNE, MPC_AUTOTUNE_FANCY)
61+
if (parser.seen_test('T')) {
62+
#if ENABLED(MPC_AUTOTUNE_FANCY)
63+
Temperature::MPCTuningType tuning_type;
64+
const uint8_t type = parser.byteval('S', 0);
65+
switch (type) {
66+
case 1: tuning_type = Temperature::MPCTuningType::FORCE_DIFFERENTIAL; break;
67+
case 2: tuning_type = Temperature::MPCTuningType::FORCE_ASYMPTOTIC; break;
68+
default: tuning_type = Temperature::MPCTuningType::AUTO; break;
69+
}
70+
LCD_MESSAGE(MSG_MPC_AUTOTUNE);
71+
thermalManager.MPC_autotune(e, tuning_type);
72+
#else
73+
LCD_MESSAGE(MSG_MPC_AUTOTUNE);
74+
thermalManager.MPC_autotune(e);
75+
#endif
6976
ui.reset_status();
70-
#else
71-
SERIAL_ECHOLNPGM("M306 T requires MPC_AUTOTUNE.");
72-
#endif
73-
return;
74-
}
77+
return;
78+
}
79+
#endif
7580

7681
if (parser.seen("ACFPRH")) {
7782
MPC_t &mpc = thermalManager.temp_hotend[e].mpc;

Marlin/src/inc/SanityCheck.h

+5
Original file line numberDiff line numberDiff line change
@@ -976,9 +976,14 @@ static_assert(COUNT(arm) == LOGICAL_AXES, "AXIS_RELATIVE_MODES must contain " _L
976976
#error "Only enable PIDTEMP or MPCTEMP, but not both."
977977
#undef MPCTEMP
978978
#undef MPC_AUTOTUNE
979+
#undef MPC_AUTOTUNE_FANCY
979980
#undef MPC_EDIT_MENU
980981
#undef MPC_AUTOTUNE_MENU
981982
#endif
983+
#if BOTH(MPC_AUTOTUNE, MPC_AUTOTUNE_FANCY)
984+
#error "Only enable MPC_AUTOTUNE or MPC_AUTOTUNE_FANCY, but not both."
985+
#undef MPC_AUTOTUNE_FANCY
986+
#endif
982987

983988
#if ENABLED(MPC_INCLUDE_FAN)
984989
#if !HAS_FAN

Marlin/src/module/temperature.cpp

+248-3
Original file line numberDiff line numberDiff line change
@@ -938,7 +938,7 @@ volatile bool Temperature::raw_temps_ready = false;
938938

939939
#endif // HAS_PID_HEATING
940940

941-
#if ENABLED(MPC_AUTOTUNE)
941+
#if ENABLED(MPC_AUTOTUNE_FANCY)
942942

943943
#if EITHER(MPC_FAN_0_ALL_HOTENDS, MPC_FAN_0_ACTIVE_HOTEND)
944944
#define SINGLEFAN 1
@@ -1041,7 +1041,8 @@ volatile bool Temperature::raw_temps_ready = false;
10411041

10421042
next_test_time_ms += test_interval_ms;
10431043

1044-
} else if (current_temp < 200.0f) {
1044+
}
1045+
else if (current_temp < 200.0f) {
10451046
// Second regime (after 100deg) measure 3 points to determine asymptotic temperature
10461047

10471048
// If there are too many samples, space them more widely
@@ -1059,7 +1060,8 @@ volatile bool Temperature::raw_temps_ready = false;
10591060

10601061
next_test_time_ms += test_interval_ms * sample_distance;
10611062

1062-
} else {
1063+
}
1064+
else {
10631065
// Third regime (after 200deg) finished gathering data so finish
10641066
break;
10651067
}
@@ -1302,6 +1304,249 @@ volatile bool Temperature::raw_temps_ready = false;
13021304
TERN_(HAS_FAN, SERIAL_ECHOLNPAIR_F("MPC_AMBIENT_XFER_COEFF_FAN255 ", ambient_xfer_coeff_fan255, 4));
13031305
}
13041306

1307+
#elif ENABLED(MPC_AUTOTUNE)
1308+
1309+
#if EITHER(MPC_FAN_0_ALL_HOTENDS, MPC_FAN_0_ACTIVE_HOTEND)
1310+
#define SINGLEFAN 1
1311+
#endif
1312+
1313+
void Temperature::MPC_autotune(const uint8_t e) {
1314+
auto housekeeping = [] (millis_t &ms, const uint8_t e, celsius_float_t &current_temp, millis_t &next_report_ms) {
1315+
ms = millis();
1316+
1317+
if (updateTemperaturesIfReady()) { // temp sample ready
1318+
current_temp = degHotend(e);
1319+
TERN_(HAS_FAN_LOGIC, manage_extruder_fans(ms));
1320+
}
1321+
1322+
if (ELAPSED(ms, next_report_ms)) {
1323+
next_report_ms += 1000UL;
1324+
1325+
print_heater_states(e);
1326+
SERIAL_EOL();
1327+
}
1328+
1329+
hal.idletask();
1330+
TERN(DWIN_CREALITY_LCD, DWIN_Update(), ui.update());
1331+
1332+
if (!wait_for_heatup) {
1333+
SERIAL_ECHOLNPGM(STR_MPC_AUTOTUNE_INTERRUPTED);
1334+
TERN_(DWIN_LCD_PROUI, DWIN_MPCTuning(MPC_INTERRUPTED));
1335+
return true;
1336+
}
1337+
1338+
return false;
1339+
};
1340+
1341+
struct OnExit {
1342+
uint8_t e;
1343+
OnExit(const uint8_t _e) { this->e = _e; }
1344+
~OnExit() {
1345+
wait_for_heatup = false;
1346+
1347+
ui.reset_status();
1348+
1349+
temp_hotend[e].target = 0.0f;
1350+
temp_hotend[e].soft_pwm_amount = 0;
1351+
#if HAS_FAN
1352+
set_fan_speed(TERN(SINGLEFAN, 0, e), 0);
1353+
planner.sync_fan_speeds(fan_speed);
1354+
#endif
1355+
1356+
do_z_clearance(MPC_TUNING_END_Z, false);
1357+
1358+
TERN_(TEMP_TUNING_MAINTAIN_FAN, adaptive_fan_slowing = true);
1359+
}
1360+
} on_exit(e);
1361+
1362+
SERIAL_ECHOLNPGM(STR_MPC_AUTOTUNE_START, e);
1363+
MPCHeaterInfo &hotend = temp_hotend[e];
1364+
MPC_t &mpc = hotend.mpc;
1365+
1366+
TERN_(TEMP_TUNING_MAINTAIN_FAN, adaptive_fan_slowing = false);
1367+
1368+
// Move to center of bed, just above bed height and cool with max fan
1369+
gcode.home_all_axes(true);
1370+
disable_all_heaters();
1371+
#if HAS_FAN
1372+
zero_fan_speeds();
1373+
set_fan_speed(TERN(SINGLEFAN, 0, e), 255);
1374+
planner.sync_fan_speeds(fan_speed);
1375+
#endif
1376+
do_blocking_move_to(xyz_pos_t(MPC_TUNING_POS));
1377+
1378+
SERIAL_ECHOLNPGM(STR_MPC_COOLING_TO_AMBIENT);
1379+
#if ENABLED(DWIN_LCD_PROUI)
1380+
DWIN_MPCTuning(MPCTEMP_START);
1381+
LCD_ALERTMESSAGE(MSG_MPC_COOLING_TO_AMBIENT);
1382+
#else
1383+
LCD_MESSAGE(MSG_COOLING);
1384+
#endif
1385+
1386+
millis_t ms = millis(), next_report_ms = ms, next_test_ms = ms + 10000UL;
1387+
celsius_float_t current_temp = degHotend(e),
1388+
ambient_temp = current_temp;
1389+
1390+
wait_for_heatup = true;
1391+
for (;;) { // Can be interrupted with M108
1392+
if (housekeeping(ms, e, current_temp, next_report_ms)) return;
1393+
1394+
if (ELAPSED(ms, next_test_ms)) {
1395+
if (current_temp >= ambient_temp) {
1396+
ambient_temp = (ambient_temp + current_temp) / 2.0f;
1397+
break;
1398+
}
1399+
ambient_temp = current_temp;
1400+
next_test_ms += 10000UL;
1401+
}
1402+
}
1403+
wait_for_heatup = false;
1404+
1405+
#if HAS_FAN
1406+
set_fan_speed(TERN(SINGLEFAN, 0, e), 0);
1407+
planner.sync_fan_speeds(fan_speed);
1408+
#endif
1409+
1410+
hotend.modeled_ambient_temp = ambient_temp;
1411+
1412+
SERIAL_ECHOLNPGM(STR_MPC_HEATING_PAST_200);
1413+
TERN(DWIN_LCD_PROUI, LCD_ALERTMESSAGE(MSG_MPC_HEATING_PAST_200), LCD_MESSAGE(MSG_HEATING));
1414+
hotend.target = 200.0f; // So M105 looks nice
1415+
hotend.soft_pwm_amount = (MPC_MAX) >> 1;
1416+
const millis_t heat_start_time = next_test_ms = ms;
1417+
celsius_float_t temp_samples[16];
1418+
uint8_t sample_count = 0;
1419+
uint16_t sample_distance = 1;
1420+
float t1_time = 0;
1421+
1422+
wait_for_heatup = true;
1423+
for (;;) { // Can be interrupted with M108
1424+
if (housekeeping(ms, e, current_temp, next_report_ms)) return;
1425+
1426+
if (ELAPSED(ms, next_test_ms)) {
1427+
// Record samples between 100C and 200C
1428+
if (current_temp >= 100.0f) {
1429+
// If there are too many samples, space them more widely
1430+
if (sample_count == COUNT(temp_samples)) {
1431+
for (uint8_t i = 0; i < COUNT(temp_samples) / 2; i++)
1432+
temp_samples[i] = temp_samples[i*2];
1433+
sample_count /= 2;
1434+
sample_distance *= 2;
1435+
}
1436+
1437+
if (sample_count == 0) t1_time = float(ms - heat_start_time) / 1000.0f;
1438+
temp_samples[sample_count++] = current_temp;
1439+
}
1440+
1441+
if (current_temp >= 200.0f) break;
1442+
1443+
next_test_ms += 1000UL * sample_distance;
1444+
}
1445+
}
1446+
wait_for_heatup = false;
1447+
1448+
hotend.soft_pwm_amount = 0;
1449+
1450+
// Calculate physical constants from three equally-spaced samples
1451+
sample_count = (sample_count + 1) / 2 * 2 - 1;
1452+
const float t1 = temp_samples[0],
1453+
t2 = temp_samples[(sample_count - 1) >> 1],
1454+
t3 = temp_samples[sample_count - 1];
1455+
float asymp_temp = (t2 * t2 - t1 * t3) / (2 * t2 - t1 - t3),
1456+
block_responsiveness = -log((t2 - asymp_temp) / (t1 - asymp_temp)) / (sample_distance * (sample_count >> 1));
1457+
1458+
mpc.ambient_xfer_coeff_fan0 = mpc.heater_power * (MPC_MAX) / 255 / (asymp_temp - ambient_temp);
1459+
mpc.block_heat_capacity = mpc.ambient_xfer_coeff_fan0 / block_responsiveness;
1460+
mpc.sensor_responsiveness = block_responsiveness / (1.0f - (ambient_temp - asymp_temp) * exp(-block_responsiveness * t1_time) / (t1 - asymp_temp));
1461+
TERN_(MPC_INCLUDE_FAN, mpc.fan255_adjustment = 0.0f);
1462+
1463+
hotend.modeled_block_temp = asymp_temp + (ambient_temp - asymp_temp) * exp(-block_responsiveness * (ms - heat_start_time) / 1000.0f);
1464+
hotend.modeled_sensor_temp = current_temp;
1465+
1466+
// Allow the system to stabilize under MPC, then get a better measure of ambient loss with and without fan
1467+
SERIAL_ECHOLNPGM(STR_MPC_MEASURING_AMBIENT, hotend.modeled_block_temp);
1468+
TERN(DWIN_LCD_PROUI, LCD_ALERTMESSAGE(MSG_MPC_MEASURING_AMBIENT), LCD_MESSAGE(MSG_MPC_MEASURING_AMBIENT));
1469+
hotend.target = hotend.modeled_block_temp;
1470+
next_test_ms = ms + MPC_dT * 1000;
1471+
constexpr millis_t settle_time = 20000UL, test_duration = 20000UL;
1472+
millis_t settle_end_ms = ms + settle_time,
1473+
test_end_ms = settle_end_ms + test_duration;
1474+
float total_energy_fan0 = 0.0f;
1475+
#if HAS_FAN
1476+
bool fan0_done = false;
1477+
float total_energy_fan255 = 0.0f;
1478+
#endif
1479+
float last_temp = current_temp;
1480+
1481+
wait_for_heatup = true;
1482+
for (;;) { // Can be interrupted with M108
1483+
if (housekeeping(ms, e, current_temp, next_report_ms)) return;
1484+
1485+
if (ELAPSED(ms, next_test_ms)) {
1486+
hotend.soft_pwm_amount = (int)get_pid_output_hotend(e) >> 1;
1487+
1488+
if (ELAPSED(ms, settle_end_ms) && !ELAPSED(ms, test_end_ms) && TERN1(HAS_FAN, !fan0_done))
1489+
total_energy_fan0 += mpc.heater_power * hotend.soft_pwm_amount / 127 * MPC_dT + (last_temp - current_temp) * mpc.block_heat_capacity;
1490+
#if HAS_FAN
1491+
else if (ELAPSED(ms, test_end_ms) && !fan0_done) {
1492+
set_fan_speed(TERN(SINGLEFAN, 0, e), 255);
1493+
planner.sync_fan_speeds(fan_speed);
1494+
settle_end_ms = ms + settle_time;
1495+
test_end_ms = settle_end_ms + test_duration;
1496+
fan0_done = true;
1497+
}
1498+
else if (ELAPSED(ms, settle_end_ms) && !ELAPSED(ms, test_end_ms))
1499+
total_energy_fan255 += mpc.heater_power * hotend.soft_pwm_amount / 127 * MPC_dT + (last_temp - current_temp) * mpc.block_heat_capacity;
1500+
#endif
1501+
else if (ELAPSED(ms, test_end_ms)) break;
1502+
1503+
last_temp = current_temp;
1504+
next_test_ms += MPC_dT * 1000;
1505+
}
1506+
1507+
if (!WITHIN(current_temp, t3 - 15.0f, hotend.target + 15.0f)) {
1508+
SERIAL_ECHOLNPGM(STR_MPC_TEMPERATURE_ERROR);
1509+
TERN_(DWIN_LCD_PROUI, DWIN_MPCTuning(MPC_TEMP_ERROR));
1510+
break;
1511+
}
1512+
}
1513+
wait_for_heatup = false;
1514+
1515+
const float power_fan0 = total_energy_fan0 * 1000 / test_duration;
1516+
mpc.ambient_xfer_coeff_fan0 = power_fan0 / (hotend.target - ambient_temp);
1517+
1518+
#if HAS_FAN
1519+
const float power_fan255 = total_energy_fan255 * 1000 / test_duration,
1520+
ambient_xfer_coeff_fan255 = power_fan255 / (hotend.target - ambient_temp);
1521+
mpc.applyFanAdjustment(ambient_xfer_coeff_fan255);
1522+
#endif
1523+
1524+
// Calculate a new and better asymptotic temperature and re-evaluate the other constants
1525+
asymp_temp = ambient_temp + mpc.heater_power * (MPC_MAX) / 255 / mpc.ambient_xfer_coeff_fan0;
1526+
block_responsiveness = -log((t2 - asymp_temp) / (t1 - asymp_temp)) / (sample_distance * (sample_count >> 1));
1527+
mpc.block_heat_capacity = mpc.ambient_xfer_coeff_fan0 / block_responsiveness;
1528+
mpc.sensor_responsiveness = block_responsiveness / (1.0f - (ambient_temp - asymp_temp) * exp(-block_responsiveness * t1_time) / (t1 - asymp_temp));
1529+
1530+
SERIAL_ECHOLNPGM(STR_MPC_AUTOTUNE_FINISHED);
1531+
TERN_(DWIN_LCD_PROUI, DWIN_MPCTuning(MPC_DONE));
1532+
1533+
#if 0
1534+
SERIAL_ECHOLNPGM("t1_time ", t1_time);
1535+
SERIAL_ECHOLNPGM("sample_count ", sample_count);
1536+
SERIAL_ECHOLNPGM("sample_distance ", sample_distance);
1537+
for (uint8_t i = 0; i < sample_count; i++)
1538+
SERIAL_ECHOLNPGM("sample ", i, " : ", temp_samples[i]);
1539+
SERIAL_ECHOLNPGM("t1 ", t1, " t2 ", t2, " t3 ", t3);
1540+
SERIAL_ECHOLNPGM("asymp_temp ", asymp_temp);
1541+
SERIAL_ECHOLNPAIR_F("block_responsiveness ", block_responsiveness, 4);
1542+
#endif
1543+
1544+
SERIAL_ECHOLNPGM("MPC_BLOCK_HEAT_CAPACITY ", mpc.block_heat_capacity);
1545+
SERIAL_ECHOLNPAIR_F("MPC_SENSOR_RESPONSIVENESS ", mpc.sensor_responsiveness, 4);
1546+
SERIAL_ECHOLNPAIR_F("MPC_AMBIENT_XFER_COEFF ", mpc.ambient_xfer_coeff_fan0, 4);
1547+
TERN_(HAS_FAN, SERIAL_ECHOLNPAIR_F("MPC_AMBIENT_XFER_COEFF_FAN255 ", ambient_xfer_coeff_fan255, 4));
1548+
}
1549+
13051550
#endif // MPC_AUTOTUNE
13061551

13071552
int16_t Temperature::getHeaterPower(const heater_id_t heater_id) {

Marlin/src/module/temperature.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -1217,7 +1217,8 @@ class Temperature {
12171217

12181218
#endif // HAS_PID_HEATING
12191219

1220-
#if ENABLED(MPC_AUTOTUNE)
1220+
#if ENABLED(MPC_AUTOTUNE_FANCY)
1221+
12211222
// Utility class to perform MPCTEMP auto tuning measurements
12221223
class MPC_autotuner {
12231224
public:
@@ -1274,7 +1275,11 @@ class Temperature {
12741275
enum MPCTuningType { AUTO, FORCE_ASYMPTOTIC, FORCE_DIFFERENTIAL };
12751276
static void MPC_autotune(const uint8_t e, MPCTuningType tuning_type);
12761277

1277-
#endif // MPC_AUTOTUNE
1278+
#elif ENABLED(MPC_AUTOTUNE)
1279+
1280+
void MPC_autotune(const uint8_t e);
1281+
1282+
#endif
12781283

12791284
#if ENABLED(PROBING_HEATERS_OFF)
12801285
static void pause_heaters(const bool p);

buildroot/tests/STM32F103RE_creality

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ exec_test $1 $2 "Ender-3 v2 - JyersUI (ABL Bilinear/Manual)" "$3"
2020

2121
use_example_configs "Creality/Ender-3 V2/CrealityV422/CrealityUI"
2222
opt_disable DWIN_CREALITY_LCD PIDTEMP
23-
opt_enable DWIN_MARLINUI_LANDSCAPE LCD_ENDSTOP_TEST AUTO_BED_LEVELING_UBL BLTOUCH Z_SAFE_HOMING MPCTEMP MPC_AUTOTUNE
23+
opt_enable DWIN_MARLINUI_LANDSCAPE LCD_ENDSTOP_TEST AUTO_BED_LEVELING_UBL BLTOUCH Z_SAFE_HOMING MPCTEMP MPC_AUTOTUNE_FANCY
2424
exec_test $1 $2 "Ender-3 v2 - MarlinUI (UBL+BLTOUCH, MPCTEMP, LCD_ENDSTOP_TEST)" "$3"
2525

2626
use_example_configs "Creality/Ender-3 S1/STM32F1"

0 commit comments

Comments
 (0)