Skip to content

Commit 331d2c2

Browse files
committed
Keep smaller option
1 parent 70ecf6e commit 331d2c2

File tree

6 files changed

+280
-21
lines changed

6 files changed

+280
-21
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

+244-1
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
@@ -1302,6 +1302,249 @@ volatile bool Temperature::raw_temps_ready = false;
13021302
TERN_(HAS_FAN, SERIAL_ECHOLNPAIR_F("MPC_AMBIENT_XFER_COEFF_FAN255 ", ambient_xfer_coeff_fan255, 4));
13031303
}
13041304

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

13071550
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)