forked from MarlinFirmware/Marlin
-
-
Notifications
You must be signed in to change notification settings - Fork 19
/
SanityCheck.h
4365 lines (4054 loc) · 192 KB
/
SanityCheck.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* 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 <https://www.gnu.org/licenses/>.
*
*/
#pragma once
/**
* SanityCheck.h
*
* Test configuration values for errors at compile-time.
*/
/**
* Require gcc 4.7 or newer (first included with Arduino 1.6.8) for C++11 features.
*/
#if __cplusplus < 201103L
#error "Marlin requires C++11 support (gcc >= 4.7, Arduino IDE >= 1.6.8). Please upgrade your toolchain."
#endif
// Strings for sanity check messages
#define _NUM_AXES_STR NUM_AXIS_GANG("X ", "Y ", "Z ", "I ", "J ", "K ", "U ", "V ", "W ")
#define _LOGICAL_AXES_STR LOGICAL_AXIS_GANG("E ", "X ", "Y ", "Z ", "I ", "J ", "K ", "U ", "V ", "W ")
// Make sure macros aren't borked
#define TEST1
#define TEST2 1
#define TEST3 0
#define TEST4 true
#if ENABLED(TEST0) || !ENABLED(TEST2) || ENABLED(TEST3) || !ENABLED(TEST1, TEST2, TEST4)
#error "ENABLED is borked!"
#endif
#if ALL(TEST0, TEST1)
#error "ALL is borked!"
#endif
#if DISABLED(TEST1) || !DISABLED(TEST3) || DISABLED(TEST4) || DISABLED(TEST0, TEST1, TEST2, TEST4) || !DISABLED(TEST0, TEST3)
#error "DISABLED is borked!"
#endif
#if !ANY(TEST1, TEST2, TEST3, TEST4) || ANY(TEST0, TEST3)
#error "ANY is borked!"
#endif
#if NONE(TEST0, TEST1, TEST2, TEST4) || !NONE(TEST0, TEST3)
#error "NONE is borked!"
#endif
#undef TEST1
#undef TEST2
#undef TEST3
#undef TEST4
/**
* This is to alert you about non-matching versions of config files.
*
* You can edit the version tag in your old config files and try the build again.
* The checks below will alert you about options that need to be changed, but they won't
* tell you about new options that you might find useful. So it's recommended to transfer
* your settings to new Configuration files matching your Marlin version as soon as possible.
*/
#define HEXIFY(H) _CAT(0x,H)
#if !defined(CONFIGURATION_H_VERSION) || HEXIFY(CONFIGURATION_H_VERSION) < HEXIFY(REQUIRED_CONFIGURATION_H_VERSION)
#error "Your Configuration.h file is for an old version of Marlin. Downgrade Marlin or upgrade your Configuration.h."
#elif HEXIFY(CONFIGURATION_H_VERSION) > HEXIFY(REQUIRED_CONFIGURATION_H_VERSION)
#error "Your Configuration.h file is for a newer version of Marlin. Upgrade Marlin or downgrade your Configuration.h."
#endif
#if !defined(CONFIGURATION_ADV_H_VERSION) || HEXIFY(CONFIGURATION_ADV_H_VERSION) < HEXIFY(REQUIRED_CONFIGURATION_ADV_H_VERSION)
#error "Your Configuration_adv.h file is for an old version of Marlin. Downgrade Marlin or upgrade your Configuration_adv.h."
#elif HEXIFY(CONFIGURATION_ADV_H_VERSION) > HEXIFY(REQUIRED_CONFIGURATION_ADV_H_VERSION)
#error "Your Configuration_adv.h file is for a newer version of Marlin. Upgrade Marlin or downgrade your Configuration_adv.h."
#endif
#undef HEXIFY
/**
* Warnings for old configurations
*/
#ifndef MOTHERBOARD
#error "MOTHERBOARD is required. You must '#define MOTHERBOARD BOARD_MYNAME' (not just '#define BOARD_MYNAME')."
#endif
/**
* Required Version defines
*/
#ifndef SHORT_BUILD_VERSION
#error "SHORT_BUILD_VERSION must be specified."
#elif !defined(DETAILED_BUILD_VERSION)
#error "BUILD_VERSION must be specified."
#elif !defined(STRING_DISTRIBUTION_DATE)
#error "STRING_DISTRIBUTION_DATE must be specified."
#elif !defined(PROTOCOL_VERSION)
#error "PROTOCOL_VERSION must be specified."
#elif !defined(MACHINE_NAME)
#error "MACHINE_NAME must be specified."
#elif !defined(SOURCE_CODE_URL)
#error "SOURCE_CODE_URL must be specified."
#elif !defined(DEFAULT_MACHINE_UUID)
#error "DEFAULT_MACHINE_UUID must be specified."
#elif !defined(WEBSITE_URL)
#error "WEBSITE_URL must be specified."
#endif
// Check AXIS_RELATIVE_MODES
constexpr float arm[] = AXIS_RELATIVE_MODES;
static_assert(COUNT(arm) == LOGICAL_AXES, "AXIS_RELATIVE_MODES must contain " _LOGICAL_AXES_STR "elements.");
/**
* RADDS is forbidden for non-DUE boards, for now.
*/
#if ENABLED(RADDS_DISPLAY) && !defined(__SAM3X8E__)
#error "RADDS_DISPLAY is currently only incompatible with DUE boards."
#endif
/**
* Heated Bed requirements
*/
#if HAS_HEATED_BED
#if !HAS_TEMP_BED
#error "The Heated Bed requires a TEMP_BED_PIN or Thermocouple."
#elif !HAS_HEATER_BED
#error "The Heated Bed requires HEATER_BED_PIN."
#endif
#endif
/**
* Hephestos 2 Heated Bed Kit requirements
*/
#if ENABLED(HEPHESTOS2_HEATED_BED_KIT)
#if TEMP_SENSOR_BED != 70
#error "HEPHESTOS2_HEATED_BED_KIT requires TEMP_SENSOR_BED 70."
#elif DISABLED(HEATER_BED_INVERTING)
#error "HEPHESTOS2_HEATED_BED_KIT requires HEATER_BED_INVERTING."
#endif
#endif
/**
* Probe temp compensation requirements
*/
#if HAS_PTC
#if TEMP_SENSOR_PROBE && TEMP_SENSOR_BED && !(defined(PTC_PARK_POS) && defined(PTC_PROBE_POS))
#error "PTC_PARK_POS and PTC_PROBE_POS are required for Probe Temperature Compensation."
#endif
#if ENABLED(PTC_PROBE)
#if !TEMP_SENSOR_PROBE
#error "PTC_PROBE requires a probe with a thermistor."
#endif
#ifdef PTC_PROBE_START
constexpr auto _ptc_sample_start = PTC_PROBE_START;
constexpr decltype(_ptc_sample_start) _test_ptc_sample_start = 12.3f;
static_assert(_test_ptc_sample_start != 12.3f, "PTC_PROBE_START must be a whole number.");
#endif
#ifdef PTC_PROBE_RES
constexpr auto _ptc_sample_res = PTC_PROBE_RES;
constexpr decltype(_ptc_sample_res) _test_ptc_sample_res = 12.3f;
static_assert(_test_ptc_sample_res != 12.3f, "PTC_PROBE_RES must be a whole number.");
#endif
#if ENABLED(PTC_BED) && defined(PTC_PROBE_TEMP)
constexpr auto _btc_probe_temp = PTC_PROBE_TEMP;
constexpr decltype(_btc_probe_temp) _test_btc_probe_temp = 12.3f;
static_assert(_test_btc_probe_temp != 12.3f, "PTC_PROBE_TEMP must be a whole number.");
#endif
#endif
#if ENABLED(PTC_BED)
#if !TEMP_SENSOR_BED
#error "PTC_BED requires a bed with a thermistor."
#endif
#ifdef PTC_BED_START
constexpr auto _btc_sample_start = PTC_BED_START;
constexpr decltype(_btc_sample_start) _test_btc_sample_start = 12.3f;
static_assert(_test_btc_sample_start != 12.3f, "PTC_BED_START must be a whole number.");
#endif
#ifdef PTC_BED_RES
constexpr auto _btc_sample_res = PTC_BED_RES;
constexpr decltype(_btc_sample_res) _test_btc_sample_res = 12.3f;
static_assert(_test_btc_sample_res != 12.3f, "PTC_BED_RES must be a whole number.");
#endif
#endif
#if ENABLED(PTC_HOTEND)
#if EXTRUDERS != 1
#error "PTC_HOTEND requires a single extruder."
#endif
#ifdef PTC_HOTEND_START
constexpr auto _etc_sample_start = PTC_HOTEND_START;
constexpr decltype(_etc_sample_start) _test_etc_sample_start = 12.3f;
static_assert(_test_etc_sample_start != 12.3f, "PTC_HOTEND_START must be a whole number.");
#endif
#ifdef PTC_HOTEND_RES
constexpr auto _etc_sample_res = PTC_HOTEND_RES;
constexpr decltype(_etc_sample_res) _test_etc_sample_res = 12.3f;
static_assert(_test_etc_sample_res != 12.3f, "PTC_HOTEND_RES must be a whole number.");
#endif
#endif
#endif // HAS_PTC
/**
* Serial
*/
#ifndef SERIAL_PORT
#error "SERIAL_PORT must be defined."
#elif defined(SERIAL_PORT_2) && SERIAL_PORT_2 == SERIAL_PORT
#error "SERIAL_PORT_2 cannot be the same as SERIAL_PORT."
#elif defined(SERIAL_PORT_3)
#ifndef SERIAL_PORT_2
#error "Use SERIAL_PORT_2 before using SERIAL_PORT_3"
#elif SERIAL_PORT_3 == SERIAL_PORT
#error "SERIAL_PORT_3 cannot be the same as SERIAL_PORT."
#elif SERIAL_PORT_3 == SERIAL_PORT_2
#error "SERIAL_PORT_3 cannot be the same as SERIAL_PORT_2."
#endif
#endif
#if !(defined(__AVR__) && defined(USBCON))
#if ENABLED(SERIAL_XON_XOFF) && RX_BUFFER_SIZE < 1024
#error "SERIAL_XON_XOFF requires RX_BUFFER_SIZE >= 1024 for reliable transfers without drops."
#elif RX_BUFFER_SIZE && (RX_BUFFER_SIZE < 2 || !IS_POWER_OF_2(RX_BUFFER_SIZE))
#error "RX_BUFFER_SIZE must be a power of 2 greater than 1."
#elif TX_BUFFER_SIZE && (TX_BUFFER_SIZE < 2 || TX_BUFFER_SIZE > 256 || !IS_POWER_OF_2(TX_BUFFER_SIZE))
#error "TX_BUFFER_SIZE must be 0 or a power of 2 between 1 and 256."
#endif
#elif ANY(SERIAL_XON_XOFF, SERIAL_STATS_MAX_RX_QUEUED, SERIAL_STATS_DROPPED_RX)
#error "SERIAL_XON_XOFF and SERIAL_STATS_* features not supported on USB-native AVR devices."
#endif
// Serial DMA is only available for some STM32 MCUs and HC32
#if ENABLED(SERIAL_DMA)
#if defined(ARDUINO_ARCH_HC32)
// checks for HC32 are located in HAL/HC32/inc/SanityCheck.h
#elif !HAL_STM32 || NONE(STM32F0xx, STM32F1xx, STM32F2xx, STM32F4xx, STM32F7xx)
#error "SERIAL_DMA is only available for some STM32 MCUs and requires HAL/STM32."
#elif !defined(HAL_UART_MODULE_ENABLED) || defined(HAL_UART_MODULE_ONLY)
#error "SERIAL_DMA requires STM32 platform HAL UART (without HAL_UART_MODULE_ONLY)."
#endif
#endif
/**
* Multiple Stepper Drivers Per Axis
*/
#define GOOD_AXIS_PINS(A) PINS_EXIST(A##_ENABLE, A##_STEP, A##_DIR)
#if HAS_X2_STEPPER && !GOOD_AXIS_PINS(X2)
#error "If X2_DRIVER_TYPE is defined, then X2 ENABLE/STEP/DIR pins are also needed."
#endif
#if HAS_Y2_STEPPER && !GOOD_AXIS_PINS(Y2)
#error "If Y2_DRIVER_TYPE is defined, then Y2 ENABLE/STEP/DIR pins are also needed."
#endif
#if HAS_Z_AXIS
#if NUM_Z_STEPPERS >= 2 && !GOOD_AXIS_PINS(Z2)
#error "If Z2_DRIVER_TYPE is defined, then Z2 ENABLE/STEP/DIR pins are also needed."
#elif NUM_Z_STEPPERS >= 3 && !GOOD_AXIS_PINS(Z3)
#error "If Z3_DRIVER_TYPE is defined, then Z3 ENABLE/STEP/DIR pins are also needed."
#elif NUM_Z_STEPPERS >= 4 && !GOOD_AXIS_PINS(Z4)
#error "If Z4_DRIVER_TYPE is defined, then Z4 ENABLE/STEP/DIR pins are also needed."
#endif
#endif
/**
* Validate bed size
*/
#if !defined(X_BED_SIZE) || !defined(Y_BED_SIZE)
#error "X_BED_SIZE and Y_BED_SIZE are required!"
#else
#if HAS_X_AXIS
static_assert(X_MAX_LENGTH >= X_BED_SIZE, "Movement bounds (X_MIN_POS, X_MAX_POS) are too narrow to contain X_BED_SIZE.");
#endif
#if HAS_Y_AXIS
static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS) are too narrow to contain Y_BED_SIZE.");
#endif
#endif
/**
* Granular software endstops (Marlin >= 1.1.7)
*/
#if ENABLED(MIN_SOFTWARE_ENDSTOPS) && NONE(MIN_SOFTWARE_ENDSTOP_Z, POLARGRAPH)
#if IS_KINEMATIC
#error "MIN_SOFTWARE_ENDSTOPS on DELTA/SCARA also requires MIN_SOFTWARE_ENDSTOP_Z."
#elif NONE(MIN_SOFTWARE_ENDSTOP_X, MIN_SOFTWARE_ENDSTOP_Y)
#error "MIN_SOFTWARE_ENDSTOPS requires at least one of the MIN_SOFTWARE_ENDSTOP_[XYZ] options."
#endif
#endif
#if ENABLED(MAX_SOFTWARE_ENDSTOPS) && NONE(MAX_SOFTWARE_ENDSTOP_Z, POLARGRAPH)
#if IS_KINEMATIC
#error "MAX_SOFTWARE_ENDSTOPS on DELTA/SCARA also requires MAX_SOFTWARE_ENDSTOP_Z."
#elif NONE(MAX_SOFTWARE_ENDSTOP_X, MAX_SOFTWARE_ENDSTOP_Y)
#error "MAX_SOFTWARE_ENDSTOPS requires at least one of the MAX_SOFTWARE_ENDSTOP_[XYZ] options."
#endif
#endif
#if ALL(ENDSTOPPULLUPS, ENDSTOPPULLDOWNS)
#error "Enable only one of ENDSTOPPULLUPS or ENDSTOPPULLDOWNS."
#elif ALL(FIL_RUNOUT_PULLUP, FIL_RUNOUT_PULLDOWN)
#error "Enable only one of FIL_RUNOUT_PULLUP or FIL_RUNOUT_PULLDOWN."
#elif ALL(ENDSTOPPULLUP_XMAX, ENDSTOPPULLDOWN_XMAX)
#error "Enable only one of ENDSTOPPULLUP_X_MAX or ENDSTOPPULLDOWN_X_MAX."
#elif ALL(ENDSTOPPULLUP_YMAX, ENDSTOPPULLDOWN_YMAX)
#error "Enable only one of ENDSTOPPULLUP_Y_MAX or ENDSTOPPULLDOWN_Y_MAX."
#elif ALL(ENDSTOPPULLUP_ZMAX, ENDSTOPPULLDOWN_ZMAX)
#error "Enable only one of ENDSTOPPULLUP_Z_MAX or ENDSTOPPULLDOWN_Z_MAX."
#elif ALL(ENDSTOPPULLUP_IMAX, ENDSTOPPULLDOWN_IMAX)
#error "Enable only one of ENDSTOPPULLUP_I_MAX or ENDSTOPPULLDOWN_I_MAX."
#elif ALL(ENDSTOPPULLUP_JMAX, ENDSTOPPULLDOWN_JMAX)
#error "Enable only one of ENDSTOPPULLUP_J_MAX or ENDSTOPPULLDOWN_J_MAX."
#elif ALL(ENDSTOPPULLUP_KMAX, ENDSTOPPULLDOWN_KMAX)
#error "Enable only one of ENDSTOPPULLUP_K_MAX or ENDSTOPPULLDOWN_K_MAX."
#elif ALL(ENDSTOPPULLUP_UMAX, ENDSTOPPULLDOWN_UMAX)
#error "Enable only one of ENDSTOPPULLUP_U_MAX or ENDSTOPPULLDOWN_U_MAX."
#elif ALL(ENDSTOPPULLUP_VMAX, ENDSTOPPULLDOWN_VMAX)
#error "Enable only one of ENDSTOPPULLUP_V_MAX or ENDSTOPPULLDOWN_V_MAX."
#elif ALL(ENDSTOPPULLUP_WMAX, ENDSTOPPULLDOWN_WMAX)
#error "Enable only one of ENDSTOPPULLUP_W_MAX or ENDSTOPPULLDOWN_W_MAX."
#elif ALL(ENDSTOPPULLUP_XMIN, ENDSTOPPULLDOWN_XMIN)
#error "Enable only one of ENDSTOPPULLUP_X_MIN or ENDSTOPPULLDOWN_X_MIN."
#elif ALL(ENDSTOPPULLUP_YMIN, ENDSTOPPULLDOWN_YMIN)
#error "Enable only one of ENDSTOPPULLUP_Y_MIN or ENDSTOPPULLDOWN_Y_MIN."
#elif ALL(ENDSTOPPULLUP_ZMIN, ENDSTOPPULLDOWN_ZMIN)
#error "Enable only one of ENDSTOPPULLUP_Z_MIN or ENDSTOPPULLDOWN_Z_MIN."
#elif ALL(ENDSTOPPULLUP_IMIN, ENDSTOPPULLDOWN_IMIN)
#error "Enable only one of ENDSTOPPULLUP_I_MIN or ENDSTOPPULLDOWN_I_MIN."
#elif ALL(ENDSTOPPULLUP_JMIN, ENDSTOPPULLDOWN_JMIN)
#error "Enable only one of ENDSTOPPULLUP_J_MIN or ENDSTOPPULLDOWN_J_MIN."
#elif ALL(ENDSTOPPULLUP_KMIN, ENDSTOPPULLDOWN_KMIN)
#error "Enable only one of ENDSTOPPULLUP_K_MIN or ENDSTOPPULLDOWN_K_MIN."
#elif ALL(ENDSTOPPULLUP_UMIN, ENDSTOPPULLDOWN_UMIN)
#error "Enable only one of ENDSTOPPULLUP_U_MIN or ENDSTOPPULLDOWN_U_MIN."
#elif ALL(ENDSTOPPULLUP_VMIN, ENDSTOPPULLDOWN_VMIN)
#error "Enable only one of ENDSTOPPULLUP_V_MIN or ENDSTOPPULLDOWN_V_MIN."
#elif ALL(ENDSTOPPULLUP_WMIN, ENDSTOPPULLDOWN_WMIN)
#error "Enable only one of ENDSTOPPULLUP_W_MIN or ENDSTOPPULLDOWN_W_MIN."
#endif
/**
* LCD Info Screen Style
*/
#if LCD_INFO_SCREEN_STYLE > 0
#if HAS_MARLINUI_U8GLIB || LCD_WIDTH < 20 || LCD_HEIGHT < 4
#error "Alternative LCD_INFO_SCREEN_STYLE requires 20x4 Character LCD."
#elif LCD_INFO_SCREEN_STYLE > 2
#error "LCD_INFO_SCREEN_STYLE only has options 0 (Classic), 1 (Průša), and 2 (CNC)."
#endif
#endif
/**
* Progress Bar
*/
#if ENABLED(LCD_PROGRESS_BAR)
#if NONE(HAS_MEDIA, SET_PROGRESS_MANUALLY)
#error "LCD_PROGRESS_BAR requires SDSUPPORT or SET_PROGRESS_MANUALLY."
#elif NONE(HAS_MARLINUI_HD44780, IS_TFTGLCD_PANEL)
#error "LCD_PROGRESS_BAR only applies to HD44780 character LCD and TFTGLCD_PANEL_(SPI|I2C)."
#elif HAS_MARLINUI_U8GLIB || IS_DWIN_MARLINUI
#error "LCD_PROGRESS_BAR does not apply to graphical displays."
#elif ENABLED(FILAMENT_LCD_DISPLAY)
#error "LCD_PROGRESS_BAR and FILAMENT_LCD_DISPLAY are not fully compatible. Comment out this line to use both."
#elif PROGRESS_MSG_EXPIRE < 0
#error "PROGRESS_MSG_EXPIRE must be greater than or equal to 0."
#endif
#endif
#if ENABLED(SET_PROGRESS_MANUALLY) && NONE(SET_PROGRESS_PERCENT, SET_REMAINING_TIME, SET_INTERACTION_TIME)
#error "SET_PROGRESS_MANUALLY requires at least one of SET_PROGRESS_PERCENT, SET_REMAINING_TIME, SET_INTERACTION_TIME to be enabled."
#endif
#if HAS_LCDPRINT && HAS_EXTRA_PROGRESS && LCD_HEIGHT < 4
#error "Displays with fewer than 4 rows can't show progress values (e.g., SHOW_PROGRESS_PERCENT, SHOW_ELAPSED_TIME, SHOW_REMAINING_TIME, SHOW_INTERACTION_TIME)."
#endif
#if !HAS_MARLINUI_MENU && ENABLED(SD_REPRINT_LAST_SELECTED_FILE)
#error "SD_REPRINT_LAST_SELECTED_FILE currently requires a Marlin-native LCD menu."
#endif
#if ANY(HAS_MARLINUI_MENU, TOUCH_UI_FTDI_EVE, EXTENSIBLE_UI, DWIN_LCD_PROUI) && !defined(MANUAL_FEEDRATE)
#error "MANUAL_FEEDRATE is required for ProUI, MarlinUI, ExtUI, or FTDI EVE Touch UI."
#endif
/**
* Custom Boot and Status screens
*/
#if ENABLED(SHOW_CUSTOM_BOOTSCREEN) && NONE(HAS_MARLINUI_HD44780, HAS_MARLINUI_U8GLIB, TOUCH_UI_FTDI_EVE, IS_DWIN_MARLINUI)
#error "SHOW_CUSTOM_BOOTSCREEN requires Character LCD, Graphical LCD, or TOUCH_UI_FTDI_EVE."
#elif ENABLED(SHOW_CUSTOM_BOOTSCREEN) && DISABLED(SHOW_BOOTSCREEN)
#error "SHOW_CUSTOM_BOOTSCREEN requires SHOW_BOOTSCREEN."
#elif ENABLED(CUSTOM_STATUS_SCREEN_IMAGE) && !HAS_MARLINUI_U8GLIB
#error "CUSTOM_STATUS_SCREEN_IMAGE requires a 128x64 DOGM B/W Graphical LCD."
#endif
#if ALL(STATUS_HEAT_PERCENT, STATUS_HEAT_POWER)
#error "Only enable STATUS_HEAT_PERCENT or STATUS_HEAT_POWER, but not both."
#endif
/**
* LCD Lightweight Screen Style
*/
#if ENABLED(LIGHTWEIGHT_UI) && !IS_U8GLIB_ST7920
#error "LIGHTWEIGHT_UI requires a U8GLIB_ST7920-based display."
#endif
/**
* SD Card Settings
*/
#if ALL(HAS_MEDIA, HAS_SD_DETECT, SD_CONNECTION_TYPICAL, ELB_FULL_GRAPHIC_CONTROLLER, HAS_MARLINUI_MENU) && SD_DETECT_STATE == LOW
#error "SD_DETECT_STATE must be set HIGH for SD on the ELB_FULL_GRAPHIC_CONTROLLER."
#endif
#undef SD_CONNECTION_TYPICAL
/**
* SD File Sorting
*/
#if ENABLED(SDCARD_SORT_ALPHA)
#if NONE(EXTENSIBLE_UI, HAS_MARLINUI_MENU, DWIN_CREALITY_LCD, DWIN_CREALITY_LCD_JYERSUI, DWIN_LCD_PROUI)
#error "SDCARD_SORT_ALPHA requires an LCD that supports it. (It doesn't apply to M20, etc.)"
#elif SDSORT_LIMIT > 256
#error "SDSORT_LIMIT must be 256 or smaller."
#elif SDSORT_LIMIT < 10
#error "SDSORT_LIMIT should be greater than 9 to be useful."
#elif ENABLED(SDSORT_DYNAMIC_RAM) && DISABLED(SDSORT_USES_RAM)
#error "SDSORT_DYNAMIC_RAM requires SDSORT_USES_RAM (which reads the directory into RAM)."
#elif ENABLED(SDSORT_CACHE_NAMES) && DISABLED(SDSORT_USES_RAM)
#error "SDSORT_CACHE_NAMES requires SDSORT_USES_RAM (which reads the directory into RAM)."
#elif ENABLED(SDSORT_DYNAMIC_RAM) && DISABLED(SDSORT_CACHE_NAMES)
#error "SDSORT_DYNAMIC_RAM requires SDSORT_CACHE_NAMES."
#endif
#if ENABLED(SDSORT_CACHE_NAMES) && DISABLED(SDSORT_DYNAMIC_RAM)
#if SDSORT_CACHE_VFATS < 2
#error "SDSORT_CACHE_VFATS must be 2 or greater!"
#elif SDSORT_CACHE_VFATS > VFAT_ENTRIES_LIMIT
#undef SDSORT_CACHE_VFATS
#define SDSORT_CACHE_VFATS VFAT_ENTRIES_LIMIT
#define SDSORT_CACHE_VFATS_WARNING 1
#endif
#endif
#endif
/**
* Custom Event G-code
*/
#if defined(EVENT_GCODE_SD_ABORT) && DISABLED(NOZZLE_PARK_FEATURE)
static_assert(nullptr == strstr(EVENT_GCODE_SD_ABORT, "G27"), "NOZZLE_PARK_FEATURE is required to use G27 in EVENT_GCODE_SD_ABORT.");
#endif
#if ANY(TC_GCODE_USE_GLOBAL_X, TC_GCODE_USE_GLOBAL_Y, TC_GCODE_USE_GLOBAL_Z) && ENABLED(NO_WORKSPACE_OFFSETS)
#error "TC_GCODE_USE_GLOBAL_* options are incompatible with NO_WORKSPACE_OFFSETS."
#endif
/**
* I2C Position Encoders
*/
#if ENABLED(I2C_POSITION_ENCODERS)
#if !ALL(BABYSTEPPING, BABYSTEP_XY)
#error "I2C_POSITION_ENCODERS requires BABYSTEPPING and BABYSTEP_XY."
#elif DISABLED(EDITABLE_STEPS_PER_UNIT)
#error "EDITABLE_STEPS_PER_UNIT is required for I2C_POSITION_ENCODERS."
#elif !WITHIN(I2CPE_ENCODER_CNT, 1, 5)
#error "I2CPE_ENCODER_CNT must be between 1 and 5."
#endif
#endif
/**
* Babystepping
*/
#if ENABLED(BABYSTEPPING)
#if ENABLED(SCARA)
#error "BABYSTEPPING is not implemented for SCARA yet."
#elif ENABLED(BABYSTEP_XY) && ANY(MARKFORGED_XY, MARKFORGED_YX)
#error "BABYSTEPPING only implemented for Z axis on MarkForged."
#elif ALL(DELTA, BABYSTEP_XY)
#error "BABYSTEPPING only implemented for Z axis on deltabots."
#elif ALL(BABYSTEP_ZPROBE_OFFSET, MESH_BED_LEVELING)
#error "MESH_BED_LEVELING and BABYSTEP_ZPROBE_OFFSET is not a valid combination"
#elif ENABLED(BABYSTEP_ZPROBE_OFFSET) && !HAS_BED_PROBE
#error "BABYSTEP_ZPROBE_OFFSET requires a probe."
#elif ENABLED(BABYSTEP_GFX_OVERLAY) && NONE(HAS_MARLINUI_U8GLIB, IS_DWIN_MARLINUI)
#error "BABYSTEP_GFX_OVERLAY requires a Graphical LCD."
#elif ENABLED(BABYSTEP_GFX_OVERLAY) && DISABLED(BABYSTEP_ZPROBE_OFFSET)
#error "BABYSTEP_GFX_OVERLAY requires a BABYSTEP_ZPROBE_OFFSET."
#elif ENABLED(BABYSTEP_HOTEND_Z_OFFSET) && !HAS_HOTEND_OFFSET
#error "BABYSTEP_HOTEND_Z_OFFSET requires 2 or more HOTENDS."
#elif ALL(BABYSTEP_ALWAYS_AVAILABLE, MOVE_Z_WHEN_IDLE)
#error "BABYSTEP_ALWAYS_AVAILABLE and MOVE_Z_WHEN_IDLE are incompatible."
#elif !defined(BABYSTEP_MULTIPLICATOR_Z)
#error "BABYSTEPPING requires BABYSTEP_MULTIPLICATOR_Z."
#elif ENABLED(BABYSTEP_XY) && !defined(BABYSTEP_MULTIPLICATOR_XY)
#error "BABYSTEP_XY requires BABYSTEP_MULTIPLICATOR_XY."
#elif ENABLED(BABYSTEP_MILLIMETER_UNITS)
static_assert(BABYSTEP_MULTIPLICATOR_Z <= 0.1f, "BABYSTEP_MULTIPLICATOR_Z must be less or equal to 0.1mm.");
#if ENABLED(BABYSTEP_XY)
static_assert(BABYSTEP_MULTIPLICATOR_XY <= 0.25f, "BABYSTEP_MULTIPLICATOR_XY must be less than or equal to 0.25mm.");
#endif
#endif
#endif
/**
* Filament Runout needs one or more pins and either SD Support or Auto print start detection
*/
#if HAS_FILAMENT_SENSOR
#if !PIN_EXISTS(FIL_RUNOUT)
#error "FILAMENT_RUNOUT_SENSOR requires FIL_RUNOUT_PIN."
#elif HAS_PRUSA_MMU2 && NUM_RUNOUT_SENSORS != 1
#error "NUM_RUNOUT_SENSORS must be 1 with MMU2 / MMU2S."
#elif NUM_RUNOUT_SENSORS != 1 && NUM_RUNOUT_SENSORS != E_STEPPERS
#error "NUM_RUNOUT_SENSORS must be either 1 or number of E steppers."
#elif NUM_RUNOUT_SENSORS >= 8 && !PIN_EXISTS(FIL_RUNOUT8)
#error "FIL_RUNOUT8_PIN is required with NUM_RUNOUT_SENSORS >= 8."
#elif NUM_RUNOUT_SENSORS >= 7 && !PIN_EXISTS(FIL_RUNOUT7)
#error "FIL_RUNOUT7_PIN is required with NUM_RUNOUT_SENSORS >= 7."
#elif NUM_RUNOUT_SENSORS >= 6 && !PIN_EXISTS(FIL_RUNOUT6)
#error "FIL_RUNOUT6_PIN is required with NUM_RUNOUT_SENSORS >= 6."
#elif NUM_RUNOUT_SENSORS >= 5 && !PIN_EXISTS(FIL_RUNOUT5)
#error "FIL_RUNOUT5_PIN is required with NUM_RUNOUT_SENSORS >= 5."
#elif NUM_RUNOUT_SENSORS >= 4 && !PIN_EXISTS(FIL_RUNOUT4)
#error "FIL_RUNOUT4_PIN is required with NUM_RUNOUT_SENSORS >= 4."
#elif NUM_RUNOUT_SENSORS >= 3 && !PIN_EXISTS(FIL_RUNOUT3)
#error "FIL_RUNOUT3_PIN is required with NUM_RUNOUT_SENSORS >= 3."
#elif NUM_RUNOUT_SENSORS >= 2 && !PIN_EXISTS(FIL_RUNOUT2)
#error "FIL_RUNOUT2_PIN is required with NUM_RUNOUT_SENSORS >= 2."
#elif ALL(FIL_RUNOUT1_PULLUP, FIL_RUNOUT1_PULLDOWN)
#error "You can't enable FIL_RUNOUT1_PULLUP and FIL_RUNOUT1_PULLDOWN at the same time."
#elif ALL(FIL_RUNOUT2_PULLUP, FIL_RUNOUT2_PULLDOWN)
#error "You can't enable FIL_RUNOUT2_PULLUP and FIL_RUNOUT2_PULLDOWN at the same time."
#elif ALL(FIL_RUNOUT3_PULLUP, FIL_RUNOUT3_PULLDOWN)
#error "You can't enable FIL_RUNOUT3_PULLUP and FIL_RUNOUT3_PULLDOWN at the same time."
#elif ALL(FIL_RUNOUT4_PULLUP, FIL_RUNOUT4_PULLDOWN)
#error "You can't enable FIL_RUNOUT4_PULLUP and FIL_RUNOUT4_PULLDOWN at the same time."
#elif ALL(FIL_RUNOUT5_PULLUP, FIL_RUNOUT5_PULLDOWN)
#error "You can't enable FIL_RUNOUT5_PULLUP and FIL_RUNOUT5_PULLDOWN at the same time."
#elif ALL(FIL_RUNOUT6_PULLUP, FIL_RUNOUT6_PULLDOWN)
#error "You can't enable FIL_RUNOUT6_PULLUP and FIL_RUNOUT6_PULLDOWN at the same time."
#elif ALL(FIL_RUNOUT7_PULLUP, FIL_RUNOUT7_PULLDOWN)
#error "You can't enable FIL_RUNOUT7_PULLUP and FIL_RUNOUT7_PULLDOWN at the same time."
#elif ALL(FIL_RUNOUT8_PULLUP, FIL_RUNOUT8_PULLDOWN)
#error "You can't enable FIL_RUNOUT8_PULLUP and FIL_RUNOUT8_PULLDOWN at the same time."
#elif FILAMENT_RUNOUT_DISTANCE_MM < 0
#error "FILAMENT_RUNOUT_DISTANCE_MM must be greater than or equal to zero."
#elif DISABLED(ADVANCED_PAUSE_FEATURE) && defined(FILAMENT_RUNOUT_SCRIPT)
static_assert(nullptr == strstr(FILAMENT_RUNOUT_SCRIPT, "M600"), "FILAMENT_RUNOUT_SCRIPT cannot make use of M600 unless ADVANCED_PAUSE_FEATURE is enabled");
#endif
#endif
/**
* Advanced Pause
*/
#if ENABLED(ADVANCED_PAUSE_FEATURE)
#if !HAS_RESUME_CONTINUE
#error "ADVANCED_PAUSE_FEATURE requires a supported LCD controller (or EMERGENCY_PARSER)."
#elif DISABLED(NOZZLE_PARK_FEATURE)
#error "ADVANCED_PAUSE_FEATURE requires NOZZLE_PARK_FEATURE."
#elif !defined(FILAMENT_UNLOAD_PURGE_FEEDRATE)
#error "ADVANCED_PAUSE_FEATURE requires FILAMENT_UNLOAD_PURGE_FEEDRATE."
#elif ENABLED(EXTRUDER_RUNOUT_PREVENT)
#error "EXTRUDER_RUNOUT_PREVENT is incompatible with ADVANCED_PAUSE_FEATURE."
#elif ENABLED(PARK_HEAD_ON_PAUSE) && NONE(HAS_MEDIA, IS_NEWPANEL, EMERGENCY_PARSER)
#error "PARK_HEAD_ON_PAUSE requires HAS_MEDIA, EMERGENCY_PARSER, or an LCD controller."
#elif ENABLED(HOME_BEFORE_FILAMENT_CHANGE) && DISABLED(PAUSE_PARK_NO_STEPPER_TIMEOUT)
#error "HOME_BEFORE_FILAMENT_CHANGE requires PAUSE_PARK_NO_STEPPER_TIMEOUT."
#elif ENABLED(PREVENT_LENGTHY_EXTRUDE) && FILAMENT_CHANGE_UNLOAD_LENGTH > EXTRUDE_MAXLENGTH
#error "FILAMENT_CHANGE_UNLOAD_LENGTH must be less than or equal to EXTRUDE_MAXLENGTH."
#elif ENABLED(PREVENT_LENGTHY_EXTRUDE) && FILAMENT_CHANGE_SLOW_LOAD_LENGTH > EXTRUDE_MAXLENGTH
#error "FILAMENT_CHANGE_SLOW_LOAD_LENGTH must be less than or equal to EXTRUDE_MAXLENGTH."
#elif ENABLED(PREVENT_LENGTHY_EXTRUDE) && FILAMENT_CHANGE_FAST_LOAD_LENGTH > EXTRUDE_MAXLENGTH
#error "FILAMENT_CHANGE_FAST_LOAD_LENGTH must be less than or equal to EXTRUDE_MAXLENGTH."
#endif
#endif
#if ENABLED(NOZZLE_PARK_FEATURE)
constexpr float npp[] = NOZZLE_PARK_POINT;
static_assert(COUNT(npp) == _MIN(NUM_AXES, XYZ), "NOZZLE_PARK_POINT requires coordinates for enabled axes, but only up to X,Y,Z.");
constexpr xyz_pos_t npp_xyz = NOZZLE_PARK_POINT;
static_assert(WITHIN(npp_xyz.x, X_MIN_POS, X_MAX_POS), "NOZZLE_PARK_POINT.X is out of bounds (X_MIN_POS, X_MAX_POS).");
static_assert(TERN1(HAS_Y_AXIS, WITHIN(npp_xyz.y, Y_MIN_POS, Y_MAX_POS)), "NOZZLE_PARK_POINT.Y is out of bounds (Y_MIN_POS, Y_MAX_POS).");
static_assert(TERN1(HAS_Z_AXIS, WITHIN(npp_xyz.z, Z_MIN_POS, Z_MAX_POS)), "NOZZLE_PARK_POINT.Z is out of bounds (Z_MIN_POS, Z_MAX_POS).");
#endif
/**
* Instant Freeze
*/
#if ENABLED(FREEZE_FEATURE) && !(PIN_EXISTS(FREEZE) && defined(FREEZE_STATE))
#error "FREEZE_FEATURE requires both FREEZE_PIN and FREEZE_STATE."
#endif
/**
* Individual axis homing is useless for DELTAS
*/
#if ALL(INDIVIDUAL_AXIS_HOMING_MENU, DELTA)
#error "INDIVIDUAL_AXIS_HOMING_MENU is incompatible with DELTA kinematics."
#endif
/**
* Multi-Material-Unit 2 / EXTENDABLE_EMU_MMU2 requirements
*/
#if HAS_PRUSA_MMU2
#if !HAS_EXTENDABLE_MMU && EXTRUDERS != 5
#undef SINGLENOZZLE
#error "PRUSA_MMU2(S) requires exactly 5 EXTRUDERS. Please update your Configuration."
#elif HAS_EXTENDABLE_MMU && EXTRUDERS > 15
#error "EXTRUDERS is too large for MMU(S) emulation mode. The maximum value is 15."
#elif DISABLED(NOZZLE_PARK_FEATURE)
#error "PRUSA_MMU2(S) requires NOZZLE_PARK_FEATURE. Enable it to continue."
#elif HAS_PRUSA_MMU2S && DISABLED(FILAMENT_RUNOUT_SENSOR)
#error "PRUSA_MMU2S requires FILAMENT_RUNOUT_SENSOR. Enable it to continue."
#elif ENABLED(MMU_EXTRUDER_SENSOR) && DISABLED(FILAMENT_RUNOUT_SENSOR)
#error "MMU_EXTRUDER_SENSOR requires FILAMENT_RUNOUT_SENSOR. Enable it to continue."
#elif ENABLED(MMU_EXTRUDER_SENSOR) && !HAS_MARLINUI_MENU
#error "MMU_EXTRUDER_SENSOR requires an LCD supporting MarlinUI."
#elif ENABLED(MMU2_MENUS) && !HAS_MARLINUI_MENU
#error "MMU2_MENUS requires an LCD supporting MarlinUI."
#elif DISABLED(ADVANCED_PAUSE_FEATURE)
static_assert(nullptr == strstr(MMU2_FILAMENT_RUNOUT_SCRIPT, "M600"), "MMU2_FILAMENT_RUNOUT_SCRIPT cannot make use of M600 unless ADVANCED_PAUSE_FEATURE is enabled");
#endif
#endif
/**
* Options only for EXTRUDERS > 1
*/
#if HAS_MULTI_EXTRUDER
#ifndef MAX_EXTRUDERS
#if HAS_EXTENDABLE_MMU
#define MAX_EXTRUDERS 15
#else
#define MAX_EXTRUDERS 8
#endif
#endif
static_assert(EXTRUDERS <= MAX_EXTRUDERS, "Marlin supports a maximum of " STRINGIFY(MAX_EXTRUDERS) " EXTRUDERS.");
#undef MAX_EXTRUDERS
#if ENABLED(HEATERS_PARALLEL)
#error "EXTRUDERS must be 1 with HEATERS_PARALLEL."
#endif
#if ENABLED(STATUS_HOTEND_INVERTED) && NONE(STATUS_HOTEND_NUMBERLESS, STATUS_HOTEND_ANIM)
#error "With multiple hotends STATUS_HOTEND_INVERTED requires STATUS_HOTEND_ANIM or STATUS_HOTEND_NUMBERLESS."
#endif
#if ENABLED(TOOLCHANGE_FILAMENT_SWAP)
#ifndef TOOLCHANGE_FS_LENGTH
#error "TOOLCHANGE_FILAMENT_SWAP requires TOOLCHANGE_FS_LENGTH."
#elif !defined(TOOLCHANGE_FS_RETRACT_SPEED)
#error "TOOLCHANGE_FILAMENT_SWAP requires TOOLCHANGE_FS_RETRACT_SPEED."
#elif !defined(TOOLCHANGE_FS_PRIME_SPEED)
#error "TOOLCHANGE_FILAMENT_SWAP requires TOOLCHANGE_FS_PRIME_SPEED."
#endif
#endif
#if ENABLED(TOOLCHANGE_PARK)
#ifndef TOOLCHANGE_PARK_XY
#error "TOOLCHANGE_PARK requires TOOLCHANGE_PARK_XY."
#elif !defined(TOOLCHANGE_PARK_XY_FEEDRATE)
#error "TOOLCHANGE_PARK requires TOOLCHANGE_PARK_XY_FEEDRATE."
#endif
#endif
#ifndef TOOLCHANGE_ZRAISE
#error "TOOLCHANGE_ZRAISE required for EXTRUDERS > 1."
#endif
#elif HAS_PRUSA_MMU1 || HAS_EXTENDABLE_MMU
#error "Multi-Material-Unit requires 2 or more EXTRUDERS."
#elif ENABLED(SINGLENOZZLE)
#error "SINGLENOZZLE requires 2 or more EXTRUDERS."
#if ENABLED(PID_PARAMS_PER_HOTEND)
#error "PID_PARAMS_PER_HOTEND must be disabled when using any SINGLENOZZLE extruder."
#endif
#endif
/**
* A Dual Nozzle carriage with switching servo
*/
#if ALL(SWITCHING_NOZZLE, MECHANICAL_SWITCHING_NOZZLE)
#error "Enable only one of SWITCHING_NOZZLE or MECHANICAL_SWITCHING_NOZZLE."
#elif ENABLED(MECHANICAL_SWITCHING_NOZZLE)
#if EXTRUDERS != 2
#error "MECHANICAL_SWITCHING_NOZZLE requires exactly 2 EXTRUDERS."
#elif ENABLED(DUAL_X_CARRIAGE)
#error "MECHANICAL_SWITCHING_NOZZLE and DUAL_X_CARRIAGE are incompatible."
#elif ENABLED(SINGLENOZZLE)
#error "MECHANICAL_SWITCHING_NOZZLE and SINGLENOZZLE are incompatible."
#elif HAS_PRUSA_MMU2
#error "MECHANICAL_SWITCHING_NOZZLE and PRUSA_MMU2(S) are incompatible."
#elif !defined(EVENT_GCODE_TOOLCHANGE_T0)
#error "MECHANICAL_SWITCHING_NOZZLE requires EVENT_GCODE_TOOLCHANGE_T0."
#elif !defined(EVENT_GCODE_TOOLCHANGE_T1)
#error "MECHANICAL_SWITCHING_NOZZLE requires EVENT_GCODE_TOOLCHANGE_T1."
#endif
#elif ENABLED(SWITCHING_NOZZLE)
#if EXTRUDERS != 2
#error "SWITCHING_NOZZLE requires exactly 2 EXTRUDERS."
#elif ENABLED(DUAL_X_CARRIAGE)
#error "SWITCHING_NOZZLE and DUAL_X_CARRIAGE are incompatible."
#elif ENABLED(SINGLENOZZLE)
#error "SWITCHING_NOZZLE and SINGLENOZZLE are incompatible."
#elif HAS_PRUSA_MMU2
#error "SWITCHING_NOZZLE and PRUSA_MMU2(S) are incompatible."
#elif NUM_SERVOS < 1
#error "SWITCHING_NOZZLE requires NUM_SERVOS >= 1."
#elif !defined(SWITCHING_NOZZLE_SERVO_NR)
#error "SWITCHING_NOZZLE requires SWITCHING_NOZZLE_SERVO_NR."
#elif SWITCHING_NOZZLE_SERVO_NR == 0 && !PIN_EXISTS(SERVO0)
#error "SERVO0_PIN must be defined for your SWITCHING_NOZZLE."
#elif SWITCHING_NOZZLE_SERVO_NR == 1 && !PIN_EXISTS(SERVO1)
#error "SERVO1_PIN must be defined for your SWITCHING_NOZZLE."
#elif SWITCHING_NOZZLE_SERVO_NR == 2 && !PIN_EXISTS(SERVO2)
#error "SERVO2_PIN must be defined for your SWITCHING_NOZZLE."
#elif SWITCHING_NOZZLE_SERVO_NR == 3 && !PIN_EXISTS(SERVO3)
#error "SERVO3_PIN must be defined for your SWITCHING_NOZZLE."
#endif
#ifdef SWITCHING_NOZZLE_E1_SERVO_NR
#if SWITCHING_NOZZLE_E1_SERVO_NR == SWITCHING_NOZZLE_SERVO_NR
#error "SWITCHING_NOZZLE_E1_SERVO_NR must be different from SWITCHING_NOZZLE_SERVO_NR."
#elif SWITCHING_NOZZLE_E1_SERVO_NR == 0 && !PIN_EXISTS(SERVO0)
#error "SERVO0_PIN must be defined for your SWITCHING_NOZZLE."
#elif SWITCHING_NOZZLE_E1_SERVO_NR == 1 && !PIN_EXISTS(SERVO1)
#error "SERVO1_PIN must be defined for your SWITCHING_NOZZLE."
#elif SWITCHING_NOZZLE_E1_SERVO_NR == 2 && !PIN_EXISTS(SERVO2)
#error "SERVO2_PIN must be defined for your SWITCHING_NOZZLE."
#elif SWITCHING_NOZZLE_E1_SERVO_NR == 3 && !PIN_EXISTS(SERVO3)
#error "SERVO3_PIN must be defined for your SWITCHING_NOZZLE."
#endif
#endif
#endif // SWITCHING_NOZZLE
/**
* Single Stepper Dual Extruder with switching servo
*/
#if ALL(SWITCHING_EXTRUDER, MECHANICAL_SWITCHING_EXTRUDER)
#error "Enable only one of SWITCHING_EXTRUDER or MECHANICAL_SWITCHING_EXTRUDER."
#elif ENABLED(MECHANICAL_SWITCHING_EXTRUDER)
#if EXTRUDERS < 2
#error "MECHANICAL_SWITCHING_EXTRUDER requires EXTRUDERS >= 2."
#elif !defined(EVENT_GCODE_TOOLCHANGE_T0)
#error "MECHANICAL_SWITCHING_EXTRUDER requires EVENT_GCODE_TOOLCHANGE_T0."
#elif !defined(EVENT_GCODE_TOOLCHANGE_T1)
#error "MECHANICAL_SWITCHING_EXTRUDER requires EVENT_GCODE_TOOLCHANGE_T1."
#endif
#elif ENABLED(SWITCHING_EXTRUDER)
#if NUM_SERVOS < 1
#error "SWITCHING_EXTRUDER requires NUM_SERVOS >= 1."
#elif !defined(SWITCHING_EXTRUDER_SERVO_NR)
#error "SWITCHING_EXTRUDER requires SWITCHING_EXTRUDER_SERVO_NR."
#elif SWITCHING_EXTRUDER_SERVO_NR == 0 && !PIN_EXISTS(SERVO0)
#error "SERVO0_PIN must be defined for your SWITCHING_EXTRUDER."
#elif SWITCHING_EXTRUDER_SERVO_NR == 1 && !PIN_EXISTS(SERVO1)
#error "SERVO1_PIN must be defined for your SWITCHING_EXTRUDER."
#elif SWITCHING_EXTRUDER_SERVO_NR == 2 && !PIN_EXISTS(SERVO2)
#error "SERVO2_PIN must be defined for your SWITCHING_EXTRUDER."
#elif SWITCHING_EXTRUDER_SERVO_NR == 3 && !PIN_EXISTS(SERVO3)
#error "SERVO3_PIN must be defined for your SWITCHING_EXTRUDER."
#endif
#if EXTRUDERS > 3
#if NUM_SERVOS < 2
#error "SWITCHING_EXTRUDER with 4 extruders requires NUM_SERVOS >= 2."
#elif SWITCHING_EXTRUDER_E23_SERVO_NR == 0 && !PIN_EXISTS(SERVO0)
#error "SERVO0_PIN must be defined for your SWITCHING_EXTRUDER."
#elif SWITCHING_EXTRUDER_E23_SERVO_NR == 1 && !PIN_EXISTS(SERVO1)
#error "SERVO1_PIN must be defined for your SWITCHING_EXTRUDER."
#elif SWITCHING_EXTRUDER_E23_SERVO_NR == 2 && !PIN_EXISTS(SERVO2)
#error "SERVO2_PIN must be defined for your SWITCHING_EXTRUDER."
#elif SWITCHING_EXTRUDER_E23_SERVO_NR == 3 && !PIN_EXISTS(SERVO3)
#error "SERVO3_PIN must be defined for your SWITCHING_EXTRUDER."
#elif SWITCHING_EXTRUDER_E23_SERVO_NR == SWITCHING_EXTRUDER_SERVO_NR
#error "SWITCHING_EXTRUDER_E23_SERVO_NR should be a different extruder from SWITCHING_EXTRUDER_SERVO_NR."
#endif
#elif EXTRUDERS < 2
#error "SWITCHING_EXTRUDER requires EXTRUDERS >= 2."
#endif
#endif // SWITCHING_EXTRUDER
/**
* Mixing Extruder requirements
*/
#if ENABLED(MIXING_EXTRUDER)
#if HAS_MULTI_EXTRUDER
#error "For MIXING_EXTRUDER set MIXING_STEPPERS > 1 instead of EXTRUDERS > 1."
#elif MIXING_STEPPERS < 2
#error "You must set MIXING_STEPPERS >= 2 for a mixing extruder."
#elif ENABLED(FILAMENT_WIDTH_SENSOR)
#error "MIXING_EXTRUDER is incompatible with FILAMENT_WIDTH_SENSOR. Comment out this line to use it anyway."
#elif HAS_SWITCHING_EXTRUDER
#error "MIXING_EXTRUDER is incompatible with (MECHANICAL_)SWITCHING_EXTRUDER."
#elif ENABLED(SINGLENOZZLE)
#error "MIXING_EXTRUDER is incompatible with SINGLENOZZLE."
#elif ENABLED(DISABLE_OTHER_EXTRUDERS)
#error "MIXING_EXTRUDER is incompatible with DISABLE_OTHER_EXTRUDERS."
#elif HAS_FILAMENT_RUNOUT_DISTANCE
#error "MIXING_EXTRUDER is incompatible with FILAMENT_RUNOUT_DISTANCE_MM."
#endif
#endif
/**
* Dual E Steppers requirements
*/
#if ENABLED(E_DUAL_STEPPER_DRIVERS)
#if EXTRUDERS > 1
#error "E_DUAL_STEPPER_DRIVERS can only be used with EXTRUDERS set to 1."
#elif ENABLED(MIXING_EXTRUDER)
#error "E_DUAL_STEPPER_DRIVERS is incompatible with MIXING_EXTRUDER."
#elif HAS_SWITCHING_EXTRUDER
#error "E_DUAL_STEPPER_DRIVERS is incompatible with (MECHANICAL_)SWITCHING_EXTRUDER."
#endif
#endif
/**
* Linear Advance 1.5 - Check K value range
*/
#if ENABLED(LIN_ADVANCE)
#if DISTINCT_E > 1
constexpr float lak[] = ADVANCE_K;
static_assert(COUNT(lak) <= DISTINCT_E, "The ADVANCE_K array has too many elements (i.e., more than " STRINGIFY(DISTINCT_E) ").");
#define _LIN_ASSERT(N) static_assert(N >= COUNT(lak) || WITHIN(lak[N], 0, 10), "ADVANCE_K values must be from 0 to 10 (Changed in LIN_ADVANCE v1.5, Marlin 1.1.9).");
REPEAT(DISTINCT_E, _LIN_ASSERT)
#undef _LIN_ASSERT
#else
static_assert(WITHIN(ADVANCE_K, 0, 10), "ADVANCE_K must be from 0 to 10 (Changed in LIN_ADVANCE v1.5, Marlin 1.1.9).");
#endif
#if ENABLED(DIRECT_STEPPING)
#error "DIRECT_STEPPING is incompatible with LIN_ADVANCE. (Extrusion is controlled externally by the Step Daemon.)"
#endif
#endif
/**
* Nonlinear Extrusion requirements
*/
#if ENABLED(NONLINEAR_EXTRUSION)
#if HAS_MULTI_EXTRUDER
#error "NONLINEAR_EXTRUSION doesn't currently support multi-extruder setups."
#elif DISABLED(CPU_32_BIT)
#error "NONLINEAR_EXTRUSION requires a 32-bit CPU."
#endif
#endif
/**
* Special tool-changing options
*/
#if MANY(SINGLENOZZLE, DUAL_X_CARRIAGE, PARKING_EXTRUDER, MAGNETIC_PARKING_EXTRUDER, SWITCHING_TOOLHEAD, MAGNETIC_SWITCHING_TOOLHEAD, ELECTROMAGNETIC_SWITCHING_TOOLHEAD)
#error "Please select only one of SINGLENOZZLE, DUAL_X_CARRIAGE, PARKING_EXTRUDER, MAGNETIC_PARKING_EXTRUDER, SWITCHING_TOOLHEAD, MAGNETIC_SWITCHING_TOOLHEAD, or ELECTROMAGNETIC_SWITCHING_TOOLHEAD."
#endif
/**
* (Magnetic) Parking Extruder requirements
*/
#if ANY(PARKING_EXTRUDER, MAGNETIC_PARKING_EXTRUDER)
#if ENABLED(EXT_SOLENOID)
#error "(MAGNETIC_)PARKING_EXTRUDER and EXT_SOLENOID are incompatible. (Pins are used twice.)"
#elif EXTRUDERS != 2
#error "(MAGNETIC_)PARKING_EXTRUDER requires exactly 2 EXTRUDERS."
#elif !defined(PARKING_EXTRUDER_PARKING_X)
#error "(MAGNETIC_)PARKING_EXTRUDER requires PARKING_EXTRUDER_PARKING_X."
#elif !defined(TOOLCHANGE_ZRAISE)
#error "(MAGNETIC_)PARKING_EXTRUDER requires TOOLCHANGE_ZRAISE."
#elif TOOLCHANGE_ZRAISE < 0
#error "TOOLCHANGE_ZRAISE must be 0 or higher."
#elif ENABLED(PARKING_EXTRUDER)
#if !PINS_EXIST(SOL0, SOL1)
#error "PARKING_EXTRUDER requires SOL0_PIN and SOL1_PIN."
#elif !defined(PARKING_EXTRUDER_SOLENOIDS_PINS_ACTIVE) || !WITHIN(PARKING_EXTRUDER_SOLENOIDS_PINS_ACTIVE, LOW, HIGH)
#error "PARKING_EXTRUDER_SOLENOIDS_PINS_ACTIVE must be defined as HIGH or LOW."
#elif !defined(PARKING_EXTRUDER_SOLENOIDS_DELAY) || !WITHIN(PARKING_EXTRUDER_SOLENOIDS_DELAY, 0, 2000)
#error "PARKING_EXTRUDER_SOLENOIDS_DELAY must be between 0 and 2000 (ms)."
#endif
#endif
#endif
/**
* Generic Switching Toolhead requirements
*/
#if ANY(SWITCHING_TOOLHEAD, MAGNETIC_SWITCHING_TOOLHEAD, ELECTROMAGNETIC_SWITCHING_TOOLHEAD)
constexpr float thpx[] = SWITCHING_TOOLHEAD_X_POS;
#if HAS_TOOL_LENGTH_COMPENSATION
static_assert(COUNT(thpx) == TOOLS, "SWITCHING_TOOLHEAD_X_POS must be an array TOOLS long.");
#else
static_assert(COUNT(thpx) == EXTRUDERS, "SWITCHING_TOOLHEAD_X_POS must be an array EXTRUDERS long.");
#endif
#endif
/**
* Switching Toolhead requirements
*/
#if ENABLED(SWITCHING_TOOLHEAD)
#ifndef SWITCHING_TOOLHEAD_SERVO_NR
#error "SWITCHING_TOOLHEAD requires SWITCHING_TOOLHEAD_SERVO_NR."
#elif HAS_TOOL_LENGTH_COMPENSATION && (TOOLS < 2)
#error "SWITCHING_TOOLHEAD requires at least 2 TOOLS."
#elif (!HAS_TOOL_LENGTH_COMPENSATION) && (EXTRUDERS < 2)
#error "SWITCHING_TOOLHEAD requires at least 2 EXTRUDERS."
#elif NUM_SERVOS < (SWITCHING_TOOLHEAD_SERVO_NR - 1)
#if SWITCHING_TOOLHEAD_SERVO_NR == 0
#error "A SWITCHING_TOOLHEAD_SERVO_NR of 0 requires NUM_SERVOS >= 1."
#elif SWITCHING_TOOLHEAD_SERVO_NR == 1
#error "A SWITCHING_TOOLHEAD_SERVO_NR of 1 requires NUM_SERVOS >= 2."
#elif SWITCHING_TOOLHEAD_SERVO_NR == 2
#error "A SWITCHING_TOOLHEAD_SERVO_NR of 2 requires NUM_SERVOS >= 3."
#elif SWITCHING_TOOLHEAD_SERVO_NR == 3
#error "A SWITCHING_TOOLHEAD_SERVO_NR of 3 requires NUM_SERVOS >= 4."
#endif
#elif !defined(TOOLCHANGE_ZRAISE)
#error "SWITCHING_TOOLHEAD requires TOOLCHANGE_ZRAISE."
#elif TOOLCHANGE_ZRAISE < 0
#error "TOOLCHANGE_ZRAISE must be 0 or higher."
#endif
#endif
/**
* Magnetic / Electromagnetic Switching Toolhead requirements
*/
#if ANY(MAGNETIC_SWITCHING_TOOLHEAD, ELECTROMAGNETIC_SWITCHING_TOOLHEAD)
#ifndef SWITCHING_TOOLHEAD_Y_POS
#error "(ELECTRO)?MAGNETIC_SWITCHING_TOOLHEAD requires SWITCHING_TOOLHEAD_Y_POS"
#elif !defined(SWITCHING_TOOLHEAD_X_POS)
#error "(ELECTRO)?MAGNETIC_SWITCHING_TOOLHEAD requires SWITCHING_TOOLHEAD_X_POS"
#elif !defined(SWITCHING_TOOLHEAD_Y_CLEAR)
#error "(ELECTRO)?MAGNETIC_SWITCHING_TOOLHEAD requires SWITCHING_TOOLHEAD_Y_CLEAR."
#endif
#endif
/**
* Electromagnetic Switching Toolhead requirements
*/
#if ENABLED(ELECTROMAGNETIC_SWITCHING_TOOLHEAD)
#if ENABLED(EXT_SOLENOID)
#error "ELECTROMAGNETIC_SWITCHING_TOOLHEAD and EXT_SOLENOID are incompatible. (Pins are used twice.)"
#elif !PIN_EXISTS(SOL0)
#error "ELECTROMAGNETIC_SWITCHING_TOOLHEAD requires SOL0_PIN."
#elif !defined(SWITCHING_TOOLHEAD_Z_HOP)
#error "ELECTROMAGNETIC_SWITCHING_TOOLHEAD requires SWITCHING_TOOLHEAD_Z_HOP."
#endif
#endif
/**
* Part-Cooling Fan Multiplexer requirements
*/
#if HAS_FANMUX && !HAS_FAN0
#error "FAN0_PIN must be defined to use Fan Multiplexing."
#elif PIN_EXISTS(FANMUX1) && !PIN_EXISTS(FANMUX0)
#error "FANMUX0_PIN must be set before FANMUX1_PIN can be set."
#elif PIN_EXISTS(FANMUX2) && !PINS_EXIST(FANMUX0, FANMUX1)
#error "FANMUX0_PIN and FANMUX1_PIN must be set before FANMUX2_PIN can be set."
#endif
// PID Fan Scaling requires a fan
#if defined(PID_FAN_SCALING) && !HAS_FAN
#error "PID_FAN_SCALING needs at least one fan enabled."
#endif
/**
* Limited user-controlled fans
*/
#if NUM_M106_FANS > FAN_COUNT
#error "The selected board doesn't support enough user-controlled fans. Reduce NUM_M106_FANS."
#endif
/**
* Limited number of servos
*/
static_assert(NUM_SERVOS <= NUM_SERVO_PLUGS, "NUM_SERVOS (or some servo index) is too large. The selected board only has " STRINGIFY(NUM_SERVO_PLUGS) " servos.");
/**
* Servo deactivation depends on servo endstops, switching nozzle, or switching extruder
*/
#if ENABLED(DEACTIVATE_SERVOS_AFTER_MOVE) && NONE(HAS_Z_SERVO_PROBE, POLARGRAPH) && !defined(SWITCHING_NOZZLE_SERVO_NR) && !defined(SWITCHING_EXTRUDER_SERVO_NR) && !defined(SWITCHING_TOOLHEAD_SERVO_NR)
#error "Z_PROBE_SERVO_NR, switching nozzle, switching toolhead, switching extruder, or POLARGRAPH is required for DEACTIVATE_SERVOS_AFTER_MOVE."
#endif
/**
* Required LCD language
*/
#if HAS_MARLINUI_HD44780 && !defined(DISPLAY_CHARSET_HD44780)
#error "You must set DISPLAY_CHARSET_HD44780 to JAPANESE, WESTERN or CYRILLIC for your LCD controller."
#endif
/**
* Extruder temperature control algorithm - There can be only one!
*/
#if ALL(PIDTEMP, MPCTEMP)
#error "Only enable PIDTEMP or MPCTEMP, but not both."
#undef MPCTEMP
#undef MPC_AUTOTUNE
#undef MPC_EDIT_MENU
#undef MPC_AUTOTUNE_MENU
#endif
#if ENABLED(MPC_INCLUDE_FAN)
#if !HAS_FAN
#error "MPC_INCLUDE_FAN requires at least one fan."
#endif