From bbf20332112948aad99bb8c7fba27fd661dc3a3a Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Thu, 4 Aug 2022 02:38:15 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20Config=20INI,=20dump=20options?= =?UTF-8?q?=20(#24528)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Marlin/Configuration.h | 101 +++-- Marlin/Configuration_adv.h | 102 ++++- Marlin/config.ini | 211 +++++++++ Marlin/src/MarlinCore.cpp | 4 +- Marlin/src/core/macros.h | 5 + Marlin/src/gcode/temp/M303.cpp | 2 +- Marlin/src/inc/Conditionals_LCD.h | 53 +++ Marlin/src/inc/Conditionals_adv.h | 39 +- Marlin/src/inc/Conditionals_post.h | 12 +- Marlin/src/inc/SanityCheck.h | 8 +- Marlin/src/module/temperature.cpp | 37 +- Marlin/src/module/temperature.h | 4 +- .../share/PlatformIO/scripts/configuration.py | 239 +++++++++++ buildroot/share/PlatformIO/scripts/schema.py | 403 ++++++++++++++++++ .../share/PlatformIO/scripts/signature.py | 125 +++++- platformio.ini | 4 +- 16 files changed, 1249 insertions(+), 100 deletions(-) create mode 100644 Marlin/config.ini create mode 100644 buildroot/share/PlatformIO/scripts/configuration.py create mode 100755 buildroot/share/PlatformIO/scripts/schema.py diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index 986e3c49ab..9259468374 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -112,6 +112,7 @@ * :[2400, 9600, 19200, 38400, 57600, 115200, 250000, 500000, 1000000] */ #define BAUDRATE 250000 + //#define BAUD_RATE_GCODE // Enable G-code M575 to set the baud rate /** @@ -120,7 +121,7 @@ * :[-2, -1, 0, 1, 2, 3, 4, 5, 6, 7] */ //#define SERIAL_PORT_2 -1 -//#define BAUDRATE_2 250000 // Enable to override BAUDRATE +//#define BAUDRATE_2 250000 // :[2400, 9600, 19200, 38400, 57600, 115200, 250000, 500000, 1000000] Enable to override BAUDRATE /** * Select a third serial port on the board to use for communication with the host. @@ -128,7 +129,7 @@ * :[-1, 0, 1, 2, 3, 4, 5, 6, 7] */ //#define SERIAL_PORT_3 1 -//#define BAUDRATE_3 250000 // Enable to override BAUDRATE +//#define BAUDRATE_3 250000 // :[2400, 9600, 19200, 38400, 57600, 115200, 250000, 500000, 1000000] Enable to override BAUDRATE // Enable the Bluetooth serial interface on AT90USB devices //#define BLUETOOTH @@ -387,7 +388,7 @@ //#define HOTEND_OFFSET_Y { 0.0, 5.00 } // (mm) relative Y-offset for each nozzle //#define HOTEND_OFFSET_Z { 0.0, 0.00 } // (mm) relative Z-offset for each nozzle -// @section machine +// @section psu control /** * Power Supply Control @@ -549,22 +550,32 @@ #define DUMMY_THERMISTOR_999_VALUE 100 // Resistor values when using MAX31865 sensors (-5) on TEMP_SENSOR_0 / 1 -//#define MAX31865_SENSOR_OHMS_0 100 // (Ω) Typically 100 or 1000 (PT100 or PT1000) -//#define MAX31865_CALIBRATION_OHMS_0 430 // (Ω) Typically 430 for Adafruit PT100; 4300 for Adafruit PT1000 -//#define MAX31865_SENSOR_OHMS_1 100 -//#define MAX31865_CALIBRATION_OHMS_1 430 +#if TEMP_SENSOR_IS_MAX_TC(0) + #define MAX31865_SENSOR_OHMS_0 100 // (Ω) Typically 100 or 1000 (PT100 or PT1000) + #define MAX31865_CALIBRATION_OHMS_0 430 // (Ω) Typically 430 for Adafruit PT100; 4300 for Adafruit PT1000 +#endif +#if TEMP_SENSOR_IS_MAX_TC(1) + #define MAX31865_SENSOR_OHMS_1 100 + #define MAX31865_CALIBRATION_OHMS_1 430 +#endif -#define TEMP_RESIDENCY_TIME 10 // (seconds) Time to wait for hotend to "settle" in M109 -#define TEMP_WINDOW 1 // (°C) Temperature proximity for the "temperature reached" timer -#define TEMP_HYSTERESIS 3 // (°C) Temperature proximity considered "close enough" to the target +#if HAS_E_TEMP_SENSOR + #define TEMP_RESIDENCY_TIME 10 // (seconds) Time to wait for hotend to "settle" in M109 + #define TEMP_WINDOW 1 // (°C) Temperature proximity for the "temperature reached" timer + #define TEMP_HYSTERESIS 3 // (°C) Temperature proximity considered "close enough" to the target +#endif -#define TEMP_BED_RESIDENCY_TIME 10 // (seconds) Time to wait for bed to "settle" in M190 -#define TEMP_BED_WINDOW 1 // (°C) Temperature proximity for the "temperature reached" timer -#define TEMP_BED_HYSTERESIS 3 // (°C) Temperature proximity considered "close enough" to the target +#if TEMP_SENSOR_BED + #define TEMP_BED_RESIDENCY_TIME 10 // (seconds) Time to wait for bed to "settle" in M190 + #define TEMP_BED_WINDOW 1 // (°C) Temperature proximity for the "temperature reached" timer + #define TEMP_BED_HYSTERESIS 3 // (°C) Temperature proximity considered "close enough" to the target +#endif -#define TEMP_CHAMBER_RESIDENCY_TIME 10 // (seconds) Time to wait for chamber to "settle" in M191 -#define TEMP_CHAMBER_WINDOW 1 // (°C) Temperature proximity for the "temperature reached" timer -#define TEMP_CHAMBER_HYSTERESIS 3 // (°C) Temperature proximity considered "close enough" to the target +#if TEMP_SENSOR_CHAMBER + #define TEMP_CHAMBER_RESIDENCY_TIME 10 // (seconds) Time to wait for chamber to "settle" in M191 + #define TEMP_CHAMBER_WINDOW 1 // (°C) Temperature proximity for the "temperature reached" timer + #define TEMP_CHAMBER_HYSTERESIS 3 // (°C) Temperature proximity considered "close enough" to the target +#endif /** * Redundant Temperature Sensor (TEMP_SENSOR_REDUNDANT) @@ -623,6 +634,8 @@ //============================= PID Settings ================================ //=========================================================================== +// @section hotend temp + // Enable PIDTEMP for PID control or MPCTEMP for Predictive Model. // temperature control. Disable both for bang-bang heating. #define PIDTEMP // See the PID Tuning Guide at https://reprap.org/wiki/PID_Tuning @@ -633,7 +646,8 @@ #define PID_K1 0.95 // Smoothing factor within any PID loop #if ENABLED(PIDTEMP) - //#define PID_PARAMS_PER_HOTEND // Uses separate PID parameters for each extruder (useful for mismatched extruders) + //#define PID_DEBUG // Print PID debug data to the serial port. Use 'M303 D' to toggle activation. + //#define PID_PARAMS_PER_HOTEND // Use separate PID parameters for each extruder (useful for mismatched extruders) // Set/get with G-code: M301 E[extruder number, 0-2] #if ENABLED(PID_PARAMS_PER_HOTEND) @@ -655,6 +669,7 @@ * Use a physical model of the hotend to control temperature. When configured correctly * this gives better responsiveness and stability than PID and it also removes the need * for PID_EXTRUSION_SCALING and PID_FAN_SCALING. Use M306 T to autotune the model. + * @section mpctemp */ #if ENABLED(MPCTEMP) //#define MPC_EDIT_MENU // Add MPC editing to the "Advanced Settings" menu. (~1300 bytes of flash) @@ -707,6 +722,7 @@ * impact FET heating. This also works fine on a Fotek SSR-10DA Solid State Relay into a 250W * heater. If your configuration is significantly different than this and you don't understand * the issues involved, don't use bed PID until someone else verifies that your hardware works. + * @section bed temp */ //#define PIDTEMPBED @@ -722,7 +738,7 @@ #if ENABLED(PIDTEMPBED) //#define MIN_BED_POWER 0 - //#define PID_BED_DEBUG // Sends debug data to the serial port. + //#define PID_BED_DEBUG // Print Bed PID debug data to the serial port. // 120V 250W silicone heater into 4mm borosilicate (MendelMax 1.5+) // from FOPDT model - kp=.39 Tp=405 Tdead=66, Tc set to 79.2, aggressive factor of .15 (vs .1, 1, 10) @@ -750,6 +766,7 @@ * impact FET heating. This also works fine on a Fotek SSR-10DA Solid State Relay into a 200W * heater. If your configuration is significantly different than this and you don't understand * the issues involved, don't use chamber PID until someone else verifies that your hardware works. + * @section chamber temp */ //#define PIDTEMPCHAMBER //#define CHAMBER_LIMIT_SWITCHING @@ -764,7 +781,7 @@ #if ENABLED(PIDTEMPCHAMBER) #define MIN_CHAMBER_POWER 0 - //#define PID_CHAMBER_DEBUG // Sends debug data to the serial port. + //#define PID_CHAMBER_DEBUG // Print Chamber PID debug data to the serial port. // Lasko "MyHeat Personal Heater" (200w) modified with a Fotek SSR-10DA to control only the heating element // and placed inside the small Creality printer enclosure tent. @@ -778,7 +795,6 @@ #endif // PIDTEMPCHAMBER #if ANY(PIDTEMP, PIDTEMPBED, PIDTEMPCHAMBER) - //#define PID_DEBUG // Sends debug data to the serial port. Use 'M303 D' to toggle activation. //#define PID_OPENLOOP // Puts PID in open loop. M104/M140 sets the output power from 0 to PID_MAX //#define SLOW_PWM_HEATERS // PWM with very low frequency (roughly 0.125Hz=8s) and minimum state time of approximately 1s useful for heaters driven by a relay #define PID_FUNCTIONAL_RANGE 10 // If the temperature difference between the target temperature and the actual temperature @@ -788,7 +804,7 @@ //#define PID_AUTOTUNE_MENU // Add PID auto-tuning to the "Advanced Settings" menu. (~250 bytes of flash) #endif -// @section extruder +// @section safety /** * Prevent extrusion if the temperature is below EXTRUDE_MINTEMP. @@ -856,6 +872,8 @@ #define POLAR_SEGMENTS_PER_SECOND 5 #endif +// @section delta + // Enable for DELTA kinematics and configure below //#define DELTA #if ENABLED(DELTA) @@ -915,6 +933,8 @@ //#define DELTA_DIAGONAL_ROD_TRIM_TOWER { 0.0, 0.0, 0.0 } #endif +// @section scara + /** * MORGAN_SCARA was developed by QHARLEY in South Africa in 2012-2013. * Implemented and slightly reworked by JCERNY in June, 2014. @@ -958,6 +978,8 @@ #endif +// @section tpara + // Enable for TPARA kinematics and configure below //#define AXEL_TPARA #if ENABLED(AXEL_TPARA) @@ -984,6 +1006,8 @@ #define PSI_HOMING_OFFSET 0 #endif +// @section machine + // Articulated robot (arm). Joints are directly mapped to axes with no kinematics. //#define ARTICULATED_ROBOT_ARM @@ -995,7 +1019,7 @@ //============================== Endstop Settings =========================== //=========================================================================== -// @section homing +// @section endstops // Specify here all the endstop connectors that are connected to any endstop or probe. // Almost all printers will be using one per axis. Probes will use one or more of the @@ -1659,7 +1683,7 @@ //#define V_HOME_DIR -1 //#define W_HOME_DIR -1 -// @section machine +// @section geometry // The size of the printable area #define X_BED_SIZE 200 @@ -2119,7 +2143,7 @@ //============================= Additional Features =========================== //============================================================================= -// @section extras +// @section eeprom /** * EEPROM @@ -2139,6 +2163,8 @@ //#define EEPROM_INIT_NOW // Init EEPROM on first boot after a new build. #endif +// @section host + // // Host Keepalive // @@ -2149,6 +2175,8 @@ #define DEFAULT_KEEPALIVE_INTERVAL 2 // Number of seconds between "busy" messages. Set with M113. #define BUSY_WHILE_HEATING // Some hosts require "busy" messages even during heating +// @section units + // // G20/G21 Inch mode support // @@ -2176,6 +2204,8 @@ #define PREHEAT_2_TEMP_CHAMBER 35 #define PREHEAT_2_FAN_SPEED 0 // Value from 0 to 255 +// @section motion + /** * Nozzle Park * @@ -2274,6 +2304,8 @@ #endif +// @section host + /** * Print Job Timer * @@ -2300,6 +2332,8 @@ */ #define PRINTJOB_TIMER_AUTOSTART +// @section stats + /** * Print Counter * @@ -2317,6 +2351,8 @@ #define PRINTCOUNTER_SAVE_INTERVAL 60 // (minutes) EEPROM save interval during print #endif +// @section security + /** * Password * @@ -2352,7 +2388,7 @@ //============================= LCD and SD support ============================ //============================================================================= -// @section lcd +// @section interface /** * LCD LANGUAGE @@ -2508,6 +2544,7 @@ //======================== LCD / Controller Selection ========================= //======================== (Character-based LCDs) ========================= //============================================================================= +// @section lcd // // RepRapDiscount Smart Controller. @@ -3142,7 +3179,7 @@ //=============================== Extra Features ============================== //============================================================================= -// @section extras +// @section fans // Set number of user-controlled fans. Disable to use all board-defined fans. // :[1,2,3,4,5,6,7,8] @@ -3166,14 +3203,18 @@ // duty cycle is attained. //#define SOFT_PWM_DITHER +// @section extras + +// Support for the BariCUDA Paste Extruder +//#define BARICUDA + +// @section lights + // Temperature status LEDs that display the hotend and bed temperature. // If all hotends, bed temperature, and target temperature are under 54C // then the BLUE led is on. Otherwise the RED led is on. (1C hysteresis) //#define TEMP_STAT_LEDS -// Support for the BariCUDA Paste Extruder -//#define BARICUDA - // Support for BlinkM/CyzRgb //#define BLINKM @@ -3259,6 +3300,8 @@ #define PRINTER_EVENT_LEDS #endif +// @section servos + /** * Number of servos * diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 3d69cb3feb..b076949a88 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -32,6 +32,24 @@ */ #define CONFIGURATION_ADV_H_VERSION 02010100 +// @section develop + +/** + * Configuration Export + * + * Export the configuration as part of the build. (See signature.py) + * Output files are saved with the build (e.g., .pio/build/mega2560). + * + * See `build_all_examples --ini` as an example of config.ini archiving. + * + * 1 = marlin_config.json - Dictionary containing the configuration. + * This file is also generated for CONFIGURATION_EMBEDDING. + * 2 = config.ini - File format for PlatformIO preprocessing. + * 3 = schema.json - The entire configuration schema. (13 = pattern groups) + * 4 = schema.yml - The entire configuration schema. + */ +//#define CONFIG_EXPORT // :[1:'JSON', 2:'config.ini', 3:'schema.json', 4:'schema.yml'] + //=========================================================================== //============================= Thermal Settings ============================ //=========================================================================== @@ -2545,6 +2563,8 @@ #endif #endif // HAS_MULTI_EXTRUDER +// @section advanced pause + /** * Advanced Pause for Filament Change * - Adds the G-code M600 Filament Change to initiate a filament change. @@ -2603,13 +2623,12 @@ //#define FILAMENT_UNLOAD_ALL_EXTRUDERS // Allow M702 to unload all extruders above a minimum target temp (as set by M302) #endif -// @section tmc - /** * TMC26X Stepper Driver options * * The TMC26XStepper library is required for this stepper driver. * https://github.com/trinamic/TMC26XStepper + * @section tmc/tmc26x */ #if HAS_DRIVER(TMC26X) @@ -2747,8 +2766,6 @@ #endif // TMC26X -// @section tmc_smart - /** * To use TMC2130, TMC2160, TMC2660, TMC5130, TMC5160 stepper drivers in SPI mode * connect your SPI pins to the hardware SPI interface on your board and define @@ -2764,6 +2781,7 @@ * * TMCStepper library is required to use TMC stepper drivers. * https://github.com/teemuatlut/TMCStepper + * @section tmc/config */ #if HAS_TRINAMIC_CONFIG @@ -2987,6 +3005,8 @@ //#define E7_HOLD_MULTIPLIER 0.5 #endif + // @section tmc/spi + /** * Override default SPI pins for TMC2130, TMC2160, TMC2660, TMC5130 and TMC5160 drivers here. * The default pins can be found in your board's pins file. @@ -3024,6 +3044,8 @@ //#define TMC_SW_MISO -1 //#define TMC_SW_SCK -1 + // @section tmc/serial + /** * Four TMC2209 drivers can use the same HW/SW serial port with hardware configured addresses. * Set the address using jumpers on pins MS1 and MS2. @@ -3059,6 +3081,8 @@ //#define E6_SLAVE_ADDRESS 0 //#define E7_SLAVE_ADDRESS 0 + // @section tmc/smart + /** * Software enable * @@ -3067,6 +3091,8 @@ */ //#define SOFTWARE_DRIVER_ENABLE + // @section tmc/stealthchop + /** * TMC2130, TMC2160, TMC2208, TMC2209, TMC5130 and TMC5160 only * Use Trinamic's ultra quiet stepping mode. @@ -3121,6 +3147,8 @@ //#define CHOPPER_TIMING_E6 CHOPPER_TIMING_E //#define CHOPPER_TIMING_E7 CHOPPER_TIMING_E + // @section tmc/status + /** * Monitor Trinamic drivers * for error conditions like overtemperature and short to ground. @@ -3140,6 +3168,8 @@ #define STOP_ON_ERROR #endif + // @section tmc/hybrid + /** * TMC2130, TMC2160, TMC2208, TMC2209, TMC5130 and TMC5160 only * The driver will switch to spreadCycle when stepper speed is over HYBRID_THRESHOLD. @@ -3196,6 +3226,7 @@ * homing and adds a guard period for endstop triggering. * * Comment *_STALL_SENSITIVITY to disable sensorless homing for that axis. + * @section tmc/stallguard */ //#define SENSORLESS_HOMING // StallGuard capable drivers only @@ -3219,6 +3250,8 @@ //#define IMPROVE_HOMING_RELIABILITY #endif + // @section tmc/config + /** * TMC Homing stepper phase. * @@ -3258,7 +3291,6 @@ #endif // HAS_TRINAMIC_CONFIG - // @section i2cbus // @@ -3300,7 +3332,7 @@ #define I2C_SLAVE_ADDRESS 0 // Set a value from 8 to 127 to act as a slave #endif -// @section extras +// @section photo /** * Photo G-code @@ -3343,6 +3375,8 @@ #endif #endif +// @section cnc + /** * Spindle & Laser control * @@ -3546,6 +3580,8 @@ #define COOLANT_FLOOD_INVERT false // Set "true" if the on/off function is reversed #endif +// @section filament width + /** * Filament Width Sensor * @@ -3579,6 +3615,8 @@ //#define FILAMENT_LCD_DISPLAY #endif +// @section power + /** * Power Monitor * Monitor voltage (V) and/or current (A), and -when possible- power (W) @@ -3602,6 +3640,8 @@ #define POWER_MONITOR_VOLTAGE_OFFSET 0 // Offset (in volts) applied to the calculated voltage #endif +// @section safety + /** * Stepper Driver Anti-SNAFU Protection * @@ -3611,6 +3651,8 @@ */ //#define DISABLE_DRIVER_SAFE_POWER_PROTECT +// @section cnc + /** * CNC Coordinate Systems * @@ -3619,6 +3661,8 @@ */ //#define CNC_COORDINATE_SYSTEMS +// @section reporting + /** * Auto-report fan speed with M123 S * Requires fans with tachometer pins @@ -3646,6 +3690,8 @@ //#define M115_GEOMETRY_REPORT #endif +// @section security + /** * Expected Printer Check * Add the M16 G-code to compare a string to the MACHINE_NAME. @@ -3653,6 +3699,8 @@ */ //#define EXPECTED_PRINTER_CHECK +// @section volumetrics + /** * Disable all Volumetric extrusion options */ @@ -3681,14 +3729,7 @@ #endif #endif -/** - * Enable this option for a leaner build of Marlin that removes all - * workspace offsets, simplifying coordinate transformations, leveling, etc. - * - * - M206 and M428 are disabled. - * - G92 will revert to its behavior from Marlin 1.0. - */ -//#define NO_WORKSPACE_OFFSETS +// @section reporting // Extra options for the M114 "Current Position" report //#define M114_DETAIL // Use 'M114` for details to check planner calculations @@ -3697,6 +3738,8 @@ //#define REPORT_FAN_CHANGE // Report the new fan speed when changed by M106 (and others) +// @section gcode + /** * Spend 28 bytes of SRAM to optimize the G-code parser */ @@ -3714,6 +3757,15 @@ //#define REPETIER_GCODE_M360 // Add commands originally from Repetier FW +/** + * Enable this option for a leaner build of Marlin that removes all + * workspace offsets, simplifying coordinate transformations, leveling, etc. + * + * - M206 and M428 are disabled. + * - G92 will revert to its behavior from Marlin 1.0. + */ +//#define NO_WORKSPACE_OFFSETS + /** * CNC G-code options * Support CNC-style G-code dialects used by laser cutters, drawing machine cams, etc. @@ -3729,6 +3781,8 @@ //#define VARIABLE_G0_FEEDRATE // The G0 feedrate is set by F in G0 motion mode #endif +// @section gcode + /** * Startup commands * @@ -3753,6 +3807,8 @@ * Up to 25 may be defined, but the actual number is LCD-dependent. */ +// @section custom main menu + // Custom Menu: Main Menu //#define CUSTOM_MENU_MAIN #if ENABLED(CUSTOM_MENU_MAIN) @@ -3783,6 +3839,8 @@ //#define MAIN_MENU_ITEM_5_CONFIRM #endif +// @section custom config menu + // Custom Menu: Configuration Menu //#define CUSTOM_MENU_CONFIG #if ENABLED(CUSTOM_MENU_CONFIG) @@ -3813,6 +3871,8 @@ //#define CONFIG_MENU_ITEM_5_CONFIRM #endif +// @section custom buttons + /** * User-defined buttons to run custom G-code. * Up to 25 may be defined. @@ -3844,6 +3904,8 @@ #endif #endif +// @section host + /** * Host Action Commands * @@ -3869,6 +3931,8 @@ //#define HOST_SHUTDOWN_MENU_ITEM // Add a menu item that tells the host to shut down #endif +// @section extras + /** * Cancel Objects * @@ -3890,6 +3954,7 @@ * Alternative Supplier: https://reliabuild3d.com/ * * Reliabuild encoders have been modified to improve reliability. + * @section i2c encoders */ //#define I2C_POSITION_ENCODERS @@ -3961,6 +4026,7 @@ /** * Analog Joystick(s) + * @section joystick */ //#define JOYSTICK #if ENABLED(JOYSTICK) @@ -3985,6 +4051,7 @@ * Modern replacement for the Prusa TMC_Z_CALIBRATION. * Adds capability to work with any adjustable current drivers. * Implemented as G34 because M915 is deprecated. + * @section calibrate */ //#define MECHANICAL_GANTRY_CALIBRATION #if ENABLED(MECHANICAL_GANTRY_CALIBRATION) @@ -4002,6 +4069,7 @@ /** * Instant freeze / unfreeze functionality * Potentially useful for emergency stop that allows being resumed. + * @section interface */ //#define FREEZE_FEATURE #if ENABLED(FREEZE_FEATURE) @@ -4014,6 +4082,7 @@ * * Add support for a low-cost 8x8 LED Matrix based on the Max7219 chip as a realtime status display. * Requires 3 signal wires. Some useful debug options are included to demonstrate its usage. + * @section debug matrix */ //#define MAX7219_DEBUG #if ENABLED(MAX7219_DEBUG) @@ -4052,6 +4121,7 @@ * Support for Synchronized Z moves when used with NanoDLP. G0/G1 axis moves will * output a "Z_move_comp" string to enable synchronization with DLP projector exposure. * This feature allows you to use [[WaitForDoneMessage]] instead of M400 commands. + * @section nanodlp */ //#define NANODLP_Z_SYNC #if ENABLED(NANODLP_Z_SYNC) @@ -4060,6 +4130,7 @@ /** * Ethernet. Use M552 to enable and set the IP address. + * @section network */ #if HAS_ETHERNET #define MAC_ADDRESS { 0xDE, 0xAD, 0xBE, 0xEF, 0xF0, 0x0D } // A MAC address unique to your network @@ -4087,6 +4158,8 @@ //#include "Configuration_Secure.h" // External file with WiFi SSID / Password #endif +// @section multi-material + /** * Průša Multi-Material Unit (MMU) * Enable in Configuration.h @@ -4192,6 +4265,7 @@ /** * Advanced Print Counter settings + * @section stats */ #if ENABLED(PRINTCOUNTER) #define SERVICE_WARNING_BUZZES 3 diff --git a/Marlin/config.ini b/Marlin/config.ini new file mode 100644 index 0000000000..0fb9fb0c93 --- /dev/null +++ b/Marlin/config.ini @@ -0,0 +1,211 @@ +# +# Marlin Firmware +# config.ini - Options to apply before the build +# +[config:base] +ini_use_config = none + +# Load all config: sections in this file +;ini_use_config = all +# Load config file relative to Marlin/ +;ini_use_config = another.ini +# Download configurations from GitHub +;ini_use_config = example/Creality/Ender-5 Plus @ bugfix-2.1.x +# Download configurations from your server +;ini_use_config = https://me.myserver.com/path/to/configs +# Evaluate config:base and do a config dump +;ini_use_config = base +;config_export = 2 + +[config:minimal] +motherboard = BOARD_RAMPS_14_EFB +serial_port = 0 +baudrate = 250000 + +use_watchdog = on +thermal_protection_hotends = on +thermal_protection_hysteresis = 4 +thermal_protection_period = 40 + +bufsize = 4 +block_buffer_size = 16 +max_cmd_size = 96 + +extruders = 1 +temp_sensor_0 = 1 + +temp_hysteresis = 3 +heater_0_mintemp = 5 +heater_0_maxtemp = 275 +preheat_1_temp_hotend = 180 + +bang_max = 255 +pidtemp = on +pid_k1 = 0.95 +pid_max = BANG_MAX +pid_functional_range = 10 + +default_kp = 22.20 +default_ki = 1.08 +default_kd = 114.00 + +x_driver_type = A4988 +y_driver_type = A4988 +z_driver_type = A4988 +e0_driver_type = A4988 + +x_bed_size = 200 +x_min_pos = 0 +x_max_pos = X_BED_SIZE + +y_bed_size = 200 +y_min_pos = 0 +y_max_pos = Y_BED_SIZE + +z_min_pos = 0 +z_max_pos = 200 + +x_home_dir = -1 +y_home_dir = -1 +z_home_dir = -1 + +use_xmin_plug = on +use_ymin_plug = on +use_zmin_plug = on + +x_min_endstop_inverting = false +y_min_endstop_inverting = false +z_min_endstop_inverting = false + +default_axis_steps_per_unit = { 80, 80, 400, 500 } +axis_relative_modes = { false, false, false, false } +default_max_feedrate = { 300, 300, 5, 25 } +default_max_acceleration = { 3000, 3000, 100, 10000 } + +homing_feedrate_mm_m = { (50*60), (50*60), (4*60) } +homing_bump_divisor = { 2, 2, 4 } + +x_enable_on = 0 +y_enable_on = 0 +z_enable_on = 0 +e_enable_on = 0 + +invert_x_dir = false +invert_y_dir = true +invert_z_dir = false +invert_e0_dir = false + +invert_e_step_pin = false +invert_x_step_pin = false +invert_y_step_pin = false +invert_z_step_pin = false + +disable_x = false +disable_y = false +disable_z = false +disable_e = false + +proportional_font_ratio = 1.0 +default_nominal_filament_dia = 1.75 + +junction_deviation_mm = 0.013 + +default_acceleration = 3000 +default_travel_acceleration = 3000 +default_retract_acceleration = 3000 + +default_minimumfeedrate = 0.0 +default_mintravelfeedrate = 0.0 + +minimum_planner_speed = 0.05 +min_steps_per_segment = 6 +default_minsegmenttime = 20000 + +[config:basic] +bed_overshoot = 10 +busy_while_heating = on +default_ejerk = 5.0 +default_keepalive_interval = 2 +default_leveling_fade_height = 0.0 +disable_inactive_extruder = on +display_charset_hd44780 = JAPANESE +eeprom_boot_silent = on +eeprom_chitchat = on +endstoppullups = on +extrude_maxlength = 200 +extrude_mintemp = 170 +host_keepalive_feature = on +hotend_overshoot = 15 +jd_handle_small_segments = on +lcd_info_screen_style = 0 +lcd_language = en +max_bed_power = 255 +mesh_inset = 0 +min_software_endstops = on +max_software_endstops = on +min_software_endstop_x = on +min_software_endstop_y = on +min_software_endstop_z = on +max_software_endstop_x = on +max_software_endstop_y = on +max_software_endstop_z = on +preheat_1_fan_speed = 0 +preheat_1_label = "PLA" +preheat_1_temp_bed = 70 +prevent_cold_extrusion = on +prevent_lengthy_extrude = on +printjob_timer_autostart = on +probing_margin = 10 +show_bootscreen = on +soft_pwm_scale = 0 +string_config_h_author = "(none, default config)" +temp_bed_hysteresis = 3 +temp_bed_residency_time = 10 +temp_bed_window = 1 +temp_residency_time = 10 +temp_window = 1 +validate_homing_endstops = on +xy_probe_feedrate = (133*60) +z_clearance_between_probes = 5 +z_clearance_deploy_probe = 10 +z_clearance_multi_probe = 5 + +[config:advanced] +arc_support = on +auto_report_temperatures = on +autotemp = on +autotemp_oldweight = 0.98 +bed_check_interval = 5000 +default_stepper_deactive_time = 120 +default_volumetric_extruder_limit = 0.00 +disable_inactive_e = true +disable_inactive_x = true +disable_inactive_y = true +disable_inactive_z = true +e0_auto_fan_pin = -1 +encoder_100x_steps_per_sec = 80 +encoder_10x_steps_per_sec = 30 +encoder_rate_multiplier = on +extended_capabilities_report = on +extruder_auto_fan_speed = 255 +extruder_auto_fan_temperature = 50 +fanmux0_pin = -1 +fanmux1_pin = -1 +fanmux2_pin = -1 +faster_gcode_parser = on +homing_bump_mm = { 5, 5, 2 } +max_arc_segment_mm = 1.0 +min_arc_segment_mm = 0.1 +min_circle_segments = 72 +n_arc_correction = 25 +serial_overrun_protection = on +slowdown = on +slowdown_divisor = 2 +temp_sensor_bed = 0 +thermal_protection_bed_hysteresis = 2 +thermocouple_max_errors = 15 +tx_buffer_size = 0 +watch_bed_temp_increase = 2 +watch_bed_temp_period = 60 +watch_temp_increase = 2 +watch_temp_period = 20 diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index 37918f619f..31b6184317 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -1220,10 +1220,10 @@ void setup() { SETUP_RUN(hal.init()); // Init and disable SPI thermocouples; this is still needed - #if TEMP_SENSOR_0_IS_MAX_TC || (TEMP_SENSOR_REDUNDANT_IS_MAX_TC && REDUNDANT_TEMP_MATCH(SOURCE, E0)) + #if TEMP_SENSOR_IS_MAX_TC(0) || (TEMP_SENSOR_IS_MAX_TC(REDUNDANT) && REDUNDANT_TEMP_MATCH(SOURCE, E0)) OUT_WRITE(TEMP_0_CS_PIN, HIGH); // Disable #endif - #if TEMP_SENSOR_1_IS_MAX_TC || (TEMP_SENSOR_REDUNDANT_IS_MAX_TC && REDUNDANT_TEMP_MATCH(SOURCE, E1)) + #if TEMP_SENSOR_IS_MAX_TC(1) || (TEMP_SENSOR_IS_MAX_TC(REDUNDANT) && REDUNDANT_TEMP_MATCH(SOURCE, E1)) OUT_WRITE(TEMP_1_CS_PIN, HIGH); #endif diff --git a/Marlin/src/core/macros.h b/Marlin/src/core/macros.h index 09a6164568..ddcf27b2b8 100644 --- a/Marlin/src/core/macros.h +++ b/Marlin/src/core/macros.h @@ -730,3 +730,8 @@ #define __MAPLIST() _MAPLIST #define MAPLIST(OP,V...) EVAL(_MAPLIST(OP,V)) + +// Temperature Sensor Config +#define _HAS_E_TEMP(N) || (TEMP_SENSOR_##N != 0) +#define HAS_E_TEMP_SENSOR (0 REPEAT(EXTRUDERS, _HAS_E_TEMP)) +#define TEMP_SENSOR_IS_MAX_TC(T) (TEMP_SENSOR_##T == -5 || TEMP_SENSOR_##T == -3 || TEMP_SENSOR_##T == -2) diff --git a/Marlin/src/gcode/temp/M303.cpp b/Marlin/src/gcode/temp/M303.cpp index ce362984a6..a4d514c733 100644 --- a/Marlin/src/gcode/temp/M303.cpp +++ b/Marlin/src/gcode/temp/M303.cpp @@ -48,7 +48,7 @@ void GcodeSuite::M303() { - #if ANY(PID_DEBUG, PID_BED_DEBUG, PID_CHAMBER_DEBUG) + #if HAS_PID_DEBUG if (parser.seen_test('D')) { thermalManager.pid_debug_flag ^= true; SERIAL_ECHO_START(); diff --git a/Marlin/src/inc/Conditionals_LCD.h b/Marlin/src/inc/Conditionals_LCD.h index 0873c1f525..a6be04e237 100644 --- a/Marlin/src/inc/Conditionals_LCD.h +++ b/Marlin/src/inc/Conditionals_LCD.h @@ -668,6 +668,31 @@ #define E_MANUAL EXTRUDERS #endif +#if E_STEPPERS <= 7 + #undef INVERT_E7_DIR + #if E_STEPPERS <= 6 + #undef INVERT_E6_DIR + #if E_STEPPERS <= 5 + #undef INVERT_E5_DIR + #if E_STEPPERS <= 4 + #undef INVERT_E4_DIR + #if E_STEPPERS <= 3 + #undef INVERT_E3_DIR + #if E_STEPPERS <= 2 + #undef INVERT_E2_DIR + #if E_STEPPERS <= 1 + #undef INVERT_E1_DIR + #if E_STEPPERS == 0 + #undef INVERT_E0_DIR + #endif + #endif + #endif + #endif + #endif + #endif + #endif +#endif + /** * Number of Linear Axes (e.g., XYZIJKUVW) * All the logical axes except for the tool (E) axis @@ -768,6 +793,9 @@ #undef Y_MIN_POS #undef Y_MAX_POS #undef MANUAL_Y_HOME_POS + #undef MIN_SOFTWARE_ENDSTOP_Y + #undef MAX_SOFTWARE_ENDSTOP_Y + #undef SAFE_BED_LEVELING_START_Y #endif #if !HAS_Z_AXIS @@ -785,6 +813,9 @@ #undef Z_MIN_POS #undef Z_MAX_POS #undef MANUAL_Z_HOME_POS + #undef MIN_SOFTWARE_ENDSTOP_Z + #undef MAX_SOFTWARE_ENDSTOP_Z + #undef SAFE_BED_LEVELING_START_Z #endif #if !HAS_I_AXIS @@ -799,6 +830,9 @@ #undef I_MIN_POS #undef I_MAX_POS #undef MANUAL_I_HOME_POS + #undef MIN_SOFTWARE_ENDSTOP_I + #undef MAX_SOFTWARE_ENDSTOP_I + #undef SAFE_BED_LEVELING_START_I #endif #if !HAS_J_AXIS @@ -813,6 +847,9 @@ #undef J_MIN_POS #undef J_MAX_POS #undef MANUAL_J_HOME_POS + #undef MIN_SOFTWARE_ENDSTOP_J + #undef MAX_SOFTWARE_ENDSTOP_J + #undef SAFE_BED_LEVELING_START_J #endif #if !HAS_K_AXIS @@ -827,6 +864,9 @@ #undef K_MIN_POS #undef K_MAX_POS #undef MANUAL_K_HOME_POS + #undef MIN_SOFTWARE_ENDSTOP_K + #undef MAX_SOFTWARE_ENDSTOP_K + #undef SAFE_BED_LEVELING_START_K #endif #if !HAS_U_AXIS @@ -841,6 +881,9 @@ #undef U_MIN_POS #undef U_MAX_POS #undef MANUAL_U_HOME_POS + #undef MIN_SOFTWARE_ENDSTOP_U + #undef MAX_SOFTWARE_ENDSTOP_U + #undef SAFE_BED_LEVELING_START_U #endif #if !HAS_V_AXIS @@ -855,6 +898,9 @@ #undef V_MIN_POS #undef V_MAX_POS #undef MANUAL_V_HOME_POS + #undef MIN_SOFTWARE_ENDSTOP_V + #undef MAX_SOFTWARE_ENDSTOP_V + #undef SAFE_BED_LEVELING_START_V #endif #if !HAS_W_AXIS @@ -869,6 +915,9 @@ #undef W_MIN_POS #undef W_MAX_POS #undef MANUAL_W_HOME_POS + #undef MIN_SOFTWARE_ENDSTOP_W + #undef MAX_SOFTWARE_ENDSTOP_W + #undef SAFE_BED_LEVELING_START_W #endif #ifdef X2_DRIVER_TYPE @@ -1398,6 +1447,10 @@ #define EXTRUDE_MINTEMP 170 #endif +#if ANY(PID_DEBUG, PID_BED_DEBUG, PID_CHAMBER_DEBUG) + #define HAS_PID_DEBUG 1 +#endif + /** * TFT Displays * diff --git a/Marlin/src/inc/Conditionals_adv.h b/Marlin/src/inc/Conditionals_adv.h index 49a1d7ef9c..21bc424f59 100644 --- a/Marlin/src/inc/Conditionals_adv.h +++ b/Marlin/src/inc/Conditionals_adv.h @@ -116,6 +116,31 @@ #undef STEALTHCHOP_E #endif +#if HOTENDS <= 7 + #undef E7_AUTO_FAN_PIN + #if HOTENDS <= 6 + #undef E6_AUTO_FAN_PIN + #if HOTENDS <= 5 + #undef E5_AUTO_FAN_PIN + #if HOTENDS <= 4 + #undef E4_AUTO_FAN_PIN + #if HOTENDS <= 3 + #undef E3_AUTO_FAN_PIN + #if HOTENDS <= 2 + #undef E2_AUTO_FAN_PIN + #if HOTENDS <= 1 + #undef E1_AUTO_FAN_PIN + #if HOTENDS == 0 + #undef E0_AUTO_FAN_PIN + #endif + #endif + #endif + #endif + #endif + #endif + #endif +#endif + /** * Temperature Sensors; define what sensor(s) we have. */ @@ -154,8 +179,7 @@ #define REDUNDANT_TEMP_MATCH(...) 0 #endif -#if TEMP_SENSOR_0 == -5 || TEMP_SENSOR_0 == -3 || TEMP_SENSOR_0 == -2 - #define TEMP_SENSOR_0_IS_MAX_TC 1 +#if TEMP_SENSOR_IS_MAX_TC(0) #if TEMP_SENSOR_0 == -5 #define TEMP_SENSOR_0_IS_MAX31865 1 #define TEMP_SENSOR_0_MAX_TC_TMIN 0 @@ -191,8 +215,7 @@ #undef HEATER_0_MAXTEMP #endif -#if TEMP_SENSOR_1 == -5 || TEMP_SENSOR_1 == -3 || TEMP_SENSOR_1 == -2 - #define TEMP_SENSOR_1_IS_MAX_TC 1 +#if TEMP_SENSOR_IS_MAX_TC(1) #if TEMP_SENSOR_1 == -5 #define TEMP_SENSOR_1_IS_MAX31865 1 #define TEMP_SENSOR_1_MAX_TC_TMIN 0 @@ -238,9 +261,7 @@ #undef HEATER_1_MAXTEMP #endif -#if TEMP_SENSOR_REDUNDANT == -5 || TEMP_SENSOR_REDUNDANT == -3 || TEMP_SENSOR_REDUNDANT == -2 - #define TEMP_SENSOR_REDUNDANT_IS_MAX_TC 1 - +#if TEMP_SENSOR_IS_MAX_TC(REDUNDANT) #if TEMP_SENSOR_REDUNDANT == -5 #if !REDUNDANT_TEMP_MATCH(SOURCE, E0) && !REDUNDANT_TEMP_MATCH(SOURCE, E1) #error "MAX31865 Thermocouples (-5) not supported for TEMP_SENSOR_REDUNDANT_SOURCE other than TEMP_SENSOR_0/TEMP_SENSOR_1 (0/1)." @@ -282,7 +303,7 @@ #endif #endif - #if (TEMP_SENSOR_0_IS_MAX_TC && TEMP_SENSOR_REDUNDANT != TEMP_SENSOR_0) || (TEMP_SENSOR_1_IS_MAX_TC && TEMP_SENSOR_REDUNDANT != TEMP_SENSOR_1) + #if (TEMP_SENSOR_IS_MAX_TC(0) && TEMP_SENSOR_REDUNDANT != TEMP_SENSOR_0) || (TEMP_SENSOR_IS_MAX_TC(1) && TEMP_SENSOR_REDUNDANT != TEMP_SENSOR_1) #if TEMP_SENSOR_REDUNDANT == -5 #error "If MAX31865 Thermocouple (-5) is used for TEMP_SENSOR_0/TEMP_SENSOR_1 then TEMP_SENSOR_REDUNDANT must match." #elif TEMP_SENSOR_REDUNDANT == -3 @@ -304,7 +325,7 @@ #endif #endif -#if TEMP_SENSOR_0_IS_MAX_TC || TEMP_SENSOR_1_IS_MAX_TC || TEMP_SENSOR_REDUNDANT_IS_MAX_TC +#if TEMP_SENSOR_IS_MAX_TC(0) || TEMP_SENSOR_IS_MAX_TC(1) || TEMP_SENSOR_IS_MAX_TC(REDUNDANT) #define HAS_MAX_TC 1 #endif #if TEMP_SENSOR_0_IS_MAX6675 || TEMP_SENSOR_1_IS_MAX6675 || TEMP_SENSOR_REDUNDANT_IS_MAX6675 diff --git a/Marlin/src/inc/Conditionals_post.h b/Marlin/src/inc/Conditionals_post.h index 1c67a43ed4..650c420532 100644 --- a/Marlin/src/inc/Conditionals_post.h +++ b/Marlin/src/inc/Conditionals_post.h @@ -681,7 +681,7 @@ #if HAS_MAX_TC // Translate old _SS, _CS, _SCK, _DO, _DI, _MISO, and _MOSI PIN defines. - #if TEMP_SENSOR_0_IS_MAX_TC || (TEMP_SENSOR_REDUNDANT_IS_MAX_TC && REDUNDANT_TEMP_MATCH(SOURCE, E1)) + #if TEMP_SENSOR_IS_MAX_TC(0) || (TEMP_SENSOR_IS_MAX_TC(REDUNDANT) && REDUNDANT_TEMP_MATCH(SOURCE, E1)) #if !PIN_EXISTS(TEMP_0_CS) // SS, CS #if PIN_EXISTS(MAX6675_SS) @@ -748,9 +748,9 @@ #endif #endif - #endif // TEMP_SENSOR_0_IS_MAX_TC + #endif // TEMP_SENSOR_IS_MAX_TC(0) - #if TEMP_SENSOR_1_IS_MAX_TC || (TEMP_SENSOR_REDUNDANT_IS_MAX_TC && REDUNDANT_TEMP_MATCH(SOURCE, E1)) + #if TEMP_SENSOR_IS_MAX_TC(1) || (TEMP_SENSOR_IS_MAX_TC(REDUNDANT) && REDUNDANT_TEMP_MATCH(SOURCE, E1)) #if !PIN_EXISTS(TEMP_1_CS) // SS2, CS2 #if PIN_EXISTS(MAX6675_SS2) @@ -817,7 +817,7 @@ #endif #endif - #endif // TEMP_SENSOR_1_IS_MAX_TC + #endif // TEMP_SENSOR_IS_MAX_TC(1) // // User-defined thermocouple libraries @@ -2656,7 +2656,7 @@ // // ADC Temp Sensors (Thermistor or Thermocouple with amplifier ADC interface) // -#define HAS_ADC_TEST(P) (PIN_EXISTS(TEMP_##P) && TEMP_SENSOR_##P != 0 && NONE(TEMP_SENSOR_##P##_IS_MAX_TC, TEMP_SENSOR_##P##_IS_DUMMY)) +#define HAS_ADC_TEST(P) (PIN_EXISTS(TEMP_##P) && TEMP_SENSOR_##P != 0 && !TEMP_SENSOR_IS_MAX_TC(P) && !TEMP_SENSOR_##P##_IS_DUMMY) #if HOTENDS > 0 && HAS_ADC_TEST(0) #define HAS_TEMP_ADC_0 1 #endif @@ -2700,7 +2700,7 @@ #define HAS_TEMP_ADC_REDUNDANT 1 #endif -#define HAS_TEMP(N) ANY(HAS_TEMP_ADC_##N, TEMP_SENSOR_##N##_IS_MAX_TC, TEMP_SENSOR_##N##_IS_DUMMY) +#define HAS_TEMP(N) (TEMP_SENSOR_IS_MAX_TC(N) || EITHER(HAS_TEMP_ADC_##N, TEMP_SENSOR_##N##_IS_DUMMY)) #if HAS_HOTEND && HAS_TEMP(0) #define HAS_TEMP_HOTEND 1 #endif diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index ed223cb6ec..234f850cdb 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -2328,9 +2328,9 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS #error "TEMP_SENSOR_REDUNDANT_TARGET can't be COOLER without TEMP_COOLER_PIN defined." #endif - #if TEMP_SENSOR_REDUNDANT_IS_MAX_TC && REDUNDANT_TEMP_MATCH(SOURCE, E0) && !PIN_EXISTS(TEMP_0_CS) + #if TEMP_SENSOR_IS_MAX_TC(REDUNDANT) && REDUNDANT_TEMP_MATCH(SOURCE, E0) && !PIN_EXISTS(TEMP_0_CS) #error "TEMP_SENSOR_REDUNDANT MAX Thermocouple with TEMP_SENSOR_REDUNDANT_SOURCE E0 requires TEMP_0_CS_PIN." - #elif TEMP_SENSOR_REDUNDANT_IS_MAX_TC && REDUNDANT_TEMP_MATCH(SOURCE, E1) && !PIN_EXISTS(TEMP_1_CS) + #elif TEMP_SENSOR_IS_MAX_TC(REDUNDANT) && REDUNDANT_TEMP_MATCH(SOURCE, E1) && !PIN_EXISTS(TEMP_1_CS) #error "TEMP_SENSOR_REDUNDANT MAX Thermocouple with TEMP_SENSOR_REDUNDANT_SOURCE E1 requires TEMP_1_CS_PIN." #endif #endif @@ -2343,7 +2343,7 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS #error "TEMP_0_PIN or TEMP_0_CS_PIN not defined for this board." #elif HAS_EXTRUDERS && !HAS_HEATER_0 #error "HEATER_0_PIN not defined for this board." -#elif TEMP_SENSOR_0_IS_MAX_TC && !PIN_EXISTS(TEMP_0_CS) +#elif TEMP_SENSOR_IS_MAX_TC(0) && !PIN_EXISTS(TEMP_0_CS) #error "TEMP_SENSOR_0 MAX thermocouple requires TEMP_0_CS_PIN." #elif HAS_HOTEND && !HAS_TEMP_HOTEND && !TEMP_SENSOR_0_IS_DUMMY #error "TEMP_0_PIN (required for TEMP_SENSOR_0) not defined for this board." @@ -2352,7 +2352,7 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS #endif #if HAS_MULTI_HOTEND - #if TEMP_SENSOR_1_IS_MAX_TC && !PIN_EXISTS(TEMP_1_CS) + #if TEMP_SENSOR_IS_MAX_TC(1) && !PIN_EXISTS(TEMP_1_CS) #error "TEMP_SENSOR_1 MAX thermocouple requires TEMP_1_CS_PIN." #elif TEMP_SENSOR_1 == 0 #error "TEMP_SENSOR_1 is required with 2 or more HOTENDS." diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp index 56eceea39d..ecd95b5e8f 100644 --- a/Marlin/src/module/temperature.cpp +++ b/Marlin/src/module/temperature.cpp @@ -77,7 +77,7 @@ // MAX TC related macros #define TEMP_SENSOR_IS_MAX(n, M) (ENABLED(TEMP_SENSOR_##n##_IS_MAX##M) || (ENABLED(TEMP_SENSOR_REDUNDANT_IS_MAX##M) && REDUNDANT_TEMP_MATCH(SOURCE, E##n))) -#define TEMP_SENSOR_IS_ANY_MAX_TC(n) (ENABLED(TEMP_SENSOR_##n##_IS_MAX_TC) || (ENABLED(TEMP_SENSOR_REDUNDANT_IS_MAX_TC) && REDUNDANT_TEMP_MATCH(SOURCE, E##n))) +#define TEMP_SENSOR_IS_ANY_MAX_TC(n) (TEMP_SENSOR_IS_MAX_TC(n) || (TEMP_SENSOR_IS_MAX_TC(REDUNDANT) && REDUNDANT_TEMP_MATCH(SOURCE, E##n))) // LIB_MAX6675 can be added to the build_flags in platformio.ini to use a user-defined library // If LIB_MAX6675 is not on the build_flags then raw SPI reads will be used. @@ -1317,8 +1317,7 @@ void Temperature::min_temp_error(const heater_id_t heater_id) { _temp_error(heater_id, F(STR_T_MINTEMP), GET_TEXT_F(MSG_ERR_MINTEMP)); } -#if ANY(PID_DEBUG, PID_BED_DEBUG, PID_CHAMBER_DEBUG) - #define HAS_PID_DEBUG 1 +#if HAS_PID_DEBUG bool Temperature::pid_debug_flag; // = false #endif @@ -1856,15 +1855,15 @@ void Temperature::task() { if (!updateTemperaturesIfReady()) return; // Will also reset the watchdog if temperatures are ready #if DISABLED(IGNORE_THERMOCOUPLE_ERRORS) - #if TEMP_SENSOR_0_IS_MAX_TC + #if TEMP_SENSOR_IS_MAX_TC(0) if (degHotend(0) > _MIN(HEATER_0_MAXTEMP, TEMP_SENSOR_0_MAX_TC_TMAX - 1.0)) max_temp_error(H_E0); if (degHotend(0) < _MAX(HEATER_0_MINTEMP, TEMP_SENSOR_0_MAX_TC_TMIN + .01)) min_temp_error(H_E0); #endif - #if TEMP_SENSOR_1_IS_MAX_TC + #if TEMP_SENSOR_IS_MAX_TC(1) if (degHotend(1) > _MIN(HEATER_1_MAXTEMP, TEMP_SENSOR_1_MAX_TC_TMAX - 1.0)) max_temp_error(H_E1); if (degHotend(1) < _MAX(HEATER_1_MINTEMP, TEMP_SENSOR_1_MAX_TC_TMIN + .01)) min_temp_error(H_E1); #endif - #if TEMP_SENSOR_REDUNDANT_IS_MAX_TC + #if TEMP_SENSOR_IS_MAX_TC(REDUNDANT) if (degRedundant() > TEMP_SENSOR_REDUNDANT_MAX_TC_TMAX - 1.0) max_temp_error(H_REDUNDANT); if (degRedundant() < TEMP_SENSOR_REDUNDANT_MAX_TC_TMIN + .01) min_temp_error(H_REDUNDANT); #endif @@ -2072,7 +2071,7 @@ void Temperature::task() { case 0: #if TEMP_SENSOR_0_IS_CUSTOM return user_thermistor_to_deg_c(CTI_HOTEND_0, raw); - #elif TEMP_SENSOR_0_IS_MAX_TC + #elif TEMP_SENSOR_IS_MAX_TC(0) #if TEMP_SENSOR_0_IS_MAX31865 return TERN(LIB_INTERNAL_MAX31865, max31865_0.temperature(raw), @@ -2091,7 +2090,7 @@ void Temperature::task() { case 1: #if TEMP_SENSOR_1_IS_CUSTOM return user_thermistor_to_deg_c(CTI_HOTEND_1, raw); - #elif TEMP_SENSOR_1_IS_MAX_TC + #elif TEMP_SENSOR_IS_MAX_TC(1) #if TEMP_SENSOR_0_IS_MAX31865 return TERN(LIB_INTERNAL_MAX31865, max31865_1.temperature(raw), @@ -2275,9 +2274,9 @@ void Temperature::task() { celsius_float_t Temperature::analog_to_celsius_redundant(const raw_adc_t raw) { #if TEMP_SENSOR_REDUNDANT_IS_CUSTOM return user_thermistor_to_deg_c(CTI_REDUNDANT, raw); - #elif TEMP_SENSOR_REDUNDANT_IS_MAX_TC && REDUNDANT_TEMP_MATCH(SOURCE, E0) + #elif TEMP_SENSOR_IS_MAX_TC(REDUNDANT) && REDUNDANT_TEMP_MATCH(SOURCE, E0) return TERN(TEMP_SENSOR_REDUNDANT_IS_MAX31865, max31865_0.temperature(raw), (int16_t)raw * 0.25); - #elif TEMP_SENSOR_REDUNDANT_IS_MAX_TC && REDUNDANT_TEMP_MATCH(SOURCE, E1) + #elif TEMP_SENSOR_IS_MAX_TC(REDUNDANT) && REDUNDANT_TEMP_MATCH(SOURCE, E1) return TERN(TEMP_SENSOR_REDUNDANT_IS_MAX31865, max31865_1.temperature(raw), (int16_t)raw * 0.25); #elif TEMP_SENSOR_REDUNDANT_IS_THERMISTOR SCAN_THERMISTOR_TABLE(TEMPTABLE_REDUNDANT, TEMPTABLE_REDUNDANT_LEN); @@ -2308,9 +2307,15 @@ void Temperature::updateTemperaturesFromRawValues() { hal.watchdog_refresh(); // Reset because raw_temps_ready was set by the interrupt - TERN_(TEMP_SENSOR_0_IS_MAX_TC, temp_hotend[0].setraw(READ_MAX_TC(0))); - TERN_(TEMP_SENSOR_1_IS_MAX_TC, temp_hotend[1].setraw(READ_MAX_TC(1))); - TERN_(TEMP_SENSOR_REDUNDANT_IS_MAX_TC, temp_redundant.setraw(READ_MAX_TC(HEATER_ID(TEMP_SENSOR_REDUNDANT_SOURCE)))); + #if TEMP_SENSOR_IS_MAX_TC(0) + temp_hotend[0].setraw(READ_MAX_TC(0)); + #endif + #if TEMP_SENSOR_IS_MAX_TC(1) + temp_hotend[1].setraw(READ_MAX_TC(1)); + #endif + #if TEMP_SENSOR_IS_MAX_TC(REDUNDANT) + temp_redundant.setraw(READ_MAX_TC(HEATER_ID(TEMP_SENSOR_REDUNDANT_SOURCE))); + #endif #if HAS_HOTEND HOTEND_LOOP() temp_hotend[e].celsius = analog_to_celsius_hotend(temp_hotend[e].getraw(), e); @@ -3139,15 +3144,15 @@ void Temperature::disable_all_heaters() { void Temperature::update_raw_temperatures() { // TODO: can this be collapsed into a HOTEND_LOOP()? - #if HAS_TEMP_ADC_0 && !TEMP_SENSOR_0_IS_MAX_TC + #if HAS_TEMP_ADC_0 && !TEMP_SENSOR_IS_MAX_TC(0) temp_hotend[0].update(); #endif - #if HAS_TEMP_ADC_1 && !TEMP_SENSOR_1_IS_MAX_TC + #if HAS_TEMP_ADC_1 && !TEMP_SENSOR_IS_MAX_TC(1) temp_hotend[1].update(); #endif - #if HAS_TEMP_ADC_REDUNDANT && !TEMP_SENSOR_REDUNDANT_IS_MAX_TC + #if HAS_TEMP_ADC_REDUNDANT && !TEMP_SENSOR_IS_MAX_TC(REDUNDANT) temp_redundant.update(); #endif diff --git a/Marlin/src/module/temperature.h b/Marlin/src/module/temperature.h index feec318050..f6cf81b8a9 100644 --- a/Marlin/src/module/temperature.h +++ b/Marlin/src/module/temperature.h @@ -953,7 +953,7 @@ class Temperature { */ #if HAS_PID_HEATING - #if ANY(PID_DEBUG, PID_BED_DEBUG, PID_CHAMBER_DEBUG) + #if HAS_PID_DEBUG static bool pid_debug_flag; #endif @@ -1035,7 +1035,7 @@ class Temperature { // MAX Thermocouples #if HAS_MAX_TC - #define MAX_TC_COUNT COUNT_ENABLED(TEMP_SENSOR_0_IS_MAX_TC, TEMP_SENSOR_1_IS_MAX_TC, TEMP_SENSOR_REDUNDANT_IS_MAX_TC) + #define MAX_TC_COUNT TEMP_SENSOR_IS_MAX_TC(0) + TEMP_SENSOR_IS_MAX_TC(1) + TEMP_SENSOR_IS_MAX_TC(REDUNDANT) #if MAX_TC_COUNT > 1 #define HAS_MULTI_MAX_TC 1 #define READ_MAX_TC(N) read_max_tc(N) diff --git a/buildroot/share/PlatformIO/scripts/configuration.py b/buildroot/share/PlatformIO/scripts/configuration.py new file mode 100644 index 0000000000..3ab0295749 --- /dev/null +++ b/buildroot/share/PlatformIO/scripts/configuration.py @@ -0,0 +1,239 @@ +# +# configuration.py +# Apply options from config.ini to the existing Configuration headers +# +import re, shutil, configparser +from pathlib import Path + +verbose = 0 +def blab(str,level=1): + if verbose >= level: print(f"[config] {str}") + +def config_path(cpath): + return Path("Marlin", cpath) + +# Apply a single name = on/off ; name = value ; etc. +# TODO: Limit to the given (optional) configuration +def apply_opt(name, val, conf=None): + if name == "lcd": name, val = val, "on" + + # Create a regex to match the option and capture parts of the line + regex = re.compile(rf'^(\s*)(//\s*)?(#define\s+)({name}\b)(\s*)(.*?)(\s*)(//.*)?$', re.IGNORECASE) + + # Find and enable and/or update all matches + for file in ("Configuration.h", "Configuration_adv.h"): + fullpath = config_path(file) + lines = fullpath.read_text().split('\n') + found = False + for i in range(len(lines)): + line = lines[i] + match = regex.match(line) + if match and match[4].upper() == name.upper(): + found = True + # For boolean options un/comment the define + if val in ("on", "", None): + newline = re.sub(r'^(\s*)//+\s*(#define)(\s{1,3})?(\s*)', r'\1\2 \4', line) + elif val == "off": + newline = re.sub(r'^(\s*)(#define)(\s{1,3})?(\s*)', r'\1//\2 \4', line) + else: + # For options with values, enable and set the value + newline = match[1] + match[3] + match[4] + match[5] + val + if match[8]: + sp = match[7] if match[7] else ' ' + newline += sp + match[8] + lines[i] = newline + blab(f"Set {name} to {val}") + + # If the option was found, write the modified lines + if found: + fullpath.write_text('\n'.join(lines)) + break + + # If the option didn't appear in either config file, add it + if not found: + # OFF options are added as disabled items so they appear + # in config dumps. Useful for custom settings. + prefix = "" + if val == "off": + prefix, val = "//", "" # Item doesn't appear in config dump + #val = "false" # Item appears in config dump + + # Uppercase the option unless already mixed/uppercase + added = name.upper() if name.islower() else name + + # Add the provided value after the name + if val != "on" and val != "" and val is not None: + added += " " + val + + # Prepend the new option after the first set of #define lines + fullpath = config_path("Configuration.h") + with fullpath.open() as f: + lines = f.readlines() + linenum = 0 + gotdef = False + for line in lines: + isdef = line.startswith("#define") + if not gotdef: + gotdef = isdef + elif not isdef: + break + linenum += 1 + lines.insert(linenum, f"{prefix}#define {added} // Added by config.ini\n") + fullpath.write_text('\n'.join(lines)) + +# Fetch configuration files from GitHub given the path. +# Return True if any files were fetched. +def fetch_example(path): + if path.endswith("/"): + path = path[:-1] + + if '@' in path: + path, brch = map(strip, path.split('@')) + + url = path.replace("%", "%25").replace(" ", "%20") + if not path.startswith('http'): + url = "https://raw.githubusercontent.com/MarlinFirmware/Configurations/bugfix-2.1.x/config/%s" % url + + # Find a suitable fetch command + if shutil.which("curl") is not None: + fetch = "curl -L -s -S -f -o" + elif shutil.which("wget") is not None: + fetch = "wget -q -O" + else: + blab("Couldn't find curl or wget", -1) + return False + + import os + + # Reset configurations to default + os.system("git reset --hard HEAD") + + gotfile = False + + # Try to fetch the remote files + for fn in ("Configuration.h", "Configuration_adv.h", "_Bootscreen.h", "_Statusscreen.h"): + if os.system("%s wgot %s/%s >/dev/null 2>&1" % (fetch, url, fn)) == 0: + shutil.move('wgot', config_path(fn)) + gotfile = True + + if Path('wgot').exists(): + shutil.rmtree('wgot') + + return gotfile + +def section_items(cp, sectkey): + return cp.items(sectkey) if sectkey in cp.sections() else [] + +# Apply all items from a config section +def apply_ini_by_name(cp, sect): + iniok = True + if sect in ('config:base', 'config:root'): + iniok = False + items = section_items(cp, 'config:base') + section_items(cp, 'config:root') + else: + items = cp.items(sect) + + for item in items: + if iniok or not item[0].startswith('ini_'): + apply_opt(item[0], item[1]) + +# Apply all config sections from a parsed file +def apply_all_sections(cp): + for sect in cp.sections(): + if sect.startswith('config:'): + apply_ini_by_name(cp, sect) + +# Apply certain config sections from a parsed file +def apply_sections(cp, ckey='all', addbase=False): + blab("[config] apply section key: %s" % ckey) + if ckey == 'all': + apply_all_sections(cp) + else: + # Apply the base/root config.ini settings after external files are done + if addbase or ckey in ('base', 'root'): + apply_ini_by_name(cp, 'config:base') + + # Apply historically 'Configuration.h' settings everywhere + if ckey == 'basic': + apply_ini_by_name(cp, 'config:basic') + + # Apply historically Configuration_adv.h settings everywhere + # (Some of which rely on defines in 'Conditionals_LCD.h') + elif ckey in ('adv', 'advanced'): + apply_ini_by_name(cp, 'config:advanced') + + # Apply a specific config: section directly + elif ckey.startswith('config:'): + apply_ini_by_name(cp, ckey) + +# Apply settings from a top level config.ini +def apply_config_ini(cp): + blab("=" * 20 + " Gather 'config.ini' entries...") + + # Pre-scan for ini_use_config to get config_keys + base_items = section_items(cp, 'config:base') + section_items(cp, 'config:root') + config_keys = ['base'] + for ikey, ival in base_items: + if ikey == 'ini_use_config': + config_keys = [ x.strip() for x in ival.split(',') ] + + # For each ini_use_config item perform an action + for ckey in config_keys: + addbase = False + + # For a key ending in .ini load and parse another .ini file + if ckey.endswith('.ini'): + sect = 'base' + if '@' in ckey: sect, ckey = ckey.split('@') + other_ini = configparser.ConfigParser() + other_ini.read(config_path(ckey)) + apply_sections(other_ini, sect) + + # (Allow 'example/' as a shortcut for 'examples/') + elif ckey.startswith('example/'): + ckey = 'examples' + ckey[7:] + + # For 'examples/' fetch an example set from GitHub. + # For https?:// do a direct fetch of the URL. + elif ckey.startswith('examples/') or ckey.startswith('http'): + addbase = True + fetch_example(ckey) + + # Apply keyed sections after external files are done + apply_sections(cp, 'config:' + ckey, addbase) + +if __name__ == "__main__": + # + # From command line use the given file name + # + import sys + args = sys.argv[1:] + if len(args) > 0: + if args[0].endswith('.ini'): + ini_file = args[0] + else: + print("Usage: %s <.ini file>" % sys.argv[0]) + else: + ini_file = config_path('config.ini') + + if ini_file: + user_ini = configparser.ConfigParser() + user_ini.read(ini_file) + apply_config_ini(user_ini) + +else: + # + # From within PlatformIO use the loaded INI file + # + import pioutil + if pioutil.is_pio_build(): + + Import("env") + + try: + verbose = int(env.GetProjectOption('custom_verbose')) + except: + pass + + from platformio.project.config import ProjectConfig + apply_config_ini(ProjectConfig()) diff --git a/buildroot/share/PlatformIO/scripts/schema.py b/buildroot/share/PlatformIO/scripts/schema.py new file mode 100755 index 0000000000..767748757e --- /dev/null +++ b/buildroot/share/PlatformIO/scripts/schema.py @@ -0,0 +1,403 @@ +#!/usr/bin/env python3 +# +# schema.py +# +# Used by signature.py via common-dependencies.py to generate a schema file during the PlatformIO build. +# This script can also be run standalone from within the Marlin repo to generate all schema files. +# +import re,json +from pathlib import Path + +def extend_dict(d:dict, k:tuple): + if len(k) >= 1 and k[0] not in d: + d[k[0]] = {} + if len(k) >= 2 and k[1] not in d[k[0]]: + d[k[0]][k[1]] = {} + if len(k) >= 3 and k[2] not in d[k[0]][k[1]]: + d[k[0]][k[1]][k[2]] = {} + +grouping_patterns = [ + re.compile(r'^([XYZIJKUVW]|[XYZ]2|Z[34]|E[0-7])$'), + re.compile(r'^AXIS\d$'), + re.compile(r'^(MIN|MAX)$'), + re.compile(r'^[0-8]$'), + re.compile(r'^HOTEND[0-7]$'), + re.compile(r'^(HOTENDS|BED|PROBE|COOLER)$'), + re.compile(r'^[XYZIJKUVW]M(IN|AX)$') +] +# If the indexed part of the option name matches a pattern +# then add it to the dictionary. +def find_grouping(gdict, filekey, sectkey, optkey, pindex): + optparts = optkey.split('_') + if 1 < len(optparts) > pindex: + for patt in grouping_patterns: + if patt.match(optparts[pindex]): + subkey = optparts[pindex] + modkey = '_'.join(optparts) + optparts[pindex] = '*' + wildkey = '_'.join(optparts) + kkey = f'{filekey}|{sectkey}|{wildkey}' + if kkey not in gdict: gdict[kkey] = [] + gdict[kkey].append((subkey, modkey)) + +# Build a list of potential groups. Only those with multiple items will be grouped. +def group_options(schema): + for pindex in range(10, -1, -1): + found_groups = {} + for filekey, f in schema.items(): + for sectkey, s in f.items(): + for optkey in s: + find_grouping(found_groups, filekey, sectkey, optkey, pindex) + + fkeys = [ k for k in found_groups.keys() ] + for kkey in fkeys: + items = found_groups[kkey] + if len(items) > 1: + f, s, w = kkey.split('|') + extend_dict(schema, (f, s, w)) # Add wildcard group to schema + for subkey, optkey in items: # Add all items to wildcard group + schema[f][s][w][subkey] = schema[f][s][optkey] # Move non-wildcard item to wildcard group + del schema[f][s][optkey] + del found_groups[kkey] + +# Extract all board names from boards.h +def load_boards(): + bpath = Path("Marlin/src/core/boards.h") + if bpath.is_file(): + with bpath.open() as bfile: + boards = [] + for line in bfile: + if line.startswith("#define BOARD_"): + bname = line.split()[1] + if bname != "BOARD_UNKNOWN": boards.append(bname) + return "['" + "','".join(boards) + "']" + return '' + +# +# Extract a schema from the current configuration files +# +def extract(): + # Load board names from boards.h + boards = load_boards() + + # Parsing states + class Parse: + NORMAL = 0 # No condition yet + BLOCK_COMMENT = 1 # Looking for the end of the block comment + EOL_COMMENT = 2 # EOL comment started, maybe add the next comment? + GET_SENSORS = 3 # Gathering temperature sensor options + ERROR = 9 # Syntax error + + # List of files to process, with shorthand + filekey = { 'Configuration.h':'basic', 'Configuration_adv.h':'advanced' } + # A JSON object to store the data + sch_out = { 'basic':{}, 'advanced':{} } + # Regex for #define NAME [VALUE] [COMMENT] with sanitized line + defgrep = re.compile(r'^(//)?\s*(#define)\s+([A-Za-z0-9_]+)\s*(.*?)\s*(//.+)?$') + # Defines to ignore + ignore = ('CONFIGURATION_H_VERSION', 'CONFIGURATION_ADV_H_VERSION', 'CONFIG_EXPORT') + # Start with unknown state + state = Parse.NORMAL + # Serial ID + sid = 0 + # Loop through files and parse them line by line + for fn, fk in filekey.items(): + with Path("Marlin", fn).open() as fileobj: + section = 'none' # Current Settings section + line_number = 0 # Counter for the line number of the file + conditions = [] # Create a condition stack for the current file + comment_buff = [] # A temporary buffer for comments + options_json = '' # A buffer for the most recent options JSON found + eol_options = False # The options came from end of line, so only apply once + join_line = False # A flag that the line should be joined with the previous one + line = '' # A line buffer to handle \ continuation + last_added_ref = None # Reference to the last added item + # Loop through the lines in the file + for the_line in fileobj.readlines(): + line_number += 1 + + # Clean the line for easier parsing + the_line = the_line.strip() + + if join_line: # A previous line is being made longer + line += (' ' if line else '') + the_line + else: # Otherwise, start the line anew + line, line_start = the_line, line_number + + # If the resulting line ends with a \, don't process now. + # Strip the end off. The next line will be joined with it. + join_line = line.endswith("\\") + if join_line: + line = line[:-1].strip() + continue + else: + line_end = line_number + + defmatch = defgrep.match(line) + + # Special handling for EOL comments after a #define. + # At this point the #define is already digested and inserted, + # so we have to extend it + if state == Parse.EOL_COMMENT: + # If the line is not a comment, we're done with the EOL comment + if not defmatch and the_line.startswith('//'): + comment_buff.append(the_line[2:].strip()) + else: + last_added_ref['comment'] = ' '.join(comment_buff) + comment_buff = [] + state = Parse.NORMAL + + def use_comment(c, opt, sec, bufref): + if c.startswith(':'): # If the comment starts with : then it has magic JSON + d = c[1:].strip() # Strip the leading : + cbr = c.rindex('}') if d.startswith('{') else c.rindex(']') if d.startswith('[') else 0 + if cbr: + opt, cmt = c[1:cbr+1].strip(), c[cbr+1:].strip() + if cmt != '': bufref.append(cmt) + else: + opt = c[1:].strip() + elif c.startswith('@section'): # Start a new section + sec = c[8:].strip() + elif not c.startswith('========'): + bufref.append(c) + return opt, sec + + # In a block comment, capture lines up to the end of the comment. + # Assume nothing follows the comment closure. + if state in (Parse.BLOCK_COMMENT, Parse.GET_SENSORS): + endpos = line.find('*/') + if endpos < 0: + cline = line + else: + cline, line = line[:endpos].strip(), line[endpos+2:].strip() + + # Temperature sensors are done + if state == Parse.GET_SENSORS: + options_json = f'[ {options_json[:-2]} ]' + + state = Parse.NORMAL + + # Strip the leading '*' from block comments + if cline.startswith('*'): cline = cline[1:].strip() + + # Collect temperature sensors + if state == Parse.GET_SENSORS: + sens = re.match(r'^(-?\d+)\s*:\s*(.+)$', cline) + if sens: + s2 = sens[2].replace("'","''") + options_json += f"{sens[1]}:'{s2}', " + + elif state == Parse.BLOCK_COMMENT: + + # Look for temperature sensors + if cline == "Temperature sensors available:": + state, cline = Parse.GET_SENSORS, "Temperature Sensors" + + options_json, section = use_comment(cline, options_json, section, comment_buff) + + # For the normal state we're looking for any non-blank line + elif state == Parse.NORMAL: + # Skip a commented define when evaluating comment opening + st = 2 if re.match(r'^//\s*#define', line) else 0 + cpos1 = line.find('/*') # Start a block comment on the line? + cpos2 = line.find('//', st) # Start an end of line comment on the line? + + # Only the first comment starter gets evaluated + cpos = -1 + if cpos1 != -1 and (cpos1 < cpos2 or cpos2 == -1): + cpos = cpos1 + comment_buff = [] + state = Parse.BLOCK_COMMENT + eol_options = False + + elif cpos2 != -1 and (cpos2 < cpos1 or cpos1 == -1): + cpos = cpos2 + + # Expire end-of-line options after first use + if cline.startswith(':'): eol_options = True + + # Comment after a define may be continued on the following lines + if state == Parse.NORMAL and defmatch != None and cpos > 10: + state = Parse.EOL_COMMENT + comment_buff = [] + + # Process the start of a new comment + if cpos != -1: + cline, line = line[cpos+2:].strip(), line[:cpos].strip() + + # Strip leading '*' from block comments + if state == Parse.BLOCK_COMMENT: + if cline.startswith('*'): cline = cline[1:].strip() + + # Buffer a non-empty comment start + if cline != '': + options_json, section = use_comment(cline, options_json, section, comment_buff) + + # If the line has nothing before the comment, go to the next line + if line == '': + options_json = '' + continue + + # Parenthesize the given expression if needed + def atomize(s): + if s == '' \ + or re.match(r'^[A-Za-z0-9_]*(\([^)]+\))?$', s) \ + or re.match(r'^[A-Za-z0-9_]+ == \d+?$', s): + return s + return f'({s})' + + # + # The conditions stack is an array containing condition-arrays. + # Each condition-array lists the conditions for the current block. + # IF/N/DEF adds a new condition-array to the stack. + # ELSE/ELIF/ENDIF pop the condition-array. + # ELSE/ELIF negate the last item in the popped condition-array. + # ELIF adds a new condition to the end of the array. + # ELSE/ELIF re-push the condition-array. + # + cparts = line.split() + iselif, iselse = cparts[0] == '#elif', cparts[0] == '#else' + if iselif or iselse or cparts[0] == '#endif': + if len(conditions) == 0: + raise Exception(f'no #if block at line {line_number}') + + # Pop the last condition-array from the stack + prev = conditions.pop() + + if iselif or iselse: + prev[-1] = '!' + prev[-1] # Invert the last condition + if iselif: prev.append(atomize(line[5:].strip())) + conditions.append(prev) + + elif cparts[0] == '#if': + conditions.append([ atomize(line[3:].strip()) ]) + elif cparts[0] == '#ifdef': + conditions.append([ f'defined({line[6:].strip()})' ]) + elif cparts[0] == '#ifndef': + conditions.append([ f'!defined({line[7:].strip()})' ]) + + # Handle a complete #define line + elif defmatch != None: + + # Get the match groups into vars + enabled, define_name, val = defmatch[1] == None, defmatch[3], defmatch[4] + + # Increment the serial ID + sid += 1 + + # Create a new dictionary for the current #define + define_info = { + 'section': section, + 'name': define_name, + 'enabled': enabled, + 'line': line_start, + 'sid': sid + } + + if val != '': define_info['value'] = val + + # Type is based on the value + if val == '': + value_type = 'switch' + elif re.match(r'^(true|false)$', val): + value_type = 'bool' + val = val == 'true' + elif re.match(r'^[-+]?\s*\d+$', val): + value_type = 'int' + val = int(val) + elif re.match(r'[-+]?\s*(\d+\.|\d*\.\d+)([eE][-+]?\d+)?[fF]?', val): + value_type = 'float' + val = float(val.replace('f','')) + else: + value_type = 'string' if val[0] == '"' \ + else 'char' if val[0] == "'" \ + else 'state' if re.match(r'^(LOW|HIGH)$', val) \ + else 'enum' if re.match(r'^[A-Za-z0-9_]{3,}$', val) \ + else 'int[]' if re.match(r'^{(\s*[-+]?\s*\d+\s*(,\s*)?)+}$', val) \ + else 'float[]' if re.match(r'^{(\s*[-+]?\s*(\d+\.|\d*\.\d+)([eE][-+]?\d+)?[fF]?\s*(,\s*)?)+}$', val) \ + else 'array' if val[0] == '{' \ + else '' + + if value_type != '': define_info['type'] = value_type + + # Join up accumulated conditions with && + if conditions: define_info['requires'] = ' && '.join(sum(conditions, [])) + + # If the comment_buff is not empty, add the comment to the info + if comment_buff: + full_comment = '\n'.join(comment_buff) + + # An EOL comment will be added later + # The handling could go here instead of above + if state == Parse.EOL_COMMENT: + define_info['comment'] = '' + else: + define_info['comment'] = full_comment + comment_buff = [] + + # If the comment specifies units, add that to the info + units = re.match(r'^\(([^)]+)\)', full_comment) + if units: + units = units[1] + if units == 's' or units == 'sec': units = 'seconds' + define_info['units'] = units + + # Set the options for the current #define + if define_name == "MOTHERBOARD" and boards != '': + define_info['options'] = boards + elif options_json != '': + define_info['options'] = options_json + if eol_options: options_json = '' + + # Create section dict if it doesn't exist yet + if section not in sch_out[fk]: sch_out[fk][section] = {} + + # If define has already been seen... + if define_name in sch_out[fk][section]: + info = sch_out[fk][section][define_name] + if isinstance(info, dict): info = [ info ] # Convert a single dict into a list + info.append(define_info) # Add to the list + else: + # Add the define dict with name as key + sch_out[fk][section][define_name] = define_info + + if state == Parse.EOL_COMMENT: + last_added_ref = define_info + + return sch_out + +def dump_json(schema:dict, jpath:Path): + with jpath.open('w') as jfile: + json.dump(schema, jfile, ensure_ascii=False, indent=2) + +def dump_yaml(schema:dict, ypath:Path): + import yaml + with ypath.open('w') as yfile: + yaml.dump(schema, yfile, default_flow_style=False, width=120, indent=2) + +def main(): + try: + schema = extract() + except Exception as exc: + print("Error: " + str(exc)) + schema = None + + if schema: + print("Generating JSON ...") + dump_json(schema, Path('schema.json')) + group_options(schema) + dump_json(schema, Path('schema_grouped.json')) + + try: + import yaml + except ImportError: + print("Installing YAML module ...") + import subprocess + subprocess.run(['python3', '-m', 'pip', 'install', 'pyyaml']) + import yaml + + print("Generating YML ...") + dump_yaml(schema, Path('schema.yml')) + +if __name__ == '__main__': + main() diff --git a/buildroot/share/PlatformIO/scripts/signature.py b/buildroot/share/PlatformIO/scripts/signature.py index f1c86bb839..43d56ac6e1 100644 --- a/buildroot/share/PlatformIO/scripts/signature.py +++ b/buildroot/share/PlatformIO/scripts/signature.py @@ -1,7 +1,11 @@ # # signature.py # -import os,subprocess,re,json,hashlib +import schema + +import subprocess,re,json,hashlib +from datetime import datetime +from pathlib import Path # # Return all macro names in a header as an array, so we can take @@ -35,8 +39,8 @@ def get_file_sha256sum(filepath): # Compress a JSON file into a zip file # import zipfile -def compress_file(filepath, outputbase): - with zipfile.ZipFile(outputbase + '.zip', 'w', compression=zipfile.ZIP_BZIP2, compresslevel=9) as zipf: +def compress_file(filepath, outpath): + with zipfile.ZipFile(outpath, 'w', compression=zipfile.ZIP_BZIP2, compresslevel=9) as zipf: zipf.write(filepath, compress_type=zipfile.ZIP_BZIP2, compresslevel=9) # @@ -51,19 +55,19 @@ def compute_build_signature(env): # Definitions from these files will be kept files_to_keep = [ 'Marlin/Configuration.h', 'Marlin/Configuration_adv.h' ] - build_dir = os.path.join(env['PROJECT_BUILD_DIR'], env['PIOENV']) + build_path = Path(env['PROJECT_BUILD_DIR'], env['PIOENV']) # Check if we can skip processing hashes = '' for header in files_to_keep: hashes += get_file_sha256sum(header)[0:10] - marlin_json = os.path.join(build_dir, 'marlin_config.json') - marlin_zip = os.path.join(build_dir, 'mc') + marlin_json = build_path / 'marlin_config.json' + marlin_zip = build_path / 'mc.zip' # Read existing config file try: - with open(marlin_json, 'r') as infile: + with marlin_json.open() as infile: conf = json.load(infile) if conf['__INITIAL_HASH'] == hashes: # Same configuration, skip recomputing the building signature @@ -109,7 +113,10 @@ def compute_build_signature(env): defines[key] = value if len(value) else "" - if not 'CONFIGURATION_EMBEDDING' in defines: + # + # Continue to gather data for CONFIGURATION_EMBEDDING or CONFIG_EXPORT + # + if not ('CONFIGURATION_EMBEDDING' in defines or 'CONFIG_EXPORT' in defines): return # Second step is to filter useless macro @@ -145,6 +152,85 @@ def compute_build_signature(env): if key in conf_defines[header]: data[header][key] = resolved_defines[key] + # Every python needs this toy + def tryint(key): + try: + return int(defines[key]) + except: + return 0 + + config_dump = tryint('CONFIG_EXPORT') + + # + # Produce an INI file if CONFIG_EXPORT == 2 + # + if config_dump == 2: + print("Generating config.ini ...") + config_ini = build_path / 'config.ini' + with config_ini.open('w') as outfile: + ignore = ('CONFIGURATION_H_VERSION', 'CONFIGURATION_ADV_H_VERSION', 'CONFIG_EXPORT') + filegrp = { 'Configuration.h':'config:basic', 'Configuration_adv.h':'config:advanced' } + vers = defines["CONFIGURATION_H_VERSION"] + dt_string = datetime.now().strftime("%Y-%m-%d at %H:%M:%S") + ini_fmt = '{0:40}{1}\n' + outfile.write( + '#\n' + + '# Marlin Firmware\n' + + '# config.ini - Options to apply before the build\n' + + '#\n' + + f'# Generated by Marlin build on {dt_string}\n' + + '#\n' + + '\n' + + '[config:base]\n' + + ini_fmt.format('ini_use_config', ' = all') + + ini_fmt.format('ini_config_vers', f' = {vers}') + ) + # Loop through the data array of arrays + for header in data: + if header.startswith('__'): + continue + outfile.write('\n[' + filegrp[header] + ']\n') + for key in sorted(data[header]): + if key not in ignore: + val = 'on' if data[header][key] == '' else data[header][key] + outfile.write(ini_fmt.format(key.lower(), ' = ' + val)) + + # + # Produce a schema.json file if CONFIG_EXPORT == 3 + # + if config_dump >= 3: + try: + conf_schema = schema.extract() + except Exception as exc: + print("Error: " + str(exc)) + conf_schema = None + + if conf_schema: + # + # Produce a schema.json file if CONFIG_EXPORT == 3 + # + if config_dump in (3, 13): + print("Generating schema.json ...") + schema.dump_json(conf_schema, build_path / 'schema.json') + if config_dump == 13: + schema.group_options(conf_schema) + schema.dump_json(conf_schema, build_path / 'schema_grouped.json') + + # + # Produce a schema.yml file if CONFIG_EXPORT == 4 + # + elif config_dump == 4: + print("Generating schema.yml ...") + try: + import yaml + except ImportError: + env.Execute(env.VerboseAction( + '$PYTHONEXE -m pip install "pyyaml"', + "Installing YAML for schema.yml export", + )) + import yaml + schema.dump_yaml(conf_schema, build_path / 'schema.yml') + # Append the source code version and date data['VERSION'] = {} data['VERSION']['DETAILED_BUILD_VERSION'] = resolved_defines['DETAILED_BUILD_VERSION'] @@ -156,10 +242,17 @@ def compute_build_signature(env): pass # - # Produce a JSON file for CONFIGURATION_EMBEDDING or CONFIG_DUMP > 0 + # Produce a JSON file for CONFIGURATION_EMBEDDING or CONFIG_EXPORT == 1 # - with open(marlin_json, 'w') as outfile: - json.dump(data, outfile, separators=(',', ':')) + if config_dump == 1 or 'CONFIGURATION_EMBEDDING' in defines: + with marlin_json.open('w') as outfile: + json.dump(data, outfile, separators=(',', ':')) + + # + # The rest only applies to CONFIGURATION_EMBEDDING + # + if not 'CONFIGURATION_EMBEDDING' in defines: + return # Compress the JSON file as much as we can compress_file(marlin_json, marlin_zip) @@ -167,17 +260,17 @@ def compute_build_signature(env): # Generate a C source file for storing this array with open('Marlin/src/mczip.h','wb') as result_file: result_file.write( - b'#ifndef NO_CONFIGURATION_EMBEDDING_WARNING\n' + b'#ifndef NO_CONFIGURATION_EMBEDDING_WARNING\n' + b' #warning "Generated file \'mc.zip\' is embedded (Define NO_CONFIGURATION_EMBEDDING_WARNING to suppress this warning.)"\n' + b'#endif\n' + b'const unsigned char mc_zip[] PROGMEM = {\n ' ) count = 0 - for b in open(os.path.join(build_dir, 'mc.zip'), 'rb').read(): + for b in (build_path / 'mc.zip').open('rb').read(): result_file.write(b' 0x%02X,' % b) count += 1 - if (count % 16 == 0): - result_file.write(b'\n ') - if (count % 16): + if count % 16 == 0: + result_file.write(b'\n ') + if count % 16: result_file.write(b'\n') result_file.write(b'};\n') diff --git a/platformio.ini b/platformio.ini index 26f58662cc..b2f178ec0f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,6 +16,7 @@ boards_dir = buildroot/share/PlatformIO/boards default_envs = mega2560 include_dir = Marlin extra_configs = + Marlin/config.ini ini/avr.ini ini/due.ini ini/esp32.ini @@ -44,12 +45,13 @@ extra_configs = build_flags = -g3 -D__MARLIN_FIRMWARE__ -DNDEBUG -fmax-errors=5 extra_scripts = + pre:buildroot/share/PlatformIO/scripts/configuration.py pre:buildroot/share/PlatformIO/scripts/common-dependencies.py pre:buildroot/share/PlatformIO/scripts/common-cxxflags.py pre:buildroot/share/PlatformIO/scripts/preflight-checks.py post:buildroot/share/PlatformIO/scripts/common-dependencies-post.py lib_deps = -default_src_filter = + - - + +default_src_filter = + - - + - - - - - - - - - - - - -