1215 Commits 008d3b0e65 ... 0be90dc5d1

Author SHA1 Message Date
  DRracer 0be90dc5d1 Merge pull request #3320 from prusa3d/MK3_3.10.1 2 years ago
  D.R.racer caca7bb9b8 Version changed (3.10.1 build 4697) 2 years ago
  DRracer 86b48ebe04 Merge pull request #3318 from 3d-gussner/MK3_3.10.1_prepare_release 2 years ago
  3d-gussner add4872292 Update po files before release 2 years ago
  3d-gussner 1c1e69e555 Update FW crash message 2 years ago
  DRracer 752e9d5544 Merge pull request #3317 from 3d-gussner/MK3_3.10.1_RepRap_documentation 2 years ago
  3d-gussner 785d94ab3a Add links to RepRap Gcode wiki for new Dcodes D20-D23 2 years ago
  DRracer 1a7a313cbf Merge pull request #3311 from wavexx/temp_runaway_fixes 2 years ago
  Yuri D'Elia 3849f9785a Make cancel_heatup also abort cooldown in M190 2 years ago
  Yuri D'Elia 320835a1b7 Do not cancel wait-for-temperature loops in disable_heaters() 2 years ago
  Yuri D'Elia 32d8d892f5 Actually call UnconditionalStop() in Stop()->lcd_print_stop() 2 years ago
  Yuri D'Elia 83693bf4cc Remove useless assignment in lcd_cooldown 2 years ago
  Yuri D'Elia 78f856c8d6 Do not unconditionally overwrite the status message in check_file() 2 years ago
  Yuri D'Elia 7ff117d0c4 temp_runaway_stop: remove spourious space in error message 2 years ago
  Yuri D'Elia 36a7b5ca56 Avoid redundant checks in lcd_setalertstatus* 2 years ago
  Yuri D'Elia a3915b57b9 Improve temp_runaway_stop robustness 2 years ago
  Yuri D'Elia 57abffda1b Update temperature-related error message to use LCD_STATUS_CRITICAL 2 years ago
  Yuri D'Elia fb025bba05 Introduce severity levels for alert messages 2 years ago
  DRracer a7dfe4b523 Merge pull request #3305 from gudnimg/sd-card-lcdtimer-fix-gudni-v2 2 years ago
  DRracer 3228f9d03c Merge pull request #3304 from gudnimg/gudni-fix-compile-issue-v2 2 years ago
  Guðni Már Gilbert be79e3791f Fixes #3262 2 years ago
  Guðni Már Gilbert 13732f162b Fix build error when TMC2130_SERVICE_CODES_M910_M918 is enabled 2 years ago
  DRracer bb9ba95bfc Merge pull request #3303 from Panayiotis-git/MK3_3.10.1 2 years ago
  Panayiotis-git f8410d8f3c Print temperatures only if filament loading is still active 2 years ago
  DRracer 98d4e6972a Merge pull request #3271 from dweekly/patch-1 2 years ago
  DRracer 13f0f4d85a Merge pull request #3291 from EV3R4/typo-m84-docs 2 years ago
  DRracer e9fe7607a7 Merge pull request #3082 from awenelo/fix-bug-template 2 years ago
  EV3R4 eff9cd9f21 Typo: M84 docs 2 years ago
  David E. Weekly b37e289a8f Fix typos & small grammar changes 2 years ago
  DRracer 3bf5d937a8 Merge pull request #3192 from 3d-gussner/PFW-1271_PF-buildv20 2 years ago
  DRracer fd6dbba06f Merge branch 'MK3_3.10.1' into PFW-1271_PF-buildv20 2 years ago
  3d-gussner da910fe861 Switch to arduino_boards v1.0.4 2 years ago
  Yuri D'Elia 4f40380474 tools: document functions in utils.gdb 2 years ago
  Yuri D'Elia 1b22aac9fc tools: add xfimg2dump 2 years ago
  Yuri D'Elia a697d00647 tools: add __pycache__ to gitignore 2 years ago
  Yuri D'Elia c79b1dcbfa tools: add dump_crash to recover XFLASH crash dumps 2 years ago
  Yuri D'Elia 8455c8e585 tools: Add/fix documentation 2 years ago
  Yuri D'Elia 43b9a2d3df dump: do not hard-code constants 2 years ago
  Yuri D'Elia 11a6ac2f4f dump parsing: refuse to continue on incomplete D23 dumps 2 years ago
  Yuri D'Elia 500515eb0a Add GDB utility functions to load/inspect binary dumps 2 years ago
  Yuri D'Elia 06eab4ac11 Handle XFLASH (D21) and serial (D23) dumps in elf_mem_map, add dump2bin 2 years ago
  Yuri D'Elia 9f40fa6834 elf_mem_map: parse D23 output directly 2 years ago
  Yuri D'Elia 8ec4104840 elf_mem_map: do not output registers in qdirstat output 2 years ago
  Yuri D'Elia d98e1b1cd9 elf_mem_map: uniquify file names in qdirstat output 2 years ago
  Yuri D'Elia 9917689fdf tools: update documentation for elf_mem_map 2 years ago
  Yuri D'Elia 9958c449e3 elf_mem_map: remove DWARF version except in help 2 years ago
  Yuri D'Elia 54e24036a8 elf_mem_map: add qdirstat output for space visualization 2 years ago
  Yuri D'Elia 676b925c5f elf_mem_map: cleanup 2 years ago
  Yuri D'Elia 7bdee552ce elf_mem_map: add declaration position in --map 2 years ago
  Yuri D'Elia d1720cba51 elf_mem_map: reduce some duplication 2 years ago
  Yuri D'Elia c875aef49c elf_mem_map: increase width again to fit new output 2 years ago
  Yuri D'Elia 71ef94da2e elf_mem_map: improve alignment of arrays 2 years ago
  Yuri D'Elia cb4f5cff9f elf_mem_map: improve display of array-of-strings 2 years ago
  Yuri D'Elia 776b82a6db elf_mem_map: expand member arrays 2 years ago
  Yuri D'Elia 29513a369d elf_mem_map: allow to customize the name column's width 2 years ago
  Yuri D'Elia 7f76f62af9 elf_mem_map: fix uleb128 decoding (fixes incorrect member offsets) 2 years ago
  Yuri D'Elia a5635997b2 elf_mem_map: allow to annotate overlapping regions for clarity 2 years ago
  Yuri D'Elia 1d82d2da64 get_elf_map: do not reprocess members twice 2 years ago
  Yuri D'Elia 615e8575bb elf_mem_map: decode structs 2 years ago
  Yuri D'Elia 9ddb5991f2 elf_mem_map: allow to disable gap dumps 2 years ago
  Yuri D'Elia bb8d171f34 elf_mem_map: decode integers with correct endianness 2 years ago
  Yuri D'Elia 1181e78484 elf_mem_map: handle all pointer types correctly 2 years ago
  Yuri D'Elia c311266a83 elf_mem_map: handle abstract locations 2 years ago
  Yuri D'Elia 2718dbb42c elf_mem_map: array n-dimensional expansion 2 years ago
  Yuri D'Elia 29b8c89ec2 elf_mem_map: decode arrays (first dimension) 2 years ago
  Yuri D'Elia 1de3fa51c9 elf_mem_map: decode doubles correctly 2 years ago
  Yuri D'Elia 40b737e33d elf_mem_map: switch to a named tuple for extensibility 2 years ago
  Yuri D'Elia f2192dc5e6 elf_mem_dump: fix unknown address 2 years ago
  Yuri D'Elia c321ba4821 elf_mem_map: also dump gaps between known regions 2 years ago
  Yuri D'Elia 4c6339ac46 elf_mem_map: decode correctly void pointers 2 years ago
  Yuri D'Elia 1095b26570 Add several low-level debugging tools 2 years ago
  3d-gussner e779803261 Add/fix few functions 2 years ago
  Guðni Már Gilbert 400f673fe0 Remove redundant extern variable lcd_encoder from menu.cpp 2 years ago
  Guðni Már Gilbert d087973e00 Remove redundant extern variable is_usb_printing from tmc2130.cpp 2 years ago
  Guðni Már Gilbert 6aee17b4ca lcd_change_fil_state has two extern's in Marlin.h, only one needed. 2 years ago
  Guðni Már Gilbert 3cfd706fff Remove useless extern in cmdqueue.cpp 2 years ago
  Guðni Már Gilbert 31c8e4bc4c * Remove redundant externs already included with temperature.h 2 years ago
  Guðni Már Gilbert eb9c8c8c20 Improve mc_arc() parameters 2 years ago
  Guðni Már Gilbert 538ce06bf0 Remove unused function lcd_choose_color() 2 years ago
  Guðni Már Gilbert bbe62b136a Remove unused Sound_Save() function declaration 2 years ago
  Yuri D'Elia d04ea859fb Consolidate "Unknown X-Code" to save 16 bytes 2 years ago
  Yuri D'Elia 47b1e6ccef Remove spourious trailing whitespace in errors 2 years ago
  Yuri D'Elia 1888c783cb Print an error on unknown D-codes 2 years ago
  Guðni Már Gilbert d853c19a21 Use fabs() instead of abs() when using floats 2 years ago
  Yuri D'Elia 31b913cddb Correct the C implementation for MultiU16X8toH16 2 years ago
  Guðni Már Gilbert 710852a1f2 Change nrFiles from int16_t to uint16_t 2 years ago
  Guðni Már Gilbert 16602f4003 change boolean to bool 2 years ago
  3d-gussner 3d871a3aa6 Merge remote-tracking branch 'upstream/MK3' into PFW-1271_PF-buildv20 2 years ago
  3d-gussner ffb7412637 Fix fresh build 2 years ago
  D.R.racer 4580b8a78c Version changed (3.10.1 build 4587) 2 years ago
  Yuri D'Elia 1279a6cf4b Correctly read FW_VERSION_NR array from progmem 2 years ago
  Yuri D'Elia 56e531d40a Improve/fix D23 for M2.5/S printers 2 years ago
  Yuri D'Elia 380e34d481 Include "Dcodes.h" after "Marlin.h" for configuration 2 years ago
  Voinea Dragos ecce6f865f write_command() no line number handling 2 years ago
  Yuri D'Elia 53fcd6fc8f Work-around GCC LTO codegen bug in process_commands() 2 years ago
  3d-gussner 9fd9ce34a9 MK404 is only supported on Linux at this moment. 2 years ago
  3d-gussner 36f2b5375f Merge remote-tracking branch 'upstream/MK3' into PFW-1271_PF-buildv20 2 years ago
  3d-gussner 17d7fdf333 Fix MK404 user interaction not to show if compiling 'All' variants 2 years ago
  3d-gussner 9f9203b280 Change atmega404 board flash argument to y 2 years ago
  Yuri D'Elia 66ee9a295f Remove useless function EEPROM_read_st 2 years ago
  Yuri D'Elia 8d11ad9d2d xfdump: correctly erase all sectors in xfdump_erase 2 years ago
  DRracer 8ff9b184c2 Merge pull request #3093 from DRracer/version-shave 2 years ago
  3d-gussner 2b29e52d53 Defined OUTPUT_FILENAME in one location 2 years ago
  D.R.racer 88074ac6c8 Remove FW version parsing 3 years ago
  DRracer 6188870c2e Merge pull request #3157 from wavexx/improve_d2 2 years ago
  DRracer f36b9173da Merge pull request #3191 from wavexx/fix_longpress_isr 2 years ago
  Yuri D'Elia 79287ffb3d Use uint8_t consistently for the block buffer's index 2 years ago
  Yuri D'Elia f4fcdae630 Fixup the DUMP_MAGIC constant 2 years ago
  Yuri D'Elia 934b567e5a Remove "bonus" exclamation points from the crash message 2 years ago
  jfestrada 72be8f7be5 Fix spanish translation for MSG_UNLOAD_SUCCESSFUL (#3185) 2 years ago
  Yuri D'Elia edde002cdc Merge remote-tracking branch 'upstream/MK3' into fix_longpress_isr 2 years ago
  3d-gussner 8d376ebee0 comment out debug echos 2 years ago
  3d-gussner 7a67d578fd Pf-build.sh 2 years ago
  3d-gussner 3c649a89d3 Merge remote-tracking branch 'upstream/MK3' into PFW-1271_PF-buildv20 2 years ago
  Alex Voinea 30d9faef9d Minor fixes to SD presence handling (#3139) 2 years ago
  Voinea Dragos 0cbc759d8b Second attempt at retrieving the SN from the 32u2 IC 2 years ago
  Alex Voinea 6d25345fc4 optiboot_xflash comment about w25x20cl messages 2 years ago
  Yuri D'Elia a92c0278fe Use LCD_WIDTH instead of hardcoding 20 2 years ago
  metacollin 8a7dd08116 Fixed C++ bug 3 years ago
  Yuri D'Elia 09e935d27b Fix two new explicit case fallthru warnings 3 years ago
  Yuri D'Elia d727a949dd Silence explicit case-fallthru 3 years ago
  Yuri D'Elia c096462aab Clarify statement by adding extra braces 3 years ago
  Yuri D'Elia 051f03119b Fix misleading indentation warnings by expanding tabs 3 years ago
  Yuri D'Elia f2fe57bfc0 Fix unused static declaration warnings 3 years ago
  Jonas Meyer 7bd43b77d7 Remove unnecessary assignment 3 years ago
  Yuri D'Elia eafdf6c216 Also move host_keepalive to manage_inactivity() 2 years ago
  Yuri D'Elia 29322d4bf3 Move host_autoreport() to manage_inactivity() 2 years ago
  Yuri D'Elia 5c8a231ed6 Move autoreporting out of the temperature ISR 2 years ago
  Yuri D'Elia 97f66a6a13 Remove ignored/incorrect PROGMEM 2 years ago
  DRracer 5bb8bb2ccb Merge pull request #3134 from wavexx/fix_partial_redraw2 2 years ago
  Yuri D'Elia 700825ff76 serial_dump: include hex prefix 2 years ago
  Yuri D'Elia bff79d290a GETPC: Do not manipulate the 32bit return address 2 years ago
  Yuri D'Elia 72b8f0d1e6 Add some warnings in lcd_buttons_update 2 years ago
  3d-gussner dde5cea48b Default extrusion graphics to line. Thanks @vintagepc point it out 2 years ago
  3d-gussner bc98be3d29 Documentation and version number 2 years ago
  3d-gussner b12c0e2326 Use atmega404 if extanded RAM or FLASH size are chosen 2 years ago
  3d-gussner ae41d6ca40 Change version to v2.0.0 2 years ago
  3d-gussner 4614400298 Save ELF files for FW3.10.1 debugging PRs 2 years ago
  Yuri D'Elia 06d91ddcee Handle Long-Press in the main loop 2 years ago
  3d-gussner 56889bae13 Update PF-build.sh to work after @DRracer Remove FW version parsing PR 2 years ago
  3d-gussner 589b781d04 Merge branch 'MK3_MK404' into PFW-1271_PF-buildv20 2 years ago
  Yuri D'Elia dd8c6c064c xfdump: simplify stack debugging (sample pc+sp) 2 years ago
  Yuri D'Elia 97535ec0c1 Fix last commit 2 years ago
  Yuri D'Elia e6d520cf6e Add test code for the stack overflow handler 2 years ago
  Yuri D'Elia a614268c94 serial_dump_and_reset: do not call manage_heater with interrupts disabled 2 years ago
  Yuri D'Elia d193d0f7ac serial_dump: manipulate WDT just once 2 years ago
  Yuri D'Elia 928c7211ad emergency handlers: always save SP _at_ the crash location 2 years ago
  Yuri D'Elia 96aad0a475 Remove duplication in crash handlers 2 years ago
  Yuri D'Elia fa0f58d5bc serial_dump: add description about bad_isr 2 years ago
  Yuri D'Elia e90f5d81ec Merge pull request #7 from leptun/improve_d2 2 years ago
  Voinea Dragos 8e667a8acd bad ISR catch 2 years ago
  Yuri D'Elia d2041ee2fe Enable debugging features on all variants 2 years ago
  Yuri D'Elia 7db667ca86 serial_dump_and_reset: turn on print fan while dumping 2 years ago
  Yuri D'Elia ea51d65137 serial_dump_and_reset: do not completely disable WDT 2 years ago
  Yuri D'Elia 6de98f7b0d Move inclusion closer to the usage point 2 years ago
  Yuri D'Elia 050cf72e98 Move stack checking to the temperature ISR 2 years ago
  Yuri D'Elia 01934b89e9 xflash_dump is now always required in all variants 2 years ago
  Yuri D'Elia 449510392d Untangle a bit some recursive include mess 2 years ago
  Yuri D'Elia e28301f391 Report crash also in MK2.5, fix stack_error abuse 2 years ago
  Yuri D'Elia bd57e00448 Implement an online crash dumper for MK2.5 boards 2 years ago
  Yuri D'Elia f7dc8dcaef Fix usage of RAMEND 2 years ago
  Yuri D'Elia f18d4757c6 Introduce STACK_GUARD_MARGIN in all variants 2 years ago
  Yuri D'Elia 9663653671 xfdump_erase: remove redundant XFLASH_SPI_ENTER() 2 years ago
  Yuri D'Elia 3187b96ca4 xfdump: report to the host that a dump is available 2 years ago
  Yuri D'Elia 31f416fd5e Rename dump_crash_source to dump_crash_reason 2 years ago
  Yuri D'Elia c375610668 Move "WDR dump" inside EMERGENGENCY_DUMP 2 years ago
  Yuri D'Elia 63f870c089 Enable the "WDR reset" menu item in DEBUG_BUILD only 2 years ago
  Yuri D'Elia 0362b6484f xfdump_full_dump_and_reset: set a guaranteed minimum WDT 2 years ago
  Yuri D'Elia 3f7b9b42b2 Merge pull request #6 from leptun/improve_d2 2 years ago
  Voinea Dragos 0a77f2c02d Dump header as well 2 years ago
  Voinea Dragos 094c577e9d Fix XFLASH_DUMP print_mem 2 years ago
  Voinea Dragos 1e786c7f55 WDR crash initial 2 years ago
  Voinea Dragos 8c3d76f85f Fix millis reference 2 years ago
  Yuri D'Elia fab04dbc6c D6: remove option for unsupported models 2 years ago
  Yuri D'Elia 318ee695c2 xfdump_layout: add some comments 2 years ago
  Yuri D'Elia c2e64c8c6e xfdump: fix another off-by-one static size check 2 years ago
  Yuri D'Elia 378f239ff0 dcode_code: fix inverted define to print larger types 2 years ago
  Yuri D'Elia 56d0848ea9 xfdump: fix size check 2 years ago
  Yuri D'Elia 9d3b19b637 xfdump: defensive static checks to ensure dump location always fits 2 years ago
  Yuri D'Elia dcfdce87c7 Document new applicable build options in the variant files 2 years ago
  Yuri D'Elia c072fbbf02 Dcodes: fix daddr_t type when only XFLASH_DUMP is enabled 2 years ago
  Yuri D'Elia c331c07b16 xfdump: reuse standard definitions for SRAM size/offset 2 years ago
  Yuri D'Elia 520f7a2e26 config: add sanity checks for XFLASH_DUMP options 2 years ago
  Yuri D'Elia 0e75bc9d8e lang/fw-build.sh: fix padding calculation 2 years ago
  Yuri D'Elia 1a88e339bf xfdump: fix build with XFLASH_DUMP disabled 2 years ago
  Yuri D'Elia a6e372e696 Typo 2 years ago
  Yuri D'Elia c31109c750 Implement MENU_DUMP: offline memory dump from "Support" 2 years ago
  Yuri D'Elia c089ac5341 Implement EMERGENCY_DUMP for offline analysis 2 years ago
  Yuri D'Elia e7f78125d3 Merge remote-tracking branch 'panayiotis/FixEepromAddressDocumentation' into improve_d2 2 years ago
  Yuri D'Elia 30402e0404 Dcodes: add D20/D21/D22 to generate/read/clear dumps 2 years ago
  Yuri D'Elia 8417083b13 lang/fw-build.sh: check for language data size during build 2 years ago
  Yuri D'Elia 6dfef76346 xfdump: implement dump-to-xflash functionality 2 years ago
  Yuri D'Elia b398a09a4f xflash: add xflash_multipage_program and documentation 2 years ago
  Yuri D'Elia 86e753fe37 xflash: remove some duplication 2 years ago
  Yuri D'Elia 5ae8bad0ba Introduce "xflash_layout" to organize XFLASH's content 2 years ago
  Yuri D'Elia af636c7f2a Move "xflash" include inside the conditional 2 years ago
  Yuri D'Elia 0fcdada579 D6: add documentation 2 years ago
  Yuri D'Elia ed9f52dd85 D6: also hide declaration behind conditional 2 years ago
  Yuri D'Elia db096557d4 D[236]: remove "busy" messages while dumping, avoid WDT 2 years ago
  Yuri D'Elia 12e124324f Remove PROGMEM handling from print_mem until D5 uses dcode_core 2 years ago
  Yuri D'Elia f9371146d0 Implement reading XFLASH with D6 2 years ago
  Yuri D'Elia 915f5a7692 Unify D2 and D3 handling 2 years ago
  Yuri D'Elia 2d25a5705f Fix D2 to read the entire SRAM content 2 years ago
  Yuri D'Elia 62f496e1d6 Allow D2 to be enabled selectively 2 years ago
  DRracer a8a253c0ae Merge pull request #3163 from DRracer/farm-m1 2 years ago
  D.R.racer 6bb7c0e3d3 Farm workaround M1 message 2 years ago
  Panayiotis-git 3c63ddaafd Fix the eeprom address documentation 2 years ago
  DRracer f5ca79926e Merge pull request #3141 from prusa3d/MK3_3.10.0 2 years ago
  D.R.racer 04de9c0c8a Version changed (3.10.0 build 4481) 2 years ago
  3d-gussner e765c300b0 Update MK404 part to '--bootloader-file ""' 2 years ago
  Yuri D'Elia 1a92f2e19f Merge remote-tracking branch 'upstream/MK3_3.10.0' into fix_partial_redraw2 2 years ago
  3d-gussner 31b38393e9 Merge branch 'MK3_more_flash_more_ram' into MK3_MK404 2 years ago
  3d-gussner a5ba31b247 Indentations 2 years ago
  3d-gussner 16359780f9 Add MK404 'atmega404' and 'atmega404_no_bootloder' option 2 years ago
  3d-gussner 41d7eaa94b Update documentation 2 years ago
  3d-gussner d8dd8e49e5 Comment out "manual" xflash update as MK404 can do it now 2 years ago
  3d-gussner 2195116beb Add EN_ONLY support for MK404 3 years ago
  3d-gussner 6ba8999607 Indentations 3 years ago
  3d-gussner 90bc572154 Merge remote-tracking branch 'upstream/MK3_3.10.0' into MK3_MK404 3 years ago
  3d-gussner 2ad0514d7c Save changes 3 years ago
  DRracer eaa6801849 Merge pull request #3136 from leptun/PFW-1225_fix_open_file_from_sd_wrong_file 3 years ago
  DRracer 80e248662c Merge pull request #3079 from 3d-gussner/PFW-1189 3 years ago
  3d-gussner fa134ee9f1 Update all po files 3 years ago
  DRracer 89386036e6 Merge pull request #3137 from leptun/PFW-1239_fix_M23_subroutine_issue 3 years ago
  D.R.racer 8e35ab0699 Looks like the last CZ message 3 years ago
  D.R.racer 6563a691ce ES missing translations 3 years ago
  Yuri D'Elia a5e40079e5 lang-check: optionally check for missing translations 3 years ago
  Yuri D'Elia a98bc1616a lang-check: add missing rulers 3 years ago
  Yuri D'Elia 8edecc911b lang-check: Fix comment indentation 3 years ago
  Yuri D'Elia 752d6c47f8 lang-check: use color names consistently 3 years ago
  Yuri D'Elia 27d64b03fa More missing Italian translations 3 years ago
  DRracer a3b392d96c Merge pull request #3130 from DRracer/move-alldata-2-end 3 years ago
  Voinea Dragos 8610d0a850 Do not allow M23 to run subroutines 3 years ago
  D.R.racer 759451c7e3 PL "Community made" 3 years ago
  3d-gussner b9ee74ba0e Update some French and Spanish missing translations 3 years ago
  3d-gussner 2580733439 Another missing Spanish translation 3 years ago
  DRracer 7c914ec2d1 Merge pull request #3125 from leptun/PFW-1238_fix_M105_from_SD 3 years ago
  3d-gussner 6b0e5ba387 Merge branch 'PFW-1189' of https://github.com/3d-gussner/Prusa-Firmware into PFW-1189 3 years ago
  3d-gussner 07cc28e1c9 Updated few missing French Spanish translations 3 years ago
  D.R.racer b77945263d IT temperatura 3 years ago
  D.R.racer eccb9bb9df PL 2 3 years ago
  D.R.racer ba90ee3e28 Add 2 missing translations for PL 3 years ago
  Voinea Dragos 1a70f3369d fix comment 3 years ago
  Voinea Dragos d9976ca69c Fix menu_action_sdfile() check_file(filename) call 3 years ago
  D.R.racer 4aba5b636c CZ Printer IP addr translation 3 years ago
  D.R.racer a2326ff986 Merge branch 'PFW-1189' of github.com:3d-gussner/Prusa-Firmware into PFW-1189 3 years ago
  D.R.racer 0e3d4932cf Add missing CZ translations 3 years ago
  3d-gussner a9ae8ea731 Remove `SpoolJoin` from translations 3 years ago
  3d-gussner 2421733b3e Update Dutch translation for `Cont.` as `Door.` 3 years ago
  3d-gussner 7357bc0b3b Thanks to @vintagepc for reviewing Dutch translation 3 years ago
  3d-gussner ef9748aa43 Update missing Dutch translations 3 years ago
  3d-gussner 48dcbbb7b2 More missing German translations 3 years ago
  3d-gussner c18243cb21 More missing German translations 3 years ago
  3d-gussner 79a5dafd25 Update few missing German translations 3 years ago
  3d-gussner 488d9d4b38 Remove "Firmware" from translations 3 years ago
  3d-gussner d251f3980a Remove FlashAir from translations 3 years ago
  3d-gussner b5ba4041d3 Remove FINDA from translations 3 years ago
  3d-gussner 08566d4121 Remove PINDA from translations 3 years ago
  3d-gussner 03c2f5482f Merge branch 'PFW-1189' of https://github.com/3d-gussner/Prusa-Firmware into PFW-1189 3 years ago
  3d-gussner 30b383195d Remove copy/paste error 3 years ago
  Yuri D'Elia aee04a43e2 Redraw "Preheating to load" in full when modified by other actions 3 years ago
  D.R.racer e172eaeb92 Fix ES 3 years ago
  D.R.racer 5089b2cddb Move All Data into last position in Factory Reset menu 3 years ago
  3d-gussner 7a54c3638c Remove web links and `Prusa i3 MKxy OK.` from translations 3 years ago
  3d-gussner 686054b95e Changed `x of 4` and `y of 9` to `x/4` and `y/4` 3 years ago
  3d-gussner 94b195123f Revert "Fix FR" 3 years ago
  3d-gussner 3983fe9ad6 Merge branch 'PFW-1189' of https://github.com/3d-gussner/Prusa-Firmware into PFW-1189 3 years ago
  D.R.racer a2fa7d34c5 Fix FR 3 years ago
  D.R.racer c8266988c8 Fix PL 3 years ago
  3d-gussner 738a24178a Update #MSG_FIND_BED_OFFSET_AND_SKEW_LINE1 c=20 r=3 3 years ago
  3d-gussner b8ae08fd3d Update `x of 4` and `x of 9` messages so longer text can be used. 3 years ago
  3d-gussner b3c5d395d4 Update French translation MSG_RESUME_PRINT 3 years ago
  3d-gussner 1145b231a9 Define MSG_SELFTEST_FS_LEVEL 3 years ago
  3d-gussner af519626aa Define MSG_FS_V_04_OR_NEWER and MSG_FS_V_03_OR_OLDER c=14 3 years ago
  Yuri D'Elia 900443286d lang-check: fancy ruler 3 years ago
  3d-gussner c11e06b2d4 Fix character ruler for too many rows 3 years ago
  3d-gussner b522ef4812 Update MSG_SEVER_SKEW and MSG_SLIGHT_SKEW message to c=14 3 years ago
  3d-gussner 79f6095bcc Add character ruler 3 years ago
  3d-gussner 946675b444 Remove white spaces and shortn some messages (inspired by FR, IT) 3 years ago
  3d-gussner 40c8f3e1ca Merge branch 'PFW-1189' of https://github.com/3d-gussner/Prusa-Firmware into PFW-1189 3 years ago
  3d-gussner 431c469688 Remove white spaces in cz,nl,pl translations 3 years ago
  D.R.racer c843feee02 Fix some trailing whitespace and puctuation CZ 3 years ago
  3d-gussner 536a8f5f87 Revert that has been commited by mistake 3 years ago
  3d-gussner 466de83ae0 Remove white spaces in messags which saves ~62bytes 3 years ago
  3d-gussner 127a8e4068 Fix typo in Spanish 3 years ago
  3d-gussner 624827e51d Use same wording in Spanish translation for "knob" 3 years ago
  3d-gussner 9cacb41cbf Fix French translation for MSG_MMU_LOAD_FAILS 3 years ago
  3d-gussner a645b30f63 Merge branch 'PFW-1189' of https://github.com/3d-gussner/Prusa-Firmware into PFW-1189 3 years ago
  3d-gussner cf4d8f68f4 Update lang/lang_en_fr.txt 3 years ago
  3d-gussner 2c687a9223 Update lang/lang_en_fr.txt 3 years ago
  3d-gussner 29ad021b23 Updated French translation of MSG_MMU_LOAD_FAILED c=20 as @awenelo suggested 3 years ago
  3d-gussner 6a52921e63 Use origin message for MSG_TOSHIBA_FLASH_AIR_COMPATIBILITY also in French 3 years ago
  3d-gussner fed80ebc73 Update French translation for MSG_SELECT_FILAMENT as @carlin57 suggested 3 years ago
  3d-gussner ff2b292234 Merge branch 'PFW-1189' of https://github.com/3d-gussner/Prusa-Firmware into PFW-1189 3 years ago
  3d-gussner 93cabea391 Update lang/lang_en_fr.txt 3 years ago
  3d-gussner 43b16a95ce Update lang/lang_en_fr.txt 3 years ago
  3d-gussner 1667e39a24 Update lang/lang_en_fr.txt 3 years ago
  3d-gussner 7d59e925be Update lang/lang_en_fr.txt 3 years ago
  3d-gussner b5478e5557 Update French translation for MSG_BED_CORRECTION_MENU by @carlin57 3 years ago
  3d-gussner c9c48a78e2 Update lang/lang_en_fr.txt 3 years ago
  3d-gussner 9f77ca31b8 Set MSG_SET_TEMPERATURE chars to c=20 3 years ago
  3d-gussner e53f37729b Update German translation 3 years ago
  Yuri D'Elia c536407ab0 Italian translation: remove trailing whitespace 3 years ago
  Yuri D'Elia ceb1d2aaaf lang-check: handle/warn about trailing whitespace 3 years ago
  Yuri D'Elia ecc375f94f lang-check: improve error strings 3 years ago
  Yuri D'Elia 0d89e48827 Two Italian translation improvements 3 years ago
  Yuri D'Elia e3ec295a0e lang-check: add checks for % escapes 3 years ago
  Yuri D'Elia 0db2c5c28a lang-check: cleanup 3 years ago
  Yuri D'Elia 5f0f6d740f lang-check: attempt at spotting short translations 3 years ago
  Yuri D'Elia 68fc2c71bb lang-check: add basic punctuation checks 3 years ago
  Yuri D'Elia 2ceb3897f1 lang-check: respect --no-warning 3 years ago
  3d-gussner 4bb13ce582 Merge branch 'PFW-1189' into MK3_MK404 3 years ago
  3d-gussner 722415703f Merge remote-tracking branch 'upstream/MK3_3.10.0' into MK3_MK404 3 years ago
  3d-gussner 1a5898c673 Improve MK404-build.sh check, update 3 years ago
  3d-gussner fe32d42101 Fix Dutch FINDA translation 3 years ago
  Yuri D'Elia 201d2a2434 lang-check: handle backslash sequences 3 years ago
  Marek Bel aa45091400 Scale extruder motor current linearly with speed. (#2813) 3 years ago
  Yuri D'Elia 27d7edae10 lang-check: highlight truncated strings 3 years ago
  Yuri D'Elia 7f9910ad28 Update Italian translations 3 years ago
  Yuri D'Elia e4655e5afa lang-check: pretty-print errors to aid in translation 3 years ago
  Yuri D'Elia 695af99ada lang-check: simplify wrapping, wrap according to cols 3 years ago
  Yuri D'Elia 340928acc3 lang-check: print a warning if cols!=20 on multiline messages 3 years ago
  3d-gussner 71ab3a9d53 Fix typo thanks to @wavexx 3 years ago
  3d-gussner 540940b6a5 Fix German too long translation MSG_MMU_MODE 3 years ago
  3d-gussner c2cde83d7e Fix MSG_SELFTEST_WIRINGERROR to c=18 3 years ago
  3d-gussner 3f51627c02 Fix MSG_NO being too short, no need to be 3 years ago
  3d-gussner 7cd76432b9 Fix German MSG_LOAD_TO_NOZZLE 3 years ago
  3d-gussner 10a026e075 Fix Dutch + German MSG_UNLOAD_FILAMENT too long translations 3 years ago
  3d-gussner 013708479f Fix MSG_UNLOAD_FILAMENT length as in MMU a number is added 3 years ago
  3d-gussner 2281bd1ef7 Merge branch 'PFW-1189' of https://github.com/3d-gussner/Prusa-Firmware into PFW-1189 3 years ago
  3d-gussner 032e8a5957 @leptun strip 1st char hack for duplicate `Cancel` 3 years ago
  D.R.racer 346044063a Fix length of some CZ messages 3 years ago
  Yuri D'Elia 90a9e350c0 Remove warnings caused by '\x00' 3 years ago
  Yuri D'Elia 2e6e2c4fde Do not output colors when redirecting output 3 years ago
  Yuri D'Elia 8b5d01e3b1 Be more strict when checking translation lenght 3 years ago
  Yuri D'Elia a9f0ff441d Allow to customize OUTDIR in config.sh 3 years ago
  3d-gussner 403b340f19 Added ` EXTENDED_M20` to RepRap Gcode wiki `M115` see https://reprap.org/mediawiki/index.php?title=G-code&type=revision&diff=189034&oldid=189033 3 years ago
  Voinea Dragos 635bc442d6 Fix M105 from SD 3 years ago
  DRracer b762a98c38 Merge pull request #3081 from 3d-gussner/MK3_Fix1_Dutch 3 years ago
  3d-gussner 28aec049a2 Fix Typo in MSG_FIL_FAILED 3 years ago
  3d-gussner ff9185d1bb Readded `>Cancel` as the `>` has some meaning. Sadly the previously 10bytes more free space are gone again 3 years ago
  3d-gussner 044c0a0a6a Update "\04Refresh" message as two "abcd" "efgh" in one line cause issues in lang-build scripts 3 years ago
  3d-gussner 8eeb177c7e Use textwarp to check the message length and rows 3 years ago
  3d-gussner e18d688b64 Fix merge issues 3 years ago
  3d-gussner 681bc560c9 Merge remote-tracking branch 'upstream/MK3_3.10.0' into PFW-1189 3 years ago
  DRracer be776f88f2 Merge pull request #3124 from prusa3d/MK3 3 years ago
  DRracer 999b93be35 Merge pull request #3123 from prusa3d/MK3_3.10.0 3 years ago
  DRracer 00ce16ee55 Merge pull request #3121 from wavexx/reduce_m600_min_height 3 years ago
  DRracer 1e0fd8af87 Merge pull request #3120 from wavexx/fix_lcd_redraw_autoload 3 years ago
  DRracer 98643825dc Merge pull request #3055 from awenelo/add-satin-sheet 3 years ago
  Yuri D'Elia daef5428d2 Rework gcode_M600_filament_change_z_shift to make it consistent 3 years ago
  Yuri D'Elia 8d04316497 Revert/cleanup mininum extruder height during M600 3 years ago
  Yuri D'Elia bc355674d9 Fix partial redraw during filament autoload 3 years ago
  DRracer bdeecd4fee Merge pull request #3116 from prusa3d/MK3_3.10.0 3 years ago
  DRracer 84a9a5fd3f Merge pull request #3113 from leptun/MK3_M20L_change_format 3 years ago
  Alex Voinea 7d82cab125 Update doxygen 3 years ago
  Alex Voinea c4b70b82f3 Document M20 T parameter 3 years ago
  Alex Voinea 1c0383c48f Add capability report for extended M20 3 years ago
  D.R.racer 7011014abb Save 16 bytes - pass ls_param by value to functions 3 years ago
  Voinea Dragos 2129bcf315 M20 T 3 years ago
  Voinea Dragos b16e1e5235 Invert size and LFN in M20 L output 3 years ago
  Voinea Dragos 6d7d8c7c75 Do not show hidden/system dirs with M20 L 3 years ago
  DRracer 10de97c042 Merge pull request #3110 from prusa3d/MK3_3.10.0 3 years ago
  Yuri D'Elia a5e4df390d Sync before/after setting mesh_bed_leveling_flag/homing_flag 3 years ago
  Yuri D'Elia 23c75da727 Fix Crash/PP recovery position on instructions with comments 3 years ago
  3d-gussner f8bec339e2 Hack to get more flash and ram for MK404 debug 3 years ago
  D.R.racer c43688a549 Version changed (3.10.0-RC2 build 4104) 3 years ago
  DESKTOP-AFI832L\admin 5a91e1de4d Calibration: fix checking of scan of calibration points 3 years ago
  Voinea Dragos 7356cd0811 EEPROM_JOB_ID 3 years ago
  DRracer f9f6284c50 Merge pull request #3100 from wavexx/powerpanic_state_fixes 3 years ago
  Yuri D'Elia 61faa49cbb Fix buffer overflow in buf 3 years ago
  Alex Voinea d31601dcb8 Use correct mfrid for gd25q20c 3 years ago
  Alex Voinea 94c1e65d83 Fix MK2.x builds 3 years ago
  Alex Voinea 56948157d0 GD25Q40C xflash support 3 years ago
  Alex Voinea 07bf4bbe48 Fix gd25120c rd_uid command 3 years ago
  Alex Voinea 9454f9d8ec Rename w25x20cl to xflash 3 years ago
  Yuri D'Elia 449d181971 G80: handle power panic 3 years ago
  Yuri D'Elia b46a52ffa8 G80: Handle crash detection 3 years ago
  Yuri D'Elia a5530593fa G80: preserve lcd status and message when aborting 3 years ago
  Yuri D'Elia 5923276a86 G80: correctly abort during crash detection 3 years ago
  Yuri D'Elia 5da39df968 G80: Use consistent XY axis feedrate 3 years ago
  Yuri D'Elia ce2e35d14d Move G80 into it's own function 3 years ago
  Yuri D'Elia d2be40491b PP recovery: clamp initial position to software endstops 3 years ago
  Yuri D'Elia 3276320a06 Hide tmc2130_sg_change behind DEBUG_CRASHDET_COUNTERS 3 years ago
  Yuri D'Elia 6a61c26955 Removed unused crashdet_stop_and_save_print2 3 years ago
  3d-gussner b74e1d6062 Update RepRap url for `M123` 3 years ago
  3d-gussner e12c1dde5c Updated RepRap Wiki and removed the @todos 3 years ago
  3d-gussner 3b431bcbc1 Fix output "Y distance from min" by removing ":" so translations can use 20 chars 3 years ago
  3d-gussner d864a5cfdb Define #MSG_PRINTER_IP c=18 3 years ago
  3d-gussner 9922774fa7 Cleanup some comments ////c= 3 years ago
  3d-gussner 8e74cbce58 Remove r=1 and some minor fixes 3 years ago
  3d-gussner 84c3853351 Fix Dutch #MSG_FINDA c=5 3 years ago
  3d-gussner fb691b44b4 Update #MSG_INFO_SENSORS c=18 3 years ago
  3d-gussner 3fb90be46f Define #MSG_SELECT c=18 3 years ago
  3d-gussner 9a0e67c1d7 Define #MSG_RENAME c=18 3 years ago
  3d-gussner 15919a0f09 Define #MSG_NOZZLE_DIFFERS_CONTINUE c=20 r=5, #MSG_NOZZLE_DIFFERS_CANCELLED c=20 r=9 3 years ago
  3d-gussner cdfc40fb62 Define #MSG_MODE_CHANGE_IN_PROGRESS c=20 r=3 3 years ago
  3d-gussner c0869efc76 Define #MSG_MK3S_FIRMWARE_ON_MK3 c=20 r=4, #MSG_MK3_FIRMWARE_ON_MK3S c=20 r=4 3 years ago
  3d-gussner 268d4020cf Define #MSG_FINDA c=5 and update #MSG_PINDA c=5 3 years ago
  3d-gussner c0b8eef6cd Define #MSG_FALSE_TRIGGERING c=20 3 years ago
  3d-gussner 1ca443f1f4 Define #MSG_CHECKS c=18 3 years ago
  3d-gussner e470ee96c8 Update #MSG_BACK c=18 3 years ago
  3d-gussner 5e049283be Define #MSG_UNKNOWN c=13 3 years ago
  3d-gussner 67a0370332 Delete "Unload" as it isn't used anymore 3 years ago
  3d-gussner 982153fee1 Define and update #MSG_PRINT_TIME c=19, #MSG_FILAMENT_USED c=19, #MSG_TOTAL_FILAMENT c=19, #MSG_TOTAL_PRINT_TIME c=19 3 years ago
  3d-gussner 7a88ce89e4 Update MSG_TEMPERATURE c=18 3 years ago
  3d-gussner c0df79d39a Define #MSG_SELECT_TEMP_MATCHES_MATERIAL c=20 r=4 3 years ago
  3d-gussner 7268f6a824 Define #MSG_SELECT_FILAMENT c=20 3 years ago
  3d-gussner 2e43234d3f Update #MSG_SUPPORT c=18 3 years ago
  3d-gussner ba38262109 Update #MSG_STATISTICS c=18 3 years ago
  3d-gussner 960ebac02d Update #MSG_SPEED c=15 3 years ago
  3d-gussner b52d87e9a5 Define #MSG_Z-LEVELING_ENFORCED c=20 r=4 3 years ago
  3d-gussner acbdc2ac8d Define #MSG_RUNOUTS c=7 3 years ago
  3d-gussner 76e7032279 Update #MSG_SOUND_LOUD c=7 3 years ago
  3d-gussner 09f4850a8a Define #MSG_SLIGHT_SKEW c=13, #MSG_SEVERE_SKEW c=13 3 years ago
  3d-gussner 4749dd1750 Define #MSG_CHECK_IR_CONNECTION c=20 r=4 3 years ago
  3d-gussner bd8ab39b39 Define #MSG_UNLOAD_FILAMENT_REPEAT c=20 r=4 3 years ago
  3d-gussner 5280a89fba Define #MSG_MMU_INSERT_FILAMENT_FIRST_TUBE c=20 r=6 3 years ago
  3d-gussner 0d48a32e87 Define #MSG_RESUME_NOZZLE_TEMP c=20 r=4 3 years ago
  3d-gussner 4f394c8c5a Update #MSG_TUNE c=18, #MSG_PREHEAT c=18 3 years ago
  3d-gussner fbecae1707 Update MSG_PREHEAT c=18 3 years ago
  3d-gussner ac810bf03a Define #MSG_REMOVE_SHIPPING_HELPERS c=20 r=3 3 years ago
  3d-gussner 00b100bf26 Define #MSG_NOZZLE_FAN c=10, #MSG_PRINT_FAN c=10 3 years ago
  3d-gussner baf231b0f1 Update MSG_NO_CARD c=18 3 years ago
  3d-gussner 742b9e9197 Fix #MSG_CRASHDETECT c=13 in Polish lang file 3 years ago
  3d-gussner 1c09e8411d Define #MSG_PINDA c=6 and remove duplicate saved 8 bytes 3 years ago
  3d-gussner 6a266d497b Define #MSG_REMOVE_TEST_PRINT c=20 r=4 3 years ago
  3d-gussner 5fcc731be1 Define #MSG_INSERT_FIL c=20 r=6 3 years ago
  3d-gussner 243fa68745 Define #MSG_LIN_CORRECTION c=18 3 years ago
  3d-gussner 44f7be134d Define #MSG_FS_VERIFIED c=20 r=3 3 years ago
  3d-gussner bcdaecb46d Define #MSG_TO_LOAD_FIL c=20, #MSG_TO_UNLOAD_FIL c=20, #MSG_PRESS_KNOB c=20 3 years ago
  3d-gussner de9697b913 Define #MSG_PREHEATING_TO_LOAD/UNLOAD/CUT/EJECT c=20 3 years ago
  3d-gussner d601355d40 Define #MSG_GCODE_NEWER_FIRMWARE_CONTINUE c=20 r=5, #MSG_GCODE_NEWER_FIRMWARE_CANCELLED c=20 r=8 3 years ago
  3d-gussner 669ab0f7f7 Define #MSG_GCODE_DIFF_CONTINUE c=20 r=4, #MSG_GCODE_DIFF_CANCELLED c=20 r=7 3 years ago
  3d-gussner e9750a7312 Update #MSG_FIL_FAILD c=20 r=5 3 years ago
  3d-gussner 4a5d7bc6b7 Define #MSG_Y_DIST_FROM_MIN c=20 3 years ago
  3d-gussner 2abdfeb4c5 Update #MSG_YES c=3 3 years ago
  3d-gussner 7f3fe6740b Update #MSG_NO_MOVE c=20 3 years ago
  3d-gussner 1a1a1d88a7 Update MSG_MOVE_X/Y/Z c=18 3 years ago
  3d-gussner c1f22a5239 Update #MSG_MOVE_AXIS c=18 3 years ago
  3d-gussner 99893d5066 Define #MSG_MMU_CONNECTED c=18 3 years ago
  3d-gussner 77a7a92a42 Define MSG_MMU_LOAD_FAILED c=20 3 years ago
  3d-gussner 4d9dffb042 Define #MSG_MEASURED_SKEW c=13 3 years ago
  3d-gussner 1e2dde9a52 Update #MSG_MEASURE_BED_REFERENCE_HEIGHT_LINE1 c=20 r=3 3 years ago
  3d-gussner afb7c82e26 Update #MSG_LOAD_TO_NOZZLE c=18 3 years ago
  3d-gussner 2408bc9f2e Update MSG_LOADING_COLOR c=20 3 years ago
  3d-gussner 62ecbb3ae3 Update #MSG_BABYSTEP_Z c=18 3 years ago
  3d-gussner 50e69fcca8 Update #MSG_NO c=3 3 years ago
  3d-gussner 75c0de741b Update #MSG_AUTO_DEPLETE c=13 3 years ago
  3d-gussner ddf47a3cac Update #MSG_NA c=3, #MSG_OFF c=3, #MSG_ON c=3 3 years ago
  3d-gussner d1813890a8 Update #MSG_STOPPED c=20 3 years ago
  3d-gussner a9ed513979 Update #MSG_STOP_PRINT c=18 3 years ago
  3d-gussner 46367c2a33 Update #MSG_STEALTH c=7, #MSG_SILENT c=7, #MSG_NORMAL c=7 3 years ago
  3d-gussner 3d848328ed Update #MSG_AUTO_POWER c=10 3 years ago
  3d-gussner 8771519880 Update #MSG_HIGH_POWER c=10 3 years ago
  3d-gussner afabfa789d Update #MSG_MODE c=6 3 years ago
  3d-gussner dc56b74263 Update #MSG_MODE c=9 3 years ago
  3d-gussner 2a08a6e441 Update #MSG_SELFTEST_WIRINGERROR c=17 3 years ago
  3d-gussner e6ea843e62 Update #MSG_SELFTEST_MOTOR c=18 3 years ago
  3d-gussner 35b000faea Update MSG_RESUMING_PRINT c=20 3 years ago
  3d-gussner 557486ee0b Update MSG_REFRESH c=18 3 years ago
  3d-gussner 54f594238b Update #MSG_NOZZLE c=12 3 years ago
  3d-gussner a878056be3 Update MSG_BACK c=18 3 years ago
  3d-gussner 610ba9bc45 Update #MSG_CARD_MENU c=18 3 years ago
  3d-gussner 5eb14dc433 Update #MSG_MAGNETS_COMP c=13 3 years ago
  3d-gussner 12372d6525 Update #MSG_Z_PROBE_NR c=14 3 years ago
  3d-gussner 7e2c272839 Update #MSG_MESH c=12 3 years ago
  3d-gussner bc8a23a0c6 Update #MSG_TOSHIBA_FLASH_AIR_COMPATIBILITY c=8 3 years ago
  3d-gussner 3367d7f8fa Update #MSG_SD_CARD c=8 3 years ago
  3d-gussner e8c3ba89dc Update #MSG_MMU_MODE c=8 3 years ago
  3d-gussner 532c7438d8 Update #MSG_NOZZLE_DIAMETER c=10, MSG_GCODE c=8 3 years ago
  3d-gussner 0a634f7d48 Update #MSG_FIRMWARE c=8 3 years ago
  3d-gussner 39b8a35f3a Update #MSG_MODEL c=8 3 years ago
  3d-gussner 5b843c3a4d Update #MSG_STRICT c=8 3 years ago
  3d-gussner f60ba6de87 Update #MSG_WARN c=8 3 years ago
  3d-gussner b465c96327 Update #MSG_NONE c=8 3 years ago
  D.R.racer cdcc06f376 SDFile - fix errorneous offset computation 3 years ago
  DRracer a251dcc14e Merge pull request #3064 from 3d-gussner/PFW-1222 3 years ago
  DRracer e76b0c40a8 Merge pull request #3066 from espr14/PFW-1223 3 years ago
  3d-gussner c4f80642b3 Update #MSG_MAIN c=18 3 years ago
  3d-gussner 4417deb9ea Update #MSG_SOUND_BLIND c=7, #MSG_SOUND_LOUD c=7, MSG_SOUND_ONCE c=7 3 years ago
  3d-gussner e4ed1ffc17 Update #MSG_SOUND c=9 3 years ago
  3d-gussner 2ce5d3a840 Update #MSG_RPI_PORT c=13 3 years ago
  3d-gussner 2ca8a23e4a Update #MSG_SORT_ALPHA c=8 #MSG_SORT_TIME c=8 3 years ago
  3d-gussner c85ebb4aa3 Update #MSG_SORT c=7 3 years ago
  3d-gussner 440ec49a36 Update #MSG_SETTINGS c=18 3 years ago
  3d-gussner 6900c206cd Define #MSG_SEL_PREHEAT_TEMP c=20 r=6 3 years ago
  3d-gussner 12dc48ff1a Update #MSG_BELTTEST c=18 3 years ago
  3d-gussner b697117654 Update #MSG_SELFTEST c=18 3 years ago
  3d-gussner 9bc702ae0a Update #MSG_SELFTEST_OK c=20 3 years ago
  3d-gussner 00fedbb08a Update MSG_LANGUAGE_SELECT c=18 3 years ago
  3d-gussner 164226c739 Define #MSG_RIGHT c=10 3 years ago
  3d-gussner 2daf1d9114 Define #MSG_LEFT c=10 3 years ago
  3d-gussner 169f8983b6 Define #MSG_ADDITIONAL_SHEETS c=20 r=9 3 years ago
  3d-gussner a82ca3def4 Update #MSG_WATCH c=18 3 years ago
  3d-gussner 0139bb5a5e Update #MSG_CHANGE_SUCCESS c=20 3 years ago
  3d-gussner 0e86ab88fd Update #MSG_FILAMENTCHANGE c=18 3 years ago
  3d-gussner 9c0300df66 Update #MSG_HEATING c=20 3 years ago
  3d-gussner 280a3c4d3a Update #MSG_PRUSA3D_HOWTO c=18 3 years ago
  3d-gussner 12bd2f1a09 Update #MSG_PRUSA3D c=18 3 years ago
  3d-gussner 4e4fb7a59f Update #MSG_PRUSA3D_FORUM c=18 3 years ago
  3d-gussner 10c586b577 Update #MSG_FLOW c=15 3 years ago
  3d-gussner db07d55c29 Define #MSG_MMU_FIX_ISSUE c=20 r=4 3 years ago
  3d-gussner 7c9891d85e Update #MSG_FSENSOR c=12 3 years ago
  3d-gussner 7a99f63ffd Define #MSG_FAIL_STATS c=18 3 years ago
  3d-gussner d427563e2b Define #MSG_MMU_FAIL_STATS c=18 3 years ago
  3d-gussner 1b4241f031 Update #MSG_ERROR c=10 3 years ago
  3d-gussner 1b8ca77b0c Define #MSG_MMU_CUTTING_FIL c=18 3 years ago
  3d-gussner b6031c02a8 Update #MSG_DISABLE_STEPPERS c=18 3 years ago
  3d-gussner 69b9acdfc1 Define #MSG_CRASH_RESUME c=20 r=3 3 years ago
  3d-gussner 411caddc55 Define #MSG_CHOOSE_FIL_1ST_LAYERCAL c=20 r=7 3 years ago
  3d-gussner 2d2dc23e81 Define #MSG_CALIBRATING_HOME c=20 3 years ago
  3d-gussner c8af50d344 Define #MSG_COPY_SEL_LANG c=20 r=3 3 years ago
  3d-gussner 71638b49fc Update #MSG_COOLDOWN c=18 3 years ago
  3d-gussner 5f9b66a23a Update #MSG_NOT_COLOR c=19 3 years ago
  3d-gussner 02ff05639b Define MSG_CHECKING_FILE c=17 3 years ago
  3d-gussner f2298b1a46 Update #MSG_SD_REMOVED c=20 3 years ago
  3d-gussner 1e2f5c8323 Update #MSG_MENU_CALIBRATION c=18 3 years ago
  3d-gussner 3733c42d38 Update #MSG_HOMEYZ_DONE c=20 3 years ago
  3d-gussner 79aa6b198d Update #MSG_HOMEYZ c=18 3 years ago
  3d-gussner fa0382fd3e Update #MSG_CALIBRATE_BED c=18 3 years ago
  3d-gussner 2985c6bd6b Update #MSG_BED c=13 3 years ago
  3d-gussner 7c906dd0b1 Update #MSG_BED_CORRECTION_MENU c=18 3 years ago
  awenelo d8996af574 minor changes 3 years ago
  3d-gussner b505dae90d Move Dutch removing part to correct loaction 3 years ago
  3d-gussner 4d464b79f4 Update MSG_BED_HEATING c=20 3 years ago
  3d-gussner 3ee23af7b7 Update MSG_BED_DONE c=20 3 years ago
  3d-gussner ba0f2b2501 Update MSG_LOOSE_PULLEY c=20 3 years ago
  3d-gussner 3fcc81ebdb Update MSG_SELFTEST_ENDSTOP c=16 3 years ago
  3d-gussner 17df4e1c8d Update MSG_SELFTEST_ENDSTOPS c=20 3 years ago
  3d-gussner 4a6c53daf8 Update MSG_SELFTEST_PLEASECHECK c=20 3 years ago
  3d-gussner b3d3feef4d Update MSG_SELFTEST_ERROR c=20 3 years ago
  3d-gussner 5d167ce813 Update MSG_SELFTEST_NOTCONNECTED c=20 3 years ago
  3d-gussner 625604d704 Update MSG_SELFTEST_HEATERTHERMISTOR c=20 3 years ago
  3d-gussner b86ad11b68 Update MSG_SELFTEST_BEDHEATER c=20 3 years ago
  3d-gussner b3b062e4ba Update MSG_SELFTEST_SWAPPED c=16 3 years ago
  3d-gussner b6684f6114 Update MSG_SELFTEST_AXIS c=16 3 years ago
  3d-gussner bc9f6b6ae6 Update MSG_SELFTEST_AXIS_LENGTH c=20 3 years ago
  3d-gussner 83cbfda113 Update MSG_AUTO_HOME c=18 3 years ago
  3d-gussner fe1b4e50be Update MSG_AMBIENT c=14 3 years ago
  3d-gussner 178fe36dc7 Create MSG_CANCEL c=9 3 years ago
  3d-gussner 1c965cd988 Update MSG_MEASURED_OFFSET c=20 3 years ago
  3d-gussner 92acac453f Update MSG_MMU_POWER_FAILS c=15 3 years ago
  3d-gussner dd67c5110a Fix some Dutch translations 3 years ago
  3d-gussner 0819a9ba35 Fix too long Dutch translation 3 years ago
  3d-gussner 746761b193 Update MSG_NEW_FIRMWARE_PLEASE_UPGRADE c=20 3 years ago
  3d-gussner 2a848923ea Update MSG_BED_LEVELING_FAILED_POINT_LOW c=20 r=6 3 years ago
  3d-gussner baabc0bd1e Fix typo "didnt" to "didn't" 3 years ago
  3d-gussner b806c017cb Update MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR c=20 r=6 3 years ago
  3d-gussner 542ff58529 Update MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR c=20 r=6 3 years ago
  3d-gussner 147fddd060 Update MSG_CALIBRATE_BED_RESET c=18 3 years ago
  3d-gussner 6300211268 Update MSG_PLACE_STEEL_SHEET c=20 r=5 3 years ago
  3d-gussner 79a148bb14 Update #MSG_DEFAULT_SETTINGS_LOADED c=20 r=6 3 years ago
  3d-gussner 8378ea8f83 Update MSG_NEW_FIRMWARE_AVAILABLE c=20 r=2 3 years ago
  3d-gussner 9e60953d44 Update "G-code sliced for a newer firmware. Please update the firmware. Print cancelled." 3 years ago
  3d-gussner a09f2c2f2d Update "G-code sliced for a newer firmware. Continue?" 3 years ago
  3d-gussner 9ed4efe5dc Update "G-code sliced for a different level. Please re-slice the model again. Print cancelled." 3 years ago
  3d-gussner 41022d7d60 Update "Choose a filament for the First Layer Calibration and select it in the on-screen menu." 3 years ago
  3d-gussner c82c72a937 Fix too long Spanish translation 3 years ago
  3d-gussner 813aabab56 Update "Select nozzle preheat temperature which matches your material." c=20 r=6 3 years ago
  3d-gussner 65e389b6fd Fix too long German translations 3 years ago
  3d-gussner d52a7326be Update "Please insert filament into the extruder, then press the knob to load it." c=20 r=6 3 years ago
  3d-gussner cc29a9485e Update "If you have additional steel sheets, calibrate their presets in Settings - HW Setup - Steel sheets." c=20 r=9 3 years ago
  3d-gussner 03a13ace5a Update "Printer nozzle diameter differs from the G-code. Please check the value in settings. Print cancelled." c=20 r=9 3 years ago
  3d-gussner 17c015f5c0 Update "Printer nozzle diameter differs from the G-code. Continue?" c=20 r=5 3 years ago
  3d-gussner ba9e26f444 Update #MSG_GCODE_DIFF_PRINTER_CANCELLED c=20 r=8 3 years ago
  3d-gussner 1bba2255c1 Update MSG_WAITING_TEMP c=20 r=4 3 years ago
  3d-gussner 787b6111e5 Fix too long translations in MSG_USERWAIT c=20 3 years ago
  3d-gussner bab4994b89 Update #MSG_FIND_BED_OFFSET_AND_SKEW_LINE1 c=20 r=2 3 years ago
  3d-gussner 6a54196862 Update "Now remove the test print from steel sheet." c=20 r=4 3 years ago
  3d-gussner 8cbf0b3318 Update "Crash detected. Resume print?" c=20 r=2 3 years ago
  3d-gussner 891861d475 Update "G-code sliced for a different level. Continue?" c= r= values 3 years ago
  3d-gussner f9a92e2277 Update "Y distance from min" c=20 3 years ago
  3d-gussner 1f6834204f Update "Select filament:" c= value 3 years ago
  3d-gussner cdd1d302fd Update MSG_PLEASE_LOAD_PLA c=20 r=4 3 years ago
  3d-gussner cbe2a4d301 Update MSG_CHECK_IDLER c=20 r=5 3 years ago
  3d-gussner 4dc99d970d Update "MMU needs user attention." c= r= values 3 years ago
  3d-gussner 0928c3d133 Update "Lin. correction" c=18 3 years ago
  3d-gussner ce42f2c2a5 Update MSG_BED_HEATING_SAFETY_DISABLED c=20 r=4 3 years ago
  3d-gussner 5f9d6a0401 Update "Fail stats" c=18 3 years ago
  3d-gussner 878200ecae Update "Fail stats MMU" c=18 3 years ago
  3d-gussner 6691f67e7d Fix too long #MSG_RECOVER_PRINT translations 3 years ago
  3d-gussner 5cbb5b0e17 Update "Printer nozzle diameter differs from the G-code. Please check the value in settings. Print cancelled." c= r= values 3 years ago
  3d-gussner c39b22724d Update "G-code sliced for a different level. Please re-slice the model again. Print cancelled." c= r= values 3 years ago
  3d-gussner 65f35e25b8 Update MSG_WIZARD_V2_CAL_2 c=20 r=12 3 years ago
  3d-gussner 5a36b49cee Update "Select temperature which matches your material." c= r= values 3 years ago
  3d-gussner 41db8eed30 Update "Select nozzle preheat temperature which matches your material." c= r= values 3 years ago
  3d-gussner f319dede23 Update "Please insert filament into the first tube of the MMU, then press the knob to load it." c= r= values 3 years ago
  3d-gussner 286c1931a5 Update #MSG_WIZARD_LOAD_FILAMENT c=20 r=5 3 years ago
  3d-gussner 6a267e5a9a Update "Press the knob to resume nozzle temperature." c= r= values 3 years ago
  3d-gussner b001eaec8d Update "If you have additional steel sheets, calibrate their presets in Settings - HW Setup - Steel sheets." c= r= values 3 years ago
  3d-gussner 18a58c504d Update #MSG_BED_HEATING_SAFETY_DISABLED c=20 r=3 3 years ago
  3d-gussner ee8c9227cf Update #MSG_SELFTEST_FANS c=20 3 years ago
  3d-gussner ba73d14511 Update "Fix the issue and then press button on MMU unit." c= r= values 3 years ago
  3d-gussner 0c18ddcb10 Update "Choose a filament for the First Layer Calibration and select it in the on-screen menu." c= r= values 3 years ago
  3d-gussner 89900a5109 Update "G-code sliced for a newer firmware. Please update the firmware. Print cancelled." c= r= values 3 years ago
  3d-gussner 49e8f84916 Update "G-code sliced for a newer firmware. Continue?" c= r= values 3 years ago
  3d-gussner fd7aa82dce Update MSG_BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND c=20 r=6 3 years ago
  3d-gussner 1ed7d8fc4b Update MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR c=20 r=5 3 years ago
  3d-gussner dbec06086c Update MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR c=20 r=5 3 years ago
  3d-gussner 83d73b9ac0 Remove unused messages from Dutch translation 3 years ago
  3d-gussner 37ec18596b Update #MSG_USERWAIT c= value 3 years ago
  3d-gussner dd4c02aa15 Update "Heating disabled by safety timer." c= r= values 3 years ago
  3d-gussner 9a719fd0b9 Update "Calibrating home" c= value 3 years ago
  3d-gussner 693f8d54cf Merge branch 'PFW-1189' of https://github.com/3d-gussner/Prusa-Firmware into PFW-1189 3 years ago
  3d-gussner 6d3f76638e Remove duplicate `PINDA` in translations 3 years ago
  3d-gussner e0f0aecee8 Delete unused translation 3 years ago
  3d-gussner 1ade96bbeb Delete unused `Sensor state` translation 3 years ago
  3d-gussner de51d10694 Delete unused `IR:` translation 3 years ago
  3d-gussner 38e7779f1f Fix `PINDA` translation 3 years ago
  3d-gussner 3bf938f052 Delete unused translations 3 years ago
  3d-gussner 02e5000efa Fix `FINDA` translation 3 years ago
  DRracer 8ac1d5b95e Attempt to workaround the M73 C0|D0 visual issue (#3067) 3 years ago
  3d-gussner 4e6fd2a92d fix merge issue 2 3 years ago
  3d-gussner 0f771b1218 Merge remote-tracking branch 'upstream/MK3' into MK3_MK404 3 years ago
  D.R.racer f31189e5be Version changed (3.10.0-RC1 build 4078) 3 years ago
  espr14 99206884b5 Calibration: detect biased PINDA and retry calibration 3 years ago
  3d-gussner 6355458052 Add def `M120_M121_ENABLED` to `Configuration_adv.h` 3 years ago
  3d-gussner 832e881955 Disable M120 M121 3 years ago
  espr14 eb4cf1a77f Calibration: return XY back after Z search 3 years ago
  awenelo 9b21ebd376 Fix numbering for custum sheet hex values 3 years ago
  awenelo 09b3632d43 Final changes + cleanup 3 years ago
  awenelo 144fc197a5 Fix index number 3 years ago
  awenelo c777199d34 Simplify sheet numbering 3 years ago
  DRracer 6489c6f17f Merge pull request #3036 from wavexx/remove_is_buffer_empty 3 years ago
  DRracer 29abff55ba Merge pull request #3050 from leptun/optiboot_updates 3 years ago
  awenelo 53a3772f09 Add satin sheet 3 years ago
  Alex Voinea f0eedf301d optiboot: check boot_app_magic as well before early exit 3 years ago
  Alex Voinea c951bea627 Remove unused/broken bootapp code 3 years ago
  Alex Voinea c95d2fcbd7 Use avr-libc wdt library 3 years ago
  DRracer 073cbe0339 Merge pull request #3048 from DRracer/bootappflags-bad-addr 3 years ago
  Alex Voinea 8ced0579d6 Move SerialHead declaration outside of loops 3 years ago
  DRracer c1849f5cc2 Merge pull request #2405 from leptun/MK3_NEW_SD_COMPILATION 3 years ago
  Alex Voinea 026145b78b Fix CFM message end pointer 3 years ago
  D.R.racer 3922bf2877 Fix flashing languages with inline wdr instructions 3 years ago
  DRracer a5a83038fe Merge pull request #2223 from 3d-gussner/MK3_Dutch 3 years ago
  3d-gussner 45500d2e29 Run `lang-community.sh` in build.sh 3 years ago
  3d-gussner 7fa93dfa1b Fix back from submenu `Community made` to `Select language` after factory reset 3 years ago
  3d-gussner fbcbba78a3 Merge remote-tracking branch 'upstream/MK3' into MK3_Dutch 3 years ago
  3d-gussner bd50eea758 Update po files 3 years ago
  3d-gussner 9f242b7b06 Add template 3 years ago
  3d-gussner 1582477926 Fix indentation 3 years ago
  3d-gussner 70912e387e Update 3 years ago
  3d-gussner 2cfe65f880 Update lang translation files 3 years ago
  3d-gussner 779c8d0a78 Move `Dutch` language parts 3 years ago
  DRracer e9c3becb2c Merge pull request #2572 from wavexx/fwretract_plan_sync 3 years ago
  3d-gussner 6ebdb004ab Add `lang-community.sh` script 3 years ago
  3d-gussner 6d7d9089f7 Add defines and templates for community languages 3 years ago
  DRracer 112119284d Merge pull request #3046 from 3d-gussner/MK3_Fix_PR3032 3 years ago
  3d-gussner d0d4298249 Update EEPROM documentation for Service prep 3 years ago
  3d-gussner 3e06a17810 Fix LCD output 3 years ago
  Alex Voinea 28f21c8630 Fix bubblesort sorting speed. ShellSort is probably broken 3 years ago
  Alex Voinea 6e0ecf5cd2 Remove redundant click sound in factory reset (MK3 merge issue) 3 years ago
  3d-gussner 274a3e93b0 Merge remote-tracking branch 'upstream/MK3' into MK3_MK404 3 years ago
  DRracer f3c422c40e Merge pull request #3039 from 3d-gussner/PFW-910 3 years ago
  DRracer 8f216ab475 Merge pull request #3006 from wavexx/babystep_fixes 3 years ago
  Alex Voinea 194438c130 Fix formatting 3 years ago
  Alex Voinea 7faffa539a Merge branch 'MK3' into MK3_NEW_SD_COMPILATION 3 years ago
  Alex Voinea f346125948 Use BubbleSort by default. Disable ShellSort until I fix it 3 years ago
  Alex Voinea 225c456ae6 Limit progressbar to LCD_WIDTH 3 years ago
  Alex Voinea c63f92ae52 Invert shellSort logic 3 years ago
  Alex Voinea 204da1cc3f Factory reset and check_file progress bar 3 years ago
  Alex Voinea 8821439878 Cardreader progressbar code 3 years ago
  Alex Voinea afa7c7ab03 Remove forgotten (?) undef at the end of menu.cpp 3 years ago
  Alex Voinea 54b2edbc8c Update debugging code 3 years ago
  Yuri D'Elia e8f6c9fac9 Exit the _lcd_move* menus when homing/leveling 3 years ago
  Yuri D'Elia b4f5633bde Enable "Move axis" and "Disable steppers" only when idle 3 years ago
  Yuri D'Elia 14a1a93bc8 Include probing/MBL in the PRINTER_ACTIVE check 3 years ago
  Yuri D'Elia 28e9c814fc Remove code duplication for babystep insertion 3 years ago
  Yuri D'Elia 019c818c05 Insert babysteps using CRITICAL_SECTION instead of cli/sei 3 years ago
  Yuri D'Elia f5e419530b Inhibit LiveZ from the settings menu during mesh bed leveling 4 years ago
  Yuri D'Elia ff4e53d2d1 Prevent babysteps in more unsafe situations through homing_flag 4 years ago
  Yuri D'Elia c241adec5f Ensure babystep_apply|undo always uses the planner 4 years ago
  DRracer ffae16bf95 Merge pull request #3033 from DRracer/wdr-inline 3 years ago
  3d-gussner bc2b2a4f76 Update workflow 3 years ago
  3d-gussner c976340dee Remove duplicate `PINDA` in translations 3 years ago
  3d-gussner 67526116e2 Delete unused translation 3 years ago
  3d-gussner d2bfa666b1 Delete unused `Sensor state` translation 3 years ago
  3d-gussner d47d0ed34f Delete unused `IR:` translation 3 years ago
  3d-gussner 6f15cc383b Fix `PINDA` translation 3 years ago
  3d-gussner 5efe33bfc6 Delete unused translations 3 years ago
  3d-gussner 1a6de14a06 Fix `FINDA` translation 3 years ago
  3d-gussner 4a241f2a9b Use new `PF-build.sh` parameter `-c 1` 3 years ago
  3d-gussner afa84b15bd Translate new messages 3 years ago
  3d-gussner d9b820690e Merge remote-tracking branch 'upstream/MK3' into MK3_Dutch 3 years ago
  DRracer 7b22895f23 Merge pull request #3032 from DRracer/service-prep2 3 years ago
  DRracer 9ed713ab13 Merge pull request #3015 from 3d-gussner/PFW-1174 3 years ago
  DRracer 0eb8303cae Merge pull request #2575 from 3d-gussner/MK3_Fix_LCD_stats 3 years ago
  3d-gussner 657767f073 Merge branch 'MK3' into MK3_Fix_LCD_stats 3 years ago
  DRracer 526e7f636d Merge pull request #3040 from 3d-gussner/PFW-969 3 years ago
  D.R.racer 03bd9276e2 Remove unrelated translations from master lang_en.txt as well 3 years ago
  D.R.racer fbca8cbe28 Remove unrelated (but still missing) translations 3 years ago
  3d-gussner 076613fd97 Show `Fil. sensor` in Support::Sensor info only if IR Sensor detected 3 years ago
  3d-gussner 08a6a4b25e Enable Filament sensor action during print even if in Support::Sensor info 3 years ago
  D.R.racer 734e497cd0 Move wizard msgs into messages.h/c + add more translation markers 3 years ago
  D.R.racer 95567b8072 Update translations of the Shipping/Service prep welcome msg 3 years ago
  3d-gussner fdff5d84b2 Move Filament sensors to Support -> Sensor Info 3 years ago
  3d-gussner 8d4176a530 Add "Fan check [On|Off]" menu to tune 3 years ago
  3d-gussner 30b60e44d2 `|| isPrintPaused` is already in `PRINTER_ACTIVE` 3 years ago
  3d-gussner fd154e4b69 Again revert Settings 3 years ago
  3d-gussner 5894883324 Fix USB/host FAN Error resume 3 years ago
  3d-gussner 9110ffd4ae Revert `M602` 3 years ago
  3d-gussner cbe207eb59 Fix issue #3037 3 years ago
  Yuri D'Elia 461d3f6749 Remove duplicate function is_buffer_empty() 3 years ago
  D.R.racer 87f416f303 Keep wizard flag==2 even when a user restarts during Z-calibration 3 years ago
  3d-gussner 495dcee066 Show LCD Settings during pause 3 years ago
  3d-gussner 4c8ba11417 Merge remote-tracking branch 'upstream/MK3' into MK3_MK404 3 years ago
  3d-gussner 5df06a64fd Merge branch 'MK3_MK404' of https://github.com/3d-gussner/Prusa-Firmware into MK3_MK404 3 years ago
  3d-gussner a109d20506 Merge remote-tracking branch 'upstream/MK3' into PFW-1174 3 years ago
  3d-gussner c1d8e6660b Indentations 3 years ago
  DRracer adf347fdf1 Merge pull request #3034 from 3d-gussner/MK3_PFW-960 3 years ago
  3d-gussner 291ee8e46d Indentations 3 years ago
  3d-gussner 75a385d614 Indentations 3 years ago
  D.R.racer 4e768057e7 Use standard wdt_reset() from AVR lib 3 years ago
  3d-gussner 2b4cf8d56e Fix FANCHECK build error 3 years ago
  D.R.racer a456c4a52d Make watchdogReset() force_inline 3 years ago
  D.R.racer c3bea4d71c Make a special welcome message for shipping/service prep 3 years ago
  3d-gussner c79bce010d Don't show Settings during pause 3 years ago
  DRracer 7a84ad71dc Merge pull request #3023 from DRracer/service-prep 3 years ago
  D.R.racer dbb0269bd4 Make indentation consistent with surrounding code in factory_reset() 3 years ago
  3d-gussner afc15b42bb Indentations 3 years ago
  3d-gussner 59c2b7e795 Fix Fan error issues. 3 years ago
  3d-gussner 66ea1bdfba Indentations 3 years ago
  3d-gussner d6c6517fcd Back to Status after Resuming 3 years ago
  3d-gussner c2637d9430 Documentation Show Main Menu 3 years ago
  3d-gussner c07bcd172a Fix NO Stop print during MBL 3 years ago
  3d-gussner 583993b7e2 Back to "Status" after gcode `M0/M1` 3 years ago
  3d-gussner 25928232e4 Merge remote-tracking branch 'upstream/MK3' into MK3_PFW-960 3 years ago
  3d-gussner 9071a9f8fd Gcode `M1` must have a string while `M0` it is optional 3 years ago
  3d-gussner fc270a356a Fix indentations 3 years ago
  3d-gussner 1550e707be Fix few issues 3 years ago
  3d-gussner bf33198866 Add copy of MK3 and MK3S `lang.bin` files to MK404 `*_xflash.bin` 3 years ago
  3d-gussner be9f8e5157 Add force recompile 3 years ago
  3d-gussner de337476cd Add MK404 support 3 years ago
  DRracer 86e117679c Merge pull request #3011 from 3d-gussner/MK3_translation_0121 3 years ago
  DRracer 2874e704f5 Merge pull request #3021 from 3d-gussner/PFW-910 3 years ago
  3d-gussner 3ad669889e Fix few issues 3 years ago
  3d-gussner f37aedd5dd Add copy of MK3 and MK3S `lang.bin` files to MK404 `*_xflash.bin` 3 years ago
  3d-gussner fb98cb2ff9 Add force recompile 3 years ago
  3d-gussner b663abf160 Add MK404 support 3 years ago
  3d-gussner c95a8e13d7 Fix indentations to 4 spaces per tab 3 years ago
  3d-gussner f810047a5c Switch between Remaing and Change time every few seconds 3 years ago
  3d-gussner d2e60aee90 Use `CLOCK_INTERVAL_TIME` 3 years ago
  3d-gussner cf982b0d4d Fix timer issue 3 years ago
  3d-gussner 14b4bf5fa5 Add CLOCK_INTERVAL_TIME and ShortTimer IntervalTimer 3 years ago
  3d-gussner b9a3fa2ddd fix time at speed 3 years ago
  3d-gussner b13d4b71d4 Add Change time behind existing message 3 years ago
  3d-gussner 4998cfb70d Fix printing time being shown without `M73` gcode 3 years ago
  3d-gussner d063ffb141 Add parameter `D` to gcode `M73` for silent/stealth mode 3 years ago
  3d-gussner 83e791cbbe Fix temp Build number 3 years ago
  3d-gussner 91c767b0f2 Reduce code size 3 years ago
  3d-gussner ae48e7c3ce indentation 3 years ago
  3d-gussner f4ca6ee59d Fix typo 3 years ago
  3d-gussner cca90da64b Include silent mode on time change 3 years ago
  3d-gussner cb61436093 Add remaining time to change/pause/user interaction to LCD Info screen 3 years ago
  Alex Voinea 1c1ff722c0 Move sort_order to stack during ::presort 3 years ago
  Alex Voinea 8397dae386 Remove comment 3 years ago
  Alex Voinea b4de57c365 Merge branch 'MK3' into MK3_NEW_SD_COMPILATION 3 years ago
  Yuri D'Elia e010ca8ceb Fix conflicting extern/inline declarations 3 years ago
  DRracer 80568cfba4 Merge pull request #2967 from DRracer/thumbnails2 3 years ago
  Alex Voinea a95d497289 Remove commented out code and deprecated comments 3 years ago
  Alex Voinea 8a068d4d36 Remove redundant position=0 in ::presort 3 years ago
  Alex Voinea b72ce00183 Reduce reserved space on stack for LONG_FILENAME_LENGTH by 1 in presort 3 years ago
  D.R.racer 7ad922e87b Report fname instead of name 3 years ago
  D.R.racer 8d39880abf Fix compilation against latest MK3 branch 3 years ago
  DRracer 7aa4595211 Merge branch 'MK3' into thumbnails2 3 years ago
  D.R.racer c1ead75a73 Remove commented debug code 3 years ago
  3d-gussner 8bc46248f6 avoid having the block body twice in the code. Thanks to @DRracer 3 years ago
  3d-gussner e5711ea84f Indentation to 4 spaces for tabs 3 years ago
  Alex Voinea a830d5b6b7 getfilename_next 3 years ago
  Alex Voinea 8d1c5cbb27 Fix position table offset 3 years ago
  Alex Voinea e6ffc99ff5 Fix compiler bug ... again ... 3 years ago
  Alex Voinea d2a7c62b50 Merge branch 'MK3' into MK3_NEW_SD_COMPILATION 3 years ago
  DRracer a47c971c75 Merge pull request #3010 from leptun/MK3_fix_M23 3 years ago
  Alex Voinea 4fcbf95db6 apply RAII principle on the lsDive recursion limiter 3 years ago
  D.R.racer 9ccda4c57f Optimize code size 3 years ago
  DRracer e491b53f48 Merge branch 'MK3' into MK3_Fix_LCD_stats 3 years ago
  DRracer 5c3683b4ad Merge pull request #3022 from 3d-gussner/PFW-1182 3 years ago
  DRracer d5d44d30a7 Merge pull request #2305 from 3d-gussner/MK3-Fix_M120_M121 3 years ago
  D.R.racer 3668cdeb30 Add Service prep. item into Factory reset 3 years ago
  D.R.racer 5f49d65546 Farmers' request - allow file sorting menu item 3 years ago
  3d-gussner 92db282eca Merge remote-tracking branch 'upstream/MK3' into PFW-960 3 years ago
  3d-gussner aecbd7ab49 Fix `c=aa` location in lang files 3 years ago
  3d-gussner 214695105c Fix issue #2958 3 years ago
  3d-gussner fb39e7296b Uniform message `Press the knob` 3 years ago
  Alex Voinea c739aa9003 M23 full path support. 3 years ago
  Alex Voinea f5cde38a7c Remove duplicit debug line 3 years ago
  Voinea Dragos 77a5082b56 Fix presort_flag duplicate declaration 3 years ago
  Voinea Dragos 52f7a71dce More fixes that were extracted from #2405 3 years ago
  Voinea Dragos f343e6432a Fix diveSubfolder string termination 3 years ago
  DRracer 2dfe21358f Merge pull request #2838 from wavexx/m204_travel_acceleration 3 years ago
  DRracer 77eb10000c Merge pull request #2948 from 3d-gussner/PFW-1168 3 years ago
  Alex Voinea 84d043d41b Fix WorkDirDepth limit (for good this time) 3 years ago
  Alex Voinea 90c0f33bc5 Fix farm filename when LFN is missing 3 years ago
  Alex Voinea 080c44cb2e Undo stupid hack for an error because of some compiler issue in the past 3 years ago
  Alex Voinea df163066fb Fix "sorting files" messages for both ShellSort and BubbleSort 3 years ago
  Alex Voinea 279c0aaa21 Fix double '>' bug and brutal refactoring 3 years ago
  Yuri D'Elia 640e8d899b Don't scroll one character past the filename end 3 years ago
  Alex Voinea 8445f76eb9 Restructure for loop to work correctly in reverse 3 years ago
  Alex Voinea 3c5c1e5167 Use enum for menu state code readability 3 years ago
  Alex Voinea ffc3a445ca Remove even more dead code 3 years ago
  Alex Voinea d25b4a6bc9 Remove dead code (SDSORT_GCODE) 3 years ago
  Alex Voinea e52e68d4c1 Merge branch 'MK3' into MK3_NEW_SD_COMPILATION 3 years ago
  Alex Voinea 4c977cc335 Merge branch 'MK3' into MK3_NEW_SD_COMPILATION 3 years ago
  Yuri D'Elia 186ce0f4b3 Handle acceleration settings in UVLO/power panic 3 years ago
  Yuri D'Elia f7542aa064 Report travel acceleration in M503 3 years ago
  Yuri D'Elia 76911f67db Take advantage of the new is_uninitialized function 3 years ago
  Yuri D'Elia 45811f82aa Initialize default travel_acceleration from EEPROM 3 years ago
  Yuri D'Elia 5589954b77 Add DEFAULT_TRAVEL_ACCELERATION in all variants 3 years ago
  Yuri D'Elia 1c76152e62 Implement separate travel acceleration (M204 T) 3 years ago
  3d-gussner 2ba24fe0d4 Add pause/resume to USB/host prints via LCD menu 3 years ago
  DRracer 0bda4504c1 Merge pull request #3013 from leptun/MK3_DIR_DEPTH 3 years ago
  Alex Voinea 896f4e1dd1 Fix compile error 3 years ago
  Alex Voinea f6ae379077 Fix dir_names array definition. Prevents overrun 3 years ago
  Alex Voinea 5c9d202871 Change MAX_DIR_DEPTH from 10 to 6 3 years ago
  DRracer f7fd7a5331 Merge pull request #3012 from leptun/MK3_IP_support 3 years ago
  3d-gussner 57e730c80c Merge remote-tracking branch 'vojtech-pavlik/MK3' into MK3_Test_PRs 3 years ago
  Alex Voinea 647cde0cae Add documentation 3 years ago
  Alex Voinea 2f4119a6d7 M552 - Printer IP address 3 years ago
  DRracer 2b81abb24c Merge pull request #2814 from wavexx/MK3_PAT9125_I2C 3 years ago
  D.R.racer 15d76a7501 Remove duplicit incrementation of consecutive comment lines 3 years ago
  DRracer 40e45c5eaa Merge pull request #3009 from leptun/PFW-1144-LongPathName 3 years ago
  DRracer 58c217a340 Merge pull request #2988 from leptun/PFW-1171-EEPROM_SN 3 years ago
  Alex Voinea 7e09df6a34 Add documentation 3 years ago
  Alex Voinea b6d56bc0f4 Change M27 argument from L to P as that makes more sense (path vs LFN)) 3 years ago
  Yuri D'Elia ec4c1be058 Silence bUpdateEEPROM unused warning in MK3 variant 3 years ago
  Voinea Dragos 30131c9ab5 Patch broken string PROGMEM transition with setTargetedHotend() error 3 years ago
  Voinea Dragos ea44d78d68 Merge branch 'MK3' into PFW-1171-EEPROM_SN 3 years ago
  Voinea Dragos 698499f00d split timer0 and timer2 initialization. Move timer2 init early 3 years ago
  Voinea Dragos 6b6205d2f6 M27 refactoring and M27 L initial implementation 3 years ago
  Yuri D'Elia b8b75186fe Remove the extra copy of CRITICAL_SECTION from fastio 3 years ago
  Yuri D'Elia 31b3ad0613 Merge remote-tracking branch 'upstream/MK3' into MK3_PAT9125_I2C 3 years ago
  Yuri D'Elia 2d71a071f0 Switch twi.c to fastio 3 years ago
  Yuri D'Elia 1fa7b8cd8d Move SDA/SCL pins into pins.h for fastio compatibility 3 years ago
  Yuri D'Elia 30262b0a6e Remove redundant definitions of CRITICAL_SECTION_* 3 years ago
  3d-gussner d75a0fdcbd Merge remote-tracking branch 'upstream/MK3' into MK3_translation_0121 3 years ago
  DRracer 42311db5f1 Merge pull request #2789 from wavexx/MK3_TMC2130_DEDGE 3 years ago
  D.R.racer caf58b16b6 Fix handling EOF 3 years ago
  Yuri D'Elia e9d5c44732 Also toggle pins efficiently in sm4.c 3 years ago
  3d-gussner fba83bd309 Add new flags -c -p -n 3 years ago
  D.R.racer 6c9c1423c6 Remove temporary changes from SdBaseFile.h 3 years ago
  D.R.racer 71d825d0f2 Return SdBaseFile into previous state 3 years ago
  D.R.racer 7279de7403 Separate reading G-code files and writing to a file 3 years ago
  D.R.racer c05b625b1c Fix occasionally skipped valid G-code lines 3 years ago
  D.R.racer d1fd5a555f Clean up gfReset() 3 years ago
  D.R.racer b2cf5b7b6c Fix seekSetFilteredGcode() 3 years ago
  D.R.racer b6c59e08ac Workaround ++gfCacheP into postincrement ld r22, Z+ 3 years ago
  D.R.racer d275fe0e83 Extract gcode filter from SdBaseFile into SdFile + optimization 3 years ago
  D.R.racer c3758d350e Fast skipping of large comment blocks 3 years ago
  D.R.racer dcc6605809 Workaround for skipping large comment blocks 3 years ago
  Yuri D'Elia a9625747db Reinstate the nop instruction as delay in non-DEDGE 3 years ago
  Yuri D'Elia d3734b02cc Also fix delay instances inside unused BACKLASH_[XY] 3 years ago
  Yuri D'Elia b17cdcd4d7 Ensure MINIMUM_PULSE is always 0 in DEDGE mode 3 years ago
  Yuri D'Elia 2a6989ecd5 Remove TMC2130 special-cases 3 years ago
  Yuri D'Elia 4fed728e08 Elide delayMicroseconds for TMC2130 in non-DEDGE mode 3 years ago
  3d-gussner f40c593d11 Fix few translations not being used due to spaces or upper case 3 years ago
  D.R.racer c28e5a9dbc Farmer's request 2 3 years ago
  DRracer bfe93d3959 Merge pull request #2951 from DRracer/pvb-08 3 years ago
  3d-gussner ad5d068690 Update char lengths part 1 3 years ago
  DRracer 891f37a622 Merge pull request #2982 from DRracer/codesize 3 years ago
  3d-gussner 0aae433bda Update po files after all these changes 3 years ago
  3d-gussner c555907a12 Remove duplicate string `G-code sliced for a different printer type. Please re-slice the model again. Print cancelled.` using `MSG_GCODE_DIFF_PRINTER_CANCELLED` 3 years ago
  3d-gussner 51d1e0bd8a Remove duplicate string `G-code sliced for a different printer type. Continue?` using `MSG_GCODE_DIFF_PRINTER_CONTINUE` 3 years ago
  3d-gussner 7916f8b9e8 Merge branch 'codesize' of https://github.com/DRracer/Prusa-Firmware into DRracer_codesize 3 years ago
  D.R.racer 0eb7261e29 Save ~100B on menu implementation 3 years ago
  3d-gussner ba8386573a Remove duplicate string `Resume print` using `MSG_RESUME_PRINT` 3 years ago
  3d-gussner c6a01b3806 Remove duplicate string `Reset` using `MSG_RESET` 3 years ago
  3d-gussner ab5aad636f Remove duplicate string `Mesh Bed Leveling` and `Mesh bed leveling` using `MSG_MESH_BED_LEVELING` 3 years ago
  DRracer f4cee7ce84 Merge pull request #2983 from leptun/MK3_fix_sm4.h_line_ending 3 years ago
  DRracer bce9f8c949 Merge pull request #2984 from leptun/MK3_Serial_LF_only 3 years ago
  DRracer c465417f50 Merge pull request #2987 from leptun/PFW-1144-LongPathName 3 years ago
  3d-gussner 84ed0725f2 Merge branch 'codesize' of https://github.com/DRracer/Prusa-Firmware into DRracer_codesize 3 years ago
  3d-gussner bce8501eea Remove duplicate string `Extruder` using `MSG_Extruder` 3 years ago
  Voinea Dragos c6588193ad Fix some more stuff in cardreader.cpp 3 years ago
  3d-gussner ab18a3ccc3 Merge branch 'codesize' of https://github.com/DRracer/Prusa-Firmware into DRracer_codesize 3 years ago
  3d-gussner 530b9f50b8 Remove duplicate string `Eject filament` using `MSG_EJECT_FILAMENT` 3 years ago
  D.R.racer 348902240e Fix code_seen_P("fv") broken by one of the merges 3 years ago
  3d-gussner 269fabc330 Remove duplicate string `Cut filament` using `MSG_CUT_FILAMENT` 3 years ago
  3d-gussner 797d8e74a3 Remove duplicate string `Checking X axis` and `Checking Y axis` using `MSG_CHECKING_X` and `MSG_CHECKING_Y` 3 years ago
  3d-gussner 2d1e1e4cee Remove duplicate string `Fans check` using `MSG_FANS_CHECK` 3 years ago
  3d-gussner d117a299f7 Remove duplicate string `Fil. runouts ` using `MSG_FIL_RUNOUTS` 3 years ago
  3d-gussner 10bbd64b41 Remove duplicate string `Crash` using `MSG_CRASH` 3 years ago
  3d-gussner 4878db7365 Remove duplicate string `Last print failures` using `MSG_LAST_PRINT_FAILURES` 3 years ago
  3d-gussner 99867c8fbd Remove duplicate string `Total failures` using `MSG_TOTAL_FAILURES` 3 years ago
  3d-gussner 8f0a45e8a4 Remove duplicate string `Steel sheets` and `Total` using `MSG_STEEL_SHEETS` and `MSG_TOTAL` 3 years ago
  3d-gussner 7222cf05cf Remove duplicate string `MMU Fails` and `MMU Load Fails` using `MSG_MMU_FAILS` and `MSG_MMU_LOAD_FAILS` 3 years ago
  3d-gussner fbb3fad64b Remove duplicate string `Last print` using `MSG_LAST_PRINT` 3 years ago
  3d-gussner 7891d12a32 Remove duplicate string `Is filament loaded?` using `MSG_FILAMENT_LOADED` 3 years ago
  3d-gussner 223f32deae Remove duplicate string `HW Setup" using `MSG_HW_SETUP` 3 years ago
  3d-gussner 5c1da227fe Merge branch 'codesize' of https://github.com/DRracer/Prusa-Firmware into DRracer_codesize 3 years ago
  3d-gussner 377a59c624 Remove duplicate string `Belt status` 3 years ago
  D.R.racer d8917a304a Reuse lcd_space() instead of in-place printing of spaces in a cycle 3 years ago
  D.R.racer 783e4bbaf9 Add lcd_putc_at (code down >150B), refactor lcd_selftest_screen_step 3 years ago
  DRracer 3975415490 Merge branch 'MK3' into codesize 3 years ago
  DRracer 58351fb27a Merge pull request #2977 from DRracer/farm-patch1 3 years ago
  D.R.racer 514321f2ce Remove farm_no completely + reuse prusa_stat_farm_number() where 3 years ago
  Voinea Dragos 2b3729125d Prevent wdt reset during file listing 3 years ago
  D.R.racer c1ff6242b0 Avoid copying FW version number into RAM - use the PROGMEM string 3 years ago
  Voinea Dragos 4f769f0faa PRUSA RESET safety precaution 3 years ago
  Voinea Dragos 9fa9d6b063 Move farm strings to PROGMEM 3 years ago
  D.R.racer 6a62674bd8 Keep PRUSA RESET available even in non-farm mode 3 years ago
  D.R.racer 98a4da571d Merge branch 'farm-patch1' of github.com:DRracer/Prusa-Firmware into farm-patch1 3 years ago
  D.R.racer 9bfada94f0 Remove commented code 3 years ago
  Voinea Dragos e8e4aabfdb Add gcode documentation 3 years ago
  Voinea Dragos 31a6270f69 Abuse \n in lcd printing instead of constantly calling other functions 3 years ago
  Voinea Dragos 4d3f056b81 Fix factory reset menu rendering 3 years ago
  Voinea Dragos 37c431abd9 another lcd_puts_at_P 3 years ago
  D.R.racer 3ccd1b2b2b Refactor lcd_set_cursor + lcd_puts_P -> lcd_puts_at_P 3 years ago
  Voinea Dragos bc7d36b6ff Fix sm4.h line ending 3 years ago
  Voinea Dragos da21916473 Change printf_P without format arguments and NL to puts_P only 3 years ago
  D.R.racer 9b34789398 Cleanup reset_menu() 3 years ago
  D.R.racer 90d22a376d Remove unused vars in xyzcal.cpp 3 years ago
  D.R.racer 2780a03967 Improve duplicate_Tcode_ignored 3 years ago
  D.R.racer 3dc85b319c Marlin_main.cpp hacks 3 years ago
  D.R.racer 4dec171266 Reduce code by 104 bytes in xyzcal.cpp 3 years ago
  D.R.racer c710253cca Code cleanup and size reduction and RAM optimization 3 years ago
  Voinea Dragos 9fda6b774d Do not send CR on the serial line 3 years ago
  DRracer f809691003 Merge pull request #2968 from espr14/PFW-1186 3 years ago
  DRracer eceb7b4e5f Merge pull request #2981 from 3d-gussner/PFW-1140-2 3 years ago
  DRracer defbaa6ff8 Merge branch 'MK3' into PFW-1186 3 years ago
  DRracer 4c952a0a9c Merge pull request #2962 from espr14/PFW-1178 3 years ago
  3d-gussner 008c6a2590 Remove defines 3 years ago
  DRracer ab59449b7c Merge pull request #2961 from espr14/PFW-1179 3 years ago
  DRracer 85f2c29dfe Merge pull request #2960 from espr14/PFW-1169-3 3 years ago
  3d-gussner ede3f34051 Documentation 3 years ago
  Voinea Dragos ced3d9fa77 "M20 L" support. Print long filenames 3 years ago
  D.R.racer 31951fe8c9 Code refactoring 3 years ago
  Voinea Dragos 70d254a1fc Allow resetting when not in farm mode 3 years ago
  Voinea Dragos 48b4bdfe8e Make the PRUSA RESET command functional on the miniRambo 3 years ago
  Voinea Dragos 542677c080 Prevent switching to serial port 1 on miniRambo and such 3 years ago
  Voinea Dragos 9b847715ac Rename F0 to FRM on the status screen 3 years ago
  Voinea Dragos 3f5cfc4e61 Move the change filament button to the main menu when farm printing 3 years ago
  D.R.racer a9d8ddc89c Farmers' requests 1 3 years ago
  3d-gussner 9264454d3a Gcode `M123` only if FANCHECK and TACHOs are defined 3 years ago
  espr14 735895c6bc Use fabs 3 years ago
  espr14 ca4e638ea2 Don't use extrusion in XYZ calibration 3 years ago
  Alex Voinea 20c3f4cb77 Update comments 3 years ago
  Voinea Dragos dea3f23a69 PRUSA SN in eeprom 3 years ago
  espr14 9867ecd3f9 Reduce unnecessary code 3 years ago
  espr14 21d6f970ef Fix build 3 years ago
  espr14 f3faf651c5 Move down before scanning 3 years ago
  espr14 9dceb488b6 Revert double scanning 3 years ago
  DRracer b71f1be37a Merge pull request #2957 from wavexx/remove_stray_newline 3 years ago
  espr14 546812294e Clean serial output 3 years ago
  espr14 d7507649d8 Remove degree char 3 years ago
  espr14 180af46fe4 Try double height 3 years ago
  espr14 333526f65d Single skew output 3 years ago
  espr14 e6e44fe188 Convert to degrees 3 years ago
  espr14 b8443b00ad Reverse reverse 3 years ago
  espr14 09892bec52 Reverse 3 years ago
  Yuri D'Elia 78f8f1e8f9 Remove stray serial newlines in fsensor autoload messages 3 years ago
  espr14 211e5f5f37 Define DBG output 3 years ago
  espr14 904a23b69e Report calibration results 3 years ago
  espr14 2e40a27460 Alignment, comments 3 years ago
  DRracer 4728bf2182 Merge pull request #2875 from wavexx/raise_while_preheating 3 years ago
  DRracer 1a6b6fb491 Merge pull request #1776 from wavexx/unused_tmc_defines 3 years ago
  DRracer ef51652e39 Merge pull request #1758 from wavexx/disable_filament_checks_in_extr_menu 3 years ago
  3d-gussner a064ce4722 Add gcode `M123` Tachometer value 3 years ago
  D.R.racer 8a083c9970 Add PVB preheat and 0.8mm nozzle presets 3 years ago
  DRracer 5c3513a6cc Merge pull request #2397 from odaki/flashair_display_ip 3 years ago
  DRracer f1f4db0a35 Merge pull request #2850 from 3d-gussner/MK3_PF-build_Fix_EN_ONLY 3 years ago
  3d-gussner 67ff9b6b48 Fix typos 3 years ago
  3d-gussner 007e59d23c Comment out 'sudo' auto installation 3 years ago
  espr14 584177e8fd Fix E mask 3 years ago
  DRracer f549ce690a Merge pull request #2949 from prusa3d/MK3_3.9.3 3 years ago
  3d-gussner 3329d34ce4 Fix doxygen issue with "^" and "|" only in one line 3 years ago
  Mesa Komarevich 2764b94558 Updated the dos2unix instructions to no longer be specific to Debian, as unix based distro's being able to handle windows line endings should be considered an exception, and not the rule. Also fixed a few spelling mistakes in the README. 3 years ago
  Mesa Komarevich f7dbc70b9c Added some specific instructions for building firmware with a Debian WSL install. Updated commands to use sudo to avoid permission errors. 3 years ago
  DRracer 0ed6b537a8 Merge branch 'MK3' into MK3_3.9.3 3 years ago
  D.R.racer 603d704178 Version changed (3.9.3 build 3556) 3 years ago
  3d-gussner 8a27b6abdb Move Z up before xy home running xyz calibration 3 years ago
  espr14 2981b9e9c5 OctoPrint needs new lines 3 years ago
  espr14 cc9e7b9376 Fix comments, remove unused 3 years ago
  espr14 49d57ed621 Median dynamic circle 3 years ago
  espr14 a9fd09ca3f Implement median 3 years ago
  espr14 13fefaa1bf Fix typo 3 years ago
  espr14 03b87ae68b Add 8 pixel circle pattern 3 years ago
  espr14 c14e725574 Fix moves 3 years ago
  espr14 c2ab8785f4 Revert Manhattan 3 years ago
  espr14 c1e6567461 Fix step axis coding 3 years ago
  espr14 922769cefb axis -> axes 3 years ago
  espr14 a01dfe26fe Add Manhattan movement 3 years ago
  espr14 6461206e08 Fix direction setting 3 years ago
  espr14 b6e78bf900 Fix direction 3 years ago
  espr14 c11e8654da Revert both directions, refactor code 3 years ago
  espr14 e86ba5e26d Scan only once 3 years ago
  D.R.racer 7e02a7f118 Version changed (3.9.3-RC1 build 3555) 3 years ago
  DRracer d61cdf2b88 Merge pull request #2932 from espr14/PFW-1165-2 3 years ago
  espr14 80e32ce1a5 Turn off speed optimization due to MK2.5 3 years ago
  DRracer ad68405321 Merge pull request #2931 from 3d-gussner/MK3_3.9.3_SuperPINDA 3 years ago
  3d-gussner 12f4102272 Group PINDA defines 3 years ago
  3d-gussner 76eb743139 Set default SuperPINDA toggle on MK2.5/S to NO 3 years ago
  3d-gussner 4b510fef0a Add PINDA_TEMP_COMP variable to enable SuperPINDA toggle menu/function 3 years ago
  3d-gussner a97587f373 Changed PINDA_MINTEMP having own value 3 years ago
  3d-gussner 732b6e0cad Cleanup 3 years ago
  3d-gussner 62c36f718b Add SuperPINDA support for MK2.5/S 3 years ago
  DRracer a8ed6cefcf Merge pull request #2930 from espr14/PFW-1165 3 years ago
  espr14 e6eb3be247 Set jerk = 1, accel. = 1000 3 years ago
  espr14 979525f028 Add more restarts 3 years ago
  espr14 25d138d198 Revert max.speed 3 years ago
  espr14 e24a763e46 Cleanup 3 years ago
  espr14 8df2eccf45 Remove defines and unused vars 3 years ago
  espr14 25d6e24778 Add comments 3 years ago
  espr14 90c8045f7d Clean the code 3 years ago
  espr14 20659ec818 Add debug output 3 years ago
  espr14 c397e9249a Restart scanning 3 years ago
  espr14 1610c96fc4 Fix and report circle divergence 3 years ago
  espr14 744763f0a9 Fix diagonal movement 3 years ago
  espr14 de23a845b4 Remove unused 3 years ago
  espr14 3c9168f8f6 Optimize diagonal 3 years ago
  espr14 9a59369c77 Diagonal acceleration 3 years ago
  espr14 27ff6b0057 Fix build 3 years ago
  espr14 9b55ff9de1 Acceleration up and down 3 years ago
  espr14 f39a0999e1 Prepare full acceleration control 3 years ago
  espr14 ccf26e4e7a Basic acceleration 3 years ago
  DRracer 9141aee916 Merge pull request #2923 from espr14/PFW-1162-4 3 years ago
  espr14 a8f4207df9 New XYZ calibration algorithm 3 years ago
  DRracer d167b3bd78 Merge pull request #2870 from leptun/MK3_random_patches 3 years ago
  Alex Voinea 2ac106d9fe Merge branch 'MK3' into MK3_random_patches 3 years ago
  odaki 61c48df0bd Merge remote-tracking branch 'upstream/MK3' into flashair_display_ip 3 years ago
  DRracer 93c3ea75ee Merge pull request #2893 from leptun/MK3_fix_XYZ_cal_warning 3 years ago
  Voinea Dragos f96f75bd17 Fix warning and a bit of indentation 3 years ago
  Voinea Dragos f3953d7c83 Merge branch 'MK3' into MK3_random_patches 3 years ago
  odaki c0070506fb Merge branch 'MK3' into flashair_display_ip 3 years ago
  DRracer d013abfd52 Merge pull request #2890 from prusa3d/MK3_3.9.2 3 years ago
  DRracer 04bb4b5dad Merge pull request #2573 from leptun/MK3_filecheck_progress_bar 3 years ago
  Alex Voinea 807eddafb0 Remove debugging code 3 years ago
  Alex Voinea 43ace00a24 Fill the progress bar at the end and code optimizations 3 years ago
  Alex Voinea 5ad125cbed Merge branch 'MK3' into MK3_filecheck_progress_bar 3 years ago
  D.R.racer 396ee0fde2 Version changed (3.9.2 build 3524) 3 years ago
  DRracer e86be5a30a Merge pull request #2888 from DRracer/391-sp 3 years ago
  Voinea Dragos 176e2674b9 Fix M105 ok hack 3 years ago
  Voinea Dragos 023ccb0e89 Fix double ok in M603 3 years ago
  Alex Voinea 0c305ee5f5 Fix warning 3 years ago
  Alex Voinea 4abf1f436a Gracefully dump the queue + fixes to fancheck 3 years ago
  Alex Voinea fdbbc7d62a Terminate last line from the SD card even if it doesn't have a \n 3 years ago
  Alex Voinea ff56ece6f8 Remove redundant get_command prototype 3 years ago
  Alex Voinea 5f0e4a1cac Temporary M602 patch. Needs more work 3 years ago
  Alex Voinea 6873a9d28e Only send capabilities when M115 is run without arguments 3 years ago
  Alex Voinea 0ee8e1f424 Fix missing ok in M601 3 years ago
  Alex Voinea e5ebf7c67f Fix missing keep-alive messages 3 years ago
  Alex Voinea 26f62f042e Use the longest filename instead of just using the long filename in M27 3 years ago
  D.R.racer 4747c8c9f5 Increase top Z-offset limit based on some test results 3 years ago
  D.R.racer 5184910ef9 Try harder finding the calibration center 3 years ago
  DRracer ed0494af18 Merge pull request #2885 from DRracer/391-sp 3 years ago
  D.R.racer cd4e16ef9f XYZ calibration tune 3 years ago
  3d-gussner 7651fbb0d1 Fix output "Configuration_prusa.h" delay if compiling failed. 3 years ago
  3d-gussner a5ba666af0 Changed from arguments to flags/options 3 years ago
  3d-gussner 16165ffcad Merge branch 'MK3' into MK3_PF-build_Fix_EN_ONLY 3 years ago
  Yuri D'Elia e28159122f Make MIN_Z_FOR_LOAD/UNLOAD the same 3 years ago
  Yuri D'Elia 5f23474c95 Raise Z while preheating when auto/[un]loading the filament 3 years ago
  DRracer 2103d2f588 Merge pull request #2852 from MartinPoupa/LCD_status_changed 3 years ago
  DRracer c05d4c9112 Update messages.h 3 years ago
  MartinPoupa a84d7ef8d6 corectino of declaration 3 years ago
  MartinPoupa c8c7563e8a LCD status changed 3 years ago
  3d-gussner e3b3e66665 Clean PF-Firmware build when changing git branch 3 years ago
  3d-gussner 93fd3c95a9 Disable pause and warnings using command line with all needed arguments 3 years ago
  3d-gussner ffc7a5344a Typo fix 3 years ago
  3d-gussner 2dbce5c0cb Add UNKNOWN as agrument option 3 years ago
  3d-gussner a6a4a0b71d Bug fix if using argument EN_ONLY. 3 years ago
  Yuri D'Elia df824414ef Fix probing in IR_SENSOR 3 years ago
  Yuri D'Elia c2e8d229a7 Be more compliant in the I2C protocol 3 years ago
  odaki ebc987bd23 Merge branch 'MK3' into flashair_display_ip 3 years ago
  Yuri D'Elia 384f40956c Remove obsolete cbi/sbi 3 years ago
  Yuri D'Elia 6d476d7144 Still use SWI2C on RAMBo10a boards 3 years ago
  DRracer 134f841380 Merge pull request #2841 from leptun/MK3_SD_REMOVED_MESSAGE 3 years ago
  DRracer 95e2e2f69c Merge pull request #2832 from leptun/MK3_AUTO_REPORT_TEMPERATURES 3 years ago
  DRracer ef0840f587 Merge pull request #2786 from leptun/MK3_M115_Capabilities_report 3 years ago
  Alex Voinea d9fa44c142 Document M155 command 3 years ago
  DRracer e24466f22e Merge pull request #2837 from prusa3d/MK3_3.9.1 3 years ago
  odaki 4c7100985e Merge branch 'MK3_3.9.1' into flashair_display_ip 3 years ago
  D.R.racer 7278458316 Version changed (3.9.1 build 3518) 3 years ago
  3d-gussner bd7bb5acb3 Update new messages and their translations (#2835) 3 years ago
  DRracer 5e322092b3 Merge pull request #2833 from leptun/MK3_3.9.1 3 years ago
  Voinea Dragos 2f5083daa7 quickfix to mistake made in the experimental menu visibility code 3 years ago
  DRracer 9fb6efc435 Add newline at the end of file 3 years ago
  Voinea Dragos c0fced2f3c Fix typo 3 years ago
  Voinea Dragos e2ef5af40e Add capability line 3 years ago
  Voinea Dragos a1dfbffedb Merge branch 'MK3_M115_Capabilities_report' into MK3_AUTO_REPORT_TEMPERATURES 3 years ago
  Voinea Dragos 073eadff7e Merge branch 'MK3' into MK3_AUTO_REPORT_TEMPERATURES 3 years ago
  DRracer 3093c8c7f6 Merge pull request #2829 from wavexx/la10_15_readjust 3 years ago
  Alex Voinea 6bc59197ad Merge branch 'MK3' into MK3_NEW_SD_COMPILATION 3 years ago
  Alex Voinea c158259970 Remove unused where C++ alternative can be used 3 years ago
  Alex Voinea 44b1b1c219 More macros 3 years ago
  Alex Voinea c3abd4ffe6 Remove io_atmega2560.h and some more macros 3 years ago
  Alex Voinea eb007c35d2 Macros initial 3 years ago
  Alex Voinea 160af0a624 Printer capabilities 3 years ago
  3d-gussner fef5b02010 Tag as community contribution 3 years ago
  3d-gussner e4a444ce5a Updated few translations 3 years ago
  3d-gussner 4d3aa1b59a Merge remote-tracking branch 'upstream/MK3' into MK3_Dutch 3 years ago
  DRracer f145769e98 Merge pull request #2807 from leptun/MK3_Polling 3 years ago
  DRracer 763d2d9849 Merge pull request #2806 from leptun/MK3_PRUSA_SN 3 years ago
  DRracer a0420c7928 Merge pull request #2619 from 3d-gussner/MK3_G21 3 years ago
  DRracer b82ee3fc06 Merge pull request #2811 from leptun/MK3_fastio_timer_patch 3 years ago
  DRracer a2db9e8f6b Merge pull request #2585 from leptun/MK3_FEEDRATE 3 years ago
  DRracer f08f71a45b Merge pull request #2828 from leptun/MK3-fix_back_view 3 years ago
  Yuri D'Elia 26e900896a Re-adjust LA10->15 conversion 3 years ago
  Alex Voinea 7ac16d5f04 Menu view return patch 3 years ago
  D.R.racer 02891f6bb8 Version changed (3.9.1-RC1 build 3512) 3 years ago
  Alex Voinea 3e7bba54e0 SD card released message 3 years ago
  Yuri D'Elia e37cdab38f PAT9125_I2C: accept either NACK or ACK in receive 3 years ago
  Yuri D'Elia d8a8837938 Document the 3 possible modes 3 years ago
  Alex Voinea 1659f61dd5 Fix fastio extra parenthesis 3 years ago
  Yuri D'Elia 30e7b777e0 Error-out with PAT9125_SWSPI (not fully implemented) 3 years ago
  Yuri D'Elia 7f425120f0 Strip down the TWI code 3 years ago
  DRracer 772844678f Merge pull request #2809 from leptun/PFW-1134-ALTFAN_KILLSWITCH 3 years ago
  Yuri D'Elia 240dc1132e Include initial implementation based on Arduino's twi 3 years ago
  Yuri D'Elia 502bc8c72d Isolate more pat9125 code 3 years ago
  Alex Voinea a41164b3df Merge pull request #5 from 3d-gussner/patch-4 3 years ago
  3d-gussner 90c36a5887 Update ALTFAN eeprom documentation 3 years ago
  Alex Voinea 2c2926882a Don't switch unnecessarily. Also "\n" the ";S" request 3 years ago
  Alex Voinea 8d9dc73d1b Fix compile error 3 years ago
  Alex Voinea 5530b99882 Reboot after factory reset 3 years ago
  Alex Voinea 6b853a4cc3 Auto-detect ALTFAN after fw. update 3 years ago
  Alex Voinea 96435ad084 Move experimental menu to HW setup 3 years ago
  Alex Voinea d8fbd46cd2 M155 3 years ago
  Alex Voinea e2856ba4f5 Make the serial number available to the user 3 years ago
  Yuri D'Elia 78bbfc6237 Fix delay calculations inside babystep() 4 years ago
  Yuri D'Elia 6ea198a866 Fix DEDGE in sm4.c (fixes xyz calibration) 4 years ago
  Yuri D'Elia e6b182aa9f Implement proper step/delay pauses in tmc2130 functions 4 years ago
  Yuri D'Elia 1181beffb1 Enable DEDGE stepping on supported variants 4 years ago
  Yuri D'Elia 6ceca9bf85 Implement double-edge stepping 4 years ago
  Yuri D'Elia 6017600714 Reintroduce the ability to disable TMC interpolation per-axis 4 years ago
  Yuri D'Elia 1c026f0e4d Always sync before manipulating the planner position in FWRETRACT 4 years ago
  Yuri D'Elia d53c55ce64 Disable filament checks inside the Move -> Extruder menu 5 years ago
  Alex Voinea a0cf5714ce M220 M221 3 years ago
  odaki 3d979a88a5 Merge remote-tracking branch 'upstream/MK3' into flashair_display_ip 3 years ago
  Alex Voinea b1a83c8add Merge branch 'MK3' into MK3_NEW_SD_COMPILATION 3 years ago
  odaki dfd60a843d Merge remote-tracking branch 'upstream/MK3' into flashair_display_ip 3 years ago
  odaki a1254b3a3c Merge branch 'MK3_3.9.0' into flashair_display_ip 4 years ago
  odaki c34c622b3c Merge branch 'MK3_3.9.0' into flashair_display_ip 4 years ago
  Alex Voinea 0e25eaee8f Merge branch 'MK3' into MK3_NEW_SD_COMPILATION 4 years ago
  3d-gussner 1bac0c1765 Add dummy G21 to prevent UNKOWN warnings in serial 4 years ago
  Alex Voinea 4c4b4c489c :bug::recycle: Update the feedrate percentage before drawing the screen 4 years ago
  3d-gussner 0daa916c8d Merge remote-tracking branch 'upstream/MK3' into MK3_Fix_LCD_stats 4 years ago
  3d-gussner dbd07c1d1c Limited LCD output of several uint16 values to 999 4 years ago
  Alex Voinea f8843b25b0 :sparkles: Progress bar for check_file() 4 years ago
  Alex Voinea 4670c42aeb Merge branch 'MK3' into MK3_NEW_SD_COMPILATION 4 years ago
  odaki 684b47e417 Update reference URL 4 years ago
  Alex Voinea e914f8e0da Show "Sorting folders" when folders are being sorted 4 years ago
  Alex Voinea 355b003b7f menu_data_reset() 4 years ago
  Alex Voinea 40ebd455f2 Safer menu interrupt call 4 years ago
  Alex Voinea 22432b1053 Reset menu state after lcd_sd_refresh 4 years ago
  Alex Voinea 5edc1ef297 Fix updir and dir again 4 years ago
  Alex Voinea 57c149e7b5 Comment cleanup 4 years ago
  Alex Voinea 7b19715c99 Cache sdSort 4 years ago
  Alex Voinea f35e553373 Code cleanup 4 years ago
  Alex Voinea 8e47cb35a3 Possible fix to longpress in sdcard menu 4 years ago
  Alex Voinea d1968f6ff0 Option to refresh/resort SDcard files when flashAir type is selected 4 years ago
  Alex Voinea f69a68b966 Fix subdir enter and exit 4 years ago
  Alex Voinea 53e130fc6d Merge branch 'MK3' into MK3_NEW_SD_COMPILATION 4 years ago
  3d-gussner 6d721c9b82 Merge remote-tracking branch 'upstream/MK3' into MK3-Fix_M120_M121 4 years ago
  odaki a2692665f8 Merge remote-tracking branch 'upstream/MK3' into flashair_display_ip 4 years ago
  odaki b09761ed71 Remove '/*' from M46 source 4 years ago
  odaki 24b748c4ac Merge remote-tracking branch 'upstream/MK3' into flashair_display_ip 4 years ago
  odaki 99545a006c M46 "Show the assigned IP address" activated 4 years ago
  odaki 2ca1bc0acb Support W-04 firmware 4.0.0.01+ 4 years ago
  Alex Voinea 29d0537004 Adjusted progress bar 4 years ago
  Alex Voinea e295c83d09 Folder sorting shellSort 4 years ago
  3d-gussner 6a7ae6231d Merge remote-tracking branch 'upstream/MK3' into MK3_Dutch 4 years ago
  3d-gussner d895890608 Merge remote-tracking branch 'upstream/MK3' into MK3-Fix_M120_M121 4 years ago
  Alex Voinea 7028583e9b Merge branch 'MK3_ShellSort' into MK3_NEW_SD_COMPILATION 4 years ago
  Alex Voinea 37dc5fed45 Merge branch 'MK3_revised_filename_scrolling' into MK3_NEW_SD_COMPILATION 4 years ago
  Alex Voinea 1ba89b9689 Merge branch 'MK3' into MK3_ShellSort 4 years ago
  odaki f923427dc1 Show the FlashAir IP address 4 years ago
  Alex Voinea f0f7db57ea Fist attempt at "saved position SD menu" 4 years ago
  Alex Voinea 3ad611ca16 Merge branch 'MK3' into MK3_revised_filename_scrolling 4 years ago
  Yuri D'Elia 19589f02f1 Remove an unused define 5 years ago
  Alex Voinea 63655a906a Merge branch 'MK3' into MK3_revised_filename_scrolling 4 years ago
  Alex Voinea 103e29d38a Merge branch 'MK3' into MK3_ShellSort 4 years ago
  Alex Voinea 7753750fa7 Change for to while 4 years ago
  Alex Voinea 11ba616d73 Define for dumping sorting process 4 years ago
  Alex Voinea 66e51aa298 Folder sorting finished 4 years ago
  Alex Voinea f5bdbcc0bd Cleanup file compare statement 4 years ago
  Alex Voinea a7f2e640e0 Put folders at the beginning 4 years ago
  Alex Voinea 960f4a88ed Status bar, lcd optimization, compile warning fix 4 years ago
  Alex Voinea f87b5a2be7 SORTING_DUMP 4 years ago
  Alex Voinea b803b2a2dd Another first implementation 4 years ago
  Alex Voinea d40656e3ca Small changes to rendering 4 years ago
  Alex Voinea 273d834b19 Fix card removal 4 years ago
  Alex Voinea 2fd192a95d Set scroll delay to 300ms 4 years ago
  Alex Voinea fdab70fa3a Dir fixes 4 years ago
  Alex Voinea 8a806bceea SDDIR 4 years ago
  Alex Voinea 6538262e75 First build that actually works 4 years ago
  Alex Voinea 8f901d2613 Temporary fix 4 years ago
  Alex Voinea b3a587f5a4 SD first attempt. Broken 4 years ago
  3d-gussner 21dcfb23f4 Follow RepRap Wiki G-codes documentation 4 years ago
  3d-gussner c772fd2cae Merge branch 'MK3' into MK3_Dutch 4 years ago
  3d-gussner 3ed694f1e1 Updated `lang_en_nl.txt` with newest translations 4 years ago
  3d-gussner a4211da8f0 Merge branch 'MK3' into MK3_Dutch 4 years ago
  3d-gussner f25f697fd0 Some more missing translation 4 years ago
  3d-gussner 2eb2385a2e Updated po files 4 years ago
  3d-gussner 527f0359f0 Update Dutch translation to v3.8.1-RC1 4 years ago
  3d-gussner b52f42b397 Merge branch 'MK3' into MK3_Dutch 4 years ago
  3d-gussner e765fe5b60 Updated Dutch translation to latest merge 4 years ago
  3d-gussner a4e9f08e16 Merge branch 'MK3' into MK3_Dutch 4 years ago
  3d-gussner 2d82374c2b Merge branch 'MK3' into MK3_Dutch 4 years ago
  3d-gussner 18e1a2266a Update `.po` files after modifying `lang_en*.txt` files 4 years ago
  3d-gussner 09360b26f4 Update `lang_en_nl.txt` to latest PRs made in MK3 branch 4 years ago
  3d-gussner 38f8d419d7 Fix translation file and odd chars in `lang-import.sh` 4 years ago
  3d-gussner 049cca297b Merge branch 'MK3' into MK3_Dutch 4 years ago
  3d-gussner 00fb7e5c5c Update Dutch po files 4 years ago
  3d-gussner 92d61c6de3 Translated to Dutch 4 years ago
  3d-gussner 036d8b1d09 Create new language files #2 4 years ago
  3d-gussner e60aceae93 Create new language files 4 years ago
  3d-gussner 749e356358 more typo 4 years ago
  3d-gussner ee6f074775 Fix typo 4 years ago
  3d-gussner c76da6ccff Another syntax fix 4 years ago
  3d-gussner 23f786ad7e Looks nicer 4 years ago
  3d-gussner 7a5aa352aa Fix syntax 4 years ago
  3d-gussner 2c174964da Update documentation 4 years ago
  3d-gussner 9a7a909c6f Prepare adding new language 4 years ago
  Vojtech Pavlik a4bc91ed2f M0/M1/M117 fix: Use CUSTOM_MSG states in M0/1/M117 4 years ago
  Vojtech Pavlik 5494f23942 M0/M1/M117 fix: Add new CUSTOM_MSG states. 4 years ago
  Vojtech Pavlik 5d8eb84965 M0/M1/M117 fix: Move M0/M1 to the top of decoder. 4 years ago
100 changed files with 6230 additions and 5146 deletions
  1. 5 5
      .github/ISSUE_TEMPLATE/bug_report.md
  2. 3 0
      .gitignore
  3. 17 5
      Firmware/Configuration.h
  4. 23 11
      Firmware/ConfigurationStore.cpp
  5. 1 0
      Firmware/ConfigurationStore.h
  6. 32 15
      Firmware/Configuration_adv.h
  7. 225 156
      Firmware/Dcodes.cpp
  8. 19 0
      Firmware/Dcodes.h
  9. 19 29
      Firmware/Marlin.h
  10. 2 2
      Firmware/MarlinSerial.cpp
  11. 1710 1210
      Firmware/Marlin_main.cpp
  12. 4 1
      Firmware/Sd2Card.cpp
  13. 1 5
      Firmware/Sd2PinMap.h
  14. 3 3
      Firmware/SdBaseFile.cpp
  15. 3 1
      Firmware/SdBaseFile.h
  16. 2 10
      Firmware/SdFatUtil.cpp
  17. 1 2
      Firmware/SdFatUtil.h
  18. 188 0
      Firmware/SdFile.cpp
  19. 22 2
      Firmware/SdFile.h
  20. 3 2
      Firmware/SdVolume.h
  21. 1 1
      Firmware/Servo.cpp
  22. 2 2
      Firmware/Timer.h
  23. 1 1
      Firmware/adc.c
  24. 24 0
      Firmware/asm.h
  25. 4 4
      Firmware/backlight.cpp
  26. 4 13
      Firmware/bootapp.c
  27. 2 2
      Firmware/bootapp.h
  28. 441 381
      Firmware/cardreader.cpp
  29. 44 61
      Firmware/cardreader.h
  30. 34 32
      Firmware/cmdqueue.cpp
  31. 5 5
      Firmware/cmdqueue.h
  32. 35 10
      Firmware/config.h
  33. 16 34
      Firmware/eeprom.cpp
  34. 43 64
      Firmware/eeprom.h
  35. 11 18
      Firmware/fastio.h
  36. 1 0
      Firmware/first_lay_cal.cpp
  37. 17 17
      Firmware/fsensor.cpp
  38. 0 2
      Firmware/heatbed_pwm.cpp
  39. 0 374
      Firmware/io_atmega2560.h
  40. 1 1
      Firmware/la10compat.cpp
  41. 38 27
      Firmware/language.c
  42. 10 5
      Firmware/language.h
  43. 12 19
      Firmware/lcd.cpp
  44. 5 2
      Firmware/lcd.h
  45. 90 0
      Firmware/macros.h
  46. 49 15
      Firmware/menu.cpp
  47. 6 3
      Firmware/menu.h
  48. 65 60
      Firmware/mesh_bed_calibration.cpp
  49. 9 8
      Firmware/mesh_bed_calibration.h
  50. 104 76
      Firmware/messages.c
  51. 31 2
      Firmware/messages.h
  52. 44 45
      Firmware/mmu.cpp
  53. 1 1
      Firmware/motion_control.cpp
  54. 2 2
      Firmware/motion_control.h
  55. 0 6
      Firmware/optiboot_w25x20cl.h
  56. 49 62
      Firmware/optiboot_w25x20cl.cpp
  57. 6 0
      Firmware/optiboot_xflash.h
  58. 57 30
      Firmware/pat9125.c
  59. 1 0
      Firmware/pat9125.h
  60. 5 0
      Firmware/pins.h
  61. 4 6
      Firmware/pins_Einsy_1_0.h
  62. 0 3
      Firmware/pins_Rambo_1_3.h
  63. 16 15
      Firmware/planner.cpp
  64. 9 5
      Firmware/planner.h
  65. 45 1
      Firmware/sm4.c
  66. 1 0
      Firmware/sm4.h
  67. 1 1
      Firmware/sound.cpp
  68. 0 1
      Firmware/sound.h
  69. 2 2
      Firmware/speed_lookuptable.h
  70. 184 122
      Firmware/stepper.cpp
  71. 32 29
      Firmware/swi2c.c
  72. 51 52
      Firmware/temperature.cpp
  73. 7 16
      Firmware/temperature.h
  74. 12 11
      Firmware/timer02.c
  75. 3 0
      Firmware/timer02.h
  76. 68 35
      Firmware/tmc2130.cpp
  77. 30 4
      Firmware/tmc2130.h
  78. 3 11
      Firmware/tone04.c
  79. 137 0
      Firmware/twi.c
  80. 63 0
      Firmware/twi.h
  81. 4 3
      Firmware/uart2.c
  82. 1481 1706
      Firmware/ultralcd.cpp
  83. 31 29
      Firmware/ultralcd.h
  84. 36 130
      Firmware/util.cpp
  85. 4 0
      Firmware/util.h
  86. 7 1
      Firmware/variants/1_75mm_MK2-RAMBo10a-E3Dv6full.h
  87. 6 1
      Firmware/variants/1_75mm_MK2-RAMBo13a-E3Dv6full.h
  88. 15 3
      Firmware/variants/1_75mm_MK25-RAMBo10a-E3Dv6full.h
  89. 15 3
      Firmware/variants/1_75mm_MK25-RAMBo13a-E3Dv6full.h
  90. 15 3
      Firmware/variants/1_75mm_MK25S-RAMBo10a-E3Dv6full.h
  91. 15 3
      Firmware/variants/1_75mm_MK25S-RAMBo13a-E3Dv6full.h
  92. 31 7
      Firmware/variants/1_75mm_MK3-EINSy10a-E3Dv6full.h
  93. 30 6
      Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h
  94. 0 44
      Firmware/w25x20cl.h
  95. 78 49
      Firmware/w25x20cl.c
  96. 59 0
      Firmware/xflash.h
  97. 109 0
      Firmware/xflash_dump.cpp
  98. 22 0
      Firmware/xflash_dump.h
  99. 51 0
      Firmware/xflash_layout.h
  100. 0 0
      Firmware/xyzcal.cpp

+ 5 - 5
.github/ISSUE_TEMPLATE/bug_report.md

@@ -6,14 +6,14 @@ labels: bug
 assignees: ''
 
 ---
-
+<!--
 Please, before you create a new bug report, please make sure you searched in open and closed issues and couldn't find anything that matches.
-
+-->
 **Printer type** - [e.g. MK3S, MK3, MK2.5S, MK2.5, MK2S, MK2]
-**Printer firmware version**-  [e.g. 3.8.1, 3.8.1-RC1, ...]
+**Printer firmware version** -  [e.g. 3.8.1, 3.8.1-RC1, ...]
 
-**MMU Upgrade** - [e.g. MMU2S, MMU2, MMU1]
-**MMU upgrade firmware version [e.g. 1.0.6, 1.0.6-RC2, ...]
+**MMU upgrade** - [e.g. MMU2S, MMU2, MMU1]
+**MMU upgrade firmware version** - [e.g. 1.0.6, 1.0.6-RC2, ...]
 
 **SD card or USB/Octoprint**
   Please let us know if you print via SD card or USB/Octoprint

+ 3 - 0
.gitignore

@@ -2,6 +2,7 @@
 .project
 .cproject
 Debug
+__pycache__
 Firmware/Configuration_prusa.h
 Firmware/Doc
 /Firmware/.vs/Firmware/v14
@@ -52,3 +53,5 @@ Firmware/Doc
 /Firmware/Firmware.vcxproj
 /Firmware/Configuration_prusa_bckp.h
 /Firmware/variants/printers.h
+Configuration.tmp
+config.tmp

+ 17 - 5
Firmware/Configuration.h

@@ -16,8 +16,19 @@ extern uint16_t nPrinterType;
 extern PGM_P sPrinterName;
 
 // Firmware version
-#define FW_VERSION "3.9.0"
-#define FW_COMMIT_NR 3421
+#define FW_MAJOR 3
+#define FW_MINOR 10
+#define FW_REVISION 1
+//#define FW_FLAVOR RC      //uncomment if DEBUG, DEVEL, APLHA, BETA or RC
+//#define FW_FLAVERSION 1     //uncomment if FW_FLAVOR is defined and versioning is needed.
+#ifndef FW_FLAVOR
+    #define FW_VERSION STR(FW_MAJOR) "." STR(FW_MINOR) "." STR(FW_REVISION)
+#else
+    #define FW_VERSION STR(FW_MAJOR) "." STR(FW_MINOR) "." STR(FW_REVISION) "-" STR(FW_FLAVOR) "" STR(FW_FLAVERSION)
+#endif
+
+#define FW_COMMIT_NR 4697
+
 // FW_VERSION_UNKNOWN means this is an unofficial build.
 // The firmware should only be checked into github with this symbol.
 #define FW_DEV_VERSION FW_VERSION_UNKNOWN
@@ -551,9 +562,10 @@ enum CalibrationStatus
 
 // Try to maintain a minimum distance from the bed even when Z is
 // unknown when doing the following operations
-#define MIN_Z_FOR_LOAD    50
-#define MIN_Z_FOR_UNLOAD  20
-#define MIN_Z_FOR_PREHEAT 10
+#define MIN_Z_FOR_LOAD    50 // lcd filament loading or autoload
+#define MIN_Z_FOR_UNLOAD  50 // lcd filament unloading
+#define MIN_Z_FOR_SWAP    27 // filament change (including M600)
+#define MIN_Z_FOR_PREHEAT 10 // lcd preheat
 
 #include "Configuration_adv.h"
 #include "thermistortables.h"

+ 23 - 11
Firmware/ConfigurationStore.cpp

@@ -96,7 +96,7 @@ void Config_PrintSettings(uint8_t level)
 		"%SMaximum feedrates - stealth (mm/s):\n%S  M203 X%.2f Y%.2f Z%.2f E%.2f\n"
 		"%SMaximum acceleration - normal (mm/s2):\n%S  M201 X%lu Y%lu Z%lu E%lu\n"
 		"%SMaximum acceleration - stealth (mm/s2):\n%S  M201 X%lu Y%lu Z%lu E%lu\n"
-		"%SAcceleration: S=acceleration, T=retract acceleration\n%S  M204 S%.2f T%.2f\n"
+		"%SAcceleration: P=print, R=retract, T=travel\n%S  M204 P%.2f R%.2f T%.2f\n"
 		"%SAdvanced variables: S=Min feedrate (mm/s), T=Min travel feedrate (mm/s), B=minimum segment time (ms), X=maximum XY jerk (mm/s),  Z=maximum Z jerk (mm/s),  E=maximum E jerk (mm/s)\n%S  M205 S%.2f T%.2f B%.2f X%.2f Y%.2f Z%.2f E%.2f\n"
 		"%SHome offset (mm):\n%S  M206 X%.2f Y%.2f Z%.2f\n"
 		),
@@ -106,7 +106,7 @@ void Config_PrintSettings(uint8_t level)
 		echomagic, echomagic, cs.max_feedrate_silent[X_AXIS], cs.max_feedrate_silent[Y_AXIS], cs.max_feedrate_silent[Z_AXIS], cs.max_feedrate_silent[E_AXIS],
 		echomagic, echomagic, cs.max_acceleration_units_per_sq_second_normal[X_AXIS], cs.max_acceleration_units_per_sq_second_normal[Y_AXIS], cs.max_acceleration_units_per_sq_second_normal[Z_AXIS], cs.max_acceleration_units_per_sq_second_normal[E_AXIS],
 		echomagic, echomagic, cs.max_acceleration_units_per_sq_second_silent[X_AXIS], cs.max_acceleration_units_per_sq_second_silent[Y_AXIS], cs.max_acceleration_units_per_sq_second_silent[Z_AXIS], cs.max_acceleration_units_per_sq_second_silent[E_AXIS],
-		echomagic, echomagic, cs.acceleration, cs.retract_acceleration,
+		echomagic, echomagic, cs.acceleration, cs.retract_acceleration, cs.travel_acceleration,
 		echomagic, echomagic, cs.minimumfeedrate, cs.mintravelfeedrate, cs.minsegmenttime, cs.max_jerk[X_AXIS], cs.max_jerk[Y_AXIS], cs.max_jerk[Z_AXIS], cs.max_jerk[E_AXIS],
 		echomagic, echomagic, cs.add_homing[X_AXIS], cs.add_homing[Y_AXIS], cs.add_homing[Z_AXIS]
 #else //TMC2130
@@ -114,14 +114,14 @@ void Config_PrintSettings(uint8_t level)
 		"%SSteps per unit:\n%S  M92 X%.2f Y%.2f Z%.2f E%.2f\n"
 		"%SMaximum feedrates (mm/s):\n%S  M203 X%.2f Y%.2f Z%.2f E%.2f\n"
 		"%SMaximum acceleration (mm/s2):\n%S  M201 X%lu Y%lu Z%lu E%lu\n"
-		"%SAcceleration: S=acceleration, T=retract acceleration\n%S  M204 S%.2f T%.2f\n"
+		"%SAcceleration: P=print, R=retract, T=travel\n%S  M204 P%.2f R%.2f T%.2f\n"
 		"%SAdvanced variables: S=Min feedrate (mm/s), T=Min travel feedrate (mm/s), B=minimum segment time (ms), X=maximum XY jerk (mm/s),  Z=maximum Z jerk (mm/s),  E=maximum E jerk (mm/s)\n%S  M205 S%.2f T%.2f B%.2f X%.2f Y%.2f Z%.2f E%.2f\n"
 		"%SHome offset (mm):\n%S  M206 X%.2f Y%.2f Z%.2f\n"
 		),
 		echomagic, echomagic, cs.axis_steps_per_unit[X_AXIS], cs.axis_steps_per_unit[Y_AXIS], cs.axis_steps_per_unit[Z_AXIS], cs.axis_steps_per_unit[E_AXIS],
 		echomagic, echomagic, max_feedrate[X_AXIS], max_feedrate[Y_AXIS], max_feedrate[Z_AXIS], max_feedrate[E_AXIS],
 		echomagic, echomagic, max_acceleration_units_per_sq_second[X_AXIS], max_acceleration_units_per_sq_second[Y_AXIS], max_acceleration_units_per_sq_second[Z_AXIS], max_acceleration_units_per_sq_second[E_AXIS],
-		echomagic, echomagic, cs.acceleration, cs.retract_acceleration,
+		echomagic, echomagic, cs.acceleration, cs.retract_acceleration, cs.travel_acceleration,
 		echomagic, echomagic, cs.minimumfeedrate, cs.mintravelfeedrate, cs.minsegmenttime, cs.max_jerk[X_AXIS], cs.max_jerk[Y_AXIS], cs.max_jerk[Z_AXIS], cs.max_jerk[E_AXIS],
 		echomagic, echomagic, cs.add_homing[X_AXIS], cs.add_homing[Y_AXIS], cs.add_homing[Z_AXIS]
 #endif //TMC2130
@@ -184,7 +184,7 @@ static_assert (false, "zprobe_zoffset was not initialized in printers in field t
         "0.0, if this is not acceptable, increment EEPROM_VERSION to force use default_conf");
 #endif
 
-static_assert (sizeof(M500_conf) == 192, "sizeof(M500_conf) has changed, ensure that EEPROM_VERSION has been incremented, "
+static_assert (sizeof(M500_conf) == 196, "sizeof(M500_conf) has changed, ensure that EEPROM_VERSION has been incremented, "
         "or if you added members in the end of struct, ensure that historically uninitialized values will be initialized."
         "If this is caused by change to more then 8bit processor, decide whether make this struct packed to save EEPROM,"
         "leave as it is to keep fast code, or reorder struct members to pack more tightly.");
@@ -232,8 +232,21 @@ static const M500_conf default_conf PROGMEM =
 #else // TMC2130
     {16,16,16,16},
 #endif
+    DEFAULT_TRAVEL_ACCELERATION,
 };
 
+
+static bool is_uninitialized(void* addr, uint8_t len)
+{
+    while(len--)
+    {
+        if(reinterpret_cast<uint8_t*>(addr)[len] != 0xff)
+            return false;
+    }
+    return true;
+}
+
+
 //! @brief Read M500 configuration
 //! @retval true Succeeded. Stored settings retrieved or default settings retrieved in case EEPROM has been erased.
 //! @retval false Failed. Default settings has been retrieved, because of older version or corrupted data.
@@ -257,13 +270,9 @@ bool Config_RetrieveSettings()
         for (uint8_t i = 0; i < (sizeof(cs.max_feedrate_silent)/sizeof(cs.max_feedrate_silent[0])); ++i)
         {
             const uint32_t erased = 0xffffffff;
-            bool initialized = false;
-
-            for(uint8_t j = 0; j < sizeof(float); ++j)
-            {
-                if(0xff != reinterpret_cast<uint8_t*>(&(cs.max_feedrate_silent[i]))[j]) initialized = true;
+            if (is_uninitialized(&(cs.max_feedrate_silent[i]), sizeof(float))) {
+                memcpy_P(&cs.max_feedrate_silent[i],&default_conf.max_feedrate_silent[i], sizeof(cs.max_feedrate_silent[i]));
             }
-            if (!initialized) memcpy_P(&cs.max_feedrate_silent[i],&default_conf.max_feedrate_silent[i], sizeof(cs.max_feedrate_silent[i]));
             if (erased == cs.max_acceleration_units_per_sq_second_silent[i]) {
                 memcpy_P(&cs.max_acceleration_units_per_sq_second_silent[i],&default_conf.max_acceleration_units_per_sq_second_silent[i],sizeof(cs.max_acceleration_units_per_sq_second_silent[i]));
             }
@@ -293,6 +302,9 @@ bool Config_RetrieveSettings()
 		tmc2130_set_res(E_AXIS, cs.axis_ustep_resolution[E_AXIS]);
 #endif //TMC2130
 
+        if(is_uninitialized(&cs.travel_acceleration, sizeof(cs.travel_acceleration)))
+            cs.travel_acceleration = cs.acceleration;
+
 		reset_acceleration_rates();
 
 		// Call updatePID (similar to when we have processed M301)

+ 1 - 0
Firmware/ConfigurationStore.h

@@ -38,6 +38,7 @@ typedef struct
     float max_feedrate_silent[4]; //!< max speeds for silent mode
     unsigned long max_acceleration_units_per_sq_second_silent[4];
     unsigned char axis_ustep_resolution[4];
+    float travel_acceleration; //!< travel acceleration mm/s^2
 } M500_conf;
 
 extern M500_conf cs;

+ 32 - 15
Firmware/Configuration_adv.h

@@ -62,8 +62,19 @@
 // before setting a PWM value. (Does not work with software PWM for fan on Sanguinololu)
 #define FAN_KICKSTART_TIME 800
 
-
-
+/**
+ * Auto-report all at once with M155 S<seconds> C[bitmask] with single timer
+ * 
+ * bit 0 = Auto-report temperatures
+ * bit 1 = Auto-report fans
+ * bit 2 = Auto-report position
+ * bit 3 = free
+ * bit 4 = free
+ * bit 5 = free
+ * bit 6 = free
+ * bit 7 = free
+*/
+#define AUTO_REPORT
 
 //===========================================================================
 //=============================Mechanical Settings===========================
@@ -220,33 +231,23 @@
 * SD sorting uses static allocation (as set by SDSORT_LIMIT), allowing the
 * compiler to calculate the worst-case usage and throw an error if the SRAM
 * limit is exceeded.
-*
-*  - SDSORT_USES_RAM provides faster sorting via a static directory buffer.
-*  - SDSORT_USES_STACK does the same, but uses a local stack-based buffer.
-*  - SDSORT_CACHE_NAMES will retain the sorted file listing in RAM. (Expensive!)
-*  - SDSORT_DYNAMIC_RAM only uses RAM when the SD menu is visible. (Use with caution!)
 */
 	#define SDCARD_SORT_ALPHA //Alphabetical sorting of SD files menu
 	
 	// SD Card Sorting options
-	// In current firmware Prusa Firmware version,
-	// SDSORT_CACHE_NAMES and SDSORT_DYNAMIC_RAM is not supported and must be set to 0.
 	#ifdef SDCARD_SORT_ALPHA
 	  #define SD_SORT_TIME 0
 	  #define SD_SORT_ALPHA 1
 	  #define SD_SORT_NONE 2
+	  // #define SHELLSORT
+	  // #define SORTING_DUMP
 	
 	  #define SDSORT_LIMIT       100    // Maximum number of sorted items (10-256).
 	  #define FOLDER_SORTING     -1     // -1=above  0=none  1=below
-	  #define SDSORT_GCODE       0  // Allow turning sorting on/off with LCD and M34 g-code.
-	  #define SDSORT_USES_RAM    0  // Pre-allocate a static array for faster pre-sorting.
-	  #define SDSORT_USES_STACK  0  // Prefer the stack for pre-sorting to give back some SRAM. (Negated by next 2 options.)
-	  #define SDSORT_CACHE_NAMES 0  // Keep sorted items in RAM longer for speedy performance. Most expensive option.
-	  #define SDSORT_DYNAMIC_RAM 0  // Use dynamic allocation (within SD menus). Least expensive option. Set SDSORT_LIMIT before use!
 	#endif
 	
 	#if defined(SDCARD_SORT_ALPHA)
-	  #define HAS_FOLDER_SORTING (FOLDER_SORTING || SDSORT_GCODE)
+	  #define HAS_FOLDER_SORTING (FOLDER_SORTING)
 	#endif
 
 // Enable the option to stop SD printing when hitting and endstops, needs to be enabled from the LCD menu when this option is enabled.
@@ -327,6 +328,11 @@ const unsigned int dropsegments=5; //everything with less than this number of st
 // Control heater 0 and heater 1 in parallel.
 //#define HEATERS_PARALLEL
 
+//LCD status clock interval timer to switch between
+// remaining print time
+// and time to change/pause/interaction
+#define CLOCK_INTERVAL_TIME 5
+
 //===========================================================================
 //=============================Buffers           ============================
 //===========================================================================
@@ -376,6 +382,17 @@ const unsigned int dropsegments=5; //everything with less than this number of st
   #endif
 #endif
 
+/**
+ * Include capabilities in M115 output
+ */
+#define EXTENDED_CAPABILITIES_REPORT
+
+/**
+ * Enable M120/M121 G-code commands
+ * 
+ */
+//#define M120_M121_ENABLED  //Be careful enabling and using these G-code commands.
+
 //===========================================================================
 //=============================  Define Defines  ============================
 //===========================================================================

+ 225 - 156
Firmware/Dcodes.cpp

@@ -1,5 +1,5 @@
+#include "Marlin.h"
 #include "Dcodes.h"
-//#include "Marlin.h"
 #include "Configuration.h"
 #include "language.h"
 #include "cmdqueue.h"
@@ -23,28 +23,25 @@ void print_hex_byte(uint8_t val)
 	print_hex_nibble(val & 15);
 }
 
-void print_hex_word(uint16_t val)
-{
-	print_hex_byte(val >> 8);
-	print_hex_byte(val & 255);
-}
+// debug range address type (fits all SRAM/PROGMEM/XFLASH memory ranges)
+#if defined(DEBUG_DCODE6) || defined(DEBUG_DCODES) || defined(XFLASH_DUMP)
+#include "xflash.h"
+#include "xflash_layout.h"
 
-void print_eeprom(uint16_t address, uint16_t count, uint8_t countperline = 16)
+#define DADDR_SIZE 32
+typedef uint32_t daddr_t; // XFLASH requires 24 bits
+#else
+#define DADDR_SIZE 16
+typedef uint16_t daddr_t;
+#endif
+
+void print_hex_word(daddr_t val)
 {
-	while (count)
-	{
-		print_hex_word(address);
-		putchar(' ');
-		uint8_t count_line = countperline;
-		while (count && count_line)
-		{
-			putchar(' ');
-			print_hex_byte(eeprom_read_byte((uint8_t*)address++));
-			count_line--;
-			count--;
-		}
-		putchar('\n');
-	}
+#if DADDR_SIZE > 16
+    print_hex_byte((val >> 16) & 0xFF);
+#endif
+    print_hex_byte((val >> 8) & 0xFF);
+    print_hex_byte(val & 0xFF);
 }
 
 int parse_hex(char* hex, uint8_t* data, int count)
@@ -71,12 +68,16 @@ int parse_hex(char* hex, uint8_t* data, int count)
 }
 
 
-void print_mem(uint32_t address, uint16_t count, uint8_t type, uint8_t countperline = 16)
+enum class dcode_mem_t:uint8_t { sram, eeprom, progmem, xflash };
+
+void print_mem(daddr_t address, daddr_t count, dcode_mem_t type, uint8_t countperline = 16)
 {
+#if defined(DEBUG_DCODE6) || defined(DEBUG_DCODES) || defined(XFLASH_DUMP)
+    if(type == dcode_mem_t::xflash)
+        XFLASH_SPI_ENTER();
+#endif
 	while (count)
 	{
-		if (type == 2)
-			print_hex_nibble(address >> 16);
 		print_hex_word(address);
 		putchar(' ');
 		uint8_t count_line = countperline;
@@ -85,19 +86,75 @@ void print_mem(uint32_t address, uint16_t count, uint8_t type, uint8_t countperl
 			uint8_t data = 0;
 			switch (type)
 			{
-			case 0: data = *((uint8_t*)address++); break;
-			case 1: data = eeprom_read_byte((uint8_t*)address++); break;
-			case 2: data = pgm_read_byte_far((uint8_t*)address++); break;
+			case dcode_mem_t::sram: data = *((uint8_t*)address); break;
+			case dcode_mem_t::eeprom: data = eeprom_read_byte((uint8_t*)address); break;
+			case dcode_mem_t::progmem: break;
+#if defined(DEBUG_DCODE6) || defined(DEBUG_DCODES) || defined(XFLASH_DUMP)
+            case dcode_mem_t::xflash: xflash_rd_data(address, &data, 1); break;
+#else
+            case dcode_mem_t::xflash: break;
+#endif
 			}
+            ++address;
 			putchar(' ');
 			print_hex_byte(data);
 			count_line--;
 			count--;
+
+            // sporadically call manage_heater, but only when interrupts are enabled (meaning
+            // print_mem is called by D2). Don't do anything otherwise: we are inside a crash
+            // handler where memory & stack needs to be preserved!
+            if((SREG & (1 << SREG_I)) && !((uint16_t)count % 8192))
+                manage_heater();
 		}
 		putchar('\n');
 	}
 }
 
+// TODO: this only handles SRAM/EEPROM 16bit addresses
+void write_mem(uint16_t address, uint16_t count, const uint8_t* data, const dcode_mem_t type)
+{
+    for (uint16_t i = 0; i < count; i++)
+    {
+        switch (type)
+        {
+        case dcode_mem_t::sram: *((uint8_t*)address) = data[i]; break;
+        case dcode_mem_t::eeprom: eeprom_write_byte((uint8_t*)address, data[i]); break;
+        case dcode_mem_t::progmem: break;
+        case dcode_mem_t::xflash: break;
+        }
+        ++address;
+    }
+}
+
+void dcode_core(daddr_t addr_start, const daddr_t addr_end, const dcode_mem_t type,
+                uint8_t dcode, const char* type_desc)
+{
+    KEEPALIVE_STATE(NOT_BUSY);
+    DBG(_N("D%d - Read/Write %S\n"), dcode, type_desc);
+    daddr_t count = -1; // RW the entire space by default
+    if (code_seen('A'))
+        addr_start = (strchr_pointer[1] == 'x')?strtol(strchr_pointer + 2, 0, 16):(int)code_value();
+    if (code_seen('C'))
+        count = code_value_long();
+    if (addr_start > addr_end)
+        addr_start = addr_end;
+    if ((addr_start + count) > addr_end || (addr_start + count) < addr_start)
+        count = addr_end - addr_start;
+    if (code_seen('X'))
+    {
+        uint8_t data[16];
+        count = parse_hex(strchr_pointer + 1, data, 16);
+        write_mem(addr_start, count, data, type);
+#if DADDR_SIZE > 16
+        DBG(_N("%lu bytes written to %S at address 0x%04lx\n"), count, type_desc, addr_start);
+#else
+        DBG(_N("%u bytes written to %S at address 0x%08x\n"), count, type_desc, addr_start);
+#endif
+    }
+    print_mem(addr_start, count, type);
+}
+
 #if defined DEBUG_DCODE3 || defined DEBUG_DCODES
 #define EEPROM_SIZE 0x1000
     /*!
@@ -120,46 +177,7 @@ void print_mem(uint32_t address, uint16_t count, uint8_t type, uint8_t countperl
     */
 void dcode_3()
 {
-	DBG(_N("D3 - Read/Write EEPROM\n"));
-	uint16_t address = 0x0000; //default 0x0000
-	uint16_t count = EEPROM_SIZE; //default 0x1000 (entire eeprom)
-	if (code_seen('A')) // Address (0x0000-0x0fff)
-		address = (strchr_pointer[1] == 'x')?strtol(strchr_pointer + 2, 0, 16):(int)code_value();
-	if (code_seen('C')) // Count (0x0001-0x1000)
-		count = (int)code_value();
-	address &= 0x1fff;
-	if (count > EEPROM_SIZE) count = EEPROM_SIZE;
-	if ((address + count) > EEPROM_SIZE) count = EEPROM_SIZE - address;
-	if (code_seen('X')) // Data
-	{
-		uint8_t data[16];
-		count = parse_hex(strchr_pointer + 1, data, 16);
-		if (count > 0)
-		{
-			for (uint16_t i = 0; i < count; i++)
-				eeprom_write_byte((uint8_t*)(address + i), data[i]);
-			printf_P(_N("%d bytes written to EEPROM at address 0x%04x"), count, address);
-			putchar('\n');
-		}
-		else
-			count = 0;
-	}
-	print_mem(address, count, 1);
-/*	while (count)
-	{
-		print_hex_word(address);
-		putchar(' ');
-		uint8_t countperline = 16;
-		while (count && countperline)
-		{
-			uint8_t data = eeprom_read_byte((uint8_t*)address++);
-			putchar(' ');
-			print_hex_byte(data);
-			countperline--;
-			count--;
-		}
-		putchar('\n');
-	}*/
+    dcode_core(0, EEPROM_SIZE, dcode_mem_t::eeprom, 3, _N("EEPROM"));
 }
 #endif //DEBUG_DCODE3
 
@@ -173,19 +191,6 @@ void dcode_3()
 #include "bootapp.h"
 
 #if 0
-#define FLASHSIZE     0x40000
-
-#define RAMSIZE        0x2000
-#define boot_src_addr  (*((uint32_t*)(RAMSIZE - 16)))
-#define boot_dst_addr  (*((uint32_t*)(RAMSIZE - 12)))
-#define boot_copy_size (*((uint16_t*)(RAMSIZE - 8)))
-#define boot_reserved  (*((uint8_t*)(RAMSIZE - 6)))
-#define boot_app_flags (*((uint8_t*)(RAMSIZE - 5)))
-#define boot_app_magic (*((uint32_t*)(RAMSIZE - 4)))
-#define BOOT_APP_FLG_ERASE 0x01
-#define BOOT_APP_FLG_COPY  0x02
-#define BOOT_APP_FLG_FLASH 0x04
-
 extern float current_temperature_pinda;
 extern float axis_steps_per_unit[NUM_AXIS];
 
@@ -204,7 +209,7 @@ extern float axis_steps_per_unit[NUM_AXIS];
     */
 void dcode__1()
 {
-	printf_P(PSTR("D-1 - Endless loop\n"));
+	DBG(_N("D-1 - Endless loop\n"));
 //	cli();
 	while (1);
 }
@@ -226,9 +231,7 @@ void dcode_0()
 	LOG("D0 - Reset\n");
 	if (code_seen('B')) //bootloader
 	{
-		cli();
-		wdt_enable(WDTO_15MS);
-		while(1);
+		softReset();
 	}
 	else //reset
 	{
@@ -252,71 +255,36 @@ void dcode_1()
 	cli();
 	for (int i = 0; i < 8192; i++)
 		eeprom_write_byte((unsigned char*)i, (unsigned char)0xff);
-	wdt_enable(WDTO_15MS);
-	while(1);
+	softReset();
 }
+#endif
 
+#if defined DEBUG_DCODE2 || defined DEBUG_DCODES
     /*!
     ### D2 - Read/Write RAM <a href="https://reprap.org/wiki/G-code#D2:_Read.2FWrite_RAM">D3: Read/Write RAM</a>
     This command can be used without any additional parameters. It will read the entire RAM.
     #### Usage
-    
+
         D2 [ A | C | X ]
-    
+
     #### Parameters
-    - `A` - Address (x0000-x1fff)
-    - `C` - Count (1-8192)
+    - `A` - Address (x0000-x21ff)
+    - `C` - Count (1-8704)
     - `X` - Data
 
 	#### Notes
 	- The hex address needs to be lowercase without the 0 before the x
-	- Count is decimal 
+	- Count is decimal
 	- The hex data needs to be lowercase
-	
+
     */
 void dcode_2()
 {
-	LOG("D2 - Read/Write RAM\n");
-	uint16_t address = 0x0000; //default 0x0000
-	uint16_t count = 0x2000; //default 0x2000 (entire ram)
-	if (code_seen('A')) // Address (0x0000-0x1fff)
-		address = (strchr_pointer[1] == 'x')?strtol(strchr_pointer + 2, 0, 16):(int)code_value();
-	if (code_seen('C')) // Count (0x0001-0x2000)
-		count = (int)code_value();
-	address &= 0x1fff;
-	if (count > 0x2000) count = 0x2000;
-	if ((address + count) > 0x2000) count = 0x2000 - address;
-	if (code_seen('X')) // Data
-	{
-		uint8_t data[16];
-		count = parse_hex(strchr_pointer + 1, data, 16);
-		if (count > 0)
-		{
-			for (uint16_t i = 0; i < count; i++)
-				*((uint8_t*)(address + i)) =  data[i];
-			LOG("%d bytes written to RAM at address %04x", count, address);
-		}
-		else
-			count = 0;
-	}
-	print_mem(address, count, 0);
-/*	while (count)
-	{
-		print_hex_word(address);
-		putchar(' ');
-		uint8_t countperline = 16;
-		while (count && countperline)
-		{
-			uint8_t data = *((uint8_t*)address++);
-			putchar(' ');
-			print_hex_byte(data);
-			countperline--;
-			count--;
-		}
-		putchar('\n');
-	}*/
+    dcode_core(RAMSTART, RAMEND+1, dcode_mem_t::sram, 2, _N("SRAM"));
 }
+#endif
 
+#ifdef DEBUG_DCODES
     /*!
     
     ### D4 - Read/Write PIN <a href="https://reprap.org/wiki/G-code#D4:_Read.2FWrite_PIN">D4: Read/Write PIN</a>
@@ -383,7 +351,7 @@ void dcode_4()
    */
 void dcode_5()
 {
-	printf_P(PSTR("D5 - Read/Write FLASH\n"));
+	puts_P(PSTR("D5 - Read/Write FLASH"));
 	uint32_t address = 0x0000; //default 0x0000
 	uint16_t count = 0x0400; //default 0x0400 (1kb block)
 	if (code_seen('A')) // Address (0x00000-0x3ffff)
@@ -420,8 +388,7 @@ void dcode_5()
 		boot_dst_addr = (uint32_t)address;
 		boot_src_addr = (uint32_t)(&data);
 		bootapp_print_vars();
-		wdt_enable(WDTO_15MS);
-		while(1);
+		softReset();
 	}
 	while (count)
 	{
@@ -442,17 +409,32 @@ void dcode_5()
 }
 #endif //DEBUG_DCODE5
 
-#ifdef DEBUG_DCODES
-
+#if defined(XFLASH) && (defined DEBUG_DCODE6 || defined DEBUG_DCODES)
     /*!
     ### D6 - Read/Write external FLASH <a href="https://reprap.org/wiki/G-code#D6:_Read.2FWrite_external_FLASH">D6: Read/Write external Flash</a>
-    Reserved
+    This command can be used without any additional parameters. It will read the entire XFLASH.
+    #### Usage
+
+        D6 [ A | C | X ]
+
+    #### Parameters
+    - `A` - Address (x0000-x3ffff)
+    - `C` - Count (1-262144)
+    - `X` - Data
+
+	#### Notes
+	- The hex address needs to be lowercase without the 0 before the x
+	- Count is decimal
+	- The hex data needs to be lowercase
+	- Writing is currently not implemented
     */
 void dcode_6()
 {
-	LOG("D6 - Read/Write external FLASH\n");
+    dcode_core(0x0, XFLASH_SIZE, dcode_mem_t::xflash, 6, _N("XFLASH"));
 }
+#endif
 
+#ifdef DEBUG_DCODES
     /*!
     ### D7 - Read/Write Bootloader <a href="https://reprap.org/wiki/G-code#D7:_Read.2FWrite_Bootloader">D7: Read/Write Bootloader</a>
     Reserved
@@ -467,8 +449,7 @@ void dcode_7()
 	boot_copy_size = (uint16_t)0xc00;
 	boot_src_addr = (uint32_t)0x0003e400;
 	boot_dst_addr = (uint32_t)0x0003f400;
-	wdt_enable(WDTO_15MS);
-	while(1);
+	softReset();
 */
 }
 
@@ -486,7 +467,7 @@ void dcode_7()
     */
 void dcode_8()
 {
-	printf_P(PSTR("D8 - Read/Write PINDA\n"));
+	puts_P(PSTR("D8 - Read/Write PINDA"));
 	uint8_t cal_status = calibration_status_pinda();
 	float temp_pinda = current_temperature_pinda;
 	float offset_z = temp_compensation_pinda_thermistor_offset(temp_pinda);
@@ -557,26 +538,20 @@ const char* dcode_9_ADC_name(uint8_t i)
 	return 0;
 }
 
-#ifdef AMBIENT_THERMISTOR
-extern int current_temperature_raw_ambient;
-#endif //AMBIENT_THERMISTOR
-
-#ifdef VOLT_PWR_PIN
-extern int current_voltage_raw_pwr;
-#endif //VOLT_PWR_PIN
-
-#ifdef VOLT_BED_PIN
-extern int current_voltage_raw_bed;
-#endif //VOLT_BED_PIN
-
 uint16_t dcode_9_ADC_val(uint8_t i)
 {
 	switch (i)
 	{
+#ifdef SHOW_TEMP_ADC_VALUES
 	case 0: return current_temperature_raw[0];
+#endif //SHOW_TEMP_ADC_VALUES
 	case 1: return 0;
+#ifdef SHOW_TEMP_ADC_VALUES
 	case 2: return current_temperature_bed_raw;
+#endif //SHOW_TEMP_ADC_VALUES
+#ifdef PINDA_THERMISTOR
 	case 3: return current_temperature_raw_pinda;
+#endif //PINDA_THERMISTOR
 #ifdef VOLT_PWR_PIN
 	case 4: return current_voltage_raw_pwr;
 #endif //VOLT_PWR_PIN
@@ -592,7 +567,7 @@ uint16_t dcode_9_ADC_val(uint8_t i)
 
 void dcode_9()
 {
-	printf_P(PSTR("D9 - Read/Write ADC\n"));
+	puts_P(PSTR("D9 - Read/Write ADC"));
 	if ((strchr_pointer[1+1] == '?') || (strchr_pointer[1+1] == 0))
 	{
 		for (uint8_t i = 0; i < ADC_CHAN_CNT; i++)
@@ -789,7 +764,7 @@ extern void st_synchronize();
 	*/
 void dcode_2130()
 {
-	printf_P(PSTR("D2130 - TMC2130\n"));
+	puts_P(PSTR("D2130 - TMC2130"));
 	uint8_t axis = 0xff;
 	switch (strchr_pointer[1+4])
 	{
@@ -945,5 +920,99 @@ void dcode_9125()
 }
 #endif //PAT9125
 
-
 #endif //DEBUG_DCODES
+
+#ifdef XFLASH_DUMP
+#include "xflash_dump.h"
+
+void dcode_20()
+{
+    if(code_seen('E'))
+        xfdump_full_dump_and_reset();
+    else
+    {
+        unsigned long ts = _millis();
+        xfdump_dump();
+        ts = _millis() - ts;
+        DBG(_N("dump completed in %lums\n"), ts);
+    }
+}
+
+void dcode_21()
+{
+    if(!xfdump_check_state())
+        DBG(_N("no dump available\n"));
+    else
+    {
+        KEEPALIVE_STATE(NOT_BUSY);
+        DBG(_N("D21 - read crash dump\n"));
+        print_mem(DUMP_OFFSET, sizeof(dump_t), dcode_mem_t::xflash);
+    }
+}
+
+void dcode_22()
+{
+    if(!xfdump_check_state())
+        DBG(_N("no dump available\n"));
+    else
+    {
+        xfdump_reset();
+        DBG(_N("dump cleared\n"));
+    }
+}
+#endif
+
+#ifdef EMERGENCY_SERIAL_DUMP
+#include "asm.h"
+#include "xflash_dump.h"
+
+bool emergency_serial_dump = false;
+
+void dcode_23()
+{
+    if(code_seen('E'))
+        serial_dump_and_reset(dump_crash_reason::manual);
+    else
+    {
+        emergency_serial_dump = !code_seen('R');
+        SERIAL_ECHOPGM("serial dump ");
+        SERIAL_ECHOLNRPGM(emergency_serial_dump? _N("enabled"): _N("disabled"));
+    }
+}
+
+void __attribute__((noinline)) serial_dump_and_reset(dump_crash_reason reason)
+{
+    uint16_t sp;
+    uint32_t pc;
+
+    // we're being called from a live state, so shut off interrupts ...
+    cli();
+
+    // sample SP/PC
+    sp = SP;
+    GETPC(&pc);
+
+    // extend WDT long enough to allow writing the entire stream
+    wdt_enable(WDTO_8S);
+
+    // ... and heaters
+    WRITE(FAN_PIN, HIGH);
+    disable_heater();
+
+    // this function can also be called from within a corrupted state, so not use
+    // printf family of functions that use the heap or grow the stack.
+    SERIAL_ECHOLNPGM("D23 - emergency serial dump");
+    SERIAL_ECHOPGM("error: ");
+    MYSERIAL.print((uint8_t)reason, DEC);
+    SERIAL_ECHOPGM(" 0x");
+    MYSERIAL.print(pc, HEX);
+    SERIAL_ECHOPGM(" 0x");
+    MYSERIAL.println(sp, HEX);
+
+    print_mem(0, RAMEND+1, dcode_mem_t::sram);
+    SERIAL_ECHOLNRPGM(MSG_OK);
+
+    // reset soon
+    softReset();
+}
+#endif

+ 19 - 0
Firmware/Dcodes.h

@@ -4,7 +4,10 @@
 extern void dcode__1(); //D-1 - Endless loop (to simulate deadlock)
 extern void dcode_0(); //D0 - Reset
 extern void dcode_1(); //D1 - Clear EEPROM
+
+#if defined DEBUG_DCODE2 || defined DEBUG_DCODES
 extern void dcode_2(); //D2 - Read/Write RAM
+#endif
 
 #if defined DEBUG_DCODE3 || defined DEBUG_DCODES
 extern void dcode_3(); //D3 - Read/Write EEPROM
@@ -16,13 +19,29 @@ extern void dcode_4(); //D4 - Read/Write PIN
 extern void dcode_5(); //D5 - Read/Write FLASH
 #endif //DEBUG_DCODE5
 
+#if defined DEBUG_DCODE6 || defined DEBUG_DCODES
 extern void dcode_6(); //D6 - Read/Write external FLASH
+#endif
+
 extern void dcode_7(); //D7 - Read/Write Bootloader
 extern void dcode_8(); //D8 - Read/Write PINDA
 extern void dcode_9(); //D9 - Read/Write ADC (Write=enable simulated, Read=disable simulated)
 extern void dcode_10(); //D10 - XYZ calibration = OK
 extern void dcode_12(); //D12 - Log time. Writes the current time in the log file.
 
+#ifdef XFLASH_DUMP
+extern void dcode_20(); //D20 - Generate an offline crash dump
+extern void dcode_21(); //D21 - Print crash dump to serial
+extern void dcode_22(); //D22 - Clear crash dump state
+#endif
+
+#ifdef EMERGENCY_SERIAL_DUMP
+#include "xflash_dump.h"
+extern void dcode_23(); //D23 - Request/generate an online serial crash dump
+extern bool emergency_serial_dump; //emergency dump enabled flag
+extern void serial_dump_and_reset(dump_crash_reason);
+#endif
+
 #ifdef HEATBED_ANALYSIS
 extern void dcode_80(); //D80 - Bed check. This command will log data to SD card file "mesh.txt".
 extern void dcode_81(); //D81 - Bed analysis. This command will log data to SD card file "wldsd.txt".

+ 19 - 29
Firmware/Marlin.h

@@ -4,7 +4,7 @@
 #ifndef MARLIN_H
 #define MARLIN_H
 
-#define  FORCE_INLINE __attribute__((always_inline)) inline
+#include "macros.h"
 
 #include <math.h>
 #include <stdio.h>
@@ -116,7 +116,6 @@ void serial_echopair_P(const char *s_P, unsigned long v);
 void serialprintPGM(const char *str);
 
 bool is_buffer_empty();
-void get_command();
 void process_commands();
 void ramming();
 
@@ -237,28 +236,19 @@ void update_currents();
 void get_coordinates();
 void prepare_move();
 void kill(const char *full_screen_message = NULL, unsigned char id = 0);
-void Stop();
-bool IsStopped();
 void finishAndDisableSteppers();
 
-//put an ASCII command at the end of the current buffer.
-void enquecommand(const char *cmd, bool from_progmem = false);
+void UnconditionalStop(); // Stop heaters, motion and clear current print status
+void Stop();              // Emergency stop used by overtemp functions which allows recovery
+bool IsStopped();         // Returns true if the print has been stopped
 
 //put an ASCII command at the end of the current buffer, read from flash
 #define enquecommand_P(cmd) enquecommand(cmd, true)
 
-//put an ASCII command at the begin of the current buffer
-void enquecommand_front(const char *cmd, bool from_progmem = false);
-
 //put an ASCII command at the begin of the current buffer, read from flash
 #define enquecommand_front_P(cmd) enquecommand_front(cmd, true)
 
-void repeatcommand_front();
-
-// Remove all lines from the command queue.
-void cmdqueue_reset();
-
-void prepare_arc_move(char isclockwise);
+void prepare_arc_move(bool isclockwise);
 void clamp_to_software_endstops(float target[3]);
 void refresh_cmd_timeout(void);
 
@@ -287,11 +277,6 @@ FORCE_INLINE unsigned long millis_nc() {
 void setPwmFrequency(uint8_t pin, int val);
 #endif
 
-#ifndef CRITICAL_SECTION_START
-  #define CRITICAL_SECTION_START  unsigned char _sreg = SREG; cli();
-  #define CRITICAL_SECTION_END    SREG = _sreg;
-#endif //CRITICAL_SECTION_START
-
 extern bool fans_check_enabled;
 extern float homing_feedrate[];
 extern uint8_t axis_relative_modes;
@@ -306,6 +291,7 @@ extern float min_pos[3];
 extern float max_pos[3];
 extern bool axis_known_position[3];
 extern int fanSpeed;
+extern uint8_t newFanSpeed;
 extern int8_t lcd_change_fil_state;
 extern float default_retraction;
 
@@ -366,11 +352,6 @@ extern unsigned long t_fan_rising_edge;
 extern bool mesh_bed_leveling_flag;
 extern bool mesh_bed_run_from_menu;
 
-extern bool sortAlpha;
-
-extern char dir_names[3][9];
-
-extern int8_t lcd_change_fil_state;
 // save/restore printing
 extern bool saved_printing;
 extern uint8_t saved_printing_type;
@@ -383,9 +364,11 @@ extern bool mmu_print_saved;
 
 //estimated time to end of the print
 extern uint8_t print_percent_done_normal;
-extern uint16_t print_time_remaining_normal;
 extern uint8_t print_percent_done_silent;
+extern uint16_t print_time_remaining_normal;
 extern uint16_t print_time_remaining_silent;
+extern uint16_t print_time_to_change_normal;
+extern uint16_t print_time_to_change_silent;
 
 #define PRINT_TIME_REMAINING_INIT 0xffff
 
@@ -394,8 +377,8 @@ extern uint16_t gcode_in_progress;
 
 extern LongTimer safetyTimer;
 
-#define PRINT_PERCENT_DONE_INIT   0xff
-#define PRINTER_ACTIVE (IS_SD_PRINTING || is_usb_printing || isPrintPaused || (custom_message_type == CustomMsg::TempCal) || saved_printing || (lcd_commands_type == LcdCommands::Layer1Cal) || mmu_print_saved)
+#define PRINT_PERCENT_DONE_INIT 0xff
+#define PRINTER_ACTIVE (IS_SD_PRINTING || is_usb_printing || isPrintPaused || (custom_message_type == CustomMsg::TempCal) || saved_printing || (lcd_commands_type == LcdCommands::Layer1Cal) || mmu_print_saved || homing_flag || mesh_bed_leveling_flag)
 
 //! Beware - mcode_in_progress is set as soon as the command gets really processed,
 //! which is not the same as posting the M600 command into the command queue
@@ -457,7 +440,6 @@ extern void cancel_saved_printing();
 
 
 //estimated time to end of the print
-extern uint16_t print_time_remaining();
 extern uint8_t calc_percent_done();
 
 
@@ -497,6 +479,9 @@ void force_high_power_mode(bool start_high_power_section);
 
 bool gcode_M45(bool onlyZ, int8_t verbosity_level);
 void gcode_M114();
+#if (defined(FANCHECK) && (((defined(TACH_0) && (TACH_0 >-1)) || (defined(TACH_1) && (TACH_1 > -1)))))
+void gcode_M123();
+#endif //FANCHECK and TACH_0 and TACH_1
 void gcode_M701();
 
 #define UVLO !(PINE & (1<<4))
@@ -512,4 +497,9 @@ void load_filament_final_feed();
 void marlin_wait_for_click();
 void raise_z_above(float target, bool plan=true);
 
+extern "C" void softReset();
+void stack_error();
+
+extern uint32_t IP_address;
+
 #endif

+ 2 - 2
Firmware/MarlinSerial.cpp

@@ -255,7 +255,7 @@ void MarlinSerial::print(double n, int digits)
 
 void MarlinSerial::println(void)
 {
-  print('\r');
+  // print('\r');
   print('\n');  
 }
 
@@ -359,7 +359,7 @@ void MarlinSerial::printFloat(double number, uint8_t digits)
 
   // Print the decimal point, but only if there are digits beyond
   if (digits > 0)
-    print("."); 
+    print('.'); 
 
   // Extract digits from the remainder one at a time
   while (digits-- > 0)

+ 1710 - 1210
Firmware/Marlin_main.cpp

@@ -47,7 +47,9 @@
 #include "Configuration.h"
 #include "Marlin.h"
 #include "config.h"
-  
+
+#include "macros.h"
+
 #ifdef ENABLE_AUTO_BED_LEVELING
 #include "vector_3.h"
   #ifdef AUTO_BED_LEVELING_GRID
@@ -64,6 +66,7 @@
 
 #include "menu.h"
 #include "ultralcd.h"
+#include "conv2str.h"
 #include "backlight.h"
 
 #include "planner.h"
@@ -88,28 +91,25 @@
 #include "la10compat.h"
 #endif
 
-#ifdef SWSPI
-#include "swspi.h"
-#endif //SWSPI
-
 #include "spi.h"
 
-#ifdef SWI2C
-#include "swi2c.h"
-#endif //SWI2C
-
 #ifdef FILAMENT_SENSOR
 #include "fsensor.h"
+#ifdef IR_SENSOR
+#include "pat9125.h" // for pat9125_probe
+#endif
 #endif //FILAMENT_SENSOR
 
 #ifdef TMC2130
 #include "tmc2130.h"
 #endif //TMC2130
 
-#ifdef W25X20CL
-#include "w25x20cl.h"
-#include "optiboot_w25x20cl.h"
-#endif //W25X20CL
+#ifdef XFLASH
+#include "xflash.h"
+#include "optiboot_xflash.h"
+#endif //XFLASH
+
+#include "xflash_dump.h"
 
 #ifdef BLINKM
 #include "BlinkM.h"
@@ -137,12 +137,6 @@
 #include "sound.h"
 
 #include "cmdqueue.h"
-#include "io_atmega2560.h"
-
-// Macros for bit masks
-#define BIT(b) (1<<(b))
-#define TEST(n,b) (((n)&BIT(b))!=0)
-#define SET_BIT(n,b,value) (n) ^= ((-value)^(n)) & (BIT(b))
 
 //Macro for print fan speed
 #define FAN_PULSE_WIDTH_LIMIT ((fanSpeed > 100) ? 3 : 4) //time in ms
@@ -228,7 +222,7 @@ unsigned int heating_status;
 unsigned int heating_status_counter;
 bool loading_flag = false;
 
-
+#define XY_NO_RESTORE_FLAG (mesh_bed_leveling_flag || homing_flag)
 
 char snmm_filaments_used = 0;
 
@@ -237,10 +231,6 @@ bool fan_state[2];
 int fan_edge_counter[2];
 int fan_speed[2];
 
-char dir_names[3][9];
-
-bool sortAlpha = false;
-
 
 float extruder_multiplier[EXTRUDERS] = {1.0
   #if EXTRUDERS > 1
@@ -274,6 +264,7 @@ float extruder_offset[NUM_EXTRUDER_OFFSETS][EXTRUDERS] = {
 
 uint8_t active_extruder = 0;
 int fanSpeed=0;
+uint8_t newFanSpeed = 0;
 
 #ifdef FWRETRACT
   bool retracted[EXTRUDERS]={false
@@ -303,7 +294,7 @@ int fanSpeed=0;
 	  bool powersupply = true;
   #endif
 
-bool cancel_heatup = false ;
+bool cancel_heatup = false;
 
 int8_t busy_state = NOT_BUSY;
 static long prev_busy_signal_ms = -1;
@@ -311,12 +302,18 @@ uint8_t host_keepalive_interval = HOST_KEEPALIVE_INTERVAL;
 
 const char errormagic[] PROGMEM = "Error:";
 const char echomagic[] PROGMEM = "echo:";
+const char G28W0[] PROGMEM = "G28 W0";
 
 bool no_response = false;
 uint8_t important_status;
 uint8_t saved_filament_type;
 
-#define SAVED_TARGET_UNSET (X_MIN_POS-1)
+// Define some coordinates outside the clamp limits (making them invalid past the parsing stage) so
+// that they can be used later for various logical checks
+#define X_COORD_INVALID (X_MIN_POS-1)
+#define Y_COORD_INVALID (Y_MIN_POS-1)
+
+#define SAVED_TARGET_UNSET X_COORD_INVALID
 float saved_target[NUM_AXIS] = {SAVED_TARGET_UNSET, 0, 0, 0};
 
 // save/restore printing in case that mmu was not responding 
@@ -324,9 +321,13 @@ bool mmu_print_saved = false;
 
 // storing estimated time to end of print counted by slicer
 uint8_t print_percent_done_normal = PRINT_PERCENT_DONE_INIT;
-uint16_t print_time_remaining_normal = PRINT_TIME_REMAINING_INIT; //estimated remaining print time in minutes
 uint8_t print_percent_done_silent = PRINT_PERCENT_DONE_INIT;
+uint16_t print_time_remaining_normal = PRINT_TIME_REMAINING_INIT; //estimated remaining print time in minutes
 uint16_t print_time_remaining_silent = PRINT_TIME_REMAINING_INIT; //estimated remaining print time in minutes
+uint16_t print_time_to_change_normal = PRINT_TIME_REMAINING_INIT; //estimated remaining time to next change in minutes
+uint16_t print_time_to_change_silent = PRINT_TIME_REMAINING_INIT; //estimated remaining time to next change in minutes
+
+uint32_t IP_address = 0;
 
 //===========================================================================
 //=============================Private Variables=============================
@@ -374,7 +375,7 @@ bool target_direction;
 //Insert variables if CHDK is defined
 #ifdef CHDK
 unsigned long chdkHigh = 0;
-boolean chdkActive = false;
+bool chdkActive = false;
 #endif
 
 //! @name RAM save/restore printing
@@ -382,7 +383,7 @@ boolean chdkActive = false;
 bool saved_printing = false; //!< Print is paused and saved in RAM
 static uint32_t saved_sdpos = 0; //!< SD card position, or line number in case of USB printing
 uint8_t saved_printing_type = PRINTING_TYPE_SD;
-static float saved_pos[4] = { 0, 0, 0, 0 };
+static float saved_pos[4] = { X_COORD_INVALID, 0, 0, 0 };
 static uint16_t saved_feedrate2 = 0; //!< Default feedrate (truncated from float)
 static int saved_feedmultiply2 = 0;
 static uint8_t saved_active_extruder = 0;
@@ -393,6 +394,59 @@ static int saved_fanSpeed = 0; //!< Print fan speed
 
 static int saved_feedmultiply_mm = 100;
 
+class AutoReportFeatures {
+    union {
+          struct {
+            uint8_t temp : 1; //Temperature flag
+            uint8_t fans : 1; //Fans flag
+            uint8_t pos: 1;   //Position flag
+            uint8_t ar4 : 1;  //Unused
+            uint8_t ar5 : 1;  //Unused
+            uint8_t ar6 : 1;  //Unused
+            uint8_t ar7 : 1;  //Unused
+          } __attribute__((packed)) bits;
+          uint8_t byte;
+        } arFunctionsActive;
+    uint8_t auto_report_period;
+public:
+    LongTimer auto_report_timer;
+    AutoReportFeatures():auto_report_period(0){ 
+#if defined(AUTO_REPORT)
+        arFunctionsActive.byte = 0xff; 
+#else
+        arFunctionsActive.byte = 0;
+#endif //AUTO_REPORT
+    }
+    
+    inline bool Temp()const { return arFunctionsActive.bits.temp != 0; }
+    inline void SetTemp(uint8_t v){ arFunctionsActive.bits.temp = v; }
+
+    inline bool Fans()const { return arFunctionsActive.bits.fans != 0; }
+    inline void SetFans(uint8_t v){ arFunctionsActive.bits.fans = v; }
+
+    inline bool Pos()const { return arFunctionsActive.bits.pos != 0; }
+    inline void SetPos(uint8_t v){ arFunctionsActive.bits.pos = v; }
+    
+    inline void SetMask(uint8_t mask){ arFunctionsActive.byte = mask; }
+    
+    /// sets the autoreporting timer's period
+    /// setting it to zero stops the timer
+    void SetPeriod(uint8_t p){
+        auto_report_period = p;
+        if (auto_report_period != 0){
+          auto_report_timer.start();
+        } else{
+          auto_report_timer.stop();
+        }
+    }
+    
+    inline void TimerStart() { auto_report_timer.start(); }
+    inline bool TimerRunning()const { return auto_report_timer.running(); }
+    inline bool TimerExpired() { return auto_report_timer.expired(auto_report_period * 1000ul); }
+};
+
+AutoReportFeatures autoReportFeatures;
+
 //===========================================================================
 //=============================Routines======================================
 //===========================================================================
@@ -402,9 +456,14 @@ static bool setTargetedHotend(int code, uint8_t &extruder);
 static void print_time_remaining_init();
 static void wait_for_heater(long codenum, uint8_t extruder);
 static void gcode_G28(bool home_x_axis, bool home_y_axis, bool home_z_axis);
+static void gcode_M105(uint8_t extruder);
+
+#ifndef PINDA_THERMISTOR
 static void temp_compensation_start();
 static void temp_compensation_apply();
+#endif
 
+static uint8_t get_PRUSA_SN(char* SN);
 
 uint16_t gcode_in_progress = 0;
 uint16_t mcode_in_progress = 0;
@@ -540,19 +599,6 @@ void crashdet_restore_print_and_continue()
 //	babystep_apply();
 }
 
-
-void crashdet_stop_and_save_print2()
-{
-	cli();
-	planner_abort_hard(); //abort printing
-	cmdqueue_reset(); //empty cmdqueue
-	card.sdprinting = false;
-	card.closefile();
-  // Reset and re-enable the stepper timer just before the global interrupts are enabled.
-  st_reset_timer();
-	sei();
-}
-
 void crashdet_detected(uint8_t mask)
 {
 	st_synchronize();
@@ -602,7 +648,7 @@ void crashdet_detected(uint8_t mask)
 		enquecommand_P(PSTR("CRASH_RECOVER"));
 	}else{
 		setTargetHotend(0, active_extruder);
-		bool yesno = lcd_show_fullscreen_message_yes_no_and_wait_P(_i("Crash detected. Resume print?"), false);
+		bool yesno = lcd_show_fullscreen_message_yes_no_and_wait_P(_i("Crash detected. Resume print?"), false);////MSG_CRASH_RESUME c=20 r=3
 		lcd_update_enable(true);
 		if (yesno)
 		{
@@ -629,7 +675,7 @@ void crashdet_cancel()
 		lcd_print_stop();
 	}else if(saved_printing_type == PRINTING_TYPE_USB){
 		SERIAL_ECHOLNRPGM(MSG_OCTOPRINT_CANCEL); //for Octoprint: works the same as clicking "Abort" button in Octoprint GUI
-		SERIAL_PROTOCOLLNRPGM(MSG_OK);
+		cmdqueue_reset();
 	}
 }
 
@@ -648,6 +694,12 @@ void failstats_reset_print()
 #endif
 }
 
+void softReset()
+{
+    cli();
+    wdt_enable(WDTO_15MS);
+    while(1);
+}
 
 
 #ifdef MESH_BED_LEVELING
@@ -655,125 +707,83 @@ void failstats_reset_print()
 #endif
 
 
+static void factory_reset_stats(){
+    eeprom_update_dword((uint32_t *)EEPROM_TOTALTIME, 0);
+    eeprom_update_dword((uint32_t *)EEPROM_FILAMENTUSED, 0);
+
+    eeprom_update_byte((uint8_t *)EEPROM_CRASH_COUNT_X, 0);
+    eeprom_update_byte((uint8_t *)EEPROM_CRASH_COUNT_Y, 0);
+    eeprom_update_byte((uint8_t *)EEPROM_FERROR_COUNT, 0);
+    eeprom_update_byte((uint8_t *)EEPROM_POWER_COUNT, 0);
+
+    eeprom_update_word((uint16_t *)EEPROM_CRASH_COUNT_X_TOT, 0);
+    eeprom_update_word((uint16_t *)EEPROM_CRASH_COUNT_Y_TOT, 0);
+    eeprom_update_word((uint16_t *)EEPROM_FERROR_COUNT_TOT, 0);
+    eeprom_update_word((uint16_t *)EEPROM_POWER_COUNT_TOT, 0);
+
+    eeprom_update_word((uint16_t *)EEPROM_MMU_FAIL_TOT, 0);
+    eeprom_update_word((uint16_t *)EEPROM_MMU_LOAD_FAIL_TOT, 0);
+    eeprom_update_byte((uint8_t *)EEPROM_MMU_FAIL, 0);
+    eeprom_update_byte((uint8_t *)EEPROM_MMU_LOAD_FAIL, 0);
+}
+
 // Factory reset function
 // This function is used to erase parts or whole EEPROM memory which is used for storing calibration and and so on.
 // Level input parameter sets depth of reset
-int  er_progress = 0;
 static void factory_reset(char level)
-{	
+{
 	lcd_clear();
-    switch (level) {
-                   
-        // Level 0: Language reset
-        case 0:
-      Sound_MakeCustom(100,0,false);
-			lang_reset();
-            break;
-         
-		//Level 1: Reset statistics
-		case 1:
-      Sound_MakeCustom(100,0,false);
-			eeprom_update_dword((uint32_t *)EEPROM_TOTALTIME, 0);
-			eeprom_update_dword((uint32_t *)EEPROM_FILAMENTUSED, 0);
-
-			eeprom_update_byte((uint8_t *)EEPROM_CRASH_COUNT_X, 0);
-			eeprom_update_byte((uint8_t *)EEPROM_CRASH_COUNT_Y, 0);
-			eeprom_update_byte((uint8_t *)EEPROM_FERROR_COUNT, 0);
-			eeprom_update_byte((uint8_t *)EEPROM_POWER_COUNT, 0);
-
-			eeprom_update_word((uint16_t *)EEPROM_CRASH_COUNT_X_TOT, 0);
-			eeprom_update_word((uint16_t *)EEPROM_CRASH_COUNT_Y_TOT, 0);
-			eeprom_update_word((uint16_t *)EEPROM_FERROR_COUNT_TOT, 0);
-			eeprom_update_word((uint16_t *)EEPROM_POWER_COUNT_TOT, 0);
-
-			eeprom_update_word((uint16_t *)EEPROM_MMU_FAIL_TOT, 0);
-			eeprom_update_word((uint16_t *)EEPROM_MMU_LOAD_FAIL_TOT, 0);
-			eeprom_update_byte((uint8_t *)EEPROM_MMU_FAIL, 0);
-			eeprom_update_byte((uint8_t *)EEPROM_MMU_LOAD_FAIL, 0);
-
-
-			lcd_menu_statistics();
-            
-			break;
+	Sound_MakeCustom(100,0,false);
+	switch (level) {
 
-        // Level 2: Prepare for shipping
-        case 2:
-			//lcd_puts_P(PSTR("Factory RESET"));
-            //lcd_puts_at_P(1,2,PSTR("Shipping prep"));
-            
-            // Force language selection at the next boot up.
-			lang_reset();
-            // Force the "Follow calibration flow" message at the next boot up.
-            calibration_status_store(CALIBRATION_STATUS_Z_CALIBRATION);
-			eeprom_write_byte((uint8_t*)EEPROM_WIZARD_ACTIVE, 1); //run wizard
-            farm_no = 0;
-			farm_mode = false;
-			eeprom_update_byte((uint8_t*)EEPROM_FARM_MODE, farm_mode);
-            EEPROM_save_B(EEPROM_FARM_NUMBER, &farm_no);
-
-            eeprom_update_dword((uint32_t *)EEPROM_TOTALTIME, 0);
-            eeprom_update_dword((uint32_t *)EEPROM_FILAMENTUSED, 0);
-
-			eeprom_update_byte((uint8_t *)EEPROM_CRASH_COUNT_X, 0);
-			eeprom_update_byte((uint8_t *)EEPROM_CRASH_COUNT_Y, 0);
-			eeprom_update_byte((uint8_t *)EEPROM_FERROR_COUNT, 0);
-			eeprom_update_byte((uint8_t *)EEPROM_POWER_COUNT, 0);
-
-            eeprom_update_word((uint16_t *)EEPROM_CRASH_COUNT_X_TOT, 0);
-            eeprom_update_word((uint16_t *)EEPROM_CRASH_COUNT_Y_TOT, 0);
-            eeprom_update_word((uint16_t *)EEPROM_FERROR_COUNT_TOT, 0);
-            eeprom_update_word((uint16_t *)EEPROM_POWER_COUNT_TOT, 0);
-
-			eeprom_update_word((uint16_t *)EEPROM_MMU_FAIL_TOT, 0);
-			eeprom_update_word((uint16_t *)EEPROM_MMU_LOAD_FAIL_TOT, 0);
-			eeprom_update_byte((uint8_t *)EEPROM_MMU_FAIL, 0);
-			eeprom_update_byte((uint8_t *)EEPROM_MMU_LOAD_FAIL, 0);
+	case 0: // Level 0: Language reset
+		lang_reset();
+		break;
 
-#ifdef FILAMENT_SENSOR
-			fsensor_enable();
-            fsensor_autoload_set(true);
-#endif //FILAMENT_SENSOR
-      Sound_MakeCustom(100,0,false);   
-			//_delay_ms(2000);
-            break;
+	case 1: //Level 1: Reset statistics
+		factory_reset_stats();
+		lcd_menu_statistics();
+		break;
 
-			// Level 3: erase everything, whole EEPROM will be set to 0xFF
+	case 2: // Level 2: Prepare for shipping
+		factory_reset_stats();
+		// FALLTHRU
 
-		case 3:
-			lcd_puts_P(PSTR("Factory RESET"));
-			lcd_puts_at_P(1, 2, PSTR("ERASING all data"));
-
-      Sound_MakeCustom(100,0,false);
-			er_progress = 0;
-			lcd_puts_at_P(3, 3, PSTR("      "));
-			lcd_set_cursor(3, 3);
-			lcd_print(er_progress);
-
-			// Erase EEPROM
-			for (int i = 0; i < 4096; i++) {
-				eeprom_update_byte((uint8_t*)i, 0xFF);
-
-				if (i % 41 == 0) {
-					er_progress++;
-					lcd_puts_at_P(3, 3, PSTR("      "));
-					lcd_set_cursor(3, 3);
-					lcd_print(er_progress);
-					lcd_puts_P(PSTR("%"));
-				}
+	case 3: // Level 3: Preparation after being serviced
+		// Force language selection at the next boot up.
+		lang_reset();
+		// Force the "Follow calibration flow" message at the next boot up.
+		calibration_status_store(CALIBRATION_STATUS_Z_CALIBRATION);
+		eeprom_write_byte((uint8_t*)EEPROM_WIZARD_ACTIVE, 2); //run wizard
+		farm_mode = false;
+		eeprom_update_byte((uint8_t*)EEPROM_FARM_MODE, farm_mode);
 
-			}
+#ifdef FILAMENT_SENSOR
+		fsensor_enable();
+		fsensor_autoload_set(true);
+#endif //FILAMENT_SENSOR
+		break;
 
+	case 4:
+		menu_progressbar_init(EEPROM_TOP, PSTR("ERASING all data"));
+		// Erase EEPROM
+		for (uint16_t i = 0; i < EEPROM_TOP; i++) {
+			eeprom_update_byte((uint8_t*)i, 0xFF);
+			menu_progressbar_update(i);
+		}
+		menu_progressbar_finish();
+		softReset();
+		break;
 
-			break;
-		case 4:
-			bowden_menu();
-			break;
-        
-        default:
-            break;
-    }
-    
 
+#ifdef SNMM
+	case 5:
+		bowden_menu();
+		break;
+#endif
+	default:
+		break;
+	}
 }
 
 extern "C" {
@@ -804,30 +814,27 @@ void factory_reset()
 		{
 			lcd_clear();
 
-
 			lcd_puts_P(PSTR("Factory RESET"));
 
-
 			SET_OUTPUT(BEEPER);
-  if(eSoundMode!=e_SOUND_MODE_SILENT)
-			WRITE(BEEPER, HIGH);
+			if(eSoundMode!=e_SOUND_MODE_SILENT)
+				WRITE(BEEPER, HIGH);
 
 			while (!READ(BTN_ENC));
 
 			WRITE(BEEPER, LOW);
 
-
-
 			_delay_ms(2000);
 
 			char level = reset_menu();
 			factory_reset(level);
 
 			switch (level) {
-			case 0: _delay_ms(0); break;
-			case 1: _delay_ms(0); break;
-			case 2: _delay_ms(0); break;
-			case 3: _delay_ms(0); break;
+			case 0:
+			case 1:
+			case 2:
+			case 3:
+			case 4: _delay_ms(0); break;
 			}
 
 		}
@@ -864,17 +871,15 @@ static void check_if_fw_is_on_right_printer(){
 #ifdef FILAMENT_SENSOR
   if((PRINTER_TYPE == PRINTER_MK3) || (PRINTER_TYPE == PRINTER_MK3S)){
     #ifdef IR_SENSOR
-    swi2c_init();
-    const uint8_t pat9125_detected = swi2c_readByte_A8(PAT9125_I2C_ADDR,0x00,NULL);
-      if (pat9125_detected){
-        lcd_show_fullscreen_message_and_wait_P(_i("MK3S firmware detected on MK3 printer"));}////c=20 r=3
+      if (pat9125_probe()){
+        lcd_show_fullscreen_message_and_wait_P(_i("MK3S firmware detected on MK3 printer"));}////MSG_MK3S_FIRMWARE_ON_MK3 c=20 r=4
     #endif //IR_SENSOR
 
     #ifdef PAT9125
       //will return 1 only if IR can detect filament in bondtech extruder so this may fail even when we have IR sensor
-      const uint8_t ir_detected = !(PIN_GET(IR_SENSOR_PIN));
+      const uint8_t ir_detected = !READ(IR_SENSOR_PIN);
       if (ir_detected){
-        lcd_show_fullscreen_message_and_wait_P(_i("MK3 firmware detected on MK3S printer"));}////c=20 r=3
+        lcd_show_fullscreen_message_and_wait_P(_i("MK3 firmware detected on MK3S printer"));}////MSG_MK3_FIRMWARE_ON_MK3S c=20 r=4
     #endif //PAT9125
   }
 #endif //FILAMENT_SENSOR
@@ -903,7 +908,7 @@ uint8_t check_printer_version()
 
 #if (LANG_MODE != 0) //secondary language support
 
-#ifdef W25X20CL
+#ifdef XFLASH
 
 
 // language update from external flash
@@ -929,7 +934,7 @@ void update_sec_lang_from_external_flash()
 				cli();
 				uint16_t size = header.size - state * LANGBOOT_BLOCKSIZE;
 				if (size > LANGBOOT_BLOCKSIZE) size = LANGBOOT_BLOCKSIZE;
-				w25x20cl_rd_data(src_addr + state * LANGBOOT_BLOCKSIZE, (uint8_t*)LANGBOOT_RAMBUFFER, size);
+				xflash_rd_data(src_addr + state * LANGBOOT_BLOCKSIZE, (uint8_t*)LANGBOOT_RAMBUFFER, size);
 				if (state == 0)
 				{
 					//TODO - check header integrity
@@ -947,7 +952,7 @@ void update_sec_lang_from_external_flash()
 }
 
 
-#ifdef DEBUG_W25X20CL
+#ifdef DEBUG_XFLASH
 
 uint8_t lang_xflash_enum_codes(uint16_t* codes)
 {
@@ -957,13 +962,13 @@ uint8_t lang_xflash_enum_codes(uint16_t* codes)
 	while (1)
 	{
 		printf_P(_n("LANGTABLE%d:"), count);
-		w25x20cl_rd_data(addr, (uint8_t*)&header, sizeof(lang_table_header_t));
+		xflash_rd_data(addr, (uint8_t*)&header, sizeof(lang_table_header_t));
 		if (header.magic != LANG_MAGIC)
 		{
-			printf_P(_n("NG!\n"));
+			puts_P(_n("NG!"));
 			break;
 		}
-		printf_P(_n("OK\n"));
+		puts_P(_n("OK"));
 		printf_P(_n(" _lt_magic        = 0x%08lx %S\n"), header.magic, (header.magic==LANG_MAGIC)?_n("OK"):_n("NA"));
 		printf_P(_n(" _lt_size         = 0x%04x (%d)\n"), header.size, header.size);
 		printf_P(_n(" _lt_count        = 0x%04x (%d)\n"), header.count, header.count);
@@ -985,17 +990,70 @@ void list_sec_lang_from_external_flash()
 	printf_P(_n("XFlash lang count = %hhd\n"), count);
 }
 
-#endif //DEBUG_W25X20CL
+#endif //DEBUG_XFLASH
 
-#endif //W25X20CL
+#endif //XFLASH
 
 #endif //(LANG_MODE != 0)
 
 
-static void w25x20cl_err_msg()
+static void fw_crash_init()
+{
+#ifdef XFLASH_DUMP
+    dump_crash_reason crash_reason;
+    if(xfdump_check_state(&crash_reason))
+    {
+        // always signal to the host that a dump is available for retrieval
+        puts_P(_N("// action:dump_available"));
+
+#ifdef EMERGENCY_DUMP
+        if(crash_reason != dump_crash_reason::manual &&
+           eeprom_read_byte((uint8_t*)EEPROM_FW_CRASH_FLAG) != 0xFF)
+        {
+            lcd_show_fullscreen_message_and_wait_P(
+                    _i("FW crash detected! "
+                       "You can continue printing. "
+                       "Debug data available for analysis. "
+                       "Contact support to submit details."));
+        }
+#endif
+    }
+#else //XFLASH_DUMP
+    dump_crash_reason crash_reason = (dump_crash_reason)eeprom_read_byte((uint8_t*)EEPROM_FW_CRASH_FLAG);
+    if(crash_reason != dump_crash_reason::manual && (uint8_t)crash_reason != 0xFF)
+    {
+        lcd_beeper_quick_feedback();
+        lcd_clear();
+
+        lcd_puts_P(_i("FIRMWARE CRASH!\nCrash reason:\n"));
+        switch(crash_reason)
+        {
+        case dump_crash_reason::stack_error:
+            lcd_puts_P(_i("Static memory has\nbeen overwritten"));
+            break;
+        case dump_crash_reason::watchdog:
+            lcd_puts_P(_i("Watchdog timeout"));
+            break;
+        case dump_crash_reason::bad_isr:
+            lcd_puts_P(_i("Bad interrupt"));
+            break;
+        default:
+            lcd_print((uint8_t)crash_reason);
+            break;
+        }
+        lcd_wait_for_click();
+    }
+#endif //XFLASH_DUMP
+
+    // prevent crash prompts to reappear once acknowledged
+    eeprom_update_byte((uint8_t*)EEPROM_FW_CRASH_FLAG, 0xFF);
+}
+
+
+static void xflash_err_msg()
 {
 	lcd_clear();
-	lcd_puts_P(_n("External SPI flash\nW25X20CL is not res-\nponding. Language\nswitch unavailable."));
+	lcd_puts_P(_n("External SPI flash\nXFLASH is not res-\nponding. Language\nswitch unavailable."));
 }
 
 // "Setup" function is called by the Arduino framework on startup.
@@ -1003,6 +1061,8 @@ static void w25x20cl_err_msg()
 // are initialized by the main() routine provided by the Arduino framework.
 void setup()
 {
+	timer2_init(); // enables functional millis
+
 	mmu_init();
 
 	ultralcd_init();
@@ -1019,45 +1079,40 @@ void setup()
 	fdev_setup_stream(uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE); //setup uart out stream
 	stdout = uartout;
 
-#ifdef W25X20CL
-    bool w25x20cl_success = w25x20cl_init();
+#ifdef XFLASH
+    bool xflash_success = xflash_init();
 	uint8_t optiboot_status = 1;
-	if (w25x20cl_success)
+	if (xflash_success)
 	{
-		optiboot_status = optiboot_w25x20cl_enter();
+		optiboot_status = optiboot_xflash_enter();
 #if (LANG_MODE != 0) //secondary language support
         update_sec_lang_from_external_flash();
 #endif //(LANG_MODE != 0)
 	}
 	else
 	{
-	    w25x20cl_err_msg();
+	    xflash_err_msg();
 	}
 #else
-	const bool w25x20cl_success = true;
-#endif //W25X20CL
+	const bool xflash_success = true;
+#endif //XFLASH
 
 
 	setup_killpin();
 	setup_powerhold();
 
 	farm_mode = eeprom_read_byte((uint8_t*)EEPROM_FARM_MODE); 
-	EEPROM_read_B(EEPROM_FARM_NUMBER, &farm_no);
-	if ((farm_mode == 0xFF && farm_no == 0) || ((uint16_t)farm_no == 0xFFFF)) 
+	if (farm_mode == 0xFF) 
 		farm_mode = false; //if farm_mode has not been stored to eeprom yet and farm number is set to zero or EEPROM is fresh, deactivate farm mode
-	if ((uint16_t)farm_no == 0xFFFF) farm_no = 0;
 	if (farm_mode)
 	{
 		no_response = true; //we need confirmation by recieving PRUSA thx
 		important_status = 8;
 		prusa_statistics(8);
+#ifdef HAS_SECOND_SERIAL_PORT
 		selectedSerialPort = 1;
+#endif //HAS_SECOND_SERIAL_PORT
 		MYSERIAL.begin(BAUDRATE);
-#ifdef TMC2130
-		//increased extruder current (PFW363)
-		tmc2130_current_h[E_AXIS] = 36;
-		tmc2130_current_r[E_AXIS] = 36;
-#endif //TMC2130
 #ifdef FILAMENT_SENSOR
 		//disabled filament autoload (PFW360)
 		fsensor_autoload_set(false);
@@ -1066,14 +1121,41 @@ void setup()
           if(!(eeprom_read_byte((uint8_t*)EEPROM_FAN_CHECK_ENABLED)))
                eeprom_update_byte((unsigned char *)EEPROM_FAN_CHECK_ENABLED,true);
 	}
-#ifndef W25X20CL
+
+#ifdef TMC2130
+    if( FarmOrUserECool() ){
+		//increased extruder current (PFW363)
+		tmc2130_current_h[E_AXIS] = TMC2130_CURRENTS_FARM;
+		tmc2130_current_r[E_AXIS] = TMC2130_CURRENTS_FARM;
+    }
+#endif //TMC2130
+
+    //Check for valid SN in EEPROM. Try to retrieve it in case it's invalid.
+    //SN is valid only if it is NULL terminated and starts with "CZPX".
+    {
+        char SN[20];
+        eeprom_read_block(SN, (uint8_t*)EEPROM_PRUSA_SN, 20);
+        if (SN[19] || strncmp_P(SN, PSTR("CZPX"), 4))
+        {
+            if (!get_PRUSA_SN(SN))
+            {
+                eeprom_update_block(SN, (uint8_t*)EEPROM_PRUSA_SN, 20);
+                puts_P(PSTR("SN updated"));
+            }
+            else
+                puts_P(PSTR("SN update failed"));
+        }
+    }
+
+
+#ifndef XFLASH
 	SERIAL_PROTOCOLLNPGM("start");
 #else
 	if ((optiboot_status != 0) || (selectedSerialPort != 0))
 		SERIAL_PROTOCOLLNPGM("start");
 #endif
 	SERIAL_ECHO_START;
-	printf_P(PSTR(" " FW_VERSION_FULL "\n"));
+	puts_P(PSTR(" " FW_VERSION_FULL));
 
 	//SERIAL_ECHOPAIR("Active sheet before:", static_cast<unsigned long int>(eeprom_read_byte(&(EEPROM_Sheets_base->active_sheet))));
 
@@ -1147,7 +1229,7 @@ void setup()
 #undef LT_PRINT_TEST
 
 #if 0
-		w25x20cl_rd_data(0x25ba, (uint8_t*)&block_buffer, 1024);
+		xflash_rd_data(0x25ba, (uint8_t*)&block_buffer, 1024);
 		for (uint16_t i = 0; i < 1024; i++)
 		{
 			if ((i % 16) == 0) printf_P(_n("%04x:"), 0x25ba+i);
@@ -1163,12 +1245,12 @@ void setup()
 		printf_P(_n("_SEC_LANG_TABLE checksum = %04x\n"), sum);
 		sum = (sum >> 8) | ((sum & 0xff) << 8); //swap bytes
 		if (sum == header.checksum)
-			printf_P(_n("Checksum OK\n"), sum);
+			puts_P(_n("Checksum OK"), sum);
 		else
-			printf_P(_n("Checksum NG\n"), sum);
+			puts_P(_n("Checksum NG"), sum);
 	}
 	else
-		printf_P(_n("lang_get_header failed!\n"));
+		puts_P(_n("lang_get_header failed!"));
 
 #if 0
 		for (uint16_t i = 0; i < 1024*10; i++)
@@ -1244,11 +1326,11 @@ void setup()
 
 	tp_init();    // Initialize temperature loop
 
-	if (w25x20cl_success) lcd_splash(); // we need to do this again, because tp_init() kills lcd
+	if (xflash_success) lcd_splash(); // we need to do this again, because tp_init() kills lcd
 	else
 	{
-	    w25x20cl_err_msg();
-	    printf_P(_n("W25X20CL not responding.\n"));
+	    xflash_err_msg();
+	    puts_P(_n("XFLASH not responding."));
 	}
 #ifdef EXTRUDER_ALTFAN_DETECT
 	SERIAL_ECHORPGM(_n("Extruder fan type: "));
@@ -1320,13 +1402,12 @@ void setup()
 #endif //TMC2130_VARIABLE_RESOLUTION
 
 #endif //TMC2130
-
 	st_init();    // Initialize stepper, this enables interrupts!
   
 #ifdef TMC2130
 	tmc2130_mode = silentMode?TMC2130_MODE_SILENT:TMC2130_MODE_NORMAL;
 	update_mode_profile();
-	tmc2130_init();
+	tmc2130_init(TMCInitParams(false, FarmOrUserECool() ));
 #endif //TMC2130
 #ifdef PSU_Delta
      init_force_z();                              // ! important for correct Z-axis initialization
@@ -1361,9 +1442,7 @@ void setup()
 #endif
 
 	farm_mode = eeprom_read_byte((uint8_t*)EEPROM_FARM_MODE);
-	EEPROM_read_B(EEPROM_FARM_NUMBER, &farm_no);
-	if ((farm_mode == 0xFF && farm_no == 0) || (farm_no == static_cast<int>(0xFFFF))) farm_mode = false; //if farm_mode has not been stored to eeprom yet and farm number is set to zero or EEPROM is fresh, deactivate farm mode
-	if (farm_no == static_cast<int>(0xFFFF)) farm_no = 0;
+	if (farm_mode == 0xFF) farm_mode = false; //if farm_mode has not been stored to eeprom yet and farm number is set to zero or EEPROM is fresh, deactivate farm mode
 	if (farm_mode)
 	{
 		prusa_statistics(8);
@@ -1435,16 +1514,16 @@ void setup()
 
 #if (LANG_MODE != 0) //secondary language support
 
-#ifdef DEBUG_W25X20CL
-	W25X20CL_SPI_ENTER();
+#ifdef DEBUG_XFLASH
+	XFLASH_SPI_ENTER();
 	uint8_t uid[8]; // 64bit unique id
-	w25x20cl_rd_uid(uid);
-	puts_P(_n("W25X20CL UID="));
+	xflash_rd_uid(uid);
+	puts_P(_n("XFLASH UID="));
 	for (uint8_t i = 0; i < 8; i ++)
 		printf_P(PSTR("%02hhx"), uid[i]);
 	putchar('\n');
 	list_sec_lang_from_external_flash();
-#endif //DEBUG_W25X20CL
+#endif //DEBUG_XFLASH
 
 //	lang_reset();
 	if (!lang_select(eeprom_read_byte((uint8_t*)EEPROM_LANG)))
@@ -1523,10 +1602,10 @@ void setup()
   }
 
   if (!previous_settings_retrieved) {
-	  lcd_show_fullscreen_message_and_wait_P(_i("Old settings found. Default PID, Esteps etc. will be set.")); //if EEPROM version or printer type was changed, inform user that default setting were loaded////MSG_DEFAULT_SETTINGS_LOADED c=20 r=5
+	  lcd_show_fullscreen_message_and_wait_P(_i("Old settings found. Default PID, Esteps etc. will be set.")); //if EEPROM version or printer type was changed, inform user that default setting were loaded////MSG_DEFAULT_SETTINGS_LOADED c=20 r=6
 	  Config_StoreSettings();
   }
-  if (eeprom_read_byte((uint8_t*)EEPROM_WIZARD_ACTIVE) == 1) {
+  if (eeprom_read_byte((uint8_t*)EEPROM_WIZARD_ACTIVE) >= 1) {
 	  lcd_wizard(WizState::Run);
   }
   if (eeprom_read_byte((uint8_t*)EEPROM_WIZARD_ACTIVE) == 0) { //dont show calibration status messages if wizard is currently active
@@ -1589,6 +1668,9 @@ void setup()
 	if (tmc2130_home_enabled == 0xff) tmc2130_home_enabled = 0;
 #endif //TMC2130
 
+    // report crash failures
+    fw_crash_init();
+
 #ifdef UVLO_SUPPORT
   if (eeprom_read_byte((uint8_t*)EEPROM_UVLO) != 0) { //previous print was terminated by UVLO
 /*
@@ -1634,10 +1716,45 @@ void setup()
   KEEPALIVE_STATE(NOT_BUSY);
 #ifdef WATCHDOG
   wdt_enable(WDTO_4S);
+#ifdef EMERGENCY_HANDLERS
+  WDTCSR |= (1 << WDIE);
+#endif //EMERGENCY_HANDLERS
 #endif //WATCHDOG
 }
 
 
+static inline void crash_and_burn(dump_crash_reason reason)
+{
+    WRITE(BEEPER, HIGH);
+    eeprom_update_byte((uint8_t*)EEPROM_FW_CRASH_FLAG, (uint8_t)reason);
+#ifdef EMERGENCY_DUMP
+    xfdump_full_dump_and_reset(reason);
+#elif defined(EMERGENCY_SERIAL_DUMP)
+    if(emergency_serial_dump)
+        serial_dump_and_reset(reason);
+#endif
+    softReset();
+}
+
+#ifdef EMERGENCY_HANDLERS
+#ifdef WATCHDOG
+ISR(WDT_vect)
+{
+    crash_and_burn(dump_crash_reason::watchdog);
+}
+#endif
+
+ISR(BADISR_vect)
+{
+    crash_and_burn(dump_crash_reason::bad_isr);
+}
+#endif //EMERGENCY_HANDLERS
+
+void stack_error() {
+    crash_and_burn(dump_crash_reason::stack_error);
+}
+
+
 void trace();
 
 #define CHUNK_SIZE 64 // bytes
@@ -1710,6 +1827,32 @@ void serial_read_stream() {
     }
 }
 
+
+/**
+ * Output autoreport values according to features requested in M155
+ */
+#if defined(AUTO_REPORT)
+static void host_autoreport()
+{
+    if (autoReportFeatures.TimerExpired())
+    {
+        if(autoReportFeatures.Temp()){
+            gcode_M105(active_extruder);
+        }
+        if(autoReportFeatures.Pos()){
+            gcode_M114();
+        }
+#if defined(AUTO_REPORT) && (defined(FANCHECK) && (((defined(TACH_0) && (TACH_0 >-1)) || (defined(TACH_1) && (TACH_1 > -1)))))
+        if(autoReportFeatures.Fans()){
+            gcode_M123();
+        }
+#endif //AUTO_REPORT and (FANCHECK and TACH_0 or TACH_1)
+        autoReportFeatures.TimerStart();
+    }
+}
+#endif //AUTO_REPORT
+
+
 /**
 * Output a "busy" message at regular intervals
 * while the machine is not accepting commands.
@@ -1720,6 +1863,7 @@ void host_keepalive() {
 #endif //HOST_KEEPALIVE_FEATURE
   if (farm_mode) return;
   long ms = _millis();
+
   if (host_keepalive_interval && busy_state != NOT_BUSY) {
     if ((ms - prev_busy_signal_ms) < (long)(1000L * host_keepalive_interval)) return;
      switch (busy_state) {
@@ -1766,8 +1910,7 @@ void loop()
 	}
     
 #ifdef FANCHECK
-    if (fan_check_error && isPrintPaused)
-    {
+    if (fan_check_error && isPrintPaused && !IS_SD_PRINTING) {
         KEEPALIVE_STATE(PAUSED_FOR_USER);
         host_keepalive(); //prevent timeouts since usb processing is disabled until print is resumed. This is for a crude way of pausing a print on all hosts.
     }
@@ -2105,7 +2248,7 @@ bool check_commands() {
 	
 		while (buflen)
 		{
-		if ((code_seen("M84")) || (code_seen("M 84"))) end_command_found = true;
+		if ((code_seen_P(PSTR("M84"))) || (code_seen_P(PSTR("M 84")))) end_command_found = true;
 		if (!cmdbuffer_front_already_processed)
 			 cmdqueue_pop_front();
 		cmdbuffer_front_already_processed = false;
@@ -2377,38 +2520,44 @@ void refresh_cmd_timeout(void)
 }
 
 #ifdef FWRETRACT
-  void retract(bool retracting, bool swapretract = false) {
+void retract(bool retracting, bool swapretract = false) {
+    // Perform FW retraction, just if needed, but behave as if the move has never took place in
+    // order to keep E/Z coordinates unchanged. This is done by manipulating the internal planner
+    // position, which requires a sync
     if(retracting && !retracted[active_extruder]) {
-      destination[X_AXIS]=current_position[X_AXIS];
-      destination[Y_AXIS]=current_position[Y_AXIS];
-      destination[Z_AXIS]=current_position[Z_AXIS];
-      destination[E_AXIS]=current_position[E_AXIS];
-      current_position[E_AXIS]+=(swapretract?retract_length_swap:cs.retract_length)*float(extrudemultiply)*0.01f;
-      plan_set_e_position(current_position[E_AXIS]);
-      float oldFeedrate = feedrate;
-      feedrate=cs.retract_feedrate*60;
-      retracted[active_extruder]=true;
-      prepare_move();
-      current_position[Z_AXIS]-=cs.retract_zlift;
-      plan_set_position_curposXYZE();
-      prepare_move();
-      feedrate = oldFeedrate;
+        st_synchronize();
+        set_destination_to_current();
+        current_position[E_AXIS]+=(swapretract?retract_length_swap:cs.retract_length)*float(extrudemultiply)*0.01f;
+        plan_set_e_position(current_position[E_AXIS]);
+        float oldFeedrate = feedrate;
+        feedrate=cs.retract_feedrate*60;
+        retracted[active_extruder]=true;
+        prepare_move();
+        if(cs.retract_zlift) {
+            st_synchronize();
+            current_position[Z_AXIS]-=cs.retract_zlift;
+            plan_set_position_curposXYZE();
+            prepare_move();
+        }
+        feedrate = oldFeedrate;
     } else if(!retracting && retracted[active_extruder]) {
-      destination[X_AXIS]=current_position[X_AXIS];
-      destination[Y_AXIS]=current_position[Y_AXIS];
-      destination[Z_AXIS]=current_position[Z_AXIS];
-      destination[E_AXIS]=current_position[E_AXIS];
-      current_position[Z_AXIS]+=cs.retract_zlift;
-      plan_set_position_curposXYZE();
-      current_position[E_AXIS]-=(swapretract?(retract_length_swap+retract_recover_length_swap):(cs.retract_length+cs.retract_recover_length))*float(extrudemultiply)*0.01f;
-      plan_set_e_position(current_position[E_AXIS]);
-      float oldFeedrate = feedrate;
-      feedrate=cs.retract_recover_feedrate*60;
-      retracted[active_extruder]=false;
-      prepare_move();
-      feedrate = oldFeedrate;
-    }
-  } //retract
+        st_synchronize();
+        set_destination_to_current();
+        float oldFeedrate = feedrate;
+        feedrate=cs.retract_recover_feedrate*60;
+        if(cs.retract_zlift) {
+            current_position[Z_AXIS]+=cs.retract_zlift;
+            plan_set_position_curposXYZE();
+            prepare_move();
+            st_synchronize();
+        }
+        current_position[E_AXIS]-=(swapretract?(retract_length_swap+retract_recover_length_swap):(cs.retract_length+cs.retract_recover_length))*float(extrudemultiply)*0.01f;
+        plan_set_e_position(current_position[E_AXIS]);
+        retracted[active_extruder]=false;
+        prepare_move();
+        feedrate = oldFeedrate;
+    }
+} //retract
 #endif //FWRETRACT
 
 void trace() {
@@ -2514,7 +2663,7 @@ void force_high_power_mode(bool start_high_power_section) {
 		cli();
 		tmc2130_mode = (start_high_power_section == true) ? TMC2130_MODE_NORMAL : TMC2130_MODE_SILENT;
 		update_mode_profile();
-		tmc2130_init();
+		tmc2130_init(TMCInitParams(FarmOrUserECool()));
     // We may have missed a stepper timer interrupt due to the time spent in the tmc2130_init() routine.
     // Be safe than sorry, reset the stepper timer before re-enabling interrupts.
     st_reset_timer();
@@ -2523,22 +2672,110 @@ void force_high_power_mode(bool start_high_power_section) {
 }
 #endif //TMC2130
 
+void gcode_M105(uint8_t extruder)
+{
+#if defined(TEMP_0_PIN) && TEMP_0_PIN > -1
+    SERIAL_PROTOCOLPGM("T:");
+    SERIAL_PROTOCOL_F(degHotend(extruder),1);
+    SERIAL_PROTOCOLPGM(" /");
+    SERIAL_PROTOCOL_F(degTargetHotend(extruder),1);
+#if defined(TEMP_BED_PIN) && TEMP_BED_PIN > -1
+    SERIAL_PROTOCOLPGM(" B:");
+    SERIAL_PROTOCOL_F(degBed(),1);
+    SERIAL_PROTOCOLPGM(" /");
+    SERIAL_PROTOCOL_F(degTargetBed(),1);
+#endif //TEMP_BED_PIN
+    for (int8_t cur_extruder = 0; cur_extruder < EXTRUDERS; ++cur_extruder) {
+        SERIAL_PROTOCOLPGM(" T");
+        SERIAL_PROTOCOL(cur_extruder);
+        SERIAL_PROTOCOL(':');
+        SERIAL_PROTOCOL_F(degHotend(cur_extruder),1);
+        SERIAL_PROTOCOLPGM(" /");
+        SERIAL_PROTOCOL_F(degTargetHotend(cur_extruder),1);
+    }
+#else
+    SERIAL_ERROR_START;
+    SERIAL_ERRORLNRPGM(_i("No thermistors - no temperature"));////MSG_ERR_NO_THERMISTORS
+#endif
+
+    SERIAL_PROTOCOLPGM(" @:");
+#ifdef EXTRUDER_WATTS
+    SERIAL_PROTOCOL((EXTRUDER_WATTS * getHeaterPower(tmp_extruder))/127);
+    SERIAL_PROTOCOLPGM("W");
+#else
+    SERIAL_PROTOCOL(getHeaterPower(extruder));
+#endif
+
+    SERIAL_PROTOCOLPGM(" B@:");
+#ifdef BED_WATTS
+    SERIAL_PROTOCOL((BED_WATTS * getHeaterPower(-1))/127);
+    SERIAL_PROTOCOLPGM("W");
+#else
+    SERIAL_PROTOCOL(getHeaterPower(-1));
+#endif
+
+#ifdef PINDA_THERMISTOR
+    SERIAL_PROTOCOLPGM(" P:");
+    SERIAL_PROTOCOL_F(current_temperature_pinda,1);
+#endif //PINDA_THERMISTOR
+
+#ifdef AMBIENT_THERMISTOR
+    SERIAL_PROTOCOLPGM(" A:");
+    SERIAL_PROTOCOL_F(current_temperature_ambient,1);
+#endif //AMBIENT_THERMISTOR
+
+
+#ifdef SHOW_TEMP_ADC_VALUES
+    {
+        float raw = 0.0;
+#if defined(TEMP_BED_PIN) && TEMP_BED_PIN > -1
+        SERIAL_PROTOCOLPGM("    ADC B:");
+        SERIAL_PROTOCOL_F(degBed(),1);
+        SERIAL_PROTOCOLPGM("C->");
+        raw = rawBedTemp();
+        SERIAL_PROTOCOL_F(raw/OVERSAMPLENR,5);
+        SERIAL_PROTOCOLPGM(" Rb->");
+        SERIAL_PROTOCOL_F(100 * (1 + (PtA * (raw/OVERSAMPLENR)) + (PtB * sq((raw/OVERSAMPLENR)))), 5);
+        SERIAL_PROTOCOLPGM(" Rxb->");
+        SERIAL_PROTOCOL_F(raw, 5);
+#endif
+        for (int8_t cur_extruder = 0; cur_extruder < EXTRUDERS; ++cur_extruder) {
+            SERIAL_PROTOCOLPGM("  T");
+            SERIAL_PROTOCOL(cur_extruder);
+            SERIAL_PROTOCOLPGM(":");
+            SERIAL_PROTOCOL_F(degHotend(cur_extruder),1);
+            SERIAL_PROTOCOLPGM("C->");
+            raw = rawHotendTemp(cur_extruder);
+            SERIAL_PROTOCOL_F(raw/OVERSAMPLENR,5);
+            SERIAL_PROTOCOLPGM(" Rt");
+            SERIAL_PROTOCOL(cur_extruder);
+            SERIAL_PROTOCOLPGM("->");
+            SERIAL_PROTOCOL_F(100 * (1 + (PtA * (raw/OVERSAMPLENR)) + (PtB * sq((raw/OVERSAMPLENR)))), 5);
+            SERIAL_PROTOCOLPGM(" Rx");
+            SERIAL_PROTOCOL(cur_extruder);
+            SERIAL_PROTOCOLPGM("->");
+            SERIAL_PROTOCOL_F(raw, 5);
+        }
+    }
+#endif
+    SERIAL_PROTOCOLLN();
+}
+
 #ifdef TMC2130
 static void gcode_G28(bool home_x_axis, long home_x_value, bool home_y_axis, long home_y_value, bool home_z_axis, long home_z_value, bool calib, bool without_mbl)
 #else
 static void gcode_G28(bool home_x_axis, long home_x_value, bool home_y_axis, long home_y_value, bool home_z_axis, long home_z_value, bool without_mbl)
 #endif //TMC2130
 {
+	// Flag for the display update routine and to disable the print cancelation during homing.
 	st_synchronize();
+	homing_flag = true;
 
 #if 0
 	SERIAL_ECHOPGM("G28, initial ");  print_world_coordinates();
 	SERIAL_ECHOPGM("G28, initial ");  print_physical_coordinates();
 #endif
 
-	// Flag for the display update routine and to disable the print cancelation during homing.
-	homing_flag = true;
-
 	// Which axes should be homed?
 	bool home_x = home_x_axis;
 	bool home_y = home_y_axis;
@@ -2574,8 +2811,7 @@ static void gcode_G28(bool home_x_axis, long home_x_value, bool home_y_axis, lon
       current_position[Z_AXIS] = st_get_position_mm(Z_AXIS);
 #endif
 
-      // Reset baby stepping to zero, if the babystepping has already been loaded before. The babystepsTodo value will be
-      // consumed during the first movements following this statement.
+      // Reset baby stepping to zero, if the babystepping has already been loaded before.
       if (home_z)
         babystep_undo();
 
@@ -2782,24 +3018,21 @@ static void gcode_G28(bool home_x_axis, long home_x_value, bool home_y_axis, lon
 
 #if (defined(MESH_BED_LEVELING) && !defined(MK1BP))
 	if (home_x_axis || home_y_axis || without_mbl || home_z_axis)
-		{
+    {
       if (! home_z && mbl_was_active) {
         // Re-enable the mesh bed leveling if only the X and Y axes were re-homed.
         mbl.active = true;
         // and re-adjust the current logical Z axis with the bed leveling offset applicable at the current XY position.
         current_position[Z_AXIS] -= mbl.get_z(st_get_position_mm(X_AXIS), st_get_position_mm(Y_AXIS));
       }
-		}
-	else
-		{
-			st_synchronize();
-			homing_flag = false;
-	  }
+    }
 #endif
 
 	  if (farm_mode) { prusa_statistics(20); };
 
+      st_synchronize();
 	  homing_flag = false;
+
 #if 0
       SERIAL_ECHOPGM("G28, final ");  print_world_coordinates();
       SERIAL_ECHOPGM("G28, final ");  print_physical_coordinates();
@@ -2816,24 +3049,440 @@ static void gcode_G28(bool home_x_axis, bool home_y_axis, bool home_z_axis)
 #endif //TMC2130
 }
 
-void adjust_bed_reset()
-{
-	eeprom_update_byte((unsigned char*)EEPROM_BED_CORRECTION_VALID, 1);
-	eeprom_update_byte((unsigned char*)EEPROM_BED_CORRECTION_LEFT, 0);
-	eeprom_update_byte((unsigned char*)EEPROM_BED_CORRECTION_RIGHT, 0);
-	eeprom_update_byte((unsigned char*)EEPROM_BED_CORRECTION_FRONT, 0);
-	eeprom_update_byte((unsigned char*)EEPROM_BED_CORRECTION_REAR, 0);
-}
 
-//! @brief Calibrate XYZ
-//! @param onlyZ if true, calibrate only Z axis
-//! @param verbosity_level
-//! @retval true Succeeded
-//! @retval false Failed
-bool gcode_M45(bool onlyZ, int8_t verbosity_level)
+// G80 - Automatic mesh bed leveling
+static void gcode_G80()
 {
-	bool final_result = false;
-	#ifdef TMC2130
+    st_synchronize();
+    if (waiting_inside_plan_buffer_line_print_aborted)
+        return;
+
+    mesh_bed_leveling_flag = true;
+#ifndef PINDA_THERMISTOR
+    static bool run = false; // thermistor-less PINDA temperature compensation is running
+#endif // ndef PINDA_THERMISTOR
+
+#ifdef SUPPORT_VERBOSITY
+    int8_t verbosity_level = 0;
+    if (code_seen('V')) {
+        // Just 'V' without a number counts as V1.
+        char c = strchr_pointer[1];
+        verbosity_level = (c == ' ' || c == '\t' || c == 0) ? 1 : code_value_short();
+    }
+#endif //SUPPORT_VERBOSITY
+    // Firstly check if we know where we are
+    if (!(axis_known_position[X_AXIS] && axis_known_position[Y_AXIS] && axis_known_position[Z_AXIS])) {
+        // We don't know where we are! HOME!
+        // Push the commands to the front of the message queue in the reverse order!
+        // There shall be always enough space reserved for these commands.
+        repeatcommand_front(); // repeat G80 with all its parameters
+        enquecommand_front_P(G28W0);
+        return;
+    }
+
+    uint8_t nMeasPoints = MESH_MEAS_NUM_X_POINTS;
+    if (code_seen('N')) {
+        nMeasPoints = code_value_uint8();
+        if (nMeasPoints != 7) {
+            nMeasPoints = 3;
+        }
+    }
+    else {
+        nMeasPoints = eeprom_read_byte((uint8_t*)EEPROM_MBL_POINTS_NR);
+    }
+
+    uint8_t nProbeRetry = 3;
+    if (code_seen('R')) {
+        nProbeRetry = code_value_uint8();
+        if (nProbeRetry > 10) {
+            nProbeRetry = 10;
+        }
+    }
+    else {
+        nProbeRetry = eeprom_read_byte((uint8_t*)EEPROM_MBL_PROBE_NR);
+    }
+    bool magnet_elimination = (eeprom_read_byte((uint8_t*)EEPROM_MBL_MAGNET_ELIMINATION) > 0);
+
+#ifndef PINDA_THERMISTOR
+    if (run == false && temp_cal_active == true && calibration_status_pinda() == true && target_temperature_bed >= 50)
+    {
+        temp_compensation_start();
+        run = true;
+        repeatcommand_front(); // repeat G80 with all its parameters
+        enquecommand_front_P(G28W0);
+        break;
+    }
+    run = false;
+#endif //PINDA_THERMISTOR
+    // Save custom message state, set a new custom message state to display: Calibrating point 9.
+    CustomMsg custom_message_type_old = custom_message_type;
+    unsigned int custom_message_state_old = custom_message_state;
+    custom_message_type = CustomMsg::MeshBedLeveling;
+    custom_message_state = (nMeasPoints * nMeasPoints) + 10;
+    lcd_update(1);
+
+    mbl.reset(); //reset mesh bed leveling
+
+    // Reset baby stepping to zero, if the babystepping has already been loaded before.
+    babystep_undo();
+
+    // Cycle through all points and probe them
+    // First move up. During this first movement, the babystepping will be reverted.
+    current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
+    plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS] / 60);
+    // The move to the first calibration point.
+    current_position[X_AXIS] = BED_X0;
+    current_position[Y_AXIS] = BED_Y0;
+
+#ifdef SUPPORT_VERBOSITY
+    if (verbosity_level >= 1)
+    {
+        bool clamped = world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
+        clamped ? SERIAL_PROTOCOLPGM("First calibration point clamped.\n") : SERIAL_PROTOCOLPGM("No clamping for first calibration point.\n");
+    }
+#else //SUPPORT_VERBOSITY
+    world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
+#endif //SUPPORT_VERBOSITY
+
+    int XY_AXIS_FEEDRATE = homing_feedrate[X_AXIS] / 20;
+    plan_buffer_line_curposXYZE(XY_AXIS_FEEDRATE);
+    // Wait until the move is finished.
+    st_synchronize();
+    if (waiting_inside_plan_buffer_line_print_aborted)
+    {
+        custom_message_type = custom_message_type_old;
+        custom_message_state = custom_message_state_old;
+        return;
+    }
+
+    uint8_t mesh_point = 0; //index number of calibration point
+    int Z_LIFT_FEEDRATE = homing_feedrate[Z_AXIS] / 40;
+    bool has_z = is_bed_z_jitter_data_valid(); //checks if we have data from Z calibration (offsets of the Z heiths of the 8 calibration points from the first point)
+#ifdef SUPPORT_VERBOSITY
+    if (verbosity_level >= 1) {
+        has_z ? SERIAL_PROTOCOLPGM("Z jitter data from Z cal. valid.\n") : SERIAL_PROTOCOLPGM("Z jitter data from Z cal. not valid.\n");
+    }
+#endif // SUPPORT_VERBOSITY
+    int l_feedmultiply = setup_for_endstop_move(false); //save feedrate and feedmultiply, sets feedmultiply to 100
+    while (mesh_point != nMeasPoints * nMeasPoints) {
+        // Get coords of a measuring point.
+        uint8_t ix = mesh_point % nMeasPoints; // from 0 to MESH_NUM_X_POINTS - 1
+        uint8_t iy = mesh_point / nMeasPoints;
+        /*if (!mbl_point_measurement_valid(ix, iy, nMeasPoints, true)) {
+          printf_P(PSTR("Skipping point [%d;%d] \n"), ix, iy);
+          custom_message_state--;
+          mesh_point++;
+          continue; //skip
+          }*/
+        if (iy & 1) ix = (nMeasPoints - 1) - ix; // Zig zag
+        if (nMeasPoints == 7) //if we have 7x7 mesh, compare with Z-calibration for points which are in 3x3 mesh
+        {
+            has_z = ((ix % 3 == 0) && (iy % 3 == 0)) && is_bed_z_jitter_data_valid();
+        }
+        float z0 = 0.f;
+        if (has_z && (mesh_point > 0)) {
+            uint16_t z_offset_u = 0;
+            if (nMeasPoints == 7) {
+                z_offset_u = eeprom_read_word((uint16_t*)(EEPROM_BED_CALIBRATION_Z_JITTER + 2 * ((ix/3) + iy - 1)));
+            }
+            else {
+                z_offset_u = eeprom_read_word((uint16_t*)(EEPROM_BED_CALIBRATION_Z_JITTER + 2 * (ix + iy * 3 - 1)));
+            }
+            z0 = mbl.z_values[0][0] + *reinterpret_cast<int16_t*>(&z_offset_u) * 0.01;
+#ifdef SUPPORT_VERBOSITY
+            if (verbosity_level >= 1) {
+                printf_P(PSTR("Bed leveling, point: %d, calibration Z stored in eeprom: %d, calibration z: %f \n"), mesh_point, z_offset_u, z0);
+            }
+#endif // SUPPORT_VERBOSITY
+        }
+
+        // Move Z up to MESH_HOME_Z_SEARCH.
+        if((ix == 0) && (iy == 0)) current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
+        else current_position[Z_AXIS] += 2.f / nMeasPoints; //use relative movement from Z coordinate where PINDa triggered on previous point. This makes calibration faster.
+        float init_z_bckp = current_position[Z_AXIS];
+        plan_buffer_line_curposXYZE(Z_LIFT_FEEDRATE);
+        st_synchronize();
+
+        // Move to XY position of the sensor point.
+        current_position[X_AXIS] = BED_X(ix, nMeasPoints);
+        current_position[Y_AXIS] = BED_Y(iy, nMeasPoints);
+
+        //printf_P(PSTR("[%f;%f]\n"), current_position[X_AXIS], current_position[Y_AXIS]);
+
+
+#ifdef SUPPORT_VERBOSITY
+        if (verbosity_level >= 1) {
+            bool clamped = world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
+            SERIAL_PROTOCOL(mesh_point);
+            clamped ? SERIAL_PROTOCOLPGM(": xy clamped.\n") : SERIAL_PROTOCOLPGM(": no xy clamping\n");
+        }
+#else //SUPPORT_VERBOSITY
+        world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
+#endif // SUPPORT_VERBOSITY
+
+        //printf_P(PSTR("after clamping: [%f;%f]\n"), current_position[X_AXIS], current_position[Y_AXIS]);
+        plan_buffer_line_curposXYZE(XY_AXIS_FEEDRATE);
+        st_synchronize();
+        if (waiting_inside_plan_buffer_line_print_aborted)
+        {
+            custom_message_type = custom_message_type_old;
+            custom_message_state = custom_message_state_old;
+            return;
+        }
+
+        // Go down until endstop is hit
+        const float Z_CALIBRATION_THRESHOLD = 1.f;
+        if (!find_bed_induction_sensor_point_z((has_z && mesh_point > 0) ? z0 - Z_CALIBRATION_THRESHOLD : -10.f, nProbeRetry)) { //if we have data from z calibration max allowed difference is 1mm for each point, if we dont have data max difference is 10mm from initial point
+            printf_P(_T(MSG_BED_LEVELING_FAILED_POINT_LOW));
+            break;
+        }
+        if (init_z_bckp - current_position[Z_AXIS] < 0.1f) { //broken cable or initial Z coordinate too low. Go to MESH_HOME_Z_SEARCH and repeat last step (z-probe) again to distinguish between these two cases.
+            //printf_P(PSTR("Another attempt! Current Z position: %f\n"), current_position[Z_AXIS]);
+            current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
+            plan_buffer_line_curposXYZE(Z_LIFT_FEEDRATE);
+            st_synchronize();
+
+            if (!find_bed_induction_sensor_point_z((has_z && mesh_point > 0) ? z0 - Z_CALIBRATION_THRESHOLD : -10.f, nProbeRetry)) { //if we have data from z calibration max allowed difference is 1mm for each point, if we dont have data max difference is 10mm from initial point
+                printf_P(_T(MSG_BED_LEVELING_FAILED_POINT_LOW));
+                break;
+            }
+            if (MESH_HOME_Z_SEARCH - current_position[Z_AXIS] < 0.1f) {
+                puts_P(PSTR("Bed leveling failed. Sensor disconnected or cable broken."));
+                break;
+            }
+        }
+        if (has_z && fabs(z0 - current_position[Z_AXIS]) > Z_CALIBRATION_THRESHOLD) { //if we have data from z calibration, max. allowed difference is 1mm for each point
+            puts_P(PSTR("Bed leveling failed. Sensor triggered too high."));
+            break;
+        }
+#ifdef SUPPORT_VERBOSITY
+        if (verbosity_level >= 10) {
+            SERIAL_ECHOPGM("X: ");
+            MYSERIAL.print(current_position[X_AXIS], 5);
+            SERIAL_ECHOLNPGM("");
+            SERIAL_ECHOPGM("Y: ");
+            MYSERIAL.print(current_position[Y_AXIS], 5);
+            SERIAL_PROTOCOLPGM("\n");
+        }
+#endif // SUPPORT_VERBOSITY
+        float offset_z = 0;
+
+#ifdef PINDA_THERMISTOR
+        offset_z = temp_compensation_pinda_thermistor_offset(current_temperature_pinda);
+#endif //PINDA_THERMISTOR
+        //			#ifdef SUPPORT_VERBOSITY
+        /*			if (verbosity_level >= 1)
+                    {
+                    SERIAL_ECHOPGM("mesh bed leveling: ");
+                    MYSERIAL.print(current_position[Z_AXIS], 5);
+                    SERIAL_ECHOPGM(" offset: ");
+                    MYSERIAL.print(offset_z, 5);
+                    SERIAL_ECHOLNPGM("");
+                    }*/
+        //			#endif // SUPPORT_VERBOSITY
+        mbl.set_z(ix, iy, current_position[Z_AXIS] - offset_z); //store measured z values z_values[iy][ix] = z - offset_z;
+
+        custom_message_state--;
+        mesh_point++;
+        lcd_update(1);
+    }
+    current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
+#ifdef SUPPORT_VERBOSITY
+    if (verbosity_level >= 20) {
+        SERIAL_ECHOLNPGM("Mesh bed leveling while loop finished.");
+        SERIAL_ECHOLNPGM("MESH_HOME_Z_SEARCH: ");
+        MYSERIAL.print(current_position[Z_AXIS], 5);
+    }
+#endif // SUPPORT_VERBOSITY
+    plan_buffer_line_curposXYZE(Z_LIFT_FEEDRATE);
+    st_synchronize();
+    if (mesh_point != nMeasPoints * nMeasPoints) {
+        Sound_MakeSound(e_SOUND_TYPE_StandardAlert);
+        bool bState;
+        do   {                             // repeat until Z-leveling o.k.
+            lcd_display_message_fullscreen_P(_i("Some problem encountered, Z-leveling enforced ..."));
+#ifdef TMC2130
+            lcd_wait_for_click_delay(MSG_BED_LEVELING_FAILED_TIMEOUT);
+            calibrate_z_auto();           // Z-leveling (X-assembly stay up!!!)
+#else // TMC2130
+            lcd_wait_for_click_delay(0);  // ~ no timeout
+            lcd_calibrate_z_end_stop_manual(true); // Z-leveling (X-assembly stay up!!!)
+#endif // TMC2130
+            // ~ Z-homing (can not be used "G28", because X & Y-homing would have been done before (Z-homing))
+            bState=enable_z_endstop(false);
+            current_position[Z_AXIS] -= 1;
+            plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS] / 40);
+            st_synchronize();
+            enable_z_endstop(true);
+#ifdef TMC2130
+            tmc2130_home_enter(Z_AXIS_MASK);
+#endif // TMC2130
+            current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
+            plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS] / 40);
+            st_synchronize();
+#ifdef TMC2130
+            tmc2130_home_exit();
+#endif // TMC2130
+            enable_z_endstop(bState);
+        } while (st_get_position_mm(Z_AXIS) > MESH_HOME_Z_SEARCH); // i.e. Z-leveling not o.k.
+        //               plan_set_z_position(MESH_HOME_Z_SEARCH); // is not necessary ('do-while' loop always ends at the expected Z-position)
+
+        custom_message_type = custom_message_type_old;
+        custom_message_state = custom_message_state_old;
+        lcd_update_enable(true);           // display / status-line recovery
+        gcode_G28(true, true, true);       // X & Y & Z-homing (must be after individual Z-homing (problem with spool-holder)!)
+        repeatcommand_front();             // re-run (i.e. of "G80")
+        return;
+    }
+    clean_up_after_endstop_move(l_feedmultiply);
+    //		SERIAL_ECHOLNPGM("clean up finished ");
+
+#ifndef PINDA_THERMISTOR
+    if(temp_cal_active == true && calibration_status_pinda() == true) temp_compensation_apply(); //apply PINDA temperature compensation
+#endif
+    babystep_apply(); // Apply Z height correction aka baby stepping before mesh bed leveing gets activated.
+    //		SERIAL_ECHOLNPGM("babystep applied");
+    bool eeprom_bed_correction_valid = eeprom_read_byte((unsigned char*)EEPROM_BED_CORRECTION_VALID) == 1;
+#ifdef SUPPORT_VERBOSITY
+    if (verbosity_level >= 1) {
+        eeprom_bed_correction_valid ? SERIAL_PROTOCOLPGM("Bed correction data valid\n") : SERIAL_PROTOCOLPGM("Bed correction data not valid\n");
+    }
+#endif // SUPPORT_VERBOSITY
+
+    for (uint8_t i = 0; i < 4; ++i) {
+        unsigned char codes[4] = { 'L', 'R', 'F', 'B' };
+        long correction = 0;
+        if (code_seen(codes[i]))
+            correction = code_value_long();
+        else if (eeprom_bed_correction_valid) {
+            unsigned char *addr = (i < 2) ?
+                                  ((i == 0) ? (unsigned char*)EEPROM_BED_CORRECTION_LEFT : (unsigned char*)EEPROM_BED_CORRECTION_RIGHT) :
+                                  ((i == 2) ? (unsigned char*)EEPROM_BED_CORRECTION_FRONT : (unsigned char*)EEPROM_BED_CORRECTION_REAR);
+            correction = eeprom_read_int8(addr);
+        }
+        if (correction == 0)
+            continue;
+
+        if (labs(correction) > BED_ADJUSTMENT_UM_MAX) {
+            SERIAL_ERROR_START;
+            SERIAL_ECHOPGM("Excessive bed leveling correction: ");
+            SERIAL_ECHO(correction);
+            SERIAL_ECHOLNPGM(" microns");
+        }
+        else {
+            float offset = float(correction) * 0.001f;
+            switch (i) {
+            case 0:
+                for (uint8_t row = 0; row < nMeasPoints; ++row) {
+                    for (uint8_t col = 0; col < nMeasPoints - 1; ++col) {
+                        mbl.z_values[row][col] += offset * (nMeasPoints - 1 - col) / (nMeasPoints - 1);
+                    }
+                }
+                break;
+            case 1:
+                for (uint8_t row = 0; row < nMeasPoints; ++row) {
+                    for (uint8_t col = 1; col < nMeasPoints; ++col) {
+                        mbl.z_values[row][col] += offset * col / (nMeasPoints - 1);
+                    }
+                }
+                break;
+            case 2:
+                for (uint8_t col = 0; col < nMeasPoints; ++col) {
+                    for (uint8_t row = 0; row < nMeasPoints; ++row) {
+                        mbl.z_values[row][col] += offset * (nMeasPoints - 1 - row) / (nMeasPoints - 1);
+                    }
+                }
+                break;
+            case 3:
+                for (uint8_t col = 0; col < nMeasPoints; ++col) {
+                    for (uint8_t row = 1; row < nMeasPoints; ++row) {
+                        mbl.z_values[row][col] += offset * row / (nMeasPoints - 1);
+                    }
+                }
+                break;
+            }
+        }
+    }
+    //		SERIAL_ECHOLNPGM("Bed leveling correction finished");
+    if (nMeasPoints == 3) {
+        mbl.upsample_3x3(); //interpolation from 3x3 to 7x7 points using largrangian polynomials while using the same array z_values[iy][ix] for storing (just coppying measured data to new destination and interpolating between them)
+    }
+    /*
+      SERIAL_PROTOCOLPGM("Num X,Y: ");
+      SERIAL_PROTOCOL(MESH_NUM_X_POINTS);
+      SERIAL_PROTOCOLPGM(",");
+      SERIAL_PROTOCOL(MESH_NUM_Y_POINTS);
+      SERIAL_PROTOCOLPGM("\nZ search height: ");
+      SERIAL_PROTOCOL(MESH_HOME_Z_SEARCH);
+      SERIAL_PROTOCOLLNPGM("\nMeasured points:");
+      for (int y = MESH_NUM_Y_POINTS-1; y >= 0; y--) {
+      for (int x = 0; x < MESH_NUM_X_POINTS; x++) {
+      SERIAL_PROTOCOLPGM("  ");
+      SERIAL_PROTOCOL_F(mbl.z_values[y][x], 5);
+      }
+      SERIAL_PROTOCOLPGM("\n");
+      }
+    */
+    if (nMeasPoints == 7 && magnet_elimination) {
+        mbl_interpolation(nMeasPoints);
+    }
+    /*
+      SERIAL_PROTOCOLPGM("Num X,Y: ");
+      SERIAL_PROTOCOL(MESH_NUM_X_POINTS);
+      SERIAL_PROTOCOLPGM(",");
+      SERIAL_PROTOCOL(MESH_NUM_Y_POINTS);
+      SERIAL_PROTOCOLPGM("\nZ search height: ");
+      SERIAL_PROTOCOL(MESH_HOME_Z_SEARCH);
+      SERIAL_PROTOCOLLNPGM("\nMeasured points:");
+      for (int y = MESH_NUM_Y_POINTS-1; y >= 0; y--) {
+      for (int x = 0; x < MESH_NUM_X_POINTS; x++) {
+      SERIAL_PROTOCOLPGM("  ");
+      SERIAL_PROTOCOL_F(mbl.z_values[y][x], 5);
+      }
+      SERIAL_PROTOCOLPGM("\n");
+      }
+    */
+    //		SERIAL_ECHOLNPGM("Upsample finished");
+    mbl.active = 1; //activate mesh bed leveling
+    //		SERIAL_ECHOLNPGM("Mesh bed leveling activated");
+    go_home_with_z_lift();
+    //		SERIAL_ECHOLNPGM("Go home finished");
+    //unretract (after PINDA preheat retraction)
+    if ((degHotend(active_extruder) > EXTRUDE_MINTEMP) && eeprom_read_byte((unsigned char *)EEPROM_TEMP_CAL_ACTIVE) && calibration_status_pinda() && (target_temperature_bed >= 50)) {
+        current_position[E_AXIS] += default_retraction;
+        plan_buffer_line_curposXYZE(400);
+    }
+    KEEPALIVE_STATE(NOT_BUSY);
+    // Restore custom message state
+    lcd_setstatuspgm(_T(WELCOME_MSG));
+    custom_message_type = custom_message_type_old;
+    custom_message_state = custom_message_state_old;
+    mesh_bed_run_from_menu = false;
+    lcd_update(2);
+
+    st_synchronize();
+    mesh_bed_leveling_flag = false;
+}
+
+
+void adjust_bed_reset()
+{
+	eeprom_update_byte((unsigned char*)EEPROM_BED_CORRECTION_VALID, 1);
+	eeprom_update_byte((unsigned char*)EEPROM_BED_CORRECTION_LEFT, 0);
+	eeprom_update_byte((unsigned char*)EEPROM_BED_CORRECTION_RIGHT, 0);
+	eeprom_update_byte((unsigned char*)EEPROM_BED_CORRECTION_FRONT, 0);
+	eeprom_update_byte((unsigned char*)EEPROM_BED_CORRECTION_REAR, 0);
+}
+
+//! @brief Calibrate XYZ
+//! @param onlyZ if true, calibrate only Z axis
+//! @param verbosity_level
+//! @retval true Succeeded
+//! @retval false Failed
+bool gcode_M45(bool onlyZ, int8_t verbosity_level)
+{
+	bool final_result = false;
+	#ifdef TMC2130
 	FORCE_HIGH_POWER_START;
 	#endif // TMC2130
     
@@ -2864,6 +3513,8 @@ bool gcode_M45(bool onlyZ, int8_t verbosity_level)
 	//set_destination_to_current();
 	int l_feedmultiply = setup_for_endstop_move();
 	lcd_display_message_fullscreen_P(_T(MSG_AUTO_HOME));
+  raise_z_above(MESH_HOME_Z_SEARCH);
+  st_synchronize();
 	home_xy();
 
 	enable_endstops(false);
@@ -2884,15 +3535,11 @@ bool gcode_M45(bool onlyZ, int8_t verbosity_level)
 		lcd_show_fullscreen_message_and_wait_P(_T(MSG_CONFIRM_NOZZLE_CLEAN));
 		if(onlyZ){
 			lcd_display_message_fullscreen_P(_T(MSG_MEASURE_BED_REFERENCE_HEIGHT_LINE1));
-			lcd_set_cursor(0, 3);
-			lcd_print(1);
-			lcd_puts_P(_T(MSG_MEASURE_BED_REFERENCE_HEIGHT_LINE2));
+			lcd_puts_at_P(0,3,_n("1/9"));
 		}else{
 			//lcd_show_fullscreen_message_and_wait_P(_T(MSG_PAPER));
 			lcd_display_message_fullscreen_P(_T(MSG_FIND_BED_OFFSET_AND_SKEW_LINE1));
-			lcd_set_cursor(0, 2);
-			lcd_print(1);
-			lcd_puts_P(_T(MSG_FIND_BED_OFFSET_AND_SKEW_LINE2));
+			lcd_puts_at_P(0,3,_n("1/4"));
 		}
 
 		refresh_cmd_timeout();
@@ -2912,9 +3559,7 @@ bool gcode_M45(bool onlyZ, int8_t verbosity_level)
 		    lcd_show_fullscreen_message_and_wait_P(_T(MSG_PAPER));
 			KEEPALIVE_STATE(IN_HANDLER);
 			lcd_display_message_fullscreen_P(_T(MSG_FIND_BED_OFFSET_AND_SKEW_LINE1));
-			lcd_set_cursor(0, 2);
-			lcd_print(1);
-			lcd_puts_P(_T(MSG_FIND_BED_OFFSET_AND_SKEW_LINE2));
+			lcd_puts_at_P(0,3,_n("1/4"));
 		}
 			
 		bool endstops_enabled  = enable_endstops(false);
@@ -2953,11 +3598,13 @@ bool gcode_M45(bool onlyZ, int8_t verbosity_level)
 				bool result = sample_mesh_and_store_reference();
 				if (result)
 				{
-					if (calibration_status() == CALIBRATION_STATUS_Z_CALIBRATION)
-						// Shipped, the nozzle height has been set already. The user can start printing now.
-						calibration_status_store(CALIBRATION_STATUS_CALIBRATED);
-						final_result = true;
-					// babystep_apply();
+                    if (calibration_status() == CALIBRATION_STATUS_Z_CALIBRATION)
+                    {
+                        // Shipped, the nozzle height has been set already. The user can start printing now.
+                        calibration_status_store(CALIBRATION_STATUS_CALIBRATED);
+                    }
+                    final_result = true;
+                    // babystep_apply();
 				}
 			}
 			else
@@ -3053,9 +3700,16 @@ void gcode_M114()
 	SERIAL_PROTOCOLPGM(" E:");
 	SERIAL_PROTOCOL(float(st_get_position(E_AXIS)) / cs.axis_steps_per_unit[E_AXIS]);
 
-	SERIAL_PROTOCOLLN("");
+	SERIAL_PROTOCOLLN();
 }
 
+#if (defined(FANCHECK) && (((defined(TACH_0) && (TACH_0 >-1)) || (defined(TACH_1) && (TACH_1 > -1)))))
+void gcode_M123()
+{
+  printf_P(_N("E0:%d RPM PRN1:%d RPM E0@:%u PRN1@:%d\n"), 60*fan_speed[active_extruder], 60*fan_speed[1], newFanSpeed, fanSpeed);
+}
+#endif //FANCHECK and TACH_0 or TACH_1
+
 //! extracted code to compute z_shift for M600 in case of filament change operation 
 //! requested from fsensors.
 //! The function ensures, that the printhead lifts to at least 25mm above the heat bed
@@ -3071,12 +3725,12 @@ static T gcode_M600_filament_change_z_shift()
 #ifdef FILAMENTCHANGE_ZADD
 	static_assert(Z_MAX_POS < (255 - FILAMENTCHANGE_ZADD), "Z-range too high, change the T type from uint8_t to uint16_t");
 	// avoid floating point arithmetics when not necessary - results in shorter code
+	T z_shift = T(FILAMENTCHANGE_ZADD); // always move above printout
 	T ztmp = T( current_position[Z_AXIS] );
-	T z_shift = 0;
-	if(ztmp < T(25)){
-		z_shift = T(25) - ztmp; // make sure to be at least 25mm above the heat bed
-	} 
-	return z_shift + T(FILAMENTCHANGE_ZADD); // always move above printout
+	if((ztmp + z_shift) < T(MIN_Z_FOR_SWAP)){
+		z_shift = T(MIN_Z_FOR_SWAP) - ztmp; // make sure to be at least 25mm above the heat bed
+	}
+	return z_shift;
 #else
 	return T(0);
 #endif
@@ -3125,7 +3779,7 @@ static void gcode_M600(bool automatic, float x_position, float y_position, float
 
     // Unload filament
     if (mmu_enabled) extr_unload();	//unload just current filament for multimaterial printers (used also in M702)
-    else unload_filament(); //unload filament for single material (used also in M702)
+    else unload_filament(true); //unload filament for single material (used also in M702)
     //finish moves
     st_synchronize();
 
@@ -3137,12 +3791,11 @@ static void gcode_M600(bool automatic, float x_position, float y_position, float
         if (lcd_change_fil_state == 0)
         {
 			lcd_clear();
-			lcd_set_cursor(0, 2);
-			lcd_puts_P(_T(MSG_PLEASE_WAIT));
+			lcd_puts_at_P(0, 2, _T(MSG_PLEASE_WAIT));
 			current_position[X_AXIS] -= 100;
 			plan_buffer_line_curposXYZE(FILAMENTCHANGE_XYFEED);
 			st_synchronize();
-			lcd_show_fullscreen_message_and_wait_P(_i("Please open idler and remove filament manually."));////MSG_CHECK_IDLER c=20 r=4
+			lcd_show_fullscreen_message_and_wait_P(_i("Please open idler and remove filament manually."));////MSG_CHECK_IDLER c=20 r=5
         }
     }
 
@@ -3154,8 +3807,7 @@ static void gcode_M600(bool automatic, float x_position, float y_position, float
             if (saved_printing) {
 
                 lcd_clear();
-                lcd_set_cursor(0, 2);
-                lcd_puts_P(_T(MSG_PLEASE_WAIT));
+                lcd_puts_at_P(0, 2, _T(MSG_PLEASE_WAIT));
 
                 mmu_command(MmuCmd::R0);
                 manage_response(false, false);
@@ -3276,47 +3928,49 @@ void gcode_M701()
  *
  * Typical format of S/N is:CZPX0917X003XC13518
  *
- * Command operates only in farm mode, if not in farm mode, "Not in farm mode." is written to MYSERIAL.
- *
  * Send command ;S to serial port 0 to retrieve serial number stored in 32U2 processor,
- * reply is transmitted to serial port 1 character by character.
- * Operation takes typically 23 ms. If the retransmit is not finished until 100 ms,
- * it is interrupted, so less, or no characters are retransmitted, only newline character is send
- * in any case.
+ * reply is stored in *SN.
+ * Operation takes typically 23 ms. If no valid SN can be retrieved within the 250ms window, the function aborts 
+ * and returns a general failure flag.
+ * The command will fail if the 32U2 processor is unpowered via USB since it is isolated from the rest of the electronics.
+ * In that case the value that is stored in the EEPROM should be used instead.
+ *
+ * @return 0 on success
+ * @return 1 on general failure
  */
-static void gcode_PRUSA_SN()
+static uint8_t get_PRUSA_SN(char* SN)
 {
-    if (farm_mode) {
-        selectedSerialPort = 0;
-        putchar(';');
-        putchar('S');
-        int numbersRead = 0;
-        ShortTimer timeout;
-        timeout.start();
-
-        while (numbersRead < 19) {
-            while (MSerial.available() > 0) {
-                uint8_t serial_char = MSerial.read();
-                selectedSerialPort = 1;
-                putchar(serial_char);
-                numbersRead++;
-                selectedSerialPort = 0;
+    uint8_t selectedSerialPort_bak = selectedSerialPort;
+    uint8_t rxIndex;
+    bool SN_valid = false;
+    ShortTimer timeout;
+
+    selectedSerialPort = 0;
+    timeout.start();
+    
+    while (!SN_valid)
+    {
+        rxIndex = 0;
+        _delay(50);
+        MYSERIAL.flush(); //clear RX buffer
+        SERIAL_ECHOLNRPGM(PSTR(";S"));
+        while (rxIndex < 19)
+        {
+            if (timeout.expired(250u))
+                goto exit;
+            if (MYSERIAL.available() > 0)
+            {
+                SN[rxIndex] = MYSERIAL.read();
+                rxIndex++;
             }
-            if (timeout.expired(100u)) break;
         }
-        selectedSerialPort = 1;
-        putchar('\n');
-#if 0
-        for (int b = 0; b < 3; b++) {
-            _tone(BEEPER, 110);
-            _delay(50);
-            _noTone(BEEPER);
-            _delay(50);
-        }
-#endif
-    } else {
-        puts_P(_N("Not in farm mode."));
+        SN[rxIndex] = 0;
+        // printf_P(PSTR("SN:%s\n"), SN);
+        SN_valid = (strncmp_P(SN, PSTR("CZPX"), 4) == 0);
     }
+exit:
+    selectedSerialPort = selectedSerialPort_bak;
+    return !SN_valid;
 }
 //! Detection of faulty RAMBo 1.1b boards equipped with bigger capacitors
 //! at the TACH_1 pin, which causes bad detection of print fan speed.
@@ -3410,6 +4064,26 @@ static void gcode_G92()
     }
 }
 
+#ifdef EXTENDED_CAPABILITIES_REPORT
+
+static void cap_line(const char* name, bool ena = false) {
+    printf_P(PSTR("Cap:%S:%c\n"), name, (char)ena + '0');
+}
+
+static void extended_capabilities_report()
+{
+    // AUTOREPORT_TEMP (M155)
+    cap_line(PSTR("AUTOREPORT_TEMP"), ENABLED(AUTO_REPORT));
+#if (defined(FANCHECK) && (((defined(TACH_0) && (TACH_0 >-1)) || (defined(TACH_1) && (TACH_1 > -1)))))
+    // AUTOREPORT_FANS (M123)
+    cap_line(PSTR("AUTOREPORT_FANS"), ENABLED(AUTO_REPORT));
+#endif //FANCHECK and TACH_0 or TACH_1
+    // AUTOREPORT_POSITION (M114)
+    cap_line(PSTR("AUTOREPORT_POSITION"), ENABLED(AUTO_REPORT));
+    // EXTENDED_M20 (support for L and T parameters)
+    cap_line(PSTR("EXTENDED_M20"), 1);
+}
+#endif //EXTENDED_CAPABILITIES_REPORT
 
 #ifdef BACKLASH_X
 extern uint8_t st_backlash_x;
@@ -3497,12 +4171,14 @@ extern uint8_t st_backlash_y;
 //!@n M115 - Capabilities string
 //!@n M117 - display message
 //!@n M119 - Output Endstop status to serial port
+//!@n M123 - Tachometer value
 //!@n M126 - Solenoid Air Valve Open (BariCUDA support by jmil)
 //!@n M127 - Solenoid Air Valve Closed (BariCUDA vent to atmospheric pressure by jmil)
 //!@n M128 - EtoP Open (BariCUDA EtoP = electricity to air pressure transducer by jmil)
 //!@n M129 - EtoP Closed (BariCUDA EtoP = electricity to air pressure transducer by jmil)
 //!@n M140 - Set bed target temp
 //!@n M150 - Set BlinkM Color Output R: Red<0-255> U(!): Green<0-255> B: Blue<0-255> over i2c, G for green does not work.
+//!@n M155 - Automatically send temperatures, fan speeds, position
 //!@n M190 - Sxxx Wait for bed current temp to reach target temp. Waits only when heating
 //!          Rxxx Wait for bed current temp to reach target temp. Waits when heating and cooling
 //!@n M200 D<millimeters>- set filament diameter and set E axis units to cubic millimeters (use S0 to set back to millimeters).
@@ -3540,6 +4216,7 @@ extern uint8_t st_backlash_y;
 //!@n M503 - print the current settings (from memory not from EEPROM)
 //!@n M509 - force language selection on next restart
 //!@n M540 - Use S[0|1] to enable or disable the stop SD card print on endstop hit (requires ABORT_ON_ENDSTOP_HIT_FEATURE_ENABLED)
+//!@n M552 - Set IP address
 //!@n M600 - Pause for filament change X[pos] Y[pos] Z[relative lift] E[initial retract] L[later retract distance for removal]
 //!@n M605 - Set dual x-carriage movement mode: S<mode> [ X<duplication x-offset> R<duplication temp offset> ]
 //!@n M860 - Wait for PINDA thermistor to reach target temperature.
@@ -3568,13 +4245,12 @@ There are reasons why some G Codes aren't in numerical order.
 void process_commands()
 {
 #ifdef FANCHECK
-    if(fan_check_error){
-        if(fan_check_error == EFCE_DETECTED){
-            fan_check_error = EFCE_REPORTED;
-            // SERIAL_PROTOCOLLNRPGM(MSG_OCTOPRINT_PAUSED);
+    if(fan_check_error == EFCE_DETECTED) {
+        fan_check_error = EFCE_REPORTED;
+        if (is_usb_printing)
+            lcd_pause_usb_print();
+        else
             lcd_pause_print();
-        } // otherwise it has already been reported, so just ignore further processing
-        return; //ignore usb stream. It is reenabled by selecting resume from the lcd.
     }
 #endif
 
@@ -3606,31 +4282,97 @@ void process_commands()
   float tmp_motor_loud[3] = DEFAULT_PWM_MOTOR_CURRENT_LOUD;
   int8_t SilentMode;
 #endif
-  /*!
-  
-  ---------------------------------------------------------------------------------
-  ### M117 - Display Message <a href="https://reprap.org/wiki/G-code#M117:_Display_Message">M117: Display Message</a>
-  This causes the given message to be shown in the status line on an attached LCD.
-  It is processed early as to allow printing messages that contain G, M, N or T.
+    /*!
+
+    ---------------------------------------------------------------------------------
+    ### M117 - Display Message <a href="https://reprap.org/wiki/G-code#M117:_Display_Message">M117: Display Message</a>
+    This causes the given message to be shown in the status line on an attached LCD.
+    It is processed early as to allow printing messages that contain G, M, N or T.
+
+    ---------------------------------------------------------------------------------
+    ### Special internal commands
+    These are used by internal functions to process certain actions in the right order. Some of these are also usable by the user.
+    They are processed early as the commands are complex (strings).
+    These are only available on the MK3(S) as these require TMC2130 drivers:
+        - CRASH DETECTED
+        - CRASH RECOVER
+        - CRASH_CANCEL
+        - TMC_SET_WAVE
+        - TMC_SET_STEP
+        - TMC_SET_CHOP
+    */
+    if (code_seen_P(PSTR("M117"))) //moved to highest priority place to be able to to print strings which includes "G", "PRUSA" and "^"
+    {
+        starpos = (strchr(strchr_pointer + 5, '*'));
+        if (starpos != NULL)
+            *(starpos) = '\0';
+        lcd_setstatus(strchr_pointer + 5);
+        custom_message_type = CustomMsg::MsgUpdate;
+    }
+
+    /*!
+    ### M0, M1 - Stop the printer <a href="https://reprap.org/wiki/G-code#M0:_Stop_or_Unconditional_stop">M0: Stop or Unconditional stop</a>
+    #### Usage
+
+      M0 [P<ms<] [S<sec>] [string]
+      M1 [P<ms>] [S<sec>] [string] 
+
+    #### Parameters
   
-  ---------------------------------------------------------------------------------
-  ### Special internal commands
-  These are used by internal functions to process certain actions in the right order. Some of these are also usable by the user.
-  They are processed early as the commands are complex (strings).
-  These are only available on the MK3(S) as these require TMC2130 drivers:
-    - CRASH DETECTED
-    - CRASH RECOVER
-    - CRASH_CANCEL
-    - TMC_SET_WAVE
-    - TMC_SET_STEP
-    - TMC_SET_CHOP
- */
-  if (code_seen("M117")) { //moved to highest priority place to be able to to print strings which includes "G", "PRUSA" and "^"
-	  starpos = (strchr(strchr_pointer + 5, '*'));
-	  if (starpos != NULL)
-		  *(starpos) = '\0';
-	  lcd_setstatus(strchr_pointer + 5);
-  }
+    - `P<ms>`  - Expire time, in milliseconds
+    - `S<sec>` - Expire time, in seconds
+    - `string` - Must for M1 and optional for M0 message to display on the LCD
+    */
+
+    else if (code_seen_P(PSTR("M0")) || code_seen_P(PSTR("M1 "))) {// M0 and M1 - (Un)conditional stop - Wait for user button press on LCD
+        char *src = strchr_pointer + 2;
+        codenum = 0;
+        bool hasP = false, hasS = false;
+        if (code_seen('P')) {
+            codenum = code_value(); // milliseconds to wait
+            hasP = codenum > 0;
+        }
+        if (code_seen('S')) {
+            codenum = code_value() * 1000; // seconds to wait
+            hasS = codenum > 0;
+        }
+        starpos = strchr(src, '*');
+        if (starpos != NULL) *(starpos) = '\0';
+        while (*src == ' ') ++src;
+        custom_message_type = CustomMsg::M0Wait;
+        if (!hasP && !hasS && *src != '\0') {
+            lcd_setstatus(src);
+        } else {
+            // farmers want to abuse a bug from the previous firmware releases
+            // - they need to see the filename on the status screen instead of "Wait for user..."
+            // So we won't update the message in farm mode...
+            if( ! farm_mode){ 
+                LCD_MESSAGERPGM(_i("Wait for user..."));////MSG_USERWAIT c=20
+            } else {
+                custom_message_type = CustomMsg::Status; // let the lcd display the name of the printed G-code file in farm mode
+            }
+        }
+        lcd_ignore_click();				//call lcd_ignore_click also for else ???
+        st_synchronize();
+        previous_millis_cmd = _millis();
+        if (codenum > 0 ) {
+            codenum += _millis();  // keep track of when we started waiting
+            KEEPALIVE_STATE(PAUSED_FOR_USER);
+            while(_millis() < codenum && !lcd_clicked()) {
+                manage_heater();
+                manage_inactivity(true);
+                lcd_update(0);
+            }
+            KEEPALIVE_STATE(IN_HANDLER);
+            lcd_ignore_click(false);
+        } else {
+            marlin_wait_for_click();
+        }
+        if (IS_SD_PRINTING)
+            custom_message_type = CustomMsg::Status;
+        else
+            LCD_MESSAGERPGM(_T(WELCOME_MSG));
+    }
 
 #ifdef TMC2130
 	else if (strncmp_P(CMDBUFFER_CURRENT_STRING, PSTR("CRASH_"), 6) == 0)
@@ -3638,7 +4380,7 @@ void process_commands()
 
     // ### CRASH_DETECTED - TMC2130
     // ---------------------------------
-	  if(code_seen("CRASH_DETECTED"))
+	  if(code_seen_P(PSTR("CRASH_DETECTED")))
 	  {
 		  uint8_t mask = 0;
 		  if (code_seen('X')) mask |= X_AXIS_MASK;
@@ -3648,12 +4390,12 @@ void process_commands()
 
     // ### CRASH_RECOVER - TMC2130
     // ----------------------------------
-	  else if(code_seen("CRASH_RECOVER"))
+	  else if(code_seen_P(PSTR("CRASH_RECOVER")))
 		  crashdet_recover();
 
     // ### CRASH_CANCEL - TMC2130
     // ----------------------------------
-	  else if(code_seen("CRASH_CANCEL"))
+	  else if(code_seen_P(PSTR("CRASH_CANCEL")))
 		  crashdet_cancel();
 	}
 	else if (strncmp_P(CMDBUFFER_CURRENT_STRING, PSTR("TMC_"), 4) == 0)
@@ -3739,7 +4481,7 @@ void process_commands()
 	}
 #endif //BACKLASH_Y
 #endif //TMC2130
-  else if(code_seen("PRUSA")){ 
+  else if(code_seen_P(PSTR("PRUSA"))){ 
     /*!
     ---------------------------------------------------------------------------------
     ### PRUSA - Internal command set <a href="https://reprap.org/wiki/G-code#G98:_Activate_farm_mode">G98: Activate farm mode - Notes</a>
@@ -3772,63 +4514,48 @@ void process_commands()
     */
 
 
-		if (code_seen("Ping")) {  // PRUSA Ping
+		if (code_seen_P(PSTR("Ping"))) {  // PRUSA Ping
 			if (farm_mode) {
 				PingTime = _millis();
-				//MYSERIAL.print(farm_no); MYSERIAL.println(": OK");
 			}	  
 		}
-		else if (code_seen("PRN")) { // PRUSA PRN
+		else if (code_seen_P(PSTR("PRN"))) { // PRUSA PRN
 		  printf_P(_N("%d"), status_number);
 
-        } else if( code_seen("FANPINTST") ){
+        } else if( code_seen_P(PSTR("FANPINTST"))){
             gcode_PRUSA_BadRAMBoFanTest();
-        }else if (code_seen("FAN")) { // PRUSA FAN
+        }else if (code_seen_P(PSTR("FAN"))) { // PRUSA FAN
 			printf_P(_N("E0:%d RPM\nPRN0:%d RPM\n"), 60*fan_speed[0], 60*fan_speed[1]);
-		}else if (code_seen("fn")) { // PRUSA fn
-		  if (farm_mode) {
-			printf_P(_N("%d"), farm_no);
-		  }
-		  else {
-			  puts_P(_N("Not in farm mode."));
-		  }
-		  
 		}
-		else if (code_seen("thx")) // PRUSA thx
+		else if (code_seen_P(PSTR("thx"))) // PRUSA thx
 		{
 			no_response = false;
 		}	
-		else if (code_seen("uvlo")) // PRUSA uvlo
+		else if (code_seen_P(PSTR("uvlo"))) // PRUSA uvlo
 		{
                eeprom_update_byte((uint8_t*)EEPROM_UVLO,0); 
                enquecommand_P(PSTR("M24")); 
 		}	
-		else if (code_seen("MMURES")) // PRUSA MMURES
+		else if (code_seen_P(PSTR("MMURES"))) // PRUSA MMURES
 		{
 			mmu_reset();
 		}
-		else if (code_seen("RESET")) { // PRUSA RESET
-            // careful!
-            if (farm_mode) {
-#if (defined(WATCHDOG) && (MOTHERBOARD == BOARD_EINSY_1_0a))
-                boot_app_magic = BOOT_APP_MAGIC;
-                boot_app_flags = BOOT_APP_FLG_RUN;
-				wdt_enable(WDTO_15MS);
-				cli();
-				while(1);
-#else //WATCHDOG
-                asm volatile("jmp 0x3E000");
-#endif //WATCHDOG
-            }
-            else {
-                MYSERIAL.println("Not in farm mode.");
-            }
-		}else if (code_seen("fv")) { // PRUSA fv
+		else if (code_seen_P(PSTR("RESET"))) { // PRUSA RESET
+#ifdef WATCHDOG
+#if defined(XFLASH) && defined(BOOTAPP)
+            boot_app_magic = BOOT_APP_MAGIC;
+            boot_app_flags = BOOT_APP_FLG_RUN;
+#endif //defined(XFLASH) && defined(BOOTAPP)
+            softReset();
+#elif defined(BOOTAPP) //this is a safety precaution. This is because the new bootloader turns off the heaters, but the old one doesn't. The watchdog should be used most of the time.
+            asm volatile("jmp 0x3E000");
+#endif
+		}else if (code_seen_P("fv")) { // PRUSA fv
         // get file version
         #ifdef SDSUPPORT
-        card.openFile(strchr_pointer + 3,true);
+        card.openFileReadFilteredGcode(strchr_pointer + 3,true);
         while (true) {
-            uint16_t readByte = card.get();
+            uint16_t readByte = card.getFilteredGcodeChar();
             MYSERIAL.write(readByte);
             if (readByte=='\n') {
                 break;
@@ -3838,38 +4565,43 @@ void process_commands()
 
         #endif // SDSUPPORT
 
-    } else if (code_seen("M28")) { // PRUSA M28
+    } else if (code_seen_P(PSTR("M28"))) { // PRUSA M28
         trace();
         prusa_sd_card_upload = true;
-        card.openFile(strchr_pointer+4,false);
+        card.openFileWrite(strchr_pointer+4);
 
-	} else if (code_seen("SN")) { // PRUSA SN
-        gcode_PRUSA_SN();
+	} else if (code_seen_P(PSTR("SN"))) { // PRUSA SN
+        char SN[20];
+        eeprom_read_block(SN, (uint8_t*)EEPROM_PRUSA_SN, 20);
+        if (SN[19])
+            puts_P(PSTR("SN invalid"));
+        else
+            puts(SN);
 
-	} else if(code_seen("Fir")){ // PRUSA Fir
+	} else if(code_seen_P(PSTR("Fir"))){ // PRUSA Fir
 
       SERIAL_PROTOCOLLN(FW_VERSION_FULL);
 
-    } else if(code_seen("Rev")){ // PRUSA Rev
+    } else if(code_seen_P(PSTR("Rev"))){ // PRUSA Rev
 
       SERIAL_PROTOCOLLN(FILAMENT_SIZE "-" ELECTRONICS "-" NOZZLE_TYPE );
 
-    } else if(code_seen("Lang")) { // PRUSA Lang
+    } else if(code_seen_P(PSTR("Lang"))) { // PRUSA Lang
 	  lang_reset();
 
-	} else if(code_seen("Lz")) { // PRUSA Lz
+	} else if(code_seen_P(PSTR("Lz"))) { // PRUSA Lz
       eeprom_update_word(reinterpret_cast<uint16_t *>(&(EEPROM_Sheets_base->s[(eeprom_read_byte(&(EEPROM_Sheets_base->active_sheet)))].z_offset)),0);
 
-	} else if(code_seen("Beat")) { // PRUSA Beat
+	} else if(code_seen_P(PSTR("Beat"))) { // PRUSA Beat
         // Kick farm link timer
         kicktime = _millis();
 
-    } else if(code_seen("FR")) { // PRUSA FR
+    } else if(code_seen_P(PSTR("FR"))) { // PRUSA FR
         // Factory full reset
         factory_reset(0);
-    } else if(code_seen("MBL")) { // PRUSA MBL
+    } else if(code_seen_P(PSTR("MBL"))) { // PRUSA MBL
         // Change the MBL status without changing the logical Z position.
-        if(code_seen("V")) {
+        if(code_seen('V')) {
             bool value = code_value_short();
             st_synchronize();
             if(value != mbl.active) {
@@ -3894,14 +4626,14 @@ eeprom_update_byte((uint8_t*)EEPROM_CHECK_MODE,0xFF);
 eeprom_update_byte((uint8_t*)EEPROM_NOZZLE_DIAMETER,0xFF);
 eeprom_update_word((uint16_t*)EEPROM_NOZZLE_DIAMETER_uM,0xFFFF);
 */
-    } else if (code_seen("nozzle")) { // PRUSA nozzle
+    } else if (code_seen_P(PSTR("nozzle"))) { // PRUSA nozzle
           uint16_t nDiameter;
           if(code_seen('D'))
                {
                nDiameter=(uint16_t)(code_value()*1000.0+0.5); // [,um]
                nozzle_diameter_check(nDiameter);
                }
-          else if(code_seen("set") && farm_mode)
+          else if(code_seen_P(PSTR("set")) && farm_mode)
                {
                strchr_pointer++;                  // skip 1st char (~ 's')
                strchr_pointer++;                  // skip 2nd char (~ 'e')
@@ -4170,21 +4902,22 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
 		if (total_filament_used > ((current_position[E_AXIS] - destination[E_AXIS]) * 100)) { //protection against total_filament_used overflow
 			total_filament_used = total_filament_used + ((destination[E_AXIS] - current_position[E_AXIS]) * 100);
 		}
-          #ifdef FWRETRACT
-            if(cs.autoretract_enabled)
-            if( !(code_seen('X') || code_seen('Y') || code_seen('Z')) && code_seen('E')) {
-              float echange=destination[E_AXIS]-current_position[E_AXIS];
-
-              if((echange<-MIN_RETRACT && !retracted[active_extruder]) || (echange>MIN_RETRACT && retracted[active_extruder])) { //move appears to be an attempt to retract or recover
-                  current_position[E_AXIS] = destination[E_AXIS]; //hide the slicer-generated retract/recover from calculations
-                  plan_set_e_position(current_position[E_AXIS]); //AND from the planner
-                  retract(!retracted[active_extruder]);
-                  return;
-              }
-
 
+#ifdef FWRETRACT
+        if(cs.autoretract_enabled) {
+            if( !(code_seen('X') || code_seen('Y') || code_seen('Z')) && code_seen('E')) {
+                float echange=destination[E_AXIS]-current_position[E_AXIS];
+                if((echange<-MIN_RETRACT && !retracted[active_extruder]) || (echange>MIN_RETRACT && retracted[active_extruder])) { //move appears to be an attempt to retract or recover
+                    st_synchronize();
+                    current_position[E_AXIS] = destination[E_AXIS]; //hide the slicer-generated retract/recover from calculations
+                    plan_set_e_position(current_position[E_AXIS]); //AND from the planner
+                    retract(!retracted[active_extruder]);
+                    return;
+                }
             }
-          #endif //FWRETRACT
+        }
+#endif //FWRETRACT
+
         prepare_move();
         //ClearToSend();
       }
@@ -4252,9 +4985,9 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
         lcd_update(0);
       }
       break;
-      #ifdef FWRETRACT
-      
 
+
+#ifdef FWRETRACT
     /*!
 	### G10 - Retract <a href="https://reprap.org/wiki/G-code#G10:_Retract">G10: Retract</a>
 	Retracts filament according to settings of `M207`
@@ -4267,7 +5000,7 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
         retract(true);
        #endif
       break;
-      
+
 
     /*!
 	### G11 - Retract recover <a href="https://reprap.org/wiki/G-code#G11:_Unretract">G11: Unretract</a>
@@ -4280,7 +5013,15 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
         retract(false);
        #endif 
       break;
-      #endif //FWRETRACT
+#endif //FWRETRACT
+
+
+    /*!
+	### G21 - Sets Units to Millimters <a href="https://reprap.org/wiki/G-code#G21:_Set_Units_to_Millimeters">G21: Set Units to Millimeters</a>
+	Units are in millimeters. Prusa doesn't support inches.
+    */
+    case 21: 
+      break; //Doing nothing. This is just to prevent serial UNKOWN warnings.
     
 
     /*!
@@ -4318,9 +5059,7 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
       gcode_G28(home_x, home_x_value, home_y, home_y_value, home_z, home_z_value, without_mbl);
 #endif //TMC2130
       if ((home_x || home_y || without_mbl || home_z) == false) {
-         // Push the commands to the front of the message queue in the reverse order!
-         // There shall be always enough space reserved for these commands.
-         goto case_G80;
+          gcode_G80();
       }
       break;
     }
@@ -4534,6 +5273,8 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
     case 30: 
         {
             st_synchronize();
+            homing_flag = true;
+
             // TODO: make sure the bed_level_rotation_matrix is identity or the planner will get set incorectly
             int l_feedmultiply = setup_for_endstop_move();
 
@@ -4544,6 +5285,7 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
 			printf_P(_N("%S X: %.5f Y: %.5f Z: %.5f\n"), _T(MSG_BED), _x, _y, _z);
 
             clean_up_after_endstop_move(l_feedmultiply);
+            homing_flag = false;
         }
         break;
 	
@@ -4567,7 +5309,7 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
   The Original i3 Prusa MK2/s uses PINDAv1 and this calibration improves the temperature drift, but not as good as the PINDAv2.
 
   superPINDA sensor has internal temperature compensation and no thermistor output. There is no point of doing temperature calibration in such case.
-  If PINDA_THERMISTOR and DETECT_SUPERPINDA is defined during compilation, calibration is skipped with serial message "No PINDA thermistor".
+  If PINDA_THERMISTOR and SUPERPINDA_SUPPORT is defined during compilation, calibration is skipped with serial message "No PINDA thermistor".
   This can be caused also if PINDA thermistor connection is broken or PINDA temperature is lower than PINDA_MINTEMP.
 
   #### Example
@@ -4603,7 +5345,7 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
             // Push the commands to the front of the message queue in the reverse order!
             // There shall be always enough space reserved for these commands.
             repeatcommand_front(); // repeat G76 with all its parameters
-            enquecommand_front_P((PSTR("G28 W0")));
+            enquecommand_front_P(G28W0);
             break;
         }
         lcd_show_fullscreen_message_and_wait_P(_i("Stable ambient temperature 21-26C is needed a rigid stand is required."));////MSG_TEMP_CAL_WARNING c=20 r=4
@@ -4634,6 +5376,10 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
                 break;
             }
         }
+
+        st_synchronize();
+        homing_flag = true; // keep homing on to avoid babystepping while the LCD is enabled
+
         lcd_update_enable(true);
         KEEPALIVE_STATE(NOT_BUSY); //no need to print busy messages as we print current temperatures periodicaly
         SERIAL_ECHOLNPGM("PINDA probe calibration start");
@@ -4678,6 +5424,7 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
         bool find_z_result = find_bed_induction_sensor_point_z(-1.f);
         if (find_z_result == false) {
             lcd_temp_cal_show_result(find_z_result);
+            homing_flag = false;
             break;
         }
         zero_z = current_position[Z_AXIS];
@@ -4728,9 +5475,9 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
             printf_P(_N("\nPINDA temperature: %.1f Z shift (mm): %.3f"), current_temperature_pinda, current_position[Z_AXIS] - zero_z);
 
             EEPROM_save_B(EEPROM_PROBE_TEMP_SHIFT + i * 2, &z_shift);
-
         }
         lcd_temp_cal_show_result(true);
+        homing_flag = false;
 
 #else //PINDA_THERMISTOR
 
@@ -4744,7 +5491,7 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
 			// Push the commands to the front of the message queue in the reverse order!
 			// There shall be always enough space reserved for these commands.
 			repeatcommand_front(); // repeat G76 with all its parameters
-			enquecommand_front_P((PSTR("G28 W0")));
+			enquecommand_front_P(G28W0);
 			break;
 		}
 		puts_P(_N("PINDA probe calibration start"));
@@ -4757,7 +5504,7 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
 		plan_buffer_line_curposXYZE(3000 / 60);
 		st_synchronize();
 		
-		while (abs(degBed() - PINDA_MIN_T) > 1) {
+		while (fabs(degBed() - PINDA_MIN_T) > 1) {
 			delay_keep_alive(1000);
 			serialecho_temperatures();
 		}
@@ -4867,405 +5614,11 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
 	*  v Y-axis
 	*/
 
-	case 80:
-
+	case 80: {
 #ifdef MK1BP
 		break;
 #endif //MK1BP
-	case_G80:
-	{
-		mesh_bed_leveling_flag = true;
-#ifndef PINDA_THERMISTOR
-        static bool run = false; // thermistor-less PINDA temperature compensation is running
-#endif // ndef PINDA_THERMISTOR
-
-#ifdef SUPPORT_VERBOSITY
-		int8_t verbosity_level = 0;
-		if (code_seen('V')) {
-			// Just 'V' without a number counts as V1.
-			char c = strchr_pointer[1];
-			verbosity_level = (c == ' ' || c == '\t' || c == 0) ? 1 : code_value_short();
-		}
-#endif //SUPPORT_VERBOSITY
-		// Firstly check if we know where we are
-		if (!(axis_known_position[X_AXIS] && axis_known_position[Y_AXIS] && axis_known_position[Z_AXIS])) {
-			// We don't know where we are! HOME!
-			// Push the commands to the front of the message queue in the reverse order!
-			// There shall be always enough space reserved for these commands.
-			repeatcommand_front(); // repeat G80 with all its parameters
-			enquecommand_front_P((PSTR("G28 W0")));
-			break;
-		} 
-		
-		uint8_t nMeasPoints = MESH_MEAS_NUM_X_POINTS;
-		if (code_seen('N')) {
-			nMeasPoints = code_value_uint8();
-			if (nMeasPoints != 7) {
-				nMeasPoints = 3;
-			}
-		}
-		else {
-			nMeasPoints = eeprom_read_byte((uint8_t*)EEPROM_MBL_POINTS_NR);
-		}
-
-		uint8_t nProbeRetry = 3;
-		if (code_seen('R')) {
-			nProbeRetry = code_value_uint8();
-			if (nProbeRetry > 10) {
-				nProbeRetry = 10;
-			}
-		}
-		else {
-			nProbeRetry = eeprom_read_byte((uint8_t*)EEPROM_MBL_PROBE_NR);
-		}
-		bool magnet_elimination = (eeprom_read_byte((uint8_t*)EEPROM_MBL_MAGNET_ELIMINATION) > 0);
-		
-#ifndef PINDA_THERMISTOR
-		if (run == false && temp_cal_active == true && calibration_status_pinda() == true && target_temperature_bed >= 50)
-		{
-			temp_compensation_start();
-			run = true;
-			repeatcommand_front(); // repeat G80 with all its parameters
-			enquecommand_front_P((PSTR("G28 W0")));
-			break;
-		}
-        run = false;
-#endif //PINDA_THERMISTOR
-		// Save custom message state, set a new custom message state to display: Calibrating point 9.
-		CustomMsg custom_message_type_old = custom_message_type;
-		unsigned int custom_message_state_old = custom_message_state;
-		custom_message_type = CustomMsg::MeshBedLeveling;
-		custom_message_state = (nMeasPoints * nMeasPoints) + 10;
-		lcd_update(1);
-
-		mbl.reset(); //reset mesh bed leveling
-
-					 // Reset baby stepping to zero, if the babystepping has already been loaded before. The babystepsTodo value will be
-					 // consumed during the first movements following this statement.
-		babystep_undo();
-
-		// Cycle through all points and probe them
-		// First move up. During this first movement, the babystepping will be reverted.
-		current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
-		plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS] / 60);
-		// The move to the first calibration point.
-		current_position[X_AXIS] = BED_X0;
-		current_position[Y_AXIS] = BED_Y0;
-
-		#ifdef SUPPORT_VERBOSITY
-		if (verbosity_level >= 1)
-		{
-		    bool clamped = world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
-			clamped ? SERIAL_PROTOCOLPGM("First calibration point clamped.\n") : SERIAL_PROTOCOLPGM("No clamping for first calibration point.\n");
-		}
-		#else //SUPPORT_VERBOSITY
-			world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
-		#endif //SUPPORT_VERBOSITY
-
-		plan_buffer_line_curposXYZE(homing_feedrate[X_AXIS] / 30);
-		// Wait until the move is finished.
-		st_synchronize();
-
-		uint8_t mesh_point = 0; //index number of calibration point
-
-		int XY_AXIS_FEEDRATE = homing_feedrate[X_AXIS] / 20;
-		int Z_LIFT_FEEDRATE = homing_feedrate[Z_AXIS] / 40;
-		bool has_z = is_bed_z_jitter_data_valid(); //checks if we have data from Z calibration (offsets of the Z heiths of the 8 calibration points from the first point)
-		#ifdef SUPPORT_VERBOSITY
-		if (verbosity_level >= 1) {
-			has_z ? SERIAL_PROTOCOLPGM("Z jitter data from Z cal. valid.\n") : SERIAL_PROTOCOLPGM("Z jitter data from Z cal. not valid.\n");
-		}
-		#endif // SUPPORT_VERBOSITY
-		int l_feedmultiply = setup_for_endstop_move(false); //save feedrate and feedmultiply, sets feedmultiply to 100
-		while (mesh_point != nMeasPoints * nMeasPoints) {
-			// Get coords of a measuring point.
-			uint8_t ix = mesh_point % nMeasPoints; // from 0 to MESH_NUM_X_POINTS - 1
-			uint8_t iy = mesh_point / nMeasPoints;
-			/*if (!mbl_point_measurement_valid(ix, iy, nMeasPoints, true)) {
-				printf_P(PSTR("Skipping point [%d;%d] \n"), ix, iy);
-				custom_message_state--;
-				mesh_point++;
-				continue; //skip
-			}*/
-			if (iy & 1) ix = (nMeasPoints - 1) - ix; // Zig zag
-			if (nMeasPoints == 7) //if we have 7x7 mesh, compare with Z-calibration for points which are in 3x3 mesh
-			{
-				has_z = ((ix % 3 == 0) && (iy % 3 == 0)) && is_bed_z_jitter_data_valid(); 
-			}
-			float z0 = 0.f;
-			if (has_z && (mesh_point > 0)) {
-				uint16_t z_offset_u = 0;
-				if (nMeasPoints == 7) {
-					z_offset_u = eeprom_read_word((uint16_t*)(EEPROM_BED_CALIBRATION_Z_JITTER + 2 * ((ix/3) + iy - 1)));
-				}
-				else {
-					z_offset_u = eeprom_read_word((uint16_t*)(EEPROM_BED_CALIBRATION_Z_JITTER + 2 * (ix + iy * 3 - 1)));
-				}
-				z0 = mbl.z_values[0][0] + *reinterpret_cast<int16_t*>(&z_offset_u) * 0.01;
-				#ifdef SUPPORT_VERBOSITY
-				if (verbosity_level >= 1) {
-					printf_P(PSTR("Bed leveling, point: %d, calibration Z stored in eeprom: %d, calibration z: %f \n"), mesh_point, z_offset_u, z0);
-				}
-				#endif // SUPPORT_VERBOSITY
-			}
-
-			// Move Z up to MESH_HOME_Z_SEARCH.
-			if((ix == 0) && (iy == 0)) current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
-			else current_position[Z_AXIS] += 2.f / nMeasPoints; //use relative movement from Z coordinate where PINDa triggered on previous point. This makes calibration faster.
-			float init_z_bckp = current_position[Z_AXIS];
-			plan_buffer_line_curposXYZE(Z_LIFT_FEEDRATE);
-			st_synchronize();
-
-			// Move to XY position of the sensor point.
-			current_position[X_AXIS] = BED_X(ix, nMeasPoints);
-			current_position[Y_AXIS] = BED_Y(iy, nMeasPoints);
-
-			//printf_P(PSTR("[%f;%f]\n"), current_position[X_AXIS], current_position[Y_AXIS]);
-
-			
-			#ifdef SUPPORT_VERBOSITY
-			if (verbosity_level >= 1) {
-				bool clamped = world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
-				SERIAL_PROTOCOL(mesh_point);
-				clamped ? SERIAL_PROTOCOLPGM(": xy clamped.\n") : SERIAL_PROTOCOLPGM(": no xy clamping\n");
-			}
-			#else //SUPPORT_VERBOSITY
-				world2machine_clamp(current_position[X_AXIS], current_position[Y_AXIS]);
-			#endif // SUPPORT_VERBOSITY
-
-			//printf_P(PSTR("after clamping: [%f;%f]\n"), current_position[X_AXIS], current_position[Y_AXIS]);
-			plan_buffer_line_curposXYZE(XY_AXIS_FEEDRATE);
-			st_synchronize();
-
-			// Go down until endstop is hit
-			const float Z_CALIBRATION_THRESHOLD = 1.f;
-			if (!find_bed_induction_sensor_point_z((has_z && mesh_point > 0) ? z0 - Z_CALIBRATION_THRESHOLD : -10.f, nProbeRetry)) { //if we have data from z calibration max allowed difference is 1mm for each point, if we dont have data max difference is 10mm from initial point  
-				printf_P(_T(MSG_BED_LEVELING_FAILED_POINT_LOW));
-				break;
-			}
-			if (init_z_bckp - current_position[Z_AXIS] < 0.1f) { //broken cable or initial Z coordinate too low. Go to MESH_HOME_Z_SEARCH and repeat last step (z-probe) again to distinguish between these two cases.
-				//printf_P(PSTR("Another attempt! Current Z position: %f\n"), current_position[Z_AXIS]);
-				current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
-				plan_buffer_line_curposXYZE(Z_LIFT_FEEDRATE);
-				st_synchronize();
-
-				if (!find_bed_induction_sensor_point_z((has_z && mesh_point > 0) ? z0 - Z_CALIBRATION_THRESHOLD : -10.f, nProbeRetry)) { //if we have data from z calibration max allowed difference is 1mm for each point, if we dont have data max difference is 10mm from initial point  
-					printf_P(_T(MSG_BED_LEVELING_FAILED_POINT_LOW));
-					break;
-				}
-				if (MESH_HOME_Z_SEARCH - current_position[Z_AXIS] < 0.1f) {
-					printf_P(PSTR("Bed leveling failed. Sensor disconnected or cable broken.\n"));
-					break;
-				}
-			}
-			if (has_z && fabs(z0 - current_position[Z_AXIS]) > Z_CALIBRATION_THRESHOLD) { //if we have data from z calibration, max. allowed difference is 1mm for each point
-				printf_P(PSTR("Bed leveling failed. Sensor triggered too high.\n"));
-				break;
-			}
-			#ifdef SUPPORT_VERBOSITY
-			if (verbosity_level >= 10) {
-				SERIAL_ECHOPGM("X: ");
-				MYSERIAL.print(current_position[X_AXIS], 5);
-				SERIAL_ECHOLNPGM("");
-				SERIAL_ECHOPGM("Y: ");
-				MYSERIAL.print(current_position[Y_AXIS], 5);
-				SERIAL_PROTOCOLPGM("\n");
-			}
-			#endif // SUPPORT_VERBOSITY
-			float offset_z = 0;
-
-#ifdef PINDA_THERMISTOR
-			offset_z = temp_compensation_pinda_thermistor_offset(current_temperature_pinda);
-#endif //PINDA_THERMISTOR
-//			#ifdef SUPPORT_VERBOSITY
-/*			if (verbosity_level >= 1)
-			{
-				SERIAL_ECHOPGM("mesh bed leveling: ");
-				MYSERIAL.print(current_position[Z_AXIS], 5);
-				SERIAL_ECHOPGM(" offset: ");
-				MYSERIAL.print(offset_z, 5);
-				SERIAL_ECHOLNPGM("");
-			}*/
-//			#endif // SUPPORT_VERBOSITY
-			mbl.set_z(ix, iy, current_position[Z_AXIS] - offset_z); //store measured z values z_values[iy][ix] = z - offset_z;
-
-			custom_message_state--;
-			mesh_point++;
-			lcd_update(1);
-		}
-		current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
-		#ifdef SUPPORT_VERBOSITY
-		if (verbosity_level >= 20) {
-			SERIAL_ECHOLNPGM("Mesh bed leveling while loop finished.");
-			SERIAL_ECHOLNPGM("MESH_HOME_Z_SEARCH: ");
-			MYSERIAL.print(current_position[Z_AXIS], 5);
-		}
-		#endif // SUPPORT_VERBOSITY
-		plan_buffer_line_curposXYZE(Z_LIFT_FEEDRATE);
-		st_synchronize();
-		if (mesh_point != nMeasPoints * nMeasPoints) {
-               Sound_MakeSound(e_SOUND_TYPE_StandardAlert);
-               bool bState;
-               do   {                             // repeat until Z-leveling o.k.
-                    lcd_display_message_fullscreen_P(_i("Some problem encountered, Z-leveling enforced ..."));
-#ifdef TMC2130
-                    lcd_wait_for_click_delay(MSG_BED_LEVELING_FAILED_TIMEOUT);
-                    calibrate_z_auto();           // Z-leveling (X-assembly stay up!!!)
-#else // TMC2130
-                    lcd_wait_for_click_delay(0);  // ~ no timeout
-                    lcd_calibrate_z_end_stop_manual(true); // Z-leveling (X-assembly stay up!!!)
-#endif // TMC2130
-                    // ~ Z-homing (can not be used "G28", because X & Y-homing would have been done before (Z-homing))
-                    bState=enable_z_endstop(false);
-                    current_position[Z_AXIS] -= 1;
-                    plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS] / 40);
-                    st_synchronize();
-                    enable_z_endstop(true);
-#ifdef TMC2130
-                    tmc2130_home_enter(Z_AXIS_MASK);
-#endif // TMC2130
-                    current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
-                    plan_buffer_line_curposXYZE(homing_feedrate[Z_AXIS] / 40);
-                    st_synchronize();
-#ifdef TMC2130
-                    tmc2130_home_exit();
-#endif // TMC2130
-                    enable_z_endstop(bState);
-                    } while (st_get_position_mm(Z_AXIS) > MESH_HOME_Z_SEARCH); // i.e. Z-leveling not o.k.
-//               plan_set_z_position(MESH_HOME_Z_SEARCH); // is not necessary ('do-while' loop always ends at the expected Z-position)
-               custom_message_type=CustomMsg::Status; // display / status-line recovery
-               lcd_update_enable(true);           // display / status-line recovery
-               gcode_G28(true, true, true);       // X & Y & Z-homing (must be after individual Z-homing (problem with spool-holder)!)
-               repeatcommand_front();             // re-run (i.e. of "G80")
-               break;
-		}
-		clean_up_after_endstop_move(l_feedmultiply);
-//		SERIAL_ECHOLNPGM("clean up finished ");
-
-#ifndef PINDA_THERMISTOR
-		if(temp_cal_active == true && calibration_status_pinda() == true) temp_compensation_apply(); //apply PINDA temperature compensation
-#endif
-		babystep_apply(); // Apply Z height correction aka baby stepping before mesh bed leveing gets activated.
-//		SERIAL_ECHOLNPGM("babystep applied");
-		bool eeprom_bed_correction_valid = eeprom_read_byte((unsigned char*)EEPROM_BED_CORRECTION_VALID) == 1;
-		#ifdef SUPPORT_VERBOSITY
-		if (verbosity_level >= 1) {
-			eeprom_bed_correction_valid ? SERIAL_PROTOCOLPGM("Bed correction data valid\n") : SERIAL_PROTOCOLPGM("Bed correction data not valid\n");
-		}
-		#endif // SUPPORT_VERBOSITY
-
-		for (uint8_t i = 0; i < 4; ++i) {
-			unsigned char codes[4] = { 'L', 'R', 'F', 'B' };
-			long correction = 0;
-			if (code_seen(codes[i]))
-				correction = code_value_long();
-			else if (eeprom_bed_correction_valid) {
-				unsigned char *addr = (i < 2) ?
-					((i == 0) ? (unsigned char*)EEPROM_BED_CORRECTION_LEFT : (unsigned char*)EEPROM_BED_CORRECTION_RIGHT) :
-					((i == 2) ? (unsigned char*)EEPROM_BED_CORRECTION_FRONT : (unsigned char*)EEPROM_BED_CORRECTION_REAR);
-				correction = eeprom_read_int8(addr);
-			}
-			if (correction == 0)
-				continue;
-			
-			if (labs(correction) > BED_ADJUSTMENT_UM_MAX) {
-				SERIAL_ERROR_START;
-				SERIAL_ECHOPGM("Excessive bed leveling correction: ");
-				SERIAL_ECHO(correction);
-				SERIAL_ECHOLNPGM(" microns");
-			}
-			else {
-				float offset = float(correction) * 0.001f;
-				switch (i) {
-				case 0:
-					for (uint8_t row = 0; row < nMeasPoints; ++row) {						
-						for (uint8_t col = 0; col < nMeasPoints - 1; ++col) {
-							mbl.z_values[row][col] += offset * (nMeasPoints - 1 - col) / (nMeasPoints - 1);
-						}
-					}
-					break;
-				case 1:
-					for (uint8_t row = 0; row < nMeasPoints; ++row) {					
-						for (uint8_t col = 1; col < nMeasPoints; ++col) {
-							mbl.z_values[row][col] += offset * col / (nMeasPoints - 1);
-						}
-					}
-					break;
-				case 2:
-					for (uint8_t col = 0; col < nMeasPoints; ++col) {						
-						for (uint8_t row = 0; row < nMeasPoints; ++row) {
-							mbl.z_values[row][col] += offset * (nMeasPoints - 1 - row) / (nMeasPoints - 1);
-						}
-					}
-					break;
-				case 3:
-					for (uint8_t col = 0; col < nMeasPoints; ++col) {						
-						for (uint8_t row = 1; row < nMeasPoints; ++row) {
-							mbl.z_values[row][col] += offset * row / (nMeasPoints - 1);
-						}
-					}
-					break;
-				}
-			}
-		}
-//		SERIAL_ECHOLNPGM("Bed leveling correction finished");
-		if (nMeasPoints == 3) {
-			mbl.upsample_3x3(); //interpolation from 3x3 to 7x7 points using largrangian polynomials while using the same array z_values[iy][ix] for storing (just coppying measured data to new destination and interpolating between them)
-		}
-/*
-		        SERIAL_PROTOCOLPGM("Num X,Y: ");
-                SERIAL_PROTOCOL(MESH_NUM_X_POINTS);
-                SERIAL_PROTOCOLPGM(",");
-                SERIAL_PROTOCOL(MESH_NUM_Y_POINTS);
-                SERIAL_PROTOCOLPGM("\nZ search height: ");
-                SERIAL_PROTOCOL(MESH_HOME_Z_SEARCH);
-                SERIAL_PROTOCOLLNPGM("\nMeasured points:");
-                for (int y = MESH_NUM_Y_POINTS-1; y >= 0; y--) {
-                    for (int x = 0; x < MESH_NUM_X_POINTS; x++) {
-                        SERIAL_PROTOCOLPGM("  ");
-                        SERIAL_PROTOCOL_F(mbl.z_values[y][x], 5);
-                    }
-                    SERIAL_PROTOCOLPGM("\n");
-                }
-*/
-		if (nMeasPoints == 7 && magnet_elimination) {
-			mbl_interpolation(nMeasPoints);
-		}
-/*
-		        SERIAL_PROTOCOLPGM("Num X,Y: ");
-                SERIAL_PROTOCOL(MESH_NUM_X_POINTS);
-                SERIAL_PROTOCOLPGM(",");
-                SERIAL_PROTOCOL(MESH_NUM_Y_POINTS);
-                SERIAL_PROTOCOLPGM("\nZ search height: ");
-                SERIAL_PROTOCOL(MESH_HOME_Z_SEARCH);
-                SERIAL_PROTOCOLLNPGM("\nMeasured points:");
-                for (int y = MESH_NUM_Y_POINTS-1; y >= 0; y--) {
-                    for (int x = 0; x < MESH_NUM_X_POINTS; x++) {
-                        SERIAL_PROTOCOLPGM("  ");
-                        SERIAL_PROTOCOL_F(mbl.z_values[y][x], 5);
-                    }
-                    SERIAL_PROTOCOLPGM("\n");
-                }
-*/
-//		SERIAL_ECHOLNPGM("Upsample finished");
-		mbl.active = 1; //activate mesh bed leveling
-//		SERIAL_ECHOLNPGM("Mesh bed leveling activated");
-		go_home_with_z_lift();
-//		SERIAL_ECHOLNPGM("Go home finished");
-		//unretract (after PINDA preheat retraction)
-		if ((degHotend(active_extruder) > EXTRUDE_MINTEMP) && eeprom_read_byte((unsigned char *)EEPROM_TEMP_CAL_ACTIVE) && calibration_status_pinda() && (target_temperature_bed >= 50)) {
-			current_position[E_AXIS] += default_retraction;
-			plan_buffer_line_curposXYZE(400);
-		}
-		KEEPALIVE_STATE(NOT_BUSY);
-		// Restore custom message state
-		lcd_setstatuspgm(_T(WELCOME_MSG));
-		custom_message_type = custom_message_type_old;
-		custom_message_state = custom_message_state_old;
-		mesh_bed_leveling_flag = false;
-		mesh_bed_run_from_menu = false;
-		lcd_update(2);
-		
+        gcode_G80();
 	}
 	break;
 
@@ -5442,10 +5795,9 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
 		farm_mode = 1;
 		PingTime = _millis();
 		eeprom_update_byte((unsigned char *)EEPROM_FARM_MODE, farm_mode);
-		EEPROM_save_B(EEPROM_FARM_NUMBER, &farm_no);
-          SilentModeMenu = SILENT_MODE_OFF;
-          eeprom_update_byte((unsigned char *)EEPROM_SILENT, SilentModeMenu);
-          fCheckModeInit();                       // alternatively invoke printer reset
+		SilentModeMenu = SILENT_MODE_OFF;
+		eeprom_update_byte((unsigned char *)EEPROM_SILENT, SilentModeMenu);
+		fCheckModeInit();                       // alternatively invoke printer reset
 		break;
 
     /*! ### G99 - Deactivate farm mode <a href="https://reprap.org/wiki/G-code#G99:_Deactivate_farm_mode">G99: Deactivate farm mode</a>
@@ -5459,7 +5811,7 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
           fCheckModeInit();                       // alternatively invoke printer reset
 		break;
 	default:
-		printf_P(PSTR("Unknown G code: %s \n"), cmdbuffer + bufindr + CMDHDRSIZE);
+		printf_P(MSG_UNKNOWN_CODE, 'G', cmdbuffer + bufindr + CMDHDRSIZE);
     }
 //	printf_P(_N("END G-CODE=%u\n"), gcode_in_progress);
 	gcode_in_progress = 0;
@@ -5482,7 +5834,7 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
 	   
 	 /*for (++strchr_pointer; *strchr_pointer == ' ' || *strchr_pointer == '\t'; ++strchr_pointer);*/
 	  if (*(strchr_pointer+index) < '0' || *(strchr_pointer+index) > '9') {
-		  printf_P(PSTR("Invalid M code: %s \n"), cmdbuffer + bufindr + CMDHDRSIZE);
+		  printf_P(PSTR("Invalid M code: %s\n"), cmdbuffer + bufindr + CMDHDRSIZE);
 
 	  } else
 	  {
@@ -5492,62 +5844,12 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
     switch(mcode_in_progress)
     {
 
-    /*!
-	### M0, M1 - Stop the printer <a href="https://reprap.org/wiki/G-code#M0:_Stop_or_Unconditional_stop">M0: Stop or Unconditional stop</a>
-    */
-    case 0: // M0 - Unconditional stop - Wait for user button press on LCD
-    case 1: // M1 - Conditional stop - Wait for user button press on LCD
-    {
-      char *src = strchr_pointer + 2;
-
-      codenum = 0;
-
-      bool hasP = false, hasS = false;
-      if (code_seen('P')) {
-        codenum = code_value(); // milliseconds to wait
-        hasP = codenum > 0;
-      }
-      if (code_seen('S')) {
-        codenum = code_value() * 1000; // seconds to wait
-        hasS = codenum > 0;
-      }
-      starpos = strchr(src, '*');
-      if (starpos != NULL) *(starpos) = '\0';
-      while (*src == ' ') ++src;
-      if (!hasP && !hasS && *src != '\0') {
-        lcd_setstatus(src);
-      } else {
-        LCD_MESSAGERPGM(_i("Wait for user..."));////MSG_USERWAIT
-      }
-
-      lcd_ignore_click();				//call lcd_ignore_click aslo for else ???
-      st_synchronize();
-      previous_millis_cmd = _millis();
-      if (codenum > 0){
-        codenum += _millis();  // keep track of when we started waiting
-		KEEPALIVE_STATE(PAUSED_FOR_USER);
-        while(_millis() < codenum && !lcd_clicked()){
-          manage_heater();
-          manage_inactivity(true);
-          lcd_update(0);
-        }
-		KEEPALIVE_STATE(IN_HANDLER);
-        lcd_ignore_click(false);
-      }else{
-        marlin_wait_for_click();
-      }
-      if (IS_SD_PRINTING)
-        LCD_MESSAGERPGM(_T(MSG_RESUMING_PRINT));
-      else
-        LCD_MESSAGERPGM(_T(WELCOME_MSG));
-    }
-    break;
-
     /*!
 	### M17 - Enable all axes <a href="https://reprap.org/wiki/G-code#M17:_Enable.2FPower_all_stepper_motors">M17: Enable/Power all stepper motors</a>
     */
+
     case 17:
-        LCD_MESSAGERPGM(_i("No move."));////MSG_NO_MOVE
+        LCD_MESSAGERPGM(_i("No move."));////MSG_NO_MOVE c=20
         enable_x();
         enable_y();
         enable_z();
@@ -5560,12 +5862,19 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
 
     /*!
 	### M20 - SD Card file list <a href="https://reprap.org/wiki/G-code#M20:_List_SD_card">M20: List SD card</a>
+    #### Usage
+    
+        M20 [ L | T ]
+    #### Parameters
+    - `T` - Report timestamps as well. The value is one uint32_t encoded as hex. Requires host software parsing (Cap:EXTENDED_M20).
+    - `L` - Reports long filenames instead of just short filenames. Requires host software parsing (Cap:EXTENDED_M20).
     */
     case 20:
+      KEEPALIVE_STATE(NOT_BUSY); // do not send busy messages during listing. Inhibits the output of manage_heater()
       SERIAL_PROTOCOLLNRPGM(_N("Begin file list"));////MSG_BEGIN_FILE_LIST
-      card.ls();
+      card.ls(CardReader::ls_param(code_seen('L'), code_seen('T')));
       SERIAL_PROTOCOLLNRPGM(_N("End file list"));////MSG_END_FILE_LIST
-      break;
+    break;
 
     /*!
 	### M21 - Init SD card <a href="https://reprap.org/wiki/G-code#M21:_Initialize_SD_card">M21: Initialize SD card</a>
@@ -5592,7 +5901,7 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
       starpos = (strchr(strchr_pointer + 4,'*'));
 	  if(starpos!=NULL)
         *(starpos)='\0';
-      card.openFile(strchr_pointer + 4,true);
+      card.openFileReadFilteredGcode(strchr_pointer + 4, true);
       break;
 
     /*!
@@ -5641,9 +5950,15 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
 
     /*!
 	### M27 - Get SD status <a href="https://reprap.org/wiki/G-code#M27:_Report_SD_print_status">M27: Report SD print status</a>
+    #### Usage
+	
+	      M27 [ P ]
+	
+	#### Parameters
+	  - `P` - Show full SFN path instead of LFN only.
     */
     case 27:
-      card.getStatus();
+      card.getStatus(code_seen('P'));
       break;
 
     /*!
@@ -5656,7 +5971,7 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
         strchr_pointer = strchr(npos,' ') + 1;
         *(starpos) = '\0';
       }
-      card.openFile(strchr_pointer+4,false);
+      card.openFileWrite(strchr_pointer+4);
       break;
 
     /*! ### M29 - Stop SD write <a href="https://reprap.org/wiki/G-code#M29:_Stop_writing_to_SD_card">M29: Stop writing to SD card</a>
@@ -5717,7 +6032,7 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
 
       if( card.cardOK )
       {
-        card.openFile(namestartpos,true,!call_procedure);
+        card.openFileReadFilteredGcode(namestartpos,!call_procedure);
         if(code_seen('S'))
           if(strchr_pointer<namestartpos) //only if "S" is occuring _before_ the filename
             card.setIndex(code_value_long());
@@ -5860,28 +6175,29 @@ if(eSoundMode!=e_SOUND_MODE_SILENT)
     /*!
 	### M46 - Show the assigned IP address <a href="https://reprap.org/wiki/G-code#M46:_Show_the_assigned_IP_address">M46: Show the assigned IP address.</a>
     */
-    /*
-     case 46:
+    case 46:
     {
         // M46: Prusa3D: Show the assigned IP address.
-        uint8_t ip[4];
-        bool hasIP = card.ToshibaFlashAir_GetIP(ip);
-        if (hasIP) {
-            SERIAL_ECHOPGM("Toshiba FlashAir current IP: ");
-            SERIAL_ECHO(int(ip[0]));
-            SERIAL_ECHOPGM(".");
-            SERIAL_ECHO(int(ip[1]));
-            SERIAL_ECHOPGM(".");
-            SERIAL_ECHO(int(ip[2]));
-            SERIAL_ECHOPGM(".");
-            SERIAL_ECHO(int(ip[3]));
-            SERIAL_ECHOLNPGM("");
+        if (card.ToshibaFlashAir_isEnabled()) {
+            uint8_t ip[4];
+            if (card.ToshibaFlashAir_GetIP(ip)) {
+                // SERIAL_PROTOCOLPGM("Toshiba FlashAir current IP: ");
+                SERIAL_PROTOCOL(uint8_t(ip[0]));
+                SERIAL_PROTOCOL('.');
+                SERIAL_PROTOCOL(uint8_t(ip[1]));
+                SERIAL_PROTOCOL('.');
+                SERIAL_PROTOCOL(uint8_t(ip[2]));
+                SERIAL_PROTOCOL('.');
+                SERIAL_PROTOCOL(uint8_t(ip[3]));
+                SERIAL_PROTOCOLLN();
+            } else {
+                SERIAL_PROTOCOLPGM("?Toshiba FlashAir GetIP failed\n");          
+            }
         } else {
-            SERIAL_ECHOLNPGM("Toshiba FlashAir GetIP failed");          
+            SERIAL_PROTOCOLLNPGM("n/a");          
         }
         break;
     }
-    */
 
     /*!
 	### M47 - Show end stops dialog on the display <a href="https://reprap.org/wiki/G-code#M47:_Show_end_stops_dialog_on_the_display">M47: Show end stops dialog on the display</a>
@@ -6190,31 +6506,41 @@ Sigma_Exit:
 #endif		// Z_PROBE_REPEATABILITY_TEST 
 #endif		// ENABLE_AUTO_BED_LEVELING
 
-	/*!
-	### M73 - Set/get print progress <a href="https://reprap.org/wiki/G-code#M73:_Set.2FGet_build_percentage">M73: Set/Get build percentage</a>
-	#### Usage
-    
-	    M73 [ P | R | Q | S ]
-    
-	#### Parameters
-    - `P` - Percent in normal mode
-    - `R` - Time remaining in normal mode
-    - `Q` - Percent in silent mode
-    - `S` - Time in silent mode
-   */
-	case 73: //M73 show percent done and time remaining
-		if(code_seen('P')) print_percent_done_normal = code_value();
-		if(code_seen('R')) print_time_remaining_normal = code_value();
-		if(code_seen('Q')) print_percent_done_silent = code_value();
-		if(code_seen('S')) print_time_remaining_silent = code_value();
-
-		{
-			const char* _msg_mode_done_remain = _N("%S MODE: Percent done: %d; print time remaining in mins: %d\n");
-			printf_P(_msg_mode_done_remain, _N("NORMAL"), int(print_percent_done_normal), print_time_remaining_normal);
-			printf_P(_msg_mode_done_remain, _N("SILENT"), int(print_percent_done_silent), print_time_remaining_silent);
-		}
-		break;
+    /*!
+    ### M73 - Set/get print progress <a href="https://reprap.org/wiki/G-code#M73:_Set.2FGet_build_percentage">M73: Set/Get build percentage</a>
+    #### Usage
+    
+        M73 [ P | R | Q | S | C | D ]
 
+    #### Parameters
+        - `P` - Percent in normal mode
+        - `R` - Time remaining in normal mode
+        - `Q` - Percent in silent mode
+        - `S` - Time in silent mode
+        - `C` - Time to change/pause/user interaction in normal mode
+        - `D` - Time to change/pause/user interaction in silent mode
+    */
+    case 73: //M73 show percent done, time remaining and time to change/pause
+    {
+        if(code_seen('P')) print_percent_done_normal = code_value();
+        if(code_seen('R')) print_time_remaining_normal = code_value();
+        if(code_seen('Q')) print_percent_done_silent = code_value();
+        if(code_seen('S')) print_time_remaining_silent = code_value();
+        if(code_seen('C')){
+            float print_time_to_change_normal_f = code_value_float();
+            print_time_to_change_normal = ( print_time_to_change_normal_f <= 0 ) ? PRINT_TIME_REMAINING_INIT : print_time_to_change_normal_f;
+        }
+        if(code_seen('D')){
+            float print_time_to_change_silent_f = code_value_float();
+            print_time_to_change_silent = ( print_time_to_change_silent_f <= 0 ) ? PRINT_TIME_REMAINING_INIT : print_time_to_change_silent_f;
+        }
+        {
+            const char* _msg_mode_done_remain = _N("%S MODE: Percent done: %hhd; print time remaining in mins: %d; Change in mins: %d\n");
+            printf_P(_msg_mode_done_remain, _N("NORMAL"), int8_t(print_percent_done_normal), print_time_remaining_normal, print_time_to_change_normal);
+            printf_P(_msg_mode_done_remain, _N("SILENT"), int8_t(print_percent_done_silent), print_time_remaining_silent, print_time_to_change_silent);
+        }
+        break;
+    }
     /*!
 	### M104 - Set hotend temperature <a href="https://reprap.org/wiki/G-code#M104:_Set_Extruder_Temperature">M104: Set Extruder Temperature</a>
 	#### Usage
@@ -6280,96 +6606,52 @@ Sigma_Exit:
       uint8_t extruder;
       if(setTargetedHotend(105, extruder)){
         break;
-        }
-      #if defined(TEMP_0_PIN) && TEMP_0_PIN > -1
-        SERIAL_PROTOCOLPGM("ok T:");
-        SERIAL_PROTOCOL_F(degHotend(extruder),1);
-        SERIAL_PROTOCOLPGM(" /");
-        SERIAL_PROTOCOL_F(degTargetHotend(extruder),1);
-        #if defined(TEMP_BED_PIN) && TEMP_BED_PIN > -1
-          SERIAL_PROTOCOLPGM(" B:");
-          SERIAL_PROTOCOL_F(degBed(),1);
-          SERIAL_PROTOCOLPGM(" /");
-          SERIAL_PROTOCOL_F(degTargetBed(),1);
-        #endif //TEMP_BED_PIN
-        for (int8_t cur_extruder = 0; cur_extruder < EXTRUDERS; ++cur_extruder) {
-          SERIAL_PROTOCOLPGM(" T");
-          SERIAL_PROTOCOL(cur_extruder);
-          SERIAL_PROTOCOL(':');
-          SERIAL_PROTOCOL_F(degHotend(cur_extruder),1);
-          SERIAL_PROTOCOLPGM(" /");
-          SERIAL_PROTOCOL_F(degTargetHotend(cur_extruder),1);
-        }
-      #else
-        SERIAL_ERROR_START;
-        SERIAL_ERRORLNRPGM(_i("No thermistors - no temperature"));////MSG_ERR_NO_THERMISTORS
-      #endif
-
-        SERIAL_PROTOCOLPGM(" @:");
-      #ifdef EXTRUDER_WATTS
-        SERIAL_PROTOCOL((EXTRUDER_WATTS * getHeaterPower(tmp_extruder))/127);
-        SERIAL_PROTOCOLPGM("W");
-      #else
-        SERIAL_PROTOCOL(getHeaterPower(extruder));
-      #endif
-
-        SERIAL_PROTOCOLPGM(" B@:");
-      #ifdef BED_WATTS
-        SERIAL_PROTOCOL((BED_WATTS * getHeaterPower(-1))/127);
-        SERIAL_PROTOCOLPGM("W");
-      #else
-        SERIAL_PROTOCOL(getHeaterPower(-1));
-      #endif
-
-#ifdef PINDA_THERMISTOR
-		SERIAL_PROTOCOLPGM(" P:");
-		SERIAL_PROTOCOL_F(current_temperature_pinda,1);
-#endif //PINDA_THERMISTOR
-
-#ifdef AMBIENT_THERMISTOR
-		SERIAL_PROTOCOLPGM(" A:");
-		SERIAL_PROTOCOL_F(current_temperature_ambient,1);
-#endif //AMBIENT_THERMISTOR
-
-
-        #ifdef SHOW_TEMP_ADC_VALUES
-          {float raw = 0.0;
-
-          #if defined(TEMP_BED_PIN) && TEMP_BED_PIN > -1
-            SERIAL_PROTOCOLPGM("    ADC B:");
-            SERIAL_PROTOCOL_F(degBed(),1);
-            SERIAL_PROTOCOLPGM("C->");
-            raw = rawBedTemp();
-            SERIAL_PROTOCOL_F(raw/OVERSAMPLENR,5);
-            SERIAL_PROTOCOLPGM(" Rb->");
-            SERIAL_PROTOCOL_F(100 * (1 + (PtA * (raw/OVERSAMPLENR)) + (PtB * sq((raw/OVERSAMPLENR)))), 5);
-            SERIAL_PROTOCOLPGM(" Rxb->");
-            SERIAL_PROTOCOL_F(raw, 5);
-          #endif
-          for (int8_t cur_extruder = 0; cur_extruder < EXTRUDERS; ++cur_extruder) {
-            SERIAL_PROTOCOLPGM("  T");
-            SERIAL_PROTOCOL(cur_extruder);
-            SERIAL_PROTOCOLPGM(":");
-            SERIAL_PROTOCOL_F(degHotend(cur_extruder),1);
-            SERIAL_PROTOCOLPGM("C->");
-            raw = rawHotendTemp(cur_extruder);
-            SERIAL_PROTOCOL_F(raw/OVERSAMPLENR,5);
-            SERIAL_PROTOCOLPGM(" Rt");
-            SERIAL_PROTOCOL(cur_extruder);
-            SERIAL_PROTOCOLPGM("->");
-            SERIAL_PROTOCOL_F(100 * (1 + (PtA * (raw/OVERSAMPLENR)) + (PtB * sq((raw/OVERSAMPLENR)))), 5);
-            SERIAL_PROTOCOLPGM(" Rx");
-            SERIAL_PROTOCOL(cur_extruder);
-            SERIAL_PROTOCOLPGM("->");
-            SERIAL_PROTOCOL_F(raw, 5);
-          }}
-        #endif
-		SERIAL_PROTOCOLLN("");
-		KEEPALIVE_STATE(NOT_BUSY);
-      return;
+      }
+      
+      SERIAL_PROTOCOLPGM("ok ");
+      gcode_M105(extruder);
+      
+      cmdqueue_pop_front(); //prevent an ok after the command since this command uses an ok at the beginning.
+      cmdbuffer_front_already_processed = true;
+      
       break;
     }
 
+#if defined(AUTO_REPORT)
+    /*!
+	### M155 - Automatically send status <a href="https://reprap.org/wiki/G-code#M155:_Automatically_send_temperatures">M155: Automatically send temperatures</a>
+	#### Usage
+	
+		M155 [ S ] [ C ]
+	
+	#### Parameters
+	
+	- `S` - Set autoreporting interval in seconds. 0 to disable. Maximum: 255
+	- `C` - Activate auto-report function (bit mask). Default is temperature.
+
+          bit 0 = Auto-report temperatures
+          bit 1 = Auto-report fans
+          bit 2 = Auto-report position
+          bit 3 = free
+          bit 4 = free
+          bit 5 = free
+          bit 6 = free
+          bit 7 = free
+     */
+    case 155:
+    {
+        if (code_seen('S')){
+            autoReportFeatures.SetPeriod( code_value_uint8() );
+        }
+        if (code_seen('C')){
+            autoReportFeatures.SetMask(code_value());
+        } else{
+            autoReportFeatures.SetMask(1); //Backwards compability to host systems like Octoprint to send only temp if paramerter `C`isn't used.
+        }
+   }
+    break;
+#endif //AUTO_REPORT
+
     /*!
 	### M109 - Wait for extruder temperature <a href="https://reprap.org/wiki/G-code#M109:_Set_Extruder_Temperature_and_Wait">M109: Set Extruder Temperature and Wait</a>
     #### Usage
@@ -6470,7 +6752,7 @@ Sigma_Exit:
         target_direction = isHeatingBed(); // true if heating, false if cooling
 
 		KEEPALIVE_STATE(NOT_BUSY);
-        while ( (target_direction)&&(!cancel_heatup) ? (isHeatingBed()) : (isCoolingBed()&&(CooldownNoWait==false)) )
+        while ( (!cancel_heatup) && (target_direction ? (isHeatingBed()) : (isCoolingBed()&&(CooldownNoWait==false))) )
         {
           if(( _millis() - codenum) > 1000 ) //Print Temp Reading every 1 second while heating up.
           {
@@ -6482,7 +6764,7 @@ Sigma_Exit:
 				  SERIAL_PROTOCOL((int)active_extruder);
 				  SERIAL_PROTOCOLPGM(" B:");
 				  SERIAL_PROTOCOL_F(degBed(), 1);
-				  SERIAL_PROTOCOLLN("");
+				  SERIAL_PROTOCOLLN();
 			  }
 				  codenum = _millis();
 			  
@@ -6606,7 +6888,7 @@ Sigma_Exit:
 	  - `X` - X axis
 	  - `Y` - Y axis
 	  - `Z` - Z axis
-	  - `E` - Exruder
+	  - `E` - Extruder
 
 	### M18 - Disable steppers <a href="https://reprap.org/wiki/G-code#M18:_Disable_all_stepper_motors">M18: Disable all stepper motors</a>
 	Equal to M84 (compatibility)
@@ -6752,7 +7034,7 @@ Sigma_Exit:
 		else {
 			SERIAL_ECHO_START;
 			SERIAL_ECHOPAIR("M113 S", (unsigned long)host_keepalive_interval);
-			SERIAL_PROTOCOLLN("");
+			SERIAL_PROTOCOLLN();
 		}
 		break;
 
@@ -6802,6 +7084,9 @@ Sigma_Exit:
           SERIAL_ECHOPGM(STRINGIFY(EXTRUDERS)); 
           SERIAL_ECHOPGM(" UUID:"); 
           SERIAL_ECHOLNPGM(MACHINE_UUID);
+#ifdef EXTENDED_CAPABILITIES_REPORT
+          extended_capabilities_report();
+#endif //EXTENDED_CAPABILITIES_REPORT
       }
       break;
 
@@ -6823,19 +7108,21 @@ Sigma_Exit:
       lcd_setstatus(strchr_pointer + 5);
       break;*/
 
+#ifdef M120_M121_ENABLED
     /*!
-	### M120 - Enable endstops <a href="https://reprap.org/wiki/G-code#M120:_Enable_endstop_detection">M120: Enable endstop detection</a>
+    ### M120 - Enable endstops <a href="https://reprap.org/wiki/G-code#M120:_Enable_endstop_detection">M120: Enable endstop detection</a>
     */
     case 120:
-      enable_endstops(false) ;
+      enable_endstops(true) ;
       break;
 
     /*!
-	### M121 - Disable endstops <a href="https://reprap.org/wiki/G-code#M121:_Disable_endstop_detection">M121: Disable endstop detection</a>
+    ### M121 - Disable endstops <a href="https://reprap.org/wiki/G-code#M121:_Disable_endstop_detection">M121: Disable endstop detection</a>
     */
     case 121:
-      enable_endstops(true) ;
+      enable_endstops(false) ;
       break;
+#endif //M120_M121_ENABLED
 
     /*!
 	### M119 - Get endstop states <a href="https://reprap.org/wiki/G-code#M119:_Get_Endstop_Status">M119: Get Endstop Status</a>
@@ -6843,7 +7130,7 @@ Sigma_Exit:
     */
     case 119:
     SERIAL_PROTOCOLRPGM(_N("Reporting endstop status"));////MSG_M119_REPORT
-    SERIAL_PROTOCOLLN("");
+    SERIAL_PROTOCOLLN();
       #if defined(X_MIN_PIN) && X_MIN_PIN > -1
         SERIAL_PROTOCOLRPGM(_n("x_min: "));////MSG_X_MIN
         if(READ(X_MIN_PIN)^X_MIN_ENDSTOP_INVERTING){
@@ -6851,7 +7138,7 @@ Sigma_Exit:
         }else{
           SERIAL_PROTOCOLRPGM(MSG_ENDSTOP_OPEN);
         }
-        SERIAL_PROTOCOLLN("");
+        SERIAL_PROTOCOLLN();
       #endif
       #if defined(X_MAX_PIN) && X_MAX_PIN > -1
         SERIAL_PROTOCOLRPGM(_n("x_max: "));////MSG_X_MAX
@@ -6860,7 +7147,7 @@ Sigma_Exit:
         }else{
           SERIAL_PROTOCOLRPGM(MSG_ENDSTOP_OPEN);
         }
-        SERIAL_PROTOCOLLN("");
+        SERIAL_PROTOCOLLN();
       #endif
       #if defined(Y_MIN_PIN) && Y_MIN_PIN > -1
         SERIAL_PROTOCOLRPGM(_n("y_min: "));////MSG_Y_MIN
@@ -6869,7 +7156,7 @@ Sigma_Exit:
         }else{
           SERIAL_PROTOCOLRPGM(MSG_ENDSTOP_OPEN);
         }
-        SERIAL_PROTOCOLLN("");
+        SERIAL_PROTOCOLLN();
       #endif
       #if defined(Y_MAX_PIN) && Y_MAX_PIN > -1
         SERIAL_PROTOCOLRPGM(_n("y_max: "));////MSG_Y_MAX
@@ -6878,7 +7165,7 @@ Sigma_Exit:
         }else{
           SERIAL_PROTOCOLRPGM(MSG_ENDSTOP_OPEN);
         }
-        SERIAL_PROTOCOLLN("");
+        SERIAL_PROTOCOLLN();
       #endif
       #if defined(Z_MIN_PIN) && Z_MIN_PIN > -1
         SERIAL_PROTOCOLRPGM(MSG_Z_MIN);
@@ -6887,7 +7174,7 @@ Sigma_Exit:
         }else{
           SERIAL_PROTOCOLRPGM(MSG_ENDSTOP_OPEN);
         }
-        SERIAL_PROTOCOLLN("");
+        SERIAL_PROTOCOLLN();
       #endif
       #if defined(Z_MAX_PIN) && Z_MAX_PIN > -1
         SERIAL_PROTOCOLRPGM(MSG_Z_MAX);
@@ -6896,11 +7183,33 @@ Sigma_Exit:
         }else{
           SERIAL_PROTOCOLRPGM(MSG_ENDSTOP_OPEN);
         }
-        SERIAL_PROTOCOLLN("");
+        SERIAL_PROTOCOLLN();
       #endif
       break;
       //!@todo update for all axes, use for loop
+
+#if (defined(FANCHECK) && (((defined(TACH_0) && (TACH_0 >-1)) || (defined(TACH_1) && (TACH_1 > -1)))))
+    /*!
+	### M123 - Tachometer value <a href="https://www.reprap.org/wiki/G-code#M123:_Tachometer_value_.28RepRap_.26_Prusa.29">M123: Tachometer value</a>
+  This command is used to report fan speeds and fan pwm values.
+  #### Usage
     
+        M123
+
+    - E0:     - Hotend fan speed in RPM
+    - PRN1:   - Part cooling fans speed in RPM
+    - E0@:    - Hotend fan PWM value
+    - PRN1@:  -Part cooling fan PWM value
+
+  _Example:_
+
+    E0:3240 RPM PRN1:4560 RPM E0@:255 PRN1@:255
+
+    */
+    case 123:
+    gcode_M123();
+    break;
+#endif //FANCHECK and TACH_0 and TACH_1
 
     #ifdef BLINKM
     /*!
@@ -7021,7 +7330,7 @@ Sigma_Exit:
     For each axis individually.
     */
     case 203: // M203 max feedrate mm/sec
-		for (int8_t i = 0; i < NUM_AXIS; i++)
+		for (uint8_t i = 0; i < NUM_AXIS; i++)
 		{
 			if (code_seen(axis_codes[i]))
 			{
@@ -7072,7 +7381,7 @@ Sigma_Exit:
           // Legacy acceleration format. This format is used by the legacy Marlin, MK2 or MK3 firmware,
           // and it is also generated by Slic3r to control acceleration per extrusion type
           // (there is a separate acceleration settings in Slicer for perimeter, first layer etc).
-          cs.acceleration = code_value();
+          cs.acceleration = cs.travel_acceleration = code_value();
           // Interpret the T value as retract acceleration in the old Marlin format.
           if(code_seen('T'))
             cs.retract_acceleration = code_value();
@@ -7082,13 +7391,8 @@ Sigma_Exit:
             cs.acceleration = code_value();
           if(code_seen('R'))
             cs.retract_acceleration = code_value();
-          if(code_seen('T')) {
-            // Interpret the T value as the travel acceleration in the new Marlin format.
-            /*!
-            @todo Prusa3D firmware currently does not support travel acceleration value independent from the extruding acceleration value.
-            */
-            // travel_acceleration = code_value();
-          }
+          if(code_seen('T'))
+            cs.travel_acceleration = code_value();
         }
       }
       break;
@@ -7142,13 +7446,14 @@ Sigma_Exit:
     - `Z` - Z axis offset
 	*/
     case 206:
-      for(int8_t i=0; i < 3; i++)
+      for(uint8_t i=0; i < 3; i++)
       {
         if(code_seen(axis_codes[i])) cs.add_homing[i] = code_value();
       }
       break;
-    #ifdef FWRETRACT
 
+
+#ifdef FWRETRACT
     /*!
 	### M207 - Set firmware retraction <a href="https://reprap.org/wiki/G-code#M207:_Set_retract_length">M207: Set retract length</a>
 	#### Usage
@@ -7246,7 +7551,9 @@ Sigma_Exit:
       }
 
     }break;
-    #endif // FWRETRACT
+#endif // FWRETRACT
+
+
     #if EXTRUDERS > 1
 
     /*!
@@ -7300,17 +7607,26 @@ Sigma_Exit:
     */
     case 220: // M220 S<factor in percent>- set speed factor override percentage
     {
-      if (code_seen('B')) //backup current speed factor
-      {
-        saved_feedmultiply_mm = feedmultiply;
-      }
-      if(code_seen('S'))
-      {		
-        feedmultiply = code_value() ;
-      }
-      if (code_seen('R')) { //restore previous feedmultiply
-        feedmultiply = saved_feedmultiply_mm;
-      }
+        bool codesWereSeen = false;
+        if (code_seen('B')) //backup current speed factor
+        {
+            saved_feedmultiply_mm = feedmultiply;
+            codesWereSeen = true;
+        }
+        if (code_seen('S'))
+        {
+            feedmultiply = code_value();
+            codesWereSeen = true;
+        }
+        if (code_seen('R')) //restore previous feedmultiply
+        {
+            feedmultiply = saved_feedmultiply_mm;
+            codesWereSeen = true;
+        }
+        if (!codesWereSeen)
+        {
+            printf_P(PSTR("%i%%\n"), feedmultiply);
+        }
     }
     break;
 
@@ -7326,23 +7642,26 @@ Sigma_Exit:
     */
     case 221: // M221 S<factor in percent>- set extrude factor override percentage
     {
-      if(code_seen('S'))
-      {
-        int tmp_code = code_value();
-        if (code_seen('T'))
+        if (code_seen('S'))
         {
-          uint8_t extruder;
-          if(setTargetedHotend(221, extruder)){
-            break;
-          }
-          extruder_multiply[extruder] = tmp_code;
+            int tmp_code = code_value();
+            if (code_seen('T'))
+            {
+                uint8_t extruder;
+                if (setTargetedHotend(221, extruder))
+                    break;
+                extruder_multiply[extruder] = tmp_code;
+            }
+            else
+            {
+                extrudemultiply = tmp_code ;
+            }
         }
         else
         {
-          extrudemultiply = tmp_code ;
+            printf_P(PSTR("%i%%\n"), extrudemultiply);
         }
-      }
-      calculate_extruder_multipliers();
+        calculate_extruder_multipliers();
     }
     break;
 
@@ -7453,7 +7772,7 @@ Sigma_Exit:
           SERIAL_PROTOCOL(servo_index);
           SERIAL_PROTOCOL(": ");
           SERIAL_PROTOCOL(servos[servo_index].read());
-          SERIAL_PROTOCOLLN("");
+          SERIAL_PROTOCOLLN();
         }
       }
       break;
@@ -7529,7 +7848,7 @@ Sigma_Exit:
         //Kc does not have scaling applied above, or in resetting defaults
         SERIAL_PROTOCOL(Kc);
         #endif
-        SERIAL_PROTOCOLLN("");
+        SERIAL_PROTOCOLLN();
       }
       break;
     #endif //PIDTEMP
@@ -7562,7 +7881,7 @@ Sigma_Exit:
         SERIAL_PROTOCOL(unscalePID_i(cs.bedKi));
         SERIAL_PROTOCOL(" d:");
         SERIAL_PROTOCOL(unscalePID_d(cs.bedKd));
-        SERIAL_PROTOCOLLN("");
+        SERIAL_PROTOCOLLN();
       }
       break;
     #endif //PIDTEMP
@@ -7785,7 +8104,7 @@ Sigma_Exit:
           cs.zprobe_zoffset = -value; // compare w/ line 278 of ConfigurationStore.cpp
           SERIAL_ECHO_START;
           SERIAL_ECHOLNRPGM(CAT4(MSG_ZPROBE_ZOFFSET, " ", MSG_OK,PSTR("")));
-          SERIAL_PROTOCOLLN("");
+          SERIAL_PROTOCOLLN();
         }
         else
         {
@@ -7795,7 +8114,7 @@ Sigma_Exit:
           SERIAL_ECHO(Z_PROBE_OFFSET_RANGE_MIN);
           SERIAL_ECHORPGM(MSG_Z_MAX);
           SERIAL_ECHO(Z_PROBE_OFFSET_RANGE_MAX);
-          SERIAL_PROTOCOLLN("");
+          SERIAL_PROTOCOLLN();
         }
       }
       else
@@ -7803,12 +8122,42 @@ Sigma_Exit:
           SERIAL_ECHO_START;
           SERIAL_ECHOLNRPGM(CAT2(MSG_ZPROBE_ZOFFSET, PSTR(" : ")));
           SERIAL_ECHO(-cs.zprobe_zoffset);
-          SERIAL_PROTOCOLLN("");
+          SERIAL_PROTOCOLLN();
       }
       break;
     }
     #endif // CUSTOM_M_CODE_SET_Z_PROBE_OFFSET
 
+	/*!
+	### M552 - Set IP address <a href="https://reprap.org/wiki/G-code#M552:_Set_IP_address.2C_enable.2Fdisable_network_interface">M552: Set IP address, enable/disable network interface"</a>
+    Sets the printer IP address that is shown in the support menu. Designed to be used with the help of host software.
+    If P is not specified nothing happens.
+    If the structure of the IP address is invalid, 0.0.0.0 is assumed and nothing is shown on the screen in the Support menu.
+    #### Usage
+    
+        M552 [ P<IP_address> ]
+    
+    #### Parameters
+    - `P` - The IP address in xxx.xxx.xxx.xxx format. Eg: P192.168.1.14
+	*/
+    case 552:
+    {
+        if (code_seen('P'))
+        {
+            uint8_t valCnt = 0;
+            IP_address = 0;
+            do
+            {
+                *strchr_pointer = '*';
+                ((uint8_t*)&IP_address)[valCnt] = code_value_short();
+                valCnt++;
+            } while ((valCnt < 4) && code_seen('.'));
+            
+            if (valCnt != 4)
+                IP_address = 0;
+        }
+    } break;
+
     #ifdef FILAMENTCHANGEENABLE
 
     /*!
@@ -7892,7 +8241,7 @@ Sigma_Exit:
           #endif
         }
 
-		if (mmu_enabled && code_seen("AUTO"))
+		if (mmu_enabled && code_seen_P(PSTR("AUTO")))
 			automatic = true;
 
 		gcode_M600(automatic, x_position, y_position, z_shift, e_shift_init, e_shift_late);
@@ -7910,34 +8259,34 @@ Sigma_Exit:
     /*!
     ### M25 - Pause SD print <a href="https://reprap.org/wiki/G-code#M25:_Pause_SD_print">M25: Pause SD print</a>
     */
-	case 25:
-	case 601:
-	{
-        if (!isPrintPaused)
-        {
+    case 25:
+    case 601:
+    {
+        if (!isPrintPaused) {
             st_synchronize();
+            ClearToSend(); //send OK even before the command finishes executing because we want to make sure it is not skipped because of cmdqueue_pop_front();
             cmdqueue_pop_front(); //trick because we want skip this command (M601) after restore
             lcd_pause_print();
         }
-	}
-	break;
+    }
+    break;
 
     /*!
-	### M602 - Resume print <a href="https://reprap.org/wiki/G-code#M602:_Resume_print">M602: Resume print</a>
+    ### M602 - Resume print <a href="https://reprap.org/wiki/G-code#M602:_Resume_print">M602: Resume print</a>
     */
-	case 602: {
-	  if (isPrintPaused)
-          lcd_resume_print();
-	}
-	break;
+    case 602:
+    {
+        if (isPrintPaused) lcd_resume_print();
+    }
+    break;
 
     /*!
     ### M603 - Stop print <a href="https://reprap.org/wiki/G-code#M603:_Stop_print">M603: Stop print</a>
     */
-	case 603: {
-		lcd_print_stop();
-	}
-	break;
+    case 603: {
+        lcd_print_stop();
+    }
+    break;
 
 #ifdef PINDA_THERMISTOR
     /*!
@@ -7965,7 +8314,7 @@ Sigma_Exit:
 
 		SERIAL_PROTOCOLPGM("Wait for PINDA target temperature:");
 		SERIAL_PROTOCOL(set_target_pinda);
-		SERIAL_PROTOCOLLN("");
+		SERIAL_PROTOCOLLN();
 
 		codenum = _millis();
 		cancel_heatup = false;
@@ -8024,7 +8373,7 @@ Sigma_Exit:
 				SERIAL_PROTOCOL(usteps);
 				SERIAL_PROTOCOLPGM(", ");
 				SERIAL_PROTOCOL(mm * 1000);
-				SERIAL_PROTOCOLLN("");
+				SERIAL_PROTOCOLLN();
 			}
 		}
 		else if (code_seen('!')) { // ! - Set factory default values
@@ -8067,7 +8416,7 @@ Sigma_Exit:
 						SERIAL_PROTOCOL(usteps);
 						SERIAL_PROTOCOLPGM(", ");
 						SERIAL_PROTOCOL(mm * 1000);
-						SERIAL_PROTOCOLLN("");
+						SERIAL_PROTOCOLLN();
 					}
 				}
 			}
@@ -8158,7 +8507,7 @@ Sigma_Exit:
                     if(code_seen('P'))
                          fw_version_check(++strchr_pointer);
                     else if(code_seen('Q'))
-                         SERIAL_PROTOCOLLN(FW_VERSION);
+                         SERIAL_PROTOCOLLNRPGM(FW_VERSION_STR_P());
                     break;
                case ClPrintChecking::_Gcode:      // ~ .5
                     if(code_seen('P'))
@@ -8196,6 +8545,7 @@ Sigma_Exit:
     /*!
 	### M907 - Set digital trimpot motor current in mA using axis codes <a href="https://reprap.org/wiki/G-code#M907:_Set_digital_trimpot_motor">M907: Set digital trimpot motor</a>
 	Set digital trimpot motor current using axis codes (X, Y, Z, E, B, S).
+    M907 has no effect when the experimental Extruder motor current scaling mode is active (that applies to farm printing as well)
 	#### Usage
     
         M907 [ X | Y | Z | E | B | S ]
@@ -8212,16 +8562,20 @@ Sigma_Exit:
     {
 #ifdef TMC2130
         // See tmc2130_cur2val() for translation to 0 .. 63 range
-        for (int i = 0; i < NUM_AXIS; i++)
-			if(code_seen(axis_codes[i]))
-			{
-				long cur_mA = code_value_long();
-				uint8_t val = tmc2130_cur2val(cur_mA);
-				tmc2130_set_current_h(i, val);
-				tmc2130_set_current_r(i, val);
-				//if (i == E_AXIS) printf_P(PSTR("E-axis current=%ldmA\n"), cur_mA);
-			}
-
+        for (uint_least8_t i = 0; i < NUM_AXIS; i++){
+            if(code_seen(axis_codes[i])){
+                if( i == E_AXIS && FarmOrUserECool() ){
+                    SERIAL_ECHORPGM(eMotorCurrentScalingEnabled);
+                    SERIAL_ECHOLNPGM(", M907 E ignored");
+                    continue;
+                }
+                long cur_mA = code_value_long();
+                uint8_t val = tmc2130_cur2val(cur_mA);
+                tmc2130_set_current_h(i, val);
+                tmc2130_set_current_r(i, val);
+                //if (i == E_AXIS) printf_P(PSTR("E-axis current=%ldmA\n"), cur_mA);
+            }
+        }
 #else //TMC2130
       #if defined(DIGIPOTSS_PIN) && DIGIPOTSS_PIN > -1
         for(int i=0;i<NUM_AXIS;i++) if(code_seen(axis_codes[i])) st_current_set(i,code_value());
@@ -8272,7 +8626,7 @@ Sigma_Exit:
     */
 	case 910:
     {
-		tmc2130_init();
+		tmc2130_init(TMCInitParams(false, FarmOrUserECool()));
     }
     break;
 
@@ -8339,7 +8693,7 @@ Sigma_Exit:
     {
 		tmc2130_mode = TMC2130_MODE_NORMAL;
 		update_mode_profile();
-		tmc2130_init();
+		tmc2130_init(TMCInitParams(false, FarmOrUserECool()));
     }
     break;
 
@@ -8351,7 +8705,7 @@ Sigma_Exit:
     {
 		tmc2130_mode = TMC2130_MODE_SILENT;
 		update_mode_profile();
-		tmc2130_init();
+		tmc2130_init(TMCInitParams(false, FarmOrUserECool()));
     }
     break;
 
@@ -8446,7 +8800,7 @@ Sigma_Exit:
     case 350: 
     {
 	#ifdef TMC2130
-		for (int i=0; i<NUM_AXIS; i++) 
+		for (uint_least8_t i=0; i<NUM_AXIS; i++) 
 		{
 			if(code_seen(axis_codes[i]))
 			{
@@ -8583,7 +8937,7 @@ Sigma_Exit:
 	#### End of M-Commands
     */
 	default: 
-		printf_P(PSTR("Unknown M code: %s \n"), cmdbuffer + bufindr + CMDHDRSIZE);
+		printf_P(MSG_UNKNOWN_CODE, 'M', cmdbuffer + bufindr + CMDHDRSIZE);
     }
 //	printf_P(_N("END M-CODE=%u\n"), mcode_in_progress);
 	mcode_in_progress = 0;
@@ -8602,11 +8956,13 @@ Sigma_Exit:
   */
   else if(code_seen('T'))
   {
+      static const char duplicate_Tcode_ignored[] PROGMEM = "Duplicate T-code ignored.";
+      
       int index;
       bool load_to_nozzle = false;
       for (index = 1; *(strchr_pointer + index) == ' ' || *(strchr_pointer + index) == '\t'; index++);
 
-	  *(strchr_pointer + index) = tolower(*(strchr_pointer + index));
+      *(strchr_pointer + index) = tolower(*(strchr_pointer + index));
 
       if ((*(strchr_pointer + index) < '0' || *(strchr_pointer + index) > '4') && *(strchr_pointer + index) != '?' && *(strchr_pointer + index) != 'x' && *(strchr_pointer + index) != 'c') {
           SERIAL_ECHOLNPGM("Invalid T code.");
@@ -8617,7 +8973,7 @@ Sigma_Exit:
 			tmp_extruder = choose_menu_P(_T(MSG_CHOOSE_FILAMENT), _T(MSG_FILAMENT));
 			if ((tmp_extruder == mmu_extruder) && mmu_fil_loaded) //dont execute the same T-code twice in a row
 			{
-				printf_P(PSTR("Duplicate T-code ignored.\n"));
+				puts_P(duplicate_Tcode_ignored);
 			}
 			else
 			{
@@ -8662,7 +9018,7 @@ Sigma_Exit:
           {
               if ((tmp_extruder == mmu_extruder) && mmu_fil_loaded) //dont execute the same T-code twice in a row
               {
-                  printf_P(PSTR("Duplicate T-code ignored.\n"));
+                  puts_P(duplicate_Tcode_ignored);
               }
 			  else
 			  {
@@ -8736,7 +9092,7 @@ Sigma_Exit:
               }
               else {
 #if EXTRUDERS > 1
-                  boolean make_move = false;
+                  bool make_move = false;
 #endif
                   if (code_seen('F')) {
 #if EXTRUDERS > 1
@@ -8818,7 +9174,9 @@ Sigma_Exit:
     */
 	case 1:
 		dcode_1(); break;
+#endif
 
+#if defined DEBUG_DCODE2 || defined DEBUG_DCODES
     /*!
     ### D2 - Read/Write RAM <a href="https://reprap.org/wiki/G-code#D2:_Read.2FWrite_RAM">D3: Read/Write RAM</a>
     This command can be used without any additional parameters. It will read the entire RAM.
@@ -8905,7 +9263,7 @@ Sigma_Exit:
 	case 5:
 		dcode_5(); break;
 #endif //DEBUG_DCODE5
-#ifdef DEBUG_DCODES
+#if defined DEBUG_DCODE6 || defined DEBUG_DCODES
 
     /*!
     ### D6 - Read/Write external FLASH <a href="https://reprap.org/wiki/G-code#D6:_Read.2FWrite_external_FLASH">D6: Read/Write external Flash</a>
@@ -8913,6 +9271,8 @@ Sigma_Exit:
     */
 	case 6:
 		dcode_6(); break;
+#endif
+#ifdef DEBUG_DCODES
 
     /*!
     ### D7 - Read/Write Bootloader <a href="https://reprap.org/wiki/G-code#D7:_Read.2FWrite_Bootloader">D7: Read/Write Bootloader</a>
@@ -8966,8 +9326,74 @@ Sigma_Exit:
     ### D12 - Time <a href="https://reprap.org/wiki/G-code#D12:_Time">D12: Time</a>
     Writes the current time in the log file.
     */
-
 #endif //DEBUG_DCODES
+
+#ifdef XFLASH_DUMP
+    /*!
+    ### D20 - Generate an offline crash dump <a href="https://reprap.org/wiki/G-code#D20:_Generate_an_offline_crash_dump">D20: Generate an offline crash dump</a>
+    Generate a crash dump for later retrival.
+    #### Usage
+
+     D20 [E]
+
+    ### Parameters
+    - `E` - Perform an emergency crash dump (resets the printer).
+    ### Notes
+    - A crash dump can be later recovered with D21, or cleared with D22.
+    - An emergency crash dump includes register data, but will cause the printer to reset after the dump
+      is completed.
+    */
+    case 20: {
+        dcode_20();
+        break;
+    };
+
+    /*!
+    ### D21 - Print crash dump to serial <a href="https://reprap.org/wiki/G-code#D21:_Print_crash_dump_to_serial">D21: Print crash dump to serial</a>
+    Output the complete crash dump (if present) to the serial.
+    #### Usage
+
+     D21
+
+    ### Notes
+    - The starting address can vary between builds, but it's always at the beginning of the data section.
+    */
+    case 21: {
+        dcode_21();
+        break;
+    };
+
+    /*!
+    ### D22 - Clear crash dump state <a href="https://reprap.org/wiki/G-code#D22:_Clear_crash_dump_state">D22: Clear crash dump state</a>
+    Clear an existing internal crash dump.
+    #### Usage
+
+     D22
+    */
+    case 22: {
+        dcode_22();
+        break;
+    };
+#endif //XFLASH_DUMP
+
+#ifdef EMERGENCY_SERIAL_DUMP
+    /*!
+    ### D23 - Request emergency dump on serial <a href="https://reprap.org/wiki/G-code#D23:_Request_emergency_dump_on_serial">D23: Request emergency dump on serial</a>
+    On boards without offline dump support, request online dumps to the serial port on firmware faults.
+    When online dumps are enabled, the FW will dump memory on the serial before resetting.
+    #### Usage
+
+     D23 [E] [R]
+    #### Parameters
+    - `E` - Perform an emergency crash dump (resets the printer).
+    - `R` - Disable online dumps.
+    */
+    case 23: {
+        dcode_23();
+        break;
+    };
+#endif
+
 #ifdef HEATBED_ANALYSIS
 
     /*!
@@ -9096,6 +9522,9 @@ Sigma_Exit:
 #endif //FILAMENT_SENSOR
 
 #endif //DEBUG_DCODES
+
+    default:
+        printf_P(MSG_UNKNOWN_CODE, 'D', cmdbuffer + bufindr + CMDHDRSIZE);
 	}
   }
 
@@ -9130,8 +9559,8 @@ void FlushSerialRequestResend()
 // Execution of a command from a SD card will not be confirmed.
 void ClearToSend()
 {
-    previous_millis_cmd = _millis();
-	if ((CMDBUFFER_CURRENT_TYPE == CMDBUFFER_CURRENT_TYPE_USB) || (CMDBUFFER_CURRENT_TYPE == CMDBUFFER_CURRENT_TYPE_USB_WITH_LINENR)) 
+	previous_millis_cmd = _millis();
+	if (buflen && ((CMDBUFFER_CURRENT_TYPE == CMDBUFFER_CURRENT_TYPE_USB) || (CMDBUFFER_CURRENT_TYPE == CMDBUFFER_CURRENT_TYPE_USB_WITH_LINENR)))
 		SERIAL_PROTOCOLLNRPGM(MSG_OK);
 }
 
@@ -9269,7 +9698,7 @@ void mesh_plan_buffer_line(const float &x, const float &y, const float &z, const
         int n_segments = 0;
 
         if (mbl.active) {
-            float len = abs(dx) + abs(dy);
+            float len = fabs(dx) + fabs(dy);
             if (len > 0)
                 // Split to 3cm segments or shorter.
                 n_segments = int(ceil(len / 30.f));
@@ -9323,7 +9752,7 @@ void prepare_move()
   set_current_to_destination();
 }
 
-void prepare_arc_move(char isclockwise) {
+void prepare_arc_move(bool isclockwise) {
   float r = hypot(offset[X_AXIS], offset[Y_AXIS]); // Compute arc radius for mc_arc
 
   // Trace the arc
@@ -9445,7 +9874,7 @@ static void handleSafetyTimer()
     {
         setTargetBed(0);
         setAllTargetHotends(0);
-        lcd_show_fullscreen_message_and_wait_P(_i("Heating disabled by safety timer."));////MSG_BED_HEATING_SAFETY_DISABLED
+        lcd_show_fullscreen_message_and_wait_P(_i("Heating disabled by safety timer."));////MSG_BED_HEATING_SAFETY_DISABLED c=20 r=4
     }
 }
 #endif //SAFETYTIMER
@@ -9479,7 +9908,7 @@ void manage_inactivity_IR_ANALOG_Check(uint16_t &nFSCheckCount, ClFsensorPCB isV
 void manage_inactivity(bool ignore_stepper_queue/*=false*/) //default argument set in Marlin.h
 {
 #ifdef FILAMENT_SENSOR
-bool bInhibitFlag;
+bool bInhibitFlag = false;
 #ifdef IR_SENSOR_ANALOG
 static uint16_t nFSCheckCount=0;
 #endif // IR_SENSOR_ANALOG
@@ -9487,16 +9916,11 @@ static uint16_t nFSCheckCount=0;
 	if (mmu_enabled == false)
 	{
 //-//		if (mcode_in_progress != 600) //M600 not in progress
-#ifdef PAT9125
-		bInhibitFlag=(menu_menu==lcd_menu_extruder_info); // Support::ExtruderInfo menu active
-#endif // PAT9125
-#ifdef IR_SENSOR
-		bInhibitFlag=(menu_menu==lcd_menu_show_sensors_state); // Support::SensorInfo menu active
+		if (!PRINTER_ACTIVE) bInhibitFlag=(menu_menu==lcd_menu_show_sensors_state); //Block Filament sensor actions if PRINTER is not active and Support::SensorInfo menu active
 #ifdef IR_SENSOR_ANALOG
-		bInhibitFlag=bInhibitFlag||bMenuFSDetect; // Settings::HWsetup::FSdetect menu active
+		bInhibitFlag=bInhibitFlag||bMenuFSDetect; // Block Filament sensor actions if Settings::HWsetup::FSdetect menu active
 #endif // IR_SENSOR_ANALOG
-#endif // IR_SENSOR
-		if ((mcode_in_progress != 600) && (eFilamentAction != FilamentAction::AutoLoad) && (!bInhibitFlag)) //M600 not in progress, preHeat @ autoLoad menu not active, Support::ExtruderInfo/SensorInfo menu not active
+		if ((mcode_in_progress != 600) && (eFilamentAction != FilamentAction::AutoLoad) && (!bInhibitFlag) && (menu_menu != lcd_move_e)) //M600 not in progress, preHeat @ autoLoad menu not active
 		{
 			if (!moves_planned() && !IS_SD_PRINTING && !is_usb_printing && (lcd_commands_type != LcdCommands::Layer1Cal) && ! eeprom_read_byte((uint8_t*)EEPROM_WIZARD_ACTIVE))
 			{
@@ -9529,7 +9953,7 @@ static uint16_t nFSCheckCount=0;
 				if( minVolt >= IRsensor_Ldiode_TRESHOLD && minVolt <= IRsensor_Lmax_TRESHOLD 
 				 && maxVolt >= IRsensor_Hmin_TRESHOLD && maxVolt <= IRsensor_Hopen_TRESHOLD
 				){
-					manage_inactivity_IR_ANALOG_Check(nFSCheckCount, ClFsensorPCB::_Old, ClFsensorPCB::_Rev04, _i("FS v0.4 or newer") ); ////c=18
+					manage_inactivity_IR_ANALOG_Check(nFSCheckCount, ClFsensorPCB::_Old, ClFsensorPCB::_Rev04, _i("FS v0.4 or newer") ); ////MSG_FS_V_04_OR_NEWER c=18
 				} 
 				//! If and only if minVolt is in range <0.0, 0.3> and maxVolt is in range  <4.6, 5.0V>, I'm considering a situation with the old fsensor
 				//! Note, we are not relying on one voltage here - getting just +5V can mean an old fsensor or a broken new sensor - that's why
@@ -9537,7 +9961,7 @@ static uint16_t nFSCheckCount=0;
 				else if( minVolt < IRsensor_Ldiode_TRESHOLD 
 				 && maxVolt > IRsensor_Hopen_TRESHOLD && maxVolt <= IRsensor_VMax_TRESHOLD
 				){
-					manage_inactivity_IR_ANALOG_Check(nFSCheckCount, ClFsensorPCB::_Rev04, oFsensorPCB=ClFsensorPCB::_Old, _i("FS v0.3 or older")); ////c=18
+					manage_inactivity_IR_ANALOG_Check(nFSCheckCount, ClFsensorPCB::_Rev04, oFsensorPCB=ClFsensorPCB::_Old, _i("FS v0.3 or older")); ////MSG_FS_V_03_OR_OLDER c=18
 				}
 #endif // IR_SENSOR_ANALOG
 				if (fsensor_check_autoload())
@@ -9671,6 +10095,22 @@ if(0)
   #endif
   check_axes_activity();
   mmu_loop();
+
+  // handle longpress
+  if(lcd_longpress_trigger)
+  {
+      // long press is not possible in modal mode, wait until ready
+      if (lcd_longpress_func && lcd_update_enabled)
+      {
+          lcd_longpress_func();
+          lcd_longpress_trigger = 0;
+      }
+  }
+
+#if defined(AUTO_REPORT)
+  host_autoreport();
+#endif //AUTO_REPORT
+  host_keepalive();
 }
 
 void kill(const char *full_screen_message, unsigned char id)
@@ -9718,6 +10158,32 @@ void kill(const char *full_screen_message, unsigned char id)
   } // Wait for reset
 }
 
+void UnconditionalStop()
+{
+    CRITICAL_SECTION_START;
+
+    // Disable all heaters and unroll the temperature wait loop stack
+    disable_heater();
+    cancel_heatup = true;
+
+    // Clear any saved printing state
+    cancel_saved_printing();
+
+    // Abort the planner
+    planner_abort_hard();
+
+    // Reset the queue
+    cmdqueue_reset();
+    cmdqueue_serial_disabled = false;
+
+    // Reset the sd status
+    card.sdprinting = false;
+    card.closefile();
+
+    st_reset_timer();
+    CRITICAL_SECTION_END;
+}
+
 // Stop: Emergency stop used by overtemp functions which allows recovery
 //
 //   In addition to stopping the print, this prevents subsequent G[0-3] commands to be
@@ -9730,15 +10196,27 @@ void kill(const char *full_screen_message, unsigned char id)
 //   the addition of disabling the headers) could allow true recovery in the future.
 void Stop()
 {
+  // Keep disabling heaters
   disable_heater();
+
+  // Call the regular stop function if that's the first time during a new print
   if(Stopped == false) {
     Stopped = true;
     lcd_print_stop();
     Stopped_gcode_LastN = gcode_LastN; // Save last g_code for restart
+
+    // Eventually report the stopped status (though this is usually overridden by a
+    // higher-priority alert status message)
     SERIAL_ERROR_START;
     SERIAL_ERRORLNRPGM(MSG_ERR_STOPPED);
     LCD_MESSAGERPGM(_T(MSG_STOPPED));
   }
+
+  // Return to the status screen to stop any pending menu action which could have been
+  // started by the user while stuck in the Stopped state. This also ensures the NEW
+  // error is immediately shown.
+  if (menu_menu != lcd_status_screen)
+      lcd_return_to_status();
 }
 
 bool IsStopped() { return Stopped; };
@@ -9852,16 +10330,16 @@ bool setTargetedHotend(int code, uint8_t &extruder)
           SERIAL_ECHORPGM(_n("M104 Invalid extruder "));////MSG_M104_INVALID_EXTRUDER
           break;
         case 105:
-          SERIAL_ECHO(_n("M105 Invalid extruder "));////MSG_M105_INVALID_EXTRUDER
+          SERIAL_ECHORPGM(_n("M105 Invalid extruder "));////MSG_M105_INVALID_EXTRUDER
           break;
         case 109:
-          SERIAL_ECHO(_n("M109 Invalid extruder "));////MSG_M109_INVALID_EXTRUDER
+          SERIAL_ECHORPGM(_n("M109 Invalid extruder "));////MSG_M109_INVALID_EXTRUDER
           break;
         case 218:
-          SERIAL_ECHO(_n("M218 Invalid extruder "));////MSG_M218_INVALID_EXTRUDER
+          SERIAL_ECHORPGM(_n("M218 Invalid extruder "));////MSG_M218_INVALID_EXTRUDER
           break;
         case 221:
-          SERIAL_ECHO(_n("M221 Invalid extruder "));////MSG_M221_INVALID_EXTRUDER
+          SERIAL_ECHORPGM(_n("M221 Invalid extruder "));////MSG_M221_INVALID_EXTRUDER
           break;
       }
       SERIAL_PROTOCOLLN((int)extruder);
@@ -9965,7 +10443,7 @@ static void wait_for_heater(long codenum, uint8_t extruder) {
 				}
 			}
 #else
-				SERIAL_PROTOCOLLN("");
+				SERIAL_PROTOCOLLN();
 #endif
 				codenum = _millis();
 		}
@@ -10272,7 +10750,7 @@ void bed_analysis(float x_dimension, float y_dimension, int x_points_num, int y_
 		// There shall be always enough space reserved for these commands.
 		repeatcommand_front(); // repeat G80 with all its parameters
 		
-		enquecommand_front_P((PSTR("G28 W0")));
+		enquecommand_front_P(G28W0);
 		enquecommand_front_P((PSTR("G1 Z5")));
 		return;
 	}
@@ -10497,9 +10975,9 @@ float temp_comp_interpolation(float inp_temperature) {
 #ifdef PINDA_THERMISTOR
 		constexpr int start_compensating_temp = 35;
 		temp_C[i] = start_compensating_temp + i * 5; //temperature in degrees C
-#ifdef DETECT_SUPERPINDA
-		static_assert(start_compensating_temp >= PINDA_MINTEMP, "Temperature compensation start point is lower than PINDA_MINTEMP.");
-#endif //DETECT_SUPERPINDA
+#ifdef SUPERPINDA_SUPPORT
+    static_assert(start_compensating_temp >= PINDA_MINTEMP, "Temperature compensation start point is lower than PINDA_MINTEMP.");
+#endif //SUPERPINDA_SUPPORT
 #else
 		temp_C[i] = 50 + i * 10; //temperature in C
 #endif
@@ -10588,7 +11066,7 @@ void serialecho_temperatures() {
 	SERIAL_PROTOCOL((int)active_extruder);
 	SERIAL_PROTOCOLPGM(" B:");
 	SERIAL_PROTOCOL_F(degBed(), 1);
-	SERIAL_PROTOCOLLN("");
+	SERIAL_PROTOCOLLN();
 }
 
 #ifdef UVLO_SUPPORT
@@ -10640,8 +11118,9 @@ void uvlo_()
     }
 
     // save the global state at planning time
+    bool pos_invalid = XY_NO_RESTORE_FLAG;
     uint16_t feedrate_bckp;
-    if (current_block)
+    if (current_block && !pos_invalid)
     {
         memcpy(saved_target, current_block->gcode_target, sizeof(saved_target));
         feedrate_bckp = current_block->gcode_feedrate;
@@ -10719,8 +11198,13 @@ void uvlo_()
     eeprom_update_word((uint16_t*)(EEPROM_UVLO_Z_MICROSTEPS), z_microsteps);
 
     // Store the current position.
-    eeprom_update_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 0), current_position[X_AXIS]);
-    eeprom_update_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 4), current_position[Y_AXIS]);
+    if (pos_invalid)
+        eeprom_update_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 0), X_COORD_INVALID);
+    else
+    {
+        eeprom_update_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 0), current_position[X_AXIS]);
+        eeprom_update_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 4), current_position[Y_AXIS]);
+    }
 
     // Store the current feed rate, temperatures, fan speed and extruder multipliers (flow rates)
 	eeprom_update_word((uint16_t*)EEPROM_UVLO_FEEDRATE, feedrate_bckp);
@@ -10737,6 +11221,10 @@ void uvlo_()
 #endif
 	eeprom_update_word((uint16_t*)(EEPROM_EXTRUDEMULTIPLY), (uint16_t)extrudemultiply);
 
+	eeprom_update_float((float*)(EEPROM_UVLO_ACCELL), cs.acceleration);
+	eeprom_update_float((float*)(EEPROM_UVLO_RETRACT_ACCELL), cs.retract_acceleration);
+	eeprom_update_float((float*)(EEPROM_UVLO_TRAVEL_ACCELL), cs.travel_acceleration);
+
     // Store the saved target
     eeprom_update_float((float*)(EEPROM_UVLO_SAVED_TARGET+0*4), saved_target[X_AXIS]);
     eeprom_update_float((float*)(EEPROM_UVLO_SAVED_TARGET+1*4), saved_target[Y_AXIS]);
@@ -10794,7 +11282,7 @@ void uvlo_tiny()
     planner_abort_hard();
 
     // Allow for small roundoffs to be ignored
-    if(abs(current_position[Z_AXIS] - eeprom_read_float((float*)(EEPROM_UVLO_TINY_CURRENT_POSITION_Z))) >= 1.f/cs.axis_steps_per_unit[Z_AXIS])
+    if(fabs(current_position[Z_AXIS] - eeprom_read_float((float*)(EEPROM_UVLO_TINY_CURRENT_POSITION_Z))) >= 1.f/cs.axis_steps_per_unit[Z_AXIS])
     {
         // Clean the input command queue, inhibit serial processing using saved_printing
         cmdqueue_reset();
@@ -10905,7 +11393,7 @@ void recover_print(uint8_t automatic) {
 	char cmd[30];
 	lcd_update_enable(true);
 	lcd_update(2);
-  lcd_setstatuspgm(_i("Recovering print    "));////MSG_RECOVERING_PRINT c=20
+  lcd_setstatuspgm(_i("Recovering print"));////MSG_RECOVERING_PRINT c=20
 
   // Recover position, temperatures and extrude_multipliers
   bool mbl_was_active = recover_machine_state_after_power_panic();
@@ -10974,11 +11462,6 @@ bool recover_machine_state_after_power_panic()
   // Recover last E axis position
   current_position[E_AXIS] = eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION_E));
 
-  memcpy(destination, current_position, sizeof(destination));
-
-  SERIAL_ECHOPGM("recover_machine_state_after_power_panic, initial ");
-  print_world_coordinates();
-
   // 3) Initialize the logical to physical coordinate system transformation.
   world2machine_initialize();
 //  SERIAL_ECHOPGM("recover_machine_state_after_power_panic, initial ");
@@ -10990,7 +11473,11 @@ bool recover_machine_state_after_power_panic()
 
   // 5) Set the physical positions from the logical positions using the world2machine transformation
   // This is only done to inizialize Z/E axes with physical locations, since X/Y are unknown.
+  clamp_to_software_endstops(current_position);
+  memcpy(destination, current_position, sizeof(destination));
   plan_set_position_curposXYZE();
+  SERIAL_ECHOPGM("recover_machine_state_after_power_panic, initial ");
+  print_world_coordinates();
 
   // 6) Power up the Z motors, mark their positions as known.
   axis_known_position[Z_AXIS] = true;
@@ -11027,7 +11514,7 @@ void restore_print_from_eeprom(bool mbl_was_active) {
 	int feedrate_rec;
 	int feedmultiply_rec;
 	uint8_t fan_speed_rec;
-	char cmd[30];
+	char cmd[48];
 	char filename[13];
 	uint8_t depth = 0;
 	char dir_name[9];
@@ -11049,8 +11536,8 @@ void restore_print_from_eeprom(bool mbl_was_active) {
 		}
 		dir_name[8] = '\0';
 		MYSERIAL.println(dir_name);
-		strcpy(dir_names[i], dir_name);
-		card.chdir(dir_name);
+		// strcpy(card.dir_names[i], dir_name);
+		card.chdir(dir_name, false);
 	}
 
 	for (int i = 0; i < 8; i++) {
@@ -11068,10 +11555,13 @@ void restore_print_from_eeprom(bool mbl_was_active) {
 
     // Move to the XY print position in logical coordinates, where the print has been killed, but
     // without shifting Z along the way. This requires performing the move without mbl.
-	sprintf_P(cmd, PSTR("G1 X%f Y%f F3000"),
-              eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 0)),
-              eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 4)));
-	enquecommand(cmd);
+    float pos_x = eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 0));
+    float pos_y = eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION + 4));
+    if (pos_x != X_COORD_INVALID)
+    {
+        sprintf_P(cmd, PSTR("G1 X%f Y%f F3000"), pos_x, pos_y);
+        enquecommand(cmd);
+    }
 
     // Enable MBL and switch to logical positioning
     if (mbl_was_active)
@@ -11081,6 +11571,13 @@ void restore_print_from_eeprom(bool mbl_was_active) {
     sprintf_P(cmd, PSTR("G1 Z%f"), eeprom_read_float((float*)(EEPROM_UVLO_CURRENT_POSITION_Z)));
 	enquecommand(cmd);
 
+    // Restore acceleration settings
+    float acceleration = eeprom_read_float((float*)(EEPROM_UVLO_ACCELL));
+    float retract_acceleration = eeprom_read_float((float*)(EEPROM_UVLO_RETRACT_ACCELL));
+    float travel_acceleration = eeprom_read_float((float*)(EEPROM_UVLO_TRAVEL_ACCELL));
+    sprintf_P(cmd, PSTR("M204 P%f R%f T%f"), acceleration, retract_acceleration, travel_acceleration);
+    enquecommand(cmd);
+
   // Unretract.
     sprintf_P(cmd, PSTR("G1 E%0.3f F2700"), default_retraction);
     enquecommand(cmd);
@@ -11244,7 +11741,8 @@ void stop_and_save_print_to_ram(float z_move, float e_move)
 #endif
 
   // save the global state at planning time
-  if (current_block)
+  bool pos_invalid = XY_NO_RESTORE_FLAG;
+  if (current_block && !pos_invalid)
   {
       memcpy(saved_target, current_block->gcode_target, sizeof(saved_target));
       saved_feedrate2 = current_block->gcode_feedrate;
@@ -11256,7 +11754,10 @@ void stop_and_save_print_to_ram(float z_move, float e_move)
   }
 
 	planner_abort_hard(); //abort printing
+
 	memcpy(saved_pos, current_position, sizeof(saved_pos));
+    if (pos_invalid) saved_pos[X_AXIS] = X_COORD_INVALID;
+
     saved_feedmultiply2 = feedmultiply; //save feedmultiply
 	saved_active_extruder = active_extruder; //save active_extruder
 	saved_extruder_temperature = degTargetHotend(active_extruder);
@@ -11350,6 +11851,13 @@ void restore_print_from_ram_and_continue(float e_move)
     fans_check_enabled = false;
   #endif
 
+    // do not restore XY for commands that do not require that
+    if (saved_pos[X_AXIS] == X_COORD_INVALID)
+    {
+        saved_pos[X_AXIS] = current_position[X_AXIS];
+        saved_pos[Y_AXIS] = current_position[Y_AXIS];
+    }
+
 	//first move print head in XY to the saved position:
 	plan_buffer_line(saved_pos[X_AXIS], saved_pos[Y_AXIS], current_position[Z_AXIS], saved_pos[E_AXIS] - e_move, homing_feedrate[Z_AXIS]/13, active_extruder);
 	//then move Z
@@ -11382,7 +11890,6 @@ void restore_print_from_ram_and_continue(float e_move)
 		//not sd printing nor usb printing
 	}
 
-	SERIAL_PROTOCOLLNRPGM(MSG_OK); //dummy response because of octoprint is waiting for this
 	lcd_setstatuspgm(_T(WELCOME_MSG));
     saved_printing_type = PRINTING_TYPE_NONE;
 	saved_printing = false;
@@ -11419,46 +11926,40 @@ void print_mesh_bed_leveling_table()
   SERIAL_ECHOLN();
 }
 
-uint16_t print_time_remaining() {
-	uint16_t print_t = PRINT_TIME_REMAINING_INIT;
-#ifdef TMC2130 
-	if (SilentModeMenu == SILENT_MODE_OFF) print_t = print_time_remaining_normal;
-	else print_t = print_time_remaining_silent;
-#else
-	print_t = print_time_remaining_normal;
-#endif //TMC2130
-	if ((print_t != PRINT_TIME_REMAINING_INIT) && (feedmultiply != 0)) print_t = 100ul * print_t / feedmultiply;
-	return print_t;
-}
-
 uint8_t calc_percent_done()
 {
-	//in case that we have information from M73 gcode return percentage counted by slicer, else return percentage counted as byte_printed/filesize
-	uint8_t percent_done = 0;
+    //in case that we have information from M73 gcode return percentage counted by slicer, else return percentage counted as byte_printed/filesize
+    uint8_t percent_done = 0;
 #ifdef TMC2130
-	if (SilentModeMenu == SILENT_MODE_OFF && print_percent_done_normal <= 100) {
-		percent_done = print_percent_done_normal;
-	}
-	else if (print_percent_done_silent <= 100) {
-		percent_done = print_percent_done_silent;
-	}
+    if (SilentModeMenu == SILENT_MODE_OFF && print_percent_done_normal <= 100)
+    {
+        percent_done = print_percent_done_normal;
+    }
+    else if (print_percent_done_silent <= 100)
+    {
+        percent_done = print_percent_done_silent;
+    }
 #else
-	if (print_percent_done_normal <= 100) {
-		percent_done = print_percent_done_normal;
-	}
+    if (print_percent_done_normal <= 100)
+    {
+        percent_done = print_percent_done_normal;
+    }
 #endif //TMC2130
-	else {
-		percent_done = card.percentDone();
-	}
-	return percent_done;
+    else
+    {
+        percent_done = card.percentDone();
+    }
+    return percent_done;
 }
 
 static void print_time_remaining_init()
 {
-	print_time_remaining_normal = PRINT_TIME_REMAINING_INIT;
-	print_time_remaining_silent = PRINT_TIME_REMAINING_INIT;
-	print_percent_done_normal = PRINT_PERCENT_DONE_INIT;
-	print_percent_done_silent = PRINT_PERCENT_DONE_INIT;
+    print_time_remaining_normal = PRINT_TIME_REMAINING_INIT;
+    print_percent_done_normal = PRINT_PERCENT_DONE_INIT;
+    print_time_remaining_silent = PRINT_TIME_REMAINING_INIT;
+    print_percent_done_silent = PRINT_PERCENT_DONE_INIT;
+    print_time_to_change_normal = PRINT_TIME_REMAINING_INIT;
+    print_time_to_change_silent = PRINT_TIME_REMAINING_INIT;
 }
 
 void load_filament_final_feed()
@@ -11548,7 +12049,7 @@ void M600_wait_for_user(float HotendTempBckp) {
 				delay_keep_alive(4);
 
 				if (_millis() > waiting_start_time + (unsigned long)M600_TIMEOUT * 1000) {
-					lcd_display_message_fullscreen_P(_i("Press knob to preheat nozzle and continue."));////MSG_PRESS_TO_PREHEAT c=20 r=4
+					lcd_display_message_fullscreen_P(_i("Press the knob to preheat nozzle and continue."));////MSG_PRESS_TO_PREHEAT c=20 r=4
 					wait_for_user_state = 1;
 					setAllTargetHotends(0);
 					st_synchronize();
@@ -11569,7 +12070,7 @@ void M600_wait_for_user(float HotendTempBckp) {
 				break;
 			case 2: //waiting for nozzle to reach target temperature
 
-				if (abs(degTargetHotend(active_extruder) - degHotend(active_extruder)) < 1) {
+				if (fabs(degTargetHotend(active_extruder) - degHotend(active_extruder)) < 1) {
 					lcd_display_message_fullscreen_P(_T(MSG_PRESS_TO_UNLOAD));
 					waiting_start_time = _millis();
 					wait_for_user_state = 0;
@@ -11712,11 +12213,10 @@ void disable_force_z()
 #ifdef TMC2130
     tmc2130_mode=TMC2130_MODE_SILENT;
     update_mode_profile();
-    tmc2130_init(true);
+    tmc2130_init(TMCInitParams(true, FarmOrUserECool()));
 #endif // TMC2130
 }
 
-
 void enable_force_z()
 {
 if(bEnableForce_z)
@@ -11727,7 +12227,7 @@ bEnableForce_z=true;
 #ifdef TMC2130
 tmc2130_mode=eeprom_read_byte((uint8_t*)EEPROM_SILENT)?TMC2130_MODE_SILENT:TMC2130_MODE_NORMAL;
 update_mode_profile();
-tmc2130_init(true);
+tmc2130_init(TMCInitParams(true, FarmOrUserECool()));
 #endif // TMC2130
 
 WRITE(Z_ENABLE_PIN,Z_ENABLE_ON);                  // slightly redundant ;-p

+ 4 - 1
Firmware/Sd2Card.cpp

@@ -767,6 +767,9 @@ uint8_t Sd2Card::waitStartBlock(void) {
 
 // Toshiba FlashAir support, copied from 
 // https://flashair-developers.com/en/documents/tutorials/arduino/
+// However, the official website was closed in September 2019.
+// There is an archived website (written in Japanese).
+// https://flashair-developers.github.io/website/docs/tutorials/arduino/2.html
 
 //------------------------------------------------------------------------------
 /** Perform Extention Read. */
@@ -774,7 +777,7 @@ uint8_t Sd2Card::readExt(uint32_t arg, uint8_t* dst, uint16_t count) {
   uint16_t i;
 
   // send command and argument.
-  if (cardCommand(CMD48, arg)) {
+  if (cardCommand(CMD48, arg) && cardCommand(CMD17, arg)) { // CMD48 for W-03, CMD17 for W-04
     error(SD_CARD_ERROR_CMD48);
     goto fail;
   }

+ 1 - 5
Firmware/Sd2PinMap.h

@@ -37,10 +37,6 @@ struct pin_map_t {
 || defined(__AVR_ATmega2560__)
 // Mega
 
-// Two Wire (aka I2C) ports
-uint8_t const SDA_PIN = 20;  // D1
-uint8_t const SCL_PIN = 21;  // D0
-
 #undef MOSI_PIN
 #undef MISO_PIN
 // SPI port
@@ -365,4 +361,4 @@ static inline __attribute__((always_inline))
 #endif  // Sd2PinMap_h
 
 
-#endif
+#endif

+ 3 - 3
Firmware/SdBaseFile.cpp

@@ -530,9 +530,9 @@ bool SdBaseFile::mkdir(SdBaseFile* parent, const uint8_t dname[11]) {
   * \return The value one, true, is returned for success and
   * the value zero, false, is returned for failure.
   */
-  bool SdBaseFile::open(const char* path, uint8_t oflag) {
-    return open(cwd_, path, oflag);
-  }
+bool SdBaseFile::open(const char* path, uint8_t oflag) {
+  return open(cwd_, path, oflag);
+}
 //------------------------------------------------------------------------------
 /** Open a file or directory by name.
  *

+ 3 - 1
Firmware/SdBaseFile.h

@@ -281,8 +281,10 @@ class SdBaseFile {
   static void printFatDate(uint16_t fatDate);
   static void printFatTime( uint16_t fatTime);
   bool printName();
+protected:  
   int16_t read();
   int16_t read(void* buf, uint16_t nbyte);
+public:
   int8_t readDir(dir_t* dir, char* longFilename);
   static bool remove(SdBaseFile* dirFile, const char* path);
   bool remove();
@@ -321,7 +323,7 @@ class SdBaseFile {
   SdVolume* volume() const {return vol_;}
   int16_t write(const void* buf, uint16_t nbyte);
 //------------------------------------------------------------------------------
- private:
+ protected:
   // allow SdFat to set cwd_
   friend class SdFat;
   // global pointer to cwd dir

+ 2 - 10
Firmware/SdFatUtil.cpp

@@ -48,24 +48,16 @@ void SdFatUtil::set_stack_guard()
 {	
 	uint32_t *stack_guard;
 
-	stack_guard = (uint32_t*)&__bss_end;
+	stack_guard = (uint32_t*)(&__bss_end + STACK_GUARD_MARGIN);
     *stack_guard = STACK_GUARD_TEST_VALUE;
 }
 
 bool SdFatUtil::test_stack_integrity()
 {
-	uint32_t* stack_guard = (uint32_t*)&__bss_end;
+	uint32_t* stack_guard = (uint32_t*)(&__bss_end + STACK_GUARD_MARGIN);
 	return (*stack_guard == STACK_GUARD_TEST_VALUE);
 }
 
-uint32_t SdFatUtil::get_stack_guard_test_value()
-{
-	uint32_t* stack_guard;
-	uint32_t output;
-	stack_guard = (uint32_t*)&__bss_end;
-	output = *stack_guard;
-	return(output);
-}
 //------------------------------------------------------------------------------
 /** %Print a string in flash memory.
  *

+ 1 - 2
Firmware/SdFatUtil.h

@@ -41,11 +41,10 @@ namespace SdFatUtil {
   void SerialPrintln_P(PGM_P str);
   void set_stack_guard();
   bool test_stack_integrity();
-  uint32_t get_stack_guard_test_value();
 }
 
 using namespace SdFatUtil;  // NOLINT
 #endif  // #define SdFatUtil_h
 
 
-#endif
+#endif

+ 188 - 0
Firmware/SdFile.cpp

@@ -30,6 +30,194 @@
  */
 SdFile::SdFile(const char* path, uint8_t oflag) : SdBaseFile(path, oflag) {
 }
+
+bool SdFile::openFilteredGcode(SdBaseFile* dirFile, const char* path){
+    if( open(dirFile, path, O_READ) ){
+        // compute the block to start with
+        if( ! gfComputeNextFileBlock() )
+            return false;
+        gfReset();
+        return true;
+    } else {
+        return false;
+    }
+}
+
+bool SdFile::seekSetFilteredGcode(uint32_t pos){
+    if(! seekSet(pos) )return false;
+    if(! gfComputeNextFileBlock() )return false;
+    gfReset();
+    return true;
+}
+
+const uint8_t *SdFile::gfBlockBuffBegin() const {
+    return vol_->cache()->data; // this is constant for the whole time, so it should be fast and sleek
+}
+
+void SdFile::gfReset(){
+    // reset cache read ptr to its begin
+    gfReadPtr = gfBlockBuffBegin() + gfOffset;
+}
+
+// think twice before allowing this to inline - manipulating 4B longs is costly
+// moreover - this function has its parameters in registers only, so no heavy stack usage besides the call/ret
+void __attribute__((noinline)) SdFile::gfUpdateCurrentPosition(uint16_t inc){
+    curPosition_ += inc;
+}
+
+#define find_endl(resultP, startP) \
+__asm__ __volatile__ (  \
+"cycle:          \n" \
+"ld  r22, Z+     \n" \
+"cpi r22, 0x0A   \n" \
+"brne cycle      \n" \
+: "=z" (resultP) /* result of the ASM code - in our case the Z register (R30:R31) */ \
+: "z" (startP)   /* input of the ASM code - in our case the Z register as well (R30:R31) */ \
+: "r22"          /* modifying register R22 - so that the compiler knows */ \
+)
+
+// avoid calling the default heavy-weight read() for just one byte
+int16_t SdFile::readFilteredGcode(){
+    if( ! gfEnsureBlock() ){
+        goto eof_or_fail; // this is unfortunate :( ... other calls are using the cache and we can loose the data block of our gcode file
+    }
+    // assume, we have the 512B block cache filled and terminated with a '\n'
+    {
+    const uint8_t *start = gfReadPtr;
+
+    // It may seem unreasonable to copy the variable into a local one and copy it back at the end of this method,
+    // but there is an important point of view: the compiler is unsure whether it can optimize the reads/writes
+    // to gfReadPtr within this method, because it is a class member variable. 
+    // The compiler cannot see, if omitting read/write won't have any incorrect side-effects to the rest of the whole FW.
+    // So this trick explicitly states, that rdPtr is a local variable limited to the scope of this method,
+    // therefore the compiler can omit read/write to it (keep it in registers!) as it sees fit.
+    // And it does! Codesize dropped by 68B!
+    const uint8_t *rdPtr = gfReadPtr;
+
+    // the same applies to gfXBegin, codesize dropped another 100B!
+    const uint8_t *blockBuffBegin = gfBlockBuffBegin();
+    
+    uint8_t consecutiveCommentLines = 0;
+    while( *rdPtr == ';' ){
+        for(;;){
+
+            //while( *(++gfReadPtr) != '\n' ); // skip until a newline is found - suboptimal code!
+            // Wondering, why this "nice while cycle" is done in such a weird way using a separate find_endl() function?
+            // Have a look at the ASM code GCC produced!
+            
+            // At first - a separate find_endl() makes the compiler understand, 
+            // that I don't need to store gfReadPtr every time, I'm only interested in the final address where the '\n' was found
+            // - the cycle can run on CPU registers only without touching memory besides reading the character being compared.
+            // Not only makes the code run considerably faster, but is also 40B shorter!
+            // This was the generated code:
+            //FORCE_INLINE const uint8_t * find_endl(const uint8_t *p){
+            //   while( *(++p) != '\n' ); // skip until a newline is found
+            //   return p; }
+            //   11c5e:	movw	r30, r18
+            //   11c60:	subi	r18, 0xFF	; 255
+            //   11c62:	sbci	r19, 0xFF	; 255
+            //   11c64:	ld	r22, Z
+            //   11c66:	cpi	r22, 0x0A	; 10
+            //   11c68:	brne	.-12     	; 0x11c5e <get_command()+0x524>            
+
+            // Still, even that was suboptimal as the compiler seems not to understand the usage of ld r22, Z+ (the plus is important)
+            // aka automatic increment of the Z register (R30:R31 pair)
+            // There is no other way than pure ASM!
+            find_endl(rdPtr, rdPtr);
+
+            // found a newline, prepare the next block if block cache end reached
+            if( rdPtr - blockBuffBegin > 512 ){
+                // at the end of block cache, fill new data in
+                gfUpdateCurrentPosition( rdPtr - start - 1 );
+                if( ! gfComputeNextFileBlock() )goto eof_or_fail;
+                if( ! gfEnsureBlock() )goto eof_or_fail; // fetch it into RAM
+                rdPtr = start = blockBuffBegin;
+            } else {
+                if(consecutiveCommentLines >= 250){
+                    --rdPtr; // unget the already consumed newline
+                    goto emit_char;
+                }
+                // peek the next byte - we are inside the block at least at 511th index - still safe
+                if( *rdPtr == ';' ){
+                    // consecutive comment
+                    ++consecutiveCommentLines;
+                } else {
+                    --rdPtr; // unget the already consumed newline
+                    goto emit_char;
+                }
+                break; // found the real end of the line even across many blocks
+            }
+        }
+    }
+emit_char:
+    {
+        gfUpdateCurrentPosition( rdPtr - start + 1 );
+        int16_t rv = *rdPtr++;
+        
+        if( curPosition_ >= fileSize_ ){
+            // past the end of file
+            goto eof_or_fail;
+        } else if( rdPtr - blockBuffBegin >= 512 ){
+            // past the end of current bufferred block - prepare the next one...
+            if( ! gfComputeNextFileBlock() )goto eof_or_fail;
+            // don't need to force fetch the block here, it will get loaded on the next call
+            rdPtr = blockBuffBegin;
+        }
+
+        // save the current read ptr for the next run
+        gfReadPtr = rdPtr;
+        return rv;
+    }
+
+}
+
+eof_or_fail:
+    // make the rdptr point to a safe location - end of file
+    gfReadPtr = gfBlockBuffBegin() + 512;
+    return -1;
+}
+
+bool SdFile::gfEnsureBlock(){
+    // this comparison is heavy-weight, especially when there is another one inside cacheRawBlock
+    // but it is necessary to avoid computing of terminateOfs if not needed
+    if( gfBlock != vol_->cacheBlockNumber_ ){
+        if ( ! vol_->cacheRawBlock(gfBlock, SdVolume::CACHE_FOR_READ)){
+            return false;
+        }
+        // terminate with a '\n'
+        const uint32_t terminateOfs = fileSize_ - gfOffset;
+        vol_->cache()->data[ terminateOfs < 512 ? terminateOfs : 512 ] = '\n';
+    }
+    return true;
+}
+
+bool SdFile::gfComputeNextFileBlock() {
+    // error if not open or write only
+    if (!isOpen() || !(flags_ & O_READ)) return false;
+
+    gfOffset = curPosition_ & 0X1FF;  // offset in block
+    if (type_ == FAT_FILE_TYPE_ROOT_FIXED) {
+        // SHR by 9 means skip the last byte and shift just 3 bytes by 1
+        // -> should be 8 instructions... and not the horrible loop shifting 4 bytes at once
+        // still need to get some work on this
+        gfBlock = vol_->rootDirStart() + (curPosition_ >> 9); 
+    } else {
+        uint8_t blockOfCluster = vol_->blockOfCluster(curPosition_);
+        if (gfOffset == 0 && blockOfCluster == 0) {
+            // start of new cluster
+            if (curPosition_ == 0) {
+                // use first cluster in file
+                curCluster_ = firstCluster_;
+            } else {
+                // get next cluster from FAT
+                if (!vol_->fatGet(curCluster_, &curCluster_)) return false;
+            }
+        }
+        gfBlock = vol_->clusterStartBlock(curCluster_) + blockOfCluster;
+    }
+    return true;
+}
+
 //------------------------------------------------------------------------------
 /** Write data to an open file.
  *

+ 22 - 2
Firmware/SdFile.h

@@ -34,7 +34,24 @@
  * \brief SdBaseFile with Print.
  */
 class SdFile : public SdBaseFile/*, public Print*/ {
- public:
+  // GCode filtering vars and methods - due to optimization reasons not wrapped in a separate class
+  
+  // beware - this read ptr is manipulated inside just 2 methods - readFilteredGcode and gfReset
+  // If you even want to call gfReset from readFilteredGcode, you must make sure
+  // to update gfReadPtr inside readFilteredGcode from a local copy (see explanation of this trick in readFilteredGcode)
+  const uint8_t *gfReadPtr;
+  
+  uint32_t gfBlock; // remember the current file block to be kept in cache - due to reuse of the memory, the block may fall out a must be read back
+  uint16_t gfOffset;
+
+  const uint8_t *gfBlockBuffBegin()const;
+  
+  void gfReset();
+  
+  bool gfEnsureBlock();
+  bool gfComputeNextFileBlock();
+  void gfUpdateCurrentPosition(uint16_t inc);
+public:
   SdFile() {}
   SdFile(const char* name, uint8_t oflag);
   #if ARDUINO >= 100
@@ -43,6 +60,9 @@ class SdFile : public SdBaseFile/*, public Print*/ {
    void write(uint8_t b);
   #endif
   
+  bool openFilteredGcode(SdBaseFile* dirFile, const char* path);
+  int16_t readFilteredGcode();
+  bool seekSetFilteredGcode(uint32_t pos);
   int16_t write(const void* buf, uint16_t nbyte);
   void write(const char* str);
   void write_P(PGM_P str);
@@ -51,4 +71,4 @@ class SdFile : public SdBaseFile/*, public Print*/ {
 #endif  // SdFile_h
 
 
-#endif
+#endif

+ 3 - 2
Firmware/SdVolume.h

@@ -36,7 +36,7 @@
  */
 union cache_t {
            /** Used to access cached file data blocks. */
-  uint8_t  data[512];
+  uint8_t  data[512 + 1]; // abuse the last byte for saving '\n' - ugly optimization of read_filtered's inner skipping loop
            /** Used to access cached FAT16 entries. */
   uint16_t fat16[256];
            /** Used to access cached FAT32 entries. */
@@ -119,6 +119,7 @@ class SdVolume {
   bool dbgFat(uint32_t n, uint32_t* v) {return fatGet(n, v);}
 //------------------------------------------------------------------------------
  private:
+  friend class SdFile;
   // Allow SdBaseFile access to SdVolume private data.
   friend class SdBaseFile;
 
@@ -211,4 +212,4 @@ class SdVolume {
 #endif  // ALLOW_DEPRECATED_FUNCTIONS
 };
 #endif  // SdVolume
-#endif
+#endif

+ 1 - 1
Firmware/Servo.cpp

@@ -231,7 +231,7 @@ static void finISR(timer16_Sequence_t timer)
 #endif
 }
 
-static boolean isTimerActive(timer16_Sequence_t timer)
+static bool isTimerActive(timer16_Sequence_t timer)
 {
   // returns true if any servo is active on this timer
   for(uint8_t channel=0; channel < SERVOS_PER_TIMER; channel++) {

+ 2 - 2
Firmware/Timer.h

@@ -20,10 +20,10 @@ public:
     Timer();
     void start();
     void stop(){m_isRunning = false;}
-    bool running(){return m_isRunning;}
+    bool running()const {return m_isRunning;}
     bool expired(T msPeriod);
 protected:
-    T started(){return m_started;}
+    T started()const {return m_started;}
 private:
     bool m_isRunning;
     T m_started;

+ 1 - 1
Firmware/adc.c

@@ -19,7 +19,7 @@ uint16_t adc_sim_mask;
 
 void adc_init(void)
 {
-	printf_P(PSTR("adc_init\n"));
+	puts_P(PSTR("adc_init"));
 	adc_sim_mask = 0x00;
 	ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
 	ADMUX |= (1 << REFS0);

+ 24 - 0
Firmware/asm.h

@@ -0,0 +1,24 @@
+#pragma once
+#include <stdint.h>
+
+#ifdef __AVR_ATmega2560__
+
+// return the current PC (on AVRs with 22bit PC)
+static inline void GETPC(uint32_t* v)
+{
+  uint8_t a, b, c;
+  asm
+  (
+      "rcall .\n"
+      "pop %2\n"
+      "pop %1\n"
+      "pop %0\n"
+      : "=r" (a), "=r" (b), "=r" (c)
+  );
+  ((uint8_t*)v)[0] = a;
+  ((uint8_t*)v)[1] = b;
+  ((uint8_t*)v)[2] = c;
+  ((uint8_t*)v)[3] = 0;
+}
+
+#endif

+ 4 - 4
Firmware/backlight.cpp

@@ -1,10 +1,10 @@
 //backlight.cpp
 
 #include "backlight.h"
+#include "macros.h"
 #include <avr/eeprom.h>
 #include <Arduino.h>
 #include "eeprom.h"
-#include "Marlin.h"
 #include "pins.h"
 #include "fastio.h"
 #include "Timer.h"
@@ -111,10 +111,10 @@ void backlight_init()
 
 #else //LCD_BL_PIN
 
-void force_bl_on(__attribute__((unused)) bool section_start) {}
+void force_bl_on(bool) {}
 void backlight_update() {}
 void backlight_init() {}
 void backlight_save() {}
-void backlight_wake(__attribute__((unused)) const uint8_t flashNo) {}
+void backlight_wake(const uint8_t) {}
 
-#endif //LCD_BL_PIN
+#endif //LCD_BL_PIN

+ 4 - 13
Firmware/bootapp.c

@@ -9,6 +9,8 @@
 extern FILE _uartout;
 #define uartout (&_uartout)
 
+extern void softReset();
+
 void bootapp_print_vars(void)
 {
 	fprintf_P(uartout, PSTR("boot_src_addr  =0x%08lx\n"), boot_src_addr);
@@ -26,21 +28,11 @@ void bootapp_ram2flash(uint16_t rptr, uint16_t fptr, uint16_t size)
 	boot_app_magic = BOOT_APP_MAGIC;
 	boot_app_flags |= BOOT_APP_FLG_COPY;
 	boot_app_flags |= BOOT_APP_FLG_ERASE;
-/*	uint16_t ui; for (ui = 0; ui < size; ui++)
-	{
-		uint8_t uc = ram_array[ui+rptr];
-		if (pgm_read_byte(ui+fptr) & uc != uc)
-		{
-			boot_app_flags |= BOOT_APP_FLG_ERASE;
-			break;
-		}
-	}*/
 	boot_copy_size = (uint16_t)size;
 	boot_src_addr = (uint32_t)rptr;
 	boot_dst_addr = (uint32_t)fptr;
 	bootapp_print_vars();
-	wdt_enable(WDTO_15MS);
-	while(1);
+	softReset();
 }
 
 void bootapp_reboot_user0(uint8_t reserved)
@@ -50,6 +42,5 @@ void bootapp_reboot_user0(uint8_t reserved)
 	boot_app_flags = BOOT_APP_FLG_USER0;
 	boot_reserved = reserved;
 	bootapp_print_vars();
-	wdt_enable(WDTO_15MS);
-	while(1);
+	softReset();
 }

+ 2 - 2
Firmware/bootapp.h

@@ -3,11 +3,11 @@
 #define BOOTAPP_H
 
 #include "config.h"
+#include <avr/io.h>
 #include <inttypes.h>
 
 
-#define RAMSIZE        0x2000
-#define ram_array ((uint8_t*)(0))
+#define RAMSIZE        (RAMEND+1-RAMSTART)
 #define boot_src_addr  (*((uint32_t*)(RAMSIZE - 16)))
 #define boot_dst_addr  (*((uint32_t*)(RAMSIZE - 12)))
 #define boot_copy_size (*((uint16_t*)(RAMSIZE - 8)))

+ 441 - 381
Firmware/cardreader.cpp

@@ -1,6 +1,9 @@
 #include "Marlin.h"
+#include "cmdqueue.h"
 #include "cardreader.h"
 #include "ultralcd.h"
+#include "conv2str.h"
+#include "menu.h"
 #include "stepper.h"
 #include "temperature.h"
 #include "language.h"
@@ -14,11 +17,6 @@ CardReader::CardReader()
 
    #ifdef SDCARD_SORT_ALPHA
      sort_count = 0;
-     #if SDSORT_GCODE
-       sort_alpha = true;
-     sort_folders = FOLDER_SORTING;
-     //sort_reverse = false;
-     #endif
    #endif
 
    filesize = 0;
@@ -31,6 +29,7 @@ CardReader::CardReader()
    workDirDepth = 0;
    file_subcall_ctr=0;
    memset(workDirParents, 0, sizeof(workDirParents));
+   presort_flag = false;
 
    autostart_stilltocheck=true; //the SD start is delayed, because otherwise the serial cannot answer fast enough to make contact with the host software.
    lastnr=0;
@@ -61,18 +60,33 @@ char *createFilename(char *buffer,const dir_t &p) //buffer>12characters
 
 /**
 +* Dive into a folder and recurse depth-first to perform a pre-set operation lsAction:
-+*   LS_Count       - Add +1 to nrFiles for every file within the parent
-+*   LS_GetFilename - Get the filename of the file indexed by nrFiles
-+*   LS_SerialPrint - Print the full path and size of each file to serial output
++*   LS_Count           - Add +1 to nrFiles for every file within the parent
++*   LS_GetFilename     - Get the filename of the file indexed by nrFiles
++*   LS_SerialPrint     - Print the full path and size of each file to serial output
 +*/
 
-void CardReader::lsDive(const char *prepend, SdFile parent, const char * const match/*=NULL*/) {
+void CardReader::lsDive(const char *prepend, SdFile parent, const char * const match/*=NULL*/, LsAction lsAction, ls_param lsParams) {
+	static uint8_t recursionCnt = 0;
+	// RAII incrementer for the recursionCnt
+	class _incrementer
+	{
+		public:
+		_incrementer() {recursionCnt++;}
+		~_incrementer() {recursionCnt--;}
+	} recursionCntIncrementer;
+	
 	dir_t p;
 	uint8_t cnt = 0;
 		// Read the next entry from a directory
-		while (parent.readDir(p, longFilename) > 0) {
-			// If the entry is a directory and the action is LS_SerialPrint
-			if (DIR_IS_SUBDIR(&p) && lsAction != LS_Count && lsAction != LS_GetFilename) {
+		for (position = parent.curPosition(); parent.readDir(p, longFilename) > 0; position = parent.curPosition()) {
+			if (recursionCnt > MAX_DIR_DEPTH)
+				return;
+			uint8_t pn0 = p.name[0];
+			if (pn0 == DIR_NAME_FREE) break;
+			if (pn0 == DIR_NAME_DELETED || pn0 == '.') continue;
+			if (longFilename[0] == '.') continue;
+			if (!DIR_IS_FILE_OR_SUBDIR(&p) || (p.attributes & DIR_ATT_HIDDEN)) continue;
+			if (DIR_IS_SUBDIR(&p) && lsAction == LS_SerialPrint) { // If the entry is a directory and the action is LS_SerialPrint
 				// Get the short name for the item, which we know is a folder
 				char lfilename[FILENAME_LENGTH];
 				createFilename(lfilename, p);
@@ -89,23 +103,23 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 				// Serial.print(path);
 				// Get a new directory object using the full path
 				// and dive recursively into it.
+				
+				if (lsParams.LFN)
+					printf_P(PSTR("DIR_ENTER: %s \"%s\"\n"), path, longFilename[0] ? longFilename : lfilename);
+				
 				SdFile dir;
 				if (!dir.open(parent, lfilename, O_READ)) {
-					if (lsAction == LS_SerialPrint) {
-						//SERIAL_ECHO_START();
-						//SERIAL_ECHOPGM(_i("Cannot open subdir"));////MSG_SD_CANT_OPEN_SUBDIR
-						//SERIAL_ECHOLN(lfilename);
-					}
+					//SERIAL_ECHO_START();
+					//SERIAL_ECHOPGM(_i("Cannot open subdir"));////MSG_SD_CANT_OPEN_SUBDIR
+					//SERIAL_ECHOLN(lfilename);
 				}
-				lsDive(path, dir);
+				lsDive(path, dir, NULL, lsAction, lsParams);
 				// close() is done automatically by destructor of SdFile
+				
+				if (lsParams.LFN)
+					puts_P(PSTR("DIR_EXIT"));
 			}
 			else {
-				uint8_t pn0 = p.name[0];
-				if (pn0 == DIR_NAME_FREE) break;
-				if (pn0 == DIR_NAME_DELETED || pn0 == '.') continue;
-				if (longFilename[0] == '.') continue;
-				if (!DIR_IS_FILE_OR_SUBDIR(&p) || (p.attributes & DIR_ATT_HIDDEN)) continue;
 				filenameIsDir = DIR_IS_SUBDIR(&p);
 				if (!filenameIsDir && (p.name[8] != 'G' || p.name[9] == '~')) continue;
 				switch (lsAction) {
@@ -117,15 +131,33 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 						createFilename(filename, p);
 						SERIAL_PROTOCOL(prepend);
 						SERIAL_PROTOCOL(filename);
+						
 						MYSERIAL.write(' ');
-						SERIAL_PROTOCOLLN(p.fileSize);
+						SERIAL_PROTOCOL(p.fileSize);
+						
+						if (lsParams.timestamp)
+						{
+							crmodDate = p.lastWriteDate;
+							crmodTime = p.lastWriteTime;
+							if( crmodDate < p.creationDate || ( crmodDate == p.creationDate && crmodTime < p.creationTime ) ){
+								crmodDate = p.creationDate;
+								crmodTime = p.creationTime;
+							}
+							printf_P(PSTR(" %#lx"), ((uint32_t)crmodDate << 16) | crmodTime);
+						}
+						
+						if (lsParams.LFN)
+							printf_P(PSTR(" \"%s\""), LONGEST_FILENAME);
+						
+						SERIAL_PROTOCOLLN();
+						manage_heater();
 						break;
 				
 					case LS_GetFilename:
-						//SERIAL_ECHOPGM("File: ");				
+						//SERIAL_ECHOPGM("File: ");
 						createFilename(filename, p);
-						cluster = parent.curCluster();
-						position = parent.curPosition();
+						// cluster = parent.curCluster();
+						// position = parent.curPosition();
 						/*MYSERIAL.println(filename);
 						SERIAL_ECHOPGM("Write date: ");
 						writeDate = p.lastWriteDate;
@@ -159,18 +191,14 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
 		} // while readDir
 }
 
-void CardReader::ls() 
+void CardReader::ls(ls_param params)
 {
-  lsAction=LS_SerialPrint;
-  //if(lsAction==LS_Count)
-  //nrFiles=0;
-
   root.rewind();
-  lsDive("",root);
+  lsDive("",root, NULL, LS_SerialPrint, params);
 }
 
 
-void CardReader::initsd()
+void CardReader::initsd(bool doPresort/* = true*/)
 {
   cardOK = false;
   if(root.isOpen())
@@ -214,7 +242,8 @@ void CardReader::initsd()
   workDirDepth = 0;
 
   #ifdef SDCARD_SORT_ALPHA
-	presort();
+  if (doPresort)
+    presort();
   #endif
 
   /*
@@ -226,23 +255,25 @@ void CardReader::initsd()
   
 }
 
-void CardReader::setroot()
+void CardReader::setroot(bool doPresort)
 {
-  /*if(!workDir.openRoot(&volume))
-  {
-    SERIAL_ECHOLNPGM(MSG_SD_WORKDIR_FAIL);
-  }*/
   workDir=root;
+  workDirDepth = 0;
   
   curDir=&workDir;
-  #ifdef SDCARD_SORT_ALPHA
-	  presort();
-  #endif
+#ifdef SDCARD_SORT_ALPHA
+	if (doPresort)
+		presort();
+	else
+		presort_flag = true;
+#endif
 }
 void CardReader::release()
 {
   sdprinting = false;
   cardOK = false;
+  SERIAL_ECHO_START;
+  SERIAL_ECHOLNRPGM(_n("SD card released"));////MSG_SD_CARD_RELEASED
 }
 
 void CardReader::startFileprint()
@@ -260,7 +291,7 @@ void CardReader::startFileprint()
 void CardReader::openLogFile(const char* name)
 {
   logging = true;
-  openFile(name, false);
+  openFileWrite(name);
 }
 
 void CardReader::getDirName(char* name, uint8_t level)
@@ -287,6 +318,18 @@ void CardReader::getAbsFilename(char *t)
   else
     t[0]=0;
 }
+
+void CardReader::printAbsFilenameFast()
+{
+    SERIAL_PROTOCOL('/');
+    for (uint8_t i = 0; i < getWorkDirDepth(); i++)
+    {
+        SERIAL_PROTOCOL(dir_names[i]);
+        SERIAL_PROTOCOL('/');
+    }
+    SERIAL_PROTOCOL(LONGEST_FILENAME);
+}
+
 /**
  * @brief Dive into subfolder
  *
@@ -300,19 +343,17 @@ void CardReader::getAbsFilename(char *t)
  * @param[in,out] fileName
  *  expects file name including path
  *  in case of absolute path, file name without path is returned
- * @param[in,out] dir SdFile object to operate with,
- *  in case of absolute path, curDir is modified to point to dir,
- *  so it is not possible to create on stack inside this function,
- *  as curDir would point to destroyed object.
  */
-void CardReader::diveSubfolder (const char *fileName, SdFile& dir)
+bool CardReader::diveSubfolder (const char *&fileName)
 {
     curDir=&root;
-    if (!fileName) return;
+    if (!fileName)
+        return 1;
 
     const char *dirname_start, *dirname_end;
     if (fileName[0] == '/') // absolute path
     {
+        setroot(false);
         dirname_start = fileName + 1;
         while (*dirname_start)
         {
@@ -323,23 +364,13 @@ void CardReader::diveSubfolder (const char *fileName, SdFile& dir)
             {
                 const size_t maxLen = 12;
                 char subdirname[maxLen+1];
-                subdirname[maxLen] = 0;
                 const size_t len = ((static_cast<size_t>(dirname_end-dirname_start))>maxLen) ? maxLen : (dirname_end-dirname_start);
                 strncpy(subdirname, dirname_start, len);
-                SERIAL_ECHOLN(subdirname);
-                if (!dir.open(curDir, subdirname, O_READ))
-                {
-                    SERIAL_PROTOCOLRPGM(MSG_SD_OPEN_FILE_FAIL);
-                    SERIAL_PROTOCOL(subdirname);
-                    SERIAL_PROTOCOLLN('.');
-                    return;
-                }
-                else
-                {
-                    //SERIAL_ECHOLN("dive ok");
-                }
-
-                curDir = &dir;
+                subdirname[len] = 0;
+                if (!chdir(subdirname, false))
+                    return 0;
+
+                curDir = &workDir;
                 dirname_start = dirname_end + 1;
             }
             else // the reminder after all /fsa/fdsa/ is the filename
@@ -356,100 +387,147 @@ void CardReader::diveSubfolder (const char *fileName, SdFile& dir)
     {
         curDir = &workDir;
     }
+    return 1;
 }
 
-void CardReader::openFile(const char* name,bool read, bool replace_current/*=true*/)
-{
-  if(!cardOK)
-    return;
-  if(file.isOpen())  //replacing current file by new file, or subfile call
-  {
-    if(!replace_current)
-    {
-     if((int)file_subcall_ctr>(int)SD_PROCEDURE_DEPTH-1)
-     {
-       // SERIAL_ERROR_START;
-       // SERIAL_ERRORPGM("trying to call sub-gcode files with too many levels. MAX level is:");
-       // SERIAL_ERRORLN(SD_PROCEDURE_DEPTH);
-       kill(_n("trying to call sub-gcode files with too many levels."), 1);
-       return;
-     }
-     
-     SERIAL_ECHO_START;
-     SERIAL_ECHOPGM("SUBROUTINE CALL target:\"");
-     SERIAL_ECHO(name);
-     SERIAL_ECHOPGM("\" parent:\"");
-     
-     //store current filename and position
-     getAbsFilename(filenames[file_subcall_ctr]);
-     
-     SERIAL_ECHO(filenames[file_subcall_ctr]);
-     SERIAL_ECHOPGM("\" pos");
-     SERIAL_ECHOLN(sdpos);
-     filespos[file_subcall_ctr]=sdpos;
-     file_subcall_ctr++;
-    }
-    else
-    {
-     SERIAL_ECHO_START;
-     SERIAL_ECHOPGM("Now doing file: ");
-     SERIAL_ECHOLN(name);
+static const char ofKill[] PROGMEM = "trying to call sub-gcode files with too many levels.";
+static const char ofSubroutineCallTgt[] PROGMEM = "SUBROUTINE CALL target:\"";
+static const char ofParent[] PROGMEM = "\" parent:\"";
+static const char ofPos[] PROGMEM = "\" pos";
+static const char ofNowDoingFile[] PROGMEM = "Now doing file: ";
+static const char ofNowFreshFile[] PROGMEM = "Now fresh file: ";
+static const char ofFileOpened[] PROGMEM = "File opened: ";
+static const char ofSize[] PROGMEM = " Size: ";
+static const char ofFileSelected[] PROGMEM = "File selected";
+static const char ofSDPrinting[] PROGMEM = "SD-PRINTING";
+static const char ofWritingToFile[] PROGMEM = "Writing to file: ";
+
+void CardReader::openFileReadFilteredGcode(const char* name, bool replace_current/* = false*/){
+    if(!cardOK)
+        return;
+    
+    if(file.isOpen()){  //replacing current file by new file, or subfile call
+        if(!replace_current){
+            if((int)file_subcall_ctr>(int)SD_PROCEDURE_DEPTH-1){
+                // SERIAL_ERROR_START;
+                // SERIAL_ERRORPGM("trying to call sub-gcode files with too many levels. MAX level is:");
+                // SERIAL_ERRORLN(SD_PROCEDURE_DEPTH);
+                kill(ofKill, 1);
+                return;
+            }
+            
+            SERIAL_ECHO_START;
+            SERIAL_ECHORPGM(ofSubroutineCallTgt);
+            SERIAL_ECHO(name);
+            SERIAL_ECHORPGM(ofParent);
+            
+            //store current filename and position
+            getAbsFilename(filenames[file_subcall_ctr]);
+            
+            SERIAL_ECHO(filenames[file_subcall_ctr]);
+            SERIAL_ECHORPGM(ofPos);
+            SERIAL_ECHOLN(sdpos);
+            filespos[file_subcall_ctr]=sdpos;
+            file_subcall_ctr++;
+        } else {
+            SERIAL_ECHO_START;
+            SERIAL_ECHORPGM(ofNowDoingFile);
+            SERIAL_ECHOLN(name);
+        }
+        file.close();
+    } else { //opening fresh file
+        file_subcall_ctr=0; //resetting procedure depth in case user cancels print while in procedure
+        SERIAL_ECHO_START;
+        SERIAL_ECHORPGM(ofNowFreshFile);
+        SERIAL_ECHOLN(name);
     }
-    file.close();
-  }
-  else //opening fresh file
-  {
-    file_subcall_ctr=0; //resetting procedure depth in case user cancels print while in procedure
-    SERIAL_ECHO_START;
-    SERIAL_ECHOPGM("Now fresh file: ");
-    SERIAL_ECHOLN(name);
-  }
-  sdprinting = false;
-
-  SdFile myDir;
-  const char *fname=name;
-  diveSubfolder(fname,myDir);
+    sdprinting = false;
+  
+    const char *fname=name;
+    if (!diveSubfolder(fname))
+      return;
+  
+    if (file.openFilteredGcode(curDir, fname)) {
+        getfilename(0, fname);
+        filesize = file.fileSize();
+        SERIAL_PROTOCOLRPGM(ofFileOpened);////MSG_SD_FILE_OPENED
+        printAbsFilenameFast();
+        SERIAL_PROTOCOLRPGM(ofSize);////MSG_SD_SIZE
+        SERIAL_PROTOCOLLN(filesize);
+        sdpos = 0;
+        
+        SERIAL_PROTOCOLLNRPGM(ofFileSelected);////MSG_SD_FILE_SELECTED
+        lcd_setstatuspgm(ofFileSelected);
+        scrollstuff = 0;
+      } else {
+        SERIAL_PROTOCOLRPGM(MSG_SD_OPEN_FILE_FAIL);
+        SERIAL_PROTOCOL(fname);
+        SERIAL_PROTOCOLLN('.');
+      }
+}
 
-  if(read)
-  {
-    if (file.open(curDir, fname, O_READ)) 
-    {
-      filesize = file.fileSize();
-      SERIAL_PROTOCOLRPGM(_N("File opened: "));////MSG_SD_FILE_OPENED
-      SERIAL_PROTOCOL(fname);
-      SERIAL_PROTOCOLRPGM(_n(" Size: "));////MSG_SD_SIZE
-      SERIAL_PROTOCOLLN(filesize);
-      sdpos = 0;
-      
-      SERIAL_PROTOCOLLNRPGM(_N("File selected"));////MSG_SD_FILE_SELECTED
-      getfilename(0, fname);
-      lcd_setstatus(longFilename[0] ? longFilename : fname);
-      lcd_setstatus("SD-PRINTING         ");
-    }
-    else
-    {
-      SERIAL_PROTOCOLRPGM(MSG_SD_OPEN_FILE_FAIL);
-      SERIAL_PROTOCOL(fname);
-      SERIAL_PROTOCOLLN('.');
-    }
-  }
-  else 
-  { //write
-    if (!file.open(curDir, fname, O_CREAT | O_APPEND | O_WRITE | O_TRUNC))
-    {
-      SERIAL_PROTOCOLRPGM(MSG_SD_OPEN_FILE_FAIL);
-      SERIAL_PROTOCOL(fname);
-      SERIAL_PROTOCOLLN('.');
+void CardReader::openFileWrite(const char* name)
+{
+    if(!cardOK)
+        return;
+    if(file.isOpen()){  //replacing current file by new file, or subfile call
+#if 0
+        // I doubt chained files support is necessary for file saving:
+        // Intentionally disabled because it takes a lot of code size while being not used
+
+        if((int)file_subcall_ctr>(int)SD_PROCEDURE_DEPTH-1){
+            // SERIAL_ERROR_START;
+            // SERIAL_ERRORPGM("trying to call sub-gcode files with too many levels. MAX level is:");
+            // SERIAL_ERRORLN(SD_PROCEDURE_DEPTH);
+            kill(ofKill, 1);
+            return;
+        }
+        
+        SERIAL_ECHO_START;
+        SERIAL_ECHORPGM(ofSubroutineCallTgt);
+        SERIAL_ECHO(name);
+        SERIAL_ECHORPGM(ofParent);
+        
+        //store current filename and position
+        getAbsFilename(filenames[file_subcall_ctr]);
+        
+        SERIAL_ECHO(filenames[file_subcall_ctr]);
+        SERIAL_ECHORPGM(ofPos);
+        SERIAL_ECHOLN(sdpos);
+        filespos[file_subcall_ctr]=sdpos;
+        file_subcall_ctr++;
+        file.close();
+#else
+        SERIAL_ECHOLNPGM("File already opened");
+#endif
+    } else { //opening fresh file
+        file_subcall_ctr=0; //resetting procedure depth in case user cancels print while in procedure
+        SERIAL_ECHO_START;
+        SERIAL_ECHORPGM(ofNowFreshFile);
+        SERIAL_ECHOLN(name);
     }
-    else
-    {
-      saving = true;
-      SERIAL_PROTOCOLRPGM(_N("Writing to file: "));////MSG_SD_WRITE_TO_FILE
-      SERIAL_PROTOCOLLN(name);
-      lcd_setstatus(fname);
+    sdprinting = false;
+    
+    const char *fname=name;
+    if (!diveSubfolder(fname))
+      return;
+    
+    //write
+    if (!file.open(curDir, fname, O_CREAT | O_APPEND | O_WRITE | O_TRUNC)){
+        SERIAL_PROTOCOLRPGM(MSG_SD_OPEN_FILE_FAIL);
+        SERIAL_PROTOCOL(fname);
+        SERIAL_PROTOCOLLN('.');
+    } else {
+        saving = true;
+        getfilename(0, fname);
+        SERIAL_PROTOCOLRPGM(ofWritingToFile);////MSG_SD_WRITE_TO_FILE
+        printAbsFilenameFast();
+        SERIAL_PROTOCOLLN();
+        
+        SERIAL_PROTOCOLLNRPGM(ofFileSelected);////MSG_SD_FILE_SELECTED
+        lcd_setstatuspgm(ofFileSelected);
+        scrollstuff = 0;
     }
-  }
-  
 }
 
 void CardReader::removeFile(const char* name)
@@ -458,9 +536,9 @@ void CardReader::removeFile(const char* name)
     file.close();
     sdprinting = false;
 
-    SdFile myDir;
     const char *fname=name;
-    diveSubfolder(fname,myDir);
+    if (!diveSubfolder(fname))
+      return;
 
     if (file.remove(curDir, fname)) 
     {
@@ -475,7 +553,7 @@ void CardReader::removeFile(const char* name)
     {
       SERIAL_PROTOCOLPGM("Deletion failed, File: ");
       SERIAL_PROTOCOL(fname);
-      SERIAL_PROTOCOLLNPGM(".");
+      SERIAL_PROTOCOLLN('.');
     }
   
 }
@@ -485,48 +563,42 @@ uint32_t CardReader::getFileSize()
 	return filesize;
 }
 
-void CardReader::getStatus()
+void CardReader::getStatus(bool arg_P)
 {
-  if(sdprinting)
-  {
-      if (isPrintPaused) {
-          SERIAL_PROTOCOLLNPGM("SD print paused");
-      }
-      else if (saved_printing) {
-          SERIAL_PROTOCOLLNPGM("Print saved");
-      }
-      else {
-          SERIAL_PROTOCOLLN(longFilename);
-          SERIAL_PROTOCOLRPGM(_N("SD printing byte "));////MSG_SD_PRINTING_BYTE
-          SERIAL_PROTOCOL(sdpos);
-          SERIAL_PROTOCOL('/');
-          SERIAL_PROTOCOLLN(filesize);
-          uint16_t time = ( _millis() - starttime ) / 60000U;
-          SERIAL_PROTOCOL(itostr2(time/60));
-          SERIAL_PROTOCOL(':');
-          SERIAL_PROTOCOLLN(itostr2(time%60));
-      }
-  }
-  else {
-    SERIAL_PROTOCOLLNPGM("Not SD printing");
-  }
+    if (isPrintPaused)
+    {
+        if (saved_printing && (saved_printing_type == PRINTING_TYPE_SD))
+            SERIAL_PROTOCOLLNPGM("SD print paused");
+        else
+            SERIAL_PROTOCOLLNPGM("Print saved");
+    }
+    else if (sdprinting)
+    {
+        if (arg_P)
+        {
+            printAbsFilenameFast();
+            SERIAL_PROTOCOLLN();
+        }
+        else
+            SERIAL_PROTOCOLLN(LONGEST_FILENAME);
+        
+        SERIAL_PROTOCOLRPGM(_N("SD printing byte "));////MSG_SD_PRINTING_BYTE
+        SERIAL_PROTOCOL(sdpos);
+        SERIAL_PROTOCOL('/');
+        SERIAL_PROTOCOLLN(filesize);
+        uint16_t time = ( _millis() - starttime ) / 60000U;
+        SERIAL_PROTOCOL(itostr2(time/60));
+        SERIAL_PROTOCOL(':');
+        SERIAL_PROTOCOLLN(itostr2(time%60));
+    }
+    else
+        SERIAL_PROTOCOLLNPGM("Not SD printing");
 }
 void CardReader::write_command(char *buf)
 {
-  char* begin = buf;
-  char* npos = 0;
-  char* end = buf + strlen(buf) - 1;
-
   file.writeError = false;
-  if((npos = strchr(buf, 'N')) != NULL)
-  {
-    begin = strchr(npos, ' ') + 1;
-    end = strchr(npos, '*') - 1;
-  }
-  end[1] = '\r';
-  end[2] = '\n';
-  end[3] = '\0';
-  file.write(begin);
+  file.write(buf); //write command
+  file.write("\r\n"); //write line termination
   if (file.writeError)
   {
     SERIAL_ERROR_START;
@@ -543,7 +615,7 @@ void CardReader::write_command_no_newline(char *buf)
   {
     SERIAL_ERROR_START;
     SERIAL_ERRORLNRPGM(MSG_SD_ERR_WRITE_TO_FILE);
-    MYSERIAL.println("An error while writing to the SD Card.");
+    SERIAL_PROTOCOLLNPGM("An error while writing to the SD Card.");
   }
 }
 
@@ -619,34 +691,39 @@ void CardReader::closefile(bool store_location)
 void CardReader::getfilename(uint16_t nr, const char * const match/*=NULL*/)
 {
   curDir=&workDir;
-  lsAction=LS_GetFilename;
   nrFiles=nr;
   curDir->rewind();
-  lsDive("",*curDir,match);
+  lsDive("",*curDir,match, LS_GetFilename);
   
 }
 
 void CardReader::getfilename_simple(uint32_t position, const char * const match/*=NULL*/)
 {
 	curDir = &workDir;
-	lsAction = LS_GetFilename;
 	nrFiles = 0;
 	curDir->seekSet(position);
-	lsDive("", *curDir, match);
+	lsDive("", *curDir, match, LS_GetFilename);
+}
+
+void CardReader::getfilename_next(uint32_t position, const char * const match/*=NULL*/)
+{
+	curDir = &workDir;
+	nrFiles = 1;
+	curDir->seekSet(position);
+	lsDive("", *curDir, match, LS_GetFilename);
 }
 
 uint16_t CardReader::getnrfilenames()
 {
   curDir=&workDir;
-  lsAction=LS_Count;
   nrFiles=0;
   curDir->rewind();
-  lsDive("",*curDir);
+  lsDive("",*curDir, NULL, LS_Count);
   //SERIAL_ECHOLN(nrFiles);
   return nrFiles;
 }
 
-void CardReader::chdir(const char * relpath)
+bool CardReader::chdir(const char * relpath, bool doPresort)
 {
   SdFile newfile;
   SdFile *parent=&root;
@@ -654,23 +731,32 @@ void CardReader::chdir(const char * relpath)
   if(workDir.isOpen())
     parent=&workDir;
   
-  if(!newfile.open(*parent,relpath, O_READ))
+  if(!newfile.open(*parent,relpath, O_READ) || ((workDirDepth + 1) >= MAX_DIR_DEPTH))
   {
    SERIAL_ECHO_START;
    SERIAL_ECHORPGM(_n("Cannot enter subdir: "));////MSG_SD_CANT_ENTER_SUBDIR
    SERIAL_ECHOLN(relpath);
+   return 0;
   }
   else
   {
+    strcpy(dir_names[workDirDepth], relpath);
+    puts(relpath);
+
     if (workDirDepth < MAX_DIR_DEPTH) {
       for (int d = ++workDirDepth; d--;)
         workDirParents[d+1] = workDirParents[d];
       workDirParents[0]=*parent;
     }
     workDir=newfile;
-	#ifdef SDCARD_SORT_ALPHA
+
+#ifdef SDCARD_SORT_ALPHA
+	if (doPresort)
 		presort();
-	#endif
+	else
+		presort_flag = true;
+#endif
+	return 1;
   }
 }
 
@@ -695,13 +781,11 @@ void CardReader::updir()
 /**
 * Get the name of a file in the current directory by sort-index
 */
-void CardReader::getfilename_sorted(const uint16_t nr) {
-	getfilename(
-	#if SDSORT_GCODE
-		sort_alpha &&
-	#endif
-		(nr < sort_count) ? sort_order[nr] : nr
-	);
+void CardReader::getfilename_sorted(const uint16_t nr, uint8_t sdSort) {
+    if (nr < sort_count)
+        getfilename_simple(sort_positions[(sdSort == SD_SORT_ALPHA) ? (sort_count - nr - 1) : nr]);
+    else
+        getfilename(nr);
 }
 
 /**
@@ -718,9 +802,6 @@ void CardReader::presort() {
 
 	if (sdSort == SD_SORT_NONE) return; //sd sort is turned off
 
-	#if SDSORT_GCODE
-	if (!sort_alpha) return;
-	#endif
 	KEEPALIVE_STATE(IN_HANDLER);
 
 	// Throw away old sort index
@@ -736,178 +817,170 @@ void CardReader::presort() {
 			lcd_show_fullscreen_message_and_wait_P(_i("Some files will not be sorted. Max. No. of files in 1 folder for sorting is 100."));////MSG_FILE_CNT c=20 r=6
 			fileCnt = SDSORT_LIMIT;
 		}
-		lcd_clear();
-		#if !SDSORT_USES_RAM
-			lcd_set_progress();
-		#endif
-		lcd_puts_at_P(0, 1, _i("Sorting files"));////MSG_SORTING c=20 r=1
-
-		// Sort order is always needed. May be static or dynamic.
-		#if SDSORT_DYNAMIC_RAM
-		sort_order = new uint8_t[fileCnt];
-		#endif
-
-		// Use RAM to store the entire directory during pre-sort.
-		// SDSORT_LIMIT should be set to prevent over-allocation.
-		#if SDSORT_USES_RAM
-
-		// If using dynamic ram for names, allocate on the heap.
-		#if SDSORT_CACHE_NAMES
-		#if SDSORT_DYNAMIC_RAM
-		sortshort = new char*[fileCnt];
-		sortnames = new char*[fileCnt];
-		#endif
-		#elif SDSORT_USES_STACK
-		char sortnames[fileCnt][LONG_FILENAME_LENGTH];
-		uint16_t modification_time[fileCnt];
-		uint16_t modification_date[fileCnt];
-		#endif
-
-		// Folder sorting needs 1 bit per entry for flags.
-		#if HAS_FOLDER_SORTING
-		#if SDSORT_DYNAMIC_RAM
-		isDir = new uint8_t[(fileCnt + 7) >> 3];
-		#elif SDSORT_USES_STACK
-		uint8_t isDir[(fileCnt + 7) >> 3];
-		#endif
-		#endif
-
-		#else // !SDSORT_USES_RAM
-
-		uint32_t positions[fileCnt];
 
 		// By default re-read the names from SD for every compare
 		// retaining only two filenames at a time. This is very
 		// slow but is safest and uses minimal RAM.
-		char name1[LONG_FILENAME_LENGTH + 1];
+		char name1[LONG_FILENAME_LENGTH];
 		uint16_t crmod_time_bckp;
 		uint16_t crmod_date_bckp;
 
+		#if HAS_FOLDER_SORTING
+		uint16_t dirCnt = 0;
 		#endif
-		position = 0;
+
 		if (fileCnt > 1) {
 			// Init sort order.
+			uint8_t sort_order[fileCnt];
 			for (uint16_t i = 0; i < fileCnt; i++) {
 				if (!IS_SD_INSERTED) return;
 				manage_heater();
+				if (i == 0)
+					getfilename(0);
+				else
+					getfilename_next(position);
 				sort_order[i] = i;
-				positions[i] = position;
-				getfilename(i);
-				// If using RAM then read all filenames now.
-				#if SDSORT_USES_RAM
-				getfilename(i);
-				#if SDSORT_DYNAMIC_RAM
-				// Use dynamic method to copy long filename
-				sortnames[i] = strdup(LONGEST_FILENAME);
-				#if SDSORT_CACHE_NAMES
-				// When caching also store the short name, since
-				// we're replacing the getfilename() behavior.
-				sortshort[i] = strdup(filename);
-				#endif
-				#else
-				// Copy filenames into the static array
-				strcpy(sortnames[i], LONGEST_FILENAME);
-				modification_time[i] = crmodTime;
-				modification_date[i] = crmodDate;
-				#if SDSORT_CACHE_NAMES
-				strcpy(sortshort[i], filename);
-				#endif
-				#endif
-				// char out[30];
-				// sprintf_P(out, PSTR("---- %i %s %s"), i, filenameIsDir ? "D" : " ", sortnames[i]);
-				// SERIAL_ECHOLN(out);
+				sort_positions[i] = position;
 				#if HAS_FOLDER_SORTING
-				const uint16_t bit = i & 0x07, ind = i >> 3;
-				if (bit == 0) isDir[ind] = 0x00;
-				if (filenameIsDir) isDir[ind] |= _BV(bit);
-				#endif
+				if (filenameIsDir) dirCnt++;
 				#endif
 			}
 
 #ifdef QUICKSORT
 			quicksort(0, fileCnt - 1);
-#else //Qicksort not defined, use Bubble Sort
-			uint32_t counter = 0;
-			uint16_t total = 0.5*(fileCnt - 1)*(fileCnt);
-
-			// Compare names from the array or just the two buffered names
-			#if SDSORT_USES_RAM
-			#define _SORT_CMP_NODIR() (strcasecmp(sortnames[o1], sortnames[o2]) > 0)
-			#define _SORT_CMP_TIME_NODIR() (((modification_date[o1] == modification_date[o2]) && (modification_time[o1] < modification_time[o2])) || \
-																	(modification_date[o1] < modification_date [o2]))
-			#else
-			#define _SORT_CMP_NODIR() (strcasecmp(name1, name2) > 0) //true if lowercase(name1) > lowercase(name2)
-			#define _SORT_CMP_TIME_NODIR() (((crmod_date_bckp == crmodDate) && (crmod_time_bckp > crmodTime)) || \
-																	(crmod_date_bckp > crmodDate))
+#elif defined(SHELLSORT)
 
-			#endif
+#define _SORT_CMP_NODIR() (strcasecmp(name1, name2) < 0) //true if lowercase(name1) < lowercase(name2)
+#define _SORT_CMP_TIME_NODIR() (((crmod_date_bckp == crmodDate) && (crmod_time_bckp < crmodTime)) || (crmod_date_bckp < crmodDate))
 
-			#if HAS_FOLDER_SORTING
-			#if SDSORT_USES_RAM
-			// Folder sorting needs an index and bit to test for folder-ness.
-			const uint8_t ind1 = o1 >> 3, bit1 = o1 & 0x07,
-				ind2 = o2 >> 3, bit2 = o2 & 0x07;
-			#define _SORT_CMP_DIR(fs) \
-										  (((isDir[ind1] & _BV(bit1)) != 0) == ((isDir[ind2] & _BV(bit2)) != 0) \
-											? _SORT_CMP_NODIR() \
-											: (isDir[fs > 0 ? ind1 : ind2] & (fs > 0 ? _BV(bit1) : _BV(bit2))) != 0)
-			#define _SORT_CMP_TIME_DIR(fs) \
-										  (((isDir[ind1] & _BV(bit1)) != 0) == ((isDir[ind2] & _BV(bit2)) != 0) \
-											? _SORT_CMP_TIME_NODIR() \
-											: (isDir[fs > 0 ? ind1 : ind2] & (fs > 0 ? _BV(bit1) : _BV(bit2))) != 0)
-			#else
-			#define _SORT_CMP_DIR(fs) ((dir1 == filenameIsDir) ? _SORT_CMP_NODIR() : (fs > 0 ? dir1 : !dir1))
-			#define _SORT_CMP_TIME_DIR(fs) ((dir1 == filenameIsDir) ? _SORT_CMP_TIME_NODIR() : (fs < 0 ? dir1 : !dir1))
-			#endif
-			#endif
+#if HAS_FOLDER_SORTING
+#define _SORT_CMP_DIR(fs) ((dir1 == filenameIsDir) ? _SORT_CMP_NODIR() : (fs < 0 ? dir1 : !dir1))
+#define _SORT_CMP_TIME_DIR(fs) ((dir1 == filenameIsDir) ? _SORT_CMP_TIME_NODIR() : (fs < 0 ? dir1 : !dir1))
+#endif
+
+			for (uint8_t runs = 0; runs < 2; runs++)
+			{
+				//run=0: sorts all files and moves folders to the beginning
+				//run=1: assumes all folders are at the beginning of the list and sorts them
+				uint16_t sortCountFiles = 0;
+				if (runs == 0)
+				{
+					sortCountFiles = fileCnt;
+				}
+				#if HAS_FOLDER_SORTING
+				else
+				{
+					sortCountFiles = dirCnt;
+				}
+				#endif
+				
+				uint16_t counter = 0;
+				uint16_t total = 0;
+				for (uint16_t i = sortCountFiles/2; i > 0; i /= 2) total += sortCountFiles - i; //total runs for progress bar
+				menu_progressbar_init(total, (runs == 0)?_i("Sorting files"):_i("Sorting folders"));
+				
+				for (uint16_t gap = sortCountFiles/2; gap > 0; gap /= 2)
+				{
+					for (uint16_t i = gap; i < sortCountFiles; i++)
+					{
+						if (!IS_SD_INSERTED) return;
+						
+						menu_progressbar_update(counter);
+						counter++;
+						
+						manage_heater();
+						uint8_t orderBckp = sort_order[i];
+						getfilename_simple(sort_positions[orderBckp]);
+						strcpy(name1, LONGEST_FILENAME); // save (or getfilename below will trounce it)
+						crmod_date_bckp = crmodDate;
+						crmod_time_bckp = crmodTime;
+						#if HAS_FOLDER_SORTING
+						bool dir1 = filenameIsDir;
+						#endif
+						
+						uint16_t j = i;
+						getfilename_simple(sort_positions[sort_order[j - gap]]);
+						char *name2 = LONGEST_FILENAME; // use the string in-place
+						#if HAS_FOLDER_SORTING
+						while (j >= gap && ((sdSort == SD_SORT_TIME)?_SORT_CMP_TIME_DIR(FOLDER_SORTING):_SORT_CMP_DIR(FOLDER_SORTING)))
+						#else
+						while (j >= gap && ((sdSort == SD_SORT_TIME)?_SORT_CMP_TIME_NODIR():_SORT_CMP_NODIR()))
+						#endif
+						{
+							sort_order[j] = sort_order[j - gap];
+							j -= gap;
+							#ifdef SORTING_DUMP
+							for (uint16_t z = 0; z < sortCountFiles; z++)
+							{
+								printf_P(PSTR("%2u "), sort_order[z]);
+							}
+							printf_P(PSTR("i%2d j%2d gap%2d orderBckp%2d\n"), i, j, gap, orderBckp);
+							#endif
+							if (j < gap) break;
+							getfilename_simple(sort_positions[sort_order[j - gap]]);
+							name2 = LONGEST_FILENAME; // use the string in-place
+						}
+						sort_order[j] = orderBckp;
+					}
+				}
+			}
+
+#else //Bubble Sort
+
+#define _SORT_CMP_NODIR() (strcasecmp(name1, name2) < 0) //true if lowercase(name1) < lowercase(name2)
+#define _SORT_CMP_TIME_NODIR() (((crmod_date_bckp == crmodDate) && (crmod_time_bckp > crmodTime)) || (crmod_date_bckp > crmodDate))
+
+#if HAS_FOLDER_SORTING
+#define _SORT_CMP_DIR(fs) ((dir1 == filenameIsDir) ? _SORT_CMP_NODIR() : (fs < 0 ? dir1 : !dir1))
+#define _SORT_CMP_TIME_DIR(fs) ((dir1 == filenameIsDir) ? _SORT_CMP_TIME_NODIR() : (fs < 0 ? dir1 : !dir1))
+#endif
+
+			uint16_t counter = 0;
+			menu_progressbar_init(0.5*(fileCnt - 1)*(fileCnt), _i("Sorting files"));
 
 			for (uint16_t i = fileCnt; --i;) {
 				if (!IS_SD_INSERTED) return;
 				bool didSwap = false;
 
-				#if !SDSORT_USES_RAM //show progresss bar only if slow sorting method is used
-				int8_t percent = (counter * 100) / total;//((counter * 100) / pow((fileCnt-1),2));
-				for (int column = 0; column < 20; column++) {
-					if (column < (percent / 5))
-					{
-						lcd_set_cursor(column, 2);
-						lcd_print('\x01'); //simple progress bar
-					}
-				}
+				menu_progressbar_update(counter);
 				counter++;
-				#endif
 
-				//MYSERIAL.println(int(i));
 				for (uint16_t j = 0; j < i; ++j) {
 					if (!IS_SD_INSERTED) return;
+					#ifdef SORTING_DUMP
+					for (uint16_t z = 0; z < fileCnt; z++)
+					{
+						printf_P(PSTR("%2u "), sort_order[z]);
+					}
+					MYSERIAL.println();
+					#endif
 					manage_heater();
 					const uint16_t o1 = sort_order[j], o2 = sort_order[j + 1];
 
-					// The most economical method reads names as-needed
-					// throughout the loop. Slow if there are many.
-					#if !SDSORT_USES_RAM
 					counter++;
-					getfilename_simple(positions[o1]);
+					getfilename_simple(sort_positions[o1]);
 					strcpy(name1, LONGEST_FILENAME); // save (or getfilename below will trounce it)
 					crmod_date_bckp = crmodDate;
 					crmod_time_bckp = crmodTime;
 					#if HAS_FOLDER_SORTING
 					bool dir1 = filenameIsDir;
 					#endif
-					getfilename_simple(positions[o2]);
+					getfilename_simple(sort_positions[o2]);
 					char *name2 = LONGEST_FILENAME; // use the string in-place
 
-					#endif // !SDSORT_USES_RAM
-
 													// Sort the current pair according to settings.
 					if (
 					#if HAS_FOLDER_SORTING
-					(sdSort == SD_SORT_TIME && _SORT_CMP_TIME_DIR(FOLDER_SORTING)) || (sdSort == SD_SORT_ALPHA && _SORT_CMP_DIR(FOLDER_SORTING))
+						(sdSort == SD_SORT_TIME && _SORT_CMP_TIME_DIR(FOLDER_SORTING)) || (sdSort == SD_SORT_ALPHA && !_SORT_CMP_DIR(FOLDER_SORTING))
 					#else
-						(sdSort == SD_SORT_TIME && _SORT_CMP_TIME_NODIR()) || (sdSort == SD_SORT_ALPHA && _SORT_CMP_NODIR())
+						(sdSort == SD_SORT_TIME && _SORT_CMP_TIME_NODIR()) || (sdSort == SD_SORT_ALPHA && !_SORT_CMP_NODIR())
 					#endif
 						)
 					{
+						#ifdef SORTING_DUMP
+						puts_P(PSTR("swap"));
+						#endif
+						
 						sort_order[j] = o2;
 						sort_order[j + 1] = o1;
 						didSwap = true;
@@ -916,64 +989,51 @@ void CardReader::presort() {
 				if (!didSwap) break;
 			} //end of bubble sort loop
 #endif
-			  // Using RAM but not keeping names around
-			#if (SDSORT_USES_RAM && !SDSORT_CACHE_NAMES)
-			#if SDSORT_DYNAMIC_RAM
-			for (uint16_t i = 0; i < fileCnt; ++i) free(sortnames[i]);
-			#if HAS_FOLDER_SORTING
-			free(isDir);
-			#endif
-			#endif
+
+			#ifdef SORTING_DUMP
+			for (uint16_t z = 0; z < fileCnt; z++)
+				printf_P(PSTR("%2u "), sort_order[z]);
+			SERIAL_PROTOCOLLN();
 			#endif
+
+			uint8_t sort_order_reverse_index[fileCnt];
+			for (uint8_t i = 0; i < fileCnt; i++)
+				sort_order_reverse_index[sort_order[i]] = i;
+			for (uint8_t i = 0; i < fileCnt; i++)
+			{
+				if (sort_order_reverse_index[i] != i)
+				{
+					uint32_t el = sort_positions[i];
+					uint8_t idx = sort_order_reverse_index[i];
+					while (idx != i)
+					{
+						uint32_t el1 = sort_positions[idx];
+						uint8_t idx1 = sort_order_reverse_index[idx];
+						sort_order_reverse_index[idx] = idx;
+						sort_positions[idx] = el;
+						idx = idx1;
+						el = el1;
+					}
+					sort_order_reverse_index[idx] = idx;
+					sort_positions[idx] = el;
+				}
+			}
+			menu_progressbar_finish();
 		}
 		else {
-			sort_order[0] = 0;
-		#if (SDSORT_USES_RAM && SDSORT_CACHE_NAMES)
 			getfilename(0);
-			#if SDSORT_DYNAMIC_RAM
-			sortnames = new char*[1];
-			sortnames[0] = strdup(LONGEST_FILENAME); // malloc
-			sortshort = new char*[1];
-			sortshort[0] = strdup(filename);         // malloc
-			isDir = new uint8_t[1];
-			#else
-			strcpy(sortnames[0], LONGEST_FILENAME);
-			strcpy(sortshort[0], filename);
-			#endif
-			isDir[0] = filenameIsDir ? 0x01 : 0x00;
-		#endif
+			sort_positions[0] = position;
 		}
 
 		sort_count = fileCnt;
 	}
-#if !SDSORT_USES_RAM //show progresss bar only if slow sorting method is used
-	for (int column = 0; column <= 19; column++)
-	{
-		lcd_set_cursor(column, 2);
-		lcd_print('\x01'); //simple progress bar
-	}
-	_delay(300);
-	lcd_set_degree();
-	lcd_clear();
-#endif
+
 	lcd_update(2);
 	KEEPALIVE_STATE(NOT_BUSY);
-	lcd_timeoutToStatus.start();
 }
 
 void CardReader::flush_presort() {
 	if (sort_count > 0) {
-		#if SDSORT_DYNAMIC_RAM
-		delete sort_order;
-		#if SDSORT_CACHE_NAMES
-		for (uint8_t i = 0; i < sort_count; ++i) {
-			free(sortshort[i]); // strdup
-			free(sortnames[i]); // strdup
-		}
-		delete sortshort;
-		delete sortnames;
-		#endif
-		#endif
 		sort_count = 0;
 	}
 }
@@ -989,7 +1049,7 @@ void CardReader::printingHasFinished()
     {
       file.close();
       file_subcall_ctr--;
-      openFile(filenames[file_subcall_ctr],true,true);
+      openFileReadFilteredGcode(filenames[file_subcall_ctr],true);
       setIndex(filespos[file_subcall_ctr]);
       startFileprint();
     }

+ 44 - 61
Firmware/cardreader.h

@@ -1,47 +1,65 @@
 #ifndef CARDREADER_H
 #define CARDREADER_H
 
+#define SDSUPPORT
+
 #ifdef SDSUPPORT
 
-#define MAX_DIR_DEPTH 10
+#define MAX_DIR_DEPTH 6
 
 #include "SdFile.h"
-enum LsAction {LS_SerialPrint,LS_Count,LS_GetFilename};
 class CardReader
 {
 public:
   CardReader();
   
-  void initsd();
+  enum LsAction : uint8_t
+  {
+    LS_SerialPrint,
+    LS_Count,
+    LS_GetFilename,
+  };
+  struct ls_param
+  {
+    bool LFN : 1;
+    bool timestamp : 1;
+    inline ls_param():LFN(0), timestamp(0) { }
+    inline ls_param(bool LFN, bool timestamp):LFN(LFN), timestamp(timestamp) { }
+  } __attribute__((packed));
+  
+  void initsd(bool doPresort = true);
   void write_command(char *buf);
   void write_command_no_newline(char *buf);
   //files auto[0-9].g on the sd card are performed in a row
   //this is to delay autostart and hence the initialisaiton of the sd card to some seconds after the normal init, so the device is available quick after a reset
 
   void checkautostart(bool x); 
-  void openFile(const char* name,bool read,bool replace_current=true);
+  void openFileWrite(const char* name);
+  void openFileReadFilteredGcode(const char* name, bool replace_current = false);
   void openLogFile(const char* name);
   void removeFile(const char* name);
   void closefile(bool store_location=false);
   void release();
   void startFileprint();
   uint32_t getFileSize();
-  void getStatus();
+  void getStatus(bool arg_P);
   void printingHasFinished();
 
   void getfilename(uint16_t nr, const char* const match=NULL);
   void getfilename_simple(uint32_t position, const char * const match = NULL);
+  void getfilename_next(uint32_t position, const char * const match = NULL);
   uint16_t getnrfilenames();
   
   void getAbsFilename(char *t);
+  void printAbsFilenameFast();
   void getDirName(char* name, uint8_t level);
   uint16_t getWorkDirDepth();
   
 
-  void ls();
-  void chdir(const char * relpath);
+  void ls(ls_param params);
+  bool chdir(const char * relpath, bool doPresort);
   void updir();
-  void setroot();
+  void setroot(bool doPresort);
 
   #ifdef SDCARD_SORT_ALPHA
      void presort();
@@ -49,18 +67,18 @@ public:
 		void swap(uint8_t left, uint8_t right);
 		void quicksort(uint8_t left, uint8_t right);
 	 #endif //SDSORT_QUICKSORT
-     void getfilename_sorted(const uint16_t nr);
-     #if SDSORT_GCODE
-	 FORCE_INLINE void setSortOn(bool b) { sort_alpha = b; presort(); }
-     FORCE_INLINE void setSortFolders(int i) { sort_folders = i; presort(); }
-     //FORCE_INLINE void setSortReverse(bool b) { sort_reverse = b; }
-	 #endif
+     void getfilename_sorted(const uint16_t nr, uint8_t sdSort);
   #endif
 
   FORCE_INLINE bool isFileOpen() { return file.isOpen(); }
-  FORCE_INLINE bool eof() { return sdpos>=filesize ;};
-  FORCE_INLINE int16_t get() {  sdpos = file.curPosition();return (int16_t)file.read();};
-  FORCE_INLINE void setIndex(long index) {sdpos = index;file.seekSet(index);};
+  bool eof() { return sdpos>=filesize; }
+  FORCE_INLINE int16_t getFilteredGcodeChar()
+  {
+      int16_t c = (int16_t)file.readFilteredGcode();
+      sdpos = file.curPosition();
+      return c;
+  };
+  void setIndex(long index) {sdpos = index;file.seekSetFilteredGcode(index);};
   FORCE_INLINE uint8_t percentDone(){if(!isFileOpen()) return 0; if(filesize) return sdpos/((filesize+99)/100); else return 0;};
   FORCE_INLINE char* getWorkDirName(){workDir.getFilename(filename);return filename;};
   FORCE_INLINE uint32_t get_sdpos() { if (!isFileOpen()) return 0; else return(sdpos); };
@@ -78,10 +96,14 @@ public:
   // There are scenarios when simple modification time is not enough (on MS Windows)
   // Therefore these timestamps hold the most recent one of creation/modification date/times
   uint16_t crmodTime, crmodDate;
-  uint32_t cluster, position;
+  uint32_t /* cluster, */ position;
   char longFilename[LONG_FILENAME_LENGTH];
   bool filenameIsDir;
   int lastnr; //last number of the autostart;
+#ifdef SDCARD_SORT_ALPHA
+  bool presort_flag;
+  char dir_names[MAX_DIR_DEPTH][9];
+#endif // SDCARD_SORT_ALPHA
 private:
   SdFile root,*curDir,workDir,workDirParents[MAX_DIR_DEPTH];
   uint16_t workDirDepth;
@@ -89,45 +111,7 @@ private:
   // Sort files and folders alphabetically.
 #ifdef SDCARD_SORT_ALPHA
   uint16_t sort_count;        // Count of sorted items in the current directory
-  #if SDSORT_GCODE
-  bool sort_alpha;          // Flag to enable / disable the feature
-  int sort_folders;         // Flag to enable / disable folder sorting
-							//bool sort_reverse;      // Flag to enable / disable reverse sorting
-  #endif
-
-							// By default the sort index is static
-  #if SDSORT_DYNAMIC_RAM
-  uint8_t *sort_order;
-  #else
-  uint8_t sort_order[SDSORT_LIMIT];
-  #endif
-  // Cache filenames to speed up SD menus.
-  #if SDSORT_USES_RAM
-
-  // If using dynamic ram for names, allocate on the heap.
-  #if SDSORT_CACHE_NAMES
-    #if SDSORT_DYNAMIC_RAM
-      char **sortshort, **sortnames;
-    #else
-      char sortshort[SDSORT_LIMIT][FILENAME_LENGTH];
-      char sortnames[SDSORT_LIMIT][FILENAME_LENGTH];
-    #endif
-  #elif !SDSORT_USES_STACK
-    char sortnames[SDSORT_LIMIT][FILENAME_LENGTH];
-    uint16_t modification_time[SDSORT_LIMIT];
-    uint16_t modification_date[SDSORT_LIMIT];
-  #endif
-
-  // Folder sorting uses an isDir array when caching items.
-  #if HAS_FOLDER_SORTING
-    #if SDSORT_DYNAMIC_RAM
-      uint8_t *isDir;
-    #elif (SDSORT_CACHE_NAMES) || !(SDSORT_USES_STACK)
-      uint8_t isDir[(SDSORT_LIMIT + 7) >> 3];
-    #endif
-  #endif
-
-  #endif // SDSORT_USES_RAM
+  uint32_t sort_positions[SDSORT_LIMIT];
 
 #endif // SDCARD_SORT_ALPHA
 
@@ -151,12 +135,11 @@ private:
 
   bool autostart_stilltocheck; //the sd start is delayed, because otherwise the serial cannot answer fast enought to make contact with the hostsoftware.
   
-  LsAction lsAction; //stored for recursion.
-  int16_t nrFiles; //counter for the files in the current directory and recycled as position counter for getting the nrFiles'th name in the directory.
+  uint16_t nrFiles; //counter for the files in the current directory and recycled as position counter for getting the nrFiles'th name in the directory.
   char* diveDirName;
 
-  void diveSubfolder (const char *fileName, SdFile& dir);
-  void lsDive(const char *prepend, SdFile parent, const char * const match=NULL);
+  bool diveSubfolder (const char *&fileName);
+  void lsDive(const char *prepend, SdFile parent, const char * const match=NULL, LsAction lsAction = LS_GetFilename, ls_param lsParams = ls_param());
 #ifdef SDCARD_SORT_ALPHA
   void flush_presort();
 #endif

+ 34 - 32
Firmware/cmdqueue.cpp

@@ -2,8 +2,6 @@
 #include "cardreader.h"
 #include "ultralcd.h"
 
-extern bool Stopped;
-
 // Reserve BUFSIZE lines of length MAX_CMD_SIZE plus CMDBUFFER_RESERVE_FRONT.
 char cmdbuffer[BUFSIZE * (MAX_CMD_SIZE + 1) + CMDBUFFER_RESERVE_FRONT];
 // Head of the circular buffer, where to read.
@@ -18,8 +16,12 @@ int buflen = 0;
 // Therefore don't remove the command from the queue in the loop() function.
 bool cmdbuffer_front_already_processed = false;
 
+// Used for temporarely preventing accidental adding of Serial commands to the queue.
+// For now only check_file and the fancheck pause use this.
+bool cmdqueue_serial_disabled = false;
+
 int serial_count = 0;  //index of character read from serial line
-boolean comment_mode = false;
+bool comment_mode = false;
 char *strchr_pointer; // just a pointer to find chars in the command string like X, Y, Z, E, etc
 
 unsigned long TimeSent = _millis();
@@ -91,14 +93,19 @@ bool cmdqueue_pop_front()
 
 void cmdqueue_reset()
 {
-    bufindr = 0;
-    bufindw = 0;
-    buflen = 0;
+	while (buflen)
+	{
+		// printf_P(PSTR("dumping: \"%s\" of type %hu\n"), cmdbuffer+bufindr+CMDHDRSIZE, CMDBUFFER_CURRENT_TYPE);
+		ClearToSend();
+		cmdqueue_pop_front();
+	}
+	bufindr = 0;
+	bufindw = 0;
 
 	//commands are removed from command queue after process_command() function is finished
 	//reseting command queue and enqueing new commands during some (usually long running) command processing would cause that new commands are immediately removed from queue (or damaged)
 	//this will ensure that all new commands which are enqueued after cmdqueue reset, will be always executed
-    cmdbuffer_front_already_processed = true; 
+	cmdbuffer_front_already_processed = true; 
 }
 
 // How long a string could be pushed to the front of the command queue?
@@ -362,12 +369,6 @@ void repeatcommand_front()
     cmdbuffer_front_already_processed = true;
 } 
 
-bool is_buffer_empty()
-{
-    if (buflen == 0) return true;
-    else return false;
-}
-
 void proc_commands() {
 	if (buflen)
 	{
@@ -390,7 +391,7 @@ void get_command()
 	}
 
   // start of serial line processing loop
-  while ((MYSERIAL.available() > 0 && !saved_printing) || (MYSERIAL.available() > 0 && isPrintPaused)) {  //is print is saved (crash detection or filament detection), dont process data from serial line
+  while (((MYSERIAL.available() > 0 && !saved_printing) || (MYSERIAL.available() > 0 && isPrintPaused)) && !cmdqueue_serial_disabled) {  //is print is saved (crash detection or filament detection), dont process data from serial line
 	
     char serial_char = MYSERIAL.read();
 /*    if (selectedSerialPort == 1)
@@ -575,15 +576,14 @@ void get_command()
   sd_count.value = 0;
   // Reads whole lines from the SD card. Never leaves a half-filled line in the cmdbuffer.
   while( !card.eof() && !stop_buffering) {
-    int16_t n=card.get();
+    int16_t n=card.getFilteredGcodeChar();
     char serial_char = (char)n;
-    if(serial_char == '\n' ||
-       serial_char == '\r' ||
-       ((serial_char == '#' || serial_char == ':') && comment_mode == false) ||
-       serial_count >= (MAX_CMD_SIZE - 1) || n==-1)
-    {
-      if(card.eof()) break;
-
+    if( serial_char == '\n'
+     || serial_char == '\r'
+     || ((serial_char == '#' || serial_char == ':') )
+     || serial_count >= (MAX_CMD_SIZE - 1)
+     || n==-1
+    ){
       if(serial_char=='#')
         stop_buffering=true;
 
@@ -594,12 +594,11 @@ void get_command()
         // read from the sdcard into sd_count, 
         // so that the lenght of the already read empty lines and comments will be added
         // to the following non-empty line. 
-        comment_mode = false;
-        continue; //if empty line
+        return; // prevent cycling indefinitely - let manage_heaters do their job
       }
       // The new command buffer could be updated non-atomically, because it is not yet considered
       // to be inside the active queue.
-      sd_count.value = (card.get_sdpos()+1) - sdpos_atomic;
+      sd_count.value = card.get_sdpos() - sdpos_atomic;
       cmdbuffer[bufindw] = CMDBUFFER_CURRENT_TYPE_SDCARD;
       cmdbuffer[bufindw+1] = sd_count.lohi.lo;
       cmdbuffer[bufindw+2] = sd_count.lohi.hi;
@@ -611,10 +610,10 @@ void get_command()
 //      MYSERIAL.print(sd_count.value, DEC);
 //      SERIAL_ECHOPGM(") ");
 //      SERIAL_ECHOLN(cmdbuffer+bufindw+CMDHDRSIZE);
-//    SERIAL_ECHOPGM("cmdbuffer:");
-//    MYSERIAL.print(cmdbuffer);
-//    SERIAL_ECHOPGM("buflen:");
-//    MYSERIAL.print(buflen+1);
+//      SERIAL_ECHOPGM("cmdbuffer:");
+//      MYSERIAL.print(cmdbuffer);
+//      SERIAL_ECHOPGM("buflen:");
+//      MYSERIAL.print(buflen+1);
       sd_count.value = 0;
 
       cli();
@@ -624,21 +623,24 @@ void get_command()
       // or a 115200 Bd serial line receive interrupt, which will not trigger faster than 12kHz.
       ++ buflen;
       bufindw += len;
-      sdpos_atomic = card.get_sdpos()+1;
+      sdpos_atomic = card.get_sdpos();
       if (bufindw == sizeof(cmdbuffer))
           bufindw = 0;
       sei();
 
       comment_mode = false; //for new command
       serial_count = 0; //clear buffer
+    
+      if(card.eof()) break;
+
       // The following line will reserve buffer space if available.
       if (! cmdqueue_could_enqueue_back(MAX_CMD_SIZE-1, true))
           return;
     }
     else
     {
-      if(serial_char == ';') comment_mode = true;
-      else if(!comment_mode) cmdbuffer[bufindw+CMDHDRSIZE+serial_count++] = serial_char;
+        // there are no comments coming from the filtered file
+        cmdbuffer[bufindw+CMDHDRSIZE+serial_count++] = serial_char;
     }
   }
   if(card.eof())

+ 5 - 5
Firmware/cmdqueue.h

@@ -35,6 +35,7 @@ extern char cmdbuffer[BUFSIZE * (MAX_CMD_SIZE + 1) + CMDBUFFER_RESERVE_FRONT];
 extern size_t bufindr;
 extern int buflen;
 extern bool cmdbuffer_front_already_processed;
+extern bool cmdqueue_serial_disabled;
 
 // Type of a command, which is to be executed right now.
 #define CMDBUFFER_CURRENT_TYPE   (cmdbuffer[bufindr])
@@ -48,7 +49,7 @@ extern bool cmdbuffer_front_already_processed;
 extern uint32_t sdpos_atomic;
 
 extern int serial_count;
-extern boolean comment_mode;
+extern bool comment_mode;
 extern char *strchr_pointer;
 
 extern unsigned long TimeSent;
@@ -65,16 +66,15 @@ extern void cmdqueue_dump_to_serial_single_line(int nr, const char *p);
 extern void cmdqueue_dump_to_serial();
 #endif /* CMDBUFFER_DEBUG */
 extern bool cmd_buffer_empty();
-extern void enquecommand(const char *cmd, bool from_progmem);
-extern void enquecommand_front(const char *cmd, bool from_progmem);
+extern void enquecommand(const char *cmd, bool from_progmem = false);
+extern void enquecommand_front(const char *cmd, bool from_progmem = false);
 extern void repeatcommand_front();
-extern bool is_buffer_empty();
 extern void get_command();
 extern uint16_t cmdqueue_calc_sd_length();
 
 // Return True if a character was found
 static inline bool    code_seen(char code) { return (strchr_pointer = strchr(CMDBUFFER_CURRENT_STRING, code)) != NULL; }
-static inline bool    code_seen(const char *code) { return (strchr_pointer = strstr(CMDBUFFER_CURRENT_STRING, code)) != NULL; }
+static inline bool    code_seen_P(const char *code_PROGMEM) { return (strchr_pointer = strstr_P(CMDBUFFER_CURRENT_STRING, code_PROGMEM)) != NULL; }
 static inline float   code_value()      { return strtod(strchr_pointer+1, NULL);}
 static inline long    code_value_long()    { return strtol(strchr_pointer+1, NULL, 10); }
 static inline int16_t code_value_short()   { return int16_t(strtol(strchr_pointer+1, NULL, 10)); };

+ 35 - 10
Firmware/config.h

@@ -23,7 +23,6 @@
 #define ADC_CALLBACK      adc_ready //callback function ()
 
 //SWI2C configuration
-#define SWI2C
 //#define SWI2C_SDA         20 //SDA on P3
 //#define SWI2C_SCL         21 //SCL on P3
 #define SWI2C_A8
@@ -31,7 +30,13 @@
 #define SWI2C_TMO         2048 //2048 cycles timeout
 
 //PAT9125 configuration
-#define PAT9125_SWI2C
+//#define PAT9125_SWSPI // software SPI mode (incomplete)
+#ifdef SWI2C_SCL
+#define PAT9125_SWI2C   // software I2C mode
+#else
+#define PAT9125_I2C     // hardware I2C mode
+#endif
+
 #define PAT9125_I2C_ADDR  0x75  //ID=LO
 //#define PAT9125_I2C_ADDR  0x79  //ID=HI
 //#define PAT9125_I2C_ADDR  0x73  //ID=NC
@@ -49,19 +54,39 @@
 #define TMC2130_SPCR           SPI_SPCR(TMC2130_SPI_RATE, 1, 1, 1, 0)
 #define TMC2130_SPSR           SPI_SPSR(TMC2130_SPI_RATE)
 
-//W25X20CL configuration
-//pinout:
-#define W25X20CL_PIN_CS        32
-//spi:
-#define W25X20CL_SPI_RATE      0 // fosc/4 = 4MHz
-#define W25X20CL_SPCR          SPI_SPCR(W25X20CL_SPI_RATE, 1, 1, 1, 0)
-#define W25X20CL_SPSR          SPI_SPSR(W25X20CL_SPI_RATE)
-
 //LANG - Multi-language support
 //#define LANG_MODE              0 // primary language only
 #define LANG_MODE              1 // sec. language support
 
 #define LANG_SIZE_RESERVED     0x3000 // reserved space for secondary language (12288 bytes)
 
+//Community language support
+#define COMMUNITY_LANG_NL // Community Dutch language
+//#define COMMUNITY_LANG_QR // Community new language //..use this as a template and replace 'QR'
+
+#if defined(COMMUNITY_LANG_NL) //|| defined(COMMUNITY_LANG_QR) //..use last part as a template and replace 'QR'
+#define COMMUNITY_LANG_SUPPORT
+#endif
+
+// Sanity checks for correct configuration of XFLASH_DUMP options
+#if defined(XFLASH_DUMP) && !defined(XFLASH)
+#error "XFLASH_DUMP requires XFLASH support"
+#endif
+#if (defined(MENU_DUMP) || defined(EMERGENCY_DUMP)) && !defined(XFLASH_DUMP)
+#error "MENU_DUMP and EMERGENCY_DUMP require XFLASH_DUMP"
+#endif
+
+// Support for serial dumps is mutually exclusive with XFLASH_DUMP features
+#if defined(EMERGENCY_DUMP) && defined(EMERGENCY_SERIAL_DUMP)
+#error "EMERGENCY_DUMP and EMERGENCY_SERIAL_DUMP are mutually exclusive"
+#endif
+#if defined(MENU_DUMP) && defined(MENU_SERIAL_DUMP)
+#error "MENU_DUMP and MENU_SERIAL_DUMP are mutually exclusive"
+#endif
+
+// Reduce internal duplication
+#if defined(EMERGENCY_DUMP) || defined(EMERGENCY_SERIAL_DUMP)
+#define EMERGENCY_HANDLERS
+#endif
 
 #endif //_CONFIG_H

+ 16 - 34
Firmware/eeprom.cpp

@@ -93,6 +93,13 @@ void eeprom_init()
         eeprom_switch_to_next_sheet();
     }
     check_babystep();
+
+#ifdef PINDA_TEMP_COMP
+if (eeprom_read_byte((uint8_t*)EEPROM_PINDA_TEMP_COMPENSATION) == 0xff) eeprom_update_byte((uint8_t *)EEPROM_PINDA_TEMP_COMPENSATION, 0);
+#endif //PINDA_TEMP_COMP
+
+	if (eeprom_read_dword((uint32_t*)EEPROM_JOB_ID) == EEPROM_EMPTY_VALUE32)
+		eeprom_update_dword((uint32_t*)EEPROM_JOB_ID, 0);
 }
 
 //! @brief Get default sheet name for index
@@ -103,10 +110,10 @@ void eeprom_init()
 //! | 1     | Smooth2   |
 //! | 2     | Textur1   |
 //! | 3     | Textur2   |
-//! | 4     | Custom1   |
-//! | 5     | Custom2   |
-//! | 6     | Custom3   |
-//! | 7     | Custom4   |
+//! | 4     | Satin 1   |
+//! | 5     | Satin 2   |
+//! | 6     | Custom1   |
+//! | 7     | Custom2   |
 //!
 //! @param[in] index
 //! @param[out] sheetName
@@ -122,41 +129,16 @@ void eeprom_default_sheet_name(uint8_t index, SheetName &sheetName)
     {
         strcpy_P(sheetName.c, PSTR("Textur"));
     }
-    else
+    else if (index < 6)
     {
-        strcpy_P(sheetName.c, PSTR("Custom"));
+        strcpy_P(sheetName.c, PSTR("Satin "));
     }
-
-    switch (index)
+    else
     {
-    case 0:
-        sheetName.c[6] = '1';
-        break;
-    case 1:
-        sheetName.c[6] = '2';
-        break;
-    case 2:
-        sheetName.c[6] = '1';
-        break;
-    case 3:
-        sheetName.c[6] = '2';
-        break;
-    case 4:
-        sheetName.c[6] = '1';
-        break;
-    case 5:
-        sheetName.c[6] = '2';
-        break;
-    case 6:
-        sheetName.c[6] = '3';
-        break;
-    case 7:
-        sheetName.c[6] = '4';
-        break;
-    default:
-        break;
+        strcpy_P(sheetName.c, PSTR("Custom"));
     }
 
+    sheetName.c[6] = '0' + ((index % 2)+1);
     sheetName.c[7] = '\0';
 }
 

+ 43 - 64
Firmware/eeprom.h

@@ -53,6 +53,7 @@ static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEP
    - __L__		Language
    - __S__ 		Statistics
    - __P__ 		Shipping prep
+   - __M__ 		Service/Maintenance prep
    - __S/P__	Statistics and Shipping prep
    
   will overwrite existing values to 0 or default.
@@ -98,19 +99,9 @@ static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEP
 | 0x0FF1h 4081		| uint32	| EEPROM_FILAMENTUSED					| ???			| 00 00 00 00h 0 __S/P__| Filament used in meters							| ??? 			| D3 Ax0ff1 C4
 | 0x0FEDh 4077		| uint32	| EEPROM_TOTALTIME						| ???			| 00 00 00 00h 0 __S/P__| Total print time									| ??? 			| D3 Ax0fed C4
 | 0x0FE5h 4069		| float		| EEPROM_BED_CALIBRATION_CENTER			| ???			| ff ff ff ffh			| ???											 	| ??? 			| D3 Ax0fe5 C8
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
 | 0x0FDDh 4061		| float		| EEPROM_BED_CALIBRATION_VEC_X			| ???			| ff ff ff ffh			| ???											 	| ??? 			| D3 Ax0fdd C8
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^	
 | 0x0FD5h 4053		| float		| EEPROM_BED_CALIBRATION_VEC_Y			| ???			| ff ff ff ffh			| ???											 	| ??? 			| D3 Ax0fd5 C8
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
 | 0x0FC5h 4037		| int16		| EEPROM_BED_CALIBRATION_Z_JITTER		| ???			| ff ffh 65535			| ???											 	| ??? 			| D3 Ax0fc5 C16
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^	
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
 | 0x0FC4h 4036		| bool		| EEPROM_FARM_MODE						| 00h 0			| ffh 255 		__P__	| Prusa farm mode: __off__							| G99 			| D3 Ax0fc4 C1
 | ^					| ^			| ^										| 01h 1			| ^						| Prusa farm mode: __on__							| G98			| ^
 | 0x0FC3h 4035		| free		| _EEPROM_FREE_NR1_						| ???			| ffh 255				| _Free EEPROM space_								| _free space_	| D3 Ax0fc3 C1
@@ -129,10 +120,6 @@ static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEP
 | ^					| ^			| ^										| 01h 1			| ^						| Toshiba Air: __on__								| ^ 			| ^	
 | 0x0FBAh 4026		| uchar		| EEPROM_PRINT_FLAG						| ???			| ???					| _unsued_											| ??? 			| D3 Ax0fba C1
 | 0x0FB0h 4016		| int16		| EEPROM_PROBE_TEMP_SHIFT				| ???			| ???					| ???												| ??? 			| D3 Ax0fb0 C10
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
 | 0x0FAFh 4015		| bool		| EEPROM_TEMP_CAL_ACTIVE				| 00h 0			| 00h 0					| PINDA Temp cal.: __inactive__						| LCD menu		| D3 Ax0faf C1
 | ^					| ^			| ^										| ffh 255		| ^						| PINDA Temp cal.: __active__						| ^ 			| ^
 | 0x0FA7h 4007		| uint32	| EEPROM_BOWDEN_LENGTH					| ???			| ff 00 00 00h			| Bowden length										| ??? 			| D3 Ax0fae C8
@@ -143,16 +130,8 @@ static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEP
 | ^					| ^			| ^										| 01h 1			| ^						| Power Panic flag: __active__						| ^ 			| ^
 | ^					| ^			| ^										| 02h 2			| ^						| Power Panic flag: __???__							| ^ 			| ^
 | 0x0F9Dh 3997		| float		| EEPROM_UVLO_CURRENT_POSITION			| ???			| ffh 255				| Power Panic position 								| ??? 			| D3 Ax0f9d C8
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
 | 0x0F95h 3989		| char		| EEPROM_FILENAME						| ???			| ffh 255				| Power Panic Filename 								| ??? 			| D3 Ax0f95 C8
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| 0x0F91h 39851		| uint32	| EEPROM_FILE_POSITION					| ???			| ff ff ff ffh			| Power Panic File Position							| ??? 			| D3 Ax0f91 C4
+| 0x0F91h 3985		| uint32	| EEPROM_FILE_POSITION					| ???			| ff ff ff ffh			| Power Panic File Position							| ??? 			| D3 Ax0f91 C4
 | 0x0F8Dh 3981		| float		| EEPROM_UVLO_CURRENT_POSITION_Z		| ???			| ff ff ff ffh			| Power Panic Z Position	 						| ^ 			| D3 Ax0f8d C4
 | 0x0F8Ch 3980		| ???		| EEPROM_UVLO_UNUSED_001				| ??? 			| ffh 255				| Power Panic _unused_								| ^ 			| D3 Ax0f8c C1
 | 0x0F8Bh 3979		| uint8		| EEPROM_UVLO_TARGET_BED				| ???			| ffh 255				| Power Panic Bed temperature						| ^ 			| D3 Ax0f8b C1
@@ -161,14 +140,6 @@ static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEP
 | 0x0F87h 3975		| uint8		| EEPROM_FAN_CHECK_ENABLED				| 00h 0			| ???					| Fan Check __disabled__							| LCD menu		| D3 Ax0f87 C1
 | ^					| ^			| ^										| 01h 1			| ffh 255				| Fan Check __enabled__ 							| ^ 			| ^
 | 0x0F75h 3957		| uint16	| EEPROM_UVLO_MESH_BED_LEVELING			| ???			| ff ffh 65535			| Power Panic Mesh Bed Leveling						| ???			| D3 Ax0f75 C18 
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
-| ^					| ^			| ^										| ^				| ^						| ^													| ^ 			| ^
 | 0x0F73h 3955		| uint16	| EEPROM_UVLO_Z_MICROSTEPS				| ???			| ff ffh 65535			| Power Panic Z microsteps							| ???			| D3 Ax0f73 C2 
 | 0x0F72h 3954		| uint8		| EEPROM_UVLO_E_ABS						| ???			| ffh 255				| Power Panic ??? position							| ???			| D3 Ax0f72 C1
 | 0x0F6Eh 3950		| foat		| EEPROM_UVLO_CURRENT_POSITION_E		| ???			| ff ff ff ffh			| Power Panic E position							| ???			| D3 Ax0f6e C4
@@ -187,6 +158,7 @@ static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEP
 | 0x0F60h 3936		| float		| EEPROM_XYZ_CAL_SKEW					| ???			| ff ff ff ffh			| XYZ skew value									| ???			| D3 Ax0f60 C4
 | 0x0F5Fh 3935		| uint8		| EEPROM_WIZARD_ACTIVE					| 01h 1			| 01h 1			__P__	| Wizard __active__									| ???			| D3 Ax0f5f C1
 | ^					| ^			| ^										| 00h 0			| ^						| Wizard __inactive__								| ^ 			| ^
+| ^					| ^			| ^										| 02h 2			| 02h 2			__M__	| Wizard active - Z cal after shipping/service prep | ^ 			| ^
 | 0x0F5Dh 3933		| uint16	| EEPROM_BELTSTATUS_X					| ???			| ff ffh				| X Beltstatus 										| ???			| D3 Ax0f5d C2
 | 0x0F5Bh 3931		| uint16	| EEPROM_BELTSTATUS_Y					| ???			| ff ffh				| Y Beltstatus 										| ???			| D3 Ax0f5b C2
 | 0x0F5Ah 3930		| uint8		| EEPROM_DIR_DEPTH						| 00h-ffh 0-255	| ffh 255				| Directory depth									| ???			| D3 Ax0f5a C1
@@ -203,7 +175,7 @@ static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEP
 | 0x0F01h 3841		| uint16	| EEPROM_FERROR_COUNT_TOT				| 0000-fffe		| ff ffh		__S/P__	| Total filament sensor errors 						| ???			| D3 Ax0f01 C2
 | 0x0EFFh 3839		| uint16	| EEPROM_POWER_COUNT_TOT				| 0000-fffe		| ff ffh		__S/P__	| Total power failures		  						| ???			| D3 Ax0eff C2
 | 0x0EFEh 3838		| uint8		| EEPROM_TMC2130_HOME_X_ORIGIN			| ???			| ffh 255				| ???						  						| ???			| D3 Ax0efe C1
-| 0x0EFDh 3837		| uint8		| EEPROM	MC2130_HOME_X_BSTEPS			| ???			| ffh 255			| ???						  						| ???			| D3 Ax0efd C1
+| 0x0EFDh 3837		| uint8		| EEPROM	MC2130_HOME_X_BSTEPS		| ???			| ffh 255		    	| ???						  						| ???			| D3 Ax0efd C1
 | 0x0EFCh 3836		| uint8		| EEPROM_TMC2130_HOME_X_FSTEPS			| ???			| ffh 255				| ???						  						| ???			| D3 Ax0efc C1
 | 0x0EFBh 3835		| uint8		| EEPROM_TMC2130_HOME_Y_ORIGIN			| ???			| ffh 255				| ???						  						| ???			| D3 Ax0efb C1
 | 0x0EFAh 3834		| uint8		| EEPROM_TMC2130_HOME_Y_BSTEPS			| ???			| ffh 255				| ???						  						| ???			| D3 Ax0efa C1
@@ -257,28 +229,6 @@ static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEP
 | ^					| ^			| ^										| 01h 1			| ^						| MMU2/s cutter: __enabled__						| ^				| ^
 | ^					| ^			| ^										| 02h 2			| ^						| MMU2/s cutter: __always__							| ^				| ^
 | 0x0DAE 3502		| uint16	| EEPROM_UVLO_MESH_BED_LEVELING_FULL	| ???			| ff ffh 65535			| Power panic Mesh bed leveling points 				| ???			| D3 Ax0dae C288
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
-| ^					| ^			| ^										| ???			| ^						| ^													| ^				| ^
 | 0x0DAD 3501		| uint8		| EEPROM_MBL_TYPE						| ???			| ffh 255				| Mesh bed leveling precision 		_unused atm_	| ???			| D3 Ax0dad C1
 | 0x0DAC 3500		| bool		| EEPROM_MBL_MAGNET_ELIMINATION			| 01h 1			| ffh 255				| Mesh bed leveling does: __ignores__ magnets		| LCD menu		| D3 Ax0dac C1
 | ^					| ^			| ^										| 00h 0			| ^						| Mesh bed leveling does: __NOT ignores__ magnets	| ^				| ^
@@ -294,9 +244,11 @@ static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEP
 | ^					| ^			| ^										| 00h 0			| ^						| Check mode for nozzle is: __none__				| ^				| ^
 | 0x0DA7 3495		| uint8		| EEPROM_NOZZLE_DIAMETER				| 28h 40		| ffh 255				| Nozzle diameter is: __40 or 0.40mm__				| LCD menu		| D3 Ax0da7 C1
 | ^					| ^			| ^										| 3ch 60		| ^						| Nozzle diameter is: __60 or 0.60mm__				| ^				| ^
+| ^					| ^			| ^										| 3ch 80		| ^						| Nozzle diameter is: __80 or 0.80mm__				| ^				| ^
 | ^					| ^			| ^										| 19h 25		| ^						| Nozzle diameter is: __25 or 0.25mm__				| ^				| ^
 | 0x0DA5 3493		| uint16	| EEPROM_NOZZLE_DIAMETER_uM				| 9001h			| ff ffh 65535			| Nozzle diameter is: __400um__						| LCD menu		| D3 Ax0da5 C2
 | ^					| ^			| ^										| 5802h			| ^						| Nozzle diameter is: __600um__						| ^				| ^
+| ^					| ^			| ^										| 2003h			| ^						| Nozzle diameter is: __800um__						| ^				| ^
 | ^					| ^			| ^										| fa00h			| ^						| Nozzle diameter is: __250um__						| ^				| ^
 | 0x0DA4 3492		| uint8		| EEPROM_CHECK_MODEL					| 01h 1			| ffh 255				| Check mode for printer model is: __warn__			| LCD menu		| D3 Ax0da4 C1
 | ^					| ^			| ^										| 02h 0			| ^						| Check mode for printer model is: __strict__		| ^				| ^
@@ -324,19 +276,19 @@ static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEP
 | 0x0D71 3441		| uint16	| ^										| 00 00h 0		| ff ffh 65535			| 4th sheet - Z offset 								| ^				| D3 Ax0d71 C2	
 | 0x0D73 3443		| uint8		| ^										| 00h 0			| ffh 255				| 4th sheet - bed temp 								| ^				| D3 Ax0d73 C1	
 | 0x0D74 3444		| uint8		| ^										| 00h 0			| ffh 255				| 4th sheet - PINDA temp 							| ^				| D3 Ax0d74 C1	
-| 0x0D75 3445		| char		| _5th Sheet block_						| 437573746f6d31| ffffffffffffff		| 5th sheet - Name: 	_Custom1_					| ^				| D3 Ax0d75 C7
+| 0x0D75 3445		| char		| _5th Sheet block_						| 536174696e2031| ffffffffffffff		| 5th sheet - Name: 	_Satin 1_					| ^				| D3 Ax0d75 C7
 | 0x0D7C 3452		| uint16	| ^										| 00 00h 0		| ff ffh 65535			| 5th sheet - Z offset 								| ^				| D3 Ax0d7c C2	
 | 0x0D7E 3454		| uint8		| ^										| 00h 0			| ffh 255				| 5th sheet - bed temp 								| ^				| D3 Ax0d7e C1	
 | 0x0D7F 3455		| uint8		| ^										| 00h 0			| ffh 255				| 5th sheet - PINDA temp 							| ^				| D3 Ax0d7f C1	
-| 0x0D80 3456		| char		| _6th Sheet block_						| 437573746f6d32| ffffffffffffff		| 6th sheet - Name: 	_Custom2_					| ^				| D3 Ax0d80 C7
+| 0x0D80 3456		| char		| _6th Sheet block_						| 536174696e2032| ffffffffffffff		| 6th sheet - Name: 	_Satin 2_					| ^				| D3 Ax0d80 C7
 | 0x0D87 3463		| uint16	| ^										| 00 00h 0		| ff ffh 65535			| 6th sheet - Z offset 								| ^				| D3 Ax0d87 C2	
 | 0x0D89 3465		| uint8		| ^										| 00h 0			| ffh 255				| 6th sheet - bed temp 								| ^				| D3 Ax0d89 C1	
 | 0x0D8A 3466		| uint8		| ^										| 00h 0			| ffh 255				| 6th sheet - PINDA temp 							| ^				| D3 Ax0d8a C1	
-| 0x0D8B 3467		| char		| _7th Sheet block_						| 437573746f6d33| ffffffffffffff		| 7th sheet - Name: 	_Custom3_					| ^				| D3 Ax0d8b C7
+| 0x0D8B 3467		| char		| _7th Sheet block_						| 437573746f6d31| ffffffffffffff		| 7th sheet - Name: 	_Custom1_					| ^				| D3 Ax0d8b C7
 | 0x0D92 3474		| uint16	| ^										| 00 00h 0		| ff ffh 65535			| 7th sheet - Z offset 								| ^				| D3 Ax0d92 C2	
 | 0x0D94 3476		| uint8		| ^										| 00h 0			| ffh 255				| 7th sheet - bed temp 								| ^				| D3 Ax0d94 C1	
 | 0x0D95 3477		| uint8		| ^										| 00h 0			| ffh 255				| 7th sheet - PINDA temp 							| ^				| D3 Ax0d95 C1	
-| 0x0D96 3478		| char		| _8th Sheet block_						| 437573746f6d34| ffffffffffffff		| 8th sheet - Name: 	_Custom4_					| ^				| D3 Ax0d96 C7
+| 0x0D96 3478		| char		| _8th Sheet block_						| 437573746f6d32| ffffffffffffff		| 8th sheet - Name: 	_Custom2_					| ^				| D3 Ax0d96 C7
 | 0x0D9D 3485		| uint16	| ^										| 00 00h 0		| ff ffh 65535			| 8th sheet - Z offset 								| ^				| D3 Ax0d9d C2	
 | 0x0D9F 3487		| uint8		| ^										| 00h 0			| ffh 255				| 8th sheet - bed temp 								| ^				| D3 Ax0d9f C1	
 | 0x0DA0 3488		| uint8		| ^										| 00h 0			| ffh 255				| 8th sheet - PINDA temp 							| ^				| D3 Ax0da0 C1	
@@ -359,10 +311,24 @@ static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEP
 | ^					| ^			| ^										| 00h 0			| ^						| LCD backlight mode: __Dim__						| ^				| ^
 | 0x0D30 3376		| uint16	| EEPROM_BACKLIGHT_TIMEOUT				| 01 00 - ff ff | 0a 00h 65535			| LCD backlight timeout: __10__ seconds				| LCD menu		| D3 Ax0d30 C2
 | 0x0D2C 3372		| float		| EEPROM_UVLO_LA_K						| ???			| ff ff ff ffh			| Power panic saved Linear Advanced K value			| ???			| D3 Ax0d2c C4
-| 0x0D2B 3371		| uint8		| EEPROM_ALTFAN_OVERRIDE				| 0-1			| 00h 0					| ALTFAN override									| LCD menu		| D3 Ax0d2b C1
-| 0x0D2A 3370		| uint8		| EEPROM_EXPERIMENTAL_VISIBILITY		| 0-1			| 00h 0					| Experimental menu visibility						| LCD menu		| D3 Ax0d2a C1
+| 0x0D2B 3371		| uint8		| EEPROM_ALTFAN_OVERRIDE				| ffh 255		| ffh 255				| ALTFAN override unknown state						| LCD menu		| D3 Ax0d2b C1
+| ^					| ^			| ^										| 00h 0			| ^						| ALTFAN override deactivated						| ^				| ^
+| ^					| ^			| ^										| 01h 1			| ^						| ALTFAN override activated 						| ^				| ^
+| 0x0D2A 3370		| uint8		| EEPROM_EXPERIMENTAL_VISIBILITY		| ffh 255		| ffh 255				| Experimental menu visibility unknown state		| LCD menu		| D3 Ax0d2a C1
+| ^					| ^			| ^										| 00h 0			| ^						| Experimental menu visibility hidden				| ^				| ^
+| ^					| ^			| ^										| 01h 1			| ^						| Experimental menu visibility visible				| ^				| ^
+| 0x0D29 3369		| uint8		| EEPROM_PINDA_TEMP_COMPENSATION   		| ffh 255		| ffh 255				| PINDA temp compensation unknown state	            | LCD menu		| D3 Ax0d29 C1
+| ^					| ^			| ^										| 00h 0			| ^						| PINDA has no temp compensation PINDA v1/2    		| ^				| ^
+| ^					| ^			| ^										| 01h 1			| ^						| PINDA has temp compensation aka SuperPINDA       	| ^				| ^
+| 0x0D15 3349		| char[20]	| EEPROM_PRUSA_SN						| SN[19] == 0	| ffffffffffffffff...	| PRUSA Serial number string						| PRUSA SN		| D3 Ax0d15 C20
+| 0x0D11 3345		| float		| EEPROM_UVLO_ACCELL                	| ???			| ff ff ff ffh			| Power panic saved normal acceleration     		| ???			| D3 Ax0d11 C4
+| 0x0D0D 3341		| float		| EEPROM_UVLO_RETRACT_ACCELL			| ???			| ff ff ff ffh			| Power panic saved retract acceleration     		| ???			| D3 Ax0d0d C4
+| 0x0D09 3337		| float		| EEPROM_UVLO_TRAVEL_ACCELL				| ???			| ff ff ff ffh			| Power panic saved travel acceleration     		| ???			| D3 Ax0d09 C4
+| 0x0D05 3333		| uint32_t	| EEPROM_JOB_ID							| ???			| 00 00 00 00h			| Job ID used by host software						| D3 only		| D3 Ax0d05 C4
+| 0x0D04 3332		| uint8_t	| EEPROM_ECOOL_ENABLE					| ffh 255		| ^						| Disable extruder motor scaling for non-farm print	| LCD menu		| D3 Ax0d04 C1
+| ^					| ^			| ^										| 2ah 42		| ^						| Enable extruder motor scaling for non-farm print	| ^				| D3 Ax0d04 C1
+| 0x0D03 3321		| uint8_t	| EEPROM_FW_CRASH_FLAG					| 01h 1			| ff/00					| Last FW crash reason (dump_crash_reason)			| D21/D22		| D3 Ax0d03 C1
 
-  
 | Address begin		| Bit/Type 	| Name 									| Valid values	| Default/FactoryReset	| Description 										| Gcode/Function| Debug code
 | :--:				| :--: 		| :--: 									| :--:			| :--:					| :--:												| :--:			| :--:
 | 0x0012 18			| uint16	| EEPROM_FIRMWARE_VERSION_END			| ???			| ff ffh 65535			| ???												| ???			| D3 Ax0012 C2
@@ -375,6 +341,7 @@ static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEP
 
 #define EEPROM_EMPTY_VALUE 0xFF
 #define EEPROM_EMPTY_VALUE16 0xFFFF
+#define EEPROM_EMPTY_VALUE32 0xFFFFFFFFl
 // The total size of the EEPROM is
 // 4096 for the Atmega2560
 #define EEPROM_TOP 4096
@@ -448,7 +415,7 @@ static_assert(sizeof(Sheets) == EEPROM_SHEETS_SIZEOF, "Sizeof(Sheets) is not EEP
 #define EEPROM_POWER_COUNT       (EEPROM_FERROR_COUNT - 1)                      // uint8 (orig EEPROM_UVLO_MESH_BED_LEVELING-17)
 
 #define EEPROM_XYZ_CAL_SKEW (EEPROM_POWER_COUNT - 4)                            // float for skew backup
-#define EEPROM_WIZARD_ACTIVE (EEPROM_XYZ_CAL_SKEW - 1)
+#define EEPROM_WIZARD_ACTIVE (EEPROM_XYZ_CAL_SKEW - 1)                          // 0: wizard not active, 1: wizard active, 2: wizard active without yes/no = forced calibrate Z after shipping/service prep.
 #define EEPROM_BELTSTATUS_X (EEPROM_WIZARD_ACTIVE - 2)                          // uint16
 #define EEPROM_BELTSTATUS_Y (EEPROM_BELTSTATUS_X - 2)                           // uint16
 
@@ -565,9 +532,20 @@ static Sheets * const EEPROM_Sheets_base = (Sheets*)(EEPROM_SHEETS_BASE);
 
 #define EEPROM_ALTFAN_OVERRIDE (EEPROM_UVLO_LA_K-1) //uint8
 #define EEPROM_EXPERIMENTAL_VISIBILITY (EEPROM_ALTFAN_OVERRIDE-1) //uint8
+#define EEPROM_PINDA_TEMP_COMPENSATION (EEPROM_EXPERIMENTAL_VISIBILITY-1) //uint8
+#define EEPROM_PRUSA_SN (EEPROM_PINDA_TEMP_COMPENSATION-20) //char[20]
+
+#define EEPROM_UVLO_ACCELL (EEPROM_PRUSA_SN-4) // float
+#define EEPROM_UVLO_RETRACT_ACCELL (EEPROM_UVLO_ACCELL-4) // float
+#define EEPROM_UVLO_TRAVEL_ACCELL (EEPROM_UVLO_RETRACT_ACCELL-4) // float
+
+#define EEPROM_JOB_ID (EEPROM_UVLO_TRAVEL_ACCELL-4) //uint32_t
+
+#define EEPROM_ECOOL_ENABLE (EEPROM_JOB_ID-1) // uint8_t
+#define EEPROM_FW_CRASH_FLAG (EEPROM_ECOOL_ENABLE-1) // uint8_t
 
 //This is supposed to point to last item to allow EEPROM overrun check. Please update when adding new items.
-#define EEPROM_LAST_ITEM EEPROM_EXPERIMENTAL_VISIBILITY
+#define EEPROM_LAST_ITEM EEPROM_FW_CRASH_FLAG
 // !!!!!
 // !!!!! this is end of EEPROM section ... all updates MUST BE inserted before this mark !!!!!
 // !!!!!
@@ -583,11 +561,12 @@ static Sheets * const EEPROM_Sheets_base = (Sheets*)(EEPROM_SHEETS_BASE);
 #define EEPROM_FIRMWARE_VERSION_MAJOR     FW_PRUSA3D_MAGIC_LEN
 // Magic string, indicating that the current or the previous firmware running was the Prusa3D firmware.
 #define EEPROM_FIRMWARE_PRUSA_MAGIC 0
+#define EEPROM_ECOOL_MAGIC_NUMBER 42
 
 #ifdef __cplusplus
 #include "ConfigurationStore.h"
 static_assert(EEPROM_FIRMWARE_VERSION_END < 20, "Firmware version EEPROM address conflicts with EEPROM_M500_base");
-static constexpr M500_conf * const EEPROM_M500_base = reinterpret_cast<M500_conf*>(20); //offset for storing settings using M500
+static M500_conf * const EEPROM_M500_base = reinterpret_cast<M500_conf*>(20); //offset for storing settings using M500
 static_assert(((sizeof(M500_conf) + 20) < EEPROM_LAST_ITEM), "M500_conf address space conflicts with previous items.");
 #endif
 

+ 11 - 18
Firmware/fastio.h

@@ -7,15 +7,8 @@
 #define	_FASTIO_ARDUINO_H
 
 #include <avr/io.h>
+#include "macros.h"
 
-/*
-  utility functions
-*/
-
-#ifndef MASK
-/// MASKING- returns \f$2^PIN\f$
-#define MASK(PIN)  (1 << PIN)
-#endif
 
 /*
   magic I/O routines
@@ -23,20 +16,20 @@
 */
 
 /// Read a pin
-#define _READ(IO) ((bool)(DIO ## IO ## _RPORT & MASK(DIO ## IO ## _PIN)))
+#define _READ(IO) ((bool)(DIO ## IO ## _RPORT & _BV(DIO ## IO ## _PIN)))
 /// write to a pin
 // On some boards pins > 0x100 are used. These are not converted to atomic actions. An critical section is needed.
 
-#define _WRITE_NC(IO, v)  do { if (v) {DIO ##  IO ## _WPORT |= MASK(DIO ## IO ## _PIN); } else {DIO ##  IO ## _WPORT &= ~MASK(DIO ## IO ## _PIN); }; } while (0)
+#define _WRITE_NC(IO, v)  do { if (v) {DIO ##  IO ## _WPORT |= _BV(DIO ## IO ## _PIN); } else {DIO ##  IO ## _WPORT &= ~_BV(DIO ## IO ## _PIN); }; } while (0)
 
 #define _WRITE_C(IO, v)   do { if (v) { \
                                          CRITICAL_SECTION_START; \
-                                         {DIO ##  IO ## _WPORT |= MASK(DIO ## IO ## _PIN); }\
+                                         {DIO ##  IO ## _WPORT |= _BV(DIO ## IO ## _PIN); }\
                                          CRITICAL_SECTION_END; \
                                        }\
                                        else {\
                                          CRITICAL_SECTION_START; \
-                                         {DIO ##  IO ## _WPORT &= ~MASK(DIO ## IO ## _PIN); }\
+                                         {DIO ##  IO ## _WPORT &= ~_BV(DIO ## IO ## _PIN); }\
                                          CRITICAL_SECTION_END; \
                                        }\
                                      }\
@@ -45,20 +38,20 @@
 #define _WRITE(IO, v)  do {  if (&(DIO ##  IO ## _RPORT) >= (uint8_t *)0x100) {_WRITE_C(IO, v); } else {_WRITE_NC(IO, v); }; } while (0)
 
 /// toggle a pin
-#define _TOGGLE(IO)  do {DIO ##  IO ## _RPORT = MASK(DIO ## IO ## _PIN); } while (0)
+#define _TOGGLE(IO)  do {DIO ##  IO ## _RPORT = _BV(DIO ## IO ## _PIN); } while (0)
 
 /// set pin as input
-#define	_SET_INPUT(IO) do {DIO ##  IO ## _DDR &= ~MASK(DIO ## IO ## _PIN); } while (0)
+#define	_SET_INPUT(IO) do {DIO ##  IO ## _DDR &= ~_BV(DIO ## IO ## _PIN); } while (0)
 /// set pin as output
-#define	_SET_OUTPUT(IO) do {DIO ##  IO ## _DDR |=  MASK(DIO ## IO ## _PIN); } while (0)
+#define	_SET_OUTPUT(IO) do {DIO ##  IO ## _DDR |=  _BV(DIO ## IO ## _PIN); } while (0)
 
 /// check if pin is an input
-#define	_GET_INPUT(IO)  ((DIO ## IO ## _DDR & MASK(DIO ## IO ## _PIN)) == 0)
+#define	_GET_INPUT(IO)  ((DIO ## IO ## _DDR & _BV(DIO ## IO ## _PIN)) == 0)
 /// check if pin is an output
-#define	_GET_OUTPUT(IO)  ((DIO ## IO ## _DDR & MASK(DIO ## IO ## _PIN)) != 0)
+#define	_GET_OUTPUT(IO)  ((DIO ## IO ## _DDR & _BV(DIO ## IO ## _PIN)) != 0)
 
 /// check if pin is an timer
-#define	_GET_TIMER(IO)  ((DIO ## IO ## _PWM)
+#define	_GET_TIMER(IO)  (DIO ## IO ## _PWM)
 
 //  why double up on these macros? see http://gcc.gnu.org/onlinedocs/cpp/Stringification.html
 

+ 1 - 0
Firmware/first_lay_cal.cpp

@@ -7,6 +7,7 @@
 #include "Configuration_prusa.h"
 #include "language.h"
 #include "Marlin.h"
+#include "cmdqueue.h"
 #include "mmu.h"
 #include <avr/pgmspace.h>
 

+ 17 - 17
Firmware/fsensor.cpp

@@ -6,7 +6,6 @@
 #include <avr/pgmspace.h>
 #include "pat9125.h"
 #include "stepper.h"
-#include "io_atmega2560.h"
 #include "cmdqueue.h"
 #include "ultralcd.h"
 #include "mmu.h"
@@ -130,7 +129,7 @@ unsigned long nIRsensorLastTime;
 
 void fsensor_stop_and_save_print(void)
 {
-    printf_P(PSTR("fsensor_stop_and_save_print\n"));
+    puts_P(PSTR("fsensor_stop_and_save_print"));
     stop_and_save_print_to_ram(0, 0);
     fsensor_watch_runout = false;
 }
@@ -153,7 +152,7 @@ void fsensor_set_axis_steps_per_unit(float u)
 
 void fsensor_restore_print_and_continue(void)
 {
-    printf_P(PSTR("fsensor_restore_print_and_continue\n"));
+    puts_P(PSTR("fsensor_restore_print_and_continue"));
     fsensor_watch_runout = true;
 #ifdef PAT9125
     fsensor_reset_err_cnt();
@@ -165,7 +164,7 @@ void fsensor_restore_print_and_continue(void)
 // allowing new instructions to be inserted in the middle
 void fsensor_checkpoint_print(void)
 {
-    printf_P(PSTR("fsensor_checkpoint_print\n"));
+    puts_P(PSTR("fsensor_checkpoint_print"));
     stop_and_save_print_to_ram(0, 0);
     restore_print_from_ram_and_continue(0);
 }
@@ -234,6 +233,8 @@ void fsensor_init(void)
 bool fsensor_enable(bool bUpdateEEPROM)
 {
 #ifdef PAT9125
+    (void)bUpdateEEPROM; // silence unused warning in this variant
+
 	if (mmu_enabled == false) { //filament sensor is pat9125, enable only if it is working
 		uint8_t pat9125 = pat9125_init();
 		printf_P(PSTR("PAT9125_init:%hhu\n"), pat9125);
@@ -317,7 +318,7 @@ void fsensor_autoload_check_start(void)
 		printf_P(ERRMSG_PAT9125_NOT_RESP, 3);
 		return;
 	}
-	puts_P(_N("fsensor_autoload_check_start - autoload ENABLED\n"));
+	puts_P(_N("fsensor_autoload_check_start - autoload ENABLED"));
 	fsensor_autoload_y = pat9125_y; //save current y value
 	fsensor_autoload_c = 0; //reset number of changes counter
 	fsensor_autoload_sum = 0;
@@ -335,7 +336,7 @@ void fsensor_autoload_check_stop(void)
 	if (!fsensor_autoload_enabled) return;
 //	puts_P(_N("fsensor_autoload_check_stop 2\n"));
 	if (!fsensor_watch_autoload) return;
-	puts_P(_N("fsensor_autoload_check_stop - autoload DISABLED\n"));
+	puts_P(_N("fsensor_autoload_check_stop - autoload DISABLED"));
 	fsensor_autoload_sum = 0;
 	fsensor_watch_autoload = false;
 	fsensor_watch_runout = true;
@@ -415,7 +416,7 @@ void fsensor_oq_meassure_start(uint8_t skip)
 {
 	if (!fsensor_enabled) return;
 	if (!fsensor_oq_meassure_enabled) return;
-	printf_P(PSTR("fsensor_oq_meassure_start\n"));
+	puts_P(PSTR("fsensor_oq_meassure_start"));
 	fsensor_oq_skipchunk = skip;
 	fsensor_oq_samples = 0;
 	fsensor_oq_st_sum = 0;
@@ -448,7 +449,7 @@ bool fsensor_oq_result(void)
 {
 	if (!fsensor_enabled) return true;
 	if (!fsensor_oq_meassure_enabled) return true;
-	printf_P(_N("fsensor_oq_result\n"));
+	puts_P(_N("fsensor_oq_result"));
 	bool res_er_sum = (fsensor_oq_er_sum <= FSENSOR_OQ_MAX_ES);
 	printf_P(_N(" er_sum = %u %S\n"), fsensor_oq_er_sum, (res_er_sum?_OK:_NG));
 	bool res_er_max = (fsensor_oq_er_max <= FSENSOR_OQ_MAX_EM);
@@ -608,9 +609,8 @@ void fsensor_st_block_chunk(int cnt)
 	if (!fsensor_enabled) return;
 	fsensor_st_cnt += cnt;
 
-    // !!! bit toggling (PINxn <- 1) (for PinChangeInterrupt) does not work for some MCU pins
-    if (PIN_GET(FSENSOR_INT_PIN)) {PIN_VAL(FSENSOR_INT_PIN, LOW);}
-    else {PIN_VAL(FSENSOR_INT_PIN, HIGH);}
+	// !!! bit toggling (PINxn <- 1) (for PinChangeInterrupt) does not work for some MCU pins
+	WRITE(FSENSOR_INT_PIN, !READ(FSENSOR_INT_PIN));
 }
 #endif //PAT9125
 
@@ -618,7 +618,7 @@ void fsensor_st_block_chunk(int cnt)
 //! Common code for enqueing M600 and supplemental codes into the command queue.
 //! Used both for the IR sensor and the PAT9125
 void fsensor_enque_M600(){
-	printf_P(PSTR("fsensor_update - M600\n"));
+	puts_P(PSTR("fsensor_update - M600"));
 	eeprom_update_byte((uint8_t*)EEPROM_FERROR_COUNT, eeprom_read_byte((uint8_t*)EEPROM_FERROR_COUNT) + 1);
 	eeprom_update_word((uint16_t*)EEPROM_FERROR_COUNT_TOT, eeprom_read_word((uint16_t*)EEPROM_FERROR_COUNT_TOT) + 1);
 	enquecommand_front_P((PSTR("M600")));
@@ -672,7 +672,7 @@ void fsensor_update(void)
                 fsensor_softfail_ccnt = 0;
             if (!err && fsensor_softfail_ccnt <= FSENSOR_SOFTERR_CMAX)
             {
-                printf_P(PSTR("fsensor_err_cnt = 0\n"));
+                puts_P(PSTR("fsensor_err_cnt = 0"));
                 ++fsensor_softfail;
                 ++fsensor_softfail_ccnt;
                 fsensor_softfail_last = now;
@@ -759,19 +759,19 @@ bool fsensor_IR_check(){
         /// Or the user is so creative so that he can hold a piece of fillament in the hole in such a genius way,
         /// that the IR fsensor reading is within 1.5 and 3V ... this would have been highly unusual
         /// and would have been considered more like a sabotage than normal printer operation
-        printf_P(PSTR("fsensor in forbidden range 1.5-3V - check sensor\n"));
+        puts_P(PSTR("fsensor in forbidden range 1.5-3V - check sensor"));
         return false; 
     }
     if( oFsensorPCB == ClFsensorPCB::_Rev04 ){
         /// newer IR sensor cannot normally produce 4.6-5V, this is considered a failure/bad mount
         if( IRsensor_Hopen_TRESHOLD <= current_voltage_raw_IR && current_voltage_raw_IR <= IRsensor_VMax_TRESHOLD ){
-            printf_P(PSTR("fsensor v0.4 in fault range 4.6-5V - unconnected\n"));
+            puts_P(PSTR("fsensor v0.4 in fault range 4.6-5V - unconnected"));
             return false;
         }
         /// newer IR sensor cannot normally produce 0-0.3V, this is considered a failure 
 #if 0	//Disabled as it has to be decided if we gonna use this or not.
         if( IRsensor_Hopen_TRESHOLD <= current_voltage_raw_IR && current_voltage_raw_IR <= IRsensor_VMax_TRESHOLD ){
-            printf_P(PSTR("fsensor v0.4 in fault range 0.0-0.3V - wrong IR sensor\n"));
+            puts_P(PSTR("fsensor v0.4 in fault range 0.0-0.3V - wrong IR sensor"));
             return false;
         }
 #endif
@@ -779,7 +779,7 @@ bool fsensor_IR_check(){
     /// If IR sensor is "uknown state" and filament is not loaded > 1.5V return false
 #if 0
     if( (oFsensorPCB == ClFsensorPCB::_Undef) && ( current_voltage_raw_IR > IRsensor_Lmax_TRESHOLD ) ){
-        printf_P(PSTR("Unknown IR sensor version and no filament loaded detected.\n"));
+        puts_P(PSTR("Unknown IR sensor version and no filament loaded detected."));
         return false;
     }
 #endif

+ 0 - 2
Firmware/heatbed_pwm.cpp

@@ -1,6 +1,5 @@
 #include <avr/io.h>
 #include <avr/interrupt.h>
-#include "io_atmega2560.h"
 
 // All this is about silencing the heat bed, as it behaves like a loudspeaker.
 // Basically, we want the PWM heating switched at 30Hz (or so) which is a well ballanced
@@ -154,7 +153,6 @@ ISR(TIMER0_OVF_vect)          // timer compare interrupt service routine
 			return;           // want full duty for the next ONE cycle again - so keep on heating and just wait for the next timer ovf
 		}
 		// otherwise moving towards FALL
-		state = States::ONE;//_TO_FALL;
 		state=States::FALL;
 		fastCounter = fastMax - 1;// we'll do 16-1 cycles of RISE
 		TCNT0 = 255;              // force overflow on the next clock cycle

+ 0 - 374
Firmware/io_atmega2560.h

@@ -1,374 +0,0 @@
-//io_atmega2560.h
-#ifndef _IO_ATMEGA2560
-#define _IO_ATMEGA2560
-
-
-#define __PIN_P0  PINE
-#define __PIN_P1  PINE
-#define __PIN_P2  PINE
-#define __PIN_P3  PINE
-#define __PIN_P4  PING
-#define __PIN_P5  PINE
-#define __PIN_P6  PINH
-#define __PIN_P7  PINH
-#define __PIN_P8  PINH
-#define __PIN_P9  PINH
-#define __PIN_P10 PINB
-#define __PIN_P11 PINB
-#define __PIN_P12 PINB
-#define __PIN_P13 PINB
-#define __PIN_P14 PINJ
-#define __PIN_P15 PINJ
-#define __PIN_P16 PINH
-#define __PIN_P17 PINH
-#define __PIN_P18 PIND
-#define __PIN_P19 PIND
-#define __PIN_P20 PIND
-#define __PIN_P21 PIND
-#define __PIN_P22 PINA
-#define __PIN_P23 PINA
-#define __PIN_P24 PINA
-#define __PIN_P25 PINA
-#define __PIN_P26 PINA
-#define __PIN_P27 PINA
-#define __PIN_P28 PINA
-#define __PIN_P29 PINA
-#define __PIN_P30 PINC
-#define __PIN_P31 PINC
-#define __PIN_P32 PINC
-#define __PIN_P33 PINC
-#define __PIN_P34 PINC
-#define __PIN_P35 PINC
-#define __PIN_P36 PINC
-#define __PIN_P37 PINC
-#define __PIN_P38 PIND
-#define __PIN_P39 PING
-#define __PIN_P40 PING
-#define __PIN_P41 PING
-#define __PIN_P42 PINL
-#define __PIN_P43 PINL
-#define __PIN_P44 PINL
-#define __PIN_P45 PINL
-#define __PIN_P46 PINL
-#define __PIN_P47 PINL
-#define __PIN_P48 PINL
-#define __PIN_P49 PINL
-#define __PIN_P50 PINB
-#define __PIN_P51 PINB
-#define __PIN_P52 PINB
-#define __PIN_P53 PINB
-#define __PIN_P54 PINF
-#define __PIN_P55 PINF
-#define __PIN_P56 PINF
-#define __PIN_P57 PINF
-#define __PIN_P58 PINF
-#define __PIN_P59 PINF
-#define __PIN_P60 PINF
-#define __PIN_P61 PINF
-#define __PIN_P62 PINK
-#define __PIN_P63 PINK
-#define __PIN_P64 PINK
-#define __PIN_P65 PINK
-#define __PIN_P66 PINK
-#define __PIN_P67 PINK
-#define __PIN_P68 PINK
-#define __PIN_P69 PINK
-#define __PIN_P70 PING
-#define __PIN_P71 PING
-#define __PIN_P72 PINJ
-#define __PIN_P73 PINJ
-#define __PIN_P74 PINJ
-#define __PIN_P75 PINJ
-#define __PIN_P76 PINJ
-#define __PIN_P77 PINJ
-#define __PIN_P78 PINE
-#define __PIN_P79 PINE
-#define __PIN_P80 PINE
-#define __PIN_P81 PIND
-#define __PIN_P82 PIND
-#define __PIN_P83 PIND
-#define __PIN_P84 PINH
-#define __PIN_P85 PINH
-
-#define __PORT_P0  PORTE
-#define __PORT_P1  PORTE
-#define __PORT_P2  PORTE
-#define __PORT_P3  PORTE
-#define __PORT_P4  PORTG
-#define __PORT_P5  PORTE
-#define __PORT_P6  PORTH
-#define __PORT_P7  PORTH
-#define __PORT_P8  PORTH
-#define __PORT_P9  PORTH
-#define __PORT_P10 PORTB
-#define __PORT_P11 PORTB
-#define __PORT_P12 PORTB
-#define __PORT_P13 PORTB
-#define __PORT_P14 PORTJ
-#define __PORT_P15 PORTJ
-#define __PORT_P16 PORTH
-#define __PORT_P17 PORTH
-#define __PORT_P18 PORTD
-#define __PORT_P19 PORTD
-#define __PORT_P20 PORTD
-#define __PORT_P21 PORTD
-#define __PORT_P22 PORTA
-#define __PORT_P23 PORTA
-#define __PORT_P24 PORTA
-#define __PORT_P25 PORTA
-#define __PORT_P26 PORTA
-#define __PORT_P27 PORTA
-#define __PORT_P28 PORTA
-#define __PORT_P29 PORTA
-#define __PORT_P30 PORTC
-#define __PORT_P31 PORTC
-#define __PORT_P32 PORTC
-#define __PORT_P33 PORTC
-#define __PORT_P34 PORTC
-#define __PORT_P35 PORTC
-#define __PORT_P36 PORTC
-#define __PORT_P37 PORTC
-#define __PORT_P38 PORTD
-#define __PORT_P39 PORTG
-#define __PORT_P40 PORTG
-#define __PORT_P41 PORTG
-#define __PORT_P42 PORTL
-#define __PORT_P43 PORTL
-#define __PORT_P44 PORTL
-#define __PORT_P45 PORTL
-#define __PORT_P46 PORTL
-#define __PORT_P47 PORTL
-#define __PORT_P48 PORTL
-#define __PORT_P49 PORTL
-#define __PORT_P50 PORTB
-#define __PORT_P51 PORTB
-#define __PORT_P52 PORTB
-#define __PORT_P53 PORTB
-#define __PORT_P54 PORTF
-#define __PORT_P55 PORTF
-#define __PORT_P56 PORTF
-#define __PORT_P57 PORTF
-#define __PORT_P58 PORTF
-#define __PORT_P59 PORTF
-#define __PORT_P60 PORTF
-#define __PORT_P61 PORTF
-#define __PORT_P62 PORTK
-#define __PORT_P63 PORTK
-#define __PORT_P64 PORTK
-#define __PORT_P65 PORTK
-#define __PORT_P66 PORTK
-#define __PORT_P67 PORTK
-#define __PORT_P68 PORTK
-#define __PORT_P69 PORTK
-#define __PORT_P70 PORTG
-#define __PORT_P71 PORTG
-#define __PORT_P72 PORTJ
-#define __PORT_P73 PORTJ
-#define __PORT_P74 PORTJ
-#define __PORT_P75 PORTJ
-#define __PORT_P76 PORTJ
-#define __PORT_P77 PORTJ
-#define __PORT_P78 PORTE
-#define __PORT_P79 PORTE
-#define __PORT_P80 PORTE
-#define __PORT_P81 PORTD
-#define __PORT_P82 PORTD
-#define __PORT_P83 PORTD
-#define __PORT_P84 PORTH
-#define __PORT_P85 PORTH
-
-#define __DDR_P0  DDRE
-#define __DDR_P1  DDRE
-#define __DDR_P2  DDRE
-#define __DDR_P3  DDRE
-#define __DDR_P4  DDRG
-#define __DDR_P5  DDRE
-#define __DDR_P6  DDRH
-#define __DDR_P7  DDRH
-#define __DDR_P8  DDRH
-#define __DDR_P9  DDRH
-#define __DDR_P10 DDRB
-#define __DDR_P11 DDRB
-#define __DDR_P12 DDRB
-#define __DDR_P13 DDRB
-#define __DDR_P14 DDRJ
-#define __DDR_P15 DDRJ
-#define __DDR_P16 DDRH
-#define __DDR_P17 DDRH
-#define __DDR_P18 DDRD
-#define __DDR_P19 DDRD
-#define __DDR_P20 DDRD
-#define __DDR_P21 DDRD
-#define __DDR_P22 DDRA
-#define __DDR_P23 DDRA
-#define __DDR_P24 DDRA
-#define __DDR_P25 DDRA
-#define __DDR_P26 DDRA
-#define __DDR_P27 DDRA
-#define __DDR_P28 DDRA
-#define __DDR_P29 DDRA
-#define __DDR_P30 DDRC
-#define __DDR_P31 DDRC
-#define __DDR_P32 DDRC
-#define __DDR_P33 DDRC
-#define __DDR_P34 DDRC
-#define __DDR_P35 DDRC
-#define __DDR_P36 DDRC
-#define __DDR_P37 DDRC
-#define __DDR_P38 DDRD
-#define __DDR_P39 DDRG
-#define __DDR_P40 DDRG
-#define __DDR_P41 DDRG
-#define __DDR_P42 DDRL
-#define __DDR_P43 DDRL
-#define __DDR_P44 DDRL
-#define __DDR_P45 DDRL
-#define __DDR_P46 DDRL
-#define __DDR_P47 DDRL
-#define __DDR_P48 DDRL
-#define __DDR_P49 DDRL
-#define __DDR_P50 DDRB
-#define __DDR_P51 DDRB
-#define __DDR_P52 DDRB
-#define __DDR_P53 DDRB
-#define __DDR_P54 DDRF
-#define __DDR_P55 DDRF
-#define __DDR_P56 DDRF
-#define __DDR_P57 DDRF
-#define __DDR_P58 DDRF
-#define __DDR_P59 DDRF
-#define __DDR_P60 DDRF
-#define __DDR_P61 DDRF
-#define __DDR_P62 DDRK
-#define __DDR_P63 DDRK
-#define __DDR_P64 DDRK
-#define __DDR_P65 DDRK
-#define __DDR_P66 DDRK
-#define __DDR_P67 DDRK
-#define __DDR_P68 DDRK
-#define __DDR_P69 DDRK
-#define __DDR_P70 DDRG
-#define __DDR_P71 DDRG
-#define __DDR_P72 DDRJ
-#define __DDR_P73 DDRJ
-#define __DDR_P74 DDRJ
-#define __DDR_P75 DDRJ
-#define __DDR_P76 DDRJ
-#define __DDR_P77 DDRJ
-#define __DDR_P78 DDRE
-#define __DDR_P79 DDRE
-#define __DDR_P80 DDRE
-#define __DDR_P81 DDRD
-#define __DDR_P82 DDRD
-#define __DDR_P83 DDRD
-#define __DDR_P84 DDRH
-#define __DDR_P85 DDRH
-
-#define __BIT_P0  0
-#define __BIT_P1  1
-#define __BIT_P2  4
-#define __BIT_P3  5
-#define __BIT_P4  5
-#define __BIT_P5  3
-#define __BIT_P6  3
-#define __BIT_P7  4
-#define __BIT_P8  5
-#define __BIT_P9  6
-#define __BIT_P10 4
-#define __BIT_P11 5
-#define __BIT_P12 6
-#define __BIT_P13 7
-#define __BIT_P14 1
-#define __BIT_P15 0
-#define __BIT_P16 0
-#define __BIT_P17 1
-#define __BIT_P18 3
-#define __BIT_P19 2
-#define __BIT_P20 1
-#define __BIT_P21 0
-#define __BIT_P22 0
-#define __BIT_P23 1
-#define __BIT_P24 2
-#define __BIT_P25 3
-#define __BIT_P26 4
-#define __BIT_P27 5
-#define __BIT_P28 6
-#define __BIT_P29 7
-#define __BIT_P30 7
-#define __BIT_P31 6
-#define __BIT_P32 5
-#define __BIT_P33 4
-#define __BIT_P34 3
-#define __BIT_P35 2
-#define __BIT_P36 1
-#define __BIT_P37 0
-#define __BIT_P38 7
-#define __BIT_P39 2
-#define __BIT_P40 1
-#define __BIT_P41 0
-#define __BIT_P42 7
-#define __BIT_P43 6
-#define __BIT_P44 5
-#define __BIT_P45 4
-#define __BIT_P46 3
-#define __BIT_P47 2
-#define __BIT_P48 1
-#define __BIT_P49 0
-#define __BIT_P50 3
-#define __BIT_P51 2
-#define __BIT_P52 1
-#define __BIT_P53 0
-#define __BIT_P54 0
-#define __BIT_P55 1
-#define __BIT_P56 2
-#define __BIT_P57 3
-#define __BIT_P58 4
-#define __BIT_P59 5
-#define __BIT_P60 6
-#define __BIT_P61 7
-#define __BIT_P62 0
-#define __BIT_P63 1
-#define __BIT_P64 2
-#define __BIT_P65 3
-#define __BIT_P66 4
-#define __BIT_P67 5
-#define __BIT_P68 6
-#define __BIT_P69 7
-#define __BIT_P70 4
-#define __BIT_P71 3
-#define __BIT_P72 2
-#define __BIT_P73 3
-#define __BIT_P74 7
-#define __BIT_P75 4
-#define __BIT_P76 5
-#define __BIT_P77 6
-#define __BIT_P78 2
-#define __BIT_P79 6
-#define __BIT_P80 7
-#define __BIT_P81 4
-#define __BIT_P82 5
-#define __BIT_P83 6
-#define __BIT_P84 2
-#define __BIT_P85 7
-
-#define __BIT(pin) __BIT_P##pin
-#define __MSK(pin) (1 << __BIT(pin))
-
-#define __PIN(pin) __PIN_P##pin
-#define __PORT(pin) __PORT_P##pin
-#define __DDR(pin) __DDR_P##pin
-
-#define PIN(pin) __PIN(pin)
-#define PORT(pin) __PORT(pin)
-#define DDR(pin) __DDR(pin)
-
-#define PIN_INP(pin) DDR(pin) &= ~__MSK(pin)
-#define PIN_OUT(pin) DDR(pin) |= __MSK(pin)
-#define PIN_CLR(pin) PORT(pin) &= ~__MSK(pin)
-#define PIN_SET(pin) PORT(pin) |= __MSK(pin)
-#define PIN_VAL(pin, val) if (val) PIN_SET(pin); else PIN_CLR(pin);
-#define PIN_GET(pin) (PIN(pin) & __MSK(pin))
-#define PIN_INQ(pin) (PORT(pin) & __MSK(pin))
-
-
-#endif //_IO_ATMEGA2560

+ 1 - 1
Firmware/la10compat.cpp

@@ -38,7 +38,7 @@ void la10c_mode_change(LA10C_MODE mode)
 // Approximate a LA10 value to a LA15 equivalent.
 static float la10c_convert(float k)
 {
-    float new_K = k * 0.004 - 0.05;
+    float new_K = k * 0.002 - 0.01;
     return new_K < 0? 0:
            new_K > (LA_K_MAX - FLT_EPSILON)? (LA_K_MAX - FLT_EPSILON):
            new_K;

+ 38 - 27
Firmware/language.c

@@ -7,9 +7,10 @@
 #include "Configuration.h"
 #include "pins.h"
 
-#ifdef W25X20CL
-#include "w25x20cl.h"
-#endif //W25X20CL
+#ifdef XFLASH
+#include "xflash.h"
+#include "xflash_layout.h"
+#endif //XFLASH
 
 // Currently active language selection.
 uint8_t lang_selected = 0;
@@ -17,10 +18,10 @@ uint8_t lang_selected = 0;
 
 #if (LANG_MODE == 0) //primary language only
 
-uint8_t lang_select(__attribute__((unused)) uint8_t lang) { return 0; }
+uint8_t lang_select(_UNUSED uint8_t lang) { return 0; }
 uint8_t lang_get_count() { return 1; }
-uint16_t lang_get_code(__attribute__((unused)) uint8_t lang) { return LANG_CODE_EN; }
-const char* lang_get_name_by_code(__attribute__((unused)) uint16_t code) { return _n("English"); }
+uint16_t lang_get_code(_UNUSED uint8_t lang) { return LANG_CODE_EN; }
+const char* lang_get_name_by_code(_UNUSED uint16_t code) { return _n("English"); }
 void lang_reset(void) { }
 uint8_t lang_is_selected(void) { return 1; }
 
@@ -54,7 +55,7 @@ uint8_t lang_select(uint8_t lang)
 		lang_table = 0;
 		lang_selected = lang;
 	}
-#ifdef W25X20CL
+#ifdef XFLASH
 	if (lang_get_code(lang) == lang_get_code(LANG_ID_SEC)) lang = LANG_ID_SEC;
 	if (lang == LANG_ID_SEC) //current secondary language
 	{
@@ -68,7 +69,7 @@ uint8_t lang_select(uint8_t lang)
 				}
 		}
 	}
-#else //W25X20CL
+#else //XFLASH
 	if (lang == LANG_ID_SEC)
 	{
 		uint16_t table = _SEC_LANG_TABLE;
@@ -82,7 +83,7 @@ uint8_t lang_select(uint8_t lang)
 				}
 		}
 	}
-#endif //W25X20CL
+#endif //XFLASH
 	if (lang_selected == lang)
 	{
 		eeprom_update_byte((unsigned char*)EEPROM_LANG, lang_selected);
@@ -107,19 +108,19 @@ uint8_t lang_get_count()
 {
 	if (pgm_read_dword(((uint32_t*)(_PRI_LANG_SIGNATURE))) == 0xffffffff)
 		return 1; //signature not set - only primary language will be available
-#ifdef W25X20CL
-	W25X20CL_SPI_ENTER();
+#ifdef XFLASH
+	XFLASH_SPI_ENTER();
 	uint8_t count = 2; //count = 1+n (primary + secondary + all in xflash)
-	uint32_t addr = 0x00000; //start of xflash
+	uint32_t addr = LANG_OFFSET;
 	lang_table_header_t header; //table header structure
 	while (1)
 	{
-		w25x20cl_rd_data(addr, (uint8_t*)&header, sizeof(lang_table_header_t)); //read table header from xflash
+		xflash_rd_data(addr, (uint8_t*)&header, sizeof(lang_table_header_t)); //read table header from xflash
 		if (header.magic != LANG_MAGIC) break; //break if magic not valid
 		addr += header.size; //calc address of next table
 		count++; //inc counter
 	}
-#else //W25X20CL
+#else //XFLASH
 	uint16_t table = _SEC_LANG_TABLE;
 	uint8_t count = 1; //count = 1 (primary)
 	while (pgm_read_dword(((uint32_t*)table)) == LANG_MAGIC) //magic valid
@@ -127,14 +128,14 @@ uint8_t lang_get_count()
 		table += pgm_read_word((uint16_t*)(table + 4));
 		count++;
 	}
-#endif //W25X20CL
+#endif //XFLASH
 	return count;
 }
 
 uint8_t lang_get_header(uint8_t lang, lang_table_header_t* header, uint32_t* offset)
 {
 	if (lang == LANG_ID_PRI) return 0; //primary lang not supported for this function
-#ifdef W25X20CL
+#ifdef XFLASH
 	if (lang == LANG_ID_SEC)
 	{
 		uint16_t ui = _SEC_LANG_TABLE; //table pointer
@@ -142,18 +143,18 @@ uint8_t lang_get_header(uint8_t lang, lang_table_header_t* header, uint32_t* off
 		if (offset) *offset = ui;
 		return (header->magic == LANG_MAGIC)?1:0; //return 1 if magic valid
 	}
-	W25X20CL_SPI_ENTER();
-	uint32_t addr = 0x00000; //start of xflash
+	XFLASH_SPI_ENTER();
+	uint32_t addr = LANG_OFFSET;
 	lang--;
 	while (1)
 	{
-		w25x20cl_rd_data(addr, (uint8_t*)(header), sizeof(lang_table_header_t)); //read table header from xflash
+		xflash_rd_data(addr, (uint8_t*)(header), sizeof(lang_table_header_t)); //read table header from xflash
 		if (header->magic != LANG_MAGIC) break; //break if not valid
 		if (offset) *offset = addr;
 		if (--lang == 0) return 1;
 		addr += header->size; //calc address of next table
 	}
-#else //W25X20CL
+#else //XFLASH
 	if (lang == LANG_ID_SEC)
 	{
 		uint16_t ui = _SEC_LANG_TABLE; //table pointer
@@ -161,32 +162,32 @@ uint8_t lang_get_header(uint8_t lang, lang_table_header_t* header, uint32_t* off
 		if (offset) *offset = ui;
 		return (header->magic == LANG_MAGIC)?1:0; //return 1 if magic valid
 	}
-#endif //W25X20CL
+#endif //XFLASH
 	return 0;
 }
 
 uint16_t lang_get_code(uint8_t lang)
 {
 	if (lang == LANG_ID_PRI) return LANG_CODE_EN; //primary lang = EN
-#ifdef W25X20CL
+#ifdef XFLASH
 	if (lang == LANG_ID_SEC)
 	{
 		uint16_t ui = _SEC_LANG_TABLE; //table pointer
 		if (pgm_read_dword(((uint32_t*)(ui + 0))) != LANG_MAGIC) return LANG_CODE_XX; //magic not valid
 		return pgm_read_word(((uint32_t*)(ui + 10))); //return lang code from progmem
 	}
-	W25X20CL_SPI_ENTER();
-	uint32_t addr = 0x00000; //start of xflash
+	XFLASH_SPI_ENTER();
+	uint32_t addr = LANG_OFFSET;
 	lang_table_header_t header; //table header structure
 	lang--;
 	while (1)
 	{
-		w25x20cl_rd_data(addr, (uint8_t*)&header, sizeof(lang_table_header_t)); //read table header from xflash
+		xflash_rd_data(addr, (uint8_t*)&header, sizeof(lang_table_header_t)); //read table header from xflash
 		if (header.magic != LANG_MAGIC) break; //break if not valid
 		if (--lang == 0) return header.code;
 		addr += header.size; //calc address of next table
 	}
-#else //W25X20CL
+#else //XFLASH
 	uint16_t table = _SEC_LANG_TABLE;
 	uint8_t count = 1; //count = 1 (primary)
 	while (pgm_read_dword((uint32_t*)table) == LANG_MAGIC) //magic valid
@@ -195,7 +196,7 @@ uint16_t lang_get_code(uint8_t lang)
 		table += pgm_read_word((uint16_t*)(table + 4));
 		count++;
 	}
-#endif //W25X20CL
+#endif //XFLASH
 	return LANG_CODE_XX;
 }
 
@@ -210,6 +211,16 @@ const char* lang_get_name_by_code(uint16_t code)
 	case LANG_CODE_FR: return _n("Francais");
 	case LANG_CODE_IT: return _n("Italiano");
 	case LANG_CODE_PL: return _n("Polski");
+#ifdef COMMUNITY_LANG_SUPPORT //Community language support
+#ifdef COMMUNITY_LANG_NL
+	case LANG_CODE_NL: return _n("Nederlands"); //community contribution
+#endif // COMMUNITY_LANG_NL
+
+//Use the 3 lines below as a template and replace 'QR' and 'New language'
+//#ifdef COMMUNITY_LANG_QR 
+//	case LANG_CODE_QR: return _n("New language"); //community contribution
+//#endif // COMMUNITY_LANG_QR
+#endif // COMMUNITY_LANG_SUPPORT
 	}
 	return _n("??");
 }

+ 10 - 5
Firmware/language.h

@@ -5,6 +5,7 @@
 
 
 #include "config.h"
+#include "macros.h"
 #include <inttypes.h>
 #ifdef DEBUG_SEC_LANG
     #include <stdio.h>
@@ -20,11 +21,6 @@
    #define MACHINE_UUID "00000000-0000-0000-0000-000000000000"
 #endif
 
-#define MSG_FW_VERSION                   "Firmware"
-
-#define STRINGIFY_(n) #n
-#define STRINGIFY(n) STRINGIFY_(n)
-
 #if (LANG_MODE == 0) //primary language only
 #define PROGMEM_I2 __attribute__((section(".progmem0")))
 #define PROGMEM_I1 __attribute__((section(".progmem1")))
@@ -98,6 +94,15 @@ typedef struct
 #define LANG_CODE_FR 0x6672 //!<'fr'
 #define LANG_CODE_IT 0x6974 //!<'it'
 #define LANG_CODE_PL 0x706c //!<'pl'
+#ifdef COMMUNITY_LANG_SUPPORT //Community language support
+#ifdef COMMUNITY_LANG_NL
+#define LANG_CODE_NL 0x6e6c //!<'nl'
+#endif // COMMUNITY_LANG_NL
+//Use the 3 lines below as a template and replace 'QR', '0X7172' and 'qr'
+//#ifdef COMMUNITY_LANG_QR
+//#define LANG_CODE_QR 0x7172 //!<'qr'
+//#endif // COMMUNITY_LANG_QR
+#endif // COMMUNITY_LANG_SUPPORT
 ///@}
 
 #if defined(__cplusplus)

+ 12 - 19
Firmware/lcd.cpp

@@ -486,11 +486,17 @@ void lcd_escape_write(uint8_t chr)
 #endif //VT100
 
 
-int lcd_putc(int c)
+int lcd_putc(char c)
 {
 	return fputc(c, lcdout);
 }
 
+int lcd_putc_at(uint8_t c, uint8_t r, char ch)
+{
+	lcd_set_cursor(c, r);
+	return fputc(ch, lcdout);
+}
+
 int lcd_puts_P(const char* str)
 {
 	return fputs_P(str, lcdout);
@@ -722,6 +728,10 @@ void lcd_update_enable(uint8_t enabled)
 	}
 }
 
+bool lcd_longpress_trigger = 0;
+
+// WARNING: this function is called from the temperature ISR.
+//          Only update flags, but do not perform any menu/lcd operation!
 void lcd_buttons_update(void)
 {
     static uint8_t lcd_long_press_active = 0;
@@ -743,9 +753,7 @@ void lcd_buttons_update(void)
             else if (longPressTimer.expired(LONG_PRESS_TIME))
             {
                 lcd_long_press_active = 1;
-                //long press is not possible in modal mode
-                if (lcd_longpress_func && lcd_update_enabled)
-                    lcd_longpress_func();
+                lcd_longpress_trigger = 1;
             }
         }
     }
@@ -957,21 +965,6 @@ void lcd_set_custom_characters_arrows(void)
 	lcd_createChar_P(1, lcd_chardata_arrdown);
 }
 
-const uint8_t lcd_chardata_progress[8] PROGMEM = {
-	B11111,
-	B11111,
-	B11111,
-	B11111,
-	B11111,
-	B11111,
-	B11111,
-	B11111};
-
-void lcd_set_custom_characters_progress(void)
-{
-	lcd_createChar_P(1, lcd_chardata_progress);
-}
-
 const uint8_t lcd_chardata_arr2down[8] PROGMEM = {
 	B00000,
 	B00000,

+ 5 - 2
Firmware/lcd.h

@@ -40,7 +40,10 @@ extern void lcd_set_cursor(uint8_t col, uint8_t row);
 extern void lcd_createChar_P(uint8_t, const uint8_t*);
 
 
-extern int lcd_putc(int c);
+// char c is non-standard, however it saves 1B on stack
+extern int lcd_putc(char c);
+extern int lcd_putc_at(uint8_t c, uint8_t r, char ch);
+
 extern int lcd_puts_P(const char* str);
 extern int lcd_puts_at_P(uint8_t c, uint8_t r, const char* str);
 extern int lcd_printf_P(const char* format, ...);
@@ -107,6 +110,7 @@ extern uint32_t lcd_next_update_millis;
 extern uint8_t lcd_status_update_delay;
 
 extern lcd_longpress_func_t lcd_longpress_func;
+extern bool lcd_longpress_trigger;
 
 extern lcd_charsetup_func_t lcd_charsetup_func;
 
@@ -201,7 +205,6 @@ private:
 
 extern void lcd_set_custom_characters(void);
 extern void lcd_set_custom_characters_arrows(void);
-extern void lcd_set_custom_characters_progress(void);
 extern void lcd_set_custom_characters_nextpage(void);
 extern void lcd_set_custom_characters_degree(void);
 

+ 90 - 0
Firmware/macros.h

@@ -0,0 +1,90 @@
+#ifndef MACROS_H
+#define MACROS_H
+
+#include <avr/interrupt.h> //for cli() and sei()
+
+#define  FORCE_INLINE __attribute__((always_inline)) inline
+#define _UNUSED __attribute__((unused))
+
+#ifndef CRITICAL_SECTION_START
+  #define CRITICAL_SECTION_START  unsigned char _sreg = SREG; cli();
+  #define CRITICAL_SECTION_END    SREG = _sreg;
+#endif //CRITICAL_SECTION_START
+
+// Macros to make a string from a macro
+#define STRINGIFY_(M) #M
+#define STRINGIFY(M) STRINGIFY_(M)
+
+// Macros for bit masks
+#undef _BV
+#define _BV(n) (1<<(n))
+#define TEST(n,b) (!!((n)&_BV(b)))
+#define SET_BIT_TO(N,B,TF) do{ if (TF) SBI(N,B); else CBI(N,B); }while(0)
+
+#ifndef SBI
+  #define SBI(A,B) (A |= (1 << (B)))
+#endif
+
+#ifndef CBI
+  #define CBI(A,B) (A &= ~(1 << (B)))
+#endif
+
+#define TBI(N,B) (N ^= _BV(B))
+
+
+// Macros to chain up to 12 conditions
+#define _DO_1(W,C,A)       (_##W##_1(A))
+#define _DO_2(W,C,A,B)     (_##W##_1(A) C _##W##_1(B))
+#define _DO_3(W,C,A,V...)  (_##W##_1(A) C _DO_2(W,C,V))
+#define _DO_4(W,C,A,V...)  (_##W##_1(A) C _DO_3(W,C,V))
+#define _DO_5(W,C,A,V...)  (_##W##_1(A) C _DO_4(W,C,V))
+#define _DO_6(W,C,A,V...)  (_##W##_1(A) C _DO_5(W,C,V))
+#define _DO_7(W,C,A,V...)  (_##W##_1(A) C _DO_6(W,C,V))
+#define _DO_8(W,C,A,V...)  (_##W##_1(A) C _DO_7(W,C,V))
+#define _DO_9(W,C,A,V...)  (_##W##_1(A) C _DO_8(W,C,V))
+#define _DO_10(W,C,A,V...) (_##W##_1(A) C _DO_9(W,C,V))
+#define _DO_11(W,C,A,V...) (_##W##_1(A) C _DO_10(W,C,V))
+#define _DO_12(W,C,A,V...) (_##W##_1(A) C _DO_11(W,C,V))
+#define __DO_N(W,C,N,V...) _DO_##N(W,C,V)
+#define _DO_N(W,C,N,V...)  __DO_N(W,C,N,V)
+#define DO(W,C,V...)       _DO_N(W,C,NUM_ARGS(V),V)
+
+// Macros to support option testing
+#define _CAT(a,V...) a##V
+#define CAT(a,V...) _CAT(a,V)
+
+#define _ISENA_     ~,1
+#define _ISENA_1    ~,1
+#define _ISENA_0x1  ~,1
+#define _ISENA_true ~,1
+#define _ISENA(V...)        IS_PROBE(V)
+
+#define _ENA_1(O)           _ISENA(CAT(_IS,CAT(ENA_, O)))
+#define _DIS_1(O)           NOT(_ENA_1(O))
+#define ENABLED(V...)       DO(ENA,&&,V)
+#define DISABLED(V...)      DO(DIS,&&,V)
+
+#define TERN(O,A,B)         _TERN(_ENA_1(O),B,A)    // OPTION converted to '0' or '1'
+#define TERN0(O,A)          _TERN(_ENA_1(O),0,A)    // OPTION converted to A or '0'
+#define TERN1(O,A)          _TERN(_ENA_1(O),1,A)    // OPTION converted to A or '1'
+#define TERN_(O,A)          _TERN(_ENA_1(O),,A)     // OPTION converted to A or '<nul>'
+#define _TERN(E,V...)       __TERN(_CAT(T_,E),V)    // Prepend 'T_' to get 'T_0' or 'T_1'
+#define __TERN(T,V...)      ___TERN(_CAT(_NO,T),V)  // Prepend '_NO' to get '_NOT_0' or '_NOT_1'
+#define ___TERN(P,V...)     THIRD(P,V)              // If first argument has a comma, A. Else B.
+
+
+// Use NUM_ARGS(__VA_ARGS__) to get the number of variadic arguments
+#define _NUM_ARGS(_,Z,Y,X,W,V,U,T,S,R,Q,P,O,N,M,L,K,J,I,H,G,F,E,D,C,B,A,OUT,...) OUT
+#define NUM_ARGS(V...) _NUM_ARGS(0,V,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)
+
+//
+// Primitives supporting precompiler REPEAT
+//
+#define FIRST(a,...)     a
+#define SECOND(a,b,...)  b
+#define THIRD(a,b,c,...) c
+
+#define IS_PROBE(V...) SECOND(V, 0)     // Get the second item passed, or 0
+#define NOT(x) IS_PROBE(_CAT(_NOT_, x)) // NOT('0') gets '1'. Anything else gets '0'.
+
+#endif //MACROS_H

+ 49 - 15
Firmware/menu.cpp

@@ -8,13 +8,12 @@
 #include "lcd.h"
 #include "Configuration.h"
 #include "Marlin.h"
+#include "cmdqueue.h"
 #include "ultralcd.h"
 #include "language.h"
 #include "static_assert.h"
 #include "sound.h"
 
-extern int32_t lcd_encoder;
-
 #define MENU_DEPTH_MAX       7
 
 static menu_record_t menu_stack[MENU_DEPTH_MAX];
@@ -33,32 +32,35 @@ uint8_t menu_top = 0;
 
 uint8_t menu_clicked = 0;
 
-uint8_t menu_entering = 0;
 uint8_t menu_leaving = 0;
 
 menu_func_t menu_menu = 0;
 
 static_assert(sizeof(menu_data)>= sizeof(menu_data_edit_t),"menu_data_edit_t doesn't fit into menu_data");
 
+void menu_data_reset(void)
+{
+	// Resets the global shared C union.
+	// This ensures, that the menu entered will find out, that it shall initialize itself.
+	memset(&menu_data, 0, sizeof(menu_data));
+}
 
 void menu_goto(menu_func_t menu, const uint32_t encoder, const bool feedback, bool reset_menu_state)
 {
-	asm("cli");
+	CRITICAL_SECTION_START;
 	if (menu_menu != menu)
 	{
 		menu_menu = menu;
 		lcd_encoder = encoder;
-		asm("sei");
+		menu_top = 0; //reset menu view. Needed if menu_back() is called from deep inside a menu, such as Support
+		CRITICAL_SECTION_END;
 		if (reset_menu_state)
-		{
-			// Resets the global shared C union.
-			// This ensures, that the menu entered will find out, that it shall initialize itself.
-			memset(&menu_data, 0, sizeof(menu_data));
-		}
+			menu_data_reset();
+
 		if (feedback) lcd_quick_feedback();
 	}
 	else
-		asm("sei");
+		CRITICAL_SECTION_END;
 }
 
 void menu_start(void)
@@ -250,8 +252,7 @@ static void menu_draw_item_puts_P(char type_char, const char* str, char num)
     lcd_set_cursor(0, menu_row);
     lcd_printf_P(PSTR("%c%-.16S "), menu_selection_mark(), str);
     lcd_putc(num);
-    lcd_set_cursor(19, menu_row);
-    lcd_putc(type_char);
+    lcd_putc_at(19, menu_row, type_char);
 }
 
 /*
@@ -310,7 +311,7 @@ uint8_t menu_item_submenu_E(const Sheet &sheet, menu_func_t submenu)
     return 0;
 }
 
-uint8_t menu_item_function_E(const Sheet &sheet, menu_func_t func)
+uint8_t __attribute__((noinline)) menu_item_function_E(const Sheet &sheet, menu_func_t func)
 {
     if (menu_item == menu_line)
     {
@@ -344,6 +345,10 @@ uint8_t menu_item_back_P(const char* str)
 	return 0;
 }
 
+bool __attribute__((noinline)) menu_item_leave(){
+    return ((menu_item == menu_line) && menu_clicked && (lcd_encoder == menu_item)) || menu_leaving;
+}
+
 uint8_t menu_item_function_P(const char* str, menu_func_t func)
 {
 	if (menu_item == menu_line)
@@ -546,4 +551,33 @@ uint8_t menu_item_edit_P(const char* str, T pval, int16_t min_val, int16_t max_v
 template uint8_t menu_item_edit_P<int16_t*>(const char* str, int16_t *pval, int16_t min_val, int16_t max_val);
 template uint8_t menu_item_edit_P<uint8_t*>(const char* str, uint8_t *pval, int16_t min_val, int16_t max_val);
 
-#undef _menu_data
+static uint8_t progressbar_block_count = 0;
+static uint16_t progressbar_total = 0;
+void menu_progressbar_init(uint16_t total, const char* title)
+{
+	lcd_clear();
+	progressbar_block_count = 0;
+	progressbar_total = total;
+	
+	lcd_set_cursor(0, 1);
+	lcd_printf_P(PSTR("%-20.20S\n"), title);
+}
+
+void menu_progressbar_update(uint16_t newVal)
+{
+	uint8_t newCnt = (newVal * LCD_WIDTH) / progressbar_total;
+	if (newCnt > LCD_WIDTH)
+		newCnt = LCD_WIDTH;
+	while (newCnt > progressbar_block_count)
+	{
+		lcd_print('\xFF');
+		progressbar_block_count++;
+	}
+}
+
+void menu_progressbar_finish(void)
+{
+	progressbar_total = 1;
+	menu_progressbar_update(1);
+	_delay(300);
+}

+ 6 - 3
Firmware/menu.h

@@ -59,13 +59,12 @@ extern uint8_t menu_top;
 
 extern uint8_t menu_clicked;
 
-extern uint8_t menu_entering;
 extern uint8_t menu_leaving;
 
 //function pointer to the currently active menu
 extern menu_func_t menu_menu;
 
-
+extern void menu_data_reset(void);
 
 extern void menu_goto(menu_func_t menu, const uint32_t encoder, const bool feedback, bool reset_menu_state);
 
@@ -112,7 +111,8 @@ extern uint8_t menu_item_function_E(const Sheet &sheet, menu_func_t func);
 extern uint8_t menu_item_back_P(const char* str);
 
 // leaving menu - this condition must be immediately before MENU_ITEM_BACK_P
-#define ON_MENU_LEAVE(func) do { if (((menu_item == menu_line) && menu_clicked && (lcd_encoder == menu_item)) || menu_leaving){ func } } while (0)
+#define ON_MENU_LEAVE(func) do { if (menu_item_leave()){ func } } while (0)
+extern bool menu_item_leave();
 
 #define MENU_ITEM_FUNCTION_P(str, func) do { if (menu_item_function_P(str, func)) return; } while (0)
 extern uint8_t menu_item_function_P(const char* str, menu_func_t func);
@@ -150,5 +150,8 @@ extern void menu_format_sheet_E(const Sheet &sheet_E, SheetFormatBuffer &buffer)
 template <typename T>
 extern uint8_t menu_item_edit_P(const char* str, T pval, int16_t min_val, int16_t max_val);
 
+extern void menu_progressbar_init(uint16_t total, const char* title);
+extern void menu_progressbar_update(uint16_t newVal);
+extern void menu_progressbar_finish(void);
 
 #endif //_MENU_H

+ 65 - 60
Firmware/mesh_bed_calibration.cpp

@@ -1,4 +1,3 @@
-#include "Marlin.h"
 #include "Configuration.h"
 #include "ConfigurationStore.h"
 #include "language.h"
@@ -12,6 +11,8 @@
 #include "tmc2130.h"
 #endif //TMC2130
 
+#define DBG(args...) printf_P(args)
+
 uint8_t world2machine_correction_mode;
 float   world2machine_rotation_and_skew[2][2];
 float   world2machine_rotation_and_skew_inv[2][2];
@@ -369,7 +370,9 @@ BedSkewOffsetDetectionResultType calculate_machine_skew_and_offset_LS(
     BedSkewOffsetDetectionResultType result = BED_SKEW_OFFSET_DETECTION_PERFECT;
     {
         angleDiff = fabs(a2 - a1);
-		eeprom_update_float((float*)(EEPROM_XYZ_CAL_SKEW), angleDiff); //storing xyz cal. skew to be able to show in support menu later 
+        /// XY skew and Y-bed skew
+        DBG(_n("Measured skews: %f %f\n"), degrees(a2 - a1), degrees(a2));
+        eeprom_update_float((float *)(EEPROM_XYZ_CAL_SKEW), angleDiff); //storing xyz cal. skew to be able to show in support menu later
         if (angleDiff > bed_skew_angle_mild)
             result = (angleDiff > bed_skew_angle_extreme) ?
                 BED_SKEW_OFFSET_DETECTION_SKEW_EXTREME :
@@ -940,7 +943,7 @@ static inline void update_current_position_z()
 
 // At the current position, find the Z stop.
 
-inline bool find_bed_induction_sensor_point_z(float minimum_z, uint8_t n_iter, int
+bool find_bed_induction_sensor_point_z(float minimum_z, uint8_t n_iter, int
 #ifdef SUPPORT_VERBOSITY
     verbosity_level
 #endif //SUPPORT_VERBOSITY
@@ -990,7 +993,7 @@ inline bool find_bed_induction_sensor_point_z(float minimum_z, uint8_t n_iter, i
         // we have to let the planner know where we are right now as it is not where we said to go.
         update_current_position_z();
 		//printf_P(PSTR("Zs: %f, Z: %f, delta Z: %f"), z_bckp, current_position[Z_AXIS], (z_bckp - current_position[Z_AXIS]));
-		if (abs(current_position[Z_AXIS] - z_bckp) < 0.025) {
+		if (fabs(current_position[Z_AXIS] - z_bckp) < 0.025) {
 			//printf_P(PSTR("PINDA triggered immediately, move Z higher and repeat measurement\n")); 
 			current_position[Z_AXIS] += 0.5;
 			go_to_current(homing_feedrate[Z_AXIS]/60);
@@ -1016,7 +1019,7 @@ inline bool find_bed_induction_sensor_point_z(float minimum_z, uint8_t n_iter, i
 //        SERIAL_ECHOPGM("Bed find_bed_induction_sensor_point_z low, height: ");
 //        MYSERIAL.print(current_position[Z_AXIS], 5);
 //        SERIAL_ECHOLNPGM("");
-		float dz = i?abs(current_position[Z_AXIS] - (z / i)):0;
+		float dz = i?fabs(current_position[Z_AXIS] - (z / i)):0;
         z += current_position[Z_AXIS];
 		//printf_P(PSTR("Z[%d] = %d, dz=%d\n"), i, (int)(current_position[Z_AXIS] * 1000), (int)(dz * 1000));
 		//printf_P(PSTR("Z- measurement deviation from avg value %f um\n"), dz);
@@ -1061,7 +1064,7 @@ error:
 }
 
 #ifdef NEW_XYZCAL
-extern bool xyzcal_find_bed_induction_sensor_point_xy();
+BedSkewOffsetDetectionResultType xyzcal_find_bed_induction_sensor_point_xy();
 #endif //NEW_XYZCAL
 // Search around the current_position[X,Y],
 // look for the induction sensor response.
@@ -1077,7 +1080,7 @@ extern bool xyzcal_find_bed_induction_sensor_point_xy();
 #endif //HEATBED_V2
 
 #ifdef HEATBED_V2
-inline bool find_bed_induction_sensor_point_xy(int
+BedSkewOffsetDetectionResultType find_bed_induction_sensor_point_xy(int
 #if !defined (NEW_XYZCAL) && defined (SUPPORT_VERBOSITY)
         verbosity_level
 #endif
@@ -1133,7 +1136,7 @@ inline bool find_bed_induction_sensor_point_xy(int
 
 		//        go_xyz(current_position[X_AXIS], current_position[Y_AXIS], MESH_HOME_Z_SEARCH, homing_feedrate[Z_AXIS]/60);
 		go_xyz(x0, y0, current_position[Z_AXIS], feedrate);
-		// Continously lower the Z axis.
+		// Continuously lower the Z axis.
 		endstops_hit_on_purpose();
 		enable_z_endstop(true);
 		bool direction = false;
@@ -1331,7 +1334,7 @@ inline bool find_bed_induction_sensor_point_xy(int
 #endif //NEW_XYZCAL
 }
 #else //HEATBED_V2
-inline bool find_bed_induction_sensor_point_xy(int verbosity_level)
+BedSkewOffsetDetectionResultType find_bed_induction_sensor_point_xy(int verbosity_level)
 {
 #ifdef NEW_XYZCAL
 	return xyzcal_find_bed_induction_sensor_point_xy();
@@ -1380,7 +1383,7 @@ inline bool find_bed_induction_sensor_point_xy(int verbosity_level)
 
 		//        go_xyz(current_position[X_AXIS], current_position[Y_AXIS], MESH_HOME_Z_SEARCH, homing_feedrate[Z_AXIS]/60);
 		go_xyz(x0, y0, current_position[Z_AXIS], feedrate);
-		// Continously lower the Z axis.
+		// Continuously lower the Z axis.
 		endstops_hit_on_purpose();
 		enable_z_endstop(true);
 		while (current_position[Z_AXIS] > -10.f) {
@@ -1527,7 +1530,9 @@ inline bool find_bed_induction_sensor_point_xy(int verbosity_level)
 	}
 
 	enable_z_endstop(false);
-	return found;
+    if (found)
+        return BED_SKEW_OFFSET_DETECTION_POINT_FOUND;
+    return BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND;
 #endif //NEW_XYZCAL
 }
 
@@ -2226,25 +2231,26 @@ BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level
 		}
 		#endif // SUPPORT_VERBOSITY
 #ifdef MESH_BED_CALIBRATION_SHOW_LCD
-    uint8_t next_line;
-    lcd_display_message_fullscreen_P(_T(MSG_FIND_BED_OFFSET_AND_SKEW_LINE1), next_line);
-    if (next_line > 3)
-        next_line = 3;
+    lcd_display_message_fullscreen_P(_T(MSG_FIND_BED_OFFSET_AND_SKEW_LINE1));
 #endif /* MESH_BED_CALIBRATION_SHOW_LCD */
 
     // Collect the rear 2x3 points.
 	current_position[Z_AXIS] = MESH_HOME_Z_SEARCH + FIND_BED_INDUCTION_SENSOR_POINT_Z_STEP * iteration * 0.3;
-	for (int k = 0; k < 4; ++k) {
-		// Don't let the manage_inactivity() function remove power from the motors.
-		refresh_cmd_timeout();
+
+    /// Retry point scanning if a point with bad data appears.
+    /// Bad data could be cause by "cold" sensor.
+    /// This behavior vanishes after few point scans so retry will help.
+    for (int retries = 0; retries <= 1; ++retries) {
+        bool retry = false;
+        for (int k = 0; k < 4; ++k) {
+            // Don't let the manage_inactivity() function remove power from the motors.
+            refresh_cmd_timeout();
 #ifdef MESH_BED_CALIBRATION_SHOW_LCD
-		lcd_set_cursor(0, next_line);
-		lcd_print(k + 1);
-		lcd_puts_P(_T(MSG_FIND_BED_OFFSET_AND_SKEW_LINE2));
+		lcd_set_cursor(0, 3);
+		lcd_printf_P(PSTR("%d/4"),(k+1));
 
 		if (iteration > 0) {
-			lcd_puts_at_P(0, next_line + 1, _i("Iteration "));////MSG_FIND_BED_OFFSET_AND_SKEW_ITERATION c=20
-			lcd_print(int(iteration + 1));
+			lcd_printf_P(PSTR(" %S %d/1"),_T(MSG_ITERATION),int(iteration + 1));
 		}
 #endif /* MESH_BED_CALIBRATION_SHOW_LCD */
 		float *pt = pts + k * 2;
@@ -2271,7 +2277,7 @@ BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level
 		/*}
 		else {
 			// if first iteration failed, count corrected point coordinates as initial
-			// Use the coorrected coordinate, which is a result of find_bed_offset_and_skew().
+			// Use the corrected coordinate, which is a result of find_bed_offset_and_skew().
 			
 			current_position[X_AXIS] = vec_x[0] * pgm_read_float(bed_ref_points_4 + k * 2) + vec_y[0] * pgm_read_float(bed_ref_points_4 + k * 2 + 1) + cntr[0];
 			current_position[Y_AXIS] = vec_x[1] * pgm_read_float(bed_ref_points_4 + k * 2) + vec_y[1] * pgm_read_float(bed_ref_points_4 + k * 2 + 1) + cntr[1];
@@ -2300,8 +2306,19 @@ BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level
 		if (verbosity_level >= 10)
 			delay_keep_alive(3000);
 		#endif // SUPPORT_VERBOSITY
-		if (!find_bed_induction_sensor_point_xy(verbosity_level))
-			return BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND;
+
+        BedSkewOffsetDetectionResultType result;
+        result = find_bed_induction_sensor_point_xy(verbosity_level);
+        switch(result){
+            case BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND:
+                return BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND;
+            case BED_SKEW_OFFSET_DETECTION_POINT_SCAN_FAILED:
+                retry = true;
+                break;
+            default:
+                break;
+        }
+
 #ifndef NEW_XYZCAL
 #ifndef HEATBED_V2
 		
@@ -2375,8 +2392,12 @@ BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level
 				delay_keep_alive(3000);
 			}
 			#endif // SUPPORT_VERBOSITY
-		}
-		delay_keep_alive(0); //manage_heater, reset watchdog, manage inactivity
+        }
+        if (!retry)
+            break;
+    }
+        DBG(_n("All 4 calibration points found.\n"));
+        delay_keep_alive(0); //manage_heater, reset watchdog, manage inactivity
 		
 		#ifdef SUPPORT_VERBOSITY
 		if (verbosity_level >= 20) {
@@ -2386,7 +2407,7 @@ BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level
 				// Don't let the manage_inactivity() function remove power from the motors.
 				refresh_cmd_timeout();
 				// Go to the measurement point.
-				// Use the coorrected coordinate, which is a result of find_bed_offset_and_skew().
+				// Use the corrected coordinate, which is a result of find_bed_offset_and_skew().
 				current_position[X_AXIS] = pts[mesh_point * 2];
 				current_position[Y_AXIS] = pts[mesh_point * 2 + 1];
 				go_to_current(homing_feedrate[X_AXIS] / 60);
@@ -2406,6 +2427,7 @@ BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level
 		delay_keep_alive(0); //manage_heater, reset watchdog, manage inactivity
 		
 		if (result >= 0) {
+            DBG(_n("Calibration success.\n"));
 			world2machine_update(vec_x, vec_y, cntr);
 #if 1
 			// Fearlessly store the calibration values into the eeprom.
@@ -2450,7 +2472,7 @@ BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level
 					// Don't let the manage_inactivity() function remove power from the motors.
 					refresh_cmd_timeout();
 					// Go to the measurement point.
-					// Use the coorrected coordinate, which is a result of find_bed_offset_and_skew().
+					// Use the corrected coordinate, which is a result of find_bed_offset_and_skew().
 					uint8_t ix = mesh_point % MESH_MEAS_NUM_X_POINTS; // from 0 to MESH_NUM_X_POINTS - 1
 					uint8_t iy = mesh_point / MESH_MEAS_NUM_X_POINTS;
 					if (iy & 1) ix = (MESH_MEAS_NUM_X_POINTS - 1) - ix;
@@ -2462,9 +2484,12 @@ BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level
 			}
 			#endif // SUPPORT_VERBOSITY
 			return result;
-		}		
-		if (result == BED_SKEW_OFFSET_DETECTION_FITTING_FAILED && too_far_mask == 2) return result; //if fitting failed and front center point is out of reach, terminate calibration and inform user
-		iteration++;
+		}
+        if (result == BED_SKEW_OFFSET_DETECTION_FITTING_FAILED && too_far_mask == 2){
+            DBG(_n("Fitting failed => calibration failed.\n"));
+            return result; //if fitting failed and front center point is out of reach, terminate calibration and inform user
+        }
+        iteration++;
 	}
 	return result;    
 }
@@ -2503,10 +2528,7 @@ BedSkewOffsetDetectionResultType improve_bed_offset_and_skew(int8_t method, int8
     bool endstop_z_enabled = enable_z_endstop(false);
 
 #ifdef MESH_BED_CALIBRATION_SHOW_LCD
-    uint8_t next_line;
-    lcd_display_message_fullscreen_P(_i("Improving bed calibration point"), next_line);////MSG_IMPROVE_BED_OFFSET_AND_SKEW_LINE1 c=60
-    if (next_line > 3)
-        next_line = 3;
+    lcd_display_message_fullscreen_P(_i("Improving bed calibration point"));////MSG_IMPROVE_BED_OFFSET_AND_SKEW_LINE1 c=60
 #endif /* MESH_BED_CALIBRATION_SHOW_LCD */
 
     // Collect a matrix of 9x9 points.
@@ -2516,9 +2538,8 @@ BedSkewOffsetDetectionResultType improve_bed_offset_and_skew(int8_t method, int8
         refresh_cmd_timeout();
         // Print the decrasing ID of the measurement point.
 #ifdef MESH_BED_CALIBRATION_SHOW_LCD
-        lcd_set_cursor(0, next_line);
-		lcd_print(mesh_point+1);
-        lcd_puts_P(_T(MSG_FIND_BED_OFFSET_AND_SKEW_LINE2));////MSG_IMPROVE_BED_OFFSET_AND_SKEW_LINE2 c=14
+        lcd_set_cursor(0, 3);
+        lcd_printf_P(PSTR("%d/4"),mesh_point+1);
 #endif /* MESH_BED_CALIBRATION_SHOW_LCD */
 
         // Move up.
@@ -2816,14 +2837,9 @@ bool sample_mesh_and_store_reference()
     refresh_cmd_timeout();
 
 #ifdef MESH_BED_CALIBRATION_SHOW_LCD
-    uint8_t next_line;
-    lcd_display_message_fullscreen_P(_T(MSG_MEASURE_BED_REFERENCE_HEIGHT_LINE1), next_line);
-    if (next_line > 3)
-        next_line = 3;
+    lcd_display_message_fullscreen_P(_T(MSG_MEASURE_BED_REFERENCE_HEIGHT_LINE1));
     // display "point xx of yy"
-	lcd_set_cursor(0, next_line);
-    lcd_print(1);
-    lcd_puts_P(_T(MSG_MEASURE_BED_REFERENCE_HEIGHT_LINE2));
+    lcd_puts_at_P(0,3,_n("1/9"));
 #endif /* MESH_BED_CALIBRATION_SHOW_LCD */
 
     // Sample Z heights for the mesh bed leveling.
@@ -2871,9 +2887,8 @@ bool sample_mesh_and_store_reference()
         go_to_current(homing_feedrate[X_AXIS]/60);
 #ifdef MESH_BED_CALIBRATION_SHOW_LCD
         // display "point xx of yy"
-		lcd_set_cursor(0, next_line);
-        lcd_print(mesh_point+1);
-        lcd_puts_P(_T(MSG_MEASURE_BED_REFERENCE_HEIGHT_LINE2));
+		lcd_set_cursor(0, 3);
+        lcd_printf_P(PSTR("%d/9"),mesh_point+1);
 #endif /* MESH_BED_CALIBRATION_SHOW_LCD */
 		if (!find_bed_induction_sensor_point_z()) //Z crash or deviation > 50um
 		{
@@ -3021,8 +3036,6 @@ static void shift_z(float delta)
     plan_set_z_position(current_position[Z_AXIS]);
 }
 
-#define BABYSTEP_LOADZ_BY_PLANNER
-
 // Number of baby steps applied
 static int babystepLoadZ = 0;
 
@@ -3053,20 +3066,12 @@ void babystep_load()
 void babystep_apply()
 {
     babystep_load();
-#ifdef BABYSTEP_LOADZ_BY_PLANNER
     shift_z(- float(babystepLoadZ) / float(cs.axis_steps_per_unit[Z_AXIS]));
-#else
-    babystepsTodoZadd(babystepLoadZ);
-#endif /* BABYSTEP_LOADZ_BY_PLANNER */
 }
 
 void babystep_undo()
 {
-#ifdef BABYSTEP_LOADZ_BY_PLANNER
       shift_z(float(babystepLoadZ) / float(cs.axis_steps_per_unit[Z_AXIS]));
-#else
-      babystepsTodoZsubtract(babystepLoadZ);
-#endif /* BABYSTEP_LOADZ_BY_PLANNER */
       babystepLoadZ = 0;
 }
 

+ 9 - 8
Firmware/mesh_bed_calibration.h

@@ -1,5 +1,6 @@
-#ifndef MESH_BED_CALIBRATION_H
-#define MESH_BED_CALIBRATION_H
+#pragma once
+
+#include "Marlin.h"
 
 #define BED_ZERO_REF_X (- 22.f + X_PROBE_OFFSET_FROM_EXTRUDER) // -22 + 23 = 1
 #define BED_ZERO_REF_Y (- 0.6f + Y_PROBE_OFFSET_FROM_EXTRUDER + 4.f) // -0.6 + 5 + 4 = 8.4
@@ -145,11 +146,6 @@ inline bool world2machine_clamp(float &x, float &y)
         machine2world(tmpx, tmpy, x, y);
     return clamped;
 }
-
-extern bool find_bed_induction_sensor_point_z(float minimum_z = -10.f, uint8_t n_iter = 3, int verbosity_level = 0);
-extern bool find_bed_induction_sensor_point_xy(int verbosity_level = 0);
-extern void go_home_with_z_lift();
-
 /**
  * @brief Bed skew and offest detection result
  *
@@ -159,8 +155,10 @@ extern void go_home_with_z_lift();
 
 enum BedSkewOffsetDetectionResultType {
 	// Detection failed, some point was not found.
+	BED_SKEW_OFFSET_DETECTION_POINT_FOUND       =  0, //!< Point found
 	BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND   = -1, //!< Point not found.
 	BED_SKEW_OFFSET_DETECTION_FITTING_FAILED    = -2, //!< Fitting failed
+	BED_SKEW_OFFSET_DETECTION_POINT_SCAN_FAILED = -3, //!< Point scan failed, try again
 	
 	// Detection finished with success.
 	BED_SKEW_OFFSET_DETECTION_PERFECT 			= 0,  //!< Perfect.
@@ -168,6 +166,10 @@ enum BedSkewOffsetDetectionResultType {
 	BED_SKEW_OFFSET_DETECTION_SKEW_EXTREME		= 2   //!< Extremely skewed.
 };
 
+bool find_bed_induction_sensor_point_z(float minimum_z = -10.f, uint8_t n_iter = 3, int verbosity_level = 0);
+BedSkewOffsetDetectionResultType find_bed_induction_sensor_point_xy(int verbosity_level = 0);
+void go_home_with_z_lift();
+
 extern BedSkewOffsetDetectionResultType find_bed_offset_and_skew(int8_t verbosity_level, uint8_t &too_far_mask);
 #ifndef NEW_XYZCAL
 extern BedSkewOffsetDetectionResultType improve_bed_offset_and_skew(int8_t method, int8_t verbosity_level, uint8_t &too_far_mask);
@@ -213,4 +215,3 @@ extern void mbl_settings_init();
 
 extern bool mbl_point_measurement_valid(uint8_t ix, uint8_t iy, uint8_t meas_points, bool zigzag);
 extern void mbl_interpolation(uint8_t meas_points);
-#endif /* MESH_BED_CALIBRATION_H */

+ 104 - 76
Firmware/messages.c

@@ -8,126 +8,146 @@
 #include "Configuration_prusa.h"
 
 //internationalized messages
-const char MSG_AUTO_HOME[] PROGMEM_I1 = ISTR("Auto home"); ////
-const char MSG_BABYSTEP_Z[] PROGMEM_I1 = ISTR("Live adjust Z"); //// c=18
+const char MSG_AUTO_HOME[] PROGMEM_I1 = ISTR("Auto home"); ////c=18
+const char MSG_BABYSTEP_Z[] PROGMEM_I1 = ISTR("Live adjust Z"); ////c=18
 const char MSG_BABYSTEP_Z_NOT_SET[] PROGMEM_I1 = ISTR("Distance between tip of the nozzle and the bed surface has not been set yet. Please follow the manual, chapter First steps, section First layer calibration."); ////c=20 r=12
-const char MSG_BED[] PROGMEM_I1 = ISTR("Bed"); ////
-const char MSG_BED_DONE[] PROGMEM_I1 = ISTR("Bed done"); ////
-const char MSG_BED_HEATING[] PROGMEM_I1 = ISTR("Bed Heating"); ////
-const char MSG_BED_LEVELING_FAILED_POINT_LOW[] PROGMEM_I1 = ISTR("Bed leveling failed. Sensor didnt trigger. Debris on nozzle? Waiting for reset."); ////c=20 r=5
+const char MSG_BED[] PROGMEM_I1 = ISTR("Bed"); ////c=13
+const char MSG_BED_DONE[] PROGMEM_I1 = ISTR("Bed done"); ////c=20
+const char MSG_BED_HEATING[] PROGMEM_I1 = ISTR("Bed Heating"); ////c=20
+const char MSG_BED_LEVELING_FAILED_POINT_LOW[] PROGMEM_I1 = ISTR("Bed leveling failed. Sensor didn't trigger. Debris on nozzle? Waiting for reset."); ////c=20 r=6
 const char MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED[] PROGMEM_I1 = ISTR("XYZ calibration failed. Please consult the manual."); ////c=20 r=8
+const char MSG_BELT_STATUS[] PROGMEM_I1 = ISTR("Belt status");////c=18
+const char MSG_CANCEL[] PROGMEM_I1 = ISTR(">Cancel");////c=9
 const char MSG_CALIBRATE_Z_AUTO[] PROGMEM_I1 = ISTR("Calibrating Z"); ////c=20 r=2
-const char MSG_CARD_MENU[] PROGMEM_I1 = ISTR("Print from SD"); ////
+const char MSG_CARD_MENU[] PROGMEM_I1 = ISTR("Print from SD"); ////c=18
+const char MSG_CHECKING_X[] PROGMEM_I1 = ISTR("Checking X axis"); ////c=20
+const char MSG_CHECKING_Y[] PROGMEM_I1 = ISTR("Checking Y axis"); ////c=20
+const char MSG_COMMUNITY_MADE[] PROGMEM_I1 = ISTR("Community made"); ////c=18
 const char MSG_CONFIRM_NOZZLE_CLEAN[] PROGMEM_I1 = ISTR("Please clean the nozzle for calibration. Click when done."); ////c=20 r=8
-const char MSG_COOLDOWN[] PROGMEM_I1 = ISTR("Cooldown"); ////
-const char MSG_CRASH_DETECTED[] PROGMEM_I1 = ISTR("Crash detected."); ////c=20 r=1
+const char MSG_COOLDOWN[] PROGMEM_I1 = ISTR("Cooldown"); ////c=18
+const char MSG_CRASH[] PROGMEM_I1 = ISTR("Crash"); ////c=7
+const char MSG_CRASH_DETECTED[] PROGMEM_I1 = ISTR("Crash detected."); ////c=20
 const char MSG_CRASHDETECT[] PROGMEM_I1 = ISTR("Crash det."); ////c=13
-const char MSG_ERROR[] PROGMEM_I1 = ISTR("ERROR:"); ////
-const char MSG_EXTRUDER[] PROGMEM_I1 = ISTR("Extruder"); ////c=17 r=1
-const char MSG_FILAMENT[] PROGMEM_I1 = ISTR("Filament"); ////c=17 r=1
+const char MSG_ERROR[] PROGMEM_I1 = ISTR("ERROR:"); ////c=10
+const char MSG_EXTRUDER[] PROGMEM_I1 = ISTR("Extruder"); ////c=17
+const char MSG_FANS_CHECK[] PROGMEM_I1 = ISTR("Fans check"); ////c=13
+const char MSG_FIL_RUNOUTS[] PROGMEM_I1 = ISTR("Fil. runouts"); ////c=15
+const char MSG_FILAMENT[] PROGMEM_I1 = ISTR("Filament"); ////c=17
 const char MSG_FAN_SPEED[] PROGMEM_I1 = ISTR("Fan speed"); ////c=14
 const char MSG_FILAMENT_CLEAN[] PROGMEM_I1 = ISTR("Filament extruding & with correct color?"); ////c=20 r=2
+const char MSG_FILAMENT_LOADED[] PROGMEM_I1 = ISTR("Is filament loaded?"); ////c=20 r=2
 const char MSG_FILAMENT_LOADING_T0[] PROGMEM_I1 = ISTR("Insert filament into extruder 1. Click when done."); ////c=20 r=4
 const char MSG_FILAMENT_LOADING_T1[] PROGMEM_I1 = ISTR("Insert filament into extruder 2. Click when done."); ////c=20 r=4
 const char MSG_FILAMENT_LOADING_T2[] PROGMEM_I1 = ISTR("Insert filament into extruder 3. Click when done."); ////c=20 r=4
 const char MSG_FILAMENT_LOADING_T3[] PROGMEM_I1 = ISTR("Insert filament into extruder 4. Click when done."); ////c=20 r=4
-const char MSG_FILAMENTCHANGE[] PROGMEM_I1 = ISTR("Change filament"); ////
-const char MSG_FIND_BED_OFFSET_AND_SKEW_LINE1[] PROGMEM_I1 = ISTR("Searching bed calibration point"); ////c=60
-const char MSG_FIND_BED_OFFSET_AND_SKEW_LINE2[] PROGMEM_I1 = ISTR(" of 4"); ////c=14
+const char MSG_FILAMENTCHANGE[] PROGMEM_I1 = ISTR("Change filament"); ////c=18
+const char MSG_FIND_BED_OFFSET_AND_SKEW_LINE1[] PROGMEM_I1 = ISTR("Searching bed calibration point"); ////c=20 r=3
 const char MSG_FINISHING_MOVEMENTS[] PROGMEM_I1 = ISTR("Finishing movements"); ////c=20
 const char MSG_FOLLOW_CALIBRATION_FLOW[] PROGMEM_I1 = ISTR("Printer has not been calibrated yet. Please follow the manual, chapter First steps, section Calibration flow."); ////c=20 r=8
 const char MSG_FOLLOW_Z_CALIBRATION_FLOW[] PROGMEM_I1 = ISTR("There is still a need to make Z calibration. Please follow the manual, chapter First steps, section Calibration flow."); ////c=20 r=9
 const char MSG_FSENSOR_AUTOLOAD[] PROGMEM_I1 = ISTR("F. autoload"); ////c=13
-const char MSG_FSENSOR[] PROGMEM_I1 = ISTR("Fil. sensor"); ////
-const char MSG_HEATING[] PROGMEM_I1 = ISTR("Heating"); ////
+const char MSG_FSENSOR[] PROGMEM_I1 = ISTR("Fil. sensor"); ////c=12
+const char MSG_HEATING[] PROGMEM_I1 = ISTR("Heating"); ////c=20
 const char MSG_HEATING_COMPLETE[] PROGMEM_I1 = ISTR("Heating done."); ////c=20
-const char MSG_HOMEYZ[] PROGMEM_I1 = ISTR("Calibrate Z"); ////
-const char MSG_CHOOSE_EXTRUDER[] PROGMEM_I1 = ISTR("Choose extruder:"); ////c=20 r=1
-const char MSG_CHOOSE_FILAMENT[] PROGMEM_I1 = ISTR("Choose filament:"); ////c=20 r=1
+const char MSG_HOMEYZ[] PROGMEM_I1 = ISTR("Calibrate Z"); ////c=18
+const char MSG_ITERATION[] PROGMEM_I1 = ISTR("Iteration"); ////c=12
+const char MSG_CHOOSE_EXTRUDER[] PROGMEM_I1 = ISTR("Choose extruder:"); ////c=20
+const char MSG_CHOOSE_FILAMENT[] PROGMEM_I1 = ISTR("Choose filament:"); ////c=20
+const char MSG_LAST_PRINT[] PROGMEM_I1 = ISTR("Last print"); ////c=18
+const char MSG_LAST_PRINT_FAILURES[] PROGMEM_I1 = ISTR("Last print failures"); ////c=20
 const char MSG_LOAD_FILAMENT[] PROGMEM_I1 = ISTR("Load filament"); //// Number 1 to 5 is added behind text e.g. "Load filament 1" c=16
 const char MSG_LOADING_FILAMENT[] PROGMEM_I1 = ISTR("Loading filament"); ////c=20
 const char MSG_EJECT_FILAMENT[] PROGMEM_I1 = ISTR("Eject filament"); //// Number 1 to 5 is added behind text e.g. "Eject filament 1" c=16
 const char MSG_CUT_FILAMENT[] PROGMEM_I1 = ISTR("Cut filament"); //// Number 1 to 5 is added behind text e.g. "Cut filament 1" c=16
 const char MSG_M117_V2_CALIBRATION[] PROGMEM_I1 = ISTR("M117 First layer cal."); ////c=25
-const char MSG_MAIN[] PROGMEM_I1 = ISTR("Main"); ////
-const char MSG_BACK[] PROGMEM_I1 = ISTR("Back"); ////
+const char MSG_MAIN[] PROGMEM_I1 = ISTR("Main"); ////c=18
+const char MSG_BACK[] PROGMEM_I1 = ISTR("Back"); ////c=18
 const char MSG_SHEET[] PROGMEM_I1 = ISTR("Sheet"); ////c=10
-const char MSG_MEASURE_BED_REFERENCE_HEIGHT_LINE1[] PROGMEM_I1 = ISTR("Measuring reference height of calibration point"); ////c=60
-const char MSG_MEASURE_BED_REFERENCE_HEIGHT_LINE2[] PROGMEM_I1 = ISTR(" of 9"); ////c=14
-const char MSG_MENU_CALIBRATION[] PROGMEM_I1 = ISTR("Calibration"); ////
-const char MSG_NO[] PROGMEM_I1 = ISTR("No"); ////
-const char MSG_NOZZLE[] PROGMEM_I1 = ISTR("Nozzle"); ////
+const char MSG_STEEL_SHEETS[] PROGMEM_I1 = ISTR("Steel sheets"); ////c=18
+const char MSG_MEASURE_BED_REFERENCE_HEIGHT_LINE1[] PROGMEM_I1 = ISTR("Measuring reference height of calibration point"); ////c=20 r=3
+const char MSG_MENU_CALIBRATION[] PROGMEM_I1 = ISTR("Calibration"); ////c=18
+const char MSG_MMU_FAILS[] PROGMEM_I1 = ISTR("MMU fails"); ////c=15
+const char MSG_MMU_LOAD_FAILS[] PROGMEM_I1 = ISTR("MMU load fails"); ////c=15
+const char MSG_NO[] PROGMEM_I1 = ISTR("No"); ////c=4
+const char MSG_NOZZLE[] PROGMEM_I1 = ISTR("Nozzle"); ////c=12
 const char MSG_PAPER[] PROGMEM_I1 = ISTR("Place a sheet of paper under the nozzle during the calibration of first 4 points. If the nozzle catches the paper, power off the printer immediately."); ////c=20 r=10
-const char MSG_PLACE_STEEL_SHEET[] PROGMEM_I1 = ISTR("Please place steel sheet on heatbed."); ////c=20 r=4
+const char MSG_PAUSE_PRINT[] PROGMEM_I1 = ISTR("Pause print");////c=18
+const char MSG_PLACE_STEEL_SHEET[] PROGMEM_I1 = ISTR("Please place steel sheet on heatbed."); ////c=20 r=5
 const char MSG_PLEASE_WAIT[] PROGMEM_I1 = ISTR("Please wait"); ////c=20
+const char MSG_POWER_FAILURES[] PROGMEM_I1 = ISTR("Power failures"); ////c=15
 const char MSG_PREHEAT_NOZZLE[] PROGMEM_I1 = ISTR("Preheat the nozzle!"); ////c=20
 const char MSG_PRESS_TO_UNLOAD[] PROGMEM_I1 = ISTR("Please press the knob to unload filament"); ////c=20 r=4
 const char MSG_PRINT_ABORTED[] PROGMEM_I1 = ISTR("Print aborted"); ////c=20
 const char MSG_PULL_OUT_FILAMENT[] PROGMEM_I1 = ISTR("Please pull out filament immediately"); ////c=20 r=4
 const char MSG_RECOVER_PRINT[] PROGMEM_I1 = ISTR("Blackout occurred. Recover print?"); ////c=20 r=2
-const char MSG_REFRESH[] PROGMEM_I1 = ISTR("\xF8" "Refresh"); ////
-const char MSG_RESUMING_PRINT[] PROGMEM_I1 = ISTR("Resuming print"); ////
+const char MSG_REFRESH[] PROGMEM_I1 = ISTR("\x04Refresh"); ////c=18
 const char MSG_REMOVE_STEEL_SHEET[] PROGMEM_I1 = ISTR("Please remove steel sheet from heatbed."); ////c=20 r=4
+const char MSG_RESET[] PROGMEM_I1 = ISTR("Reset"); ////c=14
+const char MSG_RESUME_PRINT[] PROGMEM_I1 = ISTR("Resume print"); ////c=18
+const char MSG_RESUMING_PRINT[] PROGMEM_I1 = ISTR("Resuming print"); ////c=20
 const char MSG_SELFTEST_COOLING_FAN[] PROGMEM_I1 = ISTR("Front print fan?"); ////c=20
 const char MSG_SELFTEST_EXTRUDER_FAN[] PROGMEM_I1 = ISTR("Left hotend fan?"); ////c=20
-const char MSG_SELFTEST_FAILED[] PROGMEM_I1 = ISTR("Selftest failed  "); ////c=20
+const char MSG_SELFTEST_FAILED[] PROGMEM_I1 = ISTR("Selftest failed"); ////c=20
 const char MSG_SELFTEST_FAN[] PROGMEM_I1 = ISTR("Fan test"); ////c=20
 const char MSG_SELFTEST_FAN_NO[] PROGMEM_I1 = ISTR("Not spinning"); ////c=19
 const char MSG_SELFTEST_FAN_YES[] PROGMEM_I1 = ISTR("Spinning"); ////c=19
-const char MSG_SELFTEST_CHECK_BED[] PROGMEM_I1 = ISTR("Checking bed     "); ////c=20
-const char MSG_SELFTEST_CHECK_FSENSOR[] PROGMEM_I1 = ISTR("Checking sensors "); ////c=20
-const char MSG_SELFTEST_MOTOR[] PROGMEM_I1 = ISTR("Motor"); ////
+const char MSG_SELFTEST_CHECK_BED[] PROGMEM_I1 = ISTR("Checking bed"); ////c=20
+const char MSG_SELFTEST_CHECK_FSENSOR[] PROGMEM_I1 = ISTR("Checking sensors"); ////c=20
+const char MSG_SELFTEST_MOTOR[] PROGMEM_I1 = ISTR("Motor"); ////c=18
 const char MSG_SELFTEST_FILAMENT_SENSOR[] PROGMEM_I1 = ISTR("Filament sensor"); ////c=17
-const char MSG_SELFTEST_WIRINGERROR[] PROGMEM_I1 = ISTR("Wiring error"); ////
-const char MSG_SETTINGS[] PROGMEM_I1 = ISTR("Settings"); ////
-const char MSG_HW_SETUP[] PROGMEM_I1 = ISTR("HW Setup"); ////
-const char MSG_MODE[] PROGMEM_I1 = ISTR("Mode"); ////
-const char MSG_HIGH_POWER[] PROGMEM_I1 = ISTR("High power"); ////
-const char MSG_AUTO_POWER[] PROGMEM_I1 = ISTR("Auto power"); ////
-const char MSG_SILENT[] PROGMEM_I1 = ISTR("Silent"); ////
-const char MSG_NORMAL[] PROGMEM_I1 = ISTR("Normal"); ////
-const char MSG_STEALTH[] PROGMEM_I1 = ISTR("Stealth"); ////
+const char MSG_SELFTEST_WIRINGERROR[] PROGMEM_I1 = ISTR("Wiring error"); ////c=18
+const char MSG_SETTINGS[] PROGMEM_I1 = ISTR("Settings"); ////c=18
+const char MSG_TOTAL[] PROGMEM_I1 = ISTR("Total"); ////c=6
+const char MSG_TOTAL_FAILURES[] PROGMEM_I1 = ISTR("Total failures"); ////c=20
+const char MSG_HW_SETUP[] PROGMEM_I1 = ISTR("HW Setup"); ////c=18
+const char MSG_MODE[] PROGMEM_I1 = ISTR("Mode"); ////c=6
+const char MSG_HIGH_POWER[] PROGMEM_I1 = ISTR("High power"); ////c=10
+const char MSG_AUTO_POWER[] PROGMEM_I1 = ISTR("Auto power"); ////c=10
+const char MSG_SILENT[] PROGMEM_I1 = ISTR("Silent"); ////c=7
+const char MSG_NORMAL[] PROGMEM_I1 = ISTR("Normal"); ////c=7
+const char MSG_STEALTH[] PROGMEM_I1 = ISTR("Stealth"); ////c=7
 const char MSG_STEEL_SHEET_CHECK[] PROGMEM_I1 = ISTR("Is steel sheet on heatbed?"); ////c=20 r=2
-const char MSG_STOP_PRINT[] PROGMEM_I1 = ISTR("Stop print"); ////
-const char MSG_STOPPED[] PROGMEM_I1 = ISTR("STOPPED. "); ////
+const char MSG_STOP_PRINT[] PROGMEM_I1 = ISTR("Stop print"); ////c=18
+const char MSG_STOPPED[] PROGMEM_I1 = ISTR("STOPPED."); ////c=20
 const char MSG_TEMP_CALIBRATION[] PROGMEM_I1 = ISTR("Temp. cal."); ////c=14
 const char MSG_TEMP_CALIBRATION_DONE[] PROGMEM_I1 = ISTR("Temperature calibration is finished and active. Temp. calibration can be disabled in menu Settings->Temp. cal."); ////c=20 r=12
-const char MSG_UNLOAD_FILAMENT[] PROGMEM_I1 = ISTR("Unload filament"); ////c=17
-const char MSG_UNLOADING_FILAMENT[] PROGMEM_I1 = ISTR("Unloading filament"); ////c=20 r=1
-const char MSG_WATCH[] PROGMEM_I1 = ISTR("Info screen"); ////
+const char MSG_UNLOAD_FILAMENT[] PROGMEM_I1 = ISTR("Unload filament"); ////Number 1 to 5 is added behind text e.g. "Unload filament" c=16
+const char MSG_UNLOADING_FILAMENT[] PROGMEM_I1 = ISTR("Unloading filament"); ////c=20
+const char MSG_WATCH[] PROGMEM_I1 = ISTR("Info screen"); ////c=18
 const char MSG_WIZARD_CALIBRATION_FAILED[] PROGMEM_I1 = ISTR("Please check our handbook and fix the problem. Then resume the Wizard by rebooting the printer."); ////c=20 r=8
 const char MSG_WIZARD_DONE[] PROGMEM_I1 = ISTR("All is done. Happy printing!"); ////c=20 r=8
 const char MSG_WIZARD_HEATING[] PROGMEM_I1 = ISTR("Preheating nozzle. Please wait."); ////c=20 r=3
 const char MSG_WIZARD_QUIT[] PROGMEM_I1 = ISTR("You can always resume the Wizard from Calibration -> Wizard."); ////c=20 r=8
-const char MSG_YES[] PROGMEM_I1 = ISTR("Yes"); ////
+const char MSG_WIZARD_WELCOME[] PROGMEM_I1 = ISTR("Hi, I am your Original Prusa i3 printer. Would you like me to guide you through the setup process?"); //// c=20 r=7
+const char MSG_WIZARD_WELCOME_SHIPPING[] PROGMEM_I1 = ISTR("Hi, I am your Original Prusa i3 printer. I will guide you through a short setup process, in which the Z-axis will be calibrated. Then, you will be ready to print."); ////c=20 r=16
+const char MSG_YES[] PROGMEM_I1 = ISTR("Yes"); ////c=3
 const char MSG_V2_CALIBRATION[] PROGMEM_I1 = ISTR("First layer cal."); ////c=18
-const char WELCOME_MSG[] PROGMEM_I1 = ISTR(CUSTOM_MENDEL_NAME " OK."); ////c=20
-const char MSG_OFF[] PROGMEM_I1 = ISTR("Off"); ////
-const char MSG_ON[] PROGMEM_I1 = ISTR("On"); ////
-const char MSG_NA[] PROGMEM_I1 = ISTR("N/A"); ////
-const char MSG_AUTO_DEPLETE[] PROGMEM_I1 = ISTR("SpoolJoin"); ////
+const char MSG_OFF[] PROGMEM_I1 = ISTR("Off"); ////c=3
+const char MSG_ON[] PROGMEM_I1 = ISTR("On"); ////c=3
+const char MSG_NA[] PROGMEM_I1 = ISTR("N/A"); ////c=3
 const char MSG_CUTTER[] PROGMEM_I1 = ISTR("Cutter"); ////c=9
-const char MSG_NONE[] PROGMEM_I1 = ISTR("None"); ////
-const char MSG_WARN[] PROGMEM_I1 = ISTR("Warn"); ////
-const char MSG_STRICT[] PROGMEM_I1 = ISTR("Strict"); ////
-const char MSG_MODEL[] PROGMEM_I1 = ISTR("Model"); ////
-const char MSG_FIRMWARE[] PROGMEM_I1 = ISTR("Firmware"); ////
-const char MSG_GCODE[] PROGMEM_I1 = ISTR("Gcode"); ////
-const char MSG_NOZZLE_DIAMETER[] PROGMEM_I1 = ISTR("Nozzle d."); ////
-const char MSG_MMU_MODE[] PROGMEM_I1 = ISTR("MMU Mode"); ////
-const char MSG_SD_CARD[] PROGMEM_I1 = ISTR("SD card"); ////
-const char MSG_TOSHIBA_FLASH_AIR_COMPATIBILITY[] PROGMEM_I1 = ISTR("FlashAir"); ////
-const char MSG_SORT[] PROGMEM_I1 = ISTR("Sort"); ////
-const char MSG_SORT_TIME[] PROGMEM_I1 = ISTR("Time"); ////
-const char MSG_SORT_ALPHA[] PROGMEM_I1 = ISTR("Alphabet"); ////
-const char MSG_RPI_PORT[] PROGMEM_I1 = ISTR("RPi port"); ////
-const char MSG_SOUND[] PROGMEM_I1 = ISTR("Sound"); ////
-const char MSG_SOUND_LOUD[] PROGMEM_I1 = ISTR("Loud"); ////
-const char MSG_SOUND_ONCE[] PROGMEM_I1 = ISTR("Once"); ////
-const char MSG_SOUND_BLIND[] PROGMEM_I1 = ISTR("Assist"); ////
-const char MSG_MESH[] PROGMEM_I1 = ISTR("Mesh"); ////
-const char MSG_Z_PROBE_NR[] PROGMEM_I1 = ISTR("Z-probe nr."); ////
-const char MSG_MAGNETS_COMP[] PROGMEM_I1 = ISTR("Magnets comp."); ////
+const char MSG_NONE[] PROGMEM_I1 = ISTR("None"); ////c=8
+const char MSG_WARN[] PROGMEM_I1 = ISTR("Warn"); ////c=8
+const char MSG_STRICT[] PROGMEM_I1 = ISTR("Strict"); ////c=8
+const char MSG_MODEL[] PROGMEM_I1 = ISTR("Model"); ////c=8
+const char MSG_GCODE[] PROGMEM_I1 = ISTR("Gcode"); ////c=8
+const char MSG_GCODE_DIFF_PRINTER_CONTINUE[] PROGMEM_I1 = ISTR("G-code sliced for a different printer type. Continue?"); ////c=20 r=5
+const char MSG_GCODE_DIFF_PRINTER_CANCELLED[] PROGMEM_I1 =ISTR("G-code sliced for a different printer type. Please re-slice the model again. Print cancelled."); ////c=20 r=8
+const char MSG_NOZZLE_DIAMETER[] PROGMEM_I1 = ISTR("Nozzle d."); ////c=10
+const char MSG_MMU_MODE[] PROGMEM_I1 = ISTR("MMU Mode"); ////c=8
+const char MSG_SD_CARD[] PROGMEM_I1 = ISTR("SD card"); ////c=8
+const char MSG_SORT[] PROGMEM_I1 = ISTR("Sort"); ////c=7
+const char MSG_SORT_TIME[] PROGMEM_I1 = ISTR("Time"); ////c=8
+const char MSG_SORT_ALPHA[] PROGMEM_I1 = ISTR("Alphabet"); ////c=8
+const char MSG_RPI_PORT[] PROGMEM_I1 = ISTR("RPi port"); ////c=13
+const char MSG_SOUND[] PROGMEM_I1 = ISTR("Sound"); ////c=9
+const char MSG_SOUND_LOUD[] PROGMEM_I1 = ISTR("Loud"); ////c=7
+const char MSG_SOUND_ONCE[] PROGMEM_I1 = ISTR("Once"); ////c=7
+const char MSG_SOUND_BLIND[] PROGMEM_I1 = ISTR("Assist"); ////c=7
+const char MSG_MESH[] PROGMEM_I1 = ISTR("Mesh"); ////c=12
+const char MSG_MESH_BED_LEVELING[] PROGMEM_I1 = ISTR("Mesh Bed Leveling"); ////c=18
+const char MSG_Z_PROBE_NR[] PROGMEM_I1 = ISTR("Z-probe nr."); ////c=14
+const char MSG_MAGNETS_COMP[] PROGMEM_I1 = ISTR("Magnets comp."); ////c=13
 const char MSG_FS_ACTION[] PROGMEM_I1 = ISTR("FS Action"); ////c=10
 const char MSG_FS_CONTINUE[] PROGMEM_I1 = ISTR("Cont."); ////c=5
 const char MSG_FS_PAUSE[] PROGMEM_I1 = ISTR("Pause"); ////c=5
@@ -146,6 +166,11 @@ const char MSG_IR_UNKNOWN[] PROGMEM_I1 = ISTR("unknown state");////c=18
 #endif
 
 //not internationalized messages
+const char MSG_AUTO_DEPLETE[] PROGMEM_N1 = ISTR("SpoolJoin"); ////c=13
+const char MSG_FIRMWARE[] PROGMEM_N1 = ISTR("Firmware"); ////c=8
+const char MSG_TOSHIBA_FLASH_AIR_COMPATIBILITY[] PROGMEM_N1 = ISTR("FlashAir"); ////c=8
+const char MSG_PINDA[] PROGMEM_N1 = ISTR("PINDA");////c=5
+const char WELCOME_MSG[] PROGMEM_N1 = ISTR(CUSTOM_MENDEL_NAME " OK."); ////c=20
 const char MSG_SD_WORKDIR_FAIL[] PROGMEM_N1 = "workDir open failed"; ////
 const char MSG_BROWNOUT_RESET[] PROGMEM_N1 = " Brown out Reset"; ////
 const char MSG_EXTERNAL_RESET[] PROGMEM_N1 = " External Reset"; ////
@@ -168,7 +193,9 @@ const char MSG_ENDSTOP_OPEN[] PROGMEM_N1 = "open"; ////
 const char MSG_POWERUP[] PROGMEM_N1 = "PowerUp"; ////
 const char MSG_ERR_STOPPED[] PROGMEM_N1 = "Printer stopped due to errors. Fix the error and use M999 to restart. (Temperature is reset. Set it after restarting)"; ////
 const char MSG_ENDSTOP_HIT[] PROGMEM_N1 = "TRIGGERED"; ////
+const char MSG_OCTOPRINT_PAUSE[] PROGMEM_N1 = "// action:pause"; ////
 const char MSG_OCTOPRINT_PAUSED[] PROGMEM_N1 = "// action:paused"; ////
+const char MSG_OCTOPRINT_RESUME[] PROGMEM_N1 = "// action:resume"; ////
 const char MSG_OCTOPRINT_RESUMED[] PROGMEM_N1 = "// action:resumed"; ////
 const char MSG_OCTOPRINT_CANCEL[] PROGMEM_N1 = "// action:cancel"; ////
 const char MSG_FANCHECK_EXTRUDER[] PROGMEM_N1 = "Err: EXTR. FAN ERROR"; ////c=20
@@ -176,4 +203,5 @@ const char MSG_FANCHECK_PRINT[] PROGMEM_N1 = "Err: PRINT FAN ERROR"; ////c=20
 const char MSG_M112_KILL[] PROGMEM_N1 = "M112 called. Emergency Stop."; ////c=20
 const char MSG_ADVANCE_K[] PROGMEM_N1 = "Advance K:"; ////c=13
 const char MSG_POWERPANIC_DETECTED[] PROGMEM_N1 = "POWER PANIC DETECTED"; ////c=20
-
+const char MSG_LCD_STATUS_CHANGED[] PROGMEM_N1 = "LCD status changed";
+const char MSG_UNKNOWN_CODE[] PROGMEM_N1 = "Unknown %c code: %s\n";

+ 31 - 2
Firmware/messages.h

@@ -17,24 +17,32 @@ extern const char MSG_BED_DONE[];
 extern const char MSG_BED_HEATING[];
 extern const char MSG_BED_LEVELING_FAILED_POINT_LOW[];
 extern const char MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED[];
+extern const char MSG_BELT_STATUS[];
+extern const char MSG_CANCEL[];
 extern const char MSG_CALIBRATE_Z_AUTO[];
 extern const char MSG_CARD_MENU[];
+extern const char MSG_CHECKING_X[];
+extern const char MSG_CHECKING_Y[];
+extern const char MSG_COMMUNITY_MADE[];
 extern const char MSG_CONFIRM_NOZZLE_CLEAN[];
 extern const char MSG_COOLDOWN[];
+extern const char MSG_CRASH[];
 extern const char MSG_CRASH_DETECTED[];
 extern const char MSG_CRASHDETECT[];
 extern const char MSG_ERROR[];
 extern const char MSG_EXTRUDER[];
+extern const char MSG_FANS_CHECK[];
+extern const char MSG_FIL_RUNOUTS[];
 extern const char MSG_FILAMENT[];
 extern const char MSG_FAN_SPEED[];
 extern const char MSG_FILAMENT_CLEAN[];
+extern const char MSG_FILAMENT_LOADED[];
 extern const char MSG_FILAMENT_LOADING_T0[];
 extern const char MSG_FILAMENT_LOADING_T1[];
 extern const char MSG_FILAMENT_LOADING_T2[];
 extern const char MSG_FILAMENT_LOADING_T3[];
 extern const char MSG_FILAMENTCHANGE[];
 extern const char MSG_FIND_BED_OFFSET_AND_SKEW_LINE1[];
-extern const char MSG_FIND_BED_OFFSET_AND_SKEW_LINE2[];
 extern const char MSG_FINISHING_MOVEMENTS[];
 extern const char MSG_FOLLOW_CALIBRATION_FLOW[];
 extern const char MSG_FOLLOW_Z_CALIBRATION_FLOW[];
@@ -43,22 +51,30 @@ extern const char MSG_FSENSOR[];
 extern const char MSG_HEATING[];
 extern const char MSG_HEATING_COMPLETE[];
 extern const char MSG_HOMEYZ[];
+extern const char MSG_ITERATION[];
 extern const char MSG_CHOOSE_EXTRUDER[];
 extern const char MSG_CHOOSE_FILAMENT[];
+extern const char MSG_LAST_PRINT[];
+extern const char MSG_LAST_PRINT_FAILURES[];
 extern const char MSG_LOAD_FILAMENT[];
 extern const char MSG_LOADING_FILAMENT[];
 extern const char MSG_M117_V2_CALIBRATION[];
 extern const char MSG_MAIN[];
 extern const char MSG_BACK[];
 extern const char MSG_SHEET[];
+extern const char MSG_STEEL_SHEETS[];
 extern const char MSG_MEASURE_BED_REFERENCE_HEIGHT_LINE1[];
-extern const char MSG_MEASURE_BED_REFERENCE_HEIGHT_LINE2[];
 extern const char MSG_MENU_CALIBRATION[];
+extern const char MSG_MMU_FAILS[];
+extern const char MSG_MMU_LOAD_FAILS[];
 extern const char MSG_NO[];
 extern const char MSG_NOZZLE[];
 extern const char MSG_PAPER[];
+extern const char MSG_PAUSE_PRINT[];
+extern const char MSG_PINDA[];
 extern const char MSG_PLACE_STEEL_SHEET[];
 extern const char MSG_PLEASE_WAIT[];
+extern const char MSG_POWER_FAILURES[];
 extern const char MSG_PREHEAT_NOZZLE[];
 extern const char MSG_PRESS_TO_UNLOAD[];
 extern const char MSG_PRINT_ABORTED[];
@@ -66,6 +82,8 @@ extern const char MSG_PULL_OUT_FILAMENT[];
 extern const char MSG_RECOVER_PRINT[];
 extern const char MSG_REFRESH[];
 extern const char MSG_REMOVE_STEEL_SHEET[];
+extern const char MSG_RESET[];
+extern const char MSG_RESUME_PRINT[];
 extern const char MSG_RESUMING_PRINT[];
 extern const char MSG_SD_WORKDIR_FAIL[];
 extern const char MSG_SELFTEST_COOLING_FAN[];
@@ -80,6 +98,8 @@ extern const char MSG_SELFTEST_MOTOR[];
 extern const char MSG_SELFTEST_FILAMENT_SENSOR[];
 extern const char MSG_SELFTEST_WIRINGERROR[];
 extern const char MSG_SETTINGS[];
+extern const char MSG_TOTAL[];
+extern const char MSG_TOTAL_FAILURES[];
 extern const char MSG_HW_SETUP[];
 extern const char MSG_MODE[];
 extern const char MSG_HIGH_POWER[];
@@ -99,6 +119,8 @@ extern const char MSG_WIZARD_CALIBRATION_FAILED[];
 extern const char MSG_WIZARD_DONE[];
 extern const char MSG_WIZARD_HEATING[];
 extern const char MSG_WIZARD_QUIT[];
+extern const char MSG_WIZARD_WELCOME[];
+extern const char MSG_WIZARD_WELCOME_SHIPPING[];
 extern const char MSG_YES[];
 extern const char MSG_V2_CALIBRATION[];
 extern const char WELCOME_MSG[];
@@ -113,6 +135,8 @@ extern const char MSG_STRICT[];
 extern const char MSG_MODEL[];
 extern const char MSG_FIRMWARE[];
 extern const char MSG_GCODE[];
+extern const char MSG_GCODE_DIFF_PRINTER_CONTINUE[];
+extern const char MSG_GCODE_DIFF_PRINTER_CANCELLED[];
 extern const char MSG_NOZZLE_DIAMETER[];
 extern const char MSG_MMU_MODE[];
 extern const char MSG_SD_CARD[];
@@ -126,6 +150,7 @@ extern const char MSG_SOUND_LOUD[];
 extern const char MSG_SOUND_ONCE[];
 extern const char MSG_SOUND_BLIND[];
 extern const char MSG_MESH[];
+extern const char MSG_MESH_BED_LEVELING[];
 extern const char MSG_Z_PROBE_NR[];
 extern const char MSG_MAGNETS_COMP[];
 extern const char MSG_FS_ACTION[];
@@ -168,7 +193,9 @@ extern const char MSG_ERR_STOPPED[];
 extern const char MSG_ENDSTOP_HIT[];
 extern const char MSG_EJECT_FILAMENT[];
 extern const char MSG_CUT_FILAMENT[];
+extern const char MSG_OCTOPRINT_PAUSE[];
 extern const char MSG_OCTOPRINT_PAUSED[];
+extern const char MSG_OCTOPRINT_RESUME[];
 extern const char MSG_OCTOPRINT_RESUMED[];
 extern const char MSG_OCTOPRINT_CANCEL[];
 extern const char MSG_FANCHECK_EXTRUDER[];
@@ -176,6 +203,8 @@ extern const char MSG_FANCHECK_PRINT[];
 extern const char MSG_M112_KILL[];
 extern const char MSG_ADVANCE_K[];
 extern const char MSG_POWERPANIC_DETECTED[];
+extern const char MSG_LCD_STATUS_CHANGED[];
+extern const char MSG_UNKNOWN_CODE[];
 
 #if defined(__cplusplus)
 }

+ 44 - 45
Firmware/mmu.cpp

@@ -9,12 +9,15 @@
 #include "Configuration_prusa.h"
 #include "fsensor.h"
 #include "cardreader.h"
+#include "cmdqueue.h"
 #include "ultralcd.h"
+#include "menu.h"
 #include "sound.h"
 #include "printers.h"
 #include <avr/pgmspace.h>
-#include "io_atmega2560.h"
 #include "AutoDeplete.h"
+#include "fastio.h"
+#include "pins.h"
 //-//
 #include "util.h"
 
@@ -28,9 +31,6 @@
 #define MMU_P0_TIMEOUT 3000ul //timeout for P0 command: 3seconds
 #define MMU_MAX_RESEND_ATTEMPTS 2
 
-#ifdef MMU_HWRESET
-#define MMU_RST_PIN 76
-#endif //MMU_HWRESET
 
 namespace
 {
@@ -156,8 +156,8 @@ void mmu_init(void)
 	_delay_ms(10);                             //wait 10ms for sure
 	mmu_reset();                               //reset mmu (HW or SW), do not wait for response
 	mmu_state = S::Init;
-	PIN_INP(IR_SENSOR_PIN); //input mode
-	PIN_SET(IR_SENSOR_PIN); //pullup
+	SET_INPUT(IR_SENSOR_PIN); //input mode
+	WRITE(IR_SENSOR_PIN, 1); //pullup
 }
 
 //if IR_SENSOR defined, always returns true
@@ -170,7 +170,7 @@ bool check_for_ir_sensor()
 
 	bool detected = false;
 	//if IR_SENSOR_PIN input is low and pat9125sensor is not present we detected idler sensor
-	if ((PIN_GET(IR_SENSOR_PIN) == 0) 
+	if ((READ(IR_SENSOR_PIN) == 0) 
 #ifdef PAT9125
 		&& fsensor_not_responding
 #endif //PAT9125
@@ -363,7 +363,7 @@ void mmu_loop(void)
 	case S::GetFinda: //response to command P0
         if (mmu_idl_sens)
         {
-            if (PIN_GET(IR_SENSOR_PIN) == 0 && mmu_loading_flag)
+            if (READ(IR_SENSOR_PIN) == 0 && mmu_loading_flag)
             {
 #ifdef MMU_DEBUG
                 printf_P(PSTR("MMU <= 'A'\n"));
@@ -406,7 +406,7 @@ void mmu_loop(void)
 	case S::WaitCmd: //response to mmu commands
         if (mmu_idl_sens)
         {
-            if (PIN_GET(IR_SENSOR_PIN) == 0 && mmu_loading_flag)
+            if (READ(IR_SENSOR_PIN) == 0 && mmu_loading_flag)
             {
                 DEBUG_PRINTF_P(PSTR("MMU <= 'A'\n"));
                 mmu_puts_P(PSTR("A\n")); //send 'abort' request
@@ -568,11 +568,11 @@ bool can_extrude()
 static void get_response_print_info(uint8_t move) {
 	printf_P(PSTR("mmu_get_response - begin move: "), move);
 	switch (move) {
-		case MMU_LOAD_MOVE: printf_P(PSTR("load\n")); break;
-		case MMU_UNLOAD_MOVE: printf_P(PSTR("unload\n")); break;
-		case MMU_TCODE_MOVE: printf_P(PSTR("T-code\n")); break;
-		case MMU_NO_MOVE: printf_P(PSTR("no move\n")); break;
-		default: printf_P(PSTR("error: unknown move\n")); break;
+		case MMU_LOAD_MOVE: puts_P(PSTR("load")); break;
+		case MMU_UNLOAD_MOVE: puts_P(PSTR("unload")); break;
+		case MMU_TCODE_MOVE: puts_P(PSTR("T-code")); break;
+		case MMU_NO_MOVE: puts_P(PSTR("no move")); break;
+		default: puts_P(PSTR("error: unknown move")); break;
 	}
 }
 
@@ -596,14 +596,14 @@ bool mmu_get_response(uint8_t move)
 			    mmu_loading_flag = true;
 				if (can_extrude()) mmu_load_step();
 				//don't rely on "ok" signal from mmu unit; if filament detected by idler sensor during loading stop loading movements to prevent infinite loading
-				if (PIN_GET(IR_SENSOR_PIN) == 0) move = MMU_NO_MOVE;
+				if (READ(IR_SENSOR_PIN) == 0) move = MMU_NO_MOVE;
 				break;
 			case MMU_UNLOAD_MOVE:
-				if (PIN_GET(IR_SENSOR_PIN) == 0) //filament is still detected by idler sensor, printer helps with unlading 
+				if (READ(IR_SENSOR_PIN) == 0) //filament is still detected by idler sensor, printer helps with unlading 
 				{
 				    if (can_extrude())
 				    {
-                        printf_P(PSTR("Unload 1\n"));
+                        puts_P(PSTR("Unload 1"));
                         current_position[E_AXIS] = current_position[E_AXIS] - MMU_LOAD_FEEDRATE * MMU_LOAD_TIME_MS*0.001;
                         plan_buffer_line_curposXYZE(MMU_LOAD_FEEDRATE);
                         st_synchronize();
@@ -611,17 +611,17 @@ bool mmu_get_response(uint8_t move)
 				}
 				else //filament was unloaded from idler, no additional movements needed 
 				{ 
-					printf_P(PSTR("Unloading finished 1\n"));
+					puts_P(PSTR("Unloading finished 1"));
 					disable_e0(); //turn off E-stepper to prevent overheating and alow filament pull-out if necessary
 					move = MMU_NO_MOVE;
 				}
 				break;
 			case MMU_TCODE_MOVE: //first do unload and then continue with infinite loading movements
-				if (PIN_GET(IR_SENSOR_PIN) == 0) //filament detected by idler sensor, we must unload first 
+				if (READ(IR_SENSOR_PIN) == 0) //filament detected by idler sensor, we must unload first 
 				{
                     if (can_extrude())
                     {
-                        printf_P(PSTR("Unload 2\n"));
+                        puts_P(PSTR("Unload 2"));
                         current_position[E_AXIS] = current_position[E_AXIS] - MMU_LOAD_FEEDRATE * MMU_LOAD_TIME_MS*0.001;
                         plan_buffer_line_curposXYZE(MMU_LOAD_FEEDRATE);
                         st_synchronize();
@@ -629,7 +629,7 @@ bool mmu_get_response(uint8_t move)
 				}
 				else //delay to allow mmu unit to pull out filament from bondtech gears and then start with infinite loading 
 				{ 
-					printf_P(PSTR("Unloading finished 2\n"));
+					puts_P(PSTR("Unloading finished 2"));
 					disable_e0(); //turn off E-stepper to prevent overheating and alow filament pull-out if necessary
 					delay_keep_alive(MMU_LOAD_TIME_MS);
 					move = MMU_LOAD_MOVE;
@@ -690,7 +690,7 @@ void manage_response(bool move_axes, bool turn_off_nozzle, uint8_t move)
 				  }
 				  st_synchronize();
 				  mmu_print_saved = true;
-				  printf_P(PSTR("MMU not responding\n"));
+				  puts_P(PSTR("MMU not responding"));
 				  KEEPALIVE_STATE(PAUSED_FOR_USER);
 				  hotend_temp_bckp = degTargetHotend(active_extruder);
 				  if (move_axes) {
@@ -719,12 +719,12 @@ void manage_response(bool move_axes, bool turn_off_nozzle, uint8_t move)
 
 			  //first three lines are used for printing multiscreen message; last line contains measured and target nozzle temperature
 			  if (screen == 0) { //screen 0
-				  lcd_display_message_fullscreen_P(_i("MMU needs user attention."));
+				  lcd_display_message_fullscreen_P(_i("MMU needs user attention."));////MSG_MMU_USER_ATTENTION c=20 r=3
 				  screen++;
 			  }
 			  else {  //screen 1
-				  if((degTargetHotend(active_extruder) == 0) && turn_off_nozzle) lcd_display_message_fullscreen_P(_i("Press the knob to resume nozzle temperature."));
-				  else lcd_display_message_fullscreen_P(_i("Fix the issue and then press button on MMU unit."));
+				  if((degTargetHotend(active_extruder) == 0) && turn_off_nozzle) lcd_display_message_fullscreen_P(_i("Press the knob to resume nozzle temperature."));////MSG_RESUME_NOZZLE_TEMP c=20 r=4
+				  else lcd_display_message_fullscreen_P(_i("Fix the issue and then press button on MMU unit."));////MSG_MMU_FIX_ISSUE c=20 r=4
 				  screen=0;
 			  }
 
@@ -747,7 +747,7 @@ void manage_response(bool move_axes, bool turn_off_nozzle, uint8_t move)
 			  }
 		  }
 		  else if (mmu_print_saved) {
-			  printf_P(PSTR("MMU starts responding\n"));
+			  puts_P(PSTR("MMU starts responding"));
 			  KEEPALIVE_STATE(IN_HANDLER);
 			  mmu_loading_flag = false;
 			  if (turn_off_nozzle) 
@@ -879,8 +879,8 @@ void mmu_M600_load_filament(bool automatic, float nozzle_temp)
     }
     lcd_update_enable(false);
     lcd_clear();
-    lcd_set_cursor(0, 1); lcd_puts_P(_T(MSG_LOADING_FILAMENT));
-    lcd_print(" ");
+    lcd_puts_at_P(0, 1, _T(MSG_LOADING_FILAMENT));
+    lcd_print(' ');
     lcd_print(tmp_extruder + 1);
     snmm_filaments_used |= (1 << tmp_extruder); //for stop print
 
@@ -992,10 +992,10 @@ void extr_adj(uint8_t extruder) //loading filament for SNMM
 	
 	lcd_update_enable(false);
 	lcd_clear();
-	lcd_set_cursor(0, 1); lcd_puts_P(_T(MSG_LOADING_FILAMENT));
+	lcd_puts_at_P(0, 1, _T(MSG_LOADING_FILAMENT));
 	//if(strlen(_T(MSG_LOADING_FILAMENT))>18) lcd.setCursor(0, 1);
 	//else lcd.print(" ");
-	lcd_print(" ");
+	lcd_print(' ');
 	lcd_print(extruder + 1);
 
 	// get response
@@ -1035,7 +1035,7 @@ void extr_adj(uint8_t extruder) //loading filament for SNMM
 	lcd_clear();
 	lcd_set_cursor(0, 0); lcd_puts_P(_T(MSG_LOADING_FILAMENT));
 	if(strlen(_T(MSG_LOADING_FILAMENT))>18) lcd_set_cursor(0, 1);
-	else lcd_print(" ");
+	else lcd_print(' ');
 	lcd_print(mmu_extruder + 1);
 	lcd_set_cursor(0, 2); lcd_puts_P(_T(MSG_PLEASE_WAIT));
 	st_synchronize();
@@ -1082,9 +1082,9 @@ void mmu_filament_ramming()
 void extr_unload_view()
 {
     lcd_clear();
-    lcd_set_cursor(0, 1); lcd_puts_P(_T(MSG_UNLOADING_FILAMENT));
-    lcd_print(" ");
-    if (mmu_extruder == MMU_FILAMENT_UNKNOWN) lcd_print(" ");
+    lcd_puts_at_P(0, 1, _T(MSG_UNLOADING_FILAMENT));
+    lcd_print(' ');
+    if (mmu_extruder == MMU_FILAMENT_UNKNOWN) lcd_print(' ');
     else lcd_print(mmu_extruder + 1);
 }
 
@@ -1116,7 +1116,7 @@ void extr_unload()
 		lcd_display_message_fullscreen_P(PSTR(""));
 		max_feedrate[E_AXIS] = 50;
 		lcd_set_cursor(0, 0); lcd_puts_P(_T(MSG_UNLOADING_FILAMENT));
-		lcd_print(" ");
+		lcd_print(' ');
 		lcd_print(mmu_extruder + 1);
 		lcd_set_cursor(0, 2); lcd_puts_P(_T(MSG_PLEASE_WAIT));
 		if (current_position[Z_AXIS] < 15) {
@@ -1350,9 +1350,8 @@ void lcd_mmu_load_to_nozzle(uint8_t filament_nr)
         tmp_extruder = filament_nr;
         lcd_update_enable(false);
         lcd_clear();
-        lcd_set_cursor(0, 1);
-        lcd_puts_P(_T(MSG_LOADING_FILAMENT));
-        lcd_print(" ");
+        lcd_puts_at_P(0, 1, _T(MSG_LOADING_FILAMENT));
+        lcd_print(' ');
         lcd_print(tmp_extruder + 1);
         mmu_command(MmuCmd::T0 + tmp_extruder);
         manage_response(true, true, MMU_TCODE_MOVE);
@@ -1385,8 +1384,8 @@ void mmu_cut_filament(uint8_t filament_nr)
     {
         LcdUpdateDisabler disableLcdUpdate;
         lcd_clear();
-        lcd_set_cursor(0, 1); lcd_puts_P(_i("Cutting filament")); //// c=18
-        lcd_print(" ");
+        lcd_puts_at_P(0, 1, _i("Cutting filament")); ////MSG_MMU_CUTTING_FIL c=18
+        lcd_print(' ');
         lcd_print(filament_nr + 1);
         mmu_filament_ramming();
         mmu_command(MmuCmd::K0 + filament_nr);
@@ -1413,7 +1412,7 @@ bFilamentAction=false;                            // NOT in "mmu_fil_eject_menu(
 			{
 			    LcdUpdateDisabler disableLcdUpdate;
                 lcd_clear();
-                lcd_set_cursor(0, 1); lcd_puts_P(_i("Ejecting filament"));
+                lcd_puts_at_P(0, 1, _i("Ejecting filament"));
                 mmu_filament_ramming();
                 mmu_command(MmuCmd::E0 + filament);
                 manage_response(false, false, MMU_UNLOAD_MOVE);
@@ -1460,7 +1459,7 @@ static bool can_load()
         current_position[E_AXIS] -= e_increment;
         plan_buffer_line_curposXYZE(MMU_LOAD_FEEDRATE);
         st_synchronize();
-        if(0 == PIN_GET(IR_SENSOR_PIN))
+        if(0 == READ(IR_SENSOR_PIN))
         {
             ++filament_detected_count;
             DEBUG_PUTCHAR('O');
@@ -1491,7 +1490,7 @@ static bool load_more()
 {
     for (uint8_t i = 0; i < MMU_IDLER_SENSOR_ATTEMPTS_NR; i++)
     {
-        if (PIN_GET(IR_SENSOR_PIN) == 0) return true;
+        if (READ(IR_SENSOR_PIN) == 0) return true;
         DEBUG_PRINTF_P(PSTR("Additional load attempt nr. %d\n"), i);
         mmu_command(MmuCmd::C0);
         manage_response(true, true, MMU_LOAD_MOVE);
@@ -1557,7 +1556,7 @@ void mmu_continue_loading(bool blocking)
         {
         case Ls::Enter:
             increment_load_fail();
-            // no break
+            // FALLTHRU
         case Ls::Retry:
             ++retry; // overflow not handled, as it is not dangerous.
             if (retry >= max_retry)
@@ -1585,7 +1584,7 @@ void mmu_continue_loading(bool blocking)
             manage_response(false, true, MMU_UNLOAD_MOVE);
 
             setAllTargetHotends(0);
-            lcd_setstatuspgm(_i("MMU load failed     "));////c=20 r=1
+            lcd_setstatuspgm(_i("MMU load failed"));////MSG_MMU_LOAD_FAILED c=20
 
             if (blocking)
             {

+ 1 - 1
Firmware/motion_control.cpp

@@ -26,7 +26,7 @@
 // The arc is approximated by generating a huge number of tiny, linear segments. The length of each 
 // segment is configured in settings.mm_per_arc_segment.  
 void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8_t axis_1, 
-  uint8_t axis_linear, float feed_rate, float radius, uint8_t isclockwise, uint8_t extruder)
+  uint8_t axis_linear, float feed_rate, float radius, bool isclockwise, uint8_t extruder)
 {      
   //   int acceleration_manager_was_enabled = plan_is_acceleration_manager_enabled();
   //   plan_set_acceleration_manager_enabled(false); // disable acceleration management for the duration of the arc

+ 2 - 2
Firmware/motion_control.h

@@ -26,7 +26,7 @@
 // offset == offset from current xyz, axis_XXX defines circle plane in tool space, axis_linear is
 // the direction of helical travel, radius == circle radius, isclockwise boolean. Used
 // for vector transformation direction.
-void mc_arc(float *position, float *target, float *offset, unsigned char axis_0, unsigned char axis_1,
-  unsigned char axis_linear, float feed_rate, float radius, unsigned char isclockwise, uint8_t extruder);
+void mc_arc(float *position, float *target, float *offset, uint8_t axis_0, uint8_t axis_1,
+  uint8_t axis_linear, float feed_rate, float radius, bool isclockwise, uint8_t extruder);
   
 #endif

+ 0 - 6
Firmware/optiboot_w25x20cl.h

@@ -1,6 +0,0 @@
-#ifndef OPTIBOOT_W25X20CL_H
-#define OPTIBOOT_W25X20CL_H
-
-extern uint8_t optiboot_w25x20cl_enter();
-
-#endif /* OPTIBOOT_W25X20CL_H */

+ 49 - 62
Firmware/optiboot_w25x20cl.cpp

@@ -4,9 +4,10 @@
 // Licence GLP 2 or later.
 
 #include "Marlin.h"
-#include "w25x20cl.h"
+#include "xflash.h"
 #include "stk500.h"
 #include "bootapp.h"
+#include <avr/wdt.h>
 
 #define OPTIBOOT_MAJVER 6
 #define OPTIBOOT_CUSTOMVER 0
@@ -14,41 +15,17 @@
 static unsigned const int __attribute__((section(".version"))) 
   optiboot_version = 256*(OPTIBOOT_MAJVER + OPTIBOOT_CUSTOMVER) + OPTIBOOT_MINVER;
 
-/* Watchdog settings */
-#define WATCHDOG_OFF    (0)
-#define WATCHDOG_16MS   (_BV(WDE))
-#define WATCHDOG_32MS   (_BV(WDP0) | _BV(WDE))
-#define WATCHDOG_64MS   (_BV(WDP1) | _BV(WDE))
-#define WATCHDOG_125MS  (_BV(WDP1) | _BV(WDP0) | _BV(WDE))
-#define WATCHDOG_250MS  (_BV(WDP2) | _BV(WDE))
-#define WATCHDOG_500MS  (_BV(WDP2) | _BV(WDP0) | _BV(WDE))
-#define WATCHDOG_1S     (_BV(WDP2) | _BV(WDP1) | _BV(WDE))
-#define WATCHDOG_2S     (_BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE))
-#define WATCHDOG_4S     (_BV(WDP3) | _BV(WDE))
-#define WATCHDOG_8S     (_BV(WDP3) | _BV(WDP0) | _BV(WDE))
-
 #if 0
-#define W25X20CL_SIGNATURE_0 9
-#define W25X20CL_SIGNATURE_1 8
-#define W25X20CL_SIGNATURE_2 7
+#define XFLASH_SIGNATURE_0 9
+#define XFLASH_SIGNATURE_1 8
+#define XFLASH_SIGNATURE_2 7
 #else
 //FIXME this is a signature of ATmega2560!
-#define W25X20CL_SIGNATURE_0 0x1E
-#define W25X20CL_SIGNATURE_1 0x98
-#define W25X20CL_SIGNATURE_2 0x01
+#define XFLASH_SIGNATURE_0 0x1E
+#define XFLASH_SIGNATURE_1 0x98
+#define XFLASH_SIGNATURE_2 0x01
 #endif
 
-static void watchdogConfig(uint8_t x) {
-  WDTCSR = _BV(WDCE) | _BV(WDE);
-  WDTCSR = x;
-}
-
-static void watchdogReset() {
-  __asm__ __volatile__ (
-    "wdr\n"
-  );
-}
-
 #define RECV_READY ((UCSR0A & _BV(RXC0)) != 0)
 
 static uint8_t getch(void) {
@@ -63,7 +40,7 @@ static uint8_t getch(void) {
        * the application "soon", if it keeps happening.  (Note that we
        * don't care that an invalid char is returned...)
        */
-    watchdogReset();
+    wdt_reset();
   }
   ch = UDR0;
   return ch;
@@ -77,7 +54,7 @@ static void putch(char ch) {
 static void verifySpace() {
   if (getch() != CRC_EOP) {
     putch(STK_FAILED);
-    watchdogConfig(WATCHDOG_16MS);    // shorten WD timeout
+    wdt_enable(WDTO_15MS); // shorten WD timeout
     while (1)           // and busy-loop so that WD causes
       ;             //  a reset and app start.
   }
@@ -91,6 +68,8 @@ static void getNch(uint8_t count) {
 
 typedef uint16_t pagelen_t;
 
+//Thou shalt not change these messages, else the avrdude-slicer xflash implementation will no longer work and the language upload will fail.
+//Right now we support 2 xflash chips - the original w25x20cl and a new one GD25Q20C
 static const char entry_magic_send   [] PROGMEM = "start\n";
 static const char entry_magic_receive[] PROGMEM = "w25x20cl_enter\n";
 static const char entry_magic_cfm    [] PROGMEM = "w25x20cl_cfm\n";
@@ -101,9 +80,12 @@ extern struct block_t *block_buffer;
 //! @brief Enter an STK500 compatible Optiboot boot loader waiting for flashing the languages to an external flash memory.
 //! @return 1 if "start\n" was not sent. Optiboot was skipped
 //! @return 0 if "start\n" was sent. Optiboot ran normally. No need to send "start\n" in setup()
-uint8_t optiboot_w25x20cl_enter()
+uint8_t optiboot_xflash_enter()
 {
-  if (boot_app_flags & BOOT_APP_FLG_USER0) return 1;
+// Make sure to check boot_app_magic as well. Since these bootapp flags are located right in the middle of the stack,
+// they can be unintentionally changed. As a workaround to the language upload problem, do not only check for one bit if it's set,
+// but rather test 33 bits for the correct value before exiting optiboot early.
+  if ((boot_app_magic == BOOT_APP_MAGIC) && (boot_app_flags & BOOT_APP_FLG_USER0)) return 1;
   uint8_t ch;
   uint8_t rampz = 0;
   register uint16_t address = 0;
@@ -117,15 +99,13 @@ uint8_t optiboot_w25x20cl_enter()
   // Handshake sequence: Initialize the serial line, flush serial line, send magic, receive magic.
   // If the magic is not received on time, or it is not received correctly, continue to the application.
   {
-    watchdogReset();
-    unsigned long  boot_timeout = 2000000;
-    unsigned long  boot_timer = 0;
+    wdt_reset();
     const char    *ptr = entry_magic_send;
     const char    *end = strlen_P(entry_magic_send) + ptr;
     const uint8_t selectedSerialPort_bak = selectedSerialPort;
     // Flush the serial line.
     while (RECV_READY) {
-      watchdogReset();
+      wdt_reset();
       // Dummy register read (discard)
       (void)(*(char *)UDR0);
     }
@@ -135,17 +115,23 @@ uint8_t optiboot_w25x20cl_enter()
     // Send the initial magic string.
     while (ptr != end)
       putch(pgm_read_byte(ptr ++));
-    watchdogReset();
+    wdt_reset();
     // Wait for two seconds until a magic string (constant entry_magic) is received
     // from the serial line.
     ptr = entry_magic_receive;
     end = strlen_P(entry_magic_receive) + ptr;
     while (ptr != end) {
-      while (rx_buffer.head == SerialHead) {
-        watchdogReset();
-        delayMicroseconds(1);
-        if (++ boot_timer > boot_timeout)
-        {
+      unsigned long  boot_timer = 2000000;
+      // Beware of this volatile pointer - it is important since the while-cycle below
+      // doesn't contain any obvious references to rx_buffer.head
+      // thus the compiler is allowed to remove the check from the cycle
+      // i.e. rx_buffer.head == SerialHead would not be checked at all!
+      // With the volatile keyword the compiler generates exactly the same code as without it with only one difference:
+      // the last brne instruction jumps onto the (*rx_head == SerialHead) check and NOT onto the wdr instruction bypassing the check.
+      volatile int *rx_head = &rx_buffer.head;
+      while (*rx_head == SerialHead) {
+        wdt_reset();
+        if ( --boot_timer == 0) {
           // Timeout expired, continue with the application.
           selectedSerialPort = selectedSerialPort_bak; //revert Serial setting
           return 0;
@@ -159,18 +145,19 @@ uint8_t optiboot_w25x20cl_enter()
           selectedSerialPort = selectedSerialPort_bak; //revert Serial setting
           return 0;
       }
-      watchdogReset();
+      wdt_reset();
     }
     cbi(UCSR0B, RXCIE0); //disable the MarlinSerial0 interrupt
     // Send the cfm magic string.
     ptr = entry_magic_cfm;
+    end = strlen_P(entry_magic_cfm) + ptr;
     while (ptr != end)
       putch(pgm_read_byte(ptr ++));
   }
 
   spi_init();
-  w25x20cl_init();
-  watchdogConfig(WATCHDOG_OFF);
+  xflash_init();
+  wdt_disable();
 
   /* Forever loop: exits by causing WDT reset */
   for (;;) {
@@ -269,16 +256,16 @@ uint8_t optiboot_w25x20cl_enter()
         // During a single bootloader run, only erase a 64kB block once.
         // An 8bit bitmask 'pages_erased' covers 512kB of FLASH memory.
         if ((address == 0) && (pages_erased & (1 << (addr >> 16))) == 0) {
-          w25x20cl_wait_busy();
-          w25x20cl_enable_wr();
-          w25x20cl_block64_erase(addr);
+          xflash_wait_busy();
+          xflash_enable_wr();
+          xflash_block64_erase(addr);
           pages_erased |= (1 << (addr >> 16));
         }
-        w25x20cl_wait_busy();
-        w25x20cl_enable_wr();
-        w25x20cl_page_program(addr, buff, savelength);
-        w25x20cl_wait_busy();
-        w25x20cl_disable_wr();
+        xflash_wait_busy();
+        xflash_enable_wr();
+        xflash_page_program(addr, buff, savelength);
+        xflash_wait_busy();
+        xflash_disable_wr();
       }
     }
     /* Read memory block mode, length is big endian.  */
@@ -294,8 +281,8 @@ uint8_t optiboot_w25x20cl_enter()
       // Read the destination type. It should always be 'F' as flash. It is not checked.
       (void)getch();
       verifySpace();
-      w25x20cl_wait_busy();
-      w25x20cl_rd_data(addr, buff, length);
+      xflash_wait_busy();
+      xflash_rd_data(addr, buff, length);
       for (i = 0; i < length; ++ i)
         putch(buff[i]);
     }
@@ -303,13 +290,13 @@ uint8_t optiboot_w25x20cl_enter()
     else if(ch == STK_READ_SIGN) {
       // READ SIGN - return what Avrdude wants to hear
       verifySpace();
-      putch(W25X20CL_SIGNATURE_0);
-      putch(W25X20CL_SIGNATURE_1);
-      putch(W25X20CL_SIGNATURE_2);
+      putch(XFLASH_SIGNATURE_0);
+      putch(XFLASH_SIGNATURE_1);
+      putch(XFLASH_SIGNATURE_2);
     }
     else if (ch == STK_LEAVE_PROGMODE) { /* 'Q' */
       // Adaboot no-wait mod
-      watchdogConfig(WATCHDOG_16MS);
+      wdt_enable(WDTO_15MS);
       verifySpace();
     }
     else {

+ 6 - 0
Firmware/optiboot_xflash.h

@@ -0,0 +1,6 @@
+#ifndef OPTIBOOT_XFLASH_H
+#define OPTIBOOT_XFLASH_H
+
+extern uint8_t optiboot_xflash_enter();
+
+#endif /* OPTIBOOT_XFLASH_H */

+ 57 - 30
Firmware/pat9125.c

@@ -26,12 +26,15 @@
 #define PAT9125_BANK_SELECTION	0x7f
 
 
-#ifdef PAT9125_SWSPI
+#if defined(PAT9125_SWSPI)
 #include "swspi.h"
-#endif //PAT9125_SWSPI
-#ifdef PAT9125_SWI2C
+#elif defined(PAT9125_SWI2C)
 #include "swi2c.h"
-#endif //PAT9125_SWI2C
+#elif defined(PAT9125_I2C)
+#include "twi.h"
+#else
+#error unknown PAT9125 communication method
+#endif
 
 
 uint8_t pat9125_PID1 = 0;
@@ -103,16 +106,33 @@ extern FILE _uartout;
 #define uartout (&_uartout)
 
 
+uint8_t pat9125_probe()
+{
+#if defined(PAT9125_SWSPI)
+    swspi_init();
+  #error not implemented
+#elif defined(PAT9125_SWI2C)
+    swi2c_init();
+    return swi2c_readByte_A8(PAT9125_I2C_ADDR,0x00,NULL);
+#elif defined(PAT9125_I2C)
+    twi_init();
+  #ifdef IR_SENSOR
+    // NOTE: this is called from the MK3S variant, so it should be kept minimal
+    uint8_t data;
+    return (twi_r8(PAT9125_I2C_ADDR,PAT9125_PID1,&data) == 0);
+  #else
+    return (pat9125_rd_reg(PAT9125_PID1) != 0);
+  #endif
+#endif
+}
+
 uint8_t pat9125_init(void)
 {
-#ifdef PAT9125_SWSPI
-	swspi_init();
-#endif //PAT9125_SWSPI
-#ifdef PAT9125_SWI2C
-	swi2c_init();
-#endif //PAT9125_SWI2C
-	// Verify that the sensor responds with its correct product ID.
-	pat9125_PID1 = pat9125_rd_reg(PAT9125_PID1);
+    if (!pat9125_probe())
+        return 0;
+
+    // Verify that the sensor responds with its correct product ID.
+    pat9125_PID1 = pat9125_rd_reg(PAT9125_PID1);
 	pat9125_PID2 = pat9125_rd_reg(PAT9125_PID2);
 	if ((pat9125_PID1 != 0x31) || (pat9125_PID2 != 0x91))
 	{
@@ -234,39 +254,46 @@ uint8_t pat9125_update_bs(void)
 uint8_t pat9125_rd_reg(uint8_t addr)
 {
 	uint8_t data = 0;
-#ifdef PAT9125_SWSPI
+#if defined(PAT9125_SWSPI)
 	swspi_start();
 	swspi_tx(addr & 0x7f);
 	data = swspi_rx();
 	swspi_stop();
-#endif //PAT9125_SWSPI
-#ifdef PAT9125_SWI2C
+#elif defined(PAT9125_SWI2C)
 	if (!swi2c_readByte_A8(PAT9125_I2C_ADDR, addr, &data)) //NO ACK error
-	{
-		pat9125_PID1 = 0xff;
-		pat9125_PID2 = 0xff;
-		return 0;
-	}
-#endif //PAT9125_SWI2C
+        goto error;
+#elif defined(PAT9125_I2C)
+	if (twi_r8(PAT9125_I2C_ADDR,addr,&data))
+        goto error;
+#endif
 	return data;
+
+ error:
+    pat9125_PID1 = 0xff;
+    pat9125_PID2 = 0xff;
+    return 0;
 }
 
 void pat9125_wr_reg(uint8_t addr, uint8_t data)
 {
-#ifdef PAT9125_SWSPI
+#if defined(PAT9125_SWSPI)
 	swspi_start();
 	swspi_tx(addr | 0x80);
 	swspi_tx(data);
 	swspi_stop();
-#endif //PAT9125_SWSPI
-#ifdef PAT9125_SWI2C
+#elif defined(PAT9125_SWI2C)
 	if (!swi2c_writeByte_A8(PAT9125_I2C_ADDR, addr, &data)) //NO ACK error
-	{
-		pat9125_PID1 = 0xff;
-		pat9125_PID2 = 0xff;
-		return;
-	}
-#endif //PAT9125_SWI2C
+        goto error;
+#elif defined(PAT9125_I2C)
+	if (twi_w8(PAT9125_I2C_ADDR,addr,data))
+        goto error;
+#endif
+    return;
+
+ error:
+    pat9125_PID1 = 0xff;
+    pat9125_PID2 = 0xff;
+    return;
 }
 
 uint8_t pat9125_wr_reg_verify(uint8_t addr, uint8_t data)

+ 1 - 0
Firmware/pat9125.h

@@ -18,6 +18,7 @@ extern int16_t pat9125_y;
 extern uint8_t pat9125_b;
 extern uint8_t pat9125_s;
 
+extern uint8_t pat9125_probe(void);     // Return non-zero if PAT9125 can be trivially detected
 extern uint8_t pat9125_init(void);
 extern uint8_t pat9125_update(void);    // update all sensor data
 extern uint8_t pat9125_update_y(void);  // update _y only

+ 5 - 0
Firmware/pins.h

@@ -25,6 +25,11 @@
 #error Unknown MOTHERBOARD value in configuration.h
 #endif
 
+#if !defined(SDA_PIN) && (defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__))
+#define SDA_PIN 20
+#define SCL_PIN 21
+#endif
+
 //List of pins which to ignore when asked to change by gcode, 0 and 1 are RX and TX, do not mess with those!
 #define _E0_PINS E0_STEP_PIN, E0_DIR_PIN, E0_ENABLE_PIN, HEATER_0_PIN,
 #if EXTRUDERS > 1

+ 4 - 6
Firmware/pins_Einsy_1_0.h

@@ -15,14 +15,10 @@
 #define AMBIENT_THERMISTOR
 #define PINDA_THERMISTOR
 
-#define W25X20CL                 // external 256kB flash
+#define XFLASH                 // external 256kB flash
 #define BOOTAPP                  // bootloader support
 
-
-#define SWI2C_SDA      20 //SDA on P3
-#define SWI2C_SCL      21 //SCL on P3
-
-
+#define XFLASH_PIN_CS          32
 
 #define X_TMC2130_CS           41
 #define X_TMC2130_DIAG         64 // !!! changed from 40 (EINY03)
@@ -121,6 +117,8 @@
 
 #define IR_SENSOR_PIN 62 //idler sensor @PK0 (digital pin 62/A8)
 
+#define MMU_RST_PIN 76
+
 // Support for an 8 bit logic analyzer, for example the Saleae.
 // Channels 0-2 are fast, they could generate 2.667Mhz waveform with a software loop.
 #define LOGIC_ANALYZER_CH0		X_MIN_PIN		// PB6

+ 0 - 3
Firmware/pins_Rambo_1_3.h

@@ -11,9 +11,6 @@
 
 #define PINDA_THERMISTOR
 
-#define SWI2C_SDA      20 //SDA on P3
-#define SWI2C_SCL      21 //SCL on P3
-
 #ifdef MICROMETER_LOGGING
 #define D_DATACLOCK		24	//Y_MAX (green)
 #define D_DATA			30	//X_MAX (blue)

+ 16 - 15
Firmware/planner.cpp

@@ -109,9 +109,9 @@ unsigned char g_uc_extruder_last_move[3] = {0,0,0};
 //===========================================================================
 //=================semi-private variables, used in inline  functions    =====
 //===========================================================================
-block_t block_buffer[BLOCK_BUFFER_SIZE];            // A ring buffer for motion instfructions
-volatile unsigned char block_buffer_head;           // Index of the next block to be pushed
-volatile unsigned char block_buffer_tail;           // Index of the block to process now
+block_t block_buffer[BLOCK_BUFFER_SIZE];    // A ring buffer for motion instfructions
+volatile uint8_t block_buffer_head;         // Index of the next block to be pushed
+volatile uint8_t block_buffer_tail;         // Index of the block to process now
 
 #ifdef PLANNER_DIAGNOSTICS
 // Diagnostic function: Minimum number of planned moves since the last 
@@ -136,17 +136,17 @@ static bool plan_reset_next_e_sched;
 
 // Returns the index of the next block in the ring buffer
 // NOTE: Removed modulo (%) operator, which uses an expensive divide and multiplication.
-static inline int8_t next_block_index(int8_t block_index) {
+static inline uint8_t next_block_index(uint8_t block_index) {
   if (++ block_index == BLOCK_BUFFER_SIZE)
-    block_index = 0; 
+    block_index = 0;
   return block_index;
 }
 
 
 // Returns the index of the previous block in the ring buffer
-static inline int8_t prev_block_index(int8_t block_index) {
+static inline uint8_t prev_block_index(uint8_t block_index) {
   if (block_index == 0)
-    block_index = BLOCK_BUFFER_SIZE; 
+    block_index = BLOCK_BUFFER_SIZE;
   -- block_index;
   return block_index;
 }
@@ -358,14 +358,14 @@ void planner_recalculate(const float &safe_final_speed)
     // Reverse pass
     // Make a local copy of block_buffer_tail, because the interrupt can alter it
     // by consuming the blocks, therefore shortening the queue.
-    unsigned char tail = block_buffer_tail;
+    uint8_t tail = block_buffer_tail;
     uint8_t block_index;
     block_t *prev, *current, *next;
 
 //    SERIAL_ECHOLNPGM("planner_recalculate - 1");
 
     // At least three blocks are in the queue?
-    unsigned char n_blocks = (block_buffer_head + BLOCK_BUFFER_SIZE - tail) & (BLOCK_BUFFER_SIZE - 1);
+    uint8_t n_blocks = (block_buffer_head + BLOCK_BUFFER_SIZE - tail) & (BLOCK_BUFFER_SIZE - 1);
     if (n_blocks >= 3) {
         // Initialize the last tripple of blocks.
         block_index = prev_block_index(block_buffer_head);
@@ -709,7 +709,7 @@ float junction_deviation = 0.1;
 void plan_buffer_line(float x, float y, float z, const float &e, float feed_rate, uint8_t extruder, const float* gcode_target)
 {
     // Calculate the buffer head after we push this byte
-  int next_buffer_head = next_block_index(block_buffer_head);
+  uint8_t next_buffer_head = next_block_index(block_buffer_head);
 
   // If the buffer is full: good! That means we are well ahead of the robot. 
   // Rest here until there is room in the buffer.
@@ -1082,7 +1082,8 @@ Having the real displacement of the head, we can calculate the total movement le
   }
   else
   {
-    block->acceleration_st = ceil(cs.acceleration * steps_per_mm); // convert to: acceleration steps/sec^2
+    float acceleration = (block->steps_e.wide == 0? cs.travel_acceleration: cs.acceleration);
+    block->acceleration_st = ceil(acceleration * steps_per_mm); // convert to: acceleration steps/sec^2
 
     #ifdef LIN_ADVANCE
     /**
@@ -1094,7 +1095,7 @@ Having the real displacement of the head, we can calculate the total movement le
      */
     block->use_advance_lead = extruder_advance_K > 0
                               && delta_mm[E_AXIS] >= 0
-                              && abs(delta_mm[Z_AXIS]) < 0.5;
+                              && fabs(delta_mm[Z_AXIS]) < 0.5;
     if (block->use_advance_lead) {
 #ifdef LA_FLOWADJ
         // M221/FLOW should change uniformly the extrusion thickness
@@ -1474,7 +1475,7 @@ void update_mode_profile()
 }
 #endif //TMC2130
 
-unsigned char number_of_blocks()
+uint8_t number_of_blocks()
 {
 	return (block_buffer_head + BLOCK_BUFFER_SIZE - block_buffer_tail) & (BLOCK_BUFFER_SIZE - 1);
 }
@@ -1504,8 +1505,8 @@ void planner_add_sd_length(uint16_t sdlen)
 
 uint16_t planner_calc_sd_length()
 {
-	unsigned char _block_buffer_head = block_buffer_head;
-	unsigned char _block_buffer_tail = block_buffer_tail;
+	uint8_t _block_buffer_head = block_buffer_head;
+	uint8_t _block_buffer_tail = block_buffer_tail;
 	uint16_t sdlen = 0;
 	while (_block_buffer_head != _block_buffer_tail)
 	{

+ 9 - 5
Firmware/planner.h

@@ -202,16 +202,20 @@ extern uint8_t maxlimit_status;
     extern float autotemp_factor;
 #endif
 
-    
 
+// Check for BLOCK_BUFFER_SIZE requirements
+static_assert(!(BLOCK_BUFFER_SIZE & (BLOCK_BUFFER_SIZE - 1)),
+              "BLOCK_BUFFER_SIZE must be a power of two");
+static_assert(BLOCK_BUFFER_SIZE <= (UINT8_MAX>>1),
+              "BLOCK_BUFFER_SIZE too large for uint8_t");
 
 extern block_t block_buffer[BLOCK_BUFFER_SIZE];            // A ring buffer for motion instfructions
 // Index of the next block to be pushed into the planner queue.
-extern volatile unsigned char block_buffer_head;
+extern volatile uint8_t block_buffer_head;
 // Index of the first block in the planner queue.
 // This is the block, which is being currently processed by the stepper routine, 
 // or which is first to be processed by the stepper routine.
-extern volatile unsigned char block_buffer_tail; 
+extern volatile uint8_t block_buffer_tail;
 // Called when the current block is no longer needed. Discards the block and makes the memory
 // available for new blocks.    
 FORCE_INLINE void plan_discard_current_block()  
@@ -246,7 +250,7 @@ FORCE_INLINE uint8_t moves_planned() {
 }
 
 FORCE_INLINE bool planner_queue_full() {
-    unsigned char next_block_index = block_buffer_head;
+    uint8_t next_block_index = block_buffer_head;
     if (++ next_block_index == BLOCK_BUFFER_SIZE)
         next_block_index = 0; 
     return block_buffer_tail == next_block_index;
@@ -267,7 +271,7 @@ void reset_acceleration_rates();
 
 void update_mode_profile();
 
-unsigned char number_of_blocks();
+uint8_t number_of_blocks();
 
 // #define PLANNER_DIAGNOSTICS
 #ifdef PLANNER_DIAGNOSTICS

+ 45 - 1
Firmware/sm4.c

@@ -129,11 +129,15 @@ void sm4_set_dir_bits(uint8_t dir_bits)
 void sm4_do_step(uint8_t axes_mask)
 {
 #if ((MOTHERBOARD == BOARD_RAMBO_MINI_1_0) || (MOTHERBOARD == BOARD_RAMBO_MINI_1_3) || (MOTHERBOARD == BOARD_EINSY_1_0a))
+#ifdef TMC2130_DEDGE_STEPPING
+	PINC = (axes_mask & 0x0f); // toggle step signals by mask
+#else
     register uint8_t portC = PORTC & 0xf0;
 	PORTC = portC | (axes_mask & 0x0f); //set step signals by mask
 	asm("nop");
 	PORTC = portC; //set step signals to zero
 	asm("nop");
+#endif
 #endif //((MOTHERBOARD == BOARD_RAMBO_MINI_1_0) || (MOTHERBOARD == BOARD_RAMBO_MINI_1_3) || (MOTHERBOARD == BOARD_EINSY_1_0a))
 }
 
@@ -173,7 +177,7 @@ uint16_t sm4_line_xyze_ui(uint16_t dx, uint16_t dy, uint16_t dz, uint16_t de)
 		}
 		if (ce <= de)
 		{
-			sm |= 4;
+			sm |= 8;
 			ce += dd;
 			e++;
 		}
@@ -191,5 +195,45 @@ uint16_t sm4_line_xyze_ui(uint16_t dx, uint16_t dy, uint16_t dz, uint16_t de)
 	return nd;
 }
 
+uint16_t sm4_line_xyz_ui(uint16_t dx, uint16_t dy, uint16_t dz){
+	uint16_t dd = (uint16_t)(sqrt((float)(((uint32_t)dx)*dx + ((uint32_t)dy*dy) + ((uint32_t)dz*dz))) + 0.5);
+	uint16_t nd = dd;
+	uint16_t cx = dd;
+	uint16_t cy = dd;
+	uint16_t cz = dd;
+	uint16_t x = 0;
+	uint16_t y = 0;
+	uint16_t z = 0;
+	while (nd){
+		if (sm4_stop_cb && (*sm4_stop_cb)()) break;
+		uint8_t sm = 0; //step mask
+		if (cx <= dx){
+			sm |= 1;
+			cx += dd;
+			x++;
+		}
+		if (cy <= dy){
+			sm |= 2;
+			cy += dd;
+			y++;
+		}
+		if (cz <= dz){
+			sm |= 4;
+			cz += dd;
+			z++;
+		}
+		cx -= dx;
+		cy -= dy;
+		cz -= dz;
+		sm4_do_step(sm);
+		uint16_t delay = SM4_DEFDELAY;
+		if (sm4_calc_delay_cb) delay = (*sm4_calc_delay_cb)(nd, dd);
+		if (delay) delayMicroseconds(delay);
+		nd--;
+	}
+	if (sm4_update_pos_cb)
+		(*sm4_update_pos_cb)(x, y, z, 0);
+	return nd;
+}
 
 #endif //NEW_XYZCAL

+ 1 - 0
Firmware/sm4.h

@@ -48,6 +48,7 @@ extern void sm4_do_step(uint8_t axes_mask);
 
 // xyze linear-interpolated relative move, returns remaining diagonal steps (>0 means stoped)
 extern uint16_t sm4_line_xyze_ui(uint16_t dx, uint16_t dy, uint16_t dz, uint16_t de);
+extern uint16_t sm4_line_xyz_ui(uint16_t dx, uint16_t dy, uint16_t dz);
 
 
 #if defined(__cplusplus)

+ 1 - 1
Firmware/sound.cpp

@@ -129,7 +129,7 @@ switch(eSoundMode)
                Sound_DoSound_Encoder_Move();
           if(eSoundType==e_SOUND_TYPE_BlindAlert)
                Sound_DoSound_Blind_Alert();
-               break;
+          break;
      default:
           break;
      }

+ 0 - 1
Firmware/sound.h

@@ -19,7 +19,6 @@ extern eSOUND_MODE eSoundMode;
 
 extern void Sound_Init(void);
 extern void Sound_Default(void);
-extern void Sound_Save(void);
 extern void Sound_CycleState(void);
 extern void Sound_MakeSound(eSOUND_TYPE eSoundType);
 extern void Sound_MakeCustom(uint16_t ms,uint16_t tone_ ,bool critical);

+ 2 - 2
Firmware/speed_lookuptable.h

@@ -8,7 +8,7 @@ extern const uint16_t speed_lookuptable_slow[256][2] PROGMEM;
 
 #ifndef _NO_ASM
 
-// intRes = intIn1 * intIn2 >> 16
+// intRes = intIn1 * intIn2 >> 8
 // uses:
 // r26 to store 0
 // r27 to store the byte 1 of the 24 bit result
@@ -82,7 +82,7 @@ asm volatile ( \
 
 static inline void MultiU16X8toH16(uint16_t& intRes, uint8_t& charIn1, uint16_t& intIn2)
 {
-    intRes = ((uint32_t)charIn1 * (uint32_t)intIn2) >> 16;
+    intRes = ((uint32_t)charIn1 * (uint32_t)intIn2) >> 8;
 }
 
 static inline void MultiU24X24toH16(uint16_t& intRes, uint32_t& longIn1, uint32_t& longIn2)

+ 184 - 122
Firmware/stepper.cpp

@@ -48,6 +48,62 @@ int fsensor_counter; //counter for e-steps
 uint16_t SP_min = 0x21FF;
 #endif //DEBUG_STACK_MONITOR
 
+
+/*
+ * Stepping macros
+ */
+#define _STEP_PIN_X_AXIS X_STEP_PIN
+#define _STEP_PIN_Y_AXIS Y_STEP_PIN
+#define _STEP_PIN_Z_AXIS Z_STEP_PIN
+#define _STEP_PIN_E_AXIS E0_STEP_PIN
+
+#ifdef DEBUG_XSTEP_DUP_PIN
+#define _STEP_PIN_X_DUP_AXIS DEBUG_XSTEP_DUP_PIN
+#endif
+#ifdef DEBUG_YSTEP_DUP_PIN
+#define _STEP_PIN_Y_DUP_AXIS DEBUG_YSTEP_DUP_PIN
+#endif
+#ifdef Y_DUAL_STEPPER_DRIVERS
+#error Y_DUAL_STEPPER_DRIVERS not fully implemented
+#define _STEP_PIN_Y2_AXIS Y2_STEP_PIN
+#endif
+#ifdef Z_DUAL_STEPPER_DRIVERS
+#error Z_DUAL_STEPPER_DRIVERS not fully implemented
+#define _STEP_PIN_Z2_AXIS Z2_STEP_PIN
+#endif
+
+#ifdef TMC2130
+#define STEPPER_MINIMUM_PULSE TMC2130_MINIMUM_PULSE
+#define STEPPER_SET_DIR_DELAY TMC2130_SET_DIR_DELAY
+#define STEPPER_MINIMUM_DELAY TMC2130_MINIMUM_DELAY
+#else
+#define STEPPER_MINIMUM_PULSE 2
+#define STEPPER_SET_DIR_DELAY 100
+#define STEPPER_MINIMUM_DELAY delayMicroseconds(STEPPER_MINIMUM_PULSE)
+#endif
+
+#ifdef TMC2130_DEDGE_STEPPING
+static_assert(TMC2130_MINIMUM_DELAY 1, // this will fail to compile when non-empty
+              "DEDGE implies/requires an empty TMC2130_MINIMUM_DELAY");
+#define STEP_NC_HI(axis) TOGGLE(_STEP_PIN_##axis)
+#define STEP_NC_LO(axis) //NOP
+#else
+
+#define _STEP_HI_X_AXIS  !INVERT_X_STEP_PIN
+#define _STEP_LO_X_AXIS  INVERT_X_STEP_PIN
+#define _STEP_HI_Y_AXIS  !INVERT_Y_STEP_PIN
+#define _STEP_LO_Y_AXIS  INVERT_Y_STEP_PIN
+#define _STEP_HI_Z_AXIS  !INVERT_Z_STEP_PIN
+#define _STEP_LO_Z_AXIS  INVERT_Z_STEP_PIN
+#define _STEP_HI_E_AXIS  !INVERT_E_STEP_PIN
+#define _STEP_LO_E_AXIS  INVERT_E_STEP_PIN
+
+#define STEP_NC_HI(axis) WRITE_NC(_STEP_PIN_##axis, _STEP_HI_##axis)
+#define STEP_NC_LO(axis) WRITE_NC(_STEP_PIN_##axis, _STEP_LO_##axis)
+
+#endif //TMC2130_DEDGE_STEPPING
+
+
 //===========================================================================
 //=============================public variables  ============================
 //===========================================================================
@@ -296,13 +352,13 @@ FORCE_INLINE void stepper_next_block()
 				WRITE_NC(X_DIR_PIN, INVERT_X_DIR);
 			else
 				WRITE_NC(X_DIR_PIN, !INVERT_X_DIR);
-			_delay_us(100);
+			delayMicroseconds(STEPPER_SET_DIR_DELAY);
 			for (uint8_t i = 0; i < st_backlash_x; i++)
 			{
-				WRITE_NC(X_STEP_PIN, !INVERT_X_STEP_PIN);
-				_delay_us(100);
-				WRITE_NC(X_STEP_PIN, INVERT_X_STEP_PIN);
-				_delay_us(900);
+				STEP_NC_HI(X_AXIS);
+				STEPPER_MINIMUM_DELAY;
+				STEP_NC_LO(X_AXIS);
+				_delay_us(900); // hard-coded jerk! *bad*
 			}
 		}
 		last_dir_bits &= ~1;
@@ -319,13 +375,13 @@ FORCE_INLINE void stepper_next_block()
 				WRITE_NC(Y_DIR_PIN, INVERT_Y_DIR);
 			else
 				WRITE_NC(Y_DIR_PIN, !INVERT_Y_DIR);
-			_delay_us(100);
+			delayMicroseconds(STEPPER_SET_DIR_DELAY);
 			for (uint8_t i = 0; i < st_backlash_y; i++)
 			{
-				WRITE_NC(Y_STEP_PIN, !INVERT_Y_STEP_PIN);
-				_delay_us(100);
-				WRITE_NC(Y_STEP_PIN, INVERT_Y_STEP_PIN);
-				_delay_us(900);
+				STEP_NC_HI(Y_AXIS);
+				STEPPER_MINIMUM_DELAY;
+				STEP_NC_LO(Y_AXIS);
+				_delay_us(900); // hard-coded jerk! *bad*
 			}
 		}
 		last_dir_bits &= ~2;
@@ -603,44 +659,44 @@ FORCE_INLINE void stepper_tick_lowres()
     // Step in X axis
     counter_x.lo += current_block->steps_x.lo;
     if (counter_x.lo > 0) {
-      WRITE_NC(X_STEP_PIN, !INVERT_X_STEP_PIN);
+      STEP_NC_HI(X_AXIS);
 #ifdef DEBUG_XSTEP_DUP_PIN
-      WRITE_NC(DEBUG_XSTEP_DUP_PIN,!INVERT_X_STEP_PIN);
+      STEP_NC_HI(X_DUP_AXIS);
 #endif //DEBUG_XSTEP_DUP_PIN
       counter_x.lo -= current_block->step_event_count.lo;
       count_position[X_AXIS]+=count_direction[X_AXIS];
-      WRITE_NC(X_STEP_PIN, INVERT_X_STEP_PIN);
+      STEP_NC_LO(X_AXIS);
 #ifdef DEBUG_XSTEP_DUP_PIN
-      WRITE_NC(DEBUG_XSTEP_DUP_PIN,INVERT_X_STEP_PIN);
+      STEP_NC_LO(X_DUP_AXIS);
 #endif //DEBUG_XSTEP_DUP_PIN
     }
     // Step in Y axis
     counter_y.lo += current_block->steps_y.lo;
     if (counter_y.lo > 0) {
-      WRITE_NC(Y_STEP_PIN, !INVERT_Y_STEP_PIN);
+      STEP_NC_HI(Y_AXIS);
 #ifdef DEBUG_YSTEP_DUP_PIN
-      WRITE_NC(DEBUG_YSTEP_DUP_PIN,!INVERT_Y_STEP_PIN);
+      STEP_NC_HI(Y_DUP_AXIS);
 #endif //DEBUG_YSTEP_DUP_PIN
       counter_y.lo -= current_block->step_event_count.lo;
       count_position[Y_AXIS]+=count_direction[Y_AXIS];
-      WRITE_NC(Y_STEP_PIN, INVERT_Y_STEP_PIN);
+      STEP_NC_LO(Y_AXIS);
 #ifdef DEBUG_YSTEP_DUP_PIN
-      WRITE_NC(DEBUG_YSTEP_DUP_PIN,INVERT_Y_STEP_PIN);
+      STEP_NC_LO(Y_DUP_AXIS);
 #endif //DEBUG_YSTEP_DUP_PIN    
     }
     // Step in Z axis
     counter_z.lo += current_block->steps_z.lo;
     if (counter_z.lo > 0) {
-      WRITE_NC(Z_STEP_PIN, !INVERT_Z_STEP_PIN);
+      STEP_NC_HI(Z_AXIS);
       counter_z.lo -= current_block->step_event_count.lo;
       count_position[Z_AXIS]+=count_direction[Z_AXIS];
-      WRITE_NC(Z_STEP_PIN, INVERT_Z_STEP_PIN);
+      STEP_NC_LO(Z_AXIS);
     }
     // Step in E axis
     counter_e.lo += current_block->steps_e.lo;
     if (counter_e.lo > 0) {
 #ifndef LIN_ADVANCE
-      WRITE(E0_STEP_PIN, !INVERT_E_STEP_PIN);
+      STEP_NC_HI(E_AXIS);
 #endif /* LIN_ADVANCE */
       counter_e.lo -= current_block->step_event_count.lo;
       count_position[E_AXIS] += count_direction[E_AXIS];
@@ -650,7 +706,7 @@ FORCE_INLINE void stepper_tick_lowres()
 	#ifdef FILAMENT_SENSOR
 	  fsensor_counter += count_direction[E_AXIS];
 	#endif //FILAMENT_SENSOR
-      WRITE(E0_STEP_PIN, INVERT_E_STEP_PIN);
+      STEP_NC_LO(E_AXIS);
 #endif
     }
     if(++ step_events_completed.lo >= current_block->step_event_count.lo)
@@ -665,44 +721,44 @@ FORCE_INLINE void stepper_tick_highres()
     // Step in X axis
     counter_x.wide += current_block->steps_x.wide;
     if (counter_x.wide > 0) {
-      WRITE_NC(X_STEP_PIN, !INVERT_X_STEP_PIN);
+      STEP_NC_HI(X_AXIS);
 #ifdef DEBUG_XSTEP_DUP_PIN
-      WRITE_NC(DEBUG_XSTEP_DUP_PIN,!INVERT_X_STEP_PIN);
+      STEP_NC_HI(X_DUP_AXIS);
 #endif //DEBUG_XSTEP_DUP_PIN
       counter_x.wide -= current_block->step_event_count.wide;
       count_position[X_AXIS]+=count_direction[X_AXIS];   
-      WRITE_NC(X_STEP_PIN, INVERT_X_STEP_PIN);
+      STEP_NC_LO(X_AXIS);
 #ifdef DEBUG_XSTEP_DUP_PIN
-      WRITE_NC(DEBUG_XSTEP_DUP_PIN,INVERT_X_STEP_PIN);
+      STEP_NC_LO(X_DUP_AXIS);
 #endif //DEBUG_XSTEP_DUP_PIN
     }
     // Step in Y axis
     counter_y.wide += current_block->steps_y.wide;
     if (counter_y.wide > 0) {
-      WRITE_NC(Y_STEP_PIN, !INVERT_Y_STEP_PIN);
+      STEP_NC_HI(Y_AXIS);
 #ifdef DEBUG_YSTEP_DUP_PIN
-      WRITE_NC(DEBUG_YSTEP_DUP_PIN,!INVERT_Y_STEP_PIN);
+      STEP_NC_HI(Y_DUP_AXIS);
 #endif //DEBUG_YSTEP_DUP_PIN
       counter_y.wide -= current_block->step_event_count.wide;
       count_position[Y_AXIS]+=count_direction[Y_AXIS];
-      WRITE_NC(Y_STEP_PIN, INVERT_Y_STEP_PIN);
+      STEP_NC_LO(Y_AXIS);
 #ifdef DEBUG_YSTEP_DUP_PIN
-      WRITE_NC(DEBUG_YSTEP_DUP_PIN,INVERT_Y_STEP_PIN);
+      STEP_NC_LO(Y_DUP_AXIS);
 #endif //DEBUG_YSTEP_DUP_PIN    
     }
     // Step in Z axis
     counter_z.wide += current_block->steps_z.wide;
     if (counter_z.wide > 0) {
-      WRITE_NC(Z_STEP_PIN, !INVERT_Z_STEP_PIN);
+      STEP_NC_HI(Z_AXIS);
       counter_z.wide -= current_block->step_event_count.wide;
       count_position[Z_AXIS]+=count_direction[Z_AXIS];
-      WRITE_NC(Z_STEP_PIN, INVERT_Z_STEP_PIN);
+      STEP_NC_LO(Z_AXIS);
     }
     // Step in E axis
     counter_e.wide += current_block->steps_e.wide;
     if (counter_e.wide > 0) {
 #ifndef LIN_ADVANCE
-      WRITE(E0_STEP_PIN, !INVERT_E_STEP_PIN);
+      STEP_NC_HI(E_AXIS);
 #endif /* LIN_ADVANCE */
       counter_e.wide -= current_block->step_event_count.wide;
       count_position[E_AXIS]+=count_direction[E_AXIS];
@@ -712,7 +768,7 @@ FORCE_INLINE void stepper_tick_highres()
     #ifdef FILAMENT_SENSOR
       fsensor_counter += count_direction[E_AXIS];
     #endif //FILAMENT_SENSOR
-      WRITE(E0_STEP_PIN, INVERT_E_STEP_PIN);
+      STEP_NC_LO(E_AXIS);
 #endif
     }
     if(++ step_events_completed.wide >= current_block->step_event_count.wide)
@@ -1014,9 +1070,9 @@ FORCE_INLINE void advance_isr_scheduler() {
         bool rev = (e_steps < 0);
         do
         {
-            WRITE_NC(E0_STEP_PIN, !INVERT_E_STEP_PIN);
+            STEP_NC_HI(E_AXIS);
             e_steps += (rev? 1: -1);
-            WRITE_NC(E0_STEP_PIN, INVERT_E_STEP_PIN);
+            STEP_NC_LO(E_AXIS);
 #if defined(FILAMENT_SENSOR) && defined(PAT9125)
             fsensor_counter += (rev? -1: 1);
 #endif
@@ -1044,7 +1100,7 @@ FORCE_INLINE void advance_isr_scheduler() {
 void st_init()
 {
 #ifdef TMC2130
-	tmc2130_init();
+	tmc2130_init(TMCInitParams(false, FarmOrUserECool()));
 #endif //TMC2130
 
   st_current_init(); //Initialize Digipot Motor Current
@@ -1389,89 +1445,106 @@ void quickStop()
 #ifdef BABYSTEPPING
 void babystep(const uint8_t axis,const bool direction)
 {
-  //MUST ONLY BE CALLED BY A ISR, it depends on that no other ISR interrupts this
-    //store initial pin states
-  switch(axis)
-  {
-  case X_AXIS:
-  {
-    enable_x();   
-    uint8_t old_x_dir_pin= READ(X_DIR_PIN);  //if dualzstepper, both point to same direction.
-   
-    //setup new step
-    WRITE(X_DIR_PIN,(INVERT_X_DIR)^direction);
-    
-    //perform step 
-    WRITE(X_STEP_PIN, !INVERT_X_STEP_PIN); 
+    // MUST ONLY BE CALLED BY A ISR as stepper pins are manipulated directly.
+    // note: when switching direction no delay is inserted at the end when the
+    //       original is restored. We assume enough time passes as the function
+    //       returns and the stepper is manipulated again (to avoid dead times)
+    switch(axis)
+    {
+    case X_AXIS:
+    {
+        enable_x();
+        uint8_t old_x_dir_pin = READ(X_DIR_PIN);  //if dualzstepper, both point to same direction.
+        uint8_t new_x_dir_pin = (INVERT_X_DIR)^direction;
+
+        //setup new step
+        if (new_x_dir_pin != old_x_dir_pin) {
+            WRITE_NC(X_DIR_PIN, new_x_dir_pin);
+            delayMicroseconds(STEPPER_SET_DIR_DELAY);
+        }
+
+        //perform step
+        STEP_NC_HI(X_AXIS);
 #ifdef DEBUG_XSTEP_DUP_PIN
-    WRITE(DEBUG_XSTEP_DUP_PIN,!INVERT_X_STEP_PIN);
-#endif //DEBUG_XSTEP_DUP_PIN
-    delayMicroseconds(1);
-    WRITE(X_STEP_PIN, INVERT_X_STEP_PIN);
+        STEP_NC_HI(X_DUP_AXIS);
+#endif
+        STEPPER_MINIMUM_DELAY;
+        STEP_NC_LO(X_AXIS);
 #ifdef DEBUG_XSTEP_DUP_PIN
-    WRITE(DEBUG_XSTEP_DUP_PIN,INVERT_X_STEP_PIN);
-#endif //DEBUG_XSTEP_DUP_PIN
+        STEP_NC_LO(X_DUP_AXIS);
+#endif
 
-    //get old pin state back.
-    WRITE(X_DIR_PIN,old_x_dir_pin);
-  }
-  break;
-  case Y_AXIS:
-  {
-    enable_y();   
-    uint8_t old_y_dir_pin= READ(Y_DIR_PIN);  //if dualzstepper, both point to same direction.
-   
-    //setup new step
-    WRITE(Y_DIR_PIN,(INVERT_Y_DIR)^direction);
-    
-    //perform step 
-    WRITE(Y_STEP_PIN, !INVERT_Y_STEP_PIN); 
+        //get old pin state back.
+        WRITE_NC(X_DIR_PIN, old_x_dir_pin);
+    }
+    break;
+
+    case Y_AXIS:
+    {
+        enable_y();
+        uint8_t old_y_dir_pin = READ(Y_DIR_PIN);  //if dualzstepper, both point to same direction.
+        uint8_t new_y_dir_pin = (INVERT_Y_DIR)^direction;
+
+        //setup new step
+        if (new_y_dir_pin != old_y_dir_pin) {
+            WRITE_NC(Y_DIR_PIN, new_y_dir_pin);
+            delayMicroseconds(STEPPER_SET_DIR_DELAY);
+        }
+
+        //perform step
+        STEP_NC_HI(Y_AXIS);
 #ifdef DEBUG_YSTEP_DUP_PIN
-    WRITE(DEBUG_YSTEP_DUP_PIN,!INVERT_Y_STEP_PIN);
-#endif //DEBUG_YSTEP_DUP_PIN
-    delayMicroseconds(1);
-    WRITE(Y_STEP_PIN, INVERT_Y_STEP_PIN);
+        STEP_NC_HI(Y_DUP_AXIS);
+#endif
+        STEPPER_MINIMUM_DELAY;
+        STEP_NC_LO(Y_AXIS);
 #ifdef DEBUG_YSTEP_DUP_PIN
-    WRITE(DEBUG_YSTEP_DUP_PIN,INVERT_Y_STEP_PIN);
-#endif //DEBUG_YSTEP_DUP_PIN
+        STEP_NC_LO(Y_DUP_AXIS);
+#endif
 
-    //get old pin state back.
-    WRITE(Y_DIR_PIN,old_y_dir_pin);
+        //get old pin state back.
+        WRITE_NC(Y_DIR_PIN, old_y_dir_pin);
+    }
+    break;
 
-  }
-  break;
- 
-  case Z_AXIS:
-  {
-    enable_z();
-    uint8_t old_z_dir_pin= READ(Z_DIR_PIN);  //if dualzstepper, both point to same direction.
-    //setup new step
-    WRITE(Z_DIR_PIN,(INVERT_Z_DIR)^direction^BABYSTEP_INVERT_Z);
-    #ifdef Z_DUAL_STEPPER_DRIVERS
-      WRITE(Z2_DIR_PIN,(INVERT_Z_DIR)^direction^BABYSTEP_INVERT_Z);
-    #endif
-    //perform step 
-    WRITE(Z_STEP_PIN, !INVERT_Z_STEP_PIN); 
-    #ifdef Z_DUAL_STEPPER_DRIVERS
-      WRITE(Z2_STEP_PIN, !INVERT_Z_STEP_PIN);
-    #endif
-    delayMicroseconds(1);
-    WRITE(Z_STEP_PIN, INVERT_Z_STEP_PIN);
-    #ifdef Z_DUAL_STEPPER_DRIVERS
-      WRITE(Z2_STEP_PIN, INVERT_Z_STEP_PIN);
-    #endif
+    case Z_AXIS:
+    {
+        enable_z();
+        uint8_t old_z_dir_pin = READ(Z_DIR_PIN);  //if dualzstepper, both point to same direction.
+        uint8_t new_z_dir_pin = (INVERT_Z_DIR)^direction^BABYSTEP_INVERT_Z;
+
+        //setup new step
+        if (new_z_dir_pin != old_z_dir_pin) {
+            WRITE_NC(Z_DIR_PIN, new_z_dir_pin);
+#ifdef Z_DUAL_STEPPER_DRIVERS
+            WRITE_NC(Z2_DIR_PIN, new_z_dir_pin);
+#endif
+            delayMicroseconds(STEPPER_SET_DIR_DELAY);
+        }
 
-    //get old pin state back.
-    WRITE(Z_DIR_PIN,old_z_dir_pin);
-    #ifdef Z_DUAL_STEPPER_DRIVERS
-      WRITE(Z2_DIR_PIN,old_z_dir_pin);
-    #endif
+        //perform step
+        STEP_NC_HI(Z_AXIS);
+#ifdef Z_DUAL_STEPPER_DRIVERS
+        STEP_NC_HI(Z2_AXIS);
+#endif
+        STEPPER_MINIMUM_DELAY;
+        STEP_NC_LO(Z_AXIS);
+#ifdef Z_DUAL_STEPPER_DRIVERS
+        STEP_NC_LO(Z2_AXIS);
+#endif
 
-  }
-  break;
- 
-  default:    break;
-  }
+        //get old pin state back.
+        if (new_z_dir_pin != old_z_dir_pin) {
+            WRITE_NC(Z_DIR_PIN, old_z_dir_pin);
+#ifdef Z_DUAL_STEPPER_DRIVERS
+            WRITE_NC(Z2_DIR_PIN, old_z_dir_pin);
+#endif
+        }
+    }
+    break;
+
+    default: break;
+    }
 }
 #endif //BABYSTEPPING
 
@@ -1486,17 +1559,6 @@ void digitalPotWrite(int address, int value) // From Arduino DigitalPotControl e
 }
 #endif
 
-void EEPROM_read_st(int pos, uint8_t* value, uint8_t size)
-{
-    do
-    {
-        *value = eeprom_read_byte((unsigned char*)pos);
-        pos++;
-        value++;
-    }while(--size);
-}
-
-
 void st_current_init() //Initialize Digipot Motor Current
 {
 #ifdef MOTOR_CURRENT_PWM_XY_PIN

+ 32 - 29
Firmware/swi2c.c

@@ -3,10 +3,12 @@
 #include <avr/io.h>
 #include <util/delay.h>
 #include <avr/pgmspace.h>
+#include "stdbool.h"
 #include "Configuration_prusa.h"
 #include "pins.h"
-#include "io_atmega2560.h"
+#include "fastio.h"
 
+#ifdef SWI2C_SCL
 
 #define SWI2C_RMSK   0x01 //read mask (bit0 = 1)
 #define SWI2C_WMSK   0x00 //write mask (bit0 = 0)
@@ -21,75 +23,75 @@ void __delay(void)
 
 void swi2c_init(void)
 {
-	PIN_OUT(SWI2C_SDA);
-	PIN_OUT(SWI2C_SCL);
-	PIN_SET(SWI2C_SDA);
-	PIN_SET(SWI2C_SCL);
+	WRITE(SWI2C_SDA, 1);
+	WRITE(SWI2C_SCL, 1);
+	SET_OUTPUT(SWI2C_SDA);
+	SET_OUTPUT(SWI2C_SCL);
 	uint8_t i; for (i = 0; i < 100; i++)
 		__delay();
 }
 
 void swi2c_start(void)
 {
-	PIN_CLR(SWI2C_SDA);
+	WRITE(SWI2C_SDA, 0);
 	__delay();
-	PIN_CLR(SWI2C_SCL);
+	WRITE(SWI2C_SCL, 0);
 	__delay();
 }
 
 void swi2c_stop(void)
 {
-	PIN_SET(SWI2C_SCL);
+	WRITE(SWI2C_SCL, 1);
 	__delay();
-	PIN_SET(SWI2C_SDA);
+	WRITE(SWI2C_SDA, 1);
 	__delay();
 }
 
 void swi2c_ack(void)
 {
-	PIN_CLR(SWI2C_SDA);
+	WRITE(SWI2C_SDA, 0);
 	__delay();
-	PIN_SET(SWI2C_SCL);
+	WRITE(SWI2C_SCL, 1);
 	__delay();
-	PIN_CLR(SWI2C_SCL);
+	WRITE(SWI2C_SCL, 0);
 	__delay();
 }
 
 uint8_t swi2c_wait_ack()
 {
-	PIN_INP(SWI2C_SDA);
+	SET_INPUT(SWI2C_SDA);
 	__delay();
-//	PIN_SET(SWI2C_SDA);
+//	WRITE(SWI2C_SDA, 1);
 	__delay();
-	PIN_SET(SWI2C_SCL);
+	WRITE(SWI2C_SCL, 1);
 //	__delay();
 	uint8_t ack = 0;
 	uint16_t ackto = SWI2C_TMO;
-	while (!(ack = (PIN_GET(SWI2C_SDA)?0:1)) && ackto--) __delay();
-	PIN_CLR(SWI2C_SCL);
+	while (!(ack = (!READ(SWI2C_SDA))) && ackto--) __delay();
+	WRITE(SWI2C_SCL, 0);
 	__delay();
-	PIN_OUT(SWI2C_SDA);
+	SET_OUTPUT(SWI2C_SDA);
 	__delay();
-	PIN_CLR(SWI2C_SDA);
+	WRITE(SWI2C_SDA, 0);
 	__delay();
 	return ack;
 }
 
 uint8_t swi2c_read(void)
 {
-	PIN_SET(SWI2C_SDA);
+	WRITE(SWI2C_SDA, 1);
 	__delay();
-	PIN_INP(SWI2C_SDA);
+	SET_INPUT(SWI2C_SDA);
 	uint8_t data = 0;
 	int8_t bit; for (bit = 7; bit >= 0; bit--)
 	{
-		PIN_SET(SWI2C_SCL);
+		WRITE(SWI2C_SCL, 1);
 		__delay();
-		data |= (PIN_GET(SWI2C_SDA)?1:0) << bit;
-		PIN_CLR(SWI2C_SCL);
+		data |= (READ(SWI2C_SDA)) << bit;
+		WRITE(SWI2C_SCL, 0);
 		__delay();
 	}
-	PIN_OUT(SWI2C_SDA);
+	SET_OUTPUT(SWI2C_SDA);
 	return data;
 }
 
@@ -97,12 +99,11 @@ void swi2c_write(uint8_t data)
 {
 	int8_t bit; for (bit = 7; bit >= 0; bit--)
 	{
-		if (data & (1 << bit)) PIN_SET(SWI2C_SDA);
-		else PIN_CLR(SWI2C_SDA);
+		WRITE(SWI2C_SDA, data & _BV(bit));
 		__delay();
-		PIN_SET(SWI2C_SCL);
+		WRITE(SWI2C_SCL, 1);
 		__delay();
-		PIN_CLR(SWI2C_SCL);
+		WRITE(SWI2C_SCL, 0);
 		__delay();
 	}
 }
@@ -187,3 +188,5 @@ uint8_t swi2c_writeByte_A16(uint8_t dev_addr, unsigned short addr, uint8_t* pbyt
 }
 
 #endif //SWI2C_A16
+
+#endif //SWI2C_SCL

+ 51 - 52
Firmware/temperature.cpp

@@ -30,12 +30,15 @@
 
 
 #include "Marlin.h"
+#include "cmdqueue.h"
 #include "ultralcd.h"
+#include "menu.h"
+#include "conv2str.h"
 #include "sound.h"
 #include "temperature.h"
 #include "cardreader.h"
 
-#include "Sd2PinMap.h"
+#include "SdFatUtil.h"
 
 #include <avr/wdt.h>
 #include "adc.h"
@@ -196,7 +199,9 @@ static uint8_t heater_ttbllen_map[EXTRUDERS] = ARRAY_BY_EXTRUDERS( HEATER_0_TEMP
 
 static float analog2temp(int raw, uint8_t e);
 static float analog2tempBed(int raw);
+#ifdef AMBIENT_MAXTEMP
 static float analog2tempAmbient(int raw);
+#endif
 static void updateTemperaturesFromRawValues();
 
 enum TempRunawayStates
@@ -238,7 +243,7 @@ bool extruder_altfan_detect()
 	uint8_t overrideVal = eeprom_read_byte((uint8_t *)EEPROM_ALTFAN_OVERRIDE);
 	if (overrideVal == EEPROM_EMPTY_VALUE)
 	{
-		overrideVal = 0;
+		overrideVal = (calibration_status() == CALIBRATION_STATUS_CALIBRATED) ? 1 : 0;
 		eeprom_update_byte((uint8_t *)EEPROM_ALTFAN_OVERRIDE, overrideVal);
 	}
 	altfanStatus.altfanOverride = overrideVal;
@@ -282,8 +287,10 @@ bool checkAllHotends(void)
     return(result);
 }
 
-  void PID_autotune(float temp, int extruder, int ncycles)
-  {
+// WARNING: the following function has been marked noinline to avoid a GCC 4.9.2 LTO
+//          codegen bug causing a stack overwrite issue in process_commands()
+void __attribute__((noinline)) PID_autotune(float temp, int extruder, int ncycles)
+{
   pid_number_of_cycles = ncycles;
   pid_tuning_finished = false;
   float input = 0.0;
@@ -465,7 +472,7 @@ bool checkAllHotends(void)
 			//SERIAL_ECHOPGM("s. Difference between current and ambient T: ");
 			//MYSERIAL.println(input - temp_ambient);
 
-			if (abs(input - temp_ambient) < 5.0) { 
+			if (fabs(input - temp_ambient) < 5.0) { 
 				temp_runaway_stop(false, (extruder<0));
 				pid_tuning_finished = true;
 				return;
@@ -521,7 +528,7 @@ void setExtruderAutoFanState(uint8_t state)
 	//the fan to either On or Off during certain tests/errors.
 
 	fanState = state;
-	uint8_t newFanSpeed = 0;
+	newFanSpeed = 0;
 	if (fanState & 0x01)
 	{
 #ifdef EXTRUDER_ALTFAN_DETECT
@@ -566,10 +573,7 @@ void checkFanSpeed()
 	static unsigned char fan_speed_errors[2] = { 0,0 };
 #if (defined(FANCHECK) && defined(TACH_0) && (TACH_0 >-1))
 	if ((fan_speed[0] < 20) && (current_temperature[0] > EXTRUDER_AUTO_FAN_TEMPERATURE)){ fan_speed_errors[0]++;}
-	else{
-    fan_speed_errors[0] = 0;
-    host_keepalive();
-  }
+	else fan_speed_errors[0] = 0;
 #endif
 #if (defined(FANCHECK) && defined(TACH_1) && (TACH_1 >-1))
 	if ((fan_speed[1] < 5) && ((blocks_queued() ? block_buffer[block_buffer_tail].fan_speed : fanSpeed) > MIN_PRINT_FAN_SPEED)) fan_speed_errors[1]++;
@@ -632,7 +636,6 @@ void fanSpeedError(unsigned char _fan) {
 		fanSpeedErrorBeep(PSTR("Print fan speed is lower than expected"), MSG_FANCHECK_PRINT);
 		break;
 	}
-    // SERIAL_PROTOCOLLNRPGM(MSG_OK); //This ok messes things up with octoprint.
 }
 #endif //(defined(TACH_0) && TACH_0 >-1) || (defined(TACH_1) && TACH_1 > -1)
 
@@ -902,8 +905,6 @@ void manage_heater()
 		timer02_set_pwm0(soft_pwm_bed << 1);
 	  }
   #endif
-  
-  host_keepalive();
 }
 
 #define PGM_RD_W(x)   (short)pgm_read_word(&x)
@@ -1124,7 +1125,10 @@ void tp_init()
 
   adc_init();
 
-  timer0_init();
+  timer0_init(); //enables the heatbed timer.
+
+  // timer2 already enabled earlier in the code
+  // now enable the COMPB temperature interrupt
   OCR2B = 128;
   TIMSK2 |= (1<<OCIE2B);
   
@@ -1377,33 +1381,15 @@ void temp_runaway_check(int _heater_id, float _target_temperature, float _curren
 
 void temp_runaway_stop(bool isPreheat, bool isBed)
 {
-	cancel_heatup = true;
-	quickStop();
-	if (card.sdprinting)
-	{
-		card.sdprinting = false;
-		card.closefile();
-	}
-	// Clean the input command queue 
-	// This is necessary, because in command queue there can be commands which would later set heater or bed temperature.
-	cmdqueue_reset();
-	
-	disable_heater();
-	disable_x();
-	disable_y();
-	disable_e0();
-	disable_e1();
-	disable_e2();
-	manage_heater();
-	lcd_update(0);
-  Sound_MakeCustom(200,0,true);
-	
-  if (isPreheat)
+    disable_heater();
+    Sound_MakeCustom(200,0,true);
+
+    if (isPreheat)
 	{
-		Stop();
-		isBed ? LCD_ALERTMESSAGEPGM("BED PREHEAT ERROR") : LCD_ALERTMESSAGEPGM("PREHEAT ERROR");
+		lcd_setalertstatuspgm(isBed? PSTR("BED PREHEAT ERROR") : PSTR("PREHEAT ERROR"), LCD_STATUS_CRITICAL);
 		SERIAL_ERROR_START;
-		isBed ? SERIAL_ERRORLNPGM(" THERMAL RUNAWAY ( PREHEAT HEATBED)") : SERIAL_ERRORLNPGM(" THERMAL RUNAWAY ( PREHEAT HOTEND)");
+		isBed ? SERIAL_ERRORLNPGM(" THERMAL RUNAWAY (PREHEAT HEATBED)") : SERIAL_ERRORLNPGM(" THERMAL RUNAWAY (PREHEAT HOTEND)");
+
 #ifdef EXTRUDER_ALTFAN_DETECT
 		altfanStatus.altfanOverride = 1; //full speed
 #endif //EXTRUDER_ALTFAN_DETECT
@@ -1414,16 +1400,16 @@ void temp_runaway_stop(bool isPreheat, bool isBed)
 #else //FAN_SOFT_PWM
 		analogWrite(FAN_PIN, 255);
 #endif //FAN_SOFT_PWM
-
 		fanSpeed = 255;
-		delayMicroseconds(2000);
 	}
 	else
 	{
-		isBed ? LCD_ALERTMESSAGEPGM("BED THERMAL RUNAWAY") : LCD_ALERTMESSAGEPGM("THERMAL RUNAWAY");
+		lcd_setalertstatuspgm(isBed? PSTR("BED THERMAL RUNAWAY") : PSTR("THERMAL RUNAWAY"), LCD_STATUS_CRITICAL);
 		SERIAL_ERROR_START;
 		isBed ? SERIAL_ERRORLNPGM(" HEATBED THERMAL RUNAWAY") : SERIAL_ERRORLNPGM(" HOTEND THERMAL RUNAWAY");
 	}
+
+    Stop();
 }
 #endif
 
@@ -1479,13 +1465,12 @@ uint8_t last_alert_sent_to_lcd = LCDALERT_NONE;
 
 //! update the current temperature error message
 //! @param type short error abbreviation (PROGMEM)
-//! @param func optional lcd update function (lcd_setalertstatus when first setting the error)
-void temp_update_messagepgm(const char* PROGMEM type, void (*func)(const char*) = lcd_updatestatus)
+void temp_update_messagepgm(const char* PROGMEM type)
 {
     char msg[LCD_WIDTH];
     strcpy_P(msg, PSTR("Err: "));
     strcat_P(msg, type);
-    (*func)(msg);
+    lcd_setalertstatus(msg, LCD_STATUS_CRITICAL);
 }
 
 //! signal a temperature error on both the lcd and serial
@@ -1493,7 +1478,7 @@ void temp_update_messagepgm(const char* PROGMEM type, void (*func)(const char*)
 //! @param e optional extruder index for hotend errors
 void temp_error_messagepgm(const char* PROGMEM type, uint8_t e = EXTRUDERS)
 {
-    temp_update_messagepgm(type, lcd_setalertstatus);
+    temp_update_messagepgm(type);
 
     SERIAL_ERROR_START;
 
@@ -2044,22 +2029,25 @@ FORCE_INLINE static void temperature_isr()
    
     if(curTodo>0)
     {
-		asm("cli");
+      CRITICAL_SECTION_START;
       babystep(axis,/*fwd*/true);
       babystepsTodo[axis]--; //less to do next time
-		asm("sei");
+      CRITICAL_SECTION_END;
     }
     else
     if(curTodo<0)
     {
-		asm("cli");
+      CRITICAL_SECTION_START;
       babystep(axis,/*fwd*/false);
       babystepsTodo[axis]++; //less to do next time
-		asm("sei");
+      CRITICAL_SECTION_END;
     }
   }
 #endif //BABYSTEPPING
 
+  // Check if a stack overflow happened
+  if (!SdFatUtil::test_stack_integrity()) stack_error();
+
 #if (defined(FANCHECK) && defined(TACH_0) && (TACH_0 > -1))
   check_fans();
 #endif //(defined(TACH_0))
@@ -2325,11 +2313,22 @@ float unscalePID_d(float d)
 //!
 //! @retval true firmware should do temperature compensation and allow calibration
 //! @retval false PINDA thermistor is not detected, disable temperature compensation and calibration
+//! @retval true/false when forced via LCD menu Settings->HW Setup->SuperPINDA
 //!
 bool has_temperature_compensation()
 {
-#ifdef DETECT_SUPERPINDA
-    return (current_temperature_pinda >= PINDA_MINTEMP) ? true : false;
+#ifdef SUPERPINDA_SUPPORT
+#ifdef PINDA_TEMP_COMP
+   	uint8_t pinda_temp_compensation = eeprom_read_byte((uint8_t*)EEPROM_PINDA_TEMP_COMPENSATION);
+    if (pinda_temp_compensation == EEPROM_EMPTY_VALUE) //Unkown PINDA temp compenstation, so check it.
+      {
+#endif //PINDA_TEMP_COMP
+        return (current_temperature_pinda >= PINDA_MINTEMP) ? true : false;
+#ifdef PINDA_TEMP_COMP
+      }
+    else if (pinda_temp_compensation == 0) return true; //Overwritten via LCD menu SuperPINDA [No]
+    else return false; //Overwritten via LCD menu SuperPINDA [YES]
+#endif //PINDA_TEMP_COMP
 #else
     return true;
 #endif

+ 7 - 16
Firmware/temperature.h

@@ -67,7 +67,7 @@ bool has_temperature_compensation();
 #endif
 
 #ifdef AMBIENT_THERMISTOR
-//extern int current_temperature_raw_ambient;
+extern int current_temperature_raw_ambient;
 extern float current_temperature_ambient;
 #endif
 
@@ -99,13 +99,10 @@ extern bool bedPWMDisabled;
   float unscalePID_d(float d);
 
 #endif
-  
-  
-#ifdef BABYSTEPPING
-  extern volatile int babystepsTodo[3];
-#endif
 
-void resetPID(uint8_t extruder);
+
+#ifdef BABYSTEPPING
+extern volatile int babystepsTodo[3];
 
 inline void babystepsTodoZadd(int n)
 {
@@ -115,15 +112,9 @@ inline void babystepsTodoZadd(int n)
         CRITICAL_SECTION_END
     }
 }
+#endif
 
-inline void babystepsTodoZsubtract(int n)
-{
-    if (n != 0) {
-        CRITICAL_SECTION_START
-        babystepsTodo[Z_AXIS] -= n;
-        CRITICAL_SECTION_END
-    }
-}
+void resetPID(uint8_t extruder);
 
 //high level conversion routines, for use outside of temperature.cpp
 //inline so that there is no performance decrease.
@@ -229,7 +220,7 @@ FORCE_INLINE bool isCoolingBed() {
 #define CHECK_ALL_HEATERS (checkAllHotends()||(target_temperature_bed!=0))
 
 int getHeaterPower(int heater);
-void disable_heater();
+void disable_heater(); // Disable all heaters
 void updatePID();
 
 

+ 12 - 11
Firmware/timer02.c

@@ -9,16 +9,11 @@
 
 #include <avr/io.h>
 #include <avr/interrupt.h>
-#include "io_atmega2560.h"
-
-#define BEEPER              84
+#include "macros.h"
 
 void timer0_init(void)
 {
-	//save sreg
-	uint8_t _sreg = SREG;
-	//disable interrupts for sure
-	cli();
+	CRITICAL_SECTION_START;
 
 	TCNT0  = 0;
 	// Fast PWM duty (0-255). 
@@ -28,7 +23,14 @@ void timer0_init(void)
 	TCCR0A = (1 << WGM01) | (1 << WGM00) | (1 << COM0B1) | (1 << COM0B0);  
 	TCCR0B = (1 << CS01);    // CLK/8 prescaling
 	TIMSK0 |= (1 << TOIE0);  // enable timer overflow interrupt
-	
+
+	CRITICAL_SECTION_END;
+}
+
+void timer2_init(void)
+{
+	CRITICAL_SECTION_START;
+
 	// Everything, that used to be on timer0 was moved to timer2 (delay, beeping, millis etc.)
 	//setup timer2
 	TCCR2A = 0x00; //COM_A-B=00, WGM_0-1=00
@@ -39,9 +41,8 @@ void timer0_init(void)
 	TIMSK2 &= ~(1<<OCIE2B);
 	//set timer2 OCR registers (OCRB interrupt generated 0.5ms after OVF interrupt)
 	OCR2A = 0;
-	OCR2B = 128;
-	//restore sreg (enable interrupts)
-	SREG = _sreg;
+
+	CRITICAL_SECTION_END;
 }
 
 

+ 3 - 0
Firmware/timer02.h

@@ -14,6 +14,9 @@ extern "C" {
 ///! Initializes TIMER0 for fast PWM mode-driven bed heating
 extern void timer0_init(void);
 
+///! Initializes TIMER2 for time keeping and temperature interrupt
+extern void timer2_init(void);
+
 ///! Reimplemented original millis() using timer2
 extern unsigned long millis2(void);
 

+ 68 - 35
Firmware/tmc2130.cpp

@@ -9,8 +9,6 @@
 #include "language.h"
 #include "spi.h"
 
-
-
 #define TMC2130_GCONF_NORMAL 0x00000000 // spreadCycle
 #define TMC2130_GCONF_SGSENS 0x00003180 // spreadCycle with stallguard (stall activates DIAG0 and DIAG1 [pushpull])
 #define TMC2130_GCONF_SILENT 0x00000004 // stealthChop
@@ -18,7 +16,6 @@
 
 //mode
 uint8_t tmc2130_mode = TMC2130_MODE_NORMAL;
-//holding currents
 uint8_t tmc2130_current_h[4] = TMC2130_CURRENTS_H;
 //running currents
 uint8_t tmc2130_current_r[4] = TMC2130_CURRENTS_R;
@@ -45,6 +42,8 @@ uint8_t tmc2130_sg_thr_home[4] = TMC2130_SG_THRS_HOME;
 
 uint8_t tmc2130_sg_homing_axes_mask = 0x00;
 
+const char eMotorCurrentScalingEnabled[] PROGMEM = "E-motor current scaling enabled";
+
 uint8_t tmc2130_sg_meassure = 0xff;
 uint32_t tmc2130_sg_meassure_cnt = 0;
 uint32_t tmc2130_sg_meassure_val = 0;
@@ -68,8 +67,9 @@ uint8_t tmc2130_sg_diag_mask = 0x00;
 uint8_t tmc2130_sg_crash = 0;
 uint16_t tmc2130_sg_err[4] = {0, 0, 0, 0};
 uint16_t tmc2130_sg_cnt[4] = {0, 0, 0, 0};
+#ifdef DEBUG_CRASHDET_COUNTERS
 bool tmc2130_sg_change = false;
-
+#endif
 
 bool skip_debug_msg = false;
 
@@ -144,11 +144,7 @@ uint16_t __tcoolthrs(uint8_t axis)
 	}
 	return 0;
 }
-#ifdef PSU_Delta
-void tmc2130_init(bool bSupressFlag)
-#else
-void tmc2130_init()
-#endif
+void tmc2130_init(TMCInitParams params)
 {
 //	DBG(_n("tmc2130_init(), mode=%S\n"), tmc2130_mode?_n("STEALTH"):_n("NORMAL"));
 	WRITE(X_TMC2130_CS, HIGH);
@@ -193,7 +189,16 @@ void tmc2130_init()
 		tmc2130_setup_chopper(axis, tmc2130_mres[axis], tmc2130_current_h[axis], tmc2130_current_r[axis]);
 		tmc2130_wr(axis, TMC2130_REG_TPOWERDOWN, 0x00000000);
 #ifndef TMC2130_STEALTH_E
-		tmc2130_wr(axis, TMC2130_REG_GCONF, TMC2130_GCONF_SGSENS);
+        if( ! params.enableECool ){
+            tmc2130_wr(axis, TMC2130_REG_GCONF, TMC2130_GCONF_SGSENS);
+        } else {
+            tmc2130_wr(axis, TMC2130_REG_COOLCONF, (((uint32_t)tmc2130_sg_thr[axis]) << 16));
+            tmc2130_wr(axis, TMC2130_REG_TCOOLTHRS, 0);
+            tmc2130_wr(axis, TMC2130_REG_GCONF, TMC2130_GCONF_SILENT);
+            tmc2130_wr_PWMCONF(axis, TMC2130_PWM_AMPL_Ecool, TMC2130_PWM_GRAD_Ecool, tmc2130_pwm_freq[axis], TMC2130_PWM_AUTO_Ecool, 0, 0);
+            tmc2130_wr_TPWMTHRS(axis, TMC2130_TPWMTHRS_E);
+            SERIAL_ECHOLNRPGM(eMotorCurrentScalingEnabled);
+        }
 #else //TMC2130_STEALTH_E
 		tmc2130_wr(axis, TMC2130_REG_COOLCONF, (((uint32_t)tmc2130_sg_thr[axis]) << 16));
 		tmc2130_wr(axis, TMC2130_REG_TCOOLTHRS, 0);
@@ -222,7 +227,7 @@ void tmc2130_init()
 #endif //TMC2130_LINEARITY_CORRECTION
 
 #ifdef PSU_Delta
-     if(!bSupressFlag)
+     if(!params.bSuppressFlag)
           check_force_z();
 #endif // PSU_Delta
 
@@ -238,8 +243,6 @@ uint8_t tmc2130_sample_diag()
 	return mask;
 }
 
-extern bool is_usb_printing;
-
 void tmc2130_st_isr()
 {
 	if (tmc2130_mode == TMC2130_MODE_SILENT || tmc2130_sg_stop_on_crash == false) return;
@@ -255,7 +258,9 @@ void tmc2130_st_isr()
 		if (tmc2130_sg_cnt[axis] < tmc2130_sg_err[axis])
 		{
 			tmc2130_sg_cnt[axis] = tmc2130_sg_err[axis];
+#ifdef DEBUG_CRASHDET_COUNTERS
 			tmc2130_sg_change = true;
+#endif
 			uint8_t sg_thr = 64;
 //			if (axis == Y_AXIS) sg_thr = 64;
 			if (tmc2130_sg_err[axis] >= sg_thr)
@@ -409,7 +414,9 @@ void tmc2130_check_overtemp()
 
 		}
 		checktime = _millis();
+#ifdef DEBUG_CRASHDET_COUNTERS
 		tmc2130_sg_change = true;
+#endif
 	}
 #ifdef DEBUG_CRASHDET_COUNTERS
 	if (tmc2130_sg_change)
@@ -428,6 +435,11 @@ void tmc2130_check_overtemp()
 void tmc2130_setup_chopper(uint8_t axis, uint8_t mres, uint8_t current_h, uint8_t current_r)
 {
 	uint8_t intpol = (mres != 0); // intpol to 256 only if microsteps aren't 256
+#ifdef TMC2130_DEDGE_STEPPING
+	uint8_t dedge = 1;
+#else
+	uint8_t dedge = 0;
+#endif
 	uint8_t toff = tmc2130_chopper_config[axis].toff; // toff = 3 (fchop = 27.778kHz)
 	uint8_t hstrt = tmc2130_chopper_config[axis].hstr; //initial 4, modified to 5
 	uint8_t hend = tmc2130_chopper_config[axis].hend; //original value = 1
@@ -437,6 +449,9 @@ void tmc2130_setup_chopper(uint8_t axis, uint8_t mres, uint8_t current_h, uint8_
 	uint8_t tbl = tmc2130_chopper_config[axis].tbl; //blanking time, original value = 2
 	if (axis == E_AXIS)
 	{
+#if defined(TMC2130_INTPOL_E) && (TMC2130_INTPOL_E == 0)
+        intpol = 0;
+#endif
 #ifdef TMC2130_CNSTOFF_E
 		// fd = 0 (slow decay only)
 		hstrt = 0; //fd0..2
@@ -447,16 +462,26 @@ void tmc2130_setup_chopper(uint8_t axis, uint8_t mres, uint8_t current_h, uint8_
 //		toff = TMC2130_TOFF_E; // toff = 3-5
 //		rndtf = 1;
 	}
+#if defined(TMC2130_INTPOL_XY) && (TMC2130_INTPOL_XY == 0)
+    else if (axis == X_AXIS || axis == Y_AXIS) {
+        intpol = 0;
+    }
+#endif
+#if defined(TMC2130_INTPOL_Z) && (TMC2130_INTPOL_Z == 0)
+    else if (axis == Z_AXIS) {
+        intpol = 0;
+    }
+#endif
 //	DBG(_n("tmc2130_setup_chopper(axis=%hhd, mres=%hhd, curh=%hhd, curr=%hhd\n"), axis, mres, current_h, current_r);
 //	DBG(_n(" toff=%hhd, hstr=%hhd, hend=%hhd, tbl=%hhd\n"), toff, hstrt, hend, tbl);
 	if (current_r <= 31)
 	{
-		tmc2130_wr_CHOPCONF(axis, toff, hstrt, hend, fd3, 0, rndtf, chm, tbl, 1, 0, 0, 0, mres, intpol, 0, 0);
+		tmc2130_wr_CHOPCONF(axis, toff, hstrt, hend, fd3, 0, rndtf, chm, tbl, 1, 0, 0, 0, mres, intpol, dedge, 0);
 		tmc2130_wr(axis, TMC2130_REG_IHOLD_IRUN, 0x000f0000 | ((current_r & 0x1f) << 8) | (current_h & 0x1f));
 	}
 	else
 	{
-		tmc2130_wr_CHOPCONF(axis, toff, hstrt, hend, fd3, 0, rndtf, chm, tbl, 0, 0, 0, 0, mres, intpol, 0, 0);
+		tmc2130_wr_CHOPCONF(axis, toff, hstrt, hend, fd3, 0, rndtf, chm, tbl, 0, 0, 0, 0, mres, intpol, dedge, 0);
 		tmc2130_wr(axis, TMC2130_REG_IHOLD_IRUN, 0x000f0000 | (((current_r >> 1) & 0x1f) << 8) | ((current_h >> 1) & 0x1f));
 	}
 }
@@ -678,25 +703,32 @@ static uint8_t tmc2130_rx(uint8_t axis, uint8_t addr, uint32_t* rval)
 #define _GET_PWR_Z      (READ(Z_ENABLE_PIN) == Z_ENABLE_ON)
 #define _GET_PWR_E      (READ(E0_ENABLE_PIN) == E_ENABLE_ON)
 
-#define _SET_PWR_X(ena) { WRITE(X_ENABLE_PIN, ena?X_ENABLE_ON:!X_ENABLE_ON); asm("nop"); }
-#define _SET_PWR_Y(ena) { WRITE(Y_ENABLE_PIN, ena?Y_ENABLE_ON:!Y_ENABLE_ON); asm("nop"); }
-#define _SET_PWR_Z(ena) { WRITE(Z_ENABLE_PIN, ena?Z_ENABLE_ON:!Z_ENABLE_ON); asm("nop"); }
-#define _SET_PWR_E(ena) { WRITE(E0_ENABLE_PIN, ena?E_ENABLE_ON:!E_ENABLE_ON); asm("nop"); }
+#define _SET_PWR_X(ena) WRITE(X_ENABLE_PIN, ena?X_ENABLE_ON:!X_ENABLE_ON)
+#define _SET_PWR_Y(ena) WRITE(Y_ENABLE_PIN, ena?Y_ENABLE_ON:!Y_ENABLE_ON)
+#define _SET_PWR_Z(ena) WRITE(Z_ENABLE_PIN, ena?Z_ENABLE_ON:!Z_ENABLE_ON)
+#define _SET_PWR_E(ena) WRITE(E0_ENABLE_PIN, ena?E_ENABLE_ON:!E_ENABLE_ON)
 
 #define _GET_DIR_X      (READ(X_DIR_PIN) == INVERT_X_DIR)
 #define _GET_DIR_Y      (READ(Y_DIR_PIN) == INVERT_Y_DIR)
 #define _GET_DIR_Z      (READ(Z_DIR_PIN) == INVERT_Z_DIR)
 #define _GET_DIR_E      (READ(E0_DIR_PIN) == INVERT_E0_DIR)
 
-#define _SET_DIR_X(dir) { WRITE(X_DIR_PIN, dir?INVERT_X_DIR:!INVERT_X_DIR); asm("nop"); }
-#define _SET_DIR_Y(dir) { WRITE(Y_DIR_PIN, dir?INVERT_Y_DIR:!INVERT_Y_DIR); asm("nop"); }
-#define _SET_DIR_Z(dir) { WRITE(Z_DIR_PIN, dir?INVERT_Z_DIR:!INVERT_Z_DIR); asm("nop"); }
-#define _SET_DIR_E(dir) { WRITE(E0_DIR_PIN, dir?INVERT_E0_DIR:!INVERT_E0_DIR); asm("nop"); }
+#define _SET_DIR_X(dir) WRITE(X_DIR_PIN, dir?INVERT_X_DIR:!INVERT_X_DIR)
+#define _SET_DIR_Y(dir) WRITE(Y_DIR_PIN, dir?INVERT_Y_DIR:!INVERT_Y_DIR)
+#define _SET_DIR_Z(dir) WRITE(Z_DIR_PIN, dir?INVERT_Z_DIR:!INVERT_Z_DIR)
+#define _SET_DIR_E(dir) WRITE(E0_DIR_PIN, dir?INVERT_E0_DIR:!INVERT_E0_DIR)
 
-#define _DO_STEP_X      { WRITE(X_STEP_PIN, !INVERT_X_STEP_PIN); asm("nop"); WRITE(X_STEP_PIN, INVERT_X_STEP_PIN); asm("nop"); }
-#define _DO_STEP_Y      { WRITE(Y_STEP_PIN, !INVERT_Y_STEP_PIN); asm("nop"); WRITE(Y_STEP_PIN, INVERT_Y_STEP_PIN); asm("nop"); }
-#define _DO_STEP_Z      { WRITE(Z_STEP_PIN, !INVERT_Z_STEP_PIN); asm("nop"); WRITE(Z_STEP_PIN, INVERT_Z_STEP_PIN); asm("nop"); }
-#define _DO_STEP_E      { WRITE(E0_STEP_PIN, !INVERT_E_STEP_PIN); asm("nop"); WRITE(E0_STEP_PIN, INVERT_E_STEP_PIN); asm("nop"); }
+#ifdef TMC2130_DEDGE_STEPPING
+#define _DO_STEP_X      TOGGLE(X_STEP_PIN)
+#define _DO_STEP_Y      TOGGLE(Y_STEP_PIN)
+#define _DO_STEP_Z      TOGGLE(Z_STEP_PIN)
+#define _DO_STEP_E      TOGGLE(E0_STEP_PIN)
+#else
+#define _DO_STEP_X      { WRITE(X_STEP_PIN, !INVERT_X_STEP_PIN); TMC2130_MINIMUM_DELAY; WRITE(X_STEP_PIN, INVERT_X_STEP_PIN); }
+#define _DO_STEP_Y      { WRITE(Y_STEP_PIN, !INVERT_Y_STEP_PIN); TMC2130_MINIMUM_DELAY; WRITE(Y_STEP_PIN, INVERT_Y_STEP_PIN); }
+#define _DO_STEP_Z      { WRITE(Z_STEP_PIN, !INVERT_Z_STEP_PIN); TMC2130_MINIMUM_DELAY; WRITE(Z_STEP_PIN, INVERT_Z_STEP_PIN); }
+#define _DO_STEP_E      { WRITE(E0_STEP_PIN, !INVERT_E_STEP_PIN); TMC2130_MINIMUM_DELAY; WRITE(E0_STEP_PIN, INVERT_E_STEP_PIN); }
+#endif
 
 
 uint16_t tmc2130_get_res(uint8_t axis)
@@ -737,6 +769,7 @@ void tmc2130_set_pwr(uint8_t axis, uint8_t pwr)
 	case Z_AXIS: _SET_PWR_Z(pwr); break;
 	case E_AXIS: _SET_PWR_E(pwr); break;
 	}
+    delayMicroseconds(TMC2130_SET_PWR_DELAY);
 }
 
 uint8_t tmc2130_get_inv(uint8_t axis)
@@ -773,6 +806,7 @@ void tmc2130_set_dir(uint8_t axis, uint8_t dir)
 	case Z_AXIS: _SET_DIR_Z(dir); break;
 	case E_AXIS: _SET_DIR_E(dir); break;
 	}
+    delayMicroseconds(TMC2130_SET_DIR_DELAY);
 }
 
 void tmc2130_do_step(uint8_t axis)
@@ -788,13 +822,13 @@ void tmc2130_do_step(uint8_t axis)
 
 void tmc2130_do_steps(uint8_t axis, uint16_t steps, uint8_t dir, uint16_t delay_us)
 {
-	tmc2130_set_dir(axis, dir);
-	delayMicroseconds(100);
-	while (steps--)
-	{
+    if (tmc2130_get_dir(axis) != dir)
+        tmc2130_set_dir(axis, dir);
+    while (steps--)
+    {
 		tmc2130_do_step(axis);
 		delayMicroseconds(delay_us);
-	}
+    }
 }
 
 void tmc2130_goto_step(uint8_t axis, uint8_t step, uint8_t dir, uint16_t delay_us, uint16_t microstep_resolution)
@@ -820,7 +854,6 @@ void tmc2130_goto_step(uint8_t axis, uint8_t step, uint8_t dir, uint16_t delay_u
 		cnt = steps;
 	}
 	tmc2130_set_dir(axis, dir);
-	delayMicroseconds(100);
 	mscnt = tmc2130_rd_MSCNT(axis);
 	while ((cnt--) && ((mscnt >> shift) != step))
 	{
@@ -996,11 +1029,11 @@ bool tmc2130_home_calibrate(uint8_t axis)
 	uint8_t val[16];
 	homeaxis(axis, 16, step);
 	bubblesort_uint8(step, 16, 0);
-	printf_P(PSTR("sorted samples:\n"));
+	puts_P(PSTR("sorted samples:"));
 	for (uint8_t i = 0; i < 16; i++)
 		printf_P(PSTR(" i=%2d step=%2d\n"), i, step[i]);
 	uint8_t cl = clusterize_uint8(step, 16, cnt, val, 1);
-	printf_P(PSTR("clusters:\n"));
+	puts_P(PSTR("clusters:"));
 	for (uint8_t i = 0; i < cl; i++)
 		printf_P(PSTR(" i=%2d cnt=%2d val=%2d\n"), i, cnt[i], val[i]);
 	bubblesort_uint8(cnt, cl, val);

+ 30 - 4
Firmware/tmc2130.h

@@ -1,10 +1,10 @@
 #ifndef TMC2130_H
 #define TMC2130_H
 
+#include <stdint.h>
 
 //mode
 extern uint8_t tmc2130_mode;
-//holding and running currents
 extern uint8_t tmc2130_current_h[4];
 extern uint8_t tmc2130_current_r[4];
 //microstep resolution (0 means 256usteps, 8 means 1ustep
@@ -22,6 +22,8 @@ extern uint32_t tmc2130_sg_meassure_val;
 
 extern uint8_t tmc2130_sg_homing_axes_mask;
 
+extern const char eMotorCurrentScalingEnabled[];
+
 #define TMC2130_MODE_NORMAL 0
 #define TMC2130_MODE_SILENT 1
 
@@ -29,6 +31,18 @@ extern uint8_t tmc2130_sg_homing_axes_mask;
 #define TMC2130_WAVE_FAC1000_MAX 200
 #define TMC2130_WAVE_FAC1000_STP   1
 
+#define TMC2130_MINIMUM_PULSE 0   // minimum pulse width in uS
+#define TMC2130_SET_DIR_DELAY 20  // minimum delay after setting direction in uS
+#define TMC2130_SET_PWR_DELAY 0   // minimum delay after changing pwr mode in uS
+
+#ifdef TMC2130_DEDGE_STEPPING
+#define TMC2130_MINIMUM_DELAY //NOP
+#elif TMC2130_MINIMUM_PULSE == 0
+#define TMC2130_MINIMUM_DELAY asm("nop")
+#else
+#define TMC2130_MINIMUM_DELAY delayMicroseconds(TMC2130_MINIMUM_PULSE)
+#endif
+
 extern uint8_t tmc2130_home_enabled;
 extern uint8_t tmc2130_home_origin[2];
 extern uint8_t tmc2130_home_bsteps[2];
@@ -51,11 +65,23 @@ typedef struct
 extern tmc2130_chopper_config_t tmc2130_chopper_config[4];
 
 //initialize tmc2130
-#ifdef PSU_Delta
-extern void tmc2130_init(bool bSupressFlag=false);
+
+struct TMCInitParams {
+    uint8_t bSuppressFlag : 1; // only relevant on MK3S with PSU_Delta
+    uint8_t enableECool : 1;  // experimental support for E-motor cooler operation
+    inline TMCInitParams():bSuppressFlag(0), enableECool(0) { }
+    inline explicit TMCInitParams(bool bSuppressFlag, bool enableECool):bSuppressFlag(bSuppressFlag), enableECool(enableECool) { }
+    inline explicit TMCInitParams(bool enableECool)
+        : bSuppressFlag(
+#ifdef PSU_delta
+        1
 #else
-extern void tmc2130_init();
+        0
 #endif
+        )
+        , enableECool(enableECool) { }
+};
+extern void tmc2130_init(TMCInitParams params);
 //check diag pins (called from stepper isr)
 extern void tmc2130_st_isr();
 //update stall guard (called from st_synchronize inside the loop)

+ 3 - 11
Firmware/tone04.c

@@ -7,17 +7,9 @@
 
 #ifdef SYSTEM_TIMER_2
 
-#include <avr/io.h>
-#include <avr/interrupt.h>
 #include "pins.h"
-
-#ifndef CRITICAL_SECTION_START
-	#define CRITICAL_SECTION_START  unsigned char _sreg = SREG; cli();
-	#define CRITICAL_SECTION_END    SREG = _sreg;
-#endif //CRITICAL_SECTION_START
-
-
 #include "fastio.h"
+#include "macros.h"
 
 void timer4_init(void)
 {
@@ -79,7 +71,7 @@ ISR(TIMER4_OVF_vect)
 	WRITE(BEEPER, 0);
 }
 
-void tone4(__attribute__((unused)) uint8_t _pin, uint16_t frequency)
+void tone4(_UNUSED uint8_t _pin, uint16_t frequency)
 {
 	//this ocr and prescalarbits calculation is taken from the Arduino core and simplified for one type of timer only
 	uint8_t prescalarbits = 0b001;
@@ -105,7 +97,7 @@ void tone4(__attribute__((unused)) uint8_t _pin, uint16_t frequency)
 	CRITICAL_SECTION_END;
 }
 
-void noTone4(__attribute__((unused)) uint8_t _pin)
+void noTone4(_UNUSED uint8_t _pin)
 {
 	CRITICAL_SECTION_START;
 	// Revert prescaler to CLK/1024

+ 137 - 0
Firmware/twi.c

@@ -0,0 +1,137 @@
+/*
+  twi.c - Stripped-down TWI/I2C library
+  Copyright (c) 2006 Nicholas Zambetti.  All right reserved.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+  Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts
+*/
+
+
+#include <math.h>
+#include "config.h"
+#include "fastio.h"
+#include "twi.h"
+
+
+void twi_init(void)
+{
+  // activate internal pullups for twi.
+  WRITE(SDA_PIN, 1);
+  WRITE(SCL_PIN, 1);
+
+  // initialize twi prescaler and bit rate
+  TWSR &= ~(_BV(TWPS0) | _BV(TWPS1));
+  TWBR = ((F_CPU / TWI_FREQ) - 16) / 2;
+
+  /* twi bit rate formula from atmega128 manual pg 204
+  SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
+  note: TWBR should be 10 or higher for master mode
+  It is 72 for a 16mhz Wiring board with 100kHz TWI */
+}
+
+void twi_disable(void)
+{
+  // deactivate internal pullups for twi.
+  WRITE(SDA_PIN, 0);
+  WRITE(SCL_PIN, 0);
+}
+
+
+static void twi_stop()
+{
+  TWCR = _BV(TWEN) | _BV(TWINT) | _BV(TWSTO);
+}
+
+
+static uint8_t twi_wait(uint8_t status)
+{
+  while(!(TWCR & _BV(TWINT)));
+  if(TW_STATUS != status)
+  {
+      twi_stop();
+      return 1;
+  }
+  return 0;
+}
+
+
+static uint8_t twi_start(uint8_t address, uint8_t reg)
+{
+  // send start condition
+  TWCR = _BV(TWEN) | _BV(TWINT) | _BV(TWSTA);
+  if(twi_wait(TW_START))
+      return 1;
+
+  // send address
+  TWDR = TW_WRITE | (address << 1);
+  TWCR = _BV(TWEN) | _BV(TWINT);
+  if(twi_wait(TW_MT_SLA_ACK))
+      return 2;
+
+  // send register
+  TWDR = reg;
+  TWCR = _BV(TWEN) | _BV(TWINT);
+  if(twi_wait(TW_MT_DATA_ACK))
+      return 3;
+
+  return 0;
+}
+
+
+uint8_t twi_r8(uint8_t address, uint8_t reg, uint8_t* data)
+{
+  if(twi_start(address, reg))
+      return 1;
+
+  // repeat start
+  TWCR = _BV(TWEN) | _BV(TWINT) | _BV(TWSTA);
+  if(twi_wait(TW_REP_START))
+      return 2;
+
+  // start receiving
+  TWDR = TW_READ | (address << 1);
+  TWCR = _BV(TWEN) | _BV(TWINT);
+  if(twi_wait(TW_MR_SLA_ACK))
+      return 3;
+
+  // receive data
+  TWCR = _BV(TWEN) | _BV(TWINT);
+  if(twi_wait(TW_MR_DATA_NACK))
+      return 4;
+
+  *data = TWDR;
+
+  // send stop
+  twi_stop();
+  return 0;
+}
+
+
+uint8_t twi_w8(uint8_t address, uint8_t reg, uint8_t data)
+{
+  if(twi_start(address, reg))
+      return 1;
+
+  // send data
+  TWDR = data;
+  TWCR = _BV(TWEN) | _BV(TWINT);
+  if(twi_wait(TW_MT_DATA_ACK))
+      return 2;
+
+  // send stop
+  twi_stop();
+  return 0;
+}

+ 63 - 0
Firmware/twi.h

@@ -0,0 +1,63 @@
+/*
+  twi.h - Stripped-down TWI/I2C library
+  Copyright (c) 2006 Nicholas Zambetti.  All right reserved.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#pragma once
+
+#include <inttypes.h>
+#include <compat/twi.h>
+
+#ifndef TWI_FREQ
+#define TWI_FREQ 400000L
+#endif
+
+/*
+ * Function twi_init
+ * Desc     readys twi pins and sets twi bitrate
+ * Input    none
+ * Output   none
+ */
+void twi_init(void);
+
+/*
+ * Function twi_disable
+ * Desc     disables twi pins
+ * Input    none
+ * Output   none
+ */
+void twi_disable(void);
+
+/*
+ * Function twi_r8
+ * Desc     read a single byte from a device
+ * Input    address: 7bit i2c device address
+ *          reg: register address
+ *          data: pointer to byte for result
+ * Output   0 on success
+ */
+uint8_t twi_r8(uint8_t address, uint8_t reg, uint8_t* data);
+
+/*
+ * Function twi_w8
+ * Desc     write a single byte from a device
+ * Input    address: 7bit i2c device address
+ *          reg: register address
+ *          data: byte to write
+ * Output   0 on success
+ */
+uint8_t twi_w8(uint8_t address, uint8_t reg, uint8_t data);

+ 4 - 3
Firmware/uart2.c

@@ -4,6 +4,7 @@
 #include <avr/interrupt.h>
 #include <avr/pgmspace.h>
 #include "rbuf.h"
+#include "macros.h"
 
 #define UART2_BAUD 115200
 #define UART_BAUD_SELECT(baudRate,xtalCpu) (((float)(xtalCpu))/(((float)(baudRate))*8.0)-1.0+0.5)
@@ -16,7 +17,7 @@ uint8_t uart2_ibuf[14] = {0, 0};
 FILE _uart2io = {0};
 
 
-int uart2_putchar(char c, FILE *stream __attribute__((unused)))
+int uart2_putchar(char c, _UNUSED FILE *stream)
 {
 	while (!uart2_txready);
 	UDR2 = c; // transmit byte
@@ -25,7 +26,7 @@ int uart2_putchar(char c, FILE *stream __attribute__((unused)))
 	return 0;
 }
 
-int uart2_getchar(FILE *stream __attribute__((unused)))
+int uart2_getchar(_UNUSED FILE *stream)
 {
 	if (rbuf_empty(uart2_ibuf)) return -1;
 	return rbuf_get(uart2_ibuf);
@@ -78,7 +79,7 @@ ISR(USART2_RX_vect)
 	if (rbuf_put(uart2_ibuf, UDR2) < 0) // put received byte to buffer
 	{ //rx buffer full
 		//uart2_rx_clr(); //for sure, clear input buffer
-		printf_P(PSTR("USART2 rx Full!!!\n"));
+		puts_P(PSTR("USART2 rx Full!!!"));
 	}
 }
 

+ 1481 - 1706
Firmware/ultralcd.cpp

@@ -5,6 +5,7 @@
 
 #include "temperature.h"
 #include "ultralcd.h"
+#include "conv2str.h"
 #include "fsensor.h"
 #include "Marlin.h"
 #include "language.h"
@@ -28,8 +29,6 @@
 //#include "Configuration.h"
 #include "cmdqueue.h"
 
-#include "SdFatUtil.h"
-
 #ifdef FILAMENT_SENSOR
 #include "pat9125.h"
 #include "fsensor.h"
@@ -44,7 +43,6 @@
 #include "mmu.h"
 
 #include "static_assert.h"
-#include "io_atmega2560.h"
 #include "first_lay_cal.h"
 
 #include "fsensor.h"
@@ -56,16 +54,16 @@
 #endif
 
 
-int scrollstuff = 0;
-char longFilenameOLD[LONG_FILENAME_LENGTH];
-
+int clock_interval = 0;
 
 static void lcd_sd_updir();
 static void lcd_mesh_bed_leveling_settings();
+#ifdef LCD_BL_PIN
 static void lcd_backlight_menu();
+#endif
 
 int8_t ReInitLCD = 0;
-
+uint8_t scrollstuff = 0;
 
 int8_t SilentModeMenu = SILENT_MODE_OFF;
 uint8_t SilentModeMenu_MMU = 1; //activate mmu unit stealth mode
@@ -76,11 +74,6 @@ int8_t FSensorStateMenu = 1;
 bool bMenuFSDetect=false;
 #endif //IR_SENSOR_ANALOG
 
-
-#ifdef SDCARD_SORT_ALPHA
-bool presort_flag = false;
-#endif
-
 LcdCommands lcd_commands_type = LcdCommands::Idle;
 static uint8_t lcd_commands_step = 0;
 
@@ -90,7 +83,6 @@ unsigned int custom_message_state = 0;
 
 bool isPrintPaused = false;
 uint8_t farm_mode = 0;
-int farm_no = 0;
 int farm_timer = 8;
 uint8_t farm_status = 0;
 bool printer_connected = true;
@@ -132,13 +124,14 @@ static void lcd_tune_menu();
 static void lcd_settings_menu();
 static void lcd_calibration_menu();
 static void lcd_control_temperature_menu();
+#ifdef TMC2130
 static void lcd_settings_linearity_correction_menu_save();
+#endif
 static void prusa_stat_printerstatus(int _status);
 static void prusa_stat_farm_number();
 static void prusa_stat_diameter();
 static void prusa_stat_temperatures();
 static void prusa_stat_printinfo();
-static void lcd_farm_no();
 static void lcd_menu_xyz_y_min();
 static void lcd_menu_xyz_skew();
 static void lcd_menu_xyz_offset();
@@ -170,7 +163,6 @@ static void lcd_selftest_v();
 #ifdef TMC2130
 static void reset_crash_det(unsigned char axis);
 static bool lcd_selfcheck_axis_sg(unsigned char axis);
-static bool lcd_selfcheck_axis(int _axis, int _travel);
 #else
 static bool lcd_selfcheck_axis(int _axis, int _travel);
 static bool lcd_selfcheck_pulleys(int axis);
@@ -258,7 +250,9 @@ static void fil_unload_menu();
 #endif // SNMM || SNMM_V2
 static void lcd_disable_farm_mode();
 static void lcd_set_fan_check();
+#ifdef MMU_HAS_CUTTER
 static void lcd_cutter_enabled();
+#endif
 #ifdef SNMM
 static char snmm_stop_print_menu();
 #endif //SNMM
@@ -334,173 +328,49 @@ bool bSettings;                                   // flag (i.e. 'fake parameter'
 
 const char STR_SEPARATOR[] PROGMEM = "------------";
 
-
-static void lcd_implementation_drawmenu_sdfile_selected(uint8_t row, const char* filename, char* longFilename)
+static void lcd_implementation_drawmenu_sdfile(uint8_t row, const char* longFilename)
 {
     char c;
-    int enc_dif = lcd_encoder_diff / ENCODER_PULSES_PER_STEP;
     uint8_t n = LCD_WIDTH - 1;
-
-    for(uint_least8_t g = 0; g<4;g++){
-      lcd_set_cursor(0, g);
-    lcd_print(' ');
-    }
     lcd_set_cursor(0, row);
-    lcd_print('>');
-
-    if (longFilename[0] == '\0')
-    {
-        longFilename = filename;
-    }
-
-    int i = 1;
-    int j = 0;
-    char* longFilenameTMP = longFilename;
-
-    while((c = *longFilenameTMP) != '\0')
+	lcd_print((lcd_encoder == menu_item)?'>':' ');
+    while( ((c = *longFilename) != '\0') && (n>0) )
     {
-        lcd_set_cursor(i, row);
         lcd_print(c);
-        i++;
-        longFilenameTMP++;
-        if(i==LCD_WIDTH){
-          i=1;
-          j++;
-          longFilenameTMP = longFilename + j;          
-          n = LCD_WIDTH - 1;
-          for(int g = 0; g<300 ;g++){
-			  manage_heater();
-            if(LCD_CLICKED || ( enc_dif != (lcd_encoder_diff / ENCODER_PULSES_PER_STEP))){
-				longFilenameTMP = longFilename;
-				*(longFilenameTMP + LCD_WIDTH - 2) = '\0';
-				i = 1;
-				j = 0;
-				break;
-            }else{
-				if (j == 1) _delay_ms(3);	//wait around 1.2 s to start scrolling text
-				_delay_ms(1);				//then scroll with redrawing every 300 ms 
-            }
-
-          }
-        }
-    }
-    if(c!='\0'){
-      lcd_set_cursor(i, row);
-        lcd_print(c);
-        i++;
-    }
-    n=n-i+1;
-    while(n--)
-        lcd_print(' ');
-}
-static void lcd_implementation_drawmenu_sdfile(uint8_t row, const char* filename, char* longFilename)
-{
-    char c;
-    uint8_t n = LCD_WIDTH - 1;
-    lcd_set_cursor(0, row);
-    lcd_print(' ');
-    if (longFilename[0] != '\0')
-    {
-        filename = longFilename;
-        longFilename[LCD_WIDTH-1] = '\0';
-    }
-    while( ((c = *filename) != '\0') && (n>0) )
-    {
-        lcd_print(c);
-        filename++;
-        n--;
-    }
-    while(n--)
-        lcd_print(' ');
-}
-static void lcd_implementation_drawmenu_sddirectory_selected(uint8_t row, const char* filename, char* longFilename)
-{
-    char c;
-    uint8_t n = LCD_WIDTH - 2;
-    lcd_set_cursor(0, row);
-    lcd_print('>');
-    lcd_print(LCD_STR_FOLDER[0]);
-    if (longFilename[0] != '\0')
-    {
-        filename = longFilename;
-        longFilename[LCD_WIDTH-2] = '\0';
-    }
-    while( ((c = *filename) != '\0') && (n>0) )
-    {
-        lcd_print(c);
-        filename++;
+        longFilename++;
         n--;
     }
-    while(n--)
-        lcd_print(' ');
+    lcd_space(n);
 }
-static void lcd_implementation_drawmenu_sddirectory(uint8_t row, const char* filename, char* longFilename)
+static void lcd_implementation_drawmenu_sddirectory(uint8_t row, const char* longFilename)
 {
     char c;
     uint8_t n = LCD_WIDTH - 2;
     lcd_set_cursor(0, row);
-    lcd_print(' ');
-    lcd_print(LCD_STR_FOLDER[0]);
-    if (longFilename[0] != '\0')
-    {
-        filename = longFilename;
-        longFilename[LCD_WIDTH-2] = '\0';
-    }
-    while( ((c = *filename) != '\0') && (n>0) )
+	lcd_print((lcd_encoder == menu_item)?'>':' ');
+	lcd_print(LCD_STR_FOLDER[0]);
+    while( ((c = *longFilename) != '\0') && (n>0) )
     {
         lcd_print(c);
-        filename++;
+        longFilename++;
         n--;
     }
-    while(n--)
-        lcd_print(' ');
+    lcd_space(n);
 }
 
 
 
 #define MENU_ITEM_SDDIR(str_fn, str_fnl) do { if (menu_item_sddir(str_fn, str_fnl)) return; } while (0)
-//#define MENU_ITEM_SDDIR(str, str_fn, str_fnl) MENU_ITEM(sddirectory, str, str_fn, str_fnl)
-//extern uint8_t menu_item_sddir(const char* str, const char* str_fn, char* str_fnl);
-
-#define MENU_ITEM_SDFILE(str, str_fn, str_fnl) do { if (menu_item_sdfile(str, str_fn, str_fnl)) return; } while (0)
-//#define MENU_ITEM_SDFILE(str, str_fn, str_fnl) MENU_ITEM(sdfile, str, str_fn, str_fnl)
-//extern uint8_t menu_item_sdfile(const char* str, const char* str_fn, char* str_fnl);
+#define MENU_ITEM_SDFILE(str_fn, str_fnl) do { if (menu_item_sdfile(str_fn, str_fnl)) return; } while (0)
 
 
 uint8_t menu_item_sddir(const char* str_fn, char* str_fnl)
 {
-#ifdef NEW_SD_MENU
-//	str_fnl[18] = 0;
-//	printf_P(PSTR("menu dir %d '%s' '%s'\n"), menu_row, str_fn, str_fnl);
 	if (menu_item == menu_line)
 	{
 		if (lcd_draw_update)
 		{
-			lcd_set_cursor(0, menu_row);
-			int cnt = lcd_printf_P(PSTR("%c%c%-18s"), (lcd_encoder == menu_item)?'>':' ', LCD_STR_FOLDER[0], str_fnl[0]?str_fnl:str_fn);
-//			int cnt = lcd_printf_P(PSTR("%c%c%-18s"), (lcd_encoder == menu_item)?'>':' ', LCD_STR_FOLDER[0], str_fn);
-		}
-		if (menu_clicked && (lcd_encoder == menu_item))
-		{
-			uint8_t depth = (uint8_t)card.getWorkDirDepth();
-			strcpy(dir_names[depth], str_fn);
-//			printf_P(PSTR("%s\n"), dir_names[depth]);
-			card.chdir(str_fn);
-			lcd_encoder = 0;
-			return menu_item_ret();
-		}
-	}
-	menu_item++;
-	return 0;
-#else //NEW_SD_MENU
-	if (menu_item == menu_line)
-	{
-		if (lcd_draw_update)
-		{
-			if (lcd_encoder == menu_item)
-				lcd_implementation_drawmenu_sddirectory_selected(menu_row, str_fn, str_fnl);
-			else
-				lcd_implementation_drawmenu_sddirectory(menu_row, str_fn, str_fnl);
+			lcd_implementation_drawmenu_sddirectory(menu_row, (str_fnl[0] == '\0') ? str_fn : str_fnl);
 		}
 		if (menu_clicked && (lcd_encoder == menu_item))
 		{
@@ -508,80 +378,32 @@ uint8_t menu_item_sddir(const char* str_fn, char* str_fnl)
 			lcd_update_enabled = 0;
 			menu_action_sddirectory(str_fn);
 			lcd_update_enabled = 1;
-			return menu_item_ret();
+			/* return */ menu_item_ret();
+			return 1;
 		}
 	}
 	menu_item++;
 	return 0;
-
-#endif //NEW_SD_MENU
 }
 
-static uint8_t menu_item_sdfile(const char*
-#ifdef NEW_SD_MENU
-        str
-#endif //NEW_SD_MENU
-         ,const char* str_fn, char* str_fnl)
+static uint8_t menu_item_sdfile(const char* str_fn, char* str_fnl)
 {
-#ifdef NEW_SD_MENU
-//	printf_P(PSTR("menu sdfile\n"));
-//	str_fnl[19] = 0;
-//	printf_P(PSTR("menu file %d '%s' '%s'\n"), menu_row, str_fn, str_fnl);
 	if (menu_item == menu_line)
 	{
 		if (lcd_draw_update)
 		{
-//			printf_P(PSTR("menu file %d %d '%s'\n"), menu_row, menuData.sdcard_menu.viewState, str_fnl[0]?str_fnl:str_fn);
-			lcd_set_cursor(0, menu_row);
-/*			if (lcd_encoder == menu_item)
-			{
-				lcd_printf_P(PSTR("%c%-19s"), (lcd_encoder == menu_item)?'>':' ', (str_fnl[0]?str_fnl:str_fn) + 1);
-				if (menuData.sdcard_menu.viewState == 0)
-				{
-					menuData.sdcard_menu.viewState++;
-					lcd_printf_P(PSTR("%c%-19s"), (lcd_encoder == menu_item)?'>':' ', (str_fnl[0]?str_fnl:str_fn) + 1);
-				}
-				else if (menuData.sdcard_menu.viewState == 1)
-				{
-					lcd_printf_P(PSTR("%c%-19s"), (lcd_encoder == menu_item)?'>':' ', (str_fnl[0]?str_fnl:str_fn) + 2);
-				}
-			}
-			else*/
-			{
-				str_fnl[19] = 0;
-				lcd_printf_P(PSTR("%c%-19s"), (lcd_encoder == menu_item)?'>':' ', str_fnl[0]?str_fnl:str_fn);
-			}
-
-//			int cnt = lcd_printf_P(PSTR("%c%-19s"), (lcd_encoder == menu_item)?'>':' ', str_fnl);
-//			int cnt = lcd_printf_P(PSTR("%cTESTIK.gcode"), (lcd_encoder == menu_item)?'>':' ');
+			lcd_implementation_drawmenu_sdfile(menu_row, (str_fnl[0] == '\0') ? str_fn : str_fnl);
 		}
 		if (menu_clicked && (lcd_encoder == menu_item))
 		{
-			return menu_item_ret();
-		}
-	}
-	menu_item++;
-	return 0;
-#else //NEW_SD_MENU
-	if (menu_item == menu_line)
-	{
-		if (lcd_draw_update)
-		{
-			if (lcd_encoder == menu_item)
-				lcd_implementation_drawmenu_sdfile_selected(menu_row, str_fn, str_fnl);
-			else
-				lcd_implementation_drawmenu_sdfile(menu_row, str_fn, str_fnl);
-		}
-		if (menu_clicked && (lcd_encoder == menu_item))
-		{
-		    lcd_consume_click();
+			lcd_consume_click();
 			menu_action_sdfile(str_fn);
-			return menu_item_ret();
+			/* return */ menu_item_ret();
+			return 1;
 		}
 	}
 	menu_item++;
 	return 0;
-#endif //NEW_SD_MENU
 }
 
 // Print temperature (nozzle/bed) (9 chars total)
@@ -672,39 +494,7 @@ void lcdui_print_extruder(void)
 // Print farm number (5 chars total)
 void lcdui_print_farm(void)
 {
-	int chars = lcd_printf_P(_N(" F0  "));
-//	lcd_space(5 - chars);
-/*
-	// Farm number display
-	if (farm_mode)
-	{
-		lcd_set_cursor(6, 2);
-		lcd_puts_P(PSTR(" F"));
-		lcd_print(farm_no);
-		lcd_puts_P(PSTR("  "));
-        
-        // Beat display
-        lcd_set_cursor(LCD_WIDTH - 1, 0);
-        if ( (_millis() - kicktime) < 60000 ) {
-        
-            lcd_puts_P(PSTR("L"));
-        
-        }else{
-            lcd_puts_P(PSTR(" "));
-        }
-        
-	}
-	else {
-#ifdef SNMM
-		lcd_puts_P(PSTR(" E"));
-		lcd_print(get_ext_nr() + 1);
-
-#else
-		lcd_set_cursor(LCD_WIDTH - 8 - 2, 2);
-		lcd_puts_P(PSTR(" "));
-#endif
-	}
-*/
+	lcd_printf_P(_N(" FRM "));
 }
 
 #ifdef CMD_DIAGNOSTICS
@@ -714,200 +504,199 @@ void lcdui_print_cmd_diag(void)
 	lcd_set_cursor(LCD_WIDTH - 8 -1, 2);
 	lcd_puts_P(PSTR("      C"));
 	lcd_print(buflen);	// number of commands in cmd buffer
-	if (buflen < 9) lcd_puts_P(" ");
+	if (buflen < 9) lcd_print(' ');
 }
 #endif //CMD_DIAGNOSTICS
 
 // Print time (8 chars total)
 void lcdui_print_time(void)
 {
-	//if remaining print time estimation is available print it else print elapsed time
-	uint16_t print_t = 0;
-	if (print_time_remaining_normal != PRINT_TIME_REMAINING_INIT)
-		print_t = print_time_remaining();
-	else if(starttime != 0)
-		print_t = _millis() / 60000 - starttime / 60000;
-	int chars = 0;
-	if ((PRINTER_ACTIVE) && ((print_time_remaining_normal != PRINT_TIME_REMAINING_INIT) || (starttime != 0)))
-	{
-          char suff = ' ';
-          char suff_doubt = ' ';
-		if (print_time_remaining_normal != PRINT_TIME_REMAINING_INIT)
-          {
-               suff = 'R';
-               if (feedmultiply != 100)
-                    suff_doubt = '?';
-          }
-		if (print_t < 6000) //time<100h
-			chars = lcd_printf_P(_N("%c%02u:%02u%c%c"), LCD_STR_CLOCK[0], print_t / 60, print_t % 60, suff, suff_doubt);
-		else //time>=100h
-			chars = lcd_printf_P(_N("%c%3uh %c%c"), LCD_STR_CLOCK[0], print_t / 60, suff, suff_doubt);
-	}
-	else
-		chars = lcd_printf_P(_N("%c--:--  "), LCD_STR_CLOCK[0]);
-	lcd_space(8 - chars);
+    //if remaining print time estimation is available print it else print elapsed time
+    int chars = 0;
+    if (PRINTER_ACTIVE) {
+        uint16_t print_t = PRINT_TIME_REMAINING_INIT;
+        uint16_t print_tr = PRINT_TIME_REMAINING_INIT;
+        uint16_t print_tc = PRINT_TIME_REMAINING_INIT;
+        char suff = ' ';
+        char suff_doubt = ' ';
+
+#ifdef TMC2130
+        if (SilentModeMenu != SILENT_MODE_OFF) {
+            if (print_time_remaining_silent != PRINT_TIME_REMAINING_INIT)
+                print_tr = print_time_remaining_silent;
+//#ifdef CLOCK_INTERVAL_TIME
+            if (print_time_to_change_silent != PRINT_TIME_REMAINING_INIT)
+                print_tc = print_time_to_change_silent;
+//#endif //CLOCK_INTERVAL_TIME
+        } else {
+#endif //TMC2130
+            if (print_time_remaining_normal != PRINT_TIME_REMAINING_INIT)
+                print_tr = print_time_remaining_normal;
+//#ifdef CLOCK_INTERVAL_TIME
+            if (print_time_to_change_normal != PRINT_TIME_REMAINING_INIT)
+                print_tc = print_time_to_change_normal;
+//#endif //CLOCK_INTERVAL_TIME
+#ifdef TMC2130
+        }
+#endif //TMC2130
+
+//#ifdef CLOCK_INTERVAL_TIME
+        if (clock_interval == CLOCK_INTERVAL_TIME*2)
+            clock_interval = 0;
+
+        clock_interval++;
+
+        if (print_tc != PRINT_TIME_REMAINING_INIT && clock_interval > CLOCK_INTERVAL_TIME) {
+            print_t = print_tc;
+            suff = 'C';
+        } else
+//#endif //CLOCK_INTERVAL_TIME 
+        if (print_tr != PRINT_TIME_REMAINING_INIT) {
+            print_t = print_tr;
+            suff = 'R';
+        } else 
+            print_t = _millis() / 60000 - starttime / 60000;
+
+        if (feedmultiply != 100 && (print_t == print_tr || print_t == print_tc)) {
+            suff_doubt = '?';
+            print_t = 100ul * print_t / feedmultiply;
+        }
+
+        if (print_t < 6000) //time<100h
+            chars = lcd_printf_P(_N("%c%02u:%02u%c%c"), LCD_STR_CLOCK[0], print_t / 60, print_t % 60, suff, suff_doubt);
+        else //time>=100h
+            chars = lcd_printf_P(_N("%c%3uh %c%c"), LCD_STR_CLOCK[0], print_t / 60, suff, suff_doubt);
+    }
+    else
+        chars = lcd_printf_P(_N("%c--:--  "), LCD_STR_CLOCK[0]);
+    lcd_space(8 - chars);
 }
 
-//Print status line on status screen
+//! @Brief Print status line on status screen
 void lcdui_print_status_line(void)
 {
-	if (IS_SD_PRINTING)
-	{
-		if (strcmp(longFilenameOLD, (card.longFilename[0] ? card.longFilename : card.filename)) != 0)
-		{
-			memset(longFilenameOLD, '\0', strlen(longFilenameOLD));
-			sprintf_P(longFilenameOLD, PSTR("%s"), (card.longFilename[0] ? card.longFilename : card.filename));
-			scrollstuff = 0;
-		}
-	}
+    if (heating_status) { // If heating flag, show progress of heating
+        heating_status_counter++;
+        if (heating_status_counter > 13) {
+            heating_status_counter = 0;
+        }
+        lcd_set_cursor(7, 3);
+        lcd_space(13);
 
-	if (heating_status)
-	{ // If heating flag, show progress of heating
-		heating_status_counter++;
-		if (heating_status_counter > 13)
-		{
-			heating_status_counter = 0;
-		}
-		lcd_set_cursor(7, 3);
-		lcd_puts_P(PSTR("             "));
+        for (unsigned int dots = 0; dots < heating_status_counter; dots++) {
+            lcd_putc_at(7 + dots, 3, '.');
+        }
+        switch (heating_status) {
+        case 1:
+            lcd_puts_at_P(0, 3, _T(MSG_HEATING));
+            break;
+        case 2:
+            lcd_puts_at_P(0, 3, _T(MSG_HEATING_COMPLETE));
+            heating_status = 0;
+            heating_status_counter = 0;
+            break;
+        case 3:
+            lcd_puts_at_P(0, 3, _T(MSG_BED_HEATING));
+            break;
+        case 4:
+            lcd_puts_at_P(0, 3, _T(MSG_BED_DONE));
+            heating_status = 0;
+            heating_status_counter = 0;
+            break;
+        default:
+            break;
+        }
+    }
+    else if ((IS_SD_PRINTING) && (custom_message_type == CustomMsg::Status)) { // If printing from SD, show what we are printing
+		const char* longFilenameOLD = (card.longFilename[0] ? card.longFilename : card.filename);
+        if(strlen(longFilenameOLD) > LCD_WIDTH) {
+            int inters = 0;
+            int gh = scrollstuff;
+            while (((gh - scrollstuff) < LCD_WIDTH) && (inters == 0)) {
+                if (longFilenameOLD[gh] == '\0') {
+                    lcd_set_cursor(gh - scrollstuff, 3);
+                    lcd_print(longFilenameOLD[gh - 1]);
+                    scrollstuff = 0;
+                    gh = scrollstuff;
+                    inters = 1;
+                } else {
+                    lcd_set_cursor(gh - scrollstuff, 3);
+                    lcd_print(longFilenameOLD[gh - 1]);
+                    gh++;
+                }
+            }
+            scrollstuff++;
+        } else {
+            lcd_printf_P(PSTR("%-20s"), longFilenameOLD);
+        }
+    } else { // Otherwise check for other special events
+        switch (custom_message_type) {
+        case CustomMsg::MsgUpdate: //Short message even while printing from SD
+        case CustomMsg::Status: // Nothing special, print status message normally
+        case CustomMsg::M0Wait: // M0/M1 Wait command working even from SD
+            lcd_print(lcd_status_message);
+        break;
+        case CustomMsg::MeshBedLeveling: // If mesh bed leveling in progress, show the status
+            if (custom_message_state > 10) {
+                lcd_set_cursor(0, 3);
+                lcd_space(LCD_WIDTH);
+                lcd_puts_at_P(0, 3, _T(MSG_CALIBRATE_Z_AUTO));
+                lcd_puts_P(PSTR(" : "));
+                lcd_print(custom_message_state-10);
+            } else {
+                if (custom_message_state == 3)
+                {
+                    lcd_puts_P(_T(WELCOME_MSG));
+                    lcd_setstatuspgm(_T(WELCOME_MSG));
+                    custom_message_type = CustomMsg::Status;
+                }
+                if (custom_message_state > 3 && custom_message_state <= 10 ) {
+                    lcd_set_cursor(0, 3);
+                    lcd_space(19);
+                    lcd_puts_at_P(0, 3, _i("Calibration done"));////MSG_HOMEYZ_DONE c=20
+                    custom_message_state--;
+                }
+            }
+            break;
+        case CustomMsg::FilamentLoading: // If loading filament, print status
+            lcd_print(lcd_status_message);
+            break;
+        case CustomMsg::PidCal: // PID tuning in progress
+            lcd_print(lcd_status_message);
+            if (pid_cycle <= pid_number_of_cycles && custom_message_state > 0) {
+                lcd_set_cursor(10, 3);
+                lcd_print(itostr3(pid_cycle));
+                lcd_print('/');
+                lcd_print(itostr3left(pid_number_of_cycles));
+            }
+            break;
+        case CustomMsg::TempCal: // PINDA temp calibration in progress
+            char statusLine[LCD_WIDTH + 1];
+            sprintf_P(statusLine, PSTR("%-20S"), _T(MSG_TEMP_CALIBRATION));
+            char progress[4];
+            sprintf_P(progress, PSTR("%d/6"), custom_message_state);
+            memcpy(statusLine + 12, progress, sizeof(progress) - 1);
+            lcd_set_cursor(0, 3);
+            lcd_print(statusLine);
+            break;
+        case CustomMsg::TempCompPreheat: // temp compensation preheat
+            lcd_puts_at_P(0, 3, _i("PINDA Heating"));////MSG_PINDA_PREHEAT c=20
+            if (custom_message_state <= PINDA_HEAT_T) {
+                lcd_puts_P(PSTR(": "));
+                lcd_print(custom_message_state); //seconds
+                lcd_print(' ');
+            }
+            break;
+        case CustomMsg::Resuming: //Resuming
+            lcd_puts_at_P(0, 3, _T(MSG_RESUMING_PRINT));
+            break;
+        }
+    }
 
-		for (unsigned int dots = 0; dots < heating_status_counter; dots++)
-		{
-			lcd_set_cursor(7 + dots, 3);
-			lcd_print('.');
-		}
-		switch (heating_status)
-		{
-		case 1:
-			lcd_set_cursor(0, 3);
-			lcd_puts_P(_T(MSG_HEATING));
-			break;
-		case 2:
-			lcd_set_cursor(0, 3);
-			lcd_puts_P(_T(MSG_HEATING_COMPLETE));
-			heating_status = 0;
-			heating_status_counter = 0;
-			break;
-		case 3:
-			lcd_set_cursor(0, 3);
-			lcd_puts_P(_T(MSG_BED_HEATING));
-			break;
-		case 4:
-			lcd_set_cursor(0, 3);
-			lcd_puts_P(_T(MSG_BED_DONE));
-			heating_status = 0;
-			heating_status_counter = 0;
-			break;
-		default:
-			break;
-		}
-	}
-	else if ((IS_SD_PRINTING) && (custom_message_type == CustomMsg::Status))
-	{ // If printing from SD, show what we are printing
-		if(strlen(longFilenameOLD) > LCD_WIDTH)
-		{
-			int inters = 0;
-			int gh = scrollstuff;
-			while (((gh - scrollstuff) < LCD_WIDTH) && (inters == 0))
-			{
-				if (longFilenameOLD[gh] == '\0')
-				{
-					lcd_set_cursor(gh - scrollstuff, 3);
-					lcd_print(longFilenameOLD[gh - 1]);
-					scrollstuff = 0;
-					gh = scrollstuff;
-					inters = 1;
-				}
-				else
-				{
-					lcd_set_cursor(gh - scrollstuff, 3);
-					lcd_print(longFilenameOLD[gh - 1]);
-					gh++;
-				}
-			}
-			scrollstuff++;
-		}
-		else
-		{
-			lcd_printf_P(PSTR("%-20s"), longFilenameOLD);
-		}
-	}
-	else
-	{ // Otherwise check for other special events
-   		switch (custom_message_type)
-		{
-		case CustomMsg::Status: // Nothing special, print status message normally
-			lcd_print(lcd_status_message);
-			break;
-		case CustomMsg::MeshBedLeveling: // If mesh bed leveling in progress, show the status
-			if (custom_message_state > 10)
-			{
-				lcd_set_cursor(0, 3);
-				lcd_puts_P(PSTR("                    "));
-				lcd_set_cursor(0, 3);
-				lcd_puts_P(_T(MSG_CALIBRATE_Z_AUTO));
-				lcd_puts_P(PSTR(" : "));
-				lcd_print(custom_message_state-10);
-			}
-			else
-			{
-				if (custom_message_state == 3)
-				{
-					lcd_puts_P(_T(WELCOME_MSG));
-					lcd_setstatuspgm(_T(WELCOME_MSG));
-					custom_message_type = CustomMsg::Status;
-				}
-				if (custom_message_state > 3 && custom_message_state <= 10 )
-				{
-					lcd_set_cursor(0, 3);
-					lcd_puts_P(PSTR("                   "));
-					lcd_set_cursor(0, 3);
-					lcd_puts_P(_i("Calibration done"));////MSG_HOMEYZ_DONE
-					custom_message_state--;
-				}
-			}
-			break;
-		case CustomMsg::FilamentLoading: // If loading filament, print status
-			lcd_print(lcd_status_message);
-			break;
-		case CustomMsg::PidCal: // PID tuning in progress
-			lcd_print(lcd_status_message);
-			if (pid_cycle <= pid_number_of_cycles && custom_message_state > 0)
-			{
-				lcd_set_cursor(10, 3);
-				lcd_print(itostr3(pid_cycle));
-				lcd_print('/');
-				lcd_print(itostr3left(pid_number_of_cycles));
-			}
-			break;
-		case CustomMsg::TempCal: // PINDA temp calibration in progress
-			{
-				char statusLine[LCD_WIDTH + 1];
-				sprintf_P(statusLine, PSTR("%-20S"), _T(MSG_TEMP_CALIBRATION));
-				char progress[4];
-				sprintf_P(progress, PSTR("%d/6"), custom_message_state);
-				memcpy(statusLine + 12, progress, sizeof(progress) - 1);
-				lcd_set_cursor(0, 3);
-				lcd_print(statusLine);
-			}
-			break;
-		case CustomMsg::TempCompPreheat: // temp compensation preheat
-			lcd_set_cursor(0, 3);
-			lcd_puts_P(_i("PINDA Heating"));////MSG_PINDA_PREHEAT c=20 r=1
-			if (custom_message_state <= PINDA_HEAT_T)
-			{
-				lcd_puts_P(PSTR(": "));
-				lcd_print(custom_message_state); //seconds
-				lcd_print(' ');
-			}
-			break;
-		}
-	}
-    
     // Fill the rest of line to have nice and clean output
-	for(int fillspace = 0; fillspace < 20; fillspace++)
-		if ((lcd_status_message[fillspace] <= 31 ))
-			lcd_print(' ');
+    for(int fillspace = 0; fillspace < LCD_WIDTH; fillspace++)
+        if ((lcd_status_message[fillspace] <= 31 ))
+            lcd_print(' ');
 }
 
 //! @brief Show Status Screen
@@ -1001,6 +790,36 @@ void lcd_status_screen()                          // NOT static due to using ins
 		}
 	}
 
+#ifdef ULTIPANEL_FEEDMULTIPLY
+	// Dead zone at 100% feedrate
+	if ((feedmultiply < 100 && (feedmultiply + int(lcd_encoder)) > 100) ||
+		(feedmultiply > 100 && (feedmultiply + int(lcd_encoder)) < 100))
+	{
+		lcd_encoder = 0;
+		feedmultiply = 100;
+	}
+	if (feedmultiply == 100 && int(lcd_encoder) > ENCODER_FEEDRATE_DEADZONE)
+	{
+		feedmultiply += int(lcd_encoder) - ENCODER_FEEDRATE_DEADZONE;
+		lcd_encoder = 0;
+	}
+	else if (feedmultiply == 100 && int(lcd_encoder) < -ENCODER_FEEDRATE_DEADZONE)
+	{
+		feedmultiply += int(lcd_encoder) + ENCODER_FEEDRATE_DEADZONE;
+		lcd_encoder = 0;
+	}
+	else if (feedmultiply != 100)
+	{
+		feedmultiply += int(lcd_encoder);
+		lcd_encoder = 0;
+	}
+#endif //ULTIPANEL_FEEDMULTIPLY
+
+	if (feedmultiply < 10)
+		feedmultiply = 10;
+	else if (feedmultiply > 999)
+		feedmultiply = 999;
+
 	if (lcd_status_update_delay)
 		lcd_status_update_delay--;
 	else
@@ -1077,36 +896,6 @@ void lcd_status_screen()                          // NOT static due to using ins
 		menu_submenu(lcd_main_menu);
 		lcd_refresh(); // to maybe revive the LCD if static electricity killed it.
 	}
-
-#ifdef ULTIPANEL_FEEDMULTIPLY
-	// Dead zone at 100% feedrate
-	if ((feedmultiply < 100 && (feedmultiply + int(lcd_encoder)) > 100) ||
-		(feedmultiply > 100 && (feedmultiply + int(lcd_encoder)) < 100))
-	{
-		lcd_encoder = 0;
-		feedmultiply = 100;
-	}
-	if (feedmultiply == 100 && int(lcd_encoder) > ENCODER_FEEDRATE_DEADZONE)
-	{
-		feedmultiply += int(lcd_encoder) - ENCODER_FEEDRATE_DEADZONE;
-		lcd_encoder = 0;
-	}
-	else if (feedmultiply == 100 && int(lcd_encoder) < -ENCODER_FEEDRATE_DEADZONE)
-	{
-		feedmultiply += int(lcd_encoder) + ENCODER_FEEDRATE_DEADZONE;
-		lcd_encoder = 0;
-	}
-	else if (feedmultiply != 100)
-	{
-		feedmultiply += int(lcd_encoder);
-		lcd_encoder = 0;
-	}
-#endif //ULTIPANEL_FEEDMULTIPLY
-
-	if (feedmultiply < 10)
-		feedmultiply = 10;
-	else if (feedmultiply > 999)
-		feedmultiply = 999;
 }
 
 void lcd_commands()
@@ -1115,7 +904,7 @@ void lcd_commands()
 	{
 		if (!blocks_queued() && !homing_flag)
 		{
-			lcd_setstatuspgm(_i("Print paused"));////MSG_PRINT_PAUSED c=20 r=1
+			lcd_setstatuspgm(_i("Print paused"));////MSG_PRINT_PAUSED c=20
             lcd_commands_type = LcdCommands::Idle;
             lcd_commands_step = 0;
             long_pause();
@@ -1487,7 +1276,6 @@ void lcd_commands()
 
 		if (lcd_commands_step == 1 && !blocks_queued())
 		{
-			lcd_confirm_print();
 			lcd_commands_step = 0;
 			lcd_commands_type = LcdCommands::Idle;
 		}
@@ -1536,13 +1324,13 @@ void lcd_commands()
 			strcat(cmd1, ftostr3(pid_temp));
 			// setting the correct target temperature (for visualization) is done in PID_autotune
 			enquecommand(cmd1);
-			lcd_setstatuspgm(_i("PID cal.           "));////MSG_PID_RUNNING c=20 r=1
+			lcd_setstatuspgm(_i("PID cal."));////MSG_PID_RUNNING c=20
 			lcd_commands_step = 2;
 		}
 		if (lcd_commands_step == 2 && pid_tuning_finished) { //saving to eeprom
 			pid_tuning_finished = false;
 			custom_message_state = 0;
-			lcd_setstatuspgm(_i("PID cal. finished"));////MSG_PID_FINISHED c=20 r=1
+			lcd_setstatuspgm(_i("PID cal. finished"));////MSG_PID_FINISHED c=20
 			setAllTargetHotends(0);  // reset all hotends temperature including the number displayed on the main screen
 			if (_Kp != 0 || _Ki != 0 || _Kd != 0) {
 			strcpy(cmd1, "M301 P");
@@ -1580,17 +1368,22 @@ void lcd_return_to_status()
     eFilamentAction = FilamentAction::None; // i.e. non-autoLoad
 }
 
-//! @brief Pause print, disable nozzle heater, move to park position
+//! @brief Pause print, disable nozzle heater, move to park position, send host action "paused"
 void lcd_pause_print()
 {
     stop_and_save_print_to_ram(0.0, -default_retraction);
     lcd_return_to_status();
     isPrintPaused = true;
-    if (LcdCommands::Idle == lcd_commands_type)
-    {
+    if (LcdCommands::Idle == lcd_commands_type) {
         lcd_commands_type = LcdCommands::LongPause;
     }
-	SERIAL_PROTOCOLLNRPGM(MSG_OCTOPRINT_PAUSED); //pause for octoprint
+    SERIAL_PROTOCOLLNRPGM(MSG_OCTOPRINT_PAUSED);
+}
+
+//! @brief Send host action "pause"
+void lcd_pause_usb_print()
+{
+    SERIAL_PROTOCOLLNRPGM(MSG_OCTOPRINT_PAUSE);
 }
 
 
@@ -1606,7 +1399,6 @@ static void lcd_cooldown()
   setAllTargetHotends(0);
   setTargetBed(0);
   fanSpeed = 0;
-  eFilamentAction = FilamentAction::None;
   lcd_return_to_status();
 }
 
@@ -1636,10 +1428,10 @@ static void pgmtext_with_colon(const char *ipgmLabel, char *dst, uint8_t dstSize
 //!
 //! @code{.unparsed}
 //! |01234567890123456789|
-//! |Nozzle FAN: 0000 RPM|	FAN c=10 r=1  SPEED c=3 r=1
-//! |Print FAN:  0000 RPM|	FAN c=10 r=1  SPEED c=3 r=1
-//! |Fil. Xd:000 Yd:000  |	Fil. c=4 r=1
-//! |Int:  000 Shut: 000 |	Int: c=4 r=1  Shut: c=4 r=1
+//! |Nozzle FAN: 0000 RPM|	MSG_NOZZLE_FAN c=10  SPEED c=3
+//! |Print FAN:  0000 RPM|	MSG_PRINT_FAN c=10  SPEED c=3
+//! |                    |
+//! |                    |
 //! ----------------------
 //! @endcode
 //! @todo Positioning of the messages and values on LCD aren't fixed to their exact place. This causes issues with translations.
@@ -1651,49 +1443,23 @@ void lcd_menu_extruder_info()                     // NOT static due to using ins
     lcd_home();
     static const size_t maxChars = 12;
     char nozzle[maxChars], print[maxChars];
-    pgmtext_with_colon(_i("Nozzle FAN"), nozzle, maxChars);  ////c=10 r=1
-    pgmtext_with_colon(_i("Print FAN"), print, maxChars);  ////c=10 r=1
-    lcd_printf_P(_N("%s %4d RPM\n" "%s %4d RPM\n"), nozzle, 60*fan_speed[0], print, 60*fan_speed[1] ); 
-
-#ifdef PAT9125
-	// Display X and Y difference from Filament sensor    
-    // Display Light intensity from Filament sensor
-    //  Frame_Avg register represents the average brightness of all pixels within a frame (324 pixels). This
-    //  value ranges from 0(darkest) to 255(brightest).
-    // Display LASER shutter time from Filament sensor
-    //  Shutter register is an index of LASER shutter time. It is automatically controlled by the chip's internal
-    //  auto-exposure algorithm. When the chip is tracking on a good reflection surface, the Shutter is small.
-    //  When the chip is tracking on a poor reflection surface, the Shutter is large. Value ranges from 0 to 46.
-	if (mmu_enabled == false)
-	{
-		if (!fsensor_enabled)
-			lcd_puts_P(_N("Filament sensor\n" "is disabled."));
-		else
-		{
-			if (!moves_planned() && !IS_SD_PRINTING && !is_usb_printing && (lcd_commands_type != LcdCommands::Layer1Cal))
-				pat9125_update();
-			lcd_printf_P(_N(
-				"Fil. Xd:%3d Yd:%3d\n" ////c=4 r=1
-				"Int: %3d  " ////c=4 r=1
-				"Shut: %3d"  ////c=4 r=1
-			),
-				pat9125_x, pat9125_y,
-				pat9125_b, pat9125_s
-			);
-		}
-	}
-#endif //PAT9125
-    
+    pgmtext_with_colon(_i("Nozzle FAN"), nozzle, maxChars);  ////MSG_NOZZLE_FAN c=10
+    pgmtext_with_colon(_i("Print FAN"), print, maxChars);  ////MSG_PRINT_FAN c=10
+	lcd_printf_P(_N("%s %4d RPM\n" "%s %4d RPM\n"), nozzle, 60*fan_speed[0], print, 60*fan_speed[1] ); 
     menu_back_if_clicked();
 }
 
+static uint16_t __attribute__((noinline)) clamp999(uint16_t v){
+    return v > 999 ? 999 : v;
+}
+
 //! @brief Show Fails Statistics MMU
 //!
 //! @code{.unparsed}
 //! |01234567890123456789|
-//! | Main               |	c=18 r=1
-//! | Last print         |	c=18 r=1
-//! | Total              |	c=18 r=1
+//! | Main               |	MSG_MAIN c=18
+//! | Last print         |	MSG_LAST_PRINT c=18
+//! | Total              |	MSG_TOTAL c=6
 //! |                    |
 //! ----------------------
 //! @endcode
@@ -1701,8 +1467,8 @@ static void lcd_menu_fails_stats_mmu()
 {
 	MENU_BEGIN();
 	MENU_ITEM_BACK_P(_T(MSG_MAIN));
-	MENU_ITEM_SUBMENU_P(_i("Last print"), lcd_menu_fails_stats_mmu_print); ////c=18 r=1
-	MENU_ITEM_SUBMENU_P(_i("Total"), lcd_menu_fails_stats_mmu_total); ////c=18 r=1
+	MENU_ITEM_SUBMENU_P(_T(MSG_LAST_PRINT), lcd_menu_fails_stats_mmu_print);
+	MENU_ITEM_SUBMENU_P(_T(MSG_TOTAL), lcd_menu_fails_stats_mmu_total);
 	MENU_END();
 }
 
@@ -1710,9 +1476,9 @@ static void lcd_menu_fails_stats_mmu()
 //!
 //! @code{.unparsed}
 //! |01234567890123456789|
-//! |Last print failures |	c=20 r=1
-//! | MMU fails:      000|	c=14 r=1
-//! | MMU load fails: 000|	c=14 r=1
+//! |Last print failures |	MSG_LAST_PRINT_FAILURES c=20
+//! | MMU fails       000|	MSG_MMU_FAILS c=15
+//! | MMU load fails  000|	MSG_MMU_LOAD_FAILS c=15
 //! |                    |
 //! ----------------------
 //! @endcode
@@ -1720,13 +1486,11 @@ static void lcd_menu_fails_stats_mmu()
 static void lcd_menu_fails_stats_mmu_print()
 {
 	lcd_timeoutToStatus.stop(); //infinite timeout
-    uint8_t fails = eeprom_read_byte((uint8_t*)EEPROM_MMU_FAIL);
-    uint16_t load_fails = eeprom_read_byte((uint8_t*)EEPROM_MMU_LOAD_FAIL);
     lcd_home();
     lcd_printf_P(PSTR("%S\n" " %-16.16S%-3d\n" " %-16.16S%-3d"), 
-        _i("Last print failures"), ////c=20 r=1
-        _i("MMU fails"), fails, ////c=14 r=1
-        _i("MMU load fails"), load_fails); ////c=14 r=1
+        _T(MSG_LAST_PRINT_FAILURES),
+        _T(MSG_MMU_FAILS), clamp999( eeprom_read_byte((uint8_t*)EEPROM_MMU_FAIL) ),
+        _T(MSG_MMU_LOAD_FAILS), clamp999( eeprom_read_byte((uint8_t*)EEPROM_MMU_LOAD_FAIL) ));
     menu_back_if_clicked_fb();
 }
 
@@ -1734,10 +1498,10 @@ static void lcd_menu_fails_stats_mmu_print()
 //!
 //! @code{.unparsed}
 //! |01234567890123456789|
-//! |Total failures      |	c=20 r=1
-//! | MMU fails:      000|	c=14 r=1
-//! | MMU load fails: 000|	c=14 r=1
-//! | MMU power fails:000|	c=14 r=1
+//! |Total failures      |	MSG_TOTAL_FAILURES c=20
+//! | MMU fails       000|	MSG_MMU_FAILS c=15
+//! | MMU load fails  000|	MSG_MMU_LOAD_FAILS c=15
+//! | MMU power fails 000|	MSG_MMU_POWER_FAILS c=15
 //! ----------------------
 //! @endcode
 //! @todo Positioning of the messages and values on LCD aren't fixed to their exact place. This causes issues with translations.
@@ -1745,14 +1509,12 @@ static void lcd_menu_fails_stats_mmu_total()
 {
 	mmu_command(MmuCmd::S3);
 	lcd_timeoutToStatus.stop(); //infinite timeout
-    uint8_t fails = eeprom_read_byte((uint8_t*)EEPROM_MMU_FAIL_TOT);
-    uint16_t load_fails = eeprom_read_byte((uint8_t*)EEPROM_MMU_LOAD_FAIL_TOT);
     lcd_home();
     lcd_printf_P(PSTR("%S\n" " %-16.16S%-3d\n" " %-16.16S%-3d\n" " %-16.16S%-3d"), 
-        _i("Total failures"), ////c=20 r=1
-        _i("MMU fails"), fails, ////c=14 r=1
-        _i("MMU load fails"), load_fails, ////c=14 r=1
-        _i("MMU power fails"), mmu_power_failures); ////c=14 r=1
+        _T(MSG_TOTAL_FAILURES),
+        _T(MSG_MMU_FAILS), clamp999( eeprom_read_word((uint16_t*)EEPROM_MMU_FAIL_TOT) ),
+        _T(MSG_MMU_LOAD_FAILS), clamp999( eeprom_read_word((uint16_t*)EEPROM_MMU_LOAD_FAIL_TOT) ),
+        _i("MMU power fails"), clamp999( mmu_power_failures )); ////MSG_MMU_POWER_FAILS c=15
     menu_back_if_clicked_fb();
 }
 
@@ -1763,26 +1525,24 @@ static const char failStatsFmt[] PROGMEM = "%S\n" " %-16.16S%-3d\n" " %-16.16S%-
 //!
 //! @code{.unparsed}
 //! |01234567890123456789|
-//! |Total failures      |	c=20 r=1
-//! | Power failures: 000|	c=14 r=1
-//! | Filam. runouts: 000|	c=14 r=1
-//! | Crash   X:000 Y:000|	c=7 r=1
+//! |Total failures      |	MSG_TOTAL_FAILURES c=20
+//! | Power failures  000|	MSG_POWER_FAILURES c=15
+//! | Fil. runouts    000|	MSG_FIL_RUNOUTS c=15
+//! | Crash   X:000 Y:000|	MSG_CRASH c=7
 //! ----------------------
 //! @endcode
 //! @todo Positioning of the messages and values on LCD aren't fixed to their exact place. This causes issues with translations.
 static void lcd_menu_fails_stats_total()
 {
 	lcd_timeoutToStatus.stop(); //infinite timeout
-    uint16_t power = eeprom_read_word((uint16_t*)EEPROM_POWER_COUNT_TOT);
-    uint16_t filam = eeprom_read_word((uint16_t*)EEPROM_FERROR_COUNT_TOT);
-    uint16_t crashX = eeprom_read_word((uint16_t*)EEPROM_CRASH_COUNT_X_TOT);
-    uint16_t crashY = eeprom_read_word((uint16_t*)EEPROM_CRASH_COUNT_Y_TOT);
-    lcd_home();
+	lcd_home();
     lcd_printf_P(failStatsFmt, 
-        _i("Total failures"),   ////c=20 r=1
-        _i("Power failures"), power,   ////c=14 r=1
-        _i("Filam. runouts"), filam,   ////c=14 r=1
-        _i("Crash"), crashX, crashY);  ////c=7 r=1
+        _T(MSG_TOTAL_FAILURES),
+        _T(MSG_POWER_FAILURES), clamp999( eeprom_read_word((uint16_t*)EEPROM_POWER_COUNT_TOT) ),
+        _T(MSG_FIL_RUNOUTS), clamp999( eeprom_read_word((uint16_t*)EEPROM_FERROR_COUNT_TOT) ),
+        _T(MSG_CRASH),
+            clamp999( eeprom_read_word((uint16_t*)EEPROM_CRASH_COUNT_X_TOT) ), 
+            clamp999( eeprom_read_word((uint16_t*)EEPROM_CRASH_COUNT_Y_TOT) ));
     menu_back_if_clicked_fb();
 }
 
@@ -1790,12 +1550,23 @@ static void lcd_menu_fails_stats_total()
 //!
 //! @code{.unparsed}
 //! |01234567890123456789|
-//! |Last print failures |	c=20 r=1
-//! | Power failures  000|	c=14 r=1
-//! | Filam. runouts  000|	c=14 r=1
-//! | Crash   X:000 Y:000|	c=7 r=1
+//! |Last print failures |	MSG_LAST_PRINT_FAILURES c=20
+//! | Power failures  000|	MSG_POWER_FAILURES c=15
+//! | Fil. runouts    000|	MSG_FIL_RUNOUTS c=15
+//! | Crash   X 000 Y 000|	MSG_CRASH c=7
 //! ----------------------
 //! @endcode
+//! @brief Show Last Print Failures Statistics with PAT9125
+//!
+//! @code{.unparsed}
+//! |01234567890123456789|
+//! |Last print failures |	MSG_LAST_PRINT_FAILURES c=20
+//! | Power failures  000|	MSG_POWER_FAILURES c=14
+//! | Runouts H 000 S 000|	MSG_RUNOUTS c=7
+//! | Crash   X:000 Y:000|	MSG_CRASH c=7
+//! ----------------------
+//! @endcode
+
 //! @todo Positioning of the messages and values on LCD aren't fixed to their exact place. This causes issues with translations.
 static void lcd_menu_fails_stats_print()
 {
@@ -1807,20 +1578,20 @@ static void lcd_menu_fails_stats_print()
     lcd_home();
 #ifndef PAT9125
     lcd_printf_P(failStatsFmt,
-        _i("Last print failures"),  ////c=20 r=1
-        _i("Power failures"), power,  ////c=14 r=1
-        _i("Filam. runouts"), filam,  ////c=14 r=1
-        _i("Crash"), crashX, crashY);  ////c=7 r=1
+        _T(MSG_LAST_PRINT_FAILURES),
+        _T(MSG_POWER_FAILURES), power,
+        _T(MSG_FIL_RUNOUTS), filam,
+        _T(MSG_CRASH), crashX, crashY);
 #else
     // On the MK3 include detailed PAT9125 statistics about soft failures
     lcd_printf_P(PSTR("%S\n"
                       " %-16.16S%-3d\n"
                       " %-7.7S H %-3d S %-3d\n"
                       " %-7.7S X %-3d Y %-3d"),
-                 _i("Last print failures"), ////c=20 r=1
-                 _i("Power failures"), power, ////c=14 r=1
-                 _i("Runouts"), filam, fsensor_softfail, //c=7
-                 _i("Crash"), crashX, crashY);  ////c=7 r=1
+                 _T(MSG_LAST_PRINT_FAILURES),
+                 _T(MSG_POWER_FAILURES), power,
+                 _i("Runouts"), filam, fsensor_softfail, //MSG_RUNOUTS c=7
+                 _T(MSG_CRASH), crashX, crashY);
 #endif
     menu_back_if_clicked_fb();
 }
@@ -1833,9 +1604,9 @@ static void lcd_menu_fails_stats_print()
 //! 
 //! @code{.unparsed}
 //! |01234567890123456789|
-//! | Main               |	c=18 r=1
-//! | Last print         |	c=18 r=1
-//! | Total              |	c=18 r=1
+//! | Main               |	MSG_MAIN c=18
+//! | Last print         |	MSG_LAST_PRINT c=18
+//! | Total              |	MSG_TOTAL c=6
 //! |                    |
 //! ----------------------
 //! @endcode
@@ -1844,8 +1615,8 @@ static void lcd_menu_fails_stats()
 {
 	MENU_BEGIN();
 	MENU_ITEM_BACK_P(_T(MSG_MAIN));
-	MENU_ITEM_SUBMENU_P(_i("Last print"), lcd_menu_fails_stats_print);  ////c=18 r=1
-	MENU_ITEM_SUBMENU_P(_i("Total"), lcd_menu_fails_stats_total);  ////c=18 r=1
+	MENU_ITEM_SUBMENU_P(_T(MSG_LAST_PRINT), lcd_menu_fails_stats_print);
+	MENU_ITEM_SUBMENU_P(_T(MSG_TOTAL), lcd_menu_fails_stats_total);
 	MENU_END();
 }
 
@@ -1860,10 +1631,10 @@ static const char failStatsFmt[] PROGMEM = "%S\n" " %-16.16S%-3d\n" "%S\n" " %-1
 //! Example screen:
 //! @code{.unparsed}
 //! |01234567890123456789|
-//! |Last print failures |	c=20 r=1
-//! | Filam. runouts  000|	c=14 r=1
-//! |Total failures      |	c=20 r=1
-//! | Filam. runouts  000|	c=14 r=1
+//! |Last print failures |	MSG_LAST_PRINT_FAILURES c=20
+//! | Fil. runouts    000|	MSG_FIL_RUNOUTS c=15
+//! |Total failures      |	MSG_TOTAL_FAILURES c=20
+//! | Fil. runouts    000|	MSG_FIL_RUNOUTS c=15
 //! ----------------------
 //! @endcode
 //! @todo Positioning of the messages and values on LCD aren't fixed to their exact place. This causes issues with translations.
@@ -1871,13 +1642,13 @@ static void lcd_menu_fails_stats()
 {
 	lcd_timeoutToStatus.stop(); //infinite timeout
     uint8_t filamentLast = eeprom_read_byte((uint8_t*)EEPROM_FERROR_COUNT);
-    uint16_t filamentTotal = eeprom_read_word((uint16_t*)EEPROM_FERROR_COUNT_TOT);
+    uint16_t filamentTotal = clamp999( eeprom_read_word((uint16_t*)EEPROM_FERROR_COUNT_TOT) );
 	lcd_home();
 	lcd_printf_P(failStatsFmt, 
-        _i("Last print failures"),   ////c=20 r=1
-        _i("Filam. runouts"), filamentLast,   ////c=14 r=1
-        _i("Total failures"),  ////c=20 r=1
-        _i("Filam. runouts"), filamentTotal);   ////c=14 r=1
+        _T(MSG_LAST_PRINT_FAILURES),
+        _T(MSG_FIL_RUNOUTS), filamentLast,
+        _T(MSG_TOTAL_FAILURES),
+        _T(MSG_FIL_RUNOUTS), filamentTotal);
 
 	menu_back_if_clicked();
 }
@@ -1903,10 +1674,10 @@ extern char* __malloc_heap_end;
 //!
 //! @code{.unparsed}
 //! |01234567890123456789|
-//! |RAM statistics      |	c=20 r=1
-//! | SP_min:        0000|	c=14 r=1
-//! | heap_start:    0000|	c=14 r=1
-//! | heap_end:      0000|	c=14 r=1
+//! |RAM statistics      |	c=20
+//! | SP_min:        0000|	c=14
+//! | heap_start:    0000|	c=14
+//! | heap_end:      0000|	c=14
 //! ----------------------
 //! @endcode
 //! @todo Positioning of the messages and values on LCD aren't fixed to their exact place. This causes issues with translations.
@@ -1914,10 +1685,10 @@ static void lcd_menu_debug()
 {
 #ifdef DEBUG_STACK_MONITOR
 	lcd_home();
-	lcd_printf_P(PSTR("RAM statistics\n"  ////c=20 r=1
-        " SP_min: 0x%04x\n"   ////c=14 r=1
-        " heap_start: 0x%04x\n"   ////c=14 r=1
-        " heap_end: 0x%04x"), SP_min, __malloc_heap_start, __malloc_heap_end);  ////c=14 r=1
+	lcd_printf_P(PSTR("RAM statistics\n"  ////c=20
+        " SP_min: 0x%04x\n"   ////c=14
+        " heap_start: 0x%04x\n"   ////c=14
+        " heap_end: 0x%04x"), SP_min, __malloc_heap_start, __malloc_heap_end);  ////c=14
 #endif //DEBUG_STACK_MONITOR
 
 	menu_back_if_clicked_fb();
@@ -1938,10 +1709,10 @@ static void lcd_menu_temperatures_line(const char *ipgmLabel, int value){
 //!
 //! @code{.unparsed}
 //! |01234567890123456789|
-//! | Nozzle:        000D|	c=14 r=1
-//! | Bed:           000D|	c=14 r=1
-//! | Ambient:       000D|	c=14 r=1
-//! | PINDA:         000D|	c=14 r=1
+//! | Nozzle:        000D|	MSG_NOZZLE c=14
+//! | Bed:           000D|	MSG_BEDc=14
+//! | Ambient:       000D|	MSG_AMBIENTc=14
+//! | PINDA:         000D|	MSG_PINDA c=14
 //! ----------------------
 //! D - Degree sysmbol		LCD_STR_DEGREE
 //! @endcode
@@ -1950,13 +1721,13 @@ static void lcd_menu_temperatures()
 {
     lcd_timeoutToStatus.stop(); //infinite timeout
     lcd_home();
-    lcd_menu_temperatures_line( _T(MSG_NOZZLE), (int)current_temperature[0] ); ////c=14 r=1
-    lcd_menu_temperatures_line( _T(MSG_BED), (int)current_temperature_bed );  ////c=14 r=1
+    lcd_menu_temperatures_line( _T(MSG_NOZZLE), (int)current_temperature[0] ); ////MSG_NOZZLE
+    lcd_menu_temperatures_line( _T(MSG_BED), (int)current_temperature_bed );  ////MSG_BED
 #ifdef AMBIENT_THERMISTOR
-    lcd_menu_temperatures_line( _i("Ambient"), (int)current_temperature_ambient );  ////c=14 r=1
+    lcd_menu_temperatures_line( _i("Ambient"), (int)current_temperature_ambient );  ////MSG_AMBIENT
 #endif //AMBIENT_THERMISTOR
 #ifdef PINDA_THERMISTOR
-    lcd_menu_temperatures_line( _i("PINDA"), (int)current_temperature_pinda );  ////c=14
+    lcd_menu_temperatures_line( _T(MSG_PINDA), (int)current_temperature_pinda );  ////MSG_PINDA
 #endif //PINDA_THERMISTOR
     menu_back_if_clicked();
 }
@@ -1971,9 +1742,9 @@ static void lcd_menu_temperatures()
 //! @code{.unparsed}
 //! |01234567890123456789|
 //! |                    |
-//! | PWR:         00.0V |	c=12 r=1
-//! | Bed:         00.0V |	c=12 r=1
-//! | IR :         00.0V |  c=12 r=1 optional
+//! | PWR:         00.0V |	c=12
+//! | Bed:         00.0V |	c=12
+//! | IR :         00.0V |  c=12 optional
 //! ----------------------
 //! @endcode
 //! @todo Positioning of the messages and values on LCD aren't fixed to their exact place. This causes issues with translations.
@@ -2006,7 +1777,7 @@ static void lcd_menu_voltages()
 static void lcd_menu_belt_status()
 {
 	lcd_home();
-    lcd_printf_P(PSTR("%S\n" " X %d\n" " Y %d"), _i("Belt status"), eeprom_read_word((uint16_t*)(EEPROM_BELTSTATUS_X)), eeprom_read_word((uint16_t*)(EEPROM_BELTSTATUS_Y)));
+    lcd_printf_P(PSTR("%S\n" " X %d\n" " Y %d"), _T(MSG_BELT_STATUS), eeprom_read_word((uint16_t*)(EEPROM_BELTSTATUS_X)), eeprom_read_word((uint16_t*)(EEPROM_BELTSTATUS_Y)));
     menu_back_if_clicked();
 }
 #endif //TMC2130
@@ -2033,13 +1804,68 @@ static void lcd_preheat_menu()
     lcd_generic_preheat_menu();
 }
 
+
+#ifdef MENU_DUMP
+#include "xflash_dump.h"
+
+static void lcd_dump_memory()
+{
+    lcd_beeper_quick_feedback();
+    xfdump_dump();
+    lcd_return_to_status();
+}
+#endif //MENU_DUMP
+#ifdef MENU_SERIAL_DUMP
+#include "Dcodes.h"
+
+static void lcd_serial_dump()
+{
+    serial_dump_and_reset(dump_crash_reason::manual);
+}
+#endif //MENU_SERIAL_DUMP
+
+#if defined(DEBUG_BUILD) && defined(EMERGENCY_HANDLERS)
+#include <avr/wdt.h>
+
+#ifdef WATCHDOG
+static void lcd_wdr_crash()
+{
+    while (1);
+}
+#endif
+
+static uint8_t lcd_stack_crash_(uint8_t arg, uint32_t sp = 0)
+{
+    // populate the stack with an increasing value for ease of testing
+    volatile uint16_t tmp __attribute__((unused)) = sp;
+
+    _delay(arg);
+    uint8_t ret = lcd_stack_crash_(arg, SP);
+
+    // required to avoid tail call elimination and to slow down the stack growth
+    _delay(ret);
+
+    return ret;
+}
+
+static void lcd_stack_crash()
+{
+#ifdef WATCHDOG
+    wdt_disable();
+#endif
+    // delay choosen in order to hit the stack-check in the temperature isr reliably
+    lcd_stack_crash_(10);
+}
+#endif
+
+
 //! @brief Show Support Menu
 //!
 //! @code{.unparsed}
 //! |01234567890123456789|
-//! | Main               |
-//! | Firmware:          |	c=18 r=1
-//! |  3.7.2.-2363       |	c=16 r=1
+//! | Main               |	MSG_MAIN c=18
+//! | Firmware:          |	c=18
+//! |  3.7.2.-2363       |	c=16
 //! | prusa3d.com        |	MSG_PRUSA3D
 //! | forum.prusa3d.com  |	MSG_PRUSA3D_FORUM
 //! | howto.prusa3d.com  |	MSG_PRUSA3D_HOWTO
@@ -2048,7 +1874,7 @@ static void lcd_preheat_menu()
 //! | howto.prusa3d.com  |	ELECTRONICS
 //! | howto.prusa3d.com  |	NOZZLE_TYPE
 //! | --------------     |	STR_SEPARATOR
-//! | Date:              |	c=17 r=1
+//! | Date:              |	c=17
 //! | MMM DD YYYY        |	__DATE__
 //! | --------------     |	STR_SEPARATOR
 //! @endcode
@@ -2056,21 +1882,21 @@ static void lcd_preheat_menu()
 //! If MMU is connected
 //! 
 //! 	@code{.unparsed}
-//! 	| MMU2 connected     |	c=18 r=1
+//! 	| MMU2 connected     |	c=18
 //! 	|  FW: 1.0.6-7064523 |
 //! 	@endcode
 //! 
 //! If MMU is not connected
 //! 
 //! 	@code{.unparsed}
-//! 	| MMU2       N/A     |	c=18 r=1
+//! 	| MMU2       N/A     |	c=18
 //! 	@endcode
 //! 
 //! If Flash Air is connected
 //! 
 //! 	@code{.unparsed}
 //! 	| --------------     |	STR_SEPARATOR
-//! 	| FlashAir IP Addr:  |	c=18 r=1
+//! 	| FlashAir IP Addr:  |	c=18
 //! 	|  192.168.1.100     |
 //! 	@endcode
 //! 
@@ -2084,7 +1910,7 @@ static void lcd_preheat_menu()
 //! If TMC2130 defined
 //! 
 //! 	@code{.unparsed}
-//! 	| Belt status        |	MSG_MENU_BELT_STATUS
+//! 	| Belt status        |	MSG_BELT_STATUS
 //! @endcode
 //! 
 //! @code{.unparsed}
@@ -2096,27 +1922,23 @@ static void lcd_preheat_menu()
 //! 	@code{.unparsed}
 //! 	| Voltages           |	MSG_MENU_VOLTAGES
 //! 	@endcode
-//!
-//!
-//! 	| Experimental       |	c=18
 //! 
 //! 
 //! If DEBUG_BUILD is defined
 //! 
 //! 	@code{.unparsed}
-//! 	| Debug              |	c=18 r=1
+//! 	| Debug              |	c=18
 //! 	@endcode
 //! ----------------------
 //! @endcode
 static void lcd_support_menu()
 {
 	typedef struct
-	{	// 23bytes total
+	{	// 22bytes total
 		int8_t status;                 // 1byte
 		bool is_flash_air;             // 1byte
-		uint8_t ip[4];                 // 4bytes
-		char ip_str[3*4+3+1];          // 16bytes
-		uint8_t experimental_menu_visibility; // 1byte
+		uint32_t ip;                   // 4bytes
+		char ip_str[IP4_STR_SIZE];     // 16bytes
 	} _menu_data_t;
     static_assert(sizeof(menu_data)>= sizeof(_menu_data_t),"_menu_data_t doesn't fit into menu_data");
 	_menu_data_t* _md = (_menu_data_t*)&(menu_data[0]);
@@ -2125,24 +1947,12 @@ static void lcd_support_menu()
         // Menu was entered or SD card status has changed (plugged in or removed).
         // Initialize its status.
         _md->status = 1;
-        _md->is_flash_air = card.ToshibaFlashAir_isEnabled() && card.ToshibaFlashAir_GetIP(_md->ip);
-        if (_md->is_flash_air)
-            sprintf_P(_md->ip_str, PSTR("%d.%d.%d.%d"), 
-                _md->ip[0], _md->ip[1], 
-                _md->ip[2], _md->ip[3]);
-        
-        _md->experimental_menu_visibility = eeprom_read_byte((uint8_t *)EEPROM_EXPERIMENTAL_VISIBILITY);
-        if (_md->experimental_menu_visibility == EEPROM_EMPTY_VALUE)
-        {
-            _md->experimental_menu_visibility = 0;
-            eeprom_update_byte((uint8_t *)EEPROM_EXPERIMENTAL_VISIBILITY, _md->experimental_menu_visibility);
+        _md->is_flash_air = card.ToshibaFlashAir_isEnabled();
+        if (_md->is_flash_air) {
+            card.ToshibaFlashAir_GetIP((uint8_t*)(&_md->ip)); // ip == 0 if it failed
         }
-        
-    } else if (_md->is_flash_air && 
-        _md->ip[0] == 0 && _md->ip[1] == 0 && 
-        _md->ip[2] == 0 && _md->ip[3] == 0 &&
-        ++ _md->status == 16)
-	{
+    } else if (_md->is_flash_air && _md->ip == 0 && ++ _md->status == 16)
+    {
         // Waiting for the FlashAir card to get an IP address from a router. Force an update.
         _md->status = 0;
     }
@@ -2164,15 +1974,15 @@ static void lcd_support_menu()
       MENU_ITEM_BACK_P(PSTR("FW - " FW_version));
   }*/
       
-  MENU_ITEM_BACK_P(_i("prusa3d.com"));////MSG_PRUSA3D
-  MENU_ITEM_BACK_P(_i("forum.prusa3d.com"));////MSG_PRUSA3D_FORUM
-  MENU_ITEM_BACK_P(_i("howto.prusa3d.com"));////MSG_PRUSA3D_HOWTO
+  MENU_ITEM_BACK_P(_n("prusa3d.com"));////MSG_PRUSA3D c=18
+  MENU_ITEM_BACK_P(_n("forum.prusa3d.com"));////MSG_PRUSA3D_FORUM c=18
+  MENU_ITEM_BACK_P(_n("howto.prusa3d.com"));////MSG_PRUSA3D_HOWTO c=18
   MENU_ITEM_BACK_P(STR_SEPARATOR);
   MENU_ITEM_BACK_P(PSTR(FILAMENT_SIZE));
   MENU_ITEM_BACK_P(PSTR(ELECTRONICS));
   MENU_ITEM_BACK_P(PSTR(NOZZLE_TYPE));
   MENU_ITEM_BACK_P(STR_SEPARATOR);
-  MENU_ITEM_BACK_P(_i("Date:"));////MSG_DATE c=17 r=1
+  MENU_ITEM_BACK_P(_i("Date:"));////MSG_DATE c=17
   MENU_ITEM_BACK_P(PSTR(__DATE__));
 
 #ifdef IR_SENSOR_ANALOG
@@ -2184,15 +1994,15 @@ static void lcd_support_menu()
 	MENU_ITEM_BACK_P(STR_SEPARATOR);
 	if (mmu_enabled)
 	{
-		MENU_ITEM_BACK_P(_i("MMU2 connected"));  ////c=18 r=1
-		MENU_ITEM_BACK_P(PSTR(" FW:"));  ////c=17 r=1
+		MENU_ITEM_BACK_P(_i("MMU2 connected"));  ////MSG_MMU_CONNECTED c=18
+		MENU_ITEM_BACK_P(PSTR(" FW:"));  ////c=17
 		if (((menu_item - 1) == menu_line) && lcd_draw_update)
 		{
 		    lcd_set_cursor(6, menu_row);
 			if ((mmu_version > 0) && (mmu_buildnr > 0))
 				lcd_printf_P(PSTR("%d.%d.%d-%d"), mmu_version/100, mmu_version%100/10, mmu_version%10, mmu_buildnr);
 			else
-				lcd_puts_P(_i("unknown")); 
+				lcd_puts_P(_i("unknown"));  ////MSG_UNKNOWN c=13
 		}
 	}
 	else
@@ -2202,34 +2012,59 @@ static void lcd_support_menu()
   // Show the FlashAir IP address, if the card is available.
   if (_md->is_flash_air) {
       MENU_ITEM_BACK_P(STR_SEPARATOR);
-      MENU_ITEM_BACK_P(PSTR("FlashAir IP Addr:"));  //c=18 r=1
-///!      MENU_ITEM(back_RAM, _md->ip_str, 0);
+      MENU_ITEM_BACK_P(PSTR("FlashAir IP Addr:"));  ////MSG_FLASHAIR c=18
+      MENU_ITEM_BACK_P(PSTR(" "));
+      if (((menu_item - 1) == menu_line) && lcd_draw_update) {
+          lcd_set_cursor(2, menu_row);
+          ip4_to_str(_md->ip_str, (uint8_t*)(&_md->ip));
+          lcd_printf_P(PSTR("%s"), _md->ip_str);
+      }
+  }
+  
+  // Show the printer IP address, if it is available.
+  if (IP_address) {
+      
+      MENU_ITEM_BACK_P(STR_SEPARATOR);
+      MENU_ITEM_BACK_P(PSTR("Printer IP Addr:"));  ////MSG_PRINTER_IP c=18
+      MENU_ITEM_BACK_P(PSTR(" "));
+      if (((menu_item - 1) == menu_line) && lcd_draw_update) {
+          lcd_set_cursor(2, menu_row);
+          ip4_to_str(_md->ip_str, (uint8_t*)(&IP_address));
+          lcd_printf_P(PSTR("%s"), _md->ip_str);
+      }
   }
 
   #ifndef MK1BP
   MENU_ITEM_BACK_P(STR_SEPARATOR);
   MENU_ITEM_SUBMENU_P(_i("XYZ cal. details"), lcd_menu_xyz_y_min);////MSG_XYZ_DETAILS c=18
   MENU_ITEM_SUBMENU_P(_i("Extruder info"), lcd_menu_extruder_info);////MSG_INFO_EXTRUDER c=18
-  MENU_ITEM_SUBMENU_P(_i("Sensor info"), lcd_menu_show_sensors_state);////MSG_INFO_SENSORS c=18 r=1
+  MENU_ITEM_SUBMENU_P(_i("Sensor info"), lcd_menu_show_sensors_state);////MSG_INFO_SENSORS c=18
 
 #ifdef TMC2130
-  MENU_ITEM_SUBMENU_P(_i("Belt status"), lcd_menu_belt_status);////MSG_MENU_BELT_STATUS c=18
+  MENU_ITEM_SUBMENU_P(_T(MSG_BELT_STATUS), lcd_menu_belt_status);////MSG_BELT_STATUS c=18
 #endif //TMC2130
     
-  MENU_ITEM_SUBMENU_P(_i("Temperatures"), lcd_menu_temperatures);////MSG_MENU_TEMPERATURES c=18 r=1
+  MENU_ITEM_SUBMENU_P(_i("Temperatures"), lcd_menu_temperatures);////MSG_MENU_TEMPERATURES c=18
 
 #if defined (VOLT_BED_PIN) || defined (VOLT_PWR_PIN)
-  MENU_ITEM_SUBMENU_P(_i("Voltages"), lcd_menu_voltages);////MSG_MENU_VOLTAGES c=18 r=1
+  MENU_ITEM_SUBMENU_P(_i("Voltages"), lcd_menu_voltages);////MSG_MENU_VOLTAGES c=18
 #endif //defined VOLT_BED_PIN || defined VOLT_PWR_PIN
 
-  if (_md->experimental_menu_visibility)
-  {
-    MENU_ITEM_SUBMENU_P(PSTR("Experimental"), lcd_experimental_menu);////MSG_MENU_EXPERIMENTAL c=18
-  }
-
-
+#ifdef MENU_DUMP
+    MENU_ITEM_FUNCTION_P(_i("Dump memory"), lcd_dump_memory);
+#endif //MENU_DUMP
+#ifdef MENU_SERIAL_DUMP
+    if (emergency_serial_dump)
+        MENU_ITEM_FUNCTION_P(_i("Dump to serial"), lcd_serial_dump);
+#endif
 #ifdef DEBUG_BUILD
-  MENU_ITEM_SUBMENU_P(PSTR("Debug"), lcd_menu_debug);////c=18 r=1
+#ifdef EMERGENCY_HANDLERS
+#ifdef WATCHDOG
+    MENU_ITEM_FUNCTION_P(PSTR("WDR crash"), lcd_wdr_crash);
+#endif //WATCHDOG
+    MENU_ITEM_FUNCTION_P(PSTR("Stack crash"), lcd_stack_crash);
+#endif //EMERGENCY_HANDLERS
+  MENU_ITEM_SUBMENU_P(PSTR("Debug"), lcd_menu_debug);////MSG_DEBUG c=18
 #endif /* DEBUG_BUILD */
 
   #endif //MK1BP
@@ -2292,19 +2127,18 @@ uint8_t nLevel;
 
 lcd_set_cursor(0,0);
 lcdui_print_temp(LCD_STR_THERMOMETER[0],(int)degHotend(0),(int)degTargetHotend(0));
+lcd_puts_at_P(0,1, _i("Press the knob"));                 ////MSG_PRESS_KNOB c=20
 lcd_set_cursor(0,2);
-lcd_puts_P(_i("Press the knob"));                 ////MSG_ c=20 r=1
-lcd_set_cursor(0,3);
 switch(eFilamentAction)
      {
      case FilamentAction::Load:
      case FilamentAction::AutoLoad:
      case FilamentAction::MmuLoad:
-          lcd_puts_P(_i("to load filament"));     ////MSG_ c=20 r=1
+          lcd_puts_P(_i("to load filament"));     ////MSG_TO_LOAD_FIL c=20
           break;
      case FilamentAction::UnLoad:
      case FilamentAction::MmuUnLoad:
-          lcd_puts_P(_i("to unload filament"));   ////MSG_ c=20 r=1
+          lcd_puts_P(_i("to unload filament"));   ////MSG_TO_UNLOAD_FIL c=20
           break;
      case FilamentAction::MmuEject:
      case FilamentAction::MmuCut:
@@ -2326,7 +2160,7 @@ if(lcd_clicked())
           {
           case FilamentAction::AutoLoad:
                eFilamentAction=FilamentAction::None; // i.e. non-autoLoad
-               // no break
+               // FALLTHRU
           case FilamentAction::Load:
                loading_flag=true;
                enquecommand_P(PSTR("M701"));      // load filament
@@ -2429,7 +2263,7 @@ void mFilamentItem(uint16_t nTemp, uint16_t nTempBed)
         case FilamentAction::None:
         case FilamentAction::Preheat:
         case FilamentAction::Lay1Cal:
-
+            // handled earlier
             break;
         }
         if (bFilamentWaitingFlag) Sound_MakeSound(e_SOUND_TYPE_StandardPrompt);
@@ -2437,34 +2271,56 @@ void mFilamentItem(uint16_t nTemp, uint16_t nTempBed)
     }
     else
     {
-        bFilamentWaitingFlag = true;
-        lcd_set_cursor(0, 0);
-        lcdui_print_temp(LCD_STR_THERMOMETER[0], (int) degHotend(0), (int) degTargetHotend(0));
-        lcd_set_cursor(0, 1);
-        switch (eFilamentAction)
+        if (!bFilamentWaitingFlag || lcd_draw_update)
         {
-        case FilamentAction::Load:
-        case FilamentAction::AutoLoad:
-        case FilamentAction::MmuLoad:
-            lcd_puts_P(_i("Preheating to load")); ////MSG_ c=20
-            break;
-        case FilamentAction::UnLoad:
-        case FilamentAction::MmuUnLoad:
-            lcd_puts_P(_i("Preheating to unload")); ////MSG_ c=20
-            break;
-        case FilamentAction::MmuEject:
-            lcd_puts_P(_i("Preheating to eject")); ////MSG_ c=20
-            break;
-        case FilamentAction::MmuCut:
-            lcd_puts_P(_i("Preheating to cut")); ////MSG_ c=20
-            break;
-        case FilamentAction::None:
-        case FilamentAction::Preheat:
-        case FilamentAction::Lay1Cal:
-            break;
+            // First entry from another menu OR first run after the filament preheat selection. Use
+            // bFilamentWaitingFlag to distinguish: this flag is reset exactly once when entering
+            // the menu and is used to raise the carriage *once*. In other cases, the LCD has been
+            // modified elsewhere and needs to be redrawn in full.
+
+            // reset bFilamentWaitingFlag immediately to avoid re-entry from raise_z_above()!
+            bool once = !bFilamentWaitingFlag;
+            bFilamentWaitingFlag = true;
+
+            // also force-enable lcd_draw_update (might be 0 when called from outside a menu)
+            lcd_draw_update = 1;
+
+            lcd_clear();
+            lcd_puts_at_P(0, 3, _T(MSG_CANCEL)); ////MSG_CANCEL
+
+            lcd_set_cursor(0, 1);
+            switch (eFilamentAction)
+            {
+            case FilamentAction::Load:
+            case FilamentAction::AutoLoad:
+            case FilamentAction::MmuLoad:
+                lcd_puts_P(_i("Preheating to load")); ////MSG_PREHEATING_TO_LOAD c=20
+                if (once) raise_z_above(MIN_Z_FOR_LOAD);
+                break;
+            case FilamentAction::UnLoad:
+            case FilamentAction::MmuUnLoad:
+                lcd_puts_P(_i("Preheating to unload")); ////MSG_PREHEATING_TO_UNLOAD c=20
+                if (once) raise_z_above(MIN_Z_FOR_UNLOAD);
+                break;
+            case FilamentAction::MmuEject:
+                lcd_puts_P(_i("Preheating to eject")); ////MSG_PREHEATING_TO_EJECT c=20
+                break;
+            case FilamentAction::MmuCut:
+                lcd_puts_P(_i("Preheating to cut")); ////MSG_PREHEATING_TO_CUT c=20
+                break;
+            case FilamentAction::None:
+            case FilamentAction::Preheat:
+            case FilamentAction::Lay1Cal:
+                // handled earlier
+                break;
+            }
+        }
+
+        if (bFilamentWaitingFlag) {
+            lcd_set_cursor(0, 0);
+            lcdui_print_temp(LCD_STR_THERMOMETER[0], (int) degHotend(0), (int) degTargetHotend(0));
         }
-        lcd_set_cursor(0, 3);
-        lcd_puts_P(_i(">Cancel"));                   ////MSG_ c=20 r=1
+
         if (lcd_clicked())
         {
             bFilamentWaitingFlag = false;
@@ -2544,6 +2400,12 @@ static void mFilamentItem_FLEX()
     mFilamentItem(FLEX_PREHEAT_HOTEND_TEMP, FLEX_PREHEAT_HPB_TEMP);
 }
 
+static void mFilamentItem_PVB()
+{
+    bFilamentPreheatState = false;
+    mFilamentItem(PVB_PREHEAT_HOTEND_TEMP, PVB_PREHEAT_HPB_TEMP);
+}
+
 void mFilamentBack()
 {
     menu_back();
@@ -2580,6 +2442,7 @@ void lcd_generic_preheat_menu()
         MENU_ITEM_SUBMENU_P(PSTR("PET  -  " STRINGIFY(PET_PREHEAT_HOTEND_TEMP) "/" STRINGIFY(PET_PREHEAT_HPB_TEMP)),mFilamentItem_PET);
         MENU_ITEM_SUBMENU_P(PSTR("ASA  -  " STRINGIFY(ASA_PREHEAT_HOTEND_TEMP) "/" STRINGIFY(ASA_PREHEAT_HPB_TEMP)),mFilamentItem_ASA);
         MENU_ITEM_SUBMENU_P(PSTR("PC   -  " STRINGIFY(PC_PREHEAT_HOTEND_TEMP) "/" STRINGIFY(PC_PREHEAT_HPB_TEMP)),mFilamentItem_PC);
+        MENU_ITEM_SUBMENU_P(PSTR("PVB  -  " STRINGIFY(PVB_PREHEAT_HOTEND_TEMP) "/" STRINGIFY(PVB_PREHEAT_HPB_TEMP)),mFilamentItem_PVB);
         MENU_ITEM_SUBMENU_P(PSTR("ABS  -  " STRINGIFY(ABS_PREHEAT_HOTEND_TEMP) "/" STRINGIFY(ABS_PREHEAT_HPB_TEMP)),mFilamentItem_ABS);
         MENU_ITEM_SUBMENU_P(PSTR("HIPS -  " STRINGIFY(HIPS_PREHEAT_HOTEND_TEMP) "/" STRINGIFY(HIPS_PREHEAT_HPB_TEMP)),mFilamentItem_HIPS);
         MENU_ITEM_SUBMENU_P(PSTR("PP   -  " STRINGIFY(PP_PREHEAT_HOTEND_TEMP) "/" STRINGIFY(PP_PREHEAT_HPB_TEMP)),mFilamentItem_PP);
@@ -2614,13 +2477,12 @@ void lcd_wait_interact() {
 
   lcd_set_cursor(0, 1);
 #ifdef SNMM 
-  lcd_puts_P(_i("Prepare new filament"));////MSG_PREPARE_FILAMENT c=20 r=1
+  lcd_puts_P(_i("Prepare new filament"));////MSG_PREPARE_FILAMENT c=20
 #else
   lcd_puts_P(_i("Insert filament"));////MSG_INSERT_FILAMENT c=20
 #endif
   if (!fsensor_autoload_enabled) {
-	  lcd_set_cursor(0, 2);
-	  lcd_puts_P(_i("and press the knob"));////MSG_PRESS c=20 r=2
+	  lcd_puts_at_P(0, 2, _i("and press the knob"));////MSG_PRESS c=20 r=2
   }
 }
 
@@ -2629,18 +2491,15 @@ void lcd_change_success() {
 
   lcd_clear();
 
-  lcd_set_cursor(0, 2);
-
-  lcd_puts_P(_i("Change success!"));////MSG_CHANGE_SUCCESS
+  lcd_puts_at_P(0, 2, _i("Change success!"));////MSG_CHANGE_SUCCESS c=20
 
 
 }
 
 static void lcd_loading_progress_bar(uint16_t loading_time_ms) { 
 
-	for (uint_least8_t i = 0; i < 20; i++) {
-		lcd_set_cursor(i, 3);
-		lcd_print(".");
+	for (uint_least8_t i = 0; i < LCD_WIDTH; i++) {
+		lcd_putc_at(i, 3, '.');
 		//loading_time_ms/20 delay
 		for (uint_least8_t j = 0; j < 5; j++) {
 			delay_keep_alive(loading_time_ms / 100);
@@ -2654,11 +2513,8 @@ void lcd_loading_color() {
 
   lcd_clear();
 
-  lcd_set_cursor(0, 0);
-
-  lcd_puts_P(_i("Loading color"));////MSG_LOADING_COLOR
-  lcd_set_cursor(0, 2);
-  lcd_puts_P(_T(MSG_PLEASE_WAIT));
+  lcd_puts_at_P(0, 0, _i("Loading color"));////MSG_LOADING_COLOR c=20
+  lcd_puts_at_P(0, 2, _T(MSG_PLEASE_WAIT));
   lcd_loading_progress_bar((FILAMENTCHANGE_FINALFEED * 1000ul) / FILAMENTCHANGE_EFEED_FINAL); //show progress bar during filament loading slow sequence
 }
 
@@ -2668,16 +2524,13 @@ void lcd_loading_filament() {
 
   lcd_clear();
 
-  lcd_set_cursor(0, 0);
-
-  lcd_puts_P(_T(MSG_LOADING_FILAMENT));
-  lcd_set_cursor(0, 2);
-  lcd_puts_P(_T(MSG_PLEASE_WAIT));
+  lcd_puts_at_P(0, 0, _T(MSG_LOADING_FILAMENT));
+  lcd_puts_at_P(0, 2, _T(MSG_PLEASE_WAIT));
 #ifdef SNMM
   for (int i = 0; i < 20; i++) {
 
     lcd_set_cursor(i, 3);
-    lcd_print(".");
+    lcd_print('.');
     for (int j = 0; j < 10 ; j++) {
       manage_heater();
       manage_inactivity(true);
@@ -2706,26 +2559,11 @@ void lcd_alright() {
 
   lcd_clear();
 
-  lcd_set_cursor(0, 0);
-
-  lcd_puts_P(_i("Changed correctly?"));////MSG_CORRECTLY c=20
-
-  lcd_set_cursor(1, 1);
-
-  lcd_puts_P(_T(MSG_YES));
-
-  lcd_set_cursor(1, 2);
-
-  lcd_puts_P(_i("Filament not loaded"));////MSG_NOT_LOADED c=19
-
-
-  lcd_set_cursor(1, 3);
-  lcd_puts_P(_i("Color not correct"));////MSG_NOT_COLOR
-
-
-  lcd_set_cursor(0, 1);
-
-  lcd_print(">");
+  lcd_puts_at_P(0, 0, _i("Changed correctly?"));////MSG_CORRECTLY c=20
+  lcd_puts_at_P(1, 1, _T(MSG_YES));
+  lcd_puts_at_P(1, 2, _i("Filament not loaded"));////MSG_NOT_LOADED c=19
+  lcd_puts_at_P(1, 3, _i("Color not correct"));////MSG_NOT_COLOR c=19
+  lcd_putc_at(0, 1, '>');
 
 
   enc_dif = lcd_encoder_diff;
@@ -2755,14 +2593,8 @@ void lcd_alright() {
           cursor_pos = 1;
 					Sound_MakeSound(e_SOUND_TYPE_BlindAlert);
         }
-        lcd_set_cursor(0, 1);
-        lcd_print(" ");
-        lcd_set_cursor(0, 2);
-        lcd_print(" ");
-        lcd_set_cursor(0, 3);
-        lcd_print(" ");
-        lcd_set_cursor(0, cursor_pos);
-        lcd_print(">");
+        lcd_puts_at_P(0, 1, PSTR(" \n \n "));
+        lcd_putc_at(0, cursor_pos, '>');
         enc_dif = lcd_encoder_diff;
 				Sound_MakeSound(e_SOUND_TYPE_EncoderMove);
         _delay(100);
@@ -2791,10 +2623,8 @@ void lcd_alright() {
 void show_preheat_nozzle_warning()
 {	
     lcd_clear();
-    lcd_set_cursor(0, 0);
-    lcd_puts_P(_T(MSG_ERROR));
-    lcd_set_cursor(0, 2);
-    lcd_puts_P(_T(MSG_PREHEAT_NOZZLE));
+    lcd_puts_at_P(0, 0, _T(MSG_ERROR));
+    lcd_puts_at_P(0, 2, _T(MSG_PREHEAT_NOZZLE));
     _delay(2000);
     lcd_clear();
 }
@@ -2844,9 +2674,9 @@ static void lcd_LoadFilament()
 //!
 //! @code{.unparsed}
 //! |01234567890123456789|
-//! |Filament used:      | c=19
+//! |Filament used:      | MSG_FILAMENT_USED c=19
 //! |           0000.00m |
-//! |Print time:         | c=19 r=1
+//! |Print time:         | MSG_PRINT_TIME c=19
 //! |        00h 00m 00s |
 //! ----------------------
 //! @endcode
@@ -2855,9 +2685,9 @@ static void lcd_LoadFilament()
 //!
 //! @code{.unparsed}
 //! |01234567890123456789|
-//! |Total filament:     | c=19 r=1
+//! |Total filament:     | MSG_TOTAL_FILAMENT c=19
 //! |           0000.00m |
-//! |Total print time:   | c=19 r=1
+//! |Total print time:   | MSG_TOTAL_PRINT_TIME c=19
 //! |        00d 00h 00m |
 //! ----------------------
 //! @endcode
@@ -2880,8 +2710,8 @@ void lcd_menu_statistics()
 			"%S:\n"
 			"%10ldh %02hhdm %02hhds"
 		    ),
-            _i("Filament used"), _met,  ////c=19
-            _i("Print time"), _h, _m, _s);  ////c=19 r=1
+            _i("Filament used"), _met,  ////MSG_FILAMENT_USED c=19
+            _i("Print time"), _h, _m, _s);  ////MSG_PRINT_TIME c=19
 		menu_back_if_clicked_fb();
 	}
 	else
@@ -2902,8 +2732,8 @@ void lcd_menu_statistics()
 			"%S:\n"
 			"%10ldd %02hhdh %02hhdm"
             ),
-            _i("Total filament"), _filament_m,  ////c=19 r=1
-            _i("Total print time"), _days, _hours, _minutes);  ////c=19 r=1
+            _i("Total filament"), _filament_m,  ////MSG_TOTAL_FILAMENT c=19
+            _i("Total print time"), _days, _hours, _minutes);  ////MSG_TOTAL_PRINT_TIME c=19
         menu_back_if_clicked_fb();
 	}
 }
@@ -2911,6 +2741,13 @@ void lcd_menu_statistics()
 
 static void _lcd_move(const char *name, int axis, int min, int max)
 {
+    if (homing_flag || mesh_bed_leveling_flag)
+    {
+        // printer entered a new state where axis move is forbidden
+        menu_back();
+        return;
+    }
+
 	typedef struct
 	{	// 2bytes total
 		bool initialized;              // 1byte
@@ -2947,7 +2784,7 @@ static void _lcd_move(const char *name, int axis, int min, int max)
 }
 
 
-static void lcd_move_e()
+void lcd_move_e()
 {
 	if (degHotend0() > EXTRUDE_MINTEMP)
 	{
@@ -2984,10 +2821,10 @@ static void lcd_move_e()
 //! This functionality is applied more often for MK2 printers.
 //! @code{.unparsed}
 //! |01234567890123456789|
-//! |Y distance from min |	c=19 r=1
+//! |Y distance from min |	MSG_Y_DIST_FROM_MIN
 //! | --------------     |	STR_SEPARATOR
-//! |Left:       00.00mm |	c=11 r=1
-//! |Right:      00.00mm |	c=11 r=1
+//! |Left:        00.00mm|	MSG_LEFT c=10, c=8
+//! |Right:       00.00mm|	MSG_RIGHT c=10, c=8
 //! ----------------------
 //! @endcode
 //! @todo Positioning of the messages and values on LCD aren't fixed to their exact place. This causes issues with translations.
@@ -2997,20 +2834,20 @@ static void lcd_menu_xyz_y_min()
     count_xyz_details(distanceMin);
 	lcd_home();
 	lcd_printf_P(_N(
-	  "%S:\n"
+	  "%S\n"
 	  "%S\n"
 	  "%S:\n"
 	  "%S:"
 	 ),
-	 _i("Y distance from min"),  ////c=19 r=1
+	 _i("Y distance from min"),  ////MSG_Y_DIST_FROM_MIN c=20
 	 separator,
-	 _i("Left"),  ////c=11 r=1
-	 _i("Right")  ////c=11 r=1
+	 _i("Left"),  ////MSG_LEFT c=10
+	 _i("Right")  ////MSG_RIGHT c=10
 	);
 	for (uint8_t i = 0; i < 2; i++)
 	{
 		lcd_set_cursor(11,2+i);
-		if (distanceMin[i] >= 200) lcd_puts_P(_T(MSG_NA)); ////c=3 r=1
+		if (distanceMin[i] >= 200) lcd_puts_P(_T(MSG_NA));
 		else lcd_printf_P(_N("%6.2fmm"), distanceMin[i]);
 	}
     if (lcd_clicked())
@@ -3027,10 +2864,10 @@ float _deg(float rad)
 //! 
 //! @code{.unparsed}
 //! |01234567890123456789|
-//! |Measured skew: 0.00D|	c=13 r=1
+//! |Measured skew :0.00D|	MSG_MEASURED_SKEW c=14, c=4
 //! | --------------     |	STR_SEPARATOR
-//! |Slight skew:   0.12D|	c=13 r=1  c=4 r=1
-//! |Severe skew:   0.25D|	c=13 r=1  c=4 r=1
+//! |Slight skew   :0.12D|	MSG_SLIGHT_SKEW c=14, c=4
+//! |Severe skew   :0.25D|	MSG_SEVERE_SKEW c=14, c=4
 //! ----------------------
 //! D - Degree sysmbol		LCD_STR_DEGREE
 //! @endcode
@@ -3040,23 +2877,22 @@ static void lcd_menu_xyz_skew()
     float angleDiff = eeprom_read_float((float*)(EEPROM_XYZ_CAL_SKEW));
 	lcd_home();
 	lcd_printf_P(_N(
-	  "%S:\n"
+	  "%-14.14S:\n"
 	  "%S\n"
-	  "%-15.15S%3.2f\x01\n"
-	  "%-15.15S%3.2f\x01"
+	  "%-14.14S:%3.2f\x01\n"
+	  "%-14.14S:%3.2f\x01"
 	 ),
-	 _i("Measured skew"),  ////c=13 r=1
+	 _i("Measured skew"),  ////MSG_MEASURED_SKEW c=14
 	 separator,
-	 _i("Slight skew:"), _deg(bed_skew_angle_mild),  ////c=13 r=1  c=4 r=1
-	 _i("Severe skew:"), _deg(bed_skew_angle_extreme)  ////c=13 r=1  c=4 r=1
+	 _i("Slight skew"), _deg(bed_skew_angle_mild),  ////MSG_SLIGHT_SKEW c=14, c=4
+	 _i("Severe skew"), _deg(bed_skew_angle_extreme)  ////MSG_SEVERE_SKEW c=14, c=4
 	);
 	if (angleDiff < 100){
 		lcd_set_cursor(15,0);
 		lcd_printf_P(_N("%3.2f\x01"), _deg(angleDiff));
 	}
 	else{
-		lcd_set_cursor(15,0);
-		lcd_puts_P(_T(MSG_NA));
+		lcd_puts_at_P(15,0, _T(MSG_NA));
 	}
     if (lcd_clicked())
         menu_goto(lcd_menu_xyz_offset, 0, true, true);
@@ -3065,20 +2901,19 @@ static void lcd_menu_xyz_skew()
 //! 
 //! @code{.unparsed}
 //! |01234567890123456789|
-//! |[0;0] point offset  |	c=20 r=1
+//! |[0;0] point offset  |	MSG_MEASURED_OFFSET c=20
 //! | --------------     |	STR_SEPARATOR
-//! |X:          000.00mm|	c=10 r=1
-//! |Y:          000.00mm|	c=10 r=1
+//! |X            00.00mm|	c=10
+//! |Y            00.00mm|	c=10
 //! ----------------------
 //! @endcode
 //! @todo Positioning of the messages and values on LCD aren't fixed to their exact place. This causes issues with translations.
 static void lcd_menu_xyz_offset()
 {
-    lcd_set_cursor(0,0);
-    lcd_puts_P(_i("[0;0] point offset"));////MSG_MEASURED_OFFSET
+    lcd_puts_at_P(0, 0, _i("[0;0] point offset"));////MSG_MEASURED_OFFSET c=20
     lcd_puts_at_P(0, 1, separator);
-    lcd_puts_at_P(0, 2, PSTR("X"));  ////c=10 r=1
-    lcd_puts_at_P(0, 3, PSTR("Y"));  ////c=10 r=1
+    lcd_puts_at_P(0, 2, PSTR("X"));
+    lcd_puts_at_P(0, 3, PSTR("Y"));
 
     float vec_x[2];
     float vec_y[2];
@@ -3087,9 +2922,9 @@ static void lcd_menu_xyz_offset()
 
     for (uint_least8_t i = 0; i < 2; i++)
     {
-        lcd_set_cursor((cntr[i] < 0) ? 10 : 11, i+2);
+        lcd_set_cursor((cntr[i] < 0) ? 13 : 14, i+2);
         lcd_print(cntr[i]);
-        lcd_puts_at_P(16, i + 2, PSTR("mm"));
+        lcd_puts_at_P(18, i + 2, PSTR("mm"));
     }
     menu_back_if_clicked();
 }
@@ -3134,6 +2969,13 @@ static void lcd_move_z() {
  */
 static void lcd_babystep_z()
 {
+    if (homing_flag || mesh_bed_leveling_flag)
+    {
+        // printer changed to a new state where live Z is forbidden
+        menu_back();
+        return;
+    }
+
 	typedef struct
 	{
 		int8_t status;
@@ -3169,19 +3011,13 @@ static void lcd_babystep_z()
 		lcd_timeoutToStatus.start();
 	}
 
-	if (lcd_encoder != 0) 
+	if (lcd_encoder != 0)
 	{
-		if (homing_flag) lcd_encoder = 0;
 		_md->babystepMemZ += (int)lcd_encoder;
 
-        if (_md->babystepMemZ < Z_BABYSTEP_MIN) _md->babystepMemZ = Z_BABYSTEP_MIN; //-3999 -> -9.99 mm
-        else if (_md->babystepMemZ > Z_BABYSTEP_MAX) _md->babystepMemZ = Z_BABYSTEP_MAX; //0
-        else
-        {
-            CRITICAL_SECTION_START
-            babystepsTodo[Z_AXIS] += (int)lcd_encoder;
-            CRITICAL_SECTION_END
-        }
+        if (_md->babystepMemZ < Z_BABYSTEP_MIN) _md->babystepMemZ = Z_BABYSTEP_MIN; //-3999 -> -9.99 mm
+        else if (_md->babystepMemZ > Z_BABYSTEP_MAX) _md->babystepMemZ = Z_BABYSTEP_MAX; //0
+        else babystepsTodoZadd(lcd_encoder);
 
 		_md->babystepMemMMZ = _md->babystepMemZ/cs.axis_steps_per_unit[Z_AXIS];
 		_delay(50);
@@ -3275,11 +3111,11 @@ void lcd_adjust_bed(void)
         eeprom_update_byte((unsigned char*)EEPROM_BED_CORRECTION_VALID, 1);
     );
     MENU_ITEM_BACK_P(_T(MSG_SETTINGS));
-	MENU_ITEM_EDIT_int3_P(_i("Left side [um]"),  &_md->left,  -BED_ADJUSTMENT_UM_MAX, BED_ADJUSTMENT_UM_MAX);////MSG_BED_CORRECTION_LEFT c=14 r=1
-    MENU_ITEM_EDIT_int3_P(_i("Right side[um]"), &_md->right, -BED_ADJUSTMENT_UM_MAX, BED_ADJUSTMENT_UM_MAX);////MSG_BED_CORRECTION_RIGHT c=14 r=1
-    MENU_ITEM_EDIT_int3_P(_i("Front side[um]"), &_md->front, -BED_ADJUSTMENT_UM_MAX, BED_ADJUSTMENT_UM_MAX);////MSG_BED_CORRECTION_FRONT c=14 r=1
-    MENU_ITEM_EDIT_int3_P(_i("Rear side [um]"),  &_md->rear,  -BED_ADJUSTMENT_UM_MAX, BED_ADJUSTMENT_UM_MAX);////MSG_BED_CORRECTION_REAR c=14 r=1
-    MENU_ITEM_FUNCTION_P(_i("Reset"), lcd_adjust_bed_reset);////MSG_BED_CORRECTION_RESET
+	MENU_ITEM_EDIT_int3_P(_i("Left side [um]"),  &_md->left,  -BED_ADJUSTMENT_UM_MAX, BED_ADJUSTMENT_UM_MAX);////MSG_BED_CORRECTION_LEFT c=14
+    MENU_ITEM_EDIT_int3_P(_i("Right side[um]"), &_md->right, -BED_ADJUSTMENT_UM_MAX, BED_ADJUSTMENT_UM_MAX);////MSG_BED_CORRECTION_RIGHT c=14
+    MENU_ITEM_EDIT_int3_P(_i("Front side[um]"), &_md->front, -BED_ADJUSTMENT_UM_MAX, BED_ADJUSTMENT_UM_MAX);////MSG_BED_CORRECTION_FRONT c=14
+    MENU_ITEM_EDIT_int3_P(_i("Rear side [um]"),  &_md->rear,  -BED_ADJUSTMENT_UM_MAX, BED_ADJUSTMENT_UM_MAX);////MSG_BED_CORRECTION_REAR c=14
+    MENU_ITEM_FUNCTION_P(_T(MSG_RESET), lcd_adjust_bed_reset);////MSG_RESET c=14
     MENU_END();
 }
 
@@ -3287,7 +3123,7 @@ void lcd_adjust_bed(void)
 //! 
 //! @code{.unparsed}
 //! |01234567890123456789|
-//! | Set temperature:   |	MSG_SET_TEMPERATURE
+//! |Set temperature:    |	MSG_SET_TEMPERATURE c=20
 //! |                    |
 //! | 210                |
 //! |                    |
@@ -3296,8 +3132,7 @@ void lcd_adjust_bed(void)
 void pid_extruder()
 {
 	lcd_clear();
-	lcd_set_cursor(1, 0);
-	lcd_puts_P(_i("Set temperature:"));////MSG_SET_TEMPERATURE c=19 r=1
+	lcd_puts_at_P(0, 0, _i("Set temperature:"));////MSG_SET_TEMPERATURE
 	pid_temp += int(lcd_encoder);
 	if (pid_temp > HEATER_0_MAXTEMP) pid_temp = HEATER_0_MAXTEMP;
 	if (pid_temp < HEATER_0_MINTEMP) pid_temp = HEATER_0_MINTEMP;
@@ -3332,7 +3167,7 @@ void lcd_adjust_z() {
 
   lcd_set_cursor(0, 1);
 
-  lcd_print(">");
+  lcd_print('>');
 
 
   enc_dif = lcd_encoder_diff;
@@ -3361,11 +3196,11 @@ void lcd_adjust_z() {
           cursor_pos = 1;
         }
         lcd_set_cursor(0, 1);
-        lcd_print(" ");
+        lcd_print(' ');
         lcd_set_cursor(0, 2);
-        lcd_print(" ");
+        lcd_print(' ');
         lcd_set_cursor(0, cursor_pos);
-        lcd_print(">");
+        lcd_print('>');
         enc_dif = lcd_encoder_diff;
         _delay(100);
       }
@@ -3411,7 +3246,7 @@ bool lcd_wait_for_pinda(float temp) {
 		lcd_set_cursor(0, 4);
 		lcd_print(LCD_STR_THERMOMETER[0]);
 		lcd_print(ftostr3(current_temperature_pinda));
-		lcd_print("/");
+		lcd_print('/');
 		lcd_print(ftostr3(temp));
 		lcd_print(LCD_STR_DEGREE);
 		delay_keep_alive(1000);
@@ -3433,7 +3268,7 @@ void lcd_wait_for_heater() {
 		lcd_set_cursor(0, 4);
 		lcd_print(LCD_STR_THERMOMETER[0]);
 		lcd_print(ftostr3(degHotend(active_extruder)));
-		lcd_print("/");
+		lcd_print('/');
 		lcd_print(ftostr3(degTargetHotend(active_extruder)));
 		lcd_print(LCD_STR_DEGREE);
 }
@@ -3445,7 +3280,7 @@ void lcd_wait_for_cool_down() {
 	int fanSpeedBckp = fanSpeed;
 	fanSpeed = 255;
 	while ((degHotend(0)>MAX_HOTEND_TEMP_CALIBRATION) || (degBed() > MAX_BED_TEMP_CALIBRATION)) {
-		lcd_display_message_fullscreen_P(_i("Waiting for nozzle and bed cooling"));////MSG_WAITING_TEMP c=20 r=3
+		lcd_display_message_fullscreen_P(_i("Waiting for nozzle and bed cooling"));////MSG_WAITING_TEMP c=20 r=4
 
 		lcd_set_cursor(0, 4);
 		lcd_print(LCD_STR_THERMOMETER[0]);
@@ -3576,10 +3411,10 @@ static const char* lcd_display_message_fullscreen_nonBlocking_P(const char *msg,
             // End of the message.
             break;
         lcd_set_cursor(0, row);
-        uint8_t linelen = min(strlen_P(msg), 20);
+        uint8_t linelen = min(strlen_P(msg), LCD_WIDTH);
         const char *msgend2 = msg + linelen;
         msgend = msgend2;
-        if (row == 3 && linelen == 20) {
+        if (row == 3 && linelen == LCD_WIDTH) {
             // Last line of the display, full line shall be displayed.
             // Find out, whether this message will be split into multiple screens.
             while (pgm_is_whitespace(msgend))
@@ -3752,16 +3587,14 @@ int8_t lcd_show_multiscreen_message_two_choices_and_wait_P(const char *msg, bool
 				if (msg_next == NULL) {
 					lcd_set_cursor(0, 3);
 					if (enc_dif < lcd_encoder_diff && yes) {
-						lcd_puts_P((PSTR(" ")));
-						lcd_set_cursor(7, 3);
-						lcd_puts_P((PSTR(">")));
+						lcd_print(' ');
+						lcd_putc_at(7, 3, '>');
 						yes = false;
 						Sound_MakeSound(e_SOUND_TYPE_EncoderMove);
 					}
 					else if (enc_dif > lcd_encoder_diff && !yes) {
-						lcd_puts_P((PSTR(">")));
-						lcd_set_cursor(7, 3);
-						lcd_puts_P((PSTR(" ")));
+						lcd_print('>');
+						lcd_putc_at(7, 3, ' ');
 						yes = true;
 						Sound_MakeSound(e_SOUND_TYPE_EncoderMove);
 					}
@@ -3790,13 +3623,11 @@ int8_t lcd_show_multiscreen_message_two_choices_and_wait_P(const char *msg, bool
 		}
 		if (msg_next == NULL) {
 			lcd_set_cursor(0, 3);
-			if (yes) lcd_puts_P(PSTR(">"));
-			lcd_set_cursor(1, 3);
-			lcd_puts_P(first_choice);
+			if (yes) lcd_print('>');
+			lcd_puts_at_P(1, 3, first_choice);
 			lcd_set_cursor(7, 3);
-			if (!yes) lcd_puts_P(PSTR(">"));
-			lcd_set_cursor(8, 3);
-			lcd_puts_P(second_choice);
+			if (!yes) lcd_print('>');
+			lcd_puts_at_P(8, 3, second_choice);
 		}
 	}
 }
@@ -3814,17 +3645,13 @@ int8_t lcd_show_fullscreen_message_yes_no_and_wait_P(const char *msg, bool allow
 	lcd_display_message_fullscreen_P(msg);
 	
 	if (default_yes) {
-		lcd_set_cursor(0, 2);
-		lcd_puts_P(PSTR(">"));
+		lcd_putc_at(0, 2, '>');
 		lcd_puts_P(_T(MSG_YES));
-		lcd_set_cursor(1, 3);
-		lcd_puts_P(_T(MSG_NO));
+		lcd_puts_at_P(1, 3, _T(MSG_NO));
 	}
 	else {
-		lcd_set_cursor(1, 2);
-		lcd_puts_P(_T(MSG_YES));
-		lcd_set_cursor(0, 3);
-		lcd_puts_P(PSTR(">"));
+		lcd_puts_at_P(1, 2, _T(MSG_YES));
+		lcd_putc_at(0, 3, '>');
 		lcd_puts_P(_T(MSG_NO));
 	}
 	int8_t retval = default_yes ? true : false;
@@ -3845,17 +3672,15 @@ int8_t lcd_show_fullscreen_message_yes_no_and_wait_P(const char *msg, bool allow
 		if (abs(enc_dif - lcd_encoder_diff) > 4) {
 			lcd_set_cursor(0, 2);
 				if (enc_dif < lcd_encoder_diff && retval) {
-					lcd_puts_P((PSTR(" ")));
-					lcd_set_cursor(0, 3);
-					lcd_puts_P((PSTR(">")));
+					lcd_print(' ');
+					lcd_putc_at(0, 3, '>');
 					retval = 0;
 					Sound_MakeSound(e_SOUND_TYPE_EncoderMove);
 
 				}
 				else if (enc_dif > lcd_encoder_diff && !retval) {
-					lcd_puts_P((PSTR(">")));
-					lcd_set_cursor(0, 3);
-					lcd_puts_P((PSTR(" ")));
+					lcd_print('>');
+					lcd_putc_at(0, 3, ' ');
 					retval = 1;
 					Sound_MakeSound(e_SOUND_TYPE_EncoderMove);
 				}
@@ -3875,16 +3700,16 @@ void lcd_bed_calibration_show_result(BedSkewOffsetDetectionResultType result, ui
 {
     const char *msg = NULL;
     if (result == BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND) {
-        lcd_show_fullscreen_message_and_wait_P(_i("XYZ calibration failed. Bed calibration point was not found."));////MSG_BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND c=20 r=8
+        lcd_show_fullscreen_message_and_wait_P(_i("XYZ calibration failed. Bed calibration point was not found."));////MSG_BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND c=20 r=6
     } else if (result == BED_SKEW_OFFSET_DETECTION_FITTING_FAILED) {
         if (point_too_far_mask == 0)
             msg = _T(MSG_BED_SKEW_OFFSET_DETECTION_FITTING_FAILED);
         else if (point_too_far_mask == 2 || point_too_far_mask == 7)
             // Only the center point or all the three front points.
-            msg = _i("XYZ calibration failed. Front calibration points not reachable.");////MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR c=20 r=8
+            msg = _i("XYZ calibration failed. Front calibration points not reachable.");////MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_BOTH_FAR c=20 r=6
         else if ((point_too_far_mask & 1) == 0)
             // The right and maybe the center point out of reach.
-            msg = _i("XYZ calibration failed. Right front calibration point not reachable.");////MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR c=20 r=8
+            msg = _i("XYZ calibration failed. Right front calibration point not reachable.");////MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_RIGHT_FAR c=20 r=6
         else
             // The left and maybe the center point out of reach.
             msg = _i("XYZ calibration failed. Left front calibration point not reachable.");////MSG_BED_SKEW_OFFSET_DETECTION_FAILED_FRONT_LEFT_FAR c=20 r=8
@@ -3951,14 +3776,10 @@ void lcd_temp_cal_show_result(bool result) {
 }
 
 static void lcd_show_end_stops() {
-	lcd_set_cursor(0, 0);
-	lcd_puts_P((PSTR("End stops diag")));
-	lcd_set_cursor(0, 1);
-	lcd_puts_P((READ(X_MIN_PIN) ^ (bool)X_MIN_ENDSTOP_INVERTING) ? (PSTR("X1")) : (PSTR("X0")));
-	lcd_set_cursor(0, 2);
-	lcd_puts_P((READ(Y_MIN_PIN) ^ (bool)Y_MIN_ENDSTOP_INVERTING) ? (PSTR("Y1")) : (PSTR("Y0")));
-	lcd_set_cursor(0, 3);
-	lcd_puts_P((READ(Z_MIN_PIN) ^ (bool)Z_MIN_ENDSTOP_INVERTING) ? (PSTR("Z1")) : (PSTR("Z0")));
+	lcd_puts_at_P(0, 0, (PSTR("End stops diag")));
+	lcd_puts_at_P(0, 1, (READ(X_MIN_PIN) ^ (bool)X_MIN_ENDSTOP_INVERTING) ? (PSTR("X1")) : (PSTR("X0")));
+	lcd_puts_at_P(0, 2, (READ(Y_MIN_PIN) ^ (bool)Y_MIN_ENDSTOP_INVERTING) ? (PSTR("Y1")) : (PSTR("Y0")));
+	lcd_puts_at_P(0, 3, (READ(Z_MIN_PIN) ^ (bool)Z_MIN_ENDSTOP_INVERTING) ? (PSTR("Z1")) : (PSTR("Z0")));
 }
 
 #ifndef TMC2130
@@ -4002,6 +3823,16 @@ static void lcd_print_state(uint8_t state)
 	}
 }
 
+//! @brief Show sensor state
+//!
+//! @code{.unparsed}
+//! |01234567890123456789|
+//! |PINDA N/A  FINDA N/A|  MSG_PINDA c=5 MSG_FINDA c=5
+//! |Fil. sensor      N/A|  MSG_FSENSOR 
+//! |Xd    000  Yd    000|  MSG_XD
+//! |Int   000  Shut  000|  
+//! ----------------------
+//! @endcode
 static void lcd_show_sensors_state()
 {
 	//0: N/A; 1: OFF; 2: ON
@@ -4014,21 +3845,56 @@ static void lcd_show_sensors_state()
 	{
 		finda_state = mmu_finda;
 	}
-	if (ir_sensor_detected) {
-		idler_state = !PIN_GET(IR_SENSOR_PIN);
-	}
-	lcd_puts_at_P(0, 0, _i("Sensor state"));
-	lcd_puts_at_P(1, 1, _i("PINDA:"));
-	lcd_set_cursor(LCD_WIDTH - 4, 1);
+	//lcd_puts_at_P(0, 0, _i("Sensor state"));
+	lcd_puts_at_P(0, 0, _T(MSG_PINDA));
+	lcd_set_cursor(LCD_WIDTH - 14, 0);
 	lcd_print_state(pinda_state);
 	
-	lcd_puts_at_P(1, 2, _i("FINDA:"));
-	lcd_set_cursor(LCD_WIDTH - 4, 2);
-	lcd_print_state(finda_state);
+	if (mmu_enabled == true)
+	{
+		lcd_puts_at_P(10, 0, _n("FINDA"));////MSG_FINDA c=5
+		lcd_set_cursor(LCD_WIDTH - 3, 0);
+		lcd_print_state(finda_state);
+	}
+	
+	if (ir_sensor_detected) {
+		idler_state = !READ(IR_SENSOR_PIN);
+		lcd_puts_at_P(0, 1, _i("Fil. sensor"));
+		lcd_set_cursor(LCD_WIDTH - 3, 1);
+		lcd_print_state(idler_state);
+	}
 	
-	lcd_puts_at_P(1, 3, _i("IR:"));
-	lcd_set_cursor(LCD_WIDTH - 4, 3);
-	lcd_print_state(idler_state);
+
+#ifdef PAT9125
+	// Display X and Y difference from Filament sensor    
+    // Display Light intensity from Filament sensor
+    //  Frame_Avg register represents the average brightness of all pixels within a frame (324 pixels). This
+    //  value ranges from 0(darkest) to 255(brightest).
+    // Display LASER shutter time from Filament sensor
+    //  Shutter register is an index of LASER shutter time. It is automatically controlled by the chip's internal
+    //  auto-exposure algorithm. When the chip is tracking on a good reflection surface, the Shutter is small.
+    //  When the chip is tracking on a poor reflection surface, the Shutter is large. Value ranges from 0 to 46.
+	if (mmu_enabled == false)
+	{
+		//if (!fsensor_enabled)
+		//	lcd_puts_P(_N("Filament sensor\n" "is disabled."));
+		//else
+		//{
+		if (!moves_planned() && !IS_SD_PRINTING && !is_usb_printing && (lcd_commands_type != LcdCommands::Layer1Cal))
+			pat9125_update();
+			lcd_set_cursor(0, 2);
+			lcd_printf_P(_N(
+				"Xd:  %3d  "
+				"Yd:  %3d\n" ////c=4
+				"Int: %3d  " ////c=4
+				"Shut:  %3d"  ////c=4
+			),
+				pat9125_x, pat9125_y,
+				pat9125_b, pat9125_s
+			);
+		//}
+	}
+#endif //PAT9125
 }
 
 void lcd_menu_show_sensors_state()                // NOT static due to using inside "Marlin_main" module ("manage_inactivity()")
@@ -4043,7 +3909,7 @@ void lcd_menu_show_sensors_state()                // NOT static due to using ins
 }
 
 void prusa_statistics_err(char c){
-	SERIAL_ECHO("{[ERR:");
+	SERIAL_ECHOPGM("{[ERR:");
 	SERIAL_ECHO(c);
 	SERIAL_ECHO(']');
 	prusa_stat_farm_number();
@@ -4128,7 +3994,7 @@ void prusa_statistics(int _message, uint8_t _fil_nr) {
 		return;
 		break;
 	case 4:		// print succesfull
-		SERIAL_ECHO("{[RES:1][FIL:");
+		SERIAL_ECHOPGM("{[RES:1][FIL:");
 		MYSERIAL.print(int(_fil_nr));
 		SERIAL_ECHO(']');
 		prusa_stat_printerstatus(status_number);
@@ -4136,7 +4002,7 @@ void prusa_statistics(int _message, uint8_t _fil_nr) {
 		farm_timer = 2;
 		break;
 	case 5:		// print not succesfull
-		SERIAL_ECHO("{[RES:0][FIL:");
+		SERIAL_ECHOPGM("{[RES:0][FIL:");
 		MYSERIAL.print(int(_fil_nr));
 		SERIAL_ECHO(']');
 		prusa_stat_printerstatus(status_number);
@@ -4144,22 +4010,21 @@ void prusa_statistics(int _message, uint8_t _fil_nr) {
 		farm_timer = 2;
 		break;
 	case 6:		// print done
-		SERIAL_ECHO("{[PRN:8]");
+		SERIAL_ECHOPGM("{[PRN:8]");
 		prusa_stat_farm_number();
 		status_number = 8;
 		farm_timer = 2;
 		break;
 	case 7:		// print done - stopped
-		SERIAL_ECHO("{[PRN:9]");
+		SERIAL_ECHOPGM("{[PRN:9]");
 		prusa_stat_farm_number();
 		status_number = 9;
 		farm_timer = 2;
 		break;
 	case 8:		// printer started
-		SERIAL_ECHO("{[PRN:0][PFN:");
+		SERIAL_ECHOPGM("{[PRN:0]");
+		prusa_stat_farm_number();
 		status_number = 0;
-		SERIAL_ECHO(farm_no);
-		SERIAL_ECHO(']');
 		farm_timer = 2;
 		break;
 	case 20:		// echo farm no
@@ -4175,7 +4040,7 @@ void prusa_statistics(int _message, uint8_t _fil_nr) {
 		prusa_stat_printerstatus(status_number);
 		break;
     case 22: // waiting for filament change
-        SERIAL_ECHO("{[PRN:5]");
+        SERIAL_ECHOPGM("{[PRN:5]");
 		prusa_stat_farm_number();
 		status_number = 5;
         break;
@@ -4194,12 +4059,9 @@ void prusa_statistics(int _message, uint8_t _fil_nr) {
 		break;
 
     case 99:		// heartbeat
-        SERIAL_ECHO("{[PRN:99]");
+        SERIAL_ECHOPGM("{[PRN:99]");
         prusa_stat_temperatures();
-		SERIAL_ECHO("[PFN:");
-		SERIAL_ECHO(farm_no);
-		SERIAL_ECHO(']');
-            
+		prusa_stat_farm_number();
         break;
 	}
 	SERIAL_ECHOLN('}');	
@@ -4208,47 +4070,45 @@ void prusa_statistics(int _message, uint8_t _fil_nr) {
 
 static void prusa_stat_printerstatus(int _status)
 {
-	SERIAL_ECHO("[PRN:");
+	SERIAL_ECHOPGM("[PRN:");
 	SERIAL_ECHO(_status);
 	SERIAL_ECHO(']');
 }
 
 static void prusa_stat_farm_number() {
-	SERIAL_ECHO("[PFN:");
-	SERIAL_ECHO(farm_no);
-	SERIAL_ECHO(']');
+	SERIAL_ECHOPGM("[PFN:0]");
 }
 
 static void prusa_stat_diameter() {
-	SERIAL_ECHO("[DIA:");
+	SERIAL_ECHOPGM("[DIA:");
 	SERIAL_ECHO(eeprom_read_word((uint16_t*)EEPROM_NOZZLE_DIAMETER_uM));
 	SERIAL_ECHO(']');
 }
 
 static void prusa_stat_temperatures()
 {
-	SERIAL_ECHO("[ST0:");
+	SERIAL_ECHOPGM("[ST0:");
 	SERIAL_ECHO(target_temperature[0]);
-	SERIAL_ECHO("][STB:");
+	SERIAL_ECHOPGM("][STB:");
 	SERIAL_ECHO(target_temperature_bed);
-	SERIAL_ECHO("][AT0:");
+	SERIAL_ECHOPGM("][AT0:");
 	SERIAL_ECHO(current_temperature[0]);
-	SERIAL_ECHO("][ATB:");
+	SERIAL_ECHOPGM("][ATB:");
 	SERIAL_ECHO(current_temperature_bed);
 	SERIAL_ECHO(']');
 }
 
 static void prusa_stat_printinfo()
 {
-	SERIAL_ECHO("[TFU:");
+	SERIAL_ECHOPGM("[TFU:");
 	SERIAL_ECHO(total_filament_used);
-	SERIAL_ECHO("][PCD:");
+	SERIAL_ECHOPGM("][PCD:");
 	SERIAL_ECHO(itostr3(card.percentDone()));
-	SERIAL_ECHO("][FEM:");
+	SERIAL_ECHOPGM("][FEM:");
 	SERIAL_ECHO(itostr3(feedmultiply));
-	SERIAL_ECHO("][FNM:");
-	SERIAL_ECHO(longFilenameOLD);
-	SERIAL_ECHO("][TIM:");
+	SERIAL_ECHOPGM("][FNM:");
+	SERIAL_ECHO(card.longFilename[0] ? card.longFilename : card.filename);
+	SERIAL_ECHOPGM("][TIM:");
 	if (starttime != 0)
 	{
 		SERIAL_ECHO(_millis() / 1000 - starttime / 1000);
@@ -4257,8 +4117,8 @@ static void prusa_stat_printinfo()
 	{
 		SERIAL_ECHO(0);
 	}
-	SERIAL_ECHO("][FWR:");
-	SERIAL_ECHO(FW_VERSION);
+	SERIAL_ECHOPGM("][FWR:");
+	SERIAL_ECHORPGM(FW_VERSION_STR_P());
 	SERIAL_ECHO(']');
      prusa_stat_diameter();
 }
@@ -4281,23 +4141,23 @@ void lcd_pick_babystep(){
     
     lcd_set_cursor(3, 2);
     
-    lcd_print("1");
+    lcd_print('1');
     
     lcd_set_cursor(3, 3);
     
-    lcd_print("2");
+    lcd_print('2');
     
     lcd_set_cursor(12, 2);
     
-    lcd_print("3");
+    lcd_print('3');
     
     lcd_set_cursor(12, 3);
     
-    lcd_print("4");
+    lcd_print('4');
     
     lcd_set_cursor(1, 2);
     
-    lcd_print(">");
+    lcd_print('>');
     
     
     enc_dif = lcd_encoder_diff;
@@ -4328,20 +4188,20 @@ void lcd_pick_babystep(){
 
                 
                 lcd_set_cursor(1, 2);
-                lcd_print(" ");
+                lcd_print(' ');
                 lcd_set_cursor(1, 3);
-                lcd_print(" ");
+                lcd_print(' ');
                 lcd_set_cursor(10, 2);
-                lcd_print(" ");
+                lcd_print(' ');
                 lcd_set_cursor(10, 3);
-                lcd_print(" ");
+                lcd_print(' ');
                 
                 if (cursor_pos < 3) {
                     lcd_set_cursor(1, cursor_pos+1);
-                    lcd_print(">");
+                    lcd_print('>');
                 }else{
                     lcd_set_cursor(10, cursor_pos-1);
-                    lcd_print(">");
+                    lcd_print('>');
                 }
                 
    
@@ -4370,10 +4230,10 @@ void lcd_move_menu_axis()
 {
 	MENU_BEGIN();
 	MENU_ITEM_BACK_P(_T(MSG_SETTINGS));
-	MENU_ITEM_SUBMENU_P(_i("Move X"), lcd_move_x);////MSG_MOVE_X
-	MENU_ITEM_SUBMENU_P(_i("Move Y"), lcd_move_y);////MSG_MOVE_Y
-	MENU_ITEM_SUBMENU_P(_i("Move Z"), lcd_move_z);////MSG_MOVE_Z
-	MENU_ITEM_SUBMENU_P(_i("Extruder"), lcd_move_e);////MSG_MOVE_E
+	MENU_ITEM_SUBMENU_P(_i("Move X"), lcd_move_x);////MSG_MOVE_X c=18
+	MENU_ITEM_SUBMENU_P(_i("Move Y"), lcd_move_y);////MSG_MOVE_Y c=18
+	MENU_ITEM_SUBMENU_P(_i("Move Z"), lcd_move_z);////MSG_MOVE_Z c=18
+	MENU_ITEM_SUBMENU_P(_T(MSG_EXTRUDER), lcd_move_e);
 	MENU_END();
 }
 
@@ -4414,7 +4274,7 @@ static void lcd_sort_type_set() {
 		default: sdSort = SD_SORT_TIME;
 	}
 	eeprom_update_byte((unsigned char *)EEPROM_SD_SORT, sdSort);
-	presort_flag = true;
+	card.presort_flag = true;
 }
 #endif //SDCARD_SORT_ALPHA
 
@@ -4503,7 +4363,7 @@ static void lcd_silent_mode_set() {
 	}
   eeprom_update_byte((unsigned char *)EEPROM_SILENT, SilentModeMenu);
 #ifdef TMC2130
-  lcd_display_message_fullscreen_P(_i("Mode change in progress ..."));
+  lcd_display_message_fullscreen_P(_i("Mode change in progress..."));////MSG_MODE_CHANGE_IN_PROGRESS c=20 r=3
   // Wait until the planner queue is drained and the stepper routine achieves
   // an idle state.
   st_synchronize();
@@ -4514,7 +4374,7 @@ static void lcd_silent_mode_set() {
 	cli();
 	tmc2130_mode = (SilentModeMenu != SILENT_MODE_NORMAL)?TMC2130_MODE_SILENT:TMC2130_MODE_NORMAL;
 	update_mode_profile();
-	tmc2130_init();
+	tmc2130_init(TMCInitParams(false, FarmOrUserECool()));
   // We may have missed a stepper timer interrupt due to the time spent in tmc2130_init.
   // Be safe than sorry, reset the stepper timer before re-enabling interrupts.
   st_reset_timer();
@@ -4562,24 +4422,17 @@ static void lcd_fsensor_state_set()
 }
 #endif //FILAMENT_SENSOR
 
-
-#if !SDSORT_USES_RAM
 void lcd_set_degree() {
 	lcd_set_custom_characters_degree();
 }
 
-void lcd_set_progress() {
-	lcd_set_custom_characters_progress();
-}
-#endif
-
 #if (LANG_MODE != 0)
 
 void menu_setlang(unsigned char lang)
 {
 	if (!lang_select(lang))
 	{
-		if (lcd_show_fullscreen_message_yes_no_and_wait_P(_i("Copy selected language?"), false, true))
+		if (lcd_show_fullscreen_message_yes_no_and_wait_P(_i("Copy selected language?"), false, true))////MSG_COPY_SEL_LANG c=20 r=3
 			lang_boot_update_start(lang);
 		lcd_update_enable(true);
 		lcd_clear();
@@ -4589,6 +4442,26 @@ void menu_setlang(unsigned char lang)
 	}
 }
 
+#ifdef COMMUNITY_LANG_SUPPORT
+#ifdef XFLASH
+static void lcd_community_language_menu()
+{
+	MENU_BEGIN();
+	uint8_t cnt = lang_get_count();
+	MENU_ITEM_BACK_P(_i("Select language")); //Back to previous Menu
+	for (int i = 8; i < cnt; i++) //all community languages
+		if (menu_item_text_P(lang_get_name_by_code(lang_get_code(i))))
+		{
+			menu_setlang(i);
+			return;
+		}
+	MENU_END();
+}
+#endif //XFLASH
+#endif //COMMUNITY_LANG_SUPPORT && W52X20CL
+
+
+
 static void lcd_language_menu()
 {
 	MENU_BEGIN();
@@ -4599,7 +4472,7 @@ static void lcd_language_menu()
 		return;
 	}
 	uint8_t cnt = lang_get_count();
-#ifdef W25X20CL
+#ifdef XFLASH
 	if (cnt == 2) //display secondary language in case of clear xflash 
 	{
 		if (menu_item_text_P(lang_get_name_by_code(lang_get_code(1))))
@@ -4609,15 +4482,22 @@ static void lcd_language_menu()
 		}
 	}
 	else
-		for (int i = 2; i < cnt; i++) //skip seconday language - solved in lang_select (MK3)
-#else //W25X20CL
+		for (int i = 2; i < 8; i++) //skip seconday language - solved in lang_select (MK3) 'i < 8'  for 7 official languages
+#else //XFLASH
 		for (int i = 1; i < cnt; i++) //all seconday languages (MK2/25)
-#endif //W25X20CL
+#endif //XFLASH
 			if (menu_item_text_P(lang_get_name_by_code(lang_get_code(i))))
 			{
 				menu_setlang(i);
 				return;
 			}
+
+#ifdef COMMUNITY_LANG_SUPPORT
+#ifdef XFLASH
+		MENU_ITEM_SUBMENU_P(_T(MSG_COMMUNITY_MADE), lcd_community_language_menu); ////MSG_COMMUNITY_MADE c=18
+#endif //XFLASH
+#endif //COMMUNITY_LANG_SUPPORT && W52X20CL
+
 	MENU_END();
 }
 #endif //(LANG_MODE != 0)
@@ -4646,7 +4526,7 @@ void lcd_pinda_calibration_menu()
 {
 	MENU_BEGIN();
 		MENU_ITEM_BACK_P(_T(MSG_MENU_CALIBRATION));
-		MENU_ITEM_SUBMENU_P(_i("Calibrate"), lcd_calibrate_pinda);////MSG_CALIBRATE_PINDA c=17 r=1
+		MENU_ITEM_SUBMENU_P(_i("Calibrate"), lcd_calibrate_pinda);////MSG_CALIBRATE_PINDA c=17
 	MENU_END();
 }
 
@@ -4823,8 +4703,8 @@ void lcd_v2_calibration()
 	if (mmu_enabled)
 	{
 	    const uint8_t filament = choose_menu_P(
-            _i("Select filament:"), ////c=20 r=1
-            _T(MSG_FILAMENT),_i("Cancel"));  ////c=19 r=1
+            _i("Select filament:"), ////MSG_SELECT_FILAMENT c=20
+            _T(MSG_FILAMENT),(_T(MSG_CANCEL)+1)); //Hack to reuse MSG but strip 1st char off
 	    if (filament < 5)
 	    {
 	        lay1cal_filament = filament;
@@ -4844,7 +4724,7 @@ void lcd_v2_calibration()
 	    }
 	    else
 	    {
-	        loaded = lcd_show_fullscreen_message_yes_no_and_wait_P(_i("Is filament loaded?"), false, true);////MSG_PLA_FILAMENT_LOADED c=20 r=2
+	        loaded = lcd_show_fullscreen_message_yes_no_and_wait_P(_T(MSG_FILAMENT_LOADED), false, true);
 	        lcd_update_enabled = true;
 
 	    }
@@ -4915,7 +4795,7 @@ static void wait_preheat()
     delay_keep_alive(2000);
     lcd_display_message_fullscreen_P(_T(MSG_WIZARD_HEATING));
 	lcd_set_custom_characters();
-	while (abs(degHotend(0) - degTargetHotend(0)) > 3) {
+	while (fabs(degHotend(0) - degTargetHotend(0)) > 3) {
         lcd_display_message_fullscreen_P(_T(MSG_WIZARD_HEATING));
 
         lcd_set_cursor(0, 4);
@@ -4930,12 +4810,12 @@ static void lcd_wizard_load()
 {
 	if (mmu_enabled)
 	{
-		lcd_show_fullscreen_message_and_wait_P(_i("Please insert filament into the first tube of the MMU, then press the knob to load it."));////c=20 r=8
+		lcd_show_fullscreen_message_and_wait_P(_i("Please insert filament into the first tube of the MMU, then press the knob to load it."));////MSG_MMU_INSERT_FILAMENT_FIRST_TUBE c=20 r=6
 		tmp_extruder = 0;
 	} 
 	else
 	{
-		lcd_show_fullscreen_message_and_wait_P(_i("Please insert filament into the extruder, then press the knob to load it."));////MSG_WIZARD_LOAD_FILAMENT c=20 r=8
+		lcd_show_fullscreen_message_and_wait_P(_i("Please insert filament into the extruder, then press the knob to load it."));////MSG_WIZARD_LOAD_FILAMENT c=20 r=6
 	}	
 	lcd_update_enable(false);
 	lcd_clear();
@@ -4959,12 +4839,12 @@ static void wizard_lay1cal_message(bool cold)
     if (mmu_enabled)
     {
         lcd_show_fullscreen_message_and_wait_P(
-                _i("Choose a filament for the First Layer Calibration and select it in the on-screen menu."));
+                _i("Choose a filament for the First Layer Calibration and select it in the on-screen menu."));////MSG_CHOOSE_FIL_1ST_LAYERCAL c=20 r=7
     }
     else if (cold)
     {
         lcd_show_fullscreen_message_and_wait_P(
-                _i("Select temperature which matches your material."));
+                _i("Select temperature which matches your material."));////MSG_SELECT_TEMP_MATCHES_MATERIAL c=20 r=4
     }
     lcd_show_fullscreen_message_and_wait_P(
             _i("The printer will start printing a zig-zag line. Rotate the knob until you reach the optimal height. Check the pictures in the handbook (Calibration chapter).")); ////MSG_WIZARD_V2_CAL_2 c=20 r=12
@@ -5010,7 +4890,7 @@ void lcd_wizard(WizState state)
 {
     using S = WizState;
 	bool end = false;
-	int wizard_event;
+	int8_t wizard_event;
 	const char *msg = NULL;
 	// Make sure EEPROM_WIZARD_ACTIVE is true if entering using different entry point
 	// other than WizState::Run - it is useful for debugging wizard.
@@ -5034,14 +4914,18 @@ void lcd_wizard(WizState state)
 			// Btw. the flag may even trigger the viper situation on normal start this way and the user won't be able to find out why.			
 			saved_printing = false;
 			
-			wizard_event = lcd_show_multiscreen_message_yes_no_and_wait_P(_i("Hi, I am your Original Prusa i3 printer. Would you like me to guide you through the setup process?"), false, true);////MSG_WIZARD_WELCOME c=20 r=7
-			if (wizard_event) {
+			if( eeprom_read_byte((uint8_t*)EEPROM_WIZARD_ACTIVE)==2){
+				lcd_show_fullscreen_message_and_wait_P(_T(MSG_WIZARD_WELCOME_SHIPPING));
 				state = S::Restore;
-				eeprom_update_byte((uint8_t*)EEPROM_WIZARD_ACTIVE, 1);
-			}
-			else {
-				eeprom_update_byte((uint8_t*)EEPROM_WIZARD_ACTIVE, 0);
-				end = true;
+			} else {
+				wizard_event = lcd_show_multiscreen_message_yes_no_and_wait_P(_T(MSG_WIZARD_WELCOME), false, true);
+				if (wizard_event) {
+					state = S::Restore;
+					eeprom_update_byte((uint8_t*)EEPROM_WIZARD_ACTIVE, 1);
+				} else {
+					eeprom_update_byte((uint8_t*)EEPROM_WIZARD_ACTIVE, 0);
+					end = true;
+				}
 			}
 			break;
 		case S::Restore:
@@ -5070,8 +4954,8 @@ void lcd_wizard(WizState state)
 			else end = true;
 			break;
 		case S::Z:
-			lcd_show_fullscreen_message_and_wait_P(_i("Please remove shipping helpers first."));
-			lcd_show_fullscreen_message_and_wait_P(_i("Now remove the test print from steel sheet."));
+			lcd_show_fullscreen_message_and_wait_P(_i("Please remove shipping helpers first."));////MSG_REMOVE_SHIPPING_HELPERS c=20 r=3
+			lcd_show_fullscreen_message_and_wait_P(_i("Now remove the test print from steel sheet."));////MSG_REMOVE_TEST_PRINT c=20 r=4
 			lcd_show_fullscreen_message_and_wait_P(_i("I will run z calibration now."));////MSG_WIZARD_Z_CAL c=20 r=8
 			wizard_event = lcd_show_fullscreen_message_yes_no_and_wait_P(_T(MSG_STEEL_SHEET_CHECK), false, false);
 			if (!wizard_event) lcd_show_fullscreen_message_and_wait_P(_T(MSG_PLACE_STEEL_SHEET));
@@ -5083,7 +4967,7 @@ void lcd_wizard(WizState state)
 				lcd_display_message_fullscreen_P(_i("Now I will preheat nozzle for PLA."));
 				wait_preheat();
 				//unload current filament
-				unload_filament();
+				unload_filament(true);
 				//load filament
 				lcd_wizard_load();
 				setTargetHotend(0, 0); //we are finished, cooldown nozzle
@@ -5097,10 +4981,10 @@ void lcd_wizard(WizState state)
 			setTargetBed(PLA_PREHEAT_HPB_TEMP);
 			if (mmu_enabled)
 			{
-			    wizard_event = lcd_show_fullscreen_message_yes_no_and_wait_P(_i("Is filament loaded?"), true);////c=20 r=2
+			    wizard_event = lcd_show_fullscreen_message_yes_no_and_wait_P(_T(MSG_FILAMENT_LOADED), true);
 			} else
 			{
-			    wizard_event = lcd_show_fullscreen_message_yes_no_and_wait_P(_i("Is filament loaded?"), true);////MSG_WIZARD_FILAMENT_LOADED c=20 r=2
+			    wizard_event = lcd_show_fullscreen_message_yes_no_and_wait_P(_T(MSG_FILAMENT_LOADED), true);
 			}
 			if (wizard_event) state = S::Lay1CalCold;
 			else
@@ -5111,7 +4995,7 @@ void lcd_wizard(WizState state)
 			break;
 		case S::Preheat:
 		    menu_goto(lcd_preheat_menu,0,false,true);
-		    lcd_show_fullscreen_message_and_wait_P(_i("Select nozzle preheat temperature which matches your material."));
+		    lcd_show_fullscreen_message_and_wait_P(_i("Select nozzle preheat temperature which matches your material."));////MSG_SEL_PREHEAT_TEMP c=20 r=6
 		    end = true; // Leave wizard temporarily for lcd_preheat_menu
 		    break;
 		case S::LoadFilHot:
@@ -5142,7 +5026,7 @@ void lcd_wizard(WizState state)
 			}
 			else
 			{
-			    lcd_show_fullscreen_message_and_wait_P(_i("If you have additional steel sheets, calibrate their presets in Settings - HW Setup - Steel sheets."));
+			    lcd_show_fullscreen_message_and_wait_P(_i("If you have additional steel sheets, calibrate their presets in Settings - HW Setup - Steel sheets."));////MSG_ADDITIONAL_SHEETS c=20 r=9
 				state = S::Finish;
 			}
 			break;
@@ -5237,13 +5121,13 @@ do\
         if (mmu_enabled == false)\
         {\
             if (fsensor_autoload_enabled)\
-                MENU_ITEM_TOGGLE_P(_T(MSG_FSENSOR_AUTOLOAD), _T(MSG_ON), lcd_set_filament_autoload);/*////MSG_FSENS_AUTOLOAD_ON c=17 r=1*/\
+                MENU_ITEM_TOGGLE_P(_T(MSG_FSENSOR_AUTOLOAD), _T(MSG_ON), lcd_set_filament_autoload);/*////MSG_FSENS_AUTOLOAD_ON c=17*/\
             else\
-                MENU_ITEM_TOGGLE_P(_T(MSG_FSENSOR_AUTOLOAD), _T(MSG_OFF), lcd_set_filament_autoload);/*////MSG_FSENS_AUTOLOAD_OFF c=17 r=1*/\
+                MENU_ITEM_TOGGLE_P(_T(MSG_FSENSOR_AUTOLOAD), _T(MSG_OFF), lcd_set_filament_autoload);/*////MSG_FSENS_AUTOLOAD_OFF c=17*/\
             /*if (fsensor_oq_meassure_enabled)*/\
-                /*MENU_ITEM_FUNCTION_P(_i("F. OQ meass. [on]"), lcd_set_filament_oq_meass);*//*////MSG_FSENS_OQMEASS_ON c=17 r=1*/\
+                /*MENU_ITEM_FUNCTION_P(_i("F. OQ meass. [on]"), lcd_set_filament_oq_meass);*//*////MSG_FSENS_OQMEASS_ON c=17*/\
             /*else*/\
-                /*MENU_ITEM_FUNCTION_P(_i("F. OQ meass.[off]"), lcd_set_filament_oq_meass);*//*////MSG_FSENS_OQMEASS_OFF c=17 r=1*/\
+                /*MENU_ITEM_FUNCTION_P(_i("F. OQ meass.[off]"), lcd_set_filament_oq_meass);*//*////MSG_FSENS_OQMEASS_OFF c=17*/\
         }\
     }\
 }\
@@ -5388,16 +5272,13 @@ do\
     else\
         MENU_ITEM_TOGGLE_P(_T(MSG_SD_CARD), _T(MSG_NORMAL), lcd_toshiba_flash_air_compatibility_toggle);\
 \
-    if (!farm_mode)\
+    uint8_t sdSort;\
+    EEPROM_read(EEPROM_SD_SORT, (uint8_t*)&sdSort, sizeof(sdSort));\
+    switch (sdSort)\
     {\
-        uint8_t sdSort;\
-        EEPROM_read(EEPROM_SD_SORT, (uint8_t*)&sdSort, sizeof(sdSort));\
-        switch (sdSort)\
-        {\
-          case SD_SORT_TIME: MENU_ITEM_TOGGLE_P(_T(MSG_SORT), _T(MSG_SORT_TIME), lcd_sort_type_set); break;\
-          case SD_SORT_ALPHA: MENU_ITEM_TOGGLE_P(_T(MSG_SORT), _T(MSG_SORT_ALPHA), lcd_sort_type_set); break;\
-          default: MENU_ITEM_TOGGLE_P(_T(MSG_SORT), _T(MSG_NONE), lcd_sort_type_set);\
-        }\
+      case SD_SORT_TIME: MENU_ITEM_TOGGLE_P(_T(MSG_SORT), _T(MSG_SORT_TIME), lcd_sort_type_set); break;\
+      case SD_SORT_ALPHA: MENU_ITEM_TOGGLE_P(_T(MSG_SORT), _T(MSG_SORT_ALPHA), lcd_sort_type_set); break;\
+      default: MENU_ITEM_TOGGLE_P(_T(MSG_SORT), _T(MSG_NONE), lcd_sort_type_set);\
     }\
 }\
 while (0)
@@ -5499,30 +5380,31 @@ do\
 }\
 while (0)
 
-static void lcd_nozzle_diameter_set(void)
-{
-uint16_t nDiameter;
-
-switch(oNozzleDiameter)
-     {
-     case ClNozzleDiameter::_Diameter_250:
-          oNozzleDiameter=ClNozzleDiameter::_Diameter_400;
-          nDiameter=400;
-          break;
-     case ClNozzleDiameter::_Diameter_400:
-          oNozzleDiameter=ClNozzleDiameter::_Diameter_600;
-          nDiameter=600;
-          break;
-     case ClNozzleDiameter::_Diameter_600:
-          oNozzleDiameter=ClNozzleDiameter::_Diameter_250;
-          nDiameter=250;
-          break;
-     default:
-          oNozzleDiameter=ClNozzleDiameter::_Diameter_400;
-          nDiameter=400;
-     }
-eeprom_update_byte((uint8_t*)EEPROM_NOZZLE_DIAMETER,(uint8_t)oNozzleDiameter);
-eeprom_update_word((uint16_t*)EEPROM_NOZZLE_DIAMETER_uM,nDiameter);
+static void lcd_nozzle_diameter_cycle(void) {
+    uint16_t nDiameter;
+    switch(oNozzleDiameter){
+    case ClNozzleDiameter::_Diameter_250:
+        oNozzleDiameter=ClNozzleDiameter::_Diameter_400;
+        nDiameter=400;
+        break;
+    case ClNozzleDiameter::_Diameter_400:
+        oNozzleDiameter=ClNozzleDiameter::_Diameter_600;
+        nDiameter=600;
+        break;
+    case ClNozzleDiameter::_Diameter_600:
+        oNozzleDiameter=ClNozzleDiameter::_Diameter_800;
+        nDiameter=800;
+        break;
+    case ClNozzleDiameter::_Diameter_800:
+        oNozzleDiameter=ClNozzleDiameter::_Diameter_250;
+        nDiameter=250;
+        break;
+    default:
+        oNozzleDiameter=ClNozzleDiameter::_Diameter_400;
+        nDiameter=400;
+    }
+    eeprom_update_byte((uint8_t*)EEPROM_NOZZLE_DIAMETER,(uint8_t)oNozzleDiameter);
+    eeprom_update_word((uint16_t*)EEPROM_NOZZLE_DIAMETER_uM,nDiameter);
 }
 
 #define SETTINGS_NOZZLE \
@@ -5534,9 +5416,10 @@ do\
         case ClNozzleDiameter::_Diameter_250: fNozzleDiam = 0.25f; break;\
         case ClNozzleDiameter::_Diameter_400: fNozzleDiam = 0.4f; break;\
         case ClNozzleDiameter::_Diameter_600: fNozzleDiam = 0.6f; break;\
+        case ClNozzleDiameter::_Diameter_800: fNozzleDiam = 0.8f; break;\
         default: fNozzleDiam = 0.4f; break;\
     }\
-    MENU_ITEM_TOGGLE(_T(MSG_NOZZLE_DIAMETER), ftostr12ns(fNozzleDiam), lcd_nozzle_diameter_set);\
+    MENU_ITEM_TOGGLE(_T(MSG_NOZZLE_DIAMETER), ftostr12ns(fNozzleDiam), lcd_nozzle_diameter_cycle);\
 }\
 while (0)
 
@@ -5716,7 +5599,7 @@ static void select_sheet_menu()
 static void sheets_menu()
 {
     MENU_BEGIN();
-    MENU_ITEM_BACK_P(_i("HW Setup"));
+    MENU_ITEM_BACK_P(_T(MSG_HW_SETUP));
     MENU_ITEM_SUBMENU_E(EEPROM_Sheets_base->s[0], select_sheet_menu<0>);
     MENU_ITEM_SUBMENU_E(EEPROM_Sheets_base->s[1], select_sheet_menu<1>);
     MENU_ITEM_SUBMENU_E(EEPROM_Sheets_base->s[2], select_sheet_menu<2>);
@@ -5730,12 +5613,32 @@ static void sheets_menu()
 
 void lcd_hw_setup_menu(void)                      // can not be "static"
 {
+    typedef struct
+    {// 2bytes total
+        int8_t status;
+        uint8_t experimental_menu_visibility;
+    } _menu_data_t;
+    static_assert(sizeof(menu_data)>= sizeof(_menu_data_t),"_menu_data_t doesn't fit into menu_data");
+    _menu_data_t* _md = (_menu_data_t*)&(menu_data[0]);
+
+    if (_md->status == 0 || lcd_draw_update)
+    {
+        _md->status = 1;
+        _md->experimental_menu_visibility = eeprom_read_byte((uint8_t *)EEPROM_EXPERIMENTAL_VISIBILITY);
+        if (_md->experimental_menu_visibility == EEPROM_EMPTY_VALUE)
+        {
+            _md->experimental_menu_visibility = 0;
+            eeprom_update_byte((uint8_t *)EEPROM_EXPERIMENTAL_VISIBILITY, _md->experimental_menu_visibility);
+        }
+    }
+
+
     MENU_BEGIN();
     MENU_ITEM_BACK_P(_T(bSettings?MSG_SETTINGS:MSG_BACK)); // i.e. default menu-item / menu-item after checking mismatch
 
-    MENU_ITEM_SUBMENU_P(_i("Steel sheets"), sheets_menu); ////MSG_STEEL_SHEETS c=18
+    MENU_ITEM_SUBMENU_P(_T(MSG_STEEL_SHEETS), sheets_menu);
     SETTINGS_NOZZLE;
-    MENU_ITEM_SUBMENU_P(_i("Checks"), lcd_checking_menu);
+    MENU_ITEM_SUBMENU_P(_i("Checks"), lcd_checking_menu);  ////MSG_CHECKS c=18
 
 #ifdef IR_SENSOR_ANALOG
     FSENSOR_ACTION_NA;
@@ -5743,6 +5646,21 @@ void lcd_hw_setup_menu(void)                      // can not be "static"
     //! @todo Don't forget to remove this as soon Fsensor Detection works with mmu
     if(!mmu_enabled) MENU_ITEM_FUNCTION_P(PSTR("Fsensor Detection"), lcd_detect_IRsensor);
 #endif //IR_SENSOR_ANALOG
+
+    if (_md->experimental_menu_visibility)
+    {
+        MENU_ITEM_SUBMENU_P(PSTR("Experimental"), lcd_experimental_menu);////MSG_MENU_EXPERIMENTAL c=18
+    }
+
+#ifdef PINDA_TEMP_COMP
+	//! The SuperPINDA is detected when the PINDA temp is below its defined limit.
+	//! This works well on the EINSY board but not on the miniRAMBo board as
+	//! as a disconnected SuperPINDA will show higher temps compared to an EINSY board.
+	//! 
+	//! This menu allows the user to en-/disable the SuperPINDA manualy
+	MENU_ITEM_TOGGLE_P(_N("SuperPINDA"), eeprom_read_byte((uint8_t *)EEPROM_PINDA_TEMP_COMPENSATION) ? _T(MSG_YES) : _T(MSG_NO), lcd_pinda_temp_compensation_toggle);
+#endif //PINDA_TEMP_COMP
+
     MENU_END();
 }
 
@@ -5752,11 +5670,13 @@ static void lcd_settings_menu()
 	MENU_BEGIN();
 	MENU_ITEM_BACK_P(_T(MSG_MAIN));
 
-	MENU_ITEM_SUBMENU_P(_i("Temperature"), lcd_control_temperature_menu);////MSG_TEMPERATURE
-	if (!homing_flag)
-	    MENU_ITEM_SUBMENU_P(_i("Move axis"), lcd_move_menu_1mm);////MSG_MOVE_AXIS
-	if (!isPrintPaused)
-	    MENU_ITEM_GCODE_P(_i("Disable steppers"), PSTR("M84"));////MSG_DISABLE_STEPPERS
+	MENU_ITEM_SUBMENU_P(_i("Temperature"), lcd_control_temperature_menu);////MSG_TEMPERATURE c=18
+
+	if (!PRINTER_ACTIVE || isPrintPaused)
+    {
+	    MENU_ITEM_SUBMENU_P(_i("Move axis"), lcd_move_menu_1mm);////MSG_MOVE_AXIS c=18
+	    MENU_ITEM_GCODE_P(_i("Disable steppers"), PSTR("M84"));////MSG_DISABLE_STEPPERS c=18
+    }
 
 	SETTINGS_FILAMENT_SENSOR;
 
@@ -5764,22 +5684,22 @@ static void lcd_settings_menu()
 
 	SETTINGS_CUTTER;
 
-	MENU_ITEM_TOGGLE_P(_i("Fans check"), fans_check_enabled ? _T(MSG_ON) : _T(MSG_OFF), lcd_set_fan_check);
+	MENU_ITEM_TOGGLE_P(_T(MSG_FANS_CHECK), fans_check_enabled ? _T(MSG_ON) : _T(MSG_OFF), lcd_set_fan_check);
 
 	SETTINGS_SILENT_MODE;
 
     if(!farm_mode)
     {
         bSettings=true;                              // flag ('fake parameter') for 'lcd_hw_setup_menu()' function
-        MENU_ITEM_SUBMENU_P(_i("HW Setup"), lcd_hw_setup_menu);////MSG_HW_SETUP
+        MENU_ITEM_SUBMENU_P(_T(MSG_HW_SETUP), lcd_hw_setup_menu);
     }
     
 	SETTINGS_MMU_MODE;
 
-	MENU_ITEM_SUBMENU_P(_i("Mesh bed leveling"), lcd_mesh_bed_leveling_settings);////MSG_MBL_SETTINGS c=18 r=1
+	MENU_ITEM_SUBMENU_P(_T(MSG_MESH_BED_LEVELING), lcd_mesh_bed_leveling_settings);
 
 #if defined (TMC2130) && defined (LINEARITY_CORRECTION)
-    MENU_ITEM_SUBMENU_P(_i("Lin. correction"), lcd_settings_linearity_correction_menu);
+    MENU_ITEM_SUBMENU_P(_i("Lin. correction"), lcd_settings_linearity_correction_menu);////MSG_LIN_CORRECTION c=18
 #endif //LINEARITY_CORRECTION && TMC2130
     if(has_temperature_compensation())
     {
@@ -5790,11 +5710,11 @@ static void lcd_settings_menu()
     MENU_ITEM_TOGGLE_P(_T(MSG_RPI_PORT), (selectedSerialPort == 0) ? _T(MSG_OFF) : _T(MSG_ON), lcd_second_serial_set);
 #endif //HAS_SECOND_SERIAL
 
-	if (!isPrintPaused && !homing_flag)
+	if (!isPrintPaused && !homing_flag && !mesh_bed_leveling_flag)
 		MENU_ITEM_SUBMENU_P(_T(MSG_BABYSTEP_Z), lcd_babystep_z);
 
 #if (LANG_MODE != 0)
-	MENU_ITEM_SUBMENU_P(_i("Select language"), lcd_language_menu);////MSG_LANGUAGE_SELECT
+	MENU_ITEM_SUBMENU_P(_i("Select language"), lcd_language_menu);////MSG_LANGUAGE_SELECT c=18
 #endif //(LANG_MODE != 0)
 
 	SETTINGS_SD;
@@ -5809,7 +5729,6 @@ static void lcd_settings_menu()
 
 	if (farm_mode)
 	{
-		MENU_ITEM_SUBMENU_P(PSTR("Farm number"), lcd_farm_no);
 		MENU_ITEM_FUNCTION_P(PSTR("Disable farm mode"), lcd_disable_farm_mode);
 	}
 
@@ -5839,7 +5758,7 @@ static void lcd_settings_linearity_correction_menu_save()
     changed |= (eeprom_read_byte((uint8_t*)EEPROM_TMC2130_WAVE_Z_FAC) != tmc2130_wave_fac[Z_AXIS]);
     changed |= (eeprom_read_byte((uint8_t*)EEPROM_TMC2130_WAVE_E_FAC) != tmc2130_wave_fac[E_AXIS]);
     lcd_ustep_linearity_menu_save();
-    if (changed) tmc2130_init();
+    if (changed) tmc2130_init(TMCInitParams(false, FarmOrUserECool()));
 }
 #endif //TMC2130
 
@@ -5849,40 +5768,40 @@ static void lcd_calibration_menu()
   MENU_ITEM_BACK_P(_T(MSG_MAIN));
   if (!isPrintPaused)
   {
-	MENU_ITEM_FUNCTION_P(_i("Wizard"), lcd_wizard);////MSG_WIZARD c=17 r=1
+	MENU_ITEM_FUNCTION_P(_i("Wizard"), lcd_wizard);////MSG_WIZARD c=17
     if (lcd_commands_type == LcdCommands::Idle)
     {
          MENU_ITEM_SUBMENU_P(_T(MSG_V2_CALIBRATION), lcd_first_layer_calibration_reset);////MSG_V2_CALIBRATION c=18
     }
 	MENU_ITEM_GCODE_P(_T(MSG_AUTO_HOME), PSTR("G28 W"));
 #ifdef TMC2130
-	MENU_ITEM_FUNCTION_P(_i("Belt test        "), lcd_belttest_v);////MSG_BELTTEST c=17
+	MENU_ITEM_FUNCTION_P(_i("Belt test"), lcd_belttest_v);////MSG_BELTTEST c=18
 #endif //TMC2130
-	MENU_ITEM_FUNCTION_P(_i("Selftest         "), lcd_selftest_v);////MSG_SELFTEST c=17
+	MENU_ITEM_FUNCTION_P(_i("Selftest"), lcd_selftest_v);////MSG_SELFTEST c=18
 #ifdef MK1BP
     // MK1
     // "Calibrate Z"
     MENU_ITEM_GCODE_P(_T(MSG_HOMEYZ), PSTR("G28 Z"));
 #else //MK1BP
     // MK2
-    MENU_ITEM_FUNCTION_P(_i("Calibrate XYZ"), lcd_mesh_calibration);////MSG_CALIBRATE_BED
+    MENU_ITEM_FUNCTION_P(_i("Calibrate XYZ"), lcd_mesh_calibration);////MSG_CALIBRATE_BED c=18
     // "Calibrate Z" with storing the reference values to EEPROM.
     MENU_ITEM_SUBMENU_P(_T(MSG_HOMEYZ), lcd_mesh_calibration_z);
 #ifndef SNMM
-	//MENU_ITEM_FUNCTION_P(_i("Calibrate E"), lcd_calibrate_extruder);////MSG_CALIBRATE_E c=20 r=1
+	//MENU_ITEM_FUNCTION_P(_i("Calibrate E"), lcd_calibrate_extruder);////MSG_CALIBRATE_E c=20
 #endif
     // "Mesh Bed Leveling"
-    MENU_ITEM_SUBMENU_P(_i("Mesh Bed Leveling"), lcd_mesh_bedleveling);////MSG_MESH_BED_LEVELING
+    MENU_ITEM_SUBMENU_P(_T(MSG_MESH_BED_LEVELING), lcd_mesh_bedleveling);
 	
 #endif //MK1BP
 
-    MENU_ITEM_SUBMENU_P(_i("Bed level correct"), lcd_adjust_bed);////MSG_BED_CORRECTION_MENU
-	MENU_ITEM_SUBMENU_P(_i("PID calibration"), pid_extruder);////MSG_PID_EXTRUDER c=17 r=1
+    MENU_ITEM_SUBMENU_P(_i("Bed level correct"), lcd_adjust_bed);////MSG_BED_CORRECTION_MENU c=18
+	MENU_ITEM_SUBMENU_P(_i("PID calibration"), pid_extruder);////MSG_PID_EXTRUDER c=17
 #ifndef TMC2130
     MENU_ITEM_SUBMENU_P(_i("Show end stops"), menu_show_end_stops);////MSG_SHOW_END_STOPS c=18
 #endif
 #ifndef MK1BP
-    MENU_ITEM_GCODE_P(_i("Reset XYZ calibr."), PSTR("M44"));////MSG_CALIBRATE_BED_RESET
+    MENU_ITEM_GCODE_P(_i("Reset XYZ calibr."), PSTR("M44"));////MSG_CALIBRATE_BED_RESET c=18
 #endif //MK1BP
 #ifndef SNMM
 	//MENU_ITEM_FUNCTION_P(MSG_RESET_CALIBRATE_E, lcd_extr_cal_reset);
@@ -5890,7 +5809,7 @@ static void lcd_calibration_menu()
 #ifndef MK1BP
     if(has_temperature_compensation())
     {
-	    MENU_ITEM_SUBMENU_P(_i("Temp. calibration"), lcd_pinda_calibration_menu);////MSG_CALIBRATION_PINDA_MENU c=17 r=1
+	    MENU_ITEM_SUBMENU_P(_i("Temp. calibration"), lcd_pinda_calibration_menu);////MSG_CALIBRATION_PINDA_MENU c=17
     }
 #endif //MK1BP
   }
@@ -5902,11 +5821,9 @@ void bowden_menu() {
 	int enc_dif = lcd_encoder_diff;
 	int cursor_pos = 0;
 	lcd_clear();
-	lcd_set_cursor(0, 0);
-	lcd_print(">");
+	lcd_putc_at(0, 0, '>');
 	for (uint_least8_t i = 0; i < 4; i++) {
-		lcd_set_cursor(1, i);
-		lcd_print("Extruder ");
+		lcd_puts_at_P(1, i, PSTR("Extruder "));
 		lcd_print(i);
 		lcd_print(": ");
 		EEPROM_read_B(EEPROM_BOWDEN_LENGTH + i * 2, &bowden_length[i]);
@@ -5940,16 +5857,8 @@ void bowden_menu() {
 					Sound_MakeSound(e_SOUND_TYPE_BlindAlert);
 				}
 
-				lcd_set_cursor(0, 0);
-				lcd_print(" ");
-				lcd_set_cursor(0, 1);
-				lcd_print(" ");
-				lcd_set_cursor(0, 2);
-				lcd_print(" ");
-				lcd_set_cursor(0, 3);
-				lcd_print(" ");
-				lcd_set_cursor(0, cursor_pos);
-				lcd_print(">");
+				lcd_puts_at_P(0, 0, PSTR(" \n \n \n "));
+				lcd_putc_at(0, cursor_pos, '>');
 				Sound_MakeSound(e_SOUND_TYPE_EncoderMove);
 				enc_dif = lcd_encoder_diff;
 				_delay(100);
@@ -5963,8 +5872,7 @@ void bowden_menu() {
 				manage_heater();
 				manage_inactivity(true);
 
-				lcd_set_cursor(1, 1);
-				lcd_print("Extruder ");
+				lcd_puts_at_P(1, 1, PSTR("Extruder "));
 				lcd_print(cursor_pos);
 				lcd_print(": ");
 				lcd_set_cursor(13, 1);
@@ -5993,11 +5901,9 @@ void bowden_menu() {
 						lcd_update_enable(true);
 						lcd_clear();
 						enc_dif = lcd_encoder_diff;
-						lcd_set_cursor(0, cursor_pos);
-						lcd_print(">");
+						lcd_putc_at(0, cursor_pos, '>');
 						for (uint_least8_t i = 0; i < 4; i++) {
-							lcd_set_cursor(1, i);
-							lcd_print("Extruder ");
+							lcd_puts_at_P(1, i, PSTR("Extruder "));
 							lcd_print(i);
 							lcd_print(": ");
 							EEPROM_read_B(EEPROM_BOWDEN_LENGTH + i * 2, &bowden_length[i]);
@@ -6017,10 +5923,10 @@ void bowden_menu() {
 
 static char snmm_stop_print_menu() { //menu for choosing which filaments will be unloaded in stop print
 	lcd_clear();
-	lcd_puts_at_P(0,0,_T(MSG_UNLOAD_FILAMENT)); lcd_print(":");
-	lcd_set_cursor(0, 1); lcd_print(">");
-	lcd_puts_at_P(1,2,_i("Used during print"));////MSG_USED c=19 r=1
-	lcd_puts_at_P(1,3,_i("Current"));////MSG_CURRENT c=19 r=1
+	lcd_puts_at_P(0,0,_T(MSG_UNLOAD_FILAMENT)); lcd_print(':');
+	lcd_set_cursor(0, 1); lcd_print('>');
+	lcd_puts_at_P(1,2,_i("Used during print"));////MSG_USED c=19
+	lcd_puts_at_P(1,3,_i("Current"));////MSG_CURRENT c=19
 	char cursor_pos = 1;
 	int enc_dif = 0;
 	KEEPALIVE_STATE(PAUSED_FOR_USER);
@@ -6042,14 +5948,9 @@ static char snmm_stop_print_menu() { //menu for choosing which filaments will be
 					Sound_MakeSound(e_SOUND_TYPE_BlindAlert);
 				}	
 
-				lcd_set_cursor(0, 1);
-				lcd_print(" ");
-				lcd_set_cursor(0, 2);
-				lcd_print(" ");
-				lcd_set_cursor(0, 3);
-				lcd_print(" ");
+				lcd_puts_at_P(0, 1, PSTR(" \n \n "));
 				lcd_set_cursor(0, cursor_pos);
-				lcd_print(">");
+				lcd_print('>');
 				enc_dif = lcd_encoder_diff;
 				Sound_MakeSound(e_SOUND_TYPE_EncoderMove);
 				_delay(100);
@@ -6152,14 +6053,8 @@ uint8_t choose_menu_P(const char *header, const char *item, const char *last_ite
 
         if (last_item&&last_visible) lcd_puts_at_P(1, 3, last_item);
 
-        lcd_set_cursor(0, 1);
-        lcd_print(" ");
-        lcd_set_cursor(0, 2);
-        lcd_print(" ");
-        lcd_set_cursor(0, 3);
-        lcd_print(" ");
-        lcd_set_cursor(0, cursor_pos);
-        lcd_print(">");
+        lcd_puts_at_P(0, 1, PSTR(" \n \n "));
+        lcd_putc_at(0, cursor_pos, '>');
         _delay(100);
 
 		if (lcd_clicked())
@@ -6173,34 +6068,31 @@ uint8_t choose_menu_P(const char *header, const char *item, const char *last_ite
 }
 
 char reset_menu() {
+    const uint8_t items_no =
 #ifdef SNMM
-	int items_no = 5;
+        6;
 #else
-	int items_no = 4;
+        5;
 #endif
-	static int first = 0;
-	int enc_dif = 0;
+    static int8_t first = 0;
+    int8_t enc_dif = 0;
 	char cursor_pos = 0;
-	const char *item [items_no];
-	
-	item[0] = "Language";
-	item[1] = "Statistics";
-	item[2] = "Shipping prep";
-	item[3] = "All Data";
-#ifdef SNMM
-	item[4] = "Bowden length";
-#endif // SNMM
 
+    const char *const item[items_no] = {PSTR("Language"), PSTR("Statistics"), PSTR("Shipping prep"), PSTR("Service prep"), PSTR("All Data")
+#ifdef SNMM
+    , PSTR("Bowden length")
+#endif
+    };
+	
 	enc_dif = lcd_encoder_diff;
 	lcd_clear();
 	lcd_set_cursor(0, 0);
-	lcd_print(">");
+    lcd_putc('>');
 	lcd_consume_click();
 	while (1) {		
 
 		for (uint_least8_t i = 0; i < 4; i++) {
-			lcd_set_cursor(1, i);
-			lcd_print(item[first + i]);
+			lcd_puts_at_P(1, i, item[first + i]);
 		}
 
 		manage_heater();
@@ -6234,16 +6126,9 @@ char reset_menu() {
 						lcd_clear();
 					}
 				}
-				lcd_set_cursor(0, 0);
-				lcd_print(" ");
-				lcd_set_cursor(0, 1);
-				lcd_print(" ");
-				lcd_set_cursor(0, 2);
-				lcd_print(" ");
-				lcd_set_cursor(0, 3);
-				lcd_print(" ");
+				lcd_puts_at_P(0, 0, PSTR(" \n \n \n "));
 				lcd_set_cursor(0, cursor_pos);
-				lcd_print(">");
+				lcd_putc('>');
 				Sound_MakeSound(e_SOUND_TYPE_EncoderMove);
 				enc_dif = lcd_encoder_diff;
 				_delay(100);
@@ -6388,22 +6273,23 @@ static void fil_unload_menu()
 static void change_extr_menu(){
 	MENU_BEGIN();
 	MENU_ITEM_BACK_P(_T(MSG_MAIN));
-	MENU_ITEM_FUNCTION_P(_i("Extruder 1"), extr_change_0);////MSG_EXTRUDER_1 c=17 r=1
-	MENU_ITEM_FUNCTION_P(_i("Extruder 2"), extr_change_1);////MSG_EXTRUDER_2 c=17 r=1
-	MENU_ITEM_FUNCTION_P(_i("Extruder 3"), extr_change_2);////MSG_EXTRUDER_3 c=17 r=1
-	MENU_ITEM_FUNCTION_P(_i("Extruder 4"), extr_change_3);////MSG_EXTRUDER_4 c=17 r=1
+	MENU_ITEM_FUNCTION_P(_i("Extruder 1"), extr_change_0);////MSG_EXTRUDER_1 c=17
+	MENU_ITEM_FUNCTION_P(_i("Extruder 2"), extr_change_1);////MSG_EXTRUDER_2 c=17
+	MENU_ITEM_FUNCTION_P(_i("Extruder 3"), extr_change_2);////MSG_EXTRUDER_3 c=17
+	MENU_ITEM_FUNCTION_P(_i("Extruder 4"), extr_change_3);////MSG_EXTRUDER_4 c=17
 
 	MENU_END();
 }
 #endif //SNMM
 
-//unload filament for single material printer (used in M702 gcode)
-void unload_filament()
+// unload filament for single material printer (used in M702 gcode)
+// @param automatic: If true, unload_filament is part of a unload+load sequence (M600)
+void unload_filament(bool automatic)
 {
 	custom_message_type = CustomMsg::FilamentLoading;
 	lcd_setstatuspgm(_T(MSG_UNLOADING_FILAMENT));
 
-    raise_z_above(MIN_Z_FOR_UNLOAD);
+    raise_z_above(automatic? MIN_Z_FOR_SWAP: MIN_Z_FOR_UNLOAD);
 
 	//		extr_unload2();
 
@@ -6417,270 +6303,39 @@ void unload_filament()
 	plan_buffer_line_curposXYZE(1000 / 60);
 	st_synchronize();
 
-	lcd_display_message_fullscreen_P(_T(MSG_PULL_OUT_FILAMENT));
-
-	//disable extruder steppers so filament can be removed
-	disable_e0();
-	disable_e1();
-	disable_e2();
-	_delay(100);
-
-	Sound_MakeSound(e_SOUND_TYPE_StandardPrompt);
-	uint8_t counterBeep = 0;
-	while (!lcd_clicked() && (counterBeep < 50)) {
-		delay_keep_alive(100);
-		counterBeep++;
-	}
-	st_synchronize();
-	while (lcd_clicked()) delay_keep_alive(100);
-
-	lcd_update_enable(true);
-
-	lcd_setstatuspgm(_T(WELCOME_MSG));
-	custom_message_type = CustomMsg::Status;
-
-}
-
-static void lcd_farm_no()
-{
-	char step = 0;
-	int enc_dif = 0;
-	int _farmno = farm_no;
-	int _ret = 0;
-	lcd_clear();
-
-	lcd_set_cursor(0, 0);
-	lcd_print("Farm no");
-
-	do
-	{
-
-		if (abs((enc_dif - lcd_encoder_diff)) > 2) {
-			if (enc_dif > lcd_encoder_diff) {
-				switch (step) {
-				case(0): if (_farmno >= 100) _farmno -= 100; break;
-				case(1): if (_farmno % 100 >= 10) _farmno -= 10; break;
-				case(2): if (_farmno % 10 >= 1) _farmno--; break;
-				default: break;
-				}
-			}
-
-			if (enc_dif < lcd_encoder_diff) {
-				switch (step) {
-				case(0): if (_farmno < 900) _farmno += 100; break;
-				case(1): if (_farmno % 100 < 90) _farmno += 10; break;
-				case(2): if (_farmno % 10 <= 8)_farmno++; break;
-				default: break;
-				}
-			}
-			enc_dif = 0;
-			lcd_encoder_diff = 0;
-		}
-
-		lcd_set_cursor(0, 2);
-		if (_farmno < 100) lcd_print("0");
-		if (_farmno < 10) lcd_print("0");
-		lcd_print(_farmno);
-		lcd_print("  ");
-		lcd_set_cursor(0, 3);
-		lcd_print("   ");
-
-
-		lcd_set_cursor(step, 3);
-		lcd_print("^");
-		_delay(100);
-
-		if (lcd_clicked())
-		{
-			_delay(200);
-			step++;
-			if(step == 3) {
-				_ret = 1;
-				farm_no = _farmno;
-				EEPROM_save_B(EEPROM_FARM_NUMBER, &farm_no);
-				prusa_statistics(20);
-				lcd_return_to_status();
-			}
-		}
-
-		manage_heater();
-	} while (_ret == 0);
-
-}
-
-
-unsigned char lcd_choose_color() {
-	//function returns index of currently chosen item
-	//following part can be modified from 2 to 255 items:
-	//-----------------------------------------------------
-	unsigned char items_no = 2;
-	const char *item[items_no];
-	item[0] = "Orange";
-	item[1] = "Black";
-	//-----------------------------------------------------
-	uint_least8_t active_rows;
-	static int first = 0;
-	int enc_dif = 0;
-	unsigned char cursor_pos = 1;
-	enc_dif = lcd_encoder_diff;
-	lcd_clear();
-	lcd_set_cursor(0, 1);
-	lcd_print(">");
-
-	active_rows = items_no < 3 ? items_no : 3;
-	lcd_consume_click();
-	while (1) {
-		lcd_puts_at_P(0, 0, PSTR("Choose color:"));
-		for (uint_least8_t i = 0; i < active_rows; i++) {
-			lcd_set_cursor(1, i+1);
-			lcd_print(item[first + i]);
-		}
-
-		manage_heater();
-		manage_inactivity(true);
-		proc_commands();
-		if (abs((enc_dif - lcd_encoder_diff)) > 12) {
-					
-				if (enc_dif > lcd_encoder_diff) {
-					cursor_pos--;
-				}
-
-				if (enc_dif < lcd_encoder_diff) {
-					cursor_pos++;
-				}
-				
-				if (cursor_pos > active_rows) {
-					cursor_pos = active_rows;
-					Sound_MakeSound(e_SOUND_TYPE_BlindAlert);
-					if (first < items_no - active_rows) {
-						first++;
-						lcd_clear();
-					}
-				}
-
-				if (cursor_pos < 1) {
-					cursor_pos = 1;
-					Sound_MakeSound(e_SOUND_TYPE_BlindAlert);
-					if (first > 0) {
-						first--;
-						lcd_clear();
-					}
-				}
-				lcd_set_cursor(0, 1);
-				lcd_print(" ");
-				lcd_set_cursor(0, 2);
-				lcd_print(" ");
-				lcd_set_cursor(0, 3);
-				lcd_print(" ");
-				lcd_set_cursor(0, cursor_pos);
-				lcd_print(">");
-				Sound_MakeSound(e_SOUND_TYPE_EncoderMove);
-				enc_dif = lcd_encoder_diff;
-				_delay(100);
-
-		}
-
-		if (lcd_clicked()) {
-			Sound_MakeSound(e_SOUND_TYPE_ButtonEcho);
-			switch(cursor_pos + first - 1) {
-			case 0: return 1; break;
-			case 1: return 0; break;
-			default: return 99; break;
-			}
-		}
-
-	}
-
-}
-
-void lcd_confirm_print()
-{
-	uint8_t filament_type;
-	int enc_dif = 0;
-	int cursor_pos = 1;
-	int _ret = 0;
-	int _t = 0;
-
-	enc_dif = lcd_encoder_diff;
-	lcd_clear();
-
-	lcd_set_cursor(0, 0);
-	lcd_print("Print ok ?");
-
-	do
-	{
-		if (abs(enc_dif - lcd_encoder_diff) > 12) {
-			if (enc_dif > lcd_encoder_diff) {
-				cursor_pos--;
-			}
-
-			if (enc_dif < lcd_encoder_diff) {
-				cursor_pos++;
-			}
-			enc_dif = lcd_encoder_diff;
-		}
-
-		if (cursor_pos > 2) { cursor_pos = 2; }
-		if (cursor_pos < 1) { cursor_pos = 1; }
-
-		lcd_set_cursor(0, 2); lcd_print("          ");
-		lcd_set_cursor(0, 3); lcd_print("          ");
-		lcd_set_cursor(2, 2);
-		lcd_puts_P(_T(MSG_YES));
-		lcd_set_cursor(2, 3);
-		lcd_puts_P(_T(MSG_NO));
-		lcd_set_cursor(0, 1 + cursor_pos);
-		lcd_print(">");
-		_delay(100);
-
-		_t = _t + 1;
-		if (_t>100)
-		{
-			prusa_statistics(99);
-			_t = 0;
-		}
-		if (lcd_clicked())
-		{
-               filament_type = FARM_FILAMENT_COLOR_NONE;
-			if (cursor_pos == 1)
-			{
-				_ret = 1;
-//				filament_type = lcd_choose_color();
-				prusa_statistics(4, filament_type);
-				no_response = true; //we need confirmation by recieving PRUSA thx
-				important_status = 4;
-				saved_filament_type = filament_type;
-				NcTime = _millis();
-			}
-			if (cursor_pos == 2)
-			{
-				_ret = 2;
-//				filament_type = lcd_choose_color();
-				prusa_statistics(5, filament_type);
-				no_response = true; //we need confirmation by recieving PRUSA thx
-				important_status = 5;				
-				saved_filament_type = filament_type;
-				NcTime = _millis();
-			}
-		}
+	lcd_display_message_fullscreen_P(_T(MSG_PULL_OUT_FILAMENT));
 
-		manage_heater();
-		manage_inactivity();
-		proc_commands();
+	//disable extruder steppers so filament can be removed
+	disable_e0();
+	disable_e1();
+	disable_e2();
+	_delay(100);
+
+	Sound_MakeSound(e_SOUND_TYPE_StandardPrompt);
+	uint8_t counterBeep = 0;
+	while (!lcd_clicked() && (counterBeep < 50)) {
+		delay_keep_alive(100);
+		counterBeep++;
+	}
+	st_synchronize();
+	while (lcd_clicked()) delay_keep_alive(100);
+
+	lcd_update_enable(true);
 
-	} while (_ret == 0);
+	lcd_setstatuspgm(_T(WELCOME_MSG));
+	custom_message_type = CustomMsg::Status;
 
 }
 
-#include "w25x20cl.h"
+#include "xflash.h"
 
 #ifdef LCD_TEST
 static void lcd_test_menu()
 {
-	W25X20CL_SPI_ENTER();
-	w25x20cl_enable_wr();
-	w25x20cl_chip_erase();
-	w25x20cl_disable_wr();
+	XFLASH_SPI_ENTER();
+	xflash_enable_wr();
+	xflash_chip_erase();
+	xflash_disable_wr();
 }
 #endif //LCD_TEST
 
@@ -6723,24 +6378,33 @@ static bool fan_error_selftest()
     return 0;
 }
 
-//! @brief Resume paused print
+//! @brief Resume paused print, send host action "resumed"
 //! @todo It is not good to call restore_print_from_ram_and_continue() from function called by lcd_update(),
 //! as restore_print_from_ram_and_continue() calls lcd_update() internally.
 void lcd_resume_print()
 {
     lcd_return_to_status();
     lcd_reset_alert_level(); //for fan speed error
-    if (fan_error_selftest()) return; //abort if error persists
-
+    if (fan_error_selftest()) {
+        if (is_usb_printing) SERIAL_PROTOCOLLNRPGM(MSG_OCTOPRINT_PAUSED);
+        return; //abort if error persists
+    }
+    cmdqueue_serial_disabled = false;
     lcd_setstatuspgm(_T(MSG_FINISHING_MOVEMENTS));
     st_synchronize();
-
-    lcd_setstatuspgm(_T(MSG_RESUMING_PRINT)); ////MSG_RESUMING_PRINT c=20
+    custom_message_type = CustomMsg::Resuming;
     isPrintPaused = false;
     restore_print_from_ram_and_continue(default_retraction);
     pause_time += (_millis() - start_pause_print); //accumulate time when print is paused for correct statistics calculation
     refresh_cmd_timeout();
     SERIAL_PROTOCOLLNRPGM(MSG_OCTOPRINT_RESUMED); //resume octoprint
+    custom_message_type = CustomMsg::Status;
+}
+
+//! @brief Resume paused USB/host print, send host action "resume"
+void lcd_resume_usb_print()
+{
+    SERIAL_PROTOCOLLNRPGM(MSG_OCTOPRINT_RESUME); //resume octoprint
 }
 
 static void change_sheet()
@@ -6823,204 +6487,202 @@ static void activate_calibrate_sheet()
 static void lcd_sheet_menu()
 {
     MENU_BEGIN();
-    MENU_ITEM_BACK_P(_i("Steel sheets")); ////MSG_STEEL_SHEETS c=18
+    MENU_ITEM_BACK_P(_T(MSG_STEEL_SHEETS));
 
 	if(eeprom_is_sheet_initialized(selected_sheet)){
-	    MENU_ITEM_SUBMENU_P(_i("Select"), change_sheet); //// c=18
+	    MENU_ITEM_SUBMENU_P(_i("Select"), change_sheet); ////MSG_SELECT c=18
 	}
 
     if (lcd_commands_type == LcdCommands::Idle)
     {
         MENU_ITEM_SUBMENU_P(_T(MSG_V2_CALIBRATION), activate_calibrate_sheet);////MSG_V2_CALIBRATION c=18
     }
-    MENU_ITEM_SUBMENU_P(_i("Rename"), lcd_rename_sheet_menu); //// c=18
-	MENU_ITEM_FUNCTION_P(_i("Reset"), lcd_reset_sheet); //// c=18
+    MENU_ITEM_SUBMENU_P(_i("Rename"), lcd_rename_sheet_menu); ////MSG_RENAME c=18
+	MENU_ITEM_FUNCTION_P(_T(MSG_RESET), lcd_reset_sheet); ////MSG_RESET c=14
 
     MENU_END();
 }
 
+//! @brief Show Main Menu
+//!
+//! @code{.unparsed}
+//! |01234567890123456789|
+//! | Info screen        | allways
+//!
+//! | tst - Save         | ifdef RESUME_DEBUG
+//! | tst - Restore      | ifdef RESUME_DEBUG
+//!
+//! | recover print      | ifdef TMC2130_DEBUG
+//! | power panic        | ifdef TMC2130_DEBUG
+//! 
+//! | Live adjust Z      | printing + Z low 
+//!
+//! | Change filament    | farm mode
+//!
+//! | Tune               | printing + paused
+//! | Pause print        | printing + not paused
+//! | Resume print       | printing + paused
+//! | Stop print         | printing or paused + NOT MBL
+//! | Preheat            | not printing + not paused
+//! | Print from SD      | not printing or paused
+//!
+//! | Switch sheet       | farm mode
+//!
+//! | AutoLoad filament  | not printing + not mmu or paused
+//! | Load filament      | not printing + mmu or paused
+//! | Load to nozzle      | not printing + mmu or paused
+//! | Unload filament    | not printing or paused
+//! | Eject filament     | not printing + mmu or paused
+//! | Cut filament       | not printing + mmu or paused + cut atctive
+//! | Settings           | not printing or paused
+//! | Calibration        | not printing 
+//! | Statistics         | not printing
+//! | Fail stats         | allways
+//! | Fail stats MMU     | mmu
+//! | Support            | allways
+//! @endcode
 static void lcd_main_menu()
 {
 
-  MENU_BEGIN();
+    MENU_BEGIN();
 
-  // Majkl superawesome menu
+    // Majkl superawesome menu
 
 
- MENU_ITEM_BACK_P(_T(MSG_WATCH));
+    MENU_ITEM_BACK_P(_T(MSG_WATCH));
 
 #ifdef RESUME_DEBUG 
- if (!saved_printing) 
-  MENU_ITEM_FUNCTION_P(PSTR("tst - Save"), lcd_menu_test_save);
- else
-  MENU_ITEM_FUNCTION_P(PSTR("tst - Restore"), lcd_menu_test_restore);
+    if (!saved_printing) 
+        MENU_ITEM_FUNCTION_P(PSTR("tst - Save"), lcd_menu_test_save);
+    else
+        MENU_ITEM_FUNCTION_P(PSTR("tst - Restore"), lcd_menu_test_restore);
 #endif //RESUME_DEBUG 
 
 #ifdef TMC2130_DEBUG
- MENU_ITEM_FUNCTION_P(PSTR("recover print"), recover_print);
- MENU_ITEM_FUNCTION_P(PSTR("power panic"), uvlo_);
+    MENU_ITEM_FUNCTION_P(PSTR("recover print"), recover_print);
+    MENU_ITEM_FUNCTION_P(PSTR("power panic"), uvlo_);
 #endif //TMC2130_DEBUG
- 
-  if ( ( IS_SD_PRINTING || is_usb_printing || (lcd_commands_type == LcdCommands::Layer1Cal)) && (current_position[Z_AXIS] < Z_HEIGHT_HIDE_LIVE_ADJUST_MENU) && !homing_flag && !mesh_bed_leveling_flag)
-  {
-	MENU_ITEM_SUBMENU_P(_T(MSG_BABYSTEP_Z), lcd_babystep_z);//8
-  }
 
+    if ( ( IS_SD_PRINTING || is_usb_printing || (lcd_commands_type == LcdCommands::Layer1Cal)) && (current_position[Z_AXIS] < Z_HEIGHT_HIDE_LIVE_ADJUST_MENU) && !homing_flag && !mesh_bed_leveling_flag) {
+        MENU_ITEM_SUBMENU_P(_T(MSG_BABYSTEP_Z), lcd_babystep_z);//8
+    }
 
-  if ( moves_planned() || IS_SD_PRINTING || is_usb_printing || (lcd_commands_type == LcdCommands::Layer1Cal))
-  {
-    MENU_ITEM_SUBMENU_P(_i("Tune"), lcd_tune_menu);////MSG_TUNE
-  } else 
-  {
-    MENU_ITEM_SUBMENU_P(_i("Preheat"), lcd_preheat_menu);////MSG_PREHEAT
-  }
-
+    if (farm_mode)
+        MENU_ITEM_FUNCTION_P(_T(MSG_FILAMENTCHANGE), lcd_colorprint_change);//8
 
-  if(isPrintPaused && saved_printing_type == PRINTING_TYPE_USB)
-  {
-#ifdef FANCHECK
-      if((fan_check_error == EFCE_FIXED) || (fan_check_error == EFCE_OK))
-          MENU_ITEM_SUBMENU_P(_i("Resume print"), lcd_resume_print);////MSG_RESUME_PRINT
-#else
-      MENU_ITEM_SUBMENU_P(_i("Resume print"), lcd_resume_print);////MSG_RESUME_PRINT
-#endif
-  }
+    if ( moves_planned() || PRINTER_ACTIVE ) {
+        MENU_ITEM_SUBMENU_P(_i("Tune"), lcd_tune_menu);////MSG_TUNE c=18
+    } else {
+        MENU_ITEM_SUBMENU_P(_i("Preheat"), lcd_preheat_menu);////MSG_PREHEAT c=18
+    }
 
-#ifdef SDSUPPORT
-  if (card.cardOK || lcd_commands_type == LcdCommands::Layer1Cal)
-  {
-    if (card.isFileOpen())
+    if (mesh_bed_leveling_flag == false && homing_flag == false && !isPrintPaused) {
+        if (is_usb_printing) {
+            MENU_ITEM_FUNCTION_P(_T(MSG_PAUSE_PRINT), lcd_pause_usb_print);////MSG_PAUSE_PRINT c=18
+        } else if (IS_SD_PRINTING) {
+            MENU_ITEM_FUNCTION_P(_T(MSG_PAUSE_PRINT), lcd_pause_print);////MSG_PAUSE_PRINT c=18
+        }
+    }
+    if(isPrintPaused)
     {
-		if (mesh_bed_leveling_flag == false && homing_flag == false) {
-			if (card.sdprinting)
-			{
-				MENU_ITEM_FUNCTION_P(_i("Pause print"), lcd_pause_print);////MSG_PAUSE_PRINT
-			}
-			else if(isPrintPaused)
-			{
-				#ifdef FANCHECK
-					if((fan_check_error == EFCE_FIXED) || (fan_check_error == EFCE_OK))
-						MENU_ITEM_SUBMENU_P(_i("Resume print"), lcd_resume_print);////MSG_RESUME_PRINT
-				#else
-					MENU_ITEM_SUBMENU_P(_i("Resume print"), lcd_resume_print);////MSG_RESUME_PRINT
-				#endif
-
-			}
-			MENU_ITEM_SUBMENU_P(_T(MSG_STOP_PRINT), lcd_sdcard_stop);
-		}
-	}
-	else if (lcd_commands_type == LcdCommands::Layer1Cal && mesh_bed_leveling_flag == false && homing_flag == false) {
-		//MENU_ITEM_SUBMENU_P(_T(MSG_STOP_PRINT), lcd_sdcard_stop);
-	}
-	else
-	{
-		if (!is_usb_printing && (lcd_commands_type != LcdCommands::Layer1Cal))
-		{
-			//if (farm_mode) MENU_ITEM_SUBMENU_P(MSG_FARM_CARD_MENU, lcd_farm_sdcard_menu);
-			/*else*/ {
+#ifdef FANCHECK
+        if((fan_check_error == EFCE_FIXED) || (fan_check_error == EFCE_OK))
+#endif //FANCHECK
+        {
+            if (is_usb_printing) {
+                MENU_ITEM_SUBMENU_P(_T(MSG_RESUME_PRINT), lcd_resume_usb_print);////MSG_RESUME_PRINT c=18
+            } else {
+                MENU_ITEM_SUBMENU_P(_T(MSG_RESUME_PRINT), lcd_resume_print);////MSG_RESUME_PRINT c=18
+            }
+        }
+    }
+    if((IS_SD_PRINTING || is_usb_printing || isPrintPaused) && (custom_message_type != CustomMsg::MeshBedLeveling)) {
+        MENU_ITEM_SUBMENU_P(_T(MSG_STOP_PRINT), lcd_sdcard_stop);
+    }
+#ifdef SDSUPPORT //!@todo SDSUPPORT undefined creates several issues in source code
+    if (card.cardOK || lcd_commands_type == LcdCommands::Layer1Cal) {
+        if (!card.isFileOpen()) {
+            if (!is_usb_printing && (lcd_commands_type != LcdCommands::Layer1Cal)) {
+            //if (farm_mode) MENU_ITEM_SUBMENU_P(MSG_FARM_CARD_MENU, lcd_farm_sdcard_menu);
+            /*else*/{
                         bMain=true;               // flag ('fake parameter') for 'lcd_sdcard_menu()' function
                         MENU_ITEM_SUBMENU_P(_T(MSG_CARD_MENU), lcd_sdcard_menu);
-                        }
-		}
+                    }
+        }
 #if SDCARDDETECT < 1
-      MENU_ITEM_GCODE_P(_i("Change SD card"), PSTR("M21"));  // SD-card changed by user////MSG_CNG_SDCARD
-#endif
-    }
-	
-  } else 
-  {
-    bMain=true;                                   // flag (i.e. 'fake parameter') for 'lcd_sdcard_menu()' function
-    MENU_ITEM_SUBMENU_P(_i("No SD card"), lcd_sdcard_menu);////MSG_NO_CARD
+        MENU_ITEM_GCODE_P(_i("Change SD card"), PSTR("M21"));  // SD-card changed by user////MSG_CNG_SDCARD
+#endif //SDCARDDETECT
+        }
+    } else {
+        bMain=true;                                   // flag (i.e. 'fake parameter') for 'lcd_sdcard_menu()' function
+        MENU_ITEM_SUBMENU_P(_i("No SD card"), lcd_sdcard_menu);////MSG_NO_CARD c=18
 #if SDCARDDETECT < 1
-    MENU_ITEM_GCODE_P(_i("Init. SD card"), PSTR("M21")); // Manually initialize the SD-card via user interface////MSG_INIT_SDCARD
-#endif
-  }
-#endif
-
-  if(!isPrintPaused && !IS_SD_PRINTING && !is_usb_printing && (lcd_commands_type != LcdCommands::Layer1Cal))
-  {
-    if (!farm_mode)
-    {
-        const int8_t sheet = eeprom_read_byte(&(EEPROM_Sheets_base->active_sheet));
-        const int8_t nextSheet = eeprom_next_initialized_sheet(sheet);
-        if ((nextSheet >= 0) && (sheet != nextSheet)) // show menu only if we have 2 or more sheets initialized
-        {
-            MENU_ITEM_FUNCTION_E(EEPROM_Sheets_base->s[sheet], eeprom_switch_to_next_sheet);
+        MENU_ITEM_GCODE_P(_i("Init. SD card"), PSTR("M21")); // Manually initialize the SD-card via user interface////MSG_INIT_SDCARD
+#endif //SDCARDDETECT
+    }
+#endif //SDSUPPORT
+
+    if(!isPrintPaused && !IS_SD_PRINTING && !is_usb_printing && (lcd_commands_type != LcdCommands::Layer1Cal)) {
+        if (!farm_mode) {
+            const int8_t sheet = eeprom_read_byte(&(EEPROM_Sheets_base->active_sheet));
+            const int8_t nextSheet = eeprom_next_initialized_sheet(sheet);
+            if ((nextSheet >= 0) && (sheet != nextSheet)) { // show menu only if we have 2 or more sheets initialized
+                MENU_ITEM_FUNCTION_E(EEPROM_Sheets_base->s[sheet], eeprom_switch_to_next_sheet);
+            }
         }
     }
-  }
-
 
-  if (IS_SD_PRINTING || is_usb_printing || (lcd_commands_type == LcdCommands::Layer1Cal))
-  {
-	  if (farm_mode)
-	  {
-		  MENU_ITEM_SUBMENU_P(PSTR("Farm number"), lcd_farm_no);
-	  }
-  } 
-  else 
-  {
-	if (mmu_enabled)
-	{
-		MENU_ITEM_SUBMENU_P(_T(MSG_LOAD_FILAMENT), fil_load_menu);
-		MENU_ITEM_SUBMENU_P(_i("Load to nozzle"), mmu_load_to_nozzle_menu);
+    if ( ! ( IS_SD_PRINTING || is_usb_printing || (lcd_commands_type == LcdCommands::Layer1Cal) ) ) {
+        if (mmu_enabled) {
+            MENU_ITEM_SUBMENU_P(_T(MSG_LOAD_FILAMENT), fil_load_menu);
+            MENU_ITEM_SUBMENU_P(_i("Load to nozzle"), mmu_load_to_nozzle_menu);////MSG_LOAD_TO_NOZZLE c=18
 //-//          MENU_ITEM_FUNCTION_P(_T(MSG_UNLOAD_FILAMENT), extr_unload);
 //bFilamentFirstRun=true;
-          MENU_ITEM_SUBMENU_P(_T(MSG_UNLOAD_FILAMENT), mmu_unload_filament);
-		MENU_ITEM_SUBMENU_P(_i("Eject filament"), mmu_fil_eject_menu);
+            MENU_ITEM_SUBMENU_P(_T(MSG_UNLOAD_FILAMENT), mmu_unload_filament);
+            MENU_ITEM_SUBMENU_P(_T(MSG_EJECT_FILAMENT), mmu_fil_eject_menu);
 #ifdef  MMU_HAS_CUTTER
-        MENU_ITEM_SUBMENU_P(_i("Cut filament"), mmu_cut_filament_menu);
+            MENU_ITEM_SUBMENU_P(_T(MSG_CUT_FILAMENT), mmu_cut_filament_menu);
 #endif //MMU_HAS_CUTTER
-	}
-	else
-	{
+        } else {
 #ifdef SNMM
-		MENU_ITEM_SUBMENU_P(_T(MSG_UNLOAD_FILAMENT), fil_unload_menu);
-		MENU_ITEM_SUBMENU_P(_i("Change extruder"), change_extr_menu);////MSG_CHANGE_EXTR c=20 r=1
+            MENU_ITEM_SUBMENU_P(_T(MSG_UNLOAD_FILAMENT), fil_unload_menu);
+            MENU_ITEM_SUBMENU_P(_i("Change extruder"), change_extr_menu);////MSG_CHANGE_EXTR c=20
 #endif
 #ifdef FILAMENT_SENSOR
-		if ((fsensor_autoload_enabled == true) && (fsensor_enabled == true) && (mmu_enabled == false))
-			MENU_ITEM_SUBMENU_P(_i("AutoLoad filament"), lcd_menu_AutoLoadFilament);////MSG_AUTOLOAD_FILAMENT c=18
-		else
+            if ((fsensor_autoload_enabled == true) && (fsensor_enabled == true) && (mmu_enabled == false))
+                MENU_ITEM_SUBMENU_P(_i("AutoLoad filament"), lcd_menu_AutoLoadFilament);////MSG_AUTOLOAD_FILAMENT c=18
+            else
 #endif //FILAMENT_SENSOR
-          {
-               bFilamentFirstRun=true;
-			MENU_ITEM_SUBMENU_P(_T(MSG_LOAD_FILAMENT), lcd_LoadFilament);
-          }
-          bFilamentFirstRun=true;
-		MENU_ITEM_SUBMENU_P(_T(MSG_UNLOAD_FILAMENT), lcd_unLoadFilament);
-	}
-	MENU_ITEM_SUBMENU_P(_T(MSG_SETTINGS), lcd_settings_menu);
+            {
+                bFilamentFirstRun=true;
+                MENU_ITEM_SUBMENU_P(_T(MSG_LOAD_FILAMENT), lcd_LoadFilament);
+            }
+            bFilamentFirstRun=true;
+            MENU_ITEM_SUBMENU_P(_T(MSG_UNLOAD_FILAMENT), lcd_unLoadFilament);
+        }
+    MENU_ITEM_SUBMENU_P(_T(MSG_SETTINGS), lcd_settings_menu);
     if(!isPrintPaused) MENU_ITEM_SUBMENU_P(_T(MSG_MENU_CALIBRATION), lcd_calibration_menu);
+    }
+
+    if (!is_usb_printing && (lcd_commands_type != LcdCommands::Layer1Cal)) {
+        MENU_ITEM_SUBMENU_P(_i("Statistics"), lcd_menu_statistics);////MSG_STATISTICS c=18
+    }
 
-  }
-  
-  if (!is_usb_printing && (lcd_commands_type != LcdCommands::Layer1Cal))
-  {
-	  MENU_ITEM_SUBMENU_P(_i("Statistics  "), lcd_menu_statistics);////MSG_STATISTICS
-  }
-    
 #if defined(TMC2130) || defined(FILAMENT_SENSOR)
-  MENU_ITEM_SUBMENU_P(_i("Fail stats"), lcd_menu_fails_stats);
+    MENU_ITEM_SUBMENU_P(_i("Fail stats"), lcd_menu_fails_stats);////MSG_FAIL_STATS c=18
 #endif
-  if (mmu_enabled) {
-	  MENU_ITEM_SUBMENU_P(_i("Fail stats MMU"), lcd_menu_fails_stats_mmu);
-  }
-  MENU_ITEM_SUBMENU_P(_i("Support"), lcd_support_menu);////MSG_SUPPORT
+    if (mmu_enabled) {
+        MENU_ITEM_SUBMENU_P(_i("Fail stats MMU"), lcd_menu_fails_stats_mmu);////MSG_MMU_FAIL_STATS c=18
+    }
+    MENU_ITEM_SUBMENU_P(_i("Support"), lcd_support_menu);////MSG_SUPPORT c=18
 #ifdef LCD_TEST
-    MENU_ITEM_SUBMENU_P(_i("W25x20CL init"), lcd_test_menu);////MSG_SUPPORT
+    MENU_ITEM_SUBMENU_P(_i("XFLASH init"), lcd_test_menu);////MSG_SUPPORT
 #endif //LCD_TEST
 
-  MENU_END();
+    MENU_END();
 
 }
 
-void stack_error() {
-	Sound_MakeCustom(1000,0,true);
-	lcd_display_message_fullscreen_P(_i("Error - static memory has been overwritten"));////MSG_STACK_ERROR c=20 r=4
-	//err_triggered = 1;
-	 while (1) delay_keep_alive(1000);
-}
 
 #ifdef DEBUG_STEPPER_TIMER_MISSED
 bool stepper_timer_overflow_state = false;
@@ -7138,18 +6800,19 @@ static void lcd_tune_menu()
 
 	MENU_BEGIN();
 	MENU_ITEM_BACK_P(_T(MSG_MAIN)); //1
-	MENU_ITEM_EDIT_int3_P(_i("Speed"), &feedmultiply, 10, 999);//2////MSG_SPEED
+	MENU_ITEM_EDIT_int3_P(_i("Speed"), &feedmultiply, 10, 999);//2////MSG_SPEED c=15
 
 	MENU_ITEM_EDIT_int3_P(_T(MSG_NOZZLE), &target_temperature[0], 0, HEATER_0_MAXTEMP - 10);//3
 	MENU_ITEM_EDIT_int3_P(_T(MSG_BED), &target_temperature_bed, 0, BED_MAXTEMP - 10);//4
 
 	MENU_ITEM_EDIT_int3_P(_T(MSG_FAN_SPEED), &fanSpeed, 0, 255);//5
-	MENU_ITEM_EDIT_int3_P(_i("Flow"), &extrudemultiply, 10, 999);//6////MSG_FLOW
+	MENU_ITEM_EDIT_int3_P(_i("Flow"), &extrudemultiply, 10, 999);//6////MSG_FLOW c=15
 #ifdef LA_LIVE_K
 	MENU_ITEM_EDIT_advance_K();//7
 #endif
 #ifdef FILAMENTCHANGEENABLE
-	MENU_ITEM_FUNCTION_P(_T(MSG_FILAMENTCHANGE), lcd_colorprint_change);//8
+    if (!farm_mode)
+        MENU_ITEM_FUNCTION_P(_T(MSG_FILAMENTCHANGE), lcd_colorprint_change);//8
 #endif
 
 #ifdef FILAMENT_SENSOR
@@ -7175,10 +6838,8 @@ static void lcd_tune_menu()
 
 	SETTINGS_CUTTER;
 
-     if(farm_mode)
-     {
-       MENU_ITEM_TOGGLE_P(_i("Fans check"), fans_check_enabled ? _T(MSG_ON) : _T(MSG_OFF), lcd_set_fan_check);
-     }
+	MENU_ITEM_TOGGLE_P(_T(MSG_FANS_CHECK), fans_check_enabled ? _T(MSG_ON) : _T(MSG_OFF), lcd_set_fan_check);
+
 
 #ifdef TMC2130
      if(!farm_mode)
@@ -7325,39 +6986,35 @@ static void lcd_control_temperature_menu()
 }
 
 
-#if SDCARDDETECT == -1
+
 static void lcd_sd_refresh()
 {
+#if SDCARDDETECT == -1
   card.initsd();
+#else
+  card.presort();
+#endif
   menu_top = 0;
+  lcd_encoder = 0;
+  menu_data_reset(); //Forces reloading of cached variables.
 }
-#endif
+
 static void lcd_sd_updir()
 {
   card.updir();
   menu_top = 0;
+  lcd_encoder = 0;
+  menu_data_reset(); //Forces reloading of cached variables.
 }
 
 void lcd_print_stop()
 {
     if (!card.sdprinting) {
-        SERIAL_ECHOLNRPGM(MSG_OCTOPRINT_CANCEL);   // for Octoprint
+        SERIAL_ECHOLNRPGM(MSG_OCTOPRINT_CANCEL); // for Octoprint
     }
+    UnconditionalStop();
 
-    CRITICAL_SECTION_START;
-
-    // Clear any saved printing state
-    cancel_saved_printing();
-
-    // Abort the planner/queue/sd
-    planner_abort_hard();
-	cmdqueue_reset();
-	card.sdprinting = false;
-	card.closefile();
-    st_reset_timer();
-
-    CRITICAL_SECTION_END;
-
+    // TODO: all the following should be moved in the main marlin loop!
 #ifdef MESH_BED_LEVELING
     mbl.active = false; //also prevents undoing the mbl compensation a second time in the second planner_abort_hard()
 #endif
@@ -7368,11 +7025,11 @@ void lcd_print_stop()
 	pause_time = 0;
 	save_statistics(total_filament_used, t);
 
+    // reset current command
     lcd_commands_step = 0;
     lcd_commands_type = LcdCommands::Idle;
 
     lcd_cooldown(); //turns off heaters and fan; goes to status screen.
-    cancel_heatup = true; //unroll temperature wait loop stack.
 
     current_position[Z_AXIS] += 10; //lift Z.
     plan_buffer_line_curposXYZE(manual_feedrate[Z_AXIS] / 60);
@@ -7402,20 +7059,16 @@ void lcd_print_stop()
 void lcd_sdcard_stop()
 {
 
-	lcd_set_cursor(0, 0);
-	lcd_puts_P(_T(MSG_STOP_PRINT));
-	lcd_set_cursor(2, 2);
-	lcd_puts_P(_T(MSG_NO));
-	lcd_set_cursor(2, 3);
-	lcd_puts_P(_T(MSG_YES));
-	lcd_set_cursor(0, 2); lcd_print(" ");
-	lcd_set_cursor(0, 3); lcd_print(" ");
+	lcd_puts_at_P(0, 0, _T(MSG_STOP_PRINT));
+	lcd_puts_at_P(2, 2, _T(MSG_NO));
+	lcd_puts_at_P(2, 3, _T(MSG_YES));
+	lcd_putc_at(0, 2, ' ');
+	lcd_putc_at(0, 3, ' ');
 
 	if ((int32_t)lcd_encoder > 2) { lcd_encoder = 2; }
 	if ((int32_t)lcd_encoder < 1) { lcd_encoder = 1; }
 	
-	lcd_set_cursor(0, 1 + lcd_encoder);
-	lcd_print(">");
+	lcd_putc_at(0, 1 + lcd_encoder, '>');
 
 	if (lcd_clicked())
 	{
@@ -7434,57 +7087,156 @@ void lcd_sdcard_stop()
 
 void lcd_sdcard_menu()
 {
-  uint8_t sdSort = eeprom_read_byte((uint8_t*)EEPROM_SD_SORT);
-
-  if (presort_flag == true) {
-	  presort_flag = false;
-	  card.presort();
-  }
-  if (lcd_draw_update == 0 && LCD_CLICKED == 0)
-    //_delay(100);
-    return; // nothing to do (so don't thrash the SD card)
-  uint16_t fileCnt = card.getnrfilenames();
-
-
-  MENU_BEGIN();
-  MENU_ITEM_BACK_P(_T(bMain?MSG_MAIN:MSG_BACK));  // i.e. default menu-item / menu-item after card insertion
-  card.getWorkDirName();
-  if (card.filename[0] == '/')
-  {
+	enum menuState_t : uint8_t {_uninitialized, _standard, _scrolling};
+	typedef struct
+	{
+		menuState_t menuState = _uninitialized;
+		uint8_t offset;
+		bool isDir;
+		const char* scrollPointer;
+		uint16_t selectedFileID;
+		uint16_t fileCnt;
+		int8_t row;
+		uint8_t sdSort;
+		ShortTimer lcd_scrollTimer;
+	} _menu_data_sdcard_t;
+	static_assert(sizeof(menu_data)>= sizeof(_menu_data_sdcard_t),"_menu_data_sdcard_t doesn't fit into menu_data");
+	_menu_data_sdcard_t* _md = (_menu_data_sdcard_t*)&(menu_data[0]);
+	
+	switch(_md->menuState)
+	{
+		case _uninitialized: //Initialize menu data
+		{
+			if (card.presort_flag == true) //used to force resorting if sorting type is changed.
+			{
+				card.presort_flag = false;
+				card.presort();
+			}
+			_md->fileCnt = card.getnrfilenames();
+			_md->sdSort = eeprom_read_byte((uint8_t*)EEPROM_SD_SORT);
+			_md->menuState = _standard;
+		}
+		// FALLTHRU
+		case _standard: //normal menu structure.
+		{
+			if (!_md->lcd_scrollTimer.running()) //if the timer is not running, then the menu state was just switched, so redraw the screen.
+			{
+				_md->lcd_scrollTimer.start();
+				lcd_draw_update = 1;
+			}
+			if (_md->lcd_scrollTimer.expired(500) && (_md->row != -1)) //switch to the scrolling state on timeout if a file/dir is selected.
+			{
+				_md->menuState = _scrolling;
+				_md->offset = 0;
+				_md->scrollPointer = NULL;
+				_md->lcd_scrollTimer.start();
+				lcd_draw_update = 1; //forces last load before switching to scrolling.
+			}
+			if (lcd_draw_update == 0 && !LCD_CLICKED)
+				return; // nothing to do (so don't thrash the SD card)
+			
+			_md->row = -1; // assume that no SD file/dir is currently selected. Once they are rendered, it will be changed to the correct row for the _scrolling state.
+			
+			//if we reached this point it means that the encoder moved or clicked or the state is being switched. Reset the scrollTimer.
+			_md->lcd_scrollTimer.start();
+			
+			MENU_BEGIN();
+			MENU_ITEM_BACK_P(_T(bMain?MSG_MAIN:MSG_BACK));  // i.e. default menu-item / menu-item after card insertion
+			card.getWorkDirName();
+			if (card.filename[0] == '/')
+			{
 #if SDCARDDETECT == -1
-    MENU_ITEM_FUNCTION_P(_T(MSG_REFRESH), lcd_sd_refresh);
+				MENU_ITEM_FUNCTION_P(_T(MSG_REFRESH), lcd_sd_refresh);
+#else
+				if (card.ToshibaFlashAir_isEnabled())
+					MENU_ITEM_FUNCTION_P(_T(MSG_REFRESH), lcd_sd_refresh); //show the refresh option if in flashAir mode.
 #endif
-  } else {
-    MENU_ITEM_FUNCTION_P(PSTR(LCD_STR_FOLDER ".."), lcd_sd_updir);
-  }
+			}
+			else
+				MENU_ITEM_FUNCTION_P(PSTR(LCD_STR_FOLDER ".."), lcd_sd_updir); //Show the updir button if in a subdir.
 
-  for (uint16_t i = 0; i < fileCnt; i++)
-  {
-    if (menu_item == menu_line)
-    {
-		const uint16_t nr = ((sdSort == SD_SORT_NONE) || farm_mode || (sdSort == SD_SORT_TIME)) ? (fileCnt - 1 - i) : i;
-		/*#ifdef SDCARD_RATHERRECENTFIRST
-			#ifndef SDCARD_SORT_ALPHA
-				fileCnt - 1 -
-			#endif
-		#endif
-		i;*/
-		#ifdef SDCARD_SORT_ALPHA
-			if (sdSort == SD_SORT_NONE) card.getfilename(nr);
-			else card.getfilename_sorted(nr);
-		#else
-			 card.getfilename(nr);
-		#endif
+			for (uint16_t i = _md->fileCnt; i-- > 0;) // Every file, from top to bottom.
+			{
+				if (menu_item == menu_line) //If the file is on the screen.
+				{
+					//load filename to memory.
+#ifdef SDCARD_SORT_ALPHA
+					if (_md->sdSort == SD_SORT_NONE)
+						card.getfilename(i);
+					else
+						card.getfilename_sorted(i, _md->sdSort);
+#else
+					card.getfilename(i);
+#endif
+					if (lcd_encoder == menu_item) //If the file is selected.
+					{
+						
+						_md->selectedFileID = i;
+						_md->isDir = card.filenameIsDir;
+						_md->row = menu_row;
+					}
+					if (card.filenameIsDir)
+						MENU_ITEM_SDDIR(card.filename, card.longFilename);
+					else
+						MENU_ITEM_SDFILE(card.filename, card.longFilename);
+				}
+				else MENU_ITEM_DUMMY(); //dummy item that just increments the internal menu counters.
+			}
+			MENU_END();
+		} break;
+		case _scrolling: //scrolling filename
+		{
+			const bool rewindFlag = LCD_CLICKED || lcd_draw_update; //flag that says whether the menu should return to _standard state.
 			
-		if (card.filenameIsDir)
-			MENU_ITEM_SDDIR(card.filename, card.longFilename);
-		else
-			MENU_ITEM_SDFILE(_T(MSG_CARD_MENU), card.filename, card.longFilename);
-    } else {
-      MENU_ITEM_DUMMY();
-    }
-  }
-  MENU_END();
+			if (_md->scrollPointer == NULL)
+			{
+				//load filename to memory.
+#ifdef SDCARD_SORT_ALPHA
+				if (_md->sdSort == SD_SORT_NONE)
+					card.getfilename(_md->selectedFileID);
+				else
+					card.getfilename_sorted(_md->selectedFileID, _md->sdSort);
+#else
+				card.getfilename(_md->selectedFileID);
+#endif
+				_md->scrollPointer = (card.longFilename[0] == '\0') ? card.filename : card.longFilename;
+			}
+			
+			if (rewindFlag == 1)
+				_md->offset = 0; //redraw once again from the beginning.
+			if (_md->lcd_scrollTimer.expired(300) || rewindFlag)
+			{
+				uint8_t i = LCD_WIDTH - ((_md->isDir)?2:1);
+				lcd_set_cursor(0, _md->row);
+				lcd_print('>');
+				if (_md->isDir)
+					lcd_print(LCD_STR_FOLDER[0]);
+				for (; i != 0; i--)
+				{
+					const char* c = (_md->scrollPointer + _md->offset + ((LCD_WIDTH - ((_md->isDir)?2:1)) - i));
+					lcd_print(c[0]);
+					if (c[1])
+						_md->lcd_scrollTimer.start();
+					else
+					{
+						_md->lcd_scrollTimer.stop();
+						break; //stop at the end of the string
+					}
+				}
+				if (i != 0) //adds spaces if string is incomplete or at the end (instead of null).
+				{
+					lcd_space(i);
+				}
+				_md->offset++;
+			}
+			if (rewindFlag) //go back to sd_menu.
+			{
+				_md->lcd_scrollTimer.stop(); //forces redraw in _standard state
+				_md->menuState = _standard;
+			}
+		} break;
+		default: _md->menuState = _uninitialized; //shouldn't ever happen. Anyways, initialize the menu.
+	}
 }
 #ifdef TMC2130
 static void lcd_belttest_v()
@@ -7501,7 +7253,7 @@ void lcd_belttest()
     
     uint16_t   X = eeprom_read_word((uint16_t*)(EEPROM_BELTSTATUS_X));
     uint16_t   Y = eeprom_read_word((uint16_t*)(EEPROM_BELTSTATUS_Y));
-	lcd_printf_P(_i("Checking X axis  ")); // share message with selftest
+	lcd_printf_P(_T(MSG_CHECKING_X));
 	lcd_set_cursor(0,1), lcd_printf_P(PSTR("X: %u -> ..."),X);
     KEEPALIVE_STATE(IN_HANDLER);
     
@@ -7510,7 +7262,7 @@ void lcd_belttest()
     if (lcd_selfcheck_axis_sg(X_AXIS)){
 		X = eeprom_read_word((uint16_t*)(EEPROM_BELTSTATUS_X));
 		lcd_set_cursor(10,1), lcd_printf_P(PSTR("%u"),X); // Show new X value next to old one.
-        lcd_puts_at_P(0,2,_i("Checking Y axis  "));
+        lcd_puts_at_P(0,2,_T(MSG_CHECKING_Y));
 		lcd_set_cursor(0,3), lcd_printf_P(PSTR("Y: %u -> ..."),Y);
 		if (lcd_selfcheck_axis_sg(Y_AXIS))
 		{
@@ -7546,7 +7298,7 @@ static bool lcd_selftest_IRsensor(bool bStandalone)
             lcd_selftest_error(TestError::FsensorLevel,"HIGH","");
         return(false);
     }
-    lcd_show_fullscreen_message_and_wait_P(_i("Insert the filament (do not load it) into the extruder and then press the knob."));////c=20 r=6
+    lcd_show_fullscreen_message_and_wait_P(_i("Insert the filament (do not load it) into the extruder and then press the knob."));////MSG_INSERT_FIL c=20 r=6
     volt_IR_int = current_voltage_raw_IR;
     printf_P(PSTR("Measured filament sensor low level: %4.2fV\n"), Raw2Voltage(volt_IR_int));
     if(volt_IR_int > (IRsensor_Lmax_TRESHOLD)){
@@ -7570,18 +7322,18 @@ static void lcd_detect_IRsensor(){
     /// @todo Add autodetection with MMU2s
     loaded = ! READ(IR_SENSOR_PIN);
     if(loaded ){
-        lcd_show_fullscreen_message_and_wait_P(_i("Please unload the filament first, then repeat this action."));
+        lcd_show_fullscreen_message_and_wait_P(_i("Please unload the filament first, then repeat this action."));////MSG_UNLOAD_FILAMENT_REPEAT c=20 r=4
         return;
     } else {
-        lcd_show_fullscreen_message_and_wait_P(_i("Please check the IR sensor connections and filament is unloaded."));
+        lcd_show_fullscreen_message_and_wait_P(_i("Please check the IR sensor connection, unload filament if present."));////MSG_CHECK_IR_CONNECTION c=20 r=4
         bAction = lcd_selftest_IRsensor(true);
     }
     if(bAction){
-        lcd_show_fullscreen_message_and_wait_P(_i("Sensor verified, remove the filament now."));////c=20 r=3
+        lcd_show_fullscreen_message_and_wait_P(_i("Sensor verified, remove the filament now."));////MSG_FS_VERIFIED c=20 r=3
         // the fsensor board has been successfully identified, any previous "not responding" may be cleared now
         fsensor_not_responding = false;
     } else {
-        lcd_show_fullscreen_message_and_wait_P(_i("Verification failed, remove the filament and try again."));////c=20 r=5
+        lcd_show_fullscreen_message_and_wait_P(_i("Verification failed, remove the filament and try again."));////MSG_FIL_FAILED c=20 r=5
         // here it is unclear what to to with the fsensor_not_responding flag
     }
     bMenuFSDetect=false;                              // de-inhibits some code inside "manage_inactivity()"
@@ -7613,7 +7365,7 @@ bool lcd_selftest()
 #endif //IR_SENSOR_ANALOG
 	lcd_wait_for_cool_down();
 	lcd_clear();
-	lcd_set_cursor(0, 0); lcd_puts_P(_i("Self test start  "));////MSG_SELFTEST_START c=20
+	lcd_puts_at_P(0, 0, _i("Self test start"));////MSG_SELFTEST_START c=20
 	#ifdef TMC2130
 	  FORCE_HIGH_POWER_START;
 	#endif // TMC2130
@@ -7629,7 +7381,7 @@ bool lcd_selftest()
 			break;
 		case FanCheck::SwappedFan:
 			_swapped_fan = true;
-			// no break
+			// FALLTHRU
 		default:
 			_result = true;
 			break;
@@ -7652,7 +7404,7 @@ bool lcd_selftest()
 			break;
 		case FanCheck::SwappedFan:
 			_swapped_fan = true;
-			// no break
+			// FALLTHRU
 		default:
 			_result = true;
 			break;
@@ -7845,7 +7597,7 @@ bool lcd_selftest()
 	
 	if (_result)
 	{
-		LCD_ALERTMESSAGERPGM(_i("Self test OK"));////MSG_SELFTEST_OK
+		LCD_ALERTMESSAGERPGM(_i("Self test OK"));////MSG_SELFTEST_OK c=20
 	}
 	else
 	{
@@ -7917,7 +7669,7 @@ static bool lcd_selfcheck_axis_sg(unsigned char axis) {
 	eeprom_write_word(((uint16_t*)((axis == X_AXIS)?EEPROM_BELTSTATUS_X:EEPROM_BELTSTATUS_Y)), sg1);
 
 	current_position_final = st_get_position_mm(axis);
-	measured_axis_length[0] = abs(current_position_final - current_position_init);
+	measured_axis_length[0] = fabs(current_position_final - current_position_init);
 
 
 // first measurement end and second measurement begin	
@@ -7934,7 +7686,7 @@ static bool lcd_selfcheck_axis_sg(unsigned char axis) {
 
 	current_position_init = st_get_position_mm(axis);
 
-	measured_axis_length[1] = abs(current_position_final - current_position_init);
+	measured_axis_length[1] = fabs(current_position_final - current_position_init);
 
 	tmc2130_home_exit();
 
@@ -7942,7 +7694,7 @@ static bool lcd_selfcheck_axis_sg(unsigned char axis) {
 
 	for(uint_least8_t i = 0; i < 2; i++){ //check if measured axis length corresponds to expected length
 		printf_P(_N("Measured axis length:%.3f\n"), measured_axis_length[i]);
-		if (abs(measured_axis_length[i] - axis_length) > max_error_mm) {
+		if (fabs(measured_axis_length[i] - axis_length) > max_error_mm) {
 			enable_endstops(false);
 
 			const char *_error_1;
@@ -7961,9 +7713,9 @@ static bool lcd_selfcheck_axis_sg(unsigned char axis) {
 		}
 	}
 
-		printf_P(_N("Axis length difference:%.3f\n"), abs(measured_axis_length[0] - measured_axis_length[1]));
+		printf_P(_N("Axis length difference:%.3f\n"), fabs(measured_axis_length[0] - measured_axis_length[1]));
 	
-		if (abs(measured_axis_length[0] - measured_axis_length[1]) > 1) { //check if difference between first and second measurement is low
+		if (fabs(measured_axis_length[0] - measured_axis_length[1]) > 1) { //check if difference between first and second measurement is low
 			//loose pulleys
 			const char *_error_1;
 
@@ -8287,108 +8039,81 @@ static void lcd_selftest_error(TestError testError, const char *_error_1, const
 
 	lcd_clear();
 
-	lcd_set_cursor(0, 0);
-	lcd_puts_P(_i("Selftest error !"));////MSG_SELFTEST_ERROR
-	lcd_set_cursor(0, 1);
-	lcd_puts_P(_i("Please check :"));////MSG_SELFTEST_PLEASECHECK
+	lcd_puts_at_P(0, 0, _i("Selftest error!"));////MSG_SELFTEST_ERROR c=20
+	lcd_puts_at_P(0, 1, _i("Please check:"));////MSG_SELFTEST_PLEASECHECK c=20
 
 	switch (testError)
 	{
 	case TestError::Heater:
-		lcd_set_cursor(0, 2);
-		lcd_puts_P(_i("Heater/Thermistor"));////MSG_SELFTEST_HEATERTHERMISTOR
-		lcd_set_cursor(0, 3);
-		lcd_puts_P(_i("Not connected"));////MSG_SELFTEST_NOTCONNECTED
+		lcd_puts_at_P(0, 2, _i("Heater/Thermistor"));////MSG_SELFTEST_HEATERTHERMISTOR c=20
+		lcd_puts_at_P(0, 3, _i("Not connected"));////MSG_SELFTEST_NOTCONNECTED c=20
 		break;
 	case TestError::Bed:
-		lcd_set_cursor(0, 2);
-		lcd_puts_P(_i("Bed / Heater"));////MSG_SELFTEST_BEDHEATER
-		lcd_set_cursor(0, 3);
-		lcd_puts_P(_T(MSG_SELFTEST_WIRINGERROR));
+		lcd_puts_at_P(0, 2, _i("Bed/Heater"));////MSG_SELFTEST_BEDHEATER c=20
+		lcd_puts_at_P(0, 3, _T(MSG_SELFTEST_WIRINGERROR));
 		break;
 	case TestError::Endstops:
-		lcd_set_cursor(0, 2);
-		lcd_puts_P(_i("Endstops"));////MSG_SELFTEST_ENDSTOPS
-		lcd_set_cursor(0, 3);
-		lcd_puts_P(_T(MSG_SELFTEST_WIRINGERROR));
-		lcd_set_cursor(17, 3);
+		lcd_puts_at_P(0, 2, _i("Endstops"));////MSG_SELFTEST_ENDSTOPS c=20
+		lcd_puts_at_P(0, 3, _T(MSG_SELFTEST_WIRINGERROR));
+		lcd_set_cursor(18, 3);
 		lcd_print(_error_1);
 		break;
 	case TestError::Motor:
-		lcd_set_cursor(0, 2);
-		lcd_puts_P(_T(MSG_SELFTEST_MOTOR));
+		lcd_puts_at_P(0, 2, _T(MSG_SELFTEST_MOTOR));
 		lcd_set_cursor(18, 2);
 		lcd_print(_error_1);
-		lcd_set_cursor(0, 3);
-		lcd_puts_P(_i("Endstop"));////MSG_SELFTEST_ENDSTOP
+		lcd_puts_at_P(0, 3, _i("Endstop"));////MSG_SELFTEST_ENDSTOP c=16
 		lcd_set_cursor(18, 3);
 		lcd_print(_error_2);
 		break;
 	case TestError::Endstop:
-		lcd_set_cursor(0, 2);
-		lcd_puts_P(_i("Endstop not hit"));////MSG_SELFTEST_ENDSTOP_NOTHIT c=20
-		lcd_set_cursor(0, 3);
-		lcd_puts_P(_T(MSG_SELFTEST_MOTOR));
+		lcd_puts_at_P(0, 2, _i("Endstop not hit"));////MSG_SELFTEST_ENDSTOP_NOTHIT c=20
+		lcd_puts_at_P(0, 3, _T(MSG_SELFTEST_MOTOR));
 		lcd_set_cursor(18, 3);
 		lcd_print(_error_1);
 		break;
 	case TestError::PrintFan:
-		lcd_set_cursor(0, 2);
-		lcd_puts_P(_T(MSG_SELFTEST_COOLING_FAN));
-		lcd_set_cursor(0, 3);
-		lcd_puts_P(_T(MSG_SELFTEST_WIRINGERROR));
+		lcd_puts_at_P(0, 2, _T(MSG_SELFTEST_COOLING_FAN));
+		lcd_puts_at_P(0, 3, _T(MSG_SELFTEST_WIRINGERROR));
 		lcd_set_cursor(18, 3);
 		lcd_print(_error_1);
 		break;
 	case TestError::ExtruderFan:
-		lcd_set_cursor(0, 2);
-		lcd_puts_P(_T(MSG_SELFTEST_EXTRUDER_FAN));
-		lcd_set_cursor(0, 3);
-		lcd_puts_P(_T(MSG_SELFTEST_WIRINGERROR));
+		lcd_puts_at_P(0, 2, _T(MSG_SELFTEST_EXTRUDER_FAN));
+		lcd_puts_at_P(0, 3, _T(MSG_SELFTEST_WIRINGERROR));
 		lcd_set_cursor(18, 3);
 		lcd_print(_error_1);
 		break;
 	case TestError::Pulley:
-		lcd_set_cursor(0, 2);
-		lcd_puts_P(_i("Loose pulley"));////MSG_LOOSE_PULLEY c=20 r=1
-		lcd_set_cursor(0, 3);
-		lcd_puts_P(_T(MSG_SELFTEST_MOTOR));
+		lcd_puts_at_P(0, 2, _i("Loose pulley"));////MSG_LOOSE_PULLEY c=20
+		lcd_puts_at_P(0, 3, _T(MSG_SELFTEST_MOTOR));
 		lcd_set_cursor(18, 3);
 		lcd_print(_error_1);
 		break;
 	case TestError::Axis:
-		lcd_set_cursor(0, 2);
-		lcd_puts_P(_i("Axis length"));////MSG_SELFTEST_AXIS_LENGTH
-		lcd_set_cursor(0, 3);
-		lcd_puts_P(_i("Axis"));////MSG_SELFTEST_AXIS
+		lcd_puts_at_P(0, 2, _i("Axis length"));////MSG_SELFTEST_AXIS_LENGTH c=20
+		lcd_puts_at_P(0, 3, _i("Axis"));////MSG_SELFTEST_AXIS c=16
 		lcd_set_cursor(18, 3);
 		lcd_print(_error_1);
 		break;
 	case TestError::SwappedFan:
-		lcd_set_cursor(0, 2);
-		lcd_puts_P(_i("Front/left fans"));////MSG_SELFTEST_FANS
-		lcd_set_cursor(0, 3);
-		lcd_puts_P(_i("Swapped"));////MSG_SELFTEST_SWAPPED
+		lcd_puts_at_P(0, 2, _i("Front/left fans"));////MSG_SELFTEST_FANS c=20
+		lcd_puts_at_P(0, 3, _i("Swapped"));////MSG_SELFTEST_SWAPPED c=16
 		lcd_set_cursor(18, 3);
 		lcd_print(_error_1);
 		break;
 	case TestError::WiringFsensor:
-		lcd_set_cursor(0, 2);
-		lcd_puts_P(_T(MSG_SELFTEST_FILAMENT_SENSOR));
-		lcd_set_cursor(0, 3);
-		lcd_puts_P(_T(MSG_SELFTEST_WIRINGERROR));
+		lcd_puts_at_P(0, 2, _T(MSG_SELFTEST_FILAMENT_SENSOR));
+		lcd_puts_at_P(0, 3, _T(MSG_SELFTEST_WIRINGERROR));
 		break;
 	case TestError::TriggeringFsensor:
-          lcd_set_cursor(0, 2);
-          lcd_puts_P(_T(MSG_SELFTEST_FILAMENT_SENSOR));
-          lcd_set_cursor(0, 3);
-          lcd_puts_P(_i("False triggering"));////c=20
+          lcd_puts_at_P(0, 2, _T(MSG_SELFTEST_FILAMENT_SENSOR));
+          lcd_puts_at_P(0, 3, _i("False triggering"));////MSG_FALSE_TRIGGERING c=20
           break;
 	case TestError::FsensorLevel:
-          lcd_set_cursor(0, 2);
-          lcd_puts_P(_T(MSG_SELFTEST_FILAMENT_SENSOR));
+          lcd_puts_at_P(0, 2, _T(MSG_SELFTEST_FILAMENT_SENSOR));
           lcd_set_cursor(0, 3);
-          lcd_printf_P(_i("%s level expected"),_error_1);////c=20
+          lcd_printf_P(_i("%s level expected"),_error_1);////MSG_SELFTEST_FS_LEVEL c=20
           break;
 	}
 
@@ -8465,7 +8190,7 @@ static bool selftest_irsensor()
         mmu_load_step(false);
         while (blocks_queued())
         {
-            if (PIN_GET(IR_SENSOR_PIN) == 0)
+            if (READ(IR_SENSOR_PIN) == 0)
             {
                 lcd_selftest_error(TestError::TriggeringFsensor, "", "");
                 return false;
@@ -8495,22 +8220,18 @@ static bool lcd_selftest_manual_fan_check(int _fan, bool check_opposite,
 	bool _result = check_opposite;
 	lcd_clear();
 
-	lcd_set_cursor(0, 0); lcd_puts_P(_T(MSG_SELFTEST_FAN));
+	lcd_puts_at_P(0, 0, _T(MSG_SELFTEST_FAN));
 
 	switch (_fan)
 	{
 	case 0:
 		// extruder cooling fan
-		lcd_set_cursor(0, 1);
-		if(check_opposite == true) lcd_puts_P(_T(MSG_SELFTEST_COOLING_FAN)); 
-		else lcd_puts_P(_T(MSG_SELFTEST_EXTRUDER_FAN));
+		lcd_puts_at_P(0, 1, check_opposite ? _T(MSG_SELFTEST_COOLING_FAN) : _T(MSG_SELFTEST_EXTRUDER_FAN));
 		setExtruderAutoFanState(3);
 		break;
 	case 1:
 		// object cooling fan
-		lcd_set_cursor(0, 1);
-		if (check_opposite == true) lcd_puts_P(_T(MSG_SELFTEST_EXTRUDER_FAN));
-		else lcd_puts_P(_T(MSG_SELFTEST_COOLING_FAN));
+		lcd_puts_at_P(0, 1, check_opposite ? _T(MSG_SELFTEST_EXTRUDER_FAN) : _T(MSG_SELFTEST_COOLING_FAN));
 		SET_OUTPUT(FAN_PIN);
 #ifdef FAN_SOFT_PWM
 		fanSpeedSoftPwm = 255;
@@ -8522,9 +8243,9 @@ static bool lcd_selftest_manual_fan_check(int _fan, bool check_opposite,
 	}
 	_delay(500);
 
-	lcd_set_cursor(1, 2); lcd_puts_P(_T(MSG_SELFTEST_FAN_YES));
-	lcd_set_cursor(0, 3); lcd_print(">");
-	lcd_set_cursor(1, 3); lcd_puts_P(_T(MSG_SELFTEST_FAN_NO));
+	lcd_puts_at_P(1, 2, _T(MSG_SELFTEST_FAN_YES));
+	lcd_putc_at(0, 3, '>');
+	lcd_puts_at_P(1, 3, _T(MSG_SELFTEST_FAN_NO));
 
 	int8_t enc_dif = int(_default)*3;
 
@@ -8536,18 +8257,18 @@ static bool lcd_selftest_manual_fan_check(int _fan, bool check_opposite,
 		if (abs((enc_dif - lcd_encoder_diff)) > 2) {
 			if (enc_dif > lcd_encoder_diff) {
 				_result = !check_opposite;
-				lcd_set_cursor(0, 2); lcd_print(">");
-				lcd_set_cursor(1, 2); lcd_puts_P(_T(MSG_SELFTEST_FAN_YES));
-				lcd_set_cursor(0, 3); lcd_print(" ");
-				lcd_set_cursor(1, 3); lcd_puts_P(_T(MSG_SELFTEST_FAN_NO));
+				lcd_putc_at(0, 2, '>');
+				lcd_puts_at_P(1, 2, _T(MSG_SELFTEST_FAN_YES));
+				lcd_putc_at(0, 3, ' ');
+				lcd_puts_at_P(1, 3, _T(MSG_SELFTEST_FAN_NO));
 			}
 
 			if (enc_dif < lcd_encoder_diff) {
 				_result = check_opposite;
-				lcd_set_cursor(0, 2); lcd_print(" ");
-				lcd_set_cursor(1, 2); lcd_puts_P(_T(MSG_SELFTEST_FAN_YES));
-				lcd_set_cursor(0, 3); lcd_print(">");
-				lcd_set_cursor(1, 3); lcd_puts_P(_T(MSG_SELFTEST_FAN_NO));
+				lcd_putc_at(0, 2, ' ');
+				lcd_puts_at_P(1, 2, _T(MSG_SELFTEST_FAN_YES));
+				lcd_putc_at(0, 3, '>');
+				lcd_puts_at_P(1, 3, _T(MSG_SELFTEST_FAN_NO));
 			}
 			enc_dif = 0;
 			lcd_encoder_diff = 0;
@@ -8588,9 +8309,9 @@ static FanCheck lcd_selftest_fan_auto(int _fan)
 		setExtruderAutoFanState(0); //extruder fan
 		manage_heater();			//count average fan speed from 2s delay and turn off fans
 
-		printf_P(PSTR("Test 1:\n"));
-		printf_P(PSTR("Print fan speed: %d \n"), fan_speed[1]);
-		printf_P(PSTR("Extr fan speed: %d \n"), fan_speed[0]);
+		puts_P(PSTR("Test 1:"));
+		printf_P(PSTR("Print fan speed: %d\n"), fan_speed[1]);
+		printf_P(PSTR("Extr fan speed: %d\n"), fan_speed[0]);
 
 		if (fan_speed[0] < 20) { // < 1200 RPM would mean either a faulty Noctua or Altfan
 			return FanCheck::ExtruderFan;
@@ -8614,11 +8335,9 @@ static FanCheck lcd_selftest_fan_auto(int _fan)
 #endif //FAN_SOFT_PWM
 		for (uint8_t i = 0; i < 5; i++) {
 			delay_keep_alive(1000);
-			lcd_set_cursor(18, 3);
-			lcd_print("-");
+			lcd_putc_at(18, 3, '-');
 			delay_keep_alive(1000);
-			lcd_set_cursor(18, 3);
-			lcd_print("|");
+			lcd_putc_at(18, 3, '|');
 		}
 		fanSpeed = 0;
 
@@ -8628,9 +8347,9 @@ static FanCheck lcd_selftest_fan_auto(int _fan)
 		manage_heater();			//turn off fan
 		manage_inactivity(true);	//to turn off print fan
 #endif //FAN_SOFT_PWM
-		printf_P(PSTR("Test 2:\n"));
-		printf_P(PSTR("Print fan speed: %d \n"), fan_speed[1]);
-		printf_P(PSTR("Extr fan speed: %d \n"), fan_speed[0]);
+		puts_P(PSTR("Test 2:"));
+		printf_P(PSTR("Print fan speed: %d\n"), fan_speed[1]);
+		printf_P(PSTR("Extr fan speed: %d\n"), fan_speed[0]);
 		if (!fan_speed[1]) {
 			return FanCheck::PrintFan;
 		}
@@ -8641,11 +8360,9 @@ static FanCheck lcd_selftest_fan_auto(int _fan)
 
 		for (uint8_t i = 0; i < 5; i++) {
 			delay_keep_alive(1000);
-			lcd_set_cursor(18, 3);
-			lcd_print("-");
+			lcd_putc_at(18, 3, '-');
 			delay_keep_alive(1000);
-			lcd_set_cursor(18, 3);
-			lcd_print("|");
+			lcd_putc_at(18, 3, '|');
 		}
 		fanSpeed = 0;
 
@@ -8683,20 +8400,19 @@ static int lcd_selftest_screen(TestScreen screen, int _progress, int _progress_s
 	if (screen == TestScreen::PrintFan) lcd_puts_P(_T(MSG_SELFTEST_FAN));
 	if (screen == TestScreen::FansOk) lcd_puts_P(_T(MSG_SELFTEST_FAN));
 	if (screen == TestScreen::EndStops) lcd_puts_P(_i("Checking endstops"));////MSG_SELFTEST_CHECK_ENDSTOPS c=20
-	if (screen == TestScreen::AxisX) lcd_puts_P(_i("Checking X axis  "));////MSG_SELFTEST_CHECK_X c=20
-	if (screen == TestScreen::AxisY) lcd_puts_P(_i("Checking Y axis  "));////MSG_SELFTEST_CHECK_Y c=20
-	if (screen == TestScreen::AxisZ) lcd_puts_P(_i("Checking Z axis  "));////MSG_SELFTEST_CHECK_Z c=20
+	if (screen == TestScreen::AxisX) lcd_puts_P(_T(MSG_CHECKING_X));
+	if (screen == TestScreen::AxisY) lcd_puts_P(_T(MSG_CHECKING_Y));
+	if (screen == TestScreen::AxisZ) lcd_puts_P(_i("Checking Z axis"));////MSG_SELFTEST_CHECK_Z c=20
 	if (screen == TestScreen::Bed) lcd_puts_P(_T(MSG_SELFTEST_CHECK_BED));
 	if (screen == TestScreen::Hotend
-	    || screen == TestScreen::HotendOk) lcd_puts_P(_i("Checking hotend  "));////MSG_SELFTEST_CHECK_HOTEND c=20
+	    || screen == TestScreen::HotendOk) lcd_puts_P(_i("Checking hotend"));////MSG_SELFTEST_CHECK_HOTEND c=20
 	if (screen == TestScreen::Fsensor) lcd_puts_P(_T(MSG_SELFTEST_CHECK_FSENSOR));
 	if (screen == TestScreen::FsensorOk) lcd_puts_P(_T(MSG_SELFTEST_CHECK_FSENSOR));
-	if (screen == TestScreen::AllCorrect) lcd_puts_P(_i("All correct      "));////MSG_SELFTEST_CHECK_ALLCORRECT c=20
+	if (screen == TestScreen::AllCorrect) lcd_puts_P(_i("All correct"));////MSG_SELFTEST_CHECK_ALLCORRECT c=20
 	if (screen == TestScreen::Failed) lcd_puts_P(_T(MSG_SELFTEST_FAILED));
-	if (screen == TestScreen::Home) lcd_puts_P(_i("Calibrating home"));////c=20 r=1
+	if (screen == TestScreen::Home) lcd_puts_P(_i("Calibrating home"));////MSG_CALIBRATING_HOME c=20
 
-	lcd_set_cursor(0, 1);
-	lcd_puts_P(separator);
+	lcd_puts_at_P(0, 1, separator);
 	if ((screen >= TestScreen::ExtruderFan) && (screen <= TestScreen::FansOk))
 	{
 		//SERIAL_ECHOLNPGM("Fan test");
@@ -8719,19 +8435,19 @@ static int lcd_selftest_screen(TestScreen screen, int _progress, int _progress_s
 		//SERIAL_ECHOLNPGM("Other tests");
 
 		TestScreen _step_block = TestScreen::AxisX;
-		lcd_selftest_screen_step(2, 2, ((screen == _step_block) ? 1 : (screen < _step_block) ? 0 : 2), "X", _indicator);
+		lcd_selftest_screen_step(2, 2, ((screen == _step_block) ? 1 : (screen < _step_block) ? 0 : 2), PSTR("X"), _indicator);
 
 		_step_block = TestScreen::AxisY;
-		lcd_selftest_screen_step(2, 8, ((screen == _step_block) ? 1 : (screen < _step_block) ? 0 : 2), "Y", _indicator);
+		lcd_selftest_screen_step(2, 8, ((screen == _step_block) ? 1 : (screen < _step_block) ? 0 : 2), PSTR("Y"), _indicator);
 
 		_step_block = TestScreen::AxisZ;
-		lcd_selftest_screen_step(2, 14, ((screen == _step_block) ? 1 : (screen < _step_block) ? 0 : 2), "Z", _indicator);
+		lcd_selftest_screen_step(2, 14, ((screen == _step_block) ? 1 : (screen < _step_block) ? 0 : 2), PSTR("Z"), _indicator);
 
 		_step_block = TestScreen::Bed;
-		lcd_selftest_screen_step(3, 0, ((screen == _step_block) ? 1 : (screen < _step_block) ? 0 : 2), "Bed", _indicator);
+		lcd_selftest_screen_step(3, 0, ((screen == _step_block) ? 1 : (screen < _step_block) ? 0 : 2), PSTR("Bed"), _indicator);
 
 		_step_block = TestScreen::Hotend;
-		lcd_selftest_screen_step(3, 9, ((screen == _step_block) ? 1 : (screen < _step_block) ? 0 : 2), "Hotend", _indicator);
+		lcd_selftest_screen_step(3, 9, ((screen == _step_block) ? 1 : (screen < _step_block) ? 0 : 2), PSTR("Hotend"), _indicator);
 	}
 
 	if (_delay > 0) delay_keep_alive(_delay);
@@ -8740,28 +8456,26 @@ static int lcd_selftest_screen(TestScreen screen, int _progress, int _progress_s
 	return (_progress >= _progress_scale * 2) ? 0 : _progress;
 }
 
-static void lcd_selftest_screen_step(int _row, int _col, int _state, const char *_name, const char *_indicator)
+static void lcd_selftest_screen_step(int _row, int _col, int _state, const char *_name_PROGMEM, const char *_indicator)
 {
 	lcd_set_cursor(_col, _row);
+    uint8_t strlenNameP = strlen_P(_name_PROGMEM);
 
 	switch (_state)
 	{
 	case 1:
-		lcd_print(_name);
-		lcd_set_cursor(_col + strlen(_name), _row);
-		lcd_print(":");
-		lcd_set_cursor(_col + strlen(_name) + 1, _row);
+		lcd_puts_P(_name_PROGMEM);
+		lcd_putc_at(_col + strlenNameP, _row, ':');
+		lcd_set_cursor(_col + strlenNameP + 1, _row);
 		lcd_print(_indicator);
 		break;
 	case 2:
-		lcd_print(_name);
-		lcd_set_cursor(_col + strlen(_name), _row);
-		lcd_print(":");
-		lcd_set_cursor(_col + strlen(_name) + 1, _row);
-		lcd_print("OK");
+		lcd_puts_P(_name_PROGMEM);
+		lcd_putc_at(_col + strlenNameP, _row, ':');
+		lcd_puts_at_P(_col + strlenNameP + 1, _row, PSTR("OK"));
 		break;
 	default:
-		lcd_print(_name);
+		lcd_puts_P(_name_PROGMEM);
 	}
 }
 
@@ -8772,26 +8486,34 @@ static void lcd_selftest_screen_step(int _row, int _col, int _state, const char
 
 static bool check_file(const char* filename) {
 	if (farm_mode) return true;
+	card.openFileReadFilteredGcode(filename, true);
 	bool result = false;
-	uint32_t filesize;
-	card.openFile((char*)filename, true);
-	filesize = card.getFileSize();
+	const uint32_t filesize = card.getFileSize();
+	uint32_t startPos = 0;
+	const uint16_t bytesToCheck = min(END_FILE_SECTION, filesize);
 	if (filesize > END_FILE_SECTION) {
-		card.setIndex(filesize - END_FILE_SECTION);
-		
+		startPos = filesize - END_FILE_SECTION;
+		card.setIndex(startPos);
 	}
-	
-		while (!card.eof() && !result) {
+	cmdqueue_reset();
+	cmdqueue_serial_disabled = true;
+
+	menu_progressbar_init(bytesToCheck, _i("Checking file"));////MSG_CHECKING_FILE c=17
+	while (!card.eof() && !result) {
+		menu_progressbar_update(card.get_sdpos() - startPos);
 		card.sdprinting = true;
 		get_command();
 		result = check_commands();
-		
 	}
+	
+	menu_progressbar_finish();
+	
+	cmdqueue_serial_disabled = false;
 	card.printingHasFinished();
-	strncpy_P(lcd_status_message, _T(WELCOME_MSG), LCD_WIDTH);
+
+	lcd_setstatuspgm(_T(WELCOME_MSG));
 	lcd_finishstatus();
 	return result;
-	
 }
 
 static void menu_action_sdfile(const char* filename)
@@ -8823,11 +8545,13 @@ static void menu_action_sdfile(const char* filename)
 
   for (uint_least8_t i = 0; i < depth; i++) {
 	  for (uint_least8_t j = 0; j < 8; j++) {
-		  eeprom_write_byte((uint8_t*)EEPROM_DIRS + j + 8 * i, dir_names[i][j]);
+		  eeprom_write_byte((uint8_t*)EEPROM_DIRS + j + 8 * i, card.dir_names[i][j]);
 	  }
   }
   
-  if (!check_file(filename)) {
+  //filename is just a pointer to card.filename, which changes everytime you try to open a file by filename. So you can't use filename directly
+  //to open a file. Instead, the cached filename in cmd is used as that one is static for the whole lifetime of this function.
+  if (!check_file(cmd + 4)) {
 	  result = lcd_show_fullscreen_message_yes_no_and_wait_P(_i("File incomplete. Continue anyway?"), false, false);////MSG_FILE_INCOMPLETE c=20 r=3
 	  lcd_update_enable(true);
   }
@@ -8841,12 +8565,9 @@ static void menu_action_sdfile(const char* filename)
 
 void menu_action_sddirectory(const char* filename)
 {
-	uint8_t depth = (uint8_t)card.getWorkDirDepth();
-
-	strcpy(dir_names[depth], filename);
-	MYSERIAL.println(dir_names[depth]);
-  card.chdir(filename);
-  lcd_encoder = 0;
+	card.chdir(filename, true);
+	lcd_encoder = 0;
+	menu_data_reset(); //Forces reloading of cached variables.
 }
 
 /** LCD API **/
@@ -8911,7 +8632,6 @@ static void lcd_connect_printer() {
 	
 	int i = 0;
 	int t = 0;
-	lcd_set_custom_characters_progress();
 	lcd_puts_at_P(0, 0, _i("Connect printer to")); 
 	lcd_puts_at_P(0, 1, _i("monitoring or hold"));
 	lcd_puts_at_P(0, 2, _i("the knob to continue"));
@@ -8928,12 +8648,11 @@ static void lcd_connect_printer() {
 			i = 0; 
 			lcd_puts_at_P(0, 3, PSTR("                    "));
 		}
-		if (i!=0) lcd_puts_at_P((i * 20) / (NC_BUTTON_LONG_PRESS * 10), 3, "\x01");
+		if (i!=0) lcd_puts_at_P((i * 20) / (NC_BUTTON_LONG_PRESS * 10), 3, "\xFF");
 		if (i == NC_BUTTON_LONG_PRESS * 10) {
 			no_response = false;
 		}
 	}
-	lcd_set_custom_characters_degree();
 	lcd_update_enable(true);
 	lcd_update(2);
 }
@@ -8941,7 +8660,7 @@ static void lcd_connect_printer() {
 
 void lcd_ping() { //chceck if printer is connected to monitoring when in farm mode
 	if (farm_mode) {
-		bool empty = is_buffer_empty();
+		bool empty = cmd_buffer_empty();
 		if ((_millis() - PingTime) * 0.001 > (empty ? PING_TIME : PING_TIME_LONG)) { //if commands buffer is empty use shorter time period
 																							  //if there are comamnds in buffer, some long gcodes can delay execution of ping command
 																							  //therefore longer period is used
@@ -8959,6 +8678,7 @@ void lcd_ignore_click(bool b)
 }
 
 void lcd_finishstatus() {
+  SERIAL_PROTOCOLLNRPGM(MSG_LCD_STATUS_CHANGED);
   int len = strlen(lcd_status_message);
   if (len > 0) {
     while (len < LCD_WIDTH) {
@@ -9000,18 +8720,22 @@ void lcd_updatestatus(const char *message){
 	lcd_draw_update = 1;
 }
 
-void lcd_setalertstatuspgm(const char* message)
+void lcd_setalertstatuspgm(const char* message, uint8_t severity)
 {
-  lcd_setstatuspgm(message);
-  lcd_status_message_level = 1;
-  lcd_return_to_status();
+  if (severity > lcd_status_message_level) {
+      lcd_updatestatuspgm(message);
+      lcd_status_message_level = severity;
+      lcd_return_to_status();
+  }
 }
 
-void lcd_setalertstatus(const char* message)
+void lcd_setalertstatus(const char* message, uint8_t severity)
 {
-  lcd_setstatus(message);
-  lcd_status_message_level = 1;
-  lcd_return_to_status();
+  if (severity > lcd_status_message_level) {
+      lcd_updatestatus(message);
+      lcd_status_message_level = severity;
+      lcd_return_to_status();
+  }
 }
 
 void lcd_reset_alert_level()
@@ -9115,25 +8839,29 @@ void menu_lcd_lcdupdate_func(void)
 		lcd_draw_update = 2;
 		lcd_oldcardstatus = IS_SD_INSERTED;
 		lcd_refresh(); // to maybe revive the LCD if static electricity killed it.
-        backlight_wake();
+		backlight_wake();
 		if (lcd_oldcardstatus)
 		{
-			card.initsd();
-               LCD_MESSAGERPGM(_T(WELCOME_MSG));
-               bMain=false;                       // flag (i.e. 'fake parameter') for 'lcd_sdcard_menu()' function
-               menu_submenu(lcd_sdcard_menu);
-			//get_description();
+			if (!card.cardOK)
+			{
+				card.initsd(false); //delay the sorting to the sd menu. Otherwise, removing the SD card while sorting will not menu_back()
+				card.presort_flag = true; //force sorting of the SD menu
+			}
+			LCD_MESSAGERPGM(_T(WELCOME_MSG));
+			bMain=false;                       // flag (i.e. 'fake parameter') for 'lcd_sdcard_menu()' function
+			menu_submenu(lcd_sdcard_menu);
+			lcd_timeoutToStatus.start();
 		}
 		else
 		{
-               if(menu_menu==lcd_sdcard_menu)
-                    menu_back();
+			if(menu_menu==lcd_sdcard_menu)
+				menu_back();
 			card.release();
-			LCD_MESSAGERPGM(_i("Card removed"));////MSG_SD_REMOVED
+			LCD_MESSAGERPGM(_i("Card removed"));////MSG_SD_REMOVED c=20
 		}
 	}
 #endif//CARDINSERTED
-    backlight_update();
+	backlight_update();
 	if (lcd_next_update_millis < _millis())
 	{
 		if (abs(lcd_encoder_diff) >= ENCODER_PULSES_PER_STEP)
@@ -9174,7 +8902,6 @@ void menu_lcd_lcdupdate_func(void)
 		if (lcd_draw_update) lcd_draw_update--;
 		lcd_next_update_millis = _millis() + LCD_UPDATE_INTERVAL;
 	}
-	if (!SdFatUtil::test_stack_integrity()) stack_error();
 	lcd_ping(); //check that we have received ping command if we are in farm mode
 	lcd_send_status();
 	if (lcd_commands_type == LcdCommands::Layer1Cal) lcd_commands();
@@ -9214,6 +8941,37 @@ void lcd_experimental_toggle()
     eeprom_update_byte((uint8_t *)EEPROM_EXPERIMENTAL_VISIBILITY, oldVal);
 }
 
+#ifdef TMC2130
+void UserECool_toggle(){
+    // this is only called when the experimental menu is visible, thus the first condition for enabling of the ECool mode is met in this place
+    // The condition is intentionally inverted as we are toggling the state (i.e. if it was enabled, we are disabling the feature and vice versa)
+    bool enable = ! UserECoolEnabled();
+
+    eeprom_update_byte((uint8_t *)EEPROM_ECOOL_ENABLE, enable ? EEPROM_ECOOL_MAGIC_NUMBER : EEPROM_EMPTY_VALUE);
+
+    // @@TODO I don't like this - disabling the experimental menu shall disable ECool mode, but it will not reinit the TMC
+    // and I don't want to add more code for this experimental feature ... ideally do not reinit the TMC here at all and let the user reset the printer.
+    tmc2130_init(TMCInitParams(enable));
+}
+#endif
+
+/// Enable experimental support for cooler operation of the extruder motor
+/// Beware - REQUIRES original Prusa MK3/S/+ extruder motor with adequate maximal current
+/// Therefore we don't want to allow general usage of this feature in public as the community likes to
+/// change motors for various reasons and unless the motor is rotating, we cannot verify its properties
+/// (which would be obviously too late for an improperly sized motor)
+/// For farm printing, the cooler E-motor is enabled by default.
+bool UserECoolEnabled(){
+    // We enable E-cool mode for non-farm prints IFF the experimental menu is visible AND the EEPROM_ECOOL variable has
+    // a value of the universal answer to all problems of the universe
+    return ( eeprom_read_byte((uint8_t *)EEPROM_ECOOL_ENABLE) == EEPROM_ECOOL_MAGIC_NUMBER ) 
+        && ( eeprom_read_byte((uint8_t *)EEPROM_EXPERIMENTAL_VISIBILITY) == 1 );
+}
+
+bool FarmOrUserECool(){
+    return farm_mode || UserECoolEnabled();
+}
+
 void lcd_experimental_menu()
 {
     MENU_BEGIN();
@@ -9222,6 +8980,23 @@ void lcd_experimental_menu()
 #ifdef EXTRUDER_ALTFAN_DETECT
     MENU_ITEM_TOGGLE_P(_N("ALTFAN det."), altfanOverride_get()?_T(MSG_OFF):_T(MSG_ON), altfanOverride_toggle);////MSG_MENU_ALTFAN c=18
 #endif //EXTRUDER_ALTFAN_DETECT
-
+    
+#ifdef TMC2130
+    MENU_ITEM_TOGGLE_P(_N("E-cool mode"), UserECoolEnabled()?_T(MSG_ON):_T(MSG_OFF), UserECool_toggle);////MSG_MENU_ECOOL c=18
+#endif
     MENU_END();
 }
+
+#ifdef PINDA_TEMP_COMP
+void lcd_pinda_temp_compensation_toggle()
+{
+	uint8_t pinda_temp_compensation = eeprom_read_byte((uint8_t*)EEPROM_PINDA_TEMP_COMPENSATION);
+	if (pinda_temp_compensation == EEPROM_EMPTY_VALUE) // On MK2.5/S the EEPROM_EMPTY_VALUE will be set to 0 during eeprom_init.
+		pinda_temp_compensation = 1;                   // But for MK3/S it should be 1 so SuperPINDA is "active"
+	else
+		pinda_temp_compensation = !pinda_temp_compensation;
+	eeprom_update_byte((uint8_t*)EEPROM_PINDA_TEMP_COMPENSATION, pinda_temp_compensation);
+	SERIAL_ECHOLNPGM("SuperPINDA:");
+	SERIAL_ECHOLN(pinda_temp_compensation);
+}
+#endif //PINDA_TEMP_COMP

+ 31 - 29
Firmware/ultralcd.h

@@ -1,15 +1,9 @@
 #ifndef ULTRALCD_H
 #define ULTRALCD_H
 
-#include "Marlin.h"
-#include "lcd.h"
-#include "conv2str.h"
-#include "menu.h"
 #include "mesh_bed_calibration.h"
 #include "config.h"
 
-#include "config.h"
-
 extern void menu_lcd_longpress_func(void);
 extern void menu_lcd_charsetup_func(void);
 extern void menu_lcd_lcdupdate_func(void);
@@ -18,13 +12,20 @@ extern void menu_lcd_lcdupdate_func(void);
 void ultralcd_init();
 void lcd_setstatus(const char* message);
 void lcd_setstatuspgm(const char* message);
+
+//! LCD status severities
+#define LCD_STATUS_CRITICAL 2 //< Heater failure
+#define LCD_STATUS_ALERT    1 //< Other hardware issue
+#define LCD_STATUS_NONE     0 //< No alert message set
+
 //! return to the main status screen and display the alert message
 //! Beware - it has sideeffects:
 //! - always returns the display to the main status screen
 //! - always makes lcd_reset (which is slow and causes flicker)
-//! - does not update the message if there is already one (i.e. lcd_status_message_level > 0)
-void lcd_setalertstatus(const char* message);
-void lcd_setalertstatuspgm(const char* message);
+//! - does not update the message if there is one with the same (or higher) severity present
+void lcd_setalertstatus(const char* message, uint8_t severity = LCD_STATUS_ALERT);
+void lcd_setalertstatuspgm(const char* message, uint8_t severity = LCD_STATUS_ALERT);
+
 //! only update the alert message on the main status screen
 //! has no sideeffects, may be called multiple times
 void lcd_updatestatus(const char *message);
@@ -44,11 +45,10 @@ void lcd_change_success();
 void lcd_loading_color();
 void lcd_sdcard_stop();
 void lcd_pause_print();
+void lcd_pause_usb_print();
 void lcd_resume_print();
 void lcd_print_stop();
 void prusa_statistics(int _message, uint8_t _col_nr = 0);
-void lcd_confirm_print();
-unsigned char lcd_choose_color();
 void lcd_load_filament_color_check();
 //void lcd_mylang();
 
@@ -115,22 +115,27 @@ extern int8_t FSensorStateMenu;
 
 enum class CustomMsg : uint_least8_t
 {
-	Status,          //!< status message from lcd_status_message variable
-	MeshBedLeveling, //!< Mesh bed leveling in progress
-	FilamentLoading, //!< Loading filament in progress
-	PidCal,          //!< PID tuning in progress
-	TempCal,         //!< PINDA temperature calibration
-	TempCompPreheat, //!< Temperature compensation preheat
+    Status,          //!< status message from lcd_status_message variable
+    MeshBedLeveling, //!< Mesh bed leveling in progress
+    FilamentLoading, //!< Loading filament in progress
+    PidCal,          //!< PID tuning in progress
+    TempCal,         //!< PINDA temperature calibration
+    TempCompPreheat, //!< Temperature compensation preheat
+    M0Wait,          //!< M0/M1 Wait command working even from SD
+    MsgUpdate,       //!< Short message even while printing from SD
+    Resuming,       //!< Resuming message
 };
 
 extern CustomMsg custom_message_type;
 extern unsigned int custom_message_state;
 
 extern uint8_t farm_mode;
-extern int farm_no;
 extern int farm_timer;
 extern uint8_t farm_status;
 
+extern bool UserECoolEnabled();
+extern bool FarmOrUserECool();
+
 #ifdef TMC2130
 #define SILENT_MODE_NORMAL 0
 #define SILENT_MODE_STEALTH 1
@@ -153,6 +158,8 @@ extern uint8_t SilentModeMenu_MMU;
 extern bool cancel_heatup;
 extern bool isPrintPaused;
 
+extern uint8_t scrollstuff;
+
 
 void lcd_ignore_click(bool b=true);
 void lcd_commands();
@@ -191,22 +198,16 @@ extern bool bFilamentAction;
 void mFilamentItem(uint16_t nTemp,uint16_t nTempBed);
 void mFilamentItemForce();
 void lcd_generic_preheat_menu();
-void unload_filament();
+void unload_filament(bool automatic = false);
 
-void stack_error();
 void lcd_printer_connected();
 void lcd_ping();
 
 void lcd_calibrate_extruder();
-void lcd_farm_sdcard_menu();
-
-//void getFileDescription(char *name, char *description);
-
-void lcd_farm_sdcard_menu_w();
-//void get_description();
 
 void lcd_wait_for_heater();
 void lcd_wait_for_cool_down();
+void lcd_move_e(); // NOT static due to usage in Marlin_main
 void lcd_extr_cal_reset();
 
 void lcd_temp_cal_show_result(bool result);
@@ -225,10 +226,7 @@ void lcd_temp_calibration_set();
 
 void display_loading();
 
-#if !SDSORT_USES_RAM
 void lcd_set_degree();
-void lcd_set_progress();
-#endif
 
 #if (LANG_MODE != 0)
 void lcd_language();
@@ -262,4 +260,8 @@ void lcd_wizard(WizState state);
 extern void lcd_experimental_toggle();
 extern void lcd_experimental_menu();
 
+#ifdef PINDA_TEMP_COMP
+extern void lcd_pinda_temp_compensation_toggle();
+#endif //PINDA_TEMP_COMP
+
 #endif //ULTRALCD_H

+ 36 - 130
Firmware/util.cpp

@@ -1,12 +1,15 @@
 #include "Configuration.h"
 
 #include "ultralcd.h"
+#include "menu.h"
 #include "sound.h"
 #include "language.h"
 #include "util.h"
+#include <avr/pgmspace.h>
 
 // Allocate the version string in the program memory. Otherwise the string lands either on the stack or in the global RAM.
-const char FW_VERSION_STR[] PROGMEM = FW_VERSION;
+static const char FW_VERSION_STR[] PROGMEM = FW_VERSION;
+static const uint16_t FW_VERSION_NR[4] PROGMEM = { FW_MAJOR, FW_MINOR, FW_REVISION, FW_COMMIT_NR };
 
 const char* FW_VERSION_STR_P()
 {
@@ -137,106 +140,20 @@ inline bool strncmp_PP(const char *p1, const char *p2, uint8_t n)
     return 0;
 }
 
-// Parse a major.minor.revision version number.
-// Return true if valid.
-inline bool parse_version_P(const char *str, uint16_t version[4])
-{    
-#if 0
-    SERIAL_ECHOPGM("Parsing version string ");
-    SERIAL_ECHORPGM(str);
-    SERIAL_ECHOLNPGM("");
-#endif
-
-    const char *major = str;
-    const char *p = str;
-    while (is_digit(char(pgm_read_byte(p)))) ++ p;
-    if (pgm_read_byte(p) != '.')
-        return false;
-    const char *minor = ++ p;
-    while (is_digit(char(pgm_read_byte(p)))) ++ p;
-    if (pgm_read_byte(p) != '.')
-        return false;
-    const char *rev = ++ p;
-    while (is_digit(char(pgm_read_byte(p)))) ++ p;
-    if (! is_whitespace_or_nl_or_eol(char(pgm_read_byte(p))) && pgm_read_byte(p) != '-')
-        return false;
-
-    char buf[5];
-    uint8_t n = minor - major - 1;
-    if (n > 4)
-        return false;
-    memcpy_P(buf, major, n); buf[n] = 0;
-    char *endptr = NULL;
-    version[0] = strtol(buf, &endptr, 10);
-    if (*endptr != 0)
-        return false;
-    n = rev - minor - 1;
-    if (n > 4)
-        return false;
-    memcpy_P(buf, minor, n); buf[n] = 0;
-    version[1] = strtol(buf, &endptr, 10);
-    if (*endptr != 0)
-        return false;
-    n = p - rev;
-    if (n > 4)
-        return false;
-    memcpy_P(buf, rev, n);
-    buf[n] = 0;
-    version[2] = strtol(buf, &endptr, 10);
-    if (*endptr != 0)
-        return false;
-
-    version[3] = FIRMWARE_REVISION_RELEASED;
-    if (pgm_read_byte(p ++) == '-') {
-        const char *q = p;
-        while (! is_whitespace_or_nl_or_eol(char(pgm_read_byte(q))))
-            ++ q;
-        n = q - p;
-        if (n == strlen_P(STR_REVISION_DEV) && strncmp_PP(p, STR_REVISION_DEV, n) == 0)
-            version[3] = FIRMWARE_REVISION_DEV;
-        else if (n == strlen_P(STR_REVISION_ALPHA) && strncmp_PP(p, STR_REVISION_ALPHA, n) == 0)
-            version[3] = FIRMWARE_REVISION_ALPHA;
-        else if (n == strlen_P(STR_REVISION_BETA) && strncmp_PP(p, STR_REVISION_BETA, n) == 0)
-            version[3] = FIRMWARE_REVISION_BETA;
-        else if ((n == 2 || n == 3) && strncmp_PP(p, STR_REVISION_RC, 2) == 0) {
-            if (n == 2)
-                version[3] = FIRMWARE_REVISION_RC;
-            else {
-                p += 2;
-                if (is_digit(pgm_read_byte(p)))
-                    version[3] = FIRMWARE_REVISION_RC + pgm_read_byte(p) - '1';
-                else
-                    return false;
-            }
-        } else
-            return false;
-    }
-
-#if 0
-    SERIAL_ECHOPGM("Version parsed, major: ");
-    SERIAL_ECHO(version[0]);
-    SERIAL_ECHOPGM(", minor: ");
-    SERIAL_ECHO(version[1]);
-    SERIAL_ECHOPGM(", revision: ");
-    SERIAL_ECHO(version[2]);
-    SERIAL_ECHOPGM(", flavor: ");
-    SERIAL_ECHO(version[3]);
-    SERIAL_ECHOLNPGM("");
-#endif
-    return true;
-}
-
 // 1 - yes, 0 - false, -1 - error;
 inline int8_t is_provided_version_newer(const char *version_string)
 {
-    uint16_t ver_gcode[4], ver_current[4];
+    uint16_t ver_gcode[4];
     if (! parse_version(version_string, ver_gcode))
         return -1;
-    if (! parse_version_P(FW_VERSION_STR, ver_current))
-        return 0; // this shall not happen
-    for (uint8_t i = 0; i < 3; ++ i)
-        if (ver_gcode[i] > ver_current[i])
+    for (uint8_t i = 0; i < 4; ++ i)
+    {
+        uint16_t v = (uint16_t)pgm_read_word(&FW_VERSION_NR[i]);
+        if (ver_gcode[i] > v)
             return 1;
+        else if (ver_gcode[i] < v)
+            return 0;
+    }
     return 0;
 }
 
@@ -270,24 +187,9 @@ bool force_selftest_if_fw_version()
 
 bool show_upgrade_dialog_if_version_newer(const char *version_string)
 {
-    uint16_t ver_gcode[4], ver_current[4];
-    if (! parse_version(version_string, ver_gcode)) {
-//        SERIAL_PROTOCOLLNPGM("parse_version failed");
+    int8_t upgrade = is_provided_version_newer(version_string);
+    if (upgrade < 0)
         return false;
-    }
-    if (! parse_version_P(FW_VERSION_STR, ver_current)) {
-//        SERIAL_PROTOCOLLNPGM("parse_version_P failed");
-        return false; // this shall not happen
-    }
-//    SERIAL_PROTOCOLLNPGM("versions parsed");
-    bool upgrade = false;
-    for (uint8_t i = 0; i < 4; ++ i) {
-        if (ver_gcode[i] > ver_current[i]) {
-            upgrade = true;
-            break;
-        } else if (ver_gcode[i] < ver_current[i])
-            break;
-    }
 
     if (upgrade) {
         lcd_display_message_fullscreen_P(_i("New firmware version available:"));////MSG_NEW_FIRMWARE_AVAILABLE c=20 r=2
@@ -310,16 +212,14 @@ bool show_upgrade_dialog_if_version_newer(const char *version_string)
 
 void update_current_firmware_version_to_eeprom()
 {
-    for (int8_t i = 0; i < FW_PRUSA3D_MAGIC_LEN; ++ i)
+    for (int8_t i = 0; i < FW_PRUSA3D_MAGIC_LEN; ++ i){
         eeprom_update_byte((uint8_t*)(EEPROM_FIRMWARE_PRUSA_MAGIC+i), pgm_read_byte(FW_PRUSA3D_MAGIC_STR+i));
-    uint16_t ver_current[4];
-    if (parse_version_P(FW_VERSION_STR, ver_current)) {
-        eeprom_update_word((uint16_t*)EEPROM_FIRMWARE_VERSION_MAJOR,    ver_current[0]);
-        eeprom_update_word((uint16_t*)EEPROM_FIRMWARE_VERSION_MINOR,    ver_current[1]);
-        eeprom_update_word((uint16_t*)EEPROM_FIRMWARE_VERSION_REVISION, ver_current[2]);
-        // See FirmwareRevisionFlavorType for the definition of firmware flavors.
-        eeprom_update_word((uint16_t*)EEPROM_FIRMWARE_VERSION_FLAVOR,   ver_current[3]);
     }
+    eeprom_update_word((uint16_t*)EEPROM_FIRMWARE_VERSION_MAJOR,    (uint16_t)pgm_read_word(&FW_VERSION_NR[0]));
+    eeprom_update_word((uint16_t*)EEPROM_FIRMWARE_VERSION_MINOR,    (uint16_t)pgm_read_word(&FW_VERSION_NR[1]));
+    eeprom_update_word((uint16_t*)EEPROM_FIRMWARE_VERSION_REVISION, (uint16_t)pgm_read_word(&FW_VERSION_NR[2]));
+    // See FirmwareRevisionFlavorType for the definition of firmware flavors.
+    eeprom_update_word((uint16_t*)EEPROM_FIRMWARE_VERSION_FLAVOR,   (uint16_t)pgm_read_word(&FW_VERSION_NR[3]));
 }
 
 
@@ -392,13 +292,13 @@ switch(oCheckMode)
      {
      case ClCheckMode::_Warn:
 //          lcd_show_fullscreen_message_and_wait_P(_i("Printer nozzle diameter differs from the G-code. Continue?"));
-lcd_display_message_fullscreen_P(_i("Printer nozzle diameter differs from the G-code. Continue?"));
+lcd_display_message_fullscreen_P(_i("Printer nozzle diameter differs from the G-code. Continue?"));////MSG_NOZZLE_DIFFERS_CONTINUE c=20 r=5
 lcd_wait_for_click_delay(MSG_PRINT_CHECKING_FAILED_TIMEOUT);
 //???custom_message_type=CUSTOM_MSG_TYPE_STATUS; // display / status-line recovery
 lcd_update_enable(true);           // display / status-line recovery
           break;
      case ClCheckMode::_Strict:
-          lcd_show_fullscreen_message_and_wait_P(_i("Printer nozzle diameter differs from the G-code. Please check the value in settings. Print cancelled."));
+          lcd_show_fullscreen_message_and_wait_P(_i("Printer nozzle diameter differs from the G-code. Please check the value in settings. Print cancelled."));////MSG_NOZZLE_DIFFERS_CANCELLED c=20 r=9
           lcd_print_stop();
           break;
      case ClCheckMode::_None:
@@ -428,13 +328,13 @@ switch(oCheckModel)
      {
      case ClCheckModel::_Warn:
 //          lcd_show_fullscreen_message_and_wait_P(_i("Printer model differs from the G-code. Continue?"));
-lcd_display_message_fullscreen_P(_i("G-code sliced for a different printer type. Continue?"));
+lcd_display_message_fullscreen_P(_T(MSG_GCODE_DIFF_PRINTER_CONTINUE));
 lcd_wait_for_click_delay(MSG_PRINT_CHECKING_FAILED_TIMEOUT);
 //???custom_message_type=CUSTOM_MSG_TYPE_STATUS; // display / status-line recovery
 lcd_update_enable(true);           // display / status-line recovery
           break;
      case ClCheckModel::_Strict:
-          lcd_show_fullscreen_message_and_wait_P(_i("G-code sliced for a different printer type. Please re-slice the model again. Print cancelled."));
+          lcd_show_fullscreen_message_and_wait_P(_T(MSG_GCODE_DIFF_PRINTER_CANCELLED));
           lcd_print_stop();
           break;
      case ClCheckModel::_None:
@@ -478,13 +378,13 @@ switch(oCheckVersion)
      {
      case ClCheckVersion::_Warn:
 //          lcd_show_fullscreen_message_and_wait_P(_i("Printer FW version differs from the G-code. Continue?"));
-lcd_display_message_fullscreen_P(_i("G-code sliced for a newer firmware. Continue?"));
+lcd_display_message_fullscreen_P(_i("G-code sliced for a newer firmware. Continue?"));////MSG_GCODE_NEWER_FIRMWARE_CONTINUE c=20 r=5
 lcd_wait_for_click_delay(MSG_PRINT_CHECKING_FAILED_TIMEOUT);
 //???custom_message_type=CUSTOM_MSG_TYPE_STATUS; // display / status-line recovery
 lcd_update_enable(true);           // display / status-line recovery
           break;
      case ClCheckVersion::_Strict:
-          lcd_show_fullscreen_message_and_wait_P(_i("G-code sliced for a newer firmware. Please update the firmware. Print cancelled."));
+          lcd_show_fullscreen_message_and_wait_P(_i("G-code sliced for a newer firmware. Please update the firmware. Print cancelled."));////MSG_GCODE_NEWER_FIRMWARE_CANCELLED c=20 r=8
           lcd_print_stop();
           break;
      case ClCheckVersion::_None:
@@ -511,13 +411,13 @@ switch(oCheckGcode)
      {
      case ClCheckGcode::_Warn:
 //          lcd_show_fullscreen_message_and_wait_P(_i("Printer G-code level differs from the G-code. Continue?"));
-lcd_display_message_fullscreen_P(_i("G-code sliced for a different level. Continue?"));
+lcd_display_message_fullscreen_P(_i("G-code sliced for a different level. Continue?"));////MSG_GCODE_DIFF_CONTINUE c=20 r=4
 lcd_wait_for_click_delay(MSG_PRINT_CHECKING_FAILED_TIMEOUT);
 //???custom_message_type=CUSTOM_MSG_TYPE_STATUS; // display / status-line recovery
 lcd_update_enable(true);           // display / status-line recovery
           break;
      case ClCheckGcode::_Strict:
-          lcd_show_fullscreen_message_and_wait_P(_i("G-code sliced for a different level. Please re-slice the model again. Print cancelled."));
+          lcd_show_fullscreen_message_and_wait_P(_i("G-code sliced for a different level. Please re-slice the model again. Print cancelled."));////MSG_GCODE_DIFF_CANCELLED c=20 r=7
           lcd_print_stop();
           break;
      case ClCheckGcode::_None:
@@ -577,13 +477,13 @@ switch(oCheckModel)
      {
      case ClCheckModel::_Warn:
 //          lcd_show_fullscreen_message_and_wait_P(_i("Printer model differs from the G-code. Continue?"));
-lcd_display_message_fullscreen_P(_i("G-code sliced for a different printer type. Continue?"));
+lcd_display_message_fullscreen_P(_T(MSG_GCODE_DIFF_PRINTER_CONTINUE));
 lcd_wait_for_click_delay(MSG_PRINT_CHECKING_FAILED_TIMEOUT);
 //???custom_message_type=CUSTOM_MSG_TYPE_STATUS; // display / status-line recovery
 lcd_update_enable(true);           // display / status-line recovery
           break;
      case ClCheckModel::_Strict:
-          lcd_show_fullscreen_message_and_wait_P(_i("G-code sliced for a different printer type. Please re-slice the model again. Print cancelled."));
+          lcd_show_fullscreen_message_and_wait_P(_T(MSG_GCODE_DIFF_PRINTER_CANCELLED));
           lcd_print_stop();
           break;
      case ClCheckModel::_None:
@@ -604,3 +504,9 @@ else {
      sPrinterName=_sPrinterName;
      }
 }
+
+
+void ip4_to_str(char* dest, uint8_t* IP)
+{
+    sprintf_P(dest, PSTR("%u.%u.%u.%u"), IP[0], IP[1], IP[2], IP[3]);
+}

+ 4 - 0
Firmware/util.h

@@ -51,6 +51,7 @@ enum class ClNozzleDiameter:uint_least8_t
     _Diameter_250=25,
     _Diameter_400=40,
     _Diameter_600=60,
+    _Diameter_800=80,
     _Diameter_Undef=EEPROM_EMPTY_VALUE
 };
 
@@ -109,4 +110,7 @@ void gcode_level_check(uint16_t nGcodeLevel);
 
 void fSetMmuMode(bool bMMu);
 
+#define IP4_STR_SIZE 16
+extern void ip4_to_str(char* dest, uint8_t* IP);
+
 #endif /* UTIL_H */

+ 7 - 1
Firmware/variants/1_75mm_MK2-RAMBo10a-E3Dv6full.h

@@ -90,8 +90,9 @@ AXIS SETTINGS
 #define DEFAULT_MAX_ACCELERATION      {9000,9000,500,10000}    // X, Y, Z, E maximum start speed for accelerated moves. E default values are good for Skeinforge 40+, for older versions raise them a lot.
 #define DEFAULT_MAX_ACCELERATION_SILENT     {960, 960, 200, 5000}    // (mm/sec^2) max acceleration (M201), silent mode
 
-#define DEFAULT_ACCELERATION          1500    // X, Y, Z and E max acceleration in mm/s^2 for printing moves
+#define DEFAULT_ACCELERATION          1500   // X, Y, Z and E max acceleration in mm/s^2 for printing moves
 #define DEFAULT_RETRACT_ACCELERATION  1500   // X, Y, Z and E max acceleration in mm/s^2 for retracts
+#define DEFAULT_TRAVEL_ACCELERATION   1500   // X, Y, Z and E max acceleration in mm/s^2 for travels
 
 
 #define MANUAL_FEEDRATE {3000, 3000, 1000, 100}   // set the speeds for manual moves (mm/min)
@@ -326,6 +327,10 @@ PREHEAT SETTINGS
 #define PLA_PREHEAT_HPB_TEMP 55
 #define PLA_PREHEAT_FAN_SPEED 0
 
+#define PVB_PREHEAT_HOTEND_TEMP 215
+#define PVB_PREHEAT_HPB_TEMP 75
+#define PVB_PREHEAT_FAN_SPEED 0
+
 #define ASA_PREHEAT_HOTEND_TEMP 260
 #define ASA_PREHEAT_HPB_TEMP 105
 #define ASA_PREHEAT_FAN_SPEED 0
@@ -413,6 +418,7 @@ THERMISTORS SETTINGS
 #endif
 
 #define STACK_GUARD_TEST_VALUE 0xA2A2
+#define STACK_GUARD_MARGIN     32
 
 #define MAX_BED_TEMP_CALIBRATION 50
 #define MAX_HOTEND_TEMP_CALIBRATION 50

+ 6 - 1
Firmware/variants/1_75mm_MK2-RAMBo13a-E3Dv6full.h

@@ -90,8 +90,9 @@ AXIS SETTINGS
 #define DEFAULT_MAX_ACCELERATION      {9000,9000,500,10000}    // X, Y, Z, E maximum start speed for accelerated moves. E default values are good for Skeinforge 40+, for older versions raise them a lot.
 #define DEFAULT_MAX_ACCELERATION_SILENT     {960, 960, 200, 5000}    // (mm/sec^2) max acceleration (M201), silent mode
 
-#define DEFAULT_ACCELERATION          1500    // X, Y, Z and E max acceleration in mm/s^2 for printing moves
+#define DEFAULT_ACCELERATION          1500   // X, Y, Z and E max acceleration in mm/s^2 for printing moves
 #define DEFAULT_RETRACT_ACCELERATION  1500   // X, Y, Z and E max acceleration in mm/s^2 for retracts
+#define DEFAULT_TRAVEL_ACCELERATION   1500   // X, Y, Z and E max acceleration in mm/s^2 for travels
 
 
 #define MANUAL_FEEDRATE {3000, 3000, 1000, 100}   // set the speeds for manual moves (mm/min)
@@ -323,6 +324,9 @@ PREHEAT SETTINGS
 #define PLA_PREHEAT_HOTEND_TEMP 215
 #define PLA_PREHEAT_HPB_TEMP 55
 
+#define PVB_PREHEAT_HOTEND_TEMP 215
+#define PVB_PREHEAT_HPB_TEMP 75
+
 #define ASA_PREHEAT_HOTEND_TEMP 260
 #define ASA_PREHEAT_HPB_TEMP 105
 
@@ -403,6 +407,7 @@ THERMISTORS SETTINGS
 #endif
 
 #define STACK_GUARD_TEST_VALUE 0xA2A2
+#define STACK_GUARD_MARGIN     32
 
 #define MAX_BED_TEMP_CALIBRATION 50
 #define MAX_HOTEND_TEMP_CALIBRATION 50

+ 15 - 3
Firmware/variants/1_75mm_MK25-RAMBo10a-E3Dv6full.h

@@ -94,8 +94,9 @@
 #define DEFAULT_MAX_ACCELERATION      {1000, 1000, 200, 5000}  // (mm/sec^2) max acceleration (M201)
 #define DEFAULT_MAX_ACCELERATION_SILENT     {960, 960, 200, 5000}    // (mm/sec^2) max acceleration (M201), silent mode
 
-#define DEFAULT_ACCELERATION          1250   // X, Y, Z and E max acceleration in mm/s^2 for printing moves (M204S)
-#define DEFAULT_RETRACT_ACCELERATION  1250   // X, Y, Z and E max acceleration in mm/s^2 for retracts (M204T)
+#define DEFAULT_ACCELERATION          1250   // X, Y, Z and E max acceleration in mm/s^2 for printing moves (M204P)
+#define DEFAULT_RETRACT_ACCELERATION  1250   // X, Y, Z and E max acceleration in mm/s^2 for retracts (M204R)
+#define DEFAULT_TRAVEL_ACCELERATION   1250   // X, Y, Z and E max acceleration in mm/s^2 for travels (M204T)
 
 #define MANUAL_FEEDRATE {2700, 2700, 1000, 100}   // set the speeds for manual moves (mm/min)
 
@@ -118,11 +119,15 @@
 #define DEFAULT_SAFETYTIMER_TIME_MINS 30
 #define FARM_DEFAULT_SAFETYTIMER_TIME_ms (45*60*1000ul)
 
+// Online crash dumper
+#define EMERGENCY_SERIAL_DUMP   // Request dump via serial on stack corruption and WDR
+#define MENU_SERIAL_DUMP        // Enable "Memory dump" in Settings menu
+
 // Filament sensor
 #define FILAMENT_SENSOR
 #define PAT9125
 
-
+#define DEBUG_DCODE2
 #define DEBUG_DCODE3
 
 //#define DEBUG_BUILD
@@ -175,6 +180,9 @@
 #if BED_MINTEMP_DELAY>USHRT_MAX
 #error "Check maximal allowed value @ ShortTimer (see BED_MINTEMP_DELAY definition)"
 #endif
+#define SUPERPINDA_SUPPORT
+#define PINDA_MINTEMP 30 //The miniRAMBo thermistor readings below 30°C aren't very accurate
+#define PINDA_TEMP_COMP //Used to enable SuperPINDA toggle menu/function
 
 // Maxtemps
 #if defined(E3D_PT100_EXTRUDER_WITH_AMP) || defined(E3D_PT100_EXTRUDER_NO_AMP)
@@ -381,6 +389,9 @@
 #define PLA_PREHEAT_HOTEND_TEMP 215
 #define PLA_PREHEAT_HPB_TEMP 60
 
+#define PVB_PREHEAT_HOTEND_TEMP 215
+#define PVB_PREHEAT_HPB_TEMP 75
+
 #define ASA_PREHEAT_HOTEND_TEMP 260
 #define ASA_PREHEAT_HPB_TEMP 105
 
@@ -462,6 +473,7 @@
 #define TEMP_SENSOR_PINDA 1
 
 #define STACK_GUARD_TEST_VALUE 0xA2A2
+#define STACK_GUARD_MARGIN     32
 
 #define MAX_BED_TEMP_CALIBRATION 50
 #define MAX_HOTEND_TEMP_CALIBRATION 50

+ 15 - 3
Firmware/variants/1_75mm_MK25-RAMBo13a-E3Dv6full.h

@@ -95,8 +95,9 @@
 #define DEFAULT_MAX_ACCELERATION_SILENT     {960, 960, 200, 5000}    // (mm/sec^2) max acceleration (M201), silent mode
 
 
-#define DEFAULT_ACCELERATION          1250   // X, Y, Z and E max acceleration in mm/s^2 for printing moves (M204S)
-#define DEFAULT_RETRACT_ACCELERATION  1250   // X, Y, Z and E max acceleration in mm/s^2 for retracts (M204T)
+#define DEFAULT_ACCELERATION          1250   // X, Y, Z and E max acceleration in mm/s^2 for printing moves (M204P)
+#define DEFAULT_RETRACT_ACCELERATION  1250   // X, Y, Z and E max acceleration in mm/s^2 for retracts (M204R)
+#define DEFAULT_TRAVEL_ACCELERATION   1250   // X, Y, Z and E max acceleration in mm/s^2 for travels (M204T)
 
 #define MANUAL_FEEDRATE {2700, 2700, 1000, 100}   // set the speeds for manual moves (mm/min)
 
@@ -119,11 +120,15 @@
 #define DEFAULT_SAFETYTIMER_TIME_MINS 30
 #define FARM_DEFAULT_SAFETYTIMER_TIME_ms (45*60*1000ul)
 
+// Online crash dumper
+#define EMERGENCY_SERIAL_DUMP   // Request dump via serial on stack corruption and WDR
+#define MENU_SERIAL_DUMP        // Enable "Memory dump" in Settings menu
+
 // Filament sensor
 #define FILAMENT_SENSOR
 #define PAT9125
 
-
+#define DEBUG_DCODE2
 #define DEBUG_DCODE3
 
 //#define DEBUG_BUILD
@@ -176,6 +181,9 @@
 #if BED_MINTEMP_DELAY>USHRT_MAX
 #error "Check maximal allowed value @ ShortTimer (see BED_MINTEMP_DELAY definition)"
 #endif
+#define SUPERPINDA_SUPPORT
+#define PINDA_MINTEMP 30 //The miniRAMBo thermistor readings below 30°C aren't very accurate
+#define PINDA_TEMP_COMP //Used to enable SuperPINDA toggle menu/function
 
 // Maxtemps
 #if defined(E3D_PT100_EXTRUDER_WITH_AMP) || defined(E3D_PT100_EXTRUDER_NO_AMP)
@@ -382,6 +390,9 @@
 #define PLA_PREHEAT_HOTEND_TEMP 215
 #define PLA_PREHEAT_HPB_TEMP 60
 
+#define PVB_PREHEAT_HOTEND_TEMP 215
+#define PVB_PREHEAT_HPB_TEMP 75
+
 #define ASA_PREHEAT_HOTEND_TEMP 260
 #define ASA_PREHEAT_HPB_TEMP 105
 
@@ -463,6 +474,7 @@
 #define TEMP_SENSOR_PINDA 1
 
 #define STACK_GUARD_TEST_VALUE 0xA2A2
+#define STACK_GUARD_MARGIN     32
 
 #define MAX_BED_TEMP_CALIBRATION 50
 #define MAX_HOTEND_TEMP_CALIBRATION 50

+ 15 - 3
Firmware/variants/1_75mm_MK25S-RAMBo10a-E3Dv6full.h

@@ -94,8 +94,9 @@
 #define DEFAULT_MAX_ACCELERATION      {1000, 1000, 200, 5000}  // (mm/sec^2) max acceleration (M201)
 #define DEFAULT_MAX_ACCELERATION_SILENT     {960, 960, 200, 5000}    // (mm/sec^2) max acceleration (M201), silent mode
 
-#define DEFAULT_ACCELERATION          1250   // X, Y, Z and E max acceleration in mm/s^2 for printing moves (M204S)
-#define DEFAULT_RETRACT_ACCELERATION  1250   // X, Y, Z and E max acceleration in mm/s^2 for retracts (M204T)
+#define DEFAULT_ACCELERATION          1250   // X, Y, Z and E max acceleration in mm/s^2 for printing moves (M204P)
+#define DEFAULT_RETRACT_ACCELERATION  1250   // X, Y, Z and E max acceleration in mm/s^2 for retracts (M204R)
+#define DEFAULT_TRAVEL_ACCELERATION   1250   // X, Y, Z and E max acceleration in mm/s^2 for travels (M204T)
 
 #define MANUAL_FEEDRATE {2700, 2700, 1000, 100}   // set the speeds for manual moves (mm/min)
 
@@ -118,11 +119,15 @@
 #define DEFAULT_SAFETYTIMER_TIME_MINS 30
 #define FARM_DEFAULT_SAFETYTIMER_TIME_ms (45*60*1000ul)
 
+// Online crash dumper
+#define EMERGENCY_SERIAL_DUMP   // Request dump via serial on stack corruption and WDR
+#define MENU_SERIAL_DUMP        // Enable "Memory dump" in Settings menu
+
 // Filament sensor
 #define FILAMENT_SENSOR
 #define IR_SENSOR
 
-
+#define DEBUG_DCODE2
 #define DEBUG_DCODE3
 
 //#define DEBUG_BUILD
@@ -175,6 +180,9 @@
 #if BED_MINTEMP_DELAY>USHRT_MAX
 #error "Check maximal allowed value @ ShortTimer (see BED_MINTEMP_DELAY definition)"
 #endif
+#define SUPERPINDA_SUPPORT
+#define PINDA_MINTEMP 30 //The miniRAMBo thermistor readings below 30°C aren't very accurate
+#define PINDA_TEMP_COMP //Used to enable SuperPINDA toggle menu/function
 
 // Maxtemps
 #if defined(E3D_PT100_EXTRUDER_WITH_AMP) || defined(E3D_PT100_EXTRUDER_NO_AMP)
@@ -381,6 +389,9 @@
 #define PLA_PREHEAT_HOTEND_TEMP 215
 #define PLA_PREHEAT_HPB_TEMP 60
 
+#define PVB_PREHEAT_HOTEND_TEMP 215
+#define PVB_PREHEAT_HPB_TEMP 75
+
 #define ASA_PREHEAT_HOTEND_TEMP 260
 #define ASA_PREHEAT_HPB_TEMP 105
 
@@ -462,6 +473,7 @@
 #define TEMP_SENSOR_PINDA 1
 
 #define STACK_GUARD_TEST_VALUE 0xA2A2
+#define STACK_GUARD_MARGIN     32
 
 #define MAX_BED_TEMP_CALIBRATION 50
 #define MAX_HOTEND_TEMP_CALIBRATION 50

+ 15 - 3
Firmware/variants/1_75mm_MK25S-RAMBo13a-E3Dv6full.h

@@ -95,8 +95,9 @@
 #define DEFAULT_MAX_ACCELERATION_SILENT     {960, 960, 200, 5000}    // (mm/sec^2) max acceleration (M201), silent mode
 
 
-#define DEFAULT_ACCELERATION          1250   // X, Y, Z and E max acceleration in mm/s^2 for printing moves (M204S)
-#define DEFAULT_RETRACT_ACCELERATION  1250   // X, Y, Z and E max acceleration in mm/s^2 for retracts (M204T)
+#define DEFAULT_ACCELERATION          1250   // X, Y, Z and E max acceleration in mm/s^2 for printing moves (M204P)
+#define DEFAULT_RETRACT_ACCELERATION  1250   // X, Y, Z and E max acceleration in mm/s^2 for retracts (M204R)
+#define DEFAULT_TRAVEL_ACCELERATION   1250   // X, Y, Z and E max acceleration in mm/s^2 for travels (M204T)
 
 #define MANUAL_FEEDRATE {2700, 2700, 1000, 100}   // set the speeds for manual moves (mm/min)
 
@@ -119,11 +120,15 @@
 #define DEFAULT_SAFETYTIMER_TIME_MINS 30
 #define FARM_DEFAULT_SAFETYTIMER_TIME_ms (45*60*1000ul)
 
+// Online crash dumper
+#define EMERGENCY_SERIAL_DUMP   // Request dump via serial on stack corruption and WDR
+#define MENU_SERIAL_DUMP        // Enable "Memory dump" in Settings menu
+
 // Filament sensor
 #define FILAMENT_SENSOR
 #define IR_SENSOR
 
-
+#define DEBUG_DCODE2
 #define DEBUG_DCODE3
 
 //#define DEBUG_BUILD
@@ -176,6 +181,9 @@
 #if BED_MINTEMP_DELAY>USHRT_MAX
 #error "Check maximal allowed value @ ShortTimer (see BED_MINTEMP_DELAY definition)"
 #endif
+#define SUPERPINDA_SUPPORT
+#define PINDA_MINTEMP 30 //The miniRAMBo thermistor readings below 30°C aren't very accurate
+#define PINDA_TEMP_COMP //Used to enable SuperPINDA toggle menu/function
 
 // Maxtemps
 #if defined(E3D_PT100_EXTRUDER_WITH_AMP) || defined(E3D_PT100_EXTRUDER_NO_AMP)
@@ -382,6 +390,9 @@
 #define PLA_PREHEAT_HOTEND_TEMP 215
 #define PLA_PREHEAT_HPB_TEMP 60
 
+#define PVB_PREHEAT_HOTEND_TEMP 215
+#define PVB_PREHEAT_HPB_TEMP 75
+
 #define ASA_PREHEAT_HOTEND_TEMP 260
 #define ASA_PREHEAT_HPB_TEMP 105
 
@@ -463,6 +474,7 @@
 #define TEMP_SENSOR_PINDA 1
 
 #define STACK_GUARD_TEST_VALUE 0xA2A2
+#define STACK_GUARD_MARGIN     32
 
 #define MAX_BED_TEMP_CALIBRATION 50
 #define MAX_HOTEND_TEMP_CALIBRATION 50

+ 31 - 7
Firmware/variants/1_75mm_MK3-EINSy10a-E3Dv6full.h

@@ -2,7 +2,7 @@
 #define CONFIGURATION_PRUSA_H
 
 #include <limits.h>
-//-//
+
 #include "printers.h"
 /*------------------------------------
  GENERAL SETTINGS
@@ -99,8 +99,9 @@
 #define DEFAULT_MAX_ACCELERATION_SILENT     {960, 960, 200, 5000}    // (mm/sec^2) max acceleration (M201), silent mode
 
 
-#define DEFAULT_ACCELERATION          1250   // X, Y, Z and E max acceleration in mm/s^2 for printing moves (M204S)
-#define DEFAULT_RETRACT_ACCELERATION  1250   // X, Y, Z and E max acceleration in mm/s^2 for retracts (M204T)
+#define DEFAULT_ACCELERATION          1250   // X, Y, Z and E max acceleration in mm/s^2 for printing moves (M204P)
+#define DEFAULT_RETRACT_ACCELERATION  1250   // X, Y, Z and E max acceleration in mm/s^2 for retracts (M204R)
+#define DEFAULT_TRAVEL_ACCELERATION   1250   // X, Y, Z and E max acceleration in mm/s^2 for travels (M204T)
 
 #define MANUAL_FEEDRATE {2700, 2700, 1000, 100}   // set the speeds for manual moves (mm/min)
 
@@ -138,6 +139,15 @@
 #define DEFAULT_SAFETYTIMER_TIME_MINS 30
 #define FARM_DEFAULT_SAFETYTIMER_TIME_ms (45*60*1000ul)
 
+// Offline crash dumper
+#define XFLASH_DUMP     // enable dump functionality (including D20/D21/D22)
+#define MENU_DUMP       // enable "Memory dump" in Settings menu
+#define EMERGENCY_DUMP  // trigger crash on stack corruption and WDR
+
+// Online crash dumper
+//#define EMERGENCY_SERIAL_DUMP   // Request dump via serial on stack corruption and WDR
+//#define MENU_SERIAL_DUMP        // Enable "Memory dump" in Settings menu
+
 // Filament sensor
 #define FILAMENT_SENSOR
 #define PAT9125
@@ -154,11 +164,13 @@
 #define MINTEMP_MINAMBIENT      10
 #define MINTEMP_MINAMBIENT_RAW  1002
 
+#define DEBUG_DCODE2
 #define DEBUG_DCODE3
+#define DEBUG_DCODE6
 
 //#define DEBUG_BUILD
 //#define DEBUG_SEC_LANG   //secondary language debug output at startup
-//#define DEBUG_W25X20CL   //debug external spi flash
+//#define DEBUG_XFLASH   //debug external spi flash
 #ifdef DEBUG_BUILD
 //#define _NO_ASM
 #define DEBUG_DCODES //D codes
@@ -233,6 +245,11 @@
 #define TMC2130_PWM_AUTO_E  1         // PWMCONF
 #define TMC2130_PWM_FREQ_E  2         // PWMCONF
 
+// experimental setting for E-motor cooler operation
+#define TMC2130_PWM_GRAD_Ecool  84        // PWMCONF 730mA @ 375mm/min  970mA phase peak at feedrate 900mm/min
+#define TMC2130_PWM_AMPL_Ecool  43        // PWMCONF 500mA phase peak at feedrate 10 mm/min
+#define TMC2130_PWM_AUTO_Ecool  0         // PWMCONF
+
 #define TMC2130_TOFF_XYZ    3         // CHOPCONF // fchop = 27.778kHz
 #define TMC2130_TOFF_E      3         // CHOPCONF // fchop = 27.778kHz
 //#define TMC2130_TOFF_E      4         // CHOPCONF // fchop = 21.429kHz
@@ -246,6 +263,7 @@
 #define TMC2130_PWM_CLK   (2 * TMC2130_FCLK / TMC2130_PWM_DIV) // PWM frequency (23.4kHz, 35.1kHz, 46.9kHz, 58.5kHz for 12MHz fclk)
 
 #define TMC2130_TPWMTHRS  0         // TPWMTHRS - Sets the switching speed threshold based on TSTEP from stealthChop to spreadCycle mode
+#define TMC2130_TPWMTHRS_E 403      // Switch extruder from StealthChop to SpreadCycle at around 900mm/min
 #define TMC2130_THIGH     0         // THIGH - unused
 
 //#define TMC2130_TCOOLTHRS_X 450       // TCOOLTHRS - coolstep treshold
@@ -264,11 +282,12 @@
 
 //new settings is possible for vsense = 1, running current value > 31 set vsense to zero and shift both currents by 1 bit right (Z axis only)
 #define TMC2130_CURRENTS_H {16, 20, 35, 30}  // default holding currents for all axes
+#define TMC2130_CURRENTS_FARM 36             // E 805 mA peak for ECool/farm mode
 #define TMC2130_CURRENTS_R {16, 20, 35, 30}  // default running currents for all axes
 #define TMC2130_CURRENTS_R_HOME {8, 10, 20, 18}  // homing running currents for all axes
-// #define TMC2130_UNLOAD_CURRENT_R 12			 // lower current for M600 to protect filament sensor - Unused
 
 #define TMC2130_STEALTH_Z
+#define TMC2130_DEDGE_STEPPING
 
 //#define TMC2130_SERVICE_CODES_M910_M918
 
@@ -294,8 +313,9 @@
 #if BED_MINTEMP_DELAY>USHRT_MAX
 #error "Check maximal allowed value @ ShortTimer (see BED_MINTEMP_DELAY definition)"
 #endif
-#define DETECT_SUPERPINDA
-#define PINDA_MINTEMP BED_MINTEMP
+#define SUPERPINDA_SUPPORT
+#define PINDA_MINTEMP 10
+//#define PINDA_TEMP_COMP //Used to enable SuperPINDA toggle menu/function
 #define AMBIENT_MINTEMP -30
 
 // Maxtemps
@@ -497,6 +517,9 @@
 #define PLA_PREHEAT_HOTEND_TEMP 215
 #define PLA_PREHEAT_HPB_TEMP 60
 
+#define PVB_PREHEAT_HOTEND_TEMP 215
+#define PVB_PREHEAT_HPB_TEMP 75
+
 #define ASA_PREHEAT_HOTEND_TEMP 260
 #define ASA_PREHEAT_HPB_TEMP 105
 
@@ -579,6 +602,7 @@
 #define TEMP_SENSOR_AMBIENT 2000
 
 #define STACK_GUARD_TEST_VALUE 0xA2A2
+#define STACK_GUARD_MARGIN     32
 
 #define MAX_BED_TEMP_CALIBRATION 50
 #define MAX_HOTEND_TEMP_CALIBRATION 50

+ 30 - 6
Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h

@@ -101,8 +101,9 @@
 #define DEFAULT_MAX_ACCELERATION_SILENT     {960, 960, 200, 5000}    // (mm/sec^2) max acceleration (M201), silent mode
 
 
-#define DEFAULT_ACCELERATION          1250   // X, Y, Z and E max acceleration in mm/s^2 for printing moves (M204S)
-#define DEFAULT_RETRACT_ACCELERATION  1250   // X, Y, Z and E max acceleration in mm/s^2 for retracts (M204T)
+#define DEFAULT_ACCELERATION          1250   // X, Y, Z and E max acceleration in mm/s^2 for printing moves (M204P)
+#define DEFAULT_RETRACT_ACCELERATION  1250   // X, Y, Z and E max acceleration in mm/s^2 for retracts (M204R)
+#define DEFAULT_TRAVEL_ACCELERATION   1250   // X, Y, Z and E max acceleration in mm/s^2 for travels (M204T)
 
 #define MANUAL_FEEDRATE {2700, 2700, 1000, 100}   // set the speeds for manual moves (mm/min)
 
@@ -140,6 +141,15 @@
 #define DEFAULT_SAFETYTIMER_TIME_MINS 30
 #define FARM_DEFAULT_SAFETYTIMER_TIME_ms (45*60*1000ul)
 
+// Offline crash dumper
+#define XFLASH_DUMP     // enable dump functionality (including D20/D21/D22)
+#define MENU_DUMP       // enable "Memory dump" in Settings menu
+#define EMERGENCY_DUMP  // trigger crash on stack corruption and WDR
+
+// Online crash dumper
+//#define EMERGENCY_SERIAL_DUMP   // Request dump via serial on stack corruption and WDR
+//#define MENU_SERIAL_DUMP        // Enable "Memory dump" in Settings menu
+
 // Filament sensor
 #define FILAMENT_SENSOR
 #define IR_SENSOR
@@ -156,11 +166,13 @@
 #define MINTEMP_MINAMBIENT      10
 #define MINTEMP_MINAMBIENT_RAW  1002
 
+#define DEBUG_DCODE2
 #define DEBUG_DCODE3
+#define DEBUG_DCODE6
 
 //#define DEBUG_BUILD
 //#define DEBUG_SEC_LANG   //secondary language debug output at startup
-//#define DEBUG_W25X20CL   //debug external spi flash
+//#define DEBUG_XFLASH   //debug external spi flash
 #ifdef DEBUG_BUILD
 //#define _NO_ASM
 #define DEBUG_DCODES //D codes
@@ -235,6 +247,11 @@
 #define TMC2130_PWM_AUTO_E  1         // PWMCONF
 #define TMC2130_PWM_FREQ_E  2         // PWMCONF
 
+// experimental setting for E-motor cooler operation
+#define TMC2130_PWM_GRAD_Ecool  84        // PWMCONF 730mA @ 375mm/min  970mA phase peak at feedrate 900mm/min
+#define TMC2130_PWM_AMPL_Ecool  43        // PWMCONF 500mA phase peak at feedrate 10 mm/min
+#define TMC2130_PWM_AUTO_Ecool  0         // PWMCONF
+
 #define TMC2130_TOFF_XYZ    3         // CHOPCONF // fchop = 27.778kHz
 #define TMC2130_TOFF_E      3         // CHOPCONF // fchop = 27.778kHz
 //#define TMC2130_TOFF_E      4         // CHOPCONF // fchop = 21.429kHz
@@ -248,6 +265,7 @@
 #define TMC2130_PWM_CLK   (2 * TMC2130_FCLK / TMC2130_PWM_DIV) // PWM frequency (23.4kHz, 35.1kHz, 46.9kHz, 58.5kHz for 12MHz fclk)
 
 #define TMC2130_TPWMTHRS  0         // TPWMTHRS - Sets the switching speed threshold based on TSTEP from stealthChop to spreadCycle mode
+#define TMC2130_TPWMTHRS_E 403      // Switch extruder from StealthChop to SpreadCycle at around 900mm/min
 #define TMC2130_THIGH     0         // THIGH - unused
 
 //#define TMC2130_TCOOLTHRS_X 450       // TCOOLTHRS - coolstep treshold
@@ -266,11 +284,12 @@
 
 //new settings is possible for vsense = 1, running current value > 31 set vsense to zero and shift both currents by 1 bit right (Z axis only)
 #define TMC2130_CURRENTS_H {16, 20, 35, 30}  // default holding currents for all axes
+#define TMC2130_CURRENTS_FARM 36             // E 805 mA peak for ECool/farm mode
 #define TMC2130_CURRENTS_R {16, 20, 35, 30}  // default running currents for all axes
 #define TMC2130_CURRENTS_R_HOME {8, 10, 20, 18}  // homing running currents for all axes
-// #define TMC2130_UNLOAD_CURRENT_R 12			 // lower current for M600 to protect filament sensor - Unused
 
 #define TMC2130_STEALTH_Z
+#define TMC2130_DEDGE_STEPPING
 
 //#define TMC2130_SERVICE_CODES_M910_M918
 
@@ -296,8 +315,9 @@
 #if BED_MINTEMP_DELAY>USHRT_MAX
 #error "Check maximal allowed value @ ShortTimer (see BED_MINTEMP_DELAY definition)"
 #endif
-#define DETECT_SUPERPINDA
-#define PINDA_MINTEMP BED_MINTEMP
+#define SUPERPINDA_SUPPORT
+#define PINDA_MINTEMP 10
+//#define PINDA_TEMP_COMP //Used to enable SuperPINDA toggle menu/function
 #define AMBIENT_MINTEMP -30
 
 // Maxtemps
@@ -501,6 +521,9 @@
 #define PLA_PREHEAT_HOTEND_TEMP 215
 #define PLA_PREHEAT_HPB_TEMP 60
 
+#define PVB_PREHEAT_HOTEND_TEMP 215
+#define PVB_PREHEAT_HPB_TEMP 75
+
 #define ASA_PREHEAT_HOTEND_TEMP 260
 #define ASA_PREHEAT_HPB_TEMP 105
 
@@ -583,6 +606,7 @@
 #define TEMP_SENSOR_AMBIENT 2000
 
 #define STACK_GUARD_TEST_VALUE 0xA2A2
+#define STACK_GUARD_MARGIN     32
 
 #define MAX_BED_TEMP_CALIBRATION 50
 #define MAX_HOTEND_TEMP_CALIBRATION 50

+ 0 - 44
Firmware/w25x20cl.h

@@ -1,44 +0,0 @@
-//w25x20cl.h
-#ifndef _W25X20CL_H
-#define _W25X20CL_H
-
-#include <inttypes.h>
-#include "config.h"
-#include "spi.h"
-
-
-
-#define W25X20CL_STATUS_BUSY   0x01
-#define W25X20CL_STATUS_WEL    0x02
-#define W25X20CL_STATUS_BP0    0x04
-#define W25X20CL_STATUS_BP1    0x08
-#define W25X20CL_STATUS_TB     0x20
-#define W25X20CL_STATUS_SRP    0x80
-
-#define W25X20CL_SPI_ENTER() spi_setup(W25X20CL_SPCR, W25X20CL_SPSR)
-
-#if defined(__cplusplus)
-extern "C" {
-#endif //defined(__cplusplus)
-
-
-extern int8_t w25x20cl_init(void);
-extern void w25x20cl_enable_wr(void);
-extern void w25x20cl_disable_wr(void);
-extern uint8_t w25x20cl_rd_status_reg(void);
-extern void w25x20cl_wr_status_reg(uint8_t val);
-extern void w25x20cl_rd_data(uint32_t addr, uint8_t* data, uint16_t cnt);
-extern void w25x20cl_page_program(uint32_t addr, uint8_t* data, uint16_t cnt);
-extern void w25x20cl_page_program_P(uint32_t addr, uint8_t* data, uint16_t cnt);
-extern void w25x20cl_sector_erase(uint32_t addr);
-extern void w25x20cl_block32_erase(uint32_t addr);
-extern void w25x20cl_block64_erase(uint32_t addr);
-extern void w25x20cl_chip_erase(void);
-extern void w25x20cl_page_program(uint32_t addr, uint8_t* data, uint16_t cnt);
-extern void w25x20cl_rd_uid(uint8_t* uid);
-extern void w25x20cl_wait_busy(void);
-
-#if defined(__cplusplus)
-}
-#endif //defined(__cplusplus)
-#endif //_W25X20CL_H

+ 78 - 49
Firmware/w25x20cl.c

@@ -1,13 +1,18 @@
-//w25x20cl.c
+//xflash.c
 
-#include "w25x20cl.h"
+#include "xflash.h"
 #include <avr/io.h>
 #include <avr/pgmspace.h>
-#include "io_atmega2560.h"
 #include "spi.h"
+#include "fastio.h"
 
-#define _MFRID             0xEF
-#define _DEVID             0x11
+#ifdef XFLASH
+
+#define _MFRID_W25X20CL    0xEF
+#define _DEVID_W25X20CL    0x11
+
+#define _MFRID_GD25Q20C    0xC8
+#define _DEVID_GD25Q20C    0x11
 
 #define _CMD_ENABLE_WR     0x06
 #define _CMD_ENABLE_WR_VSR 0x50
@@ -31,8 +36,8 @@
 #define _CMD_JEDEC_ID      0x9f
 #define _CMD_RD_UID        0x4b
 
-#define _CS_LOW()  PORT(W25X20CL_PIN_CS) &= ~__MSK(W25X20CL_PIN_CS)
-#define _CS_HIGH() PORT(W25X20CL_PIN_CS) |= __MSK(W25X20CL_PIN_CS)
+#define _CS_LOW() WRITE(XFLASH_PIN_CS, 0)
+#define _CS_HIGH() WRITE(XFLASH_PIN_CS, 1)
 
 //#define _SPI_TX swspi_tx
 //#define _SPI_RX swspi_rx
@@ -40,33 +45,33 @@
 #define _SPI_RX()    spi_txrx(0xff)
 
 
-int w25x20cl_mfrid_devid(void);
+int xflash_mfrid_devid(void);
 
 
-int8_t w25x20cl_init(void)
+int8_t xflash_init(void)
 {
-	PIN_OUT(W25X20CL_PIN_CS);
 	_CS_HIGH();
-	W25X20CL_SPI_ENTER();
-	if (!w25x20cl_mfrid_devid()) return 0;
+	SET_OUTPUT(XFLASH_PIN_CS);
+	XFLASH_SPI_ENTER();
+	if (!xflash_mfrid_devid()) return 0;
 	return 1;
 }
 
-void w25x20cl_enable_wr(void)
+void xflash_enable_wr(void)
 {
 	_CS_LOW();
 	_SPI_TX(_CMD_ENABLE_WR);             // send command 0x06
 	_CS_HIGH();
 }
 
-void w25x20cl_disable_wr(void)
+void xflash_disable_wr(void)
 {
 	_CS_LOW();
 	_SPI_TX(_CMD_DISABLE_WR);            // send command 0x04
 	_CS_HIGH();
 }
 
-uint8_t w25x20cl_rd_status_reg(void)
+uint8_t xflash_rd_status_reg(void)
 {
 	_CS_LOW();
 	_SPI_TX(_CMD_RD_STATUS_REG);         // send command 0x90
@@ -75,6 +80,7 @@ uint8_t w25x20cl_rd_status_reg(void)
 	return val;
 }
 
+#if 0
 void w25x20cl_wr_status_reg(uint8_t val)
 {
 	_CS_LOW();
@@ -82,69 +88,88 @@ void w25x20cl_wr_status_reg(uint8_t val)
 	_SPI_TX(val);                        // send value
 	_CS_HIGH();
 }
+#endif
 
-void w25x20cl_rd_data(uint32_t addr, uint8_t* data, uint16_t cnt)
+static void xflash_send_cmdaddr(uint8_t cmd, uint32_t addr)
 {
-	_CS_LOW();
-	_SPI_TX(_CMD_RD_DATA);               // send command 0x03
+	_SPI_TX(cmd);                        // send command 0x03
 	_SPI_TX(((uint8_t*)&addr)[2]);       // send addr bits 16..23
 	_SPI_TX(((uint8_t*)&addr)[1]);       // send addr bits 8..15
 	_SPI_TX(((uint8_t*)&addr)[0]);       // send addr bits 0..7
+}
+
+void xflash_rd_data(uint32_t addr, uint8_t* data, uint16_t cnt)
+{
+	_CS_LOW();
+	xflash_send_cmdaddr(_CMD_RD_DATA, addr);
 	while (cnt--)                        // receive data
 		*(data++) = _SPI_RX();
 	_CS_HIGH();
 }
 
-void w25x20cl_page_program(uint32_t addr, uint8_t* data, uint16_t cnt)
+void xflash_page_program(uint32_t addr, uint8_t* data, uint16_t cnt)
 {
 	_CS_LOW();
-	_SPI_TX(_CMD_PAGE_PROGRAM);          // send command 0x02
-	_SPI_TX(((uint8_t*)&addr)[2]);       // send addr bits 16..23
-	_SPI_TX(((uint8_t*)&addr)[1]);       // send addr bits 8..15
-	_SPI_TX(((uint8_t*)&addr)[0]);       // send addr bits 0..7
+	xflash_send_cmdaddr(_CMD_PAGE_PROGRAM, addr);
 	while (cnt--)                        // send data
 		_SPI_TX(*(data++));
 	_CS_HIGH();
 }
 
-void w25x20cl_page_program_P(uint32_t addr, uint8_t* data, uint16_t cnt)
+void xflash_multipage_program(uint32_t addr, uint8_t* data, uint16_t cnt)
+{
+    while(cnt)
+    {
+        xflash_enable_wr();
+        _CS_LOW();
+        xflash_send_cmdaddr(_CMD_PAGE_PROGRAM, addr);
+        while(1)
+        {
+            // send data
+            _SPI_TX(*(data++));
+            if(!--cnt || !(++addr & 0xFF))
+            {
+                // on a page boundary or end of write
+                _CS_HIGH();
+                xflash_wait_busy();
+                break;
+            }
+        }
+    }
+}
+
+void xflash_page_program_P(uint32_t addr, uint8_t* data, uint16_t cnt)
 {
 	_CS_LOW();
-	_SPI_TX(_CMD_PAGE_PROGRAM);          // send command 0x02
-	_SPI_TX(((uint8_t*)&addr)[2]);       // send addr bits 16..23
-	_SPI_TX(((uint8_t*)&addr)[1]);       // send addr bits 8..15
-	_SPI_TX(((uint8_t*)&addr)[0]);       // send addr bits 0..7
+    xflash_send_cmdaddr(_CMD_PAGE_PROGRAM, addr);
 	while (cnt--)                        // send data
 		_SPI_TX(pgm_read_byte(data++));
 	_CS_HIGH();
 }
 
-void w25x20cl_erase(uint8_t cmd, uint32_t addr)
+void xflash_erase(uint8_t cmd, uint32_t addr)
 {
 	_CS_LOW();
-	_SPI_TX(cmd);          			     // send command 0x20
-	_SPI_TX(((uint8_t*)&addr)[2]);       // send addr bits 16..23
-	_SPI_TX(((uint8_t*)&addr)[1]);       // send addr bits 8..15
-	_SPI_TX(((uint8_t*)&addr)[0]);       // send addr bits 0..7
+    xflash_send_cmdaddr(cmd, addr);
 	_CS_HIGH();
 }
 
-void w25x20cl_sector_erase(uint32_t addr)
+void xflash_sector_erase(uint32_t addr)
 {
-	return w25x20cl_erase(_CMD_SECTOR_ERASE, addr);
+	return xflash_erase(_CMD_SECTOR_ERASE, addr);
 }
 
-void w25x20cl_block32_erase(uint32_t addr)
+void xflash_block32_erase(uint32_t addr)
 {
-	return w25x20cl_erase(_CMD_BLOCK32_ERASE, addr);
+	return xflash_erase(_CMD_BLOCK32_ERASE, addr);
 }
 
-void w25x20cl_block64_erase(uint32_t addr)
+void xflash_block64_erase(uint32_t addr)
 {
-	return w25x20cl_erase(_CMD_BLOCK64_ERASE, addr);
+	return xflash_erase(_CMD_BLOCK64_ERASE, addr);
 }
 
-void w25x20cl_chip_erase(void)
+void xflash_chip_erase(void)
 {
 	_CS_LOW();
 	_SPI_TX(_CMD_CHIP_ERASE);            // send command 0xc7
@@ -152,33 +177,37 @@ void w25x20cl_chip_erase(void)
 }
 
 
-void w25x20cl_rd_uid(uint8_t* uid)
+void xflash_rd_uid(uint8_t* uid)
 {
 	_CS_LOW();
 	_SPI_TX(_CMD_RD_UID);                // send command 0x4b
 	uint8_t cnt = 4;                     // 4 dummy bytes
-	while (cnt--)                        // receive dummy bytes
-		_SPI_RX();
+	while (cnt--)                        // transmit dummy bytes
+		_SPI_TX(0x00);
 	cnt = 8;                             // 8 bytes UID
 	while (cnt--)                        // receive UID
 		uid[7 - cnt] = _SPI_RX();
 	_CS_HIGH();
 }
 
-int w25x20cl_mfrid_devid(void)
+int xflash_mfrid_devid(void)
 {
 	_CS_LOW();
 	_SPI_TX(_CMD_MFRID_DEVID);           // send command 0x90
 	uint8_t cnt = 3;                     // 3 address bytes
 	while (cnt--)                        // send address bytes
 		_SPI_TX(0x00);
-	uint8_t w25x20cl_mfrid = _SPI_RX();  // receive mfrid
-	uint8_t w25x20cl_devid = _SPI_RX();  // receive devid
+	uint8_t xflash_mfrid = _SPI_RX();  // receive mfrid
+	uint8_t xflash_devid = _SPI_RX();  // receive devid
 	_CS_HIGH();
-	return ((w25x20cl_mfrid == _MFRID) && (w25x20cl_devid == _DEVID));
+	return
+		((xflash_mfrid == _MFRID_W25X20CL) && (xflash_devid == _DEVID_W25X20CL)) ||
+		((xflash_mfrid == _MFRID_GD25Q20C) && (xflash_devid == _DEVID_GD25Q20C));
 }
 
-void w25x20cl_wait_busy(void)
+void xflash_wait_busy(void)
 {
-	while (w25x20cl_rd_status_reg() & W25X20CL_STATUS_BUSY) ;
+	while (xflash_rd_status_reg() & XFLASH_STATUS_BUSY) ;
 }
+
+#endif //XFLASH

+ 59 - 0
Firmware/xflash.h

@@ -0,0 +1,59 @@
+//xflash.h
+#ifndef _XFLASH_H
+#define _XFLASH_H
+
+#include <inttypes.h>
+#include "config.h"
+#include "spi.h"
+
+
+
+#define XFLASH_STATUS_BUSY   0x01
+#define XFLASH_STATUS_WEL    0x02
+#define XFLASH_STATUS_BP0    0x04
+#define XFLASH_STATUS_BP1    0x08
+#define XFLASH_STATUS_TB     0x20
+#define XFLASH_STATUS_SRP    0x80
+
+#define XFLASH_SPI_RATE      0 // fosc/4 = 4MHz
+#define XFLASH_SPCR          SPI_SPCR(XFLASH_SPI_RATE, 1, 1, 1, 0)
+#define XFLASH_SPSR          SPI_SPSR(XFLASH_SPI_RATE)
+
+#define XFLASH_SPI_ENTER() spi_setup(XFLASH_SPCR, XFLASH_SPSR)
+
+#if defined(__cplusplus)
+extern "C" {
+#endif //defined(__cplusplus)
+
+
+extern int8_t xflash_init(void);
+extern void xflash_enable_wr(void);
+extern void xflash_disable_wr(void);
+extern uint8_t xflash_rd_status_reg(void);
+#if 0
+extern void w25x20cl_wr_status_reg(uint8_t val);
+#endif
+extern void xflash_rd_data(uint32_t addr, uint8_t* data, uint16_t cnt);
+
+extern void xflash_sector_erase(uint32_t addr);
+extern void xflash_block32_erase(uint32_t addr);
+extern void xflash_block64_erase(uint32_t addr);
+extern void xflash_chip_erase(void);
+extern void xflash_rd_uid(uint8_t* uid);
+extern void xflash_wait_busy(void);
+
+// write up to a single page of data (256bytes)
+extern void xflash_page_program(uint32_t addr, uint8_t* data, uint16_t cnt);
+
+// write up to a single page of data from program memory
+extern void xflash_page_program_P(uint32_t addr, uint8_t* data, uint16_t cnt);
+
+// xflash_multipage_program: high-level interface for multi-page writes.
+//   Write any amount of data, chunking writes to page boundaries as needed.
+//   Automatically enables writes and waits for completion.
+extern void xflash_multipage_program(uint32_t addr, uint8_t* data, uint16_t cnt);
+
+#if defined(__cplusplus)
+}
+#endif //defined(__cplusplus)
+#endif //_XFLASH_H

+ 109 - 0
Firmware/xflash_dump.cpp

@@ -0,0 +1,109 @@
+#include <stddef.h>
+
+#include <avr/wdt.h>
+#include <avr/interrupt.h>
+
+#include "xflash_dump.h"
+#ifdef XFLASH_DUMP
+#include "asm.h"
+#include "xflash.h"
+#include "Marlin.h" // for softReset
+
+bool xfdump_check_state(dump_crash_reason* reason)
+{
+    uint32_t magic;
+
+    XFLASH_SPI_ENTER();
+    xflash_rd_data(DUMP_OFFSET + offsetof(dump_t, header.magic),
+                   (uint8_t*)&magic, sizeof(magic));
+    if (magic != DUMP_MAGIC)
+        return false;
+
+    if (reason)
+    {
+        xflash_rd_data(DUMP_OFFSET + offsetof(dump_t, header.crash_reason),
+                       (uint8_t*)reason, sizeof(*reason));
+    }
+    return true;
+}
+
+
+void xfdump_reset()
+{
+    XFLASH_SPI_ENTER();
+    xflash_enable_wr();
+    xflash_sector_erase(DUMP_OFFSET + offsetof(dump_t, header.magic));
+    xflash_wait_busy();
+}
+
+
+static void xfdump_erase()
+{
+    for(uint32_t addr = DUMP_OFFSET;
+        addr < DUMP_OFFSET + DUMP_SIZE;
+        addr += 4096)
+    {
+        xflash_enable_wr();
+        xflash_sector_erase(addr);
+        xflash_wait_busy();
+    }
+}
+
+
+static void __attribute__((noinline)) xfdump_dump_core(dump_header_t& hdr, uint32_t addr, uint8_t* buf, uint16_t cnt)
+{
+    XFLASH_SPI_ENTER();
+
+    // start by clearing all sectors (we need all of them in any case)
+    xfdump_erase();
+
+    // sample SP/PC
+    hdr.sp = SP;
+    GETPC(&hdr.pc);
+
+    // write header
+    static_assert(sizeof(hdr) <= 256, "header is larger than a single page write");
+    xflash_enable_wr();
+    xflash_page_program(DUMP_OFFSET, (uint8_t*)&hdr, sizeof(hdr));
+    xflash_wait_busy();
+
+    // write data
+    static_assert(sizeof(dump_t::data) <= RAMEND+1, "dump area size insufficient");
+    xflash_multipage_program(addr, buf, cnt);
+}
+
+
+void xfdump_dump()
+{
+    dump_header_t buf;
+    buf.magic = DUMP_MAGIC;
+    buf.regs_present = false;
+    buf.crash_reason = (uint8_t)dump_crash_reason::manual;
+
+    // write sram only
+    xfdump_dump_core(buf, DUMP_OFFSET + offsetof(dump_t, data.sram),
+                     (uint8_t*)RAMSTART, RAMSIZE);
+}
+
+
+void xfdump_full_dump_and_reset(dump_crash_reason reason)
+{
+    dump_header_t buf;
+    buf.magic = DUMP_MAGIC;
+    buf.regs_present = true;
+    buf.crash_reason = (uint8_t)reason;
+
+    // disable interrupts for a cleaner register dump
+    cli();
+
+    // ensure there's always enough time (with some margin) to dump
+    // dump time on w25x20cl: ~150ms
+    wdt_enable(WDTO_500MS);
+
+    // write all addressable ranges (this will trash bidirectional registers)
+    xfdump_dump_core(buf, DUMP_OFFSET + offsetof(dump_t, data), 0, RAMEND+1);
+
+    // force a reset even sooner
+    softReset();
+}
+#endif

+ 22 - 0
Firmware/xflash_dump.h

@@ -0,0 +1,22 @@
+// XFLASH dumper
+#pragma once
+#include "xflash_layout.h"
+
+enum class dump_crash_reason : uint8_t
+{
+    manual = 0,
+    stack_error,
+    watchdog,
+    bad_isr,
+};
+
+#ifdef XFLASH_DUMP
+void xfdump_reset();    // reset XFLASH dump state
+void xfdump_dump();     // create a new SRAM memory dump
+
+// return true if a dump is present, save type in "reason" if provided
+bool xfdump_check_state(dump_crash_reason* reason = NULL);
+
+// create a new dump containing registers and SRAM, then reset
+void xfdump_full_dump_and_reset(dump_crash_reason crash = dump_crash_reason::manual);
+#endif

+ 51 - 0
Firmware/xflash_layout.h

@@ -0,0 +1,51 @@
+// XFLASH memory layout
+#pragma once
+#include <stdint.h>
+#include "bootapp.h" // for RAMSIZE
+#include "config.h"
+
+#define XFLASH_SIZE 0x40000ul // size of XFLASH
+#define LANG_OFFSET 0x0       // offset for language data
+
+#ifndef XFLASH_DUMP
+#define LANG_SIZE   XFLASH_SIZE
+#else
+
+#define DUMP_MAGIC  0x55525547ul
+
+struct dump_header_t
+{
+    // start with a magic value to indicate the presence of a dump, so that clearing
+    // a single page is sufficient for resetting the state
+    uint32_t magic;
+
+    uint8_t regs_present; // true when the lower segment containing registers is present
+    uint8_t crash_reason; // uses values from dump_crash_source
+
+    uint32_t pc;          // PC nearby the crash location
+    uint16_t sp;          // SP nearby the crash location
+};
+
+struct dump_data_t
+{
+    // contiguous region containing all addressable ranges
+    uint8_t regs[RAMSTART];
+    uint8_t sram[RAMSIZE];
+};
+
+struct dump_t
+{
+    struct dump_header_t header;
+
+    // data is page aligned (no real space waste, due to the larger
+    // alignment required for the whole dump)
+    struct dump_data_t __attribute__((aligned(256))) data;
+};
+
+// dump offset must be aligned to lower 4kb sector boundary
+#define DUMP_OFFSET ((XFLASH_SIZE - sizeof(dump_t)) & ~0xFFFul)
+
+#define DUMP_SIZE   (XFLASH_SIZE - DUMP_OFFSET) // effective dump size area
+#define LANG_SIZE   DUMP_OFFSET                 // available language space
+
+#endif

+ 0 - 0
Firmware/xyzcal.cpp


Some files were not shown because too many files changed in this diff