Merge branch 'prusa3d:MK3' into lcd-optimisation-gudni

This commit is contained in:
Guðni Már Gilbert 2022-02-06 09:54:58 +00:00 committed by GitHub
commit c6ecc0dfc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 6024 additions and 3786 deletions

View File

@ -6,14 +6,14 @@ labels: bug
assignees: '' 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. 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 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** - [e.g. MMU2S, MMU2, MMU1]
**MMU upgrade firmware version [e.g. 1.0.6, 1.0.6-RC2, ...] **MMU upgrade firmware version** - [e.g. 1.0.6, 1.0.6-RC2, ...]
**SD card or USB/Octoprint** **SD card or USB/Octoprint**
Please let us know if you print via SD card or USB/Octoprint Please let us know if you print via SD card or USB/Octoprint

17
.github/ISSUE_TEMPLATE/community.md vendored Normal file
View File

@ -0,0 +1,17 @@
---
name: Community
about: Related to "Community made" features
title: "[Community made] <Enter comprehensive title>"
labels: community_made
assignees: ''
---
Prusa Research will NOT follow up these issues!
The maintainers of the "Community made" feature should/will react.
Please, before you create a new "Community made" ticket, please make sure you searched in open and closed issues and couldn't find anything that matches.
**Which Community made feature do you want to address?**
**What is your request/question/suggestion?**

4
.gitignore vendored
View File

@ -1,7 +1,9 @@
.settings .settings
.project .project
.cproject .cproject
.vscode
Debug Debug
__pycache__
Firmware/Configuration_prusa.h Firmware/Configuration_prusa.h
Firmware/Doc Firmware/Doc
/Firmware/.vs/Firmware/v14 /Firmware/.vs/Firmware/v14
@ -52,3 +54,5 @@ Firmware/Doc
/Firmware/Firmware.vcxproj /Firmware/Firmware.vcxproj
/Firmware/Configuration_prusa_bckp.h /Firmware/Configuration_prusa_bckp.h
/Firmware/variants/printers.h /Firmware/variants/printers.h
Configuration.tmp
config.tmp

View File

@ -1,4 +1,4 @@
dist: trusty dist: focal
before_install: before_install:
- sudo apt-get install -y ninja-build - sudo apt-get install -y ninja-build
# Arduino IDE adds a lot of noise caused by network traffic, trying to firewall it off # Arduino IDE adds a lot of noise caused by network traffic, trying to firewall it off

36
Community_made.md Normal file
View File

@ -0,0 +1,36 @@
# Community made
## Prusa-Firmware build
- `PF-build.sh`
- Maintainers: **@3d-gussner**
- Co-maintainers:
- Contributors: **@mkbel**, **@ropaha**, **@deliopoulos**, **@DRracer**, **wavexx**, **@leptun**, **@andrewluebke**, **@kuhnmarek**
- [X] **Active** since February 2019
- [X] **Maintained** since January 2019
### How-to use PF-build.sh
Start `./PF-build.sh` and follow the instructions
Help `./PF-build.sh -h`
# MK404 Simulator
## MK404-build.sh
**MK404 is a community 3d printer simulator created by @vintagepc**
Please checkout and support his github repository [MK404](https://github.com/vintagepc/MK404) and the [MK404 Wiki](https://github.com/vintagepc/MK404/wiki)
At this moment the `MK404-build.sh` script is only supported on Linux
- `MK404-build.sh`
- Maintainers: **@3d-gussner**
- Co-maintainers:
- Contributors:
- [X] **Active** since August 2021
- [X] **Maintained** since August 2021
### How-to use MK404-build.sh
After compiling with `PF-build.sh` you get the option to start the `MK404` simulator with the fresh compiled firmware. (Linux only at this moment)
Help `./MK404-build.sh -h`
## Translations
- see [/lang/Community_made_translations.md](https://github.com/prusa3d/Prusa-Firmware/blob/MK3/lang/Community_made_translations.md)

View File

@ -18,10 +18,16 @@ extern PGM_P sPrinterName;
// Firmware version // Firmware version
#define FW_MAJOR 3 #define FW_MAJOR 3
#define FW_MINOR 10 #define FW_MINOR 10
#define FW_REVISION 0 #define FW_REVISION 1
#define FW_VERSION STR(FW_MAJOR) "." STR(FW_MINOR) "." STR(FW_REVISION) //#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 4481 #define FW_COMMIT_NR 4697
// FW_VERSION_UNKNOWN means this is an unofficial build. // FW_VERSION_UNKNOWN means this is an unofficial build.
// The firmware should only be checked into github with this symbol. // The firmware should only be checked into github with this symbol.

View File

@ -236,10 +236,12 @@ void update_currents();
void get_coordinates(); void get_coordinates();
void prepare_move(); void prepare_move();
void kill(const char *full_screen_message = NULL, unsigned char id = 0); void kill(const char *full_screen_message = NULL, unsigned char id = 0);
void Stop();
bool IsStopped();
void finishAndDisableSteppers(); void finishAndDisableSteppers();
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 //put an ASCII command at the end of the current buffer, read from flash
#define enquecommand_P(cmd) enquecommand(cmd, true) #define enquecommand_P(cmd) enquecommand(cmd, true)

View File

@ -294,7 +294,7 @@ uint8_t newFanSpeed = 0;
bool powersupply = true; bool powersupply = true;
#endif #endif
bool cancel_heatup = false ; bool cancel_heatup = false;
int8_t busy_state = NOT_BUSY; int8_t busy_state = NOT_BUSY;
static long prev_busy_signal_ms = -1; static long prev_busy_signal_ms = -1;
@ -1011,7 +1011,8 @@ static void fw_crash_init()
eeprom_read_byte((uint8_t*)EEPROM_FW_CRASH_FLAG) != 0xFF) eeprom_read_byte((uint8_t*)EEPROM_FW_CRASH_FLAG) != 0xFF)
{ {
lcd_show_fullscreen_message_and_wait_P( lcd_show_fullscreen_message_and_wait_P(
_i("FIRMWARE CRASH!\n" _i("FW crash detected! "
"You can continue printing. "
"Debug data available for analysis. " "Debug data available for analysis. "
"Contact support to submit details.")); "Contact support to submit details."));
} }
@ -6751,7 +6752,7 @@ Sigma_Exit:
target_direction = isHeatingBed(); // true if heating, false if cooling target_direction = isHeatingBed(); // true if heating, false if cooling
KEEPALIVE_STATE(NOT_BUSY); 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. if(( _millis() - codenum) > 1000 ) //Print Temp Reading every 1 second while heating up.
{ {
@ -6887,7 +6888,7 @@ Sigma_Exit:
- `X` - X axis - `X` - X axis
- `Y` - Y axis - `Y` - Y axis
- `Z` - Z 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> ### 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) Equal to M84 (compatibility)
@ -8625,7 +8626,7 @@ Sigma_Exit:
*/ */
case 910: case 910:
{ {
tmc2130_init(); tmc2130_init(TMCInitParams(false, FarmOrUserECool()));
} }
break; break;
@ -8692,7 +8693,7 @@ Sigma_Exit:
{ {
tmc2130_mode = TMC2130_MODE_NORMAL; tmc2130_mode = TMC2130_MODE_NORMAL;
update_mode_profile(); update_mode_profile();
tmc2130_init(); tmc2130_init(TMCInitParams(false, FarmOrUserECool()));
} }
break; break;
@ -8704,7 +8705,7 @@ Sigma_Exit:
{ {
tmc2130_mode = TMC2130_MODE_SILENT; tmc2130_mode = TMC2130_MODE_SILENT;
update_mode_profile(); update_mode_profile();
tmc2130_init(); tmc2130_init(TMCInitParams(false, FarmOrUserECool()));
} }
break; break;
@ -9329,7 +9330,7 @@ Sigma_Exit:
#ifdef XFLASH_DUMP #ifdef XFLASH_DUMP
/*! /*!
### D20 - Generate an offline crash 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. Generate a crash dump for later retrival.
#### Usage #### Usage
@ -9348,7 +9349,7 @@ Sigma_Exit:
}; };
/*! /*!
### D21 - Print crash dump to serial ### 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. Output the complete crash dump (if present) to the serial.
#### Usage #### Usage
@ -9363,7 +9364,7 @@ Sigma_Exit:
}; };
/*! /*!
### D22 - Clear crash dump state ### 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. Clear an existing internal crash dump.
#### Usage #### Usage
@ -9377,7 +9378,7 @@ Sigma_Exit:
#ifdef EMERGENCY_SERIAL_DUMP #ifdef EMERGENCY_SERIAL_DUMP
/*! /*!
### D23 - Request emergency dump on serial ### 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. 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. When online dumps are enabled, the FW will dump memory on the serial before resetting.
#### Usage #### Usage
@ -10157,6 +10158,32 @@ void kill(const char *full_screen_message, unsigned char id)
} // Wait for reset } // 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 // 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 // In addition to stopping the print, this prevents subsequent G[0-3] commands to be
@ -10169,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. // the addition of disabling the headers) could allow true recovery in the future.
void Stop() void Stop()
{ {
// Keep disabling heaters
disable_heater(); disable_heater();
// Call the regular stop function if that's the first time during a new print
if(Stopped == false) { if(Stopped == false) {
Stopped = true; Stopped = true;
lcd_print_stop(); lcd_print_stop();
Stopped_gcode_LastN = gcode_LastN; // Save last g_code for restart 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_ERROR_START;
SERIAL_ERRORLNRPGM(MSG_ERR_STOPPED); SERIAL_ERRORLNRPGM(MSG_ERR_STOPPED);
LCD_MESSAGERPGM(_T(MSG_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; }; bool IsStopped() { return Stopped; };

View File

@ -1030,7 +1030,6 @@ void CardReader::presort() {
lcd_update(2); lcd_update(2);
KEEPALIVE_STATE(NOT_BUSY); KEEPALIVE_STATE(NOT_BUSY);
lcd_timeoutToStatus.start();
} }
void CardReader::flush_presort() { void CardReader::flush_presort() {

View File

@ -1381,33 +1381,15 @@ void temp_runaway_check(int _heater_id, float _target_temperature, float _curren
void temp_runaway_stop(bool isPreheat, bool isBed) void temp_runaway_stop(bool isPreheat, bool isBed)
{ {
cancel_heatup = true; disable_heater();
quickStop(); Sound_MakeCustom(200,0,true);
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(); if (isPreheat)
disable_x();
disable_y();
disable_e0();
disable_e1();
disable_e2();
manage_heater();
lcd_update(0);
Sound_MakeCustom(200,0,true);
if (isPreheat)
{ {
Stop(); lcd_setalertstatuspgm(isBed? PSTR("BED PREHEAT ERROR") : PSTR("PREHEAT ERROR"), LCD_STATUS_CRITICAL);
isBed ? LCD_ALERTMESSAGEPGM("BED PREHEAT ERROR") : LCD_ALERTMESSAGEPGM("PREHEAT ERROR");
SERIAL_ERROR_START; 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 #ifdef EXTRUDER_ALTFAN_DETECT
altfanStatus.altfanOverride = 1; //full speed altfanStatus.altfanOverride = 1; //full speed
#endif //EXTRUDER_ALTFAN_DETECT #endif //EXTRUDER_ALTFAN_DETECT
@ -1418,16 +1400,16 @@ void temp_runaway_stop(bool isPreheat, bool isBed)
#else //FAN_SOFT_PWM #else //FAN_SOFT_PWM
analogWrite(FAN_PIN, 255); analogWrite(FAN_PIN, 255);
#endif //FAN_SOFT_PWM #endif //FAN_SOFT_PWM
fanSpeed = 255; fanSpeed = 255;
delayMicroseconds(2000);
} }
else 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; SERIAL_ERROR_START;
isBed ? SERIAL_ERRORLNPGM(" HEATBED THERMAL RUNAWAY") : SERIAL_ERRORLNPGM(" HOTEND THERMAL RUNAWAY"); isBed ? SERIAL_ERRORLNPGM(" HEATBED THERMAL RUNAWAY") : SERIAL_ERRORLNPGM(" HOTEND THERMAL RUNAWAY");
} }
Stop();
} }
#endif #endif
@ -1483,13 +1465,12 @@ uint8_t last_alert_sent_to_lcd = LCDALERT_NONE;
//! update the current temperature error message //! update the current temperature error message
//! @param type short error abbreviation (PROGMEM) //! @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 temp_update_messagepgm(const char* PROGMEM type, void (*func)(const char*) = lcd_updatestatus)
{ {
char msg[LCD_WIDTH]; char msg[LCD_WIDTH];
strcpy_P(msg, PSTR("Err: ")); strcpy_P(msg, PSTR("Err: "));
strcat_P(msg, type); strcat_P(msg, type);
(*func)(msg); lcd_setalertstatus(msg, LCD_STATUS_CRITICAL);
} }
//! signal a temperature error on both the lcd and serial //! signal a temperature error on both the lcd and serial
@ -1497,7 +1478,7 @@ void temp_update_messagepgm(const char* PROGMEM type, void (*func)(const char*)
//! @param e optional extruder index for hotend errors //! @param e optional extruder index for hotend errors
void temp_error_messagepgm(const char* PROGMEM type, uint8_t e = EXTRUDERS) 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; SERIAL_ERROR_START;

View File

@ -220,7 +220,7 @@ FORCE_INLINE bool isCoolingBed() {
#define CHECK_ALL_HEATERS (checkAllHotends()||(target_temperature_bed!=0)) #define CHECK_ALL_HEATERS (checkAllHotends()||(target_temperature_bed!=0))
int getHeaterPower(int heater); int getHeaterPower(int heater);
void disable_heater(); void disable_heater(); // Disable all heaters
void updatePID(); void updatePID();

View File

@ -1399,7 +1399,6 @@ static void lcd_cooldown()
setAllTargetHotends(0); setAllTargetHotends(0);
setTargetBed(0); setTargetBed(0);
fanSpeed = 0; fanSpeed = 0;
eFilamentAction = FilamentAction::None;
lcd_return_to_status(); lcd_return_to_status();
} }
@ -2317,8 +2316,10 @@ void mFilamentItem(uint16_t nTemp, uint16_t nTempBed)
} }
} }
lcd_set_cursor(0, 0); if (bFilamentWaitingFlag) {
lcdui_print_temp(LCD_STR_THERMOMETER[0], (int) degHotend(0), (int) degTargetHotend(0)); lcd_set_cursor(0, 0);
lcdui_print_temp(LCD_STR_THERMOMETER[0], (int) degHotend(0), (int) degTargetHotend(0));
}
if (lcd_clicked()) if (lcd_clicked())
{ {
@ -6997,24 +6998,11 @@ static void lcd_sd_updir()
void lcd_print_stop() void lcd_print_stop()
{ {
if (!card.sdprinting) { if (!card.sdprinting) {
SERIAL_ECHOLNRPGM(MSG_OCTOPRINT_CANCEL); // for Octoprint SERIAL_ECHOLNRPGM(MSG_OCTOPRINT_CANCEL); // for Octoprint
} }
cmdqueue_serial_disabled = false; //for when canceling a print with a fancheck 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 #ifdef MESH_BED_LEVELING
mbl.active = false; //also prevents undoing the mbl compensation a second time in the second planner_abort_hard() mbl.active = false; //also prevents undoing the mbl compensation a second time in the second planner_abort_hard()
#endif #endif
@ -7025,11 +7013,11 @@ void lcd_print_stop()
pause_time = 0; pause_time = 0;
save_statistics(total_filament_used, t); save_statistics(total_filament_used, t);
// reset current command
lcd_commands_step = 0; lcd_commands_step = 0;
lcd_commands_type = LcdCommands::Idle; lcd_commands_type = LcdCommands::Idle;
lcd_cooldown(); //turns off heaters and fan; goes to status screen. 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. current_position[Z_AXIS] += 10; //lift Z.
plan_buffer_line_curposXYZE(manual_feedrate[Z_AXIS] / 60); plan_buffer_line_curposXYZE(manual_feedrate[Z_AXIS] / 60);
@ -8511,7 +8499,7 @@ static bool check_file(const char* filename) {
cmdqueue_serial_disabled = false; cmdqueue_serial_disabled = false;
card.printingHasFinished(); card.printingHasFinished();
strncpy_P(lcd_status_message, _T(WELCOME_MSG), LCD_WIDTH); lcd_setstatuspgm(_T(WELCOME_MSG));
lcd_finishstatus(); lcd_finishstatus();
return result; return result;
} }
@ -8718,18 +8706,22 @@ void lcd_updatestatus(const char *message){
lcd_draw_update = 1; lcd_draw_update = 1;
} }
void lcd_setalertstatuspgm(const char* message) void lcd_setalertstatuspgm(const char* message, uint8_t severity)
{ {
lcd_setstatuspgm(message); if (severity > lcd_status_message_level) {
lcd_status_message_level = 1; lcd_updatestatuspgm(message);
lcd_return_to_status(); 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); if (severity > lcd_status_message_level) {
lcd_status_message_level = 1; lcd_updatestatus(message);
lcd_return_to_status(); lcd_status_message_level = severity;
lcd_return_to_status();
}
} }
void lcd_reset_alert_level() void lcd_reset_alert_level()
@ -8836,6 +8828,7 @@ void menu_lcd_lcdupdate_func(void)
LCD_MESSAGERPGM(_T(WELCOME_MSG)); LCD_MESSAGERPGM(_T(WELCOME_MSG));
bMain=false; // flag (i.e. 'fake parameter') for 'lcd_sdcard_menu()' function bMain=false; // flag (i.e. 'fake parameter') for 'lcd_sdcard_menu()' function
menu_submenu(lcd_sdcard_menu); menu_submenu(lcd_sdcard_menu);
lcd_timeoutToStatus.start();
} }
else else
{ {

View File

@ -11,13 +11,20 @@ extern void menu_lcd_lcdupdate_func(void);
void ultralcd_init(); void ultralcd_init();
void lcd_setstatus(const char* message); void lcd_setstatus(const char* message);
void lcd_setstatuspgm(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 //! return to the main status screen and display the alert message
//! Beware - it has sideeffects: //! Beware - it has sideeffects:
//! - always returns the display to the main status screen //! - always returns the display to the main status screen
//! - always makes lcd_reset (which is slow and causes flicker) //! - 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) //! - does not update the message if there is one with the same (or higher) severity present
void lcd_setalertstatus(const char* message); void lcd_setalertstatus(const char* message, uint8_t severity = LCD_STATUS_ALERT);
void lcd_setalertstatuspgm(const char* message); void lcd_setalertstatuspgm(const char* message, uint8_t severity = LCD_STATUS_ALERT);
//! only update the alert message on the main status screen //! only update the alert message on the main status screen
//! has no sideeffects, may be called multiple times //! has no sideeffects, may be called multiple times
void lcd_updatestatus(const char *message); void lcd_updatestatus(const char *message);

546
MK404-build.sh Executable file
View File

@ -0,0 +1,546 @@
#!/bin/bash
# This bash script is used to compile automatically and run the MK404 simulator
#
# Supported OS: Linux64 bit
#
# Linux:
# Linux Ubuntu
# 1. Follow these instructions
# 2. Open Ubuntu bash and get latest updates with 'sudo apt-get update'
# 3. Install latest updates with 'sudo apt-get upgrade'
#
#
# Version: 1.0.0-Build_13
# Change log:
# 11 Feb 2021, 3d-gussner, Inital
# 11 Feb 2021, 3d-gussner, Optional flags to check for updates
# 12 Feb 2021, 3d-gussner, Update cmake
# 13 Feb 2021, 3d-gussner, Auto build SD cards
# 18 Jun 2021, 3d-gussner, Documentation and version number
# 18 Jun 2021, 3d-gussner, Added some arguments and checks
# 18 Jun 2021, 3d-gussner, Default extrusion graphics to line. Thanks @vintagepc point it out
# 18 Jun 2021, 3d-gussner, Added -g 3 and 4 for more details extrusion lines
# 18 Jun 2021, 3d-gussner, Check for updates is default. Fix update if internet connection is lost.
# 21 Jun 2021, 3d-gussner, Change board_flash argument to 'y' and firmware_version to 'f'
#### Start: Failures
failures()
{
case "$1" in
0) echo "$(tput setaf 2)MK404-build.sh finished with success$(tput sgr0)" ;;
2) echo "$(tput setaf 1)Unsupported OS: Linux $(uname -m)" ; echo "Please refer to the notes of MK404-build.sh$(tput sgr0)" ; exit 2 ;;
3) echo "$(tput setaf 1)This script doesn't support your Operating system!"; echo "Please use Linux 64-bit"; echo "Read the notes of MK404-build.sh$(tput sgr0)" ; exit 2 ;;
4) echo "$(tput setaf 1)Some packages are missing please install these!$(tput sgr0)" ; exit 4 ;;
5) echo "$(tput setaf 1)Wrong printer chosen.$(tput sgr0) Following Printers are supported: MK25, MK25S, MK3 and MK3S" ; exit 5 ;;
6) echo "$(tput setaf 1)Unsupported board flash size chosen.$(tput sgr0) Only '256', '384', '512', '1024' and '32M' are allowed." ; exit 6 ;;
7) echo "$(tput setaf 1)Unsupported board mem size chosen.$(tput sgr0) Only '8', '16', '32' and '64' are allowed." ; exit 7 ;;
8) echo "$(tput setaf 1)No firmware version file selected!$(tput sgr0)" ; echo "Add argument -f with path and hex filename to start MK404" ; exit 8 ;;
9) echo "$(tput setaf 1)Tried to determine MK404 printer from hex file, but failed!$(tput sgr0)" ; "Add argument -p with 'MK25', 'MK25S', 'MK3' or 'MK3S' to start MK404" ; exit 9 ;;
10) echo "$(tput setaf 1)Missing printer$(tput sgr0)" ; exit 10 ;;
esac
}
#### End: Failures
#### Start: Check options
##check_options()
##{
while getopts c:f:g:m:n:p:u:x:y:?h flag
do
case "${flag}" in
c) check_flag=${OPTARG};;
f) firmware_version_flag=${OPTARG};;
g) mk404_graphics_flag=${OPTARG};;
h) help_flag=1;;
m) mk404_flag=${OPTARG};;
n) new_build_flag=${OPTARG};;
p) mk404_printer_flag=${OPTARG};;
u) update_flag=${OPTARG};;
x) board_mem_flag=${OPTARG};;
y) board_flash_flag=${OPTARG};;
?) help_flag=1;;
esac
done
#Debug echos
#echo "c: $check_flag"
#echo "f: $firmware_version_flag"
#echo "g: $mk404_graphics_flag"
#echo "m: $mk404_flag"
#echo "n: $new_build_flag"
#echo "p: $mk404_printer_flag"
#echo "u: $update_flag"
#echo "x: $board_mem_flag"
#echo "y: $board_flash_flag"
# '?' 'h' argument usage and help
if [ "$help_flag" == "1" ] ; then
echo "***************************************"
echo "* MK404-build.sh Version: 1.0.0-Build_13 *"
echo "***************************************"
echo "Arguments:"
echo "$(tput setaf 2)-c$(tput sgr0) Check for update"
echo "$(tput setaf 2)-f$(tput sgr0) Prusa-Firmware version"
echo "$(tput setaf 2)-g$(tput sgr0) Start MK404 graphics"
echo "$(tput setaf 2)-h$(tput sgr0) Help"
echo "$(tput setaf 2)-m$(tput sgr0) Start MK404 sim"
echo "$(tput setaf 2)-n$(tput sgr0) Force new build"
echo "$(tput setaf 2)-p$(tput sgr0) MK404 Printer"
echo "$(tput setaf 2)-u$(tput sgr0) Update MK404"
echo "$(tput setaf 2)-x$(tput sgr0) Board memory size"
echo "$(tput setaf 2)-y$(tput sgr0) Board flash size"
echo "$(tput setaf 2)-?$(tput sgr0) Help"
echo
echo "Brief USAGE:"
echo " $(tput setaf 2)./MK404-build.sh$(tput sgr0) [-c] [-f] [-g] [-m] [-n] [-p] [-u] [-v] [-x] [-h] [-?]"
echo
echo " -c : '$(tput setaf 2)0$(tput sgr0)' no, '$(tput setaf 2)1$(tput sgr0)' yes"
echo " -f : '$(tput setaf 2)path+file name$(tput sgr0)'"
echo " -g : '$(tput setaf 2)0$(tput sgr0)' no, '$(tput setaf 2)1$(tput sgr0)' lite, '$(tput setaf 2)2$(tput sgr0)' fancy, '$(tput setaf 2)3$(tput sgr0)' lite with Quad_HR, '$(tput setaf 2)4$(tput sgr0)' fancy with Quad_HR"
echo " -m : '$(tput setaf 2)0$(tput sgr0)' no, '$(tput setaf 2)1$(tput sgr0)' yes '$(tput setaf 2)2$(tput sgr0)' with MMU2"
echo " -n : '$(tput setaf 2)0$(tput sgr0)' no, '$(tput setaf 2)1$(tput sgr0)' yes"
echo " -p : '$(tput setaf 2)MK25$(tput sgr0)', '$(tput setaf 2)MK25S$(tput sgr0)', '$(tput setaf 2)MK3$(tput sgr0)' or '$(tput setaf 2)MK3S$(tput sgr0)'"
echo " -u : '$(tput setaf 2)0$(tput sgr0)' no, '$(tput setaf 2)1$(tput sgr0)' yes '"
echo " -x : '$(tput setaf 2)8$(tput sgr0)',$(tput setaf 2)16$(tput sgr0)',$(tput setaf 2)32$(tput sgr0)' or '$(tput setaf 2)64$(tput sgr0)' Kb."
echo " -y : '$(tput setaf 2)256$(tput sgr0)','$(tput setaf 2)384$(tput sgr0)','$(tput setaf 2)512$(tput sgr0)','$(tput setaf 2)1024$(tput sgr0)''$(tput setaf 2)32M$(tput sgr0)'"
echo
echo "Example:"
echo " $(tput setaf 2)./MK404-build.sh -f 1$(tput sgr0)"
echo " Will force an update and rebuild the MK404 SIM"
echo
echo " $(tput setaf 2)./MK404-build.sh -m 1 -g 1 -f ../../../../Prusa-Firmware/PF-build-hex/FW3100-Build4481/BOARD_EINSY_1_0a/FW3100-Build4481-1_75mm_MK3S-EINSy10a-E3Dv6full.hex$(tput sgr0)"
echo " Will start MK404 with Prusa_MK3S and Prusa-Firmware 3.10.0-Build4481"
exit 1
fi
#Check MK404 agruments
#Set Check for updates as default
check_flag=1
#Start: Check mk404_printer_flag
if [ ! -z $mk404_printer_flag ]; then
if [[ "$mk404_printer_flag" == "MK3" || "$mk404_printer_flag" == "MK3S" || "$mk404_printer_flag" == "MK25" || "$mk404_printer_flag" == "MK25S" ]]; then
MK404_PRINTER_TEMP=$mk404_printer_flag
else
failures 5
fi
fi
#End: Check mk404_printer_flag
#Start: Check if Build is selected with argument '-f'
if [ ! -z "$board_flash_flag" ] ; then
if [ "$board_flash_flag" == "256" ] ; then
BOARD_FLASH="0x3FFFF"
echo "Board flash size : $board_flash_flag Kb, $BOARD_FLASH (hex)"
elif [ "$board_flash_flag" == "384" ] ; then
BOARD_FLASH="0x5FFFF"
echo "Board flash size : $board_flash_flag Kb, $BOARD_FLASH (hex)"
elif [ "$board_flash_flag" == "512" ] ; then
BOARD_FLASH="0x7FFFF"
echo "Board flash size : $board_flash_flag Kb, $BOARD_FLASH (hex)"
elif [ "$board_flash_flag" == "1024" ] ; then
BOARD_FLASH="0xFFFFF"
echo "Board flash size : $board_flash_flag Kb, $BOARD_FLASH (hex)"
elif [[ "$board_flash_flag" == "32M" || "$board_flash_flag" == "32768" ]] ; then
BOARD_FLASH="0x1FFFFFF"
echo "Board flash size : 32 Mb, $BOARD_FLASH (hex)"
else
failures 6
fi
fi
#End: Check if Build is selected with argument '-f'
#Start: Check if Build is selected with argument '-x'
if [ ! -z "$board_mem_flag" ] ; then
if [ "$board_mem_flag" == "8" ] ; then
BOARD_MEM="0x21FF"
echo "Board mem size : $board_mem_flag Kb, $BOARD_MEM (hex)"
elif [ "$board_mem_flag" == "16" ] ; then
BOARD_MEM="0x3DFF"
echo "Board mem size : $board_mem_flag Kb, $BOARD_MEM (hex)"
elif [ "$board_mem_flag" == "32" ] ; then
BOARD_MEM="0x7DFF"
echo "Board mem size : $board_mem_flag Kb, $BOARD_MEM (hex)"
elif [ "$board_mem_flag" == "64" ] ; then
BOARD_MEM="0xFFFF"
echo "Board mem size : $board_mem_flag Kb, $BOARD_MEM (hex)"
else
failures 7
fi
fi
#End: Check if Build is selected with argument '-x'
#Start: Check if new build is selected
if [ "$new_build_flag" == "1" ]; then
check_flag=1
update_flag=1
fi
if [ "$update_flag" == "1" ]; then
check_flag=1
fi
#End: Check if new build is selected
# Prepare run MK404
#Check MK404_Printer
if [ ! -z $firmware_version_flag ]; then
MK404_PRINTER_TEMP=$(echo $firmware_version_flag | sed 's/\(.*\)\///' | grep 'MK3')
if [ ! -z $MK404_PRINTER_TEMP ]; then
MK404_PRINTER=MK3
fi
MK404_PRINTER_TEMP=$(echo $firmware_version_flag | sed 's/\(.*\)\///' | grep 'MK3S')
if [ ! -z $MK404_PRINTER_TEMP ]; then
MK404_PRINTER=MK3S
fi
MK404_PRINTER_TEMP=$(echo $firmware_version_flag | sed 's/\(.*\)\///' | grep 'MK25')
if [ ! -z $MK404_PRINTER_TEMP ]; then
MK404_PRINTER=MK25
fi
MK404_PRINTER_TEMP=$(echo $firmware_version_flag | sed 's/\(.*\)\///' | grep 'MK25S')
if [ ! -z $MK404_PRINTER_TEMP ]; then
MK404_PRINTER=MK25S
fi
else
failures 8
fi
if [ -z "$MK404_PRINTER" ]; then
failures 9
fi
if [ ! -z $mk404_printer_flag ]; then
if [ "$mk404_printer_flag" != "$MK404_PRINTER" ]; then
echo "$(tput setaf 3)You defined a different printer type than the firmware!"
echo "This can cause unexpected issues.$(tput sgr 0)"
echo
PS3="Select $(tput setaf 2)printer$(tput sgr 0) you want to use."
select which in "$(tput setaf 2)$MK404_PRINTER$(tput sgr 0)" "$mk404_printer_flag"; do
case $which in
$MK404_PRINTER)
echo "Set $MK404_PRINTER as printer"
break
;;
$mk404_printer_flag)
echo "Set $(tput setaf 3)$mk404_printer_flag$(tput sgr 0) as printer"
echo "$(tput setaf 3)This firmware file isn't correct for this printer!!!$(tput sgr 0)"
echo
MK404_PRINTER=$mk404_printer_flag
read -p "Press Enter to continue."
break
;;
*)
break
;;
esac
done
fi
fi
if [ -z $MK404_PRINTER ]; then
failures 10
fi
if [[ "$MK404_PRINTER" == "MK25" || "$MK404_PRINTER" == "MK25S" ]]; then
MK404_PRINTER="${MK404_PRINTER}_mR13"
else
if [ "$mk404_flag" == "2" ]; then # Check if MMU2 is selected only for MK3/S
MK404_PRINTER="${MK404_PRINTER}MMU2"
fi
fi
# Run MK404 with 'debugcore' and/or 'bootloader-file'
if [[ ! -z $MK404_DEBUG && "$MK404_DEBUG" == "atmega404" || ! -z $BOARD_MEM && "$BOARD_MEM" == "0xFFFF" ]]; then
MK404_options="--debugcore"
fi
if [[ ! -z $MK404_DEBUG && "$MK404_DEBUG" == "atmega404_no_bootloader" || ! -z $BOARD_FLASH && "$BOARD_FLASH" != "0x3FFFF" ]]; then
MK404_options='--debugcore --bootloader-file ""'
fi
# Run MK404 with graphics
if [ ! -z "$mk404_graphics_flag" ]; then
if [ ! -z "$MK404_options" ]; then
MK404_options="${MK404_options} -g "
else
MK404_options=" -g "
fi
if [[ "$mk404_graphics_flag" == "1" || "$mk404_graphics_flag" == "lite" || "$mk404_graphics_flag" == "3" ]]; then
MK404_options="${MK404_options}lite"
elif [[ "$mk404_graphics_flag" == "2" || "$mk404_graphics_flag" == "fancy" || "$mk404_graphics_flag" == "4" ]]; then
MK404_options="${MK404_options}fancy"
else
echo "$(tput setaf 1)Unsupported MK404 graphics option $mk404_graphics_flag$(tput sgr 0)"
fi
if [[ "$mk404_graphics_flag" == "3" || "$mk404_graphics_flag" == "4" ]]; then
MK404_options="${MK404_options} --colour-extrusion --extrusion Quad_HR"
else
MK404_options="${MK404_options} --extrusion Line"
fi
fi
if [ ! -z $firmware_version_flag ]; then
MK404_firmware_file=" -f $firmware_version_flag"
fi
#End: Check MK404 agruments
##}
#### End: Check for options/flags
#### Start: Check if OSTYPE is supported
check_OS()
{
OS_FOUND=$( command -v uname)
case $( "${OS_FOUND}" | tr '[:upper:]' '[:lower:]') in
linux*)
TARGET_OS="linux"
;;
*)
TARGET_OS='unknown'
;;
esac
# Linux
if [ $TARGET_OS == "linux" ]; then
if [ $(uname -m) == "x86_64" ]; then
echo "$(tput setaf 2)Linux 64-bit found$(tput sgr0)"
Processor="64"
#elif [[ $(uname -m) == "i386" || $(uname -m) == "i686" ]]; then
# echo "$(tput setaf 2)Linux 32-bit found$(tput sgr0)"
# Processor="32"
else
failures 2
fi
else
failures 3
fi
sleep 2
}
#### End: Check if OSTYPE is supported
#### Start: Check MK404 dependencies
check_packages()
{
packages=(
"libelf-dev"
"gcc-7"
"gcc-avr"
"libglew-dev"
"freeglut3-dev"
"libsdl-sound1.2-dev"
"libpng-dev"
"cmake"
"zip"
"wget"
"git"
"build-essential"
"lcov"
"mtools"
)
for check_package in ${packages[@]}; do
if dpkg-query -W -f'${db:Status-Abbrev}\n' $check_package 2>/dev/null \
| grep -q '^.i $'; then
echo "$(tput setaf 2)$check_package: Installed$(tput sgr0)"
else
echo "$(tput setaf 1)$check_package: Not installed use $(tput setaf 3)'sudo apt install $check_package'$(tput setaf 1) to install missing package$(tput sgr0)"
not_installed=1;
fi
done
if [ "$not_installed" = "1" ]; then
failures 4
fi
}
#### End: Check MK404 dependencies
#### Start: Set build environment
set_build_env_variables()
{
MK404_SCRIPT_PATH="$( cd "$(dirname "$0")" ; pwd -P )"
MK404_URL="https://github.com/vintagepc/MK404.git"
MK404_owner="vintagepc"
MK404_project="MK404"
MK404_PATH="$MK404_SCRIPT_PATH/../MK404/master"
MK404_BUILD_PATH="$MK404_PATH/build"
}
#### End: Set build environment
#### Start: List few useful data
output_useful_data()
{
echo
echo "Script path :" $MK404_SCRIPT_PATH
echo "OS :" $TARGET_OS
echo ""
echo "MK404 path :" $MK404_PATH
}
#### End: List few useful data
#### Start: Clone MK404 if needed
get_MK404()
{
if [ ! -d $MK404_PATH ]; then
#release_url=$(curl -Ls -o /dev/null -w %{url_effective} https://github.com/$MK404_owner/$MK404_project/releases/latest)
#release_tag=$(basename $release_url)
#git clone -b $release_tag -- https://github.com/$MK404_owner/$MK404_project.git $MK404_PATH
git clone $MK404_URL $MK404_PATH
fi
}
#### End: Clone MK404 if needed
#### Start: Check for updates
check_for_updates()
{
if [ "$check_flag" == "1" ]; then
if [ -d $MK404_BUILD_PATH ]; then
cd $MK404_BUILD_PATH
MK404_current_version=$( command ./MK404 --version | grep "MK404" | cut -f 4 -d " ")
cd $MK404_PATH
else
echo "Cannot check current version as it has not been build."
fi
# Get local Commit_Hash
MK404_local_GIT_COMMIT_HASH=$(git log --pretty=format:"%H" -1)
# Get local Commit_Number
MK404_local_GIT_COMMIT_NUMBER=$(git rev-list HEAD --count)
# Get latest release
MK404_release_url=$(curl -Ls -o /dev/null -w %{url_effective} https://github.com/$MK404_owner/$MK404_project/releases/latest)
MK404_release_tag=$(basename $MK404_release_url)
# Get remote Commit_Hash
#MK404_remote_GIT_COMMIT_HASH=$(git ls-remote --heads $(git config --get remote.origin.url) | grep "refs/heads/master" | cut -f 1)
MK404_remote_GIT_COMMIT_HASH=$(git ls-remote | grep "refs/tags/$MK404_release_tag" | cut -f 1)
# Get remote Commit_Number
MK404_remote_GIT_COMMIT_NUMBER=$(git rev-list $MK404_release_tag --count)
# Output
echo ""
echo "Current version : $MK404_current_version"
echo ""
echo "Current local hash : $MK404_local_GIT_COMMIT_HASH"
echo "Current local commit nr : $MK404_local_GIT_COMMIT_NUMBER"
if [ "$MK404_local_GIT_COMMIT_HASH" != "$MK404_remote_GIT_COMMIT_HASH" ]; then
echo "$(tput setaf 1)"
else
echo "$(tput setaf 2)"
fi
echo "Latest release tag : $MK404_release_tag"
echo "Latest release hash : $MK404_remote_GIT_COMMIT_HASH"
echo "Latest remote commit nr : $MK404_remote_GIT_COMMIT_NUMBER"
echo "$(tput sgr 0)"
# Check for updates
if [ ! -z $MK404_remote_GIT_COMMIT_HASH ]; then
if [[ "$MK404_local_GIT_COMMIT_HASH" != "$MK404_remote_GIT_COMMIT_HASH" && -z "$update_flag" ]]; then
echo "$(tput setaf 2)Update is availible.$(tput sgr 0)"
read -t 10 -n 1 -p "$(tput setaf 3)Update now Y/n$(tput sgr 0)" update_answer
if [ "$update_answer" == "Y" ]; then
update_flag=1
fi
echo ""
fi
fi
fi
}
#### End: Check for updates
#### Start: Fetch updates and force new build
fetch_updates()
{
if [ "$update_flag" == "1" ]; then
if [ ! -z $MK404_remote_GIT_COMMIT_HASH ]; then
if [ "$MK404_local_GIT_COMMIT_HASH" != "$MK404_remote_GIT_COMMIT_HASH" ]; then
echo ""
git fetch --all
read -t 10 -p "$(tput setaf 2)Updating MK404 !$(tput sgr 0)"
echo ""
git reset --hard $MK404_release_tag
read -t 10 -p "$(tput setaf 2)Compiling MK404 !$(tput sgr 0)"
echo ""
new_build_flag=1
fi
fi
fi
}
#### End: Fetch updates and force new build
#### Start: Prepare MK404 build
prepare_MK404()
{
if [ ! -d $MK404_BUILD_PATH ]; then
mkdir -p $MK404_BUILD_PATH
fi
}
#### End: Prepare MK404 build
#### Start: Build MK404
build_MK404()
{
if [[ ! -f "$MK404_BUILD_PATH/Makefile" || "$new_build_flag" == "1" ]]; then
# Init and update submodules
if [ -d $MK404_BUILD_PATH ]; then
rm -rf $MK404_BUILD_PATH
mkdir -p $MK404_BUILD_PATH
fi
git submodule init
git submodule update
cmake -B$MK404_BUILD_PATH -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles"
fi
# Make MK404
cd $MK404_BUILD_PATH
if [[ ! -f "$MK404_BUILD_PATH/MK404" || "$new_build_flag" == "1" ]]; then
make
fi
# Make SDcards
if [[ ! -f "$MK404_BUILD_PATH/Prusa_MK3S_SDcard.bin" || "$new_build_flag" == "1" ]]; then
cmake --build $MK404_BUILD_PATH --config Release --target Prusa_MK3S_SDcard.bin
cmake --build $MK404_BUILD_PATH --config Release --target Prusa_MK3_SDcard.bin
cmake --build $MK404_BUILD_PATH --config Release --target Prusa_MK25_13_SDcard.bin
cmake --build $MK404_BUILD_PATH --config Release --target Prusa_MK25S_13_SDcard.bin
cmake --build $MK404_BUILD_PATH --config Release --target Prusa_MK3SMMU2_SDcard.bin
cmake --build $MK404_BUILD_PATH --config Release --target Prusa_MK3MMU2_SDcard.bin
fi
}
#### End: Build MK404
#### Start: Run MK404 SIM
run_MK404_SIM()
{
if [ ! -z $mk404_flag ]; then
# Output some useful data
echo "Printer : $MK404_PRINTER"
echo "Options : $MK404_options"
echo ""
read -t 5 -p "Press $(tput setaf 2)Enter$(tput sgr 0) to start MK404"
echo ""
# Change to MK404 build folder
cd $MK404_BUILD_PATH
# Start MK404
# default with serial output and terminal to manipulate it via terminal
echo ""
echo "./MK404 Prusa_$MK404_PRINTER -s --terminal $MK404_options $MK404_firmware_file"
sleep 5
./MK404 Prusa_$MK404_PRINTER -s --terminal $MK404_options $MK404_firmware_file || exit 10
fi
}
#### End: Run MK404 SIM
#### Check OS and needed packages
echo "Check OS"
check_OS
check_packages
#### Check for options/flags
echo "Check for options"
#### Prepare build environment
echo "Prepare build env"
set_build_env_variables
output_useful_data
get_MK404
#
cd $MK404_PATH
check_for_updates
fetch_updates
prepare_MK404
build_MK404
run_MK404_SIM
#### End of MK404 Simulator

File diff suppressed because it is too large Load Diff

View File

@ -63,7 +63,7 @@ _Note: Multi language build is not supported._
**a.** Install `"Arduino Software IDE"` from the official website `https://www.arduino.cc -> Software->Downloads` **a.** Install `"Arduino Software IDE"` from the official website `https://www.arduino.cc -> Software->Downloads`
_It is recommended to use version `"1.8.5"`, as it is used on out build server to produce official builds._ _It is recommended to use version `"1.8.5"`, as it is used on our build server to produce official builds._
**b.** Setup Arduino to use Prusa Rambo board definition **b.** Setup Arduino to use Prusa Rambo board definition
@ -118,7 +118,7 @@ _notes: Script and instructions contributed by 3d-gussner. Use at your own risk.
- to install zip run `sudo apt-get install zip` - to install zip run `sudo apt-get install zip`
- to install dos2unix run `sudo apt-get install dos2unix` - to install dos2unix run `sudo apt-get install dos2unix`
- run `dos2unix PF-build.sh` to convert the windows line endings to unix line endings - run `dos2unix PF-build.sh` to convert the windows line endings to unix line endings
- add few lines at the top of `~/.bashrc` by running `sudo nano ~/.bashrc` - add a few lines at the top of `~/.bashrc` by running `sudo nano ~/.bashrc`
export OS="Linux" export OS="Linux"
export JAVA_TOOL_OPTIONS="-Djava.net.preferIPv4Stack=true" export JAVA_TOOL_OPTIONS="-Djava.net.preferIPv4Stack=true"
@ -134,14 +134,14 @@ _notes: Script and instructions contributed by 3d-gussner. Use at your own risk.
- Example: You files are under `C:\Users\<your-username>\Downloads\Prusa-Firmware-MK3` - Example: You files are under `C:\Users\<your-username>\Downloads\Prusa-Firmware-MK3`
- use under Ubuntu the following command `cd /mnt/c/Users/<your-username>/Downloads/Prusa-Firmware-MK3` - use under Ubuntu the following command `cd /mnt/c/Users/<your-username>/Downloads/Prusa-Firmware-MK3`
to change to the right folder to change to the right folder
- Unix and windows have different line endings (LF vs CRLF), try dos2unix to convert - Unix and Windows have different line endings (LF vs CRLF), use dos2unix to convert
- This should fix the `"$'\r': command not found"` error - This should fix the `"$'\r': command not found"` error
- to install run `apt-get install dos2unix` - to install run `apt-get install dos2unix`
- If your Windows isn't in English the Paths may look different - If your Windows isn't in English the Paths may look different
Example in other languages Example in other languages
- English `/mnt/c/Users/<your-username>/Downloads/Prusa-Firmware-MK3` will be on a German Windows`/mnt/c/Anwender/<your-username>/Downloads/Prusa-Firmware-MK3` - English `/mnt/c/Users/<your-username>/Downloads/Prusa-Firmware-MK3` will be on a German Windows`/mnt/c/Anwender/<your-username>/Downloads/Prusa-Firmware-MK3`
#### Compile Prusa-firmware with Ubuntu/Debian Linux subsystem installed #### Compile Prusa-firmware with Ubuntu/Debian Linux subsystem installed
- open Ubuntu bash - open Ubuntu bash shell
- change to your source code folder (case sensitive) - change to your source code folder (case sensitive)
- run `./PF-build.sh` - run `./PF-build.sh`
- follow the instructions - follow the instructions
@ -211,14 +211,14 @@ Q:I built firmware using Arduino and I see "?" instead of numbers in printer use
A:Step 1.c was omitted or you updated Arduino and now platform.txt located somewhere in your user profile is used. A:Step 1.c was omitted or you updated Arduino and now platform.txt located somewhere in your user profile is used.
Q:I built firmware using Arduino and printer now speaks Klingon (nonsense characters and symbols are displayed @^#$&*°;~ÿ) Q:I built firmware using Arduino and my printer now speaks "Klingon" (nonsense characters and symbols are displayed @^#$&*°;~ÿ)
A:Step 2.c was omitted. A:Step 2.c was omitted.
Q:What environment does Prusa use to build the firmware in the first place? Q:What environment does Prusa use to build its official firmware releases?
A:Our production builds are 99.9% equivalent to https://github.com/prusa3d/Prusa-Firmware#linux this is also easiest way to build as only one step is needed - run single script, which downloads patched Arduino from github, builds using it, then extracts translated strings and creates language variants (for MK2x) or language hex file for external SPI flash (MK3x). But you need Linux or Linux in virtual machine. This is also what happens when you open pull request to our repository - all variants are built by Travis http://travis-ci.org/ (to check for compilation errors). You can see, what is happening in .travis.yml. It would be also possible to get hex built by travis, only deploy step is missing in .travis.yml. You can get inspiration how to deploy hex by travis and how to setup travis in https://github.com/prusa3d/MM-control-01/ repository. Final hex is located in ./lang/firmware.hex Community reproduced this for Windows in https://github.com/prusa3d/Prusa-Firmware#using-linux-subsystem-under-windows-10-64-bit or https://github.com/prusa3d/Prusa-Firmware#using-git-bash-under-windows-10-64-bit . A:Our production builds are 99.9% equivalent to https://github.com/prusa3d/Prusa-Firmware#linux this is also easiest way to build as only one step is needed - run a single script, which downloads patched Arduino from GitHub, builds using it, then extracts translated strings and creates language variants (for MK2x) or language hex file for external SPI flash (MK3x). But you need Linux or Linux in a virtual machine. This is also what happens when you open a pull request to our repository - all variants are built by Travis http://travis-ci.org/ (to check for compilation errors). You can see, what is happening in .travis.yml. It would be also possible to get hex built by Travis, only the deploy step is missing in .travis.yml. You can find inspiration on how to deploy hex in Travis and how to setup Travis in https://github.com/prusa3d/MM-control-01/ repository. The final hex is located in ./lang/firmware.hex - community reproduced this for Windows in https://github.com/prusa3d/Prusa-Firmware#using-linux-subsystem-under-windows-10-64-bit or https://github.com/prusa3d/Prusa-Firmware#using-git-bash-under-windows-10-64-bit .
Q:Why are build instructions for Arduino mess. Q:Why are build instructions for Arduino a mess?
Y:We are too lazy to ship proper board definition for Arduino. We plan to switch to cmake + ninja to be inherently multiplatform, easily integrate build tools, suport more IDEs, get 10 times shorter build times and be able to update compiler whenever we want. Y:We are too lazy to ship a proper board definition for Arduino. We plan to switch to CMake + ninja to be inherently multiplatform, easily integrate build tools, suport more IDEs, get 10 times shorter build times and be able to update compiler whenever we want.

View File

@ -0,0 +1,30 @@
## List of Community made translations
- **Dutch / Nederlands**
- Maintainers: **@3d-gussner** and **@vintagepc**
- Co-maintainers:
- Contributors: **@stelgenhof**
- [X] **Active** since March 2021
- [X] **Maintained** since September 2019
- **Romanian / Română**
- Maintainers: **@leptun** and **@Hauzman**
- Co-maintainers: **@QuantumRoboticsFTC**
- Contributors:
- [X] **Active** since January 2022
- [X] **Maintained** since January 2022
- **Hungarian / Magyar**
- Maintainers: **@AttilaSVK**
- Co-maintainers:
- Contributors:
- [X] **Active** since January 2022
- [X] **Maintained** since January 2022
- **Swedish / Svenska**
- Maintainers: **@Painkiller56**
- Co-maintainers:
- Contributors:
- [X] **Active** since January 2022
- [X] **Maintained** since January 2022

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

54
tools/README.md Normal file
View File

@ -0,0 +1,54 @@
# Host debugging tools for Prusa MK3 firmware
## Tools
### ``dump_eeprom``
Dump the content of the entire EEPROM using the D3 command.
Requires ``printcore`` from [Pronterface].
### ``dump_sram``
Dump the content of the entire SRAM using the D2 command.
Requires ``printcore`` from [Pronterface].
### ``dump_crash``
Dump the content of the last crash dump on MK3+ printers using D21.
Requires ``printcore`` from [Pronterface].
### ``elf_mem_map``
Generate a symbol table map with decoded information starting directly from an ELF firmware with DWARF debugging information (which is the default using the stock board definition).
When used along with a memory dump obtained from the D2 g-code, show the value of each symbol which is within the address range of the dump.
When used with ``--map`` and a single elf file, generate a map consisting of memory location and source location for each statically-addressed variable.
With ``--qdirstat`` and a single elf file, generate a [qdirstat](https://github.com/shundhammer/qdirstat) compatible cache file which can be loaded to inspect memory utilization interactively in a treemap.
This assumes the running firmware generating the dump and the elf file are the same.
Requires Python3 and the [pyelftools](https://github.com/eliben/pyelftools) module.
### ``dump2bin``
Parse and decode a memory dump obtained from the D2/D21/D23 g-code into readable metadata and binary. The output binary is padded and extended to fit the original address range.
### ``xfimg2dump``
Extract a crash dump from an external flash image and output the same format produced by the D21 g-code.
### ``update_eeprom``
Given one EEPROM dump, convert the dump to update instructions that can be sent to a printer.
Given two EEPROM dumps, produces only the required instructions needed to update the contents from the first to the second. This is currently quite crude and assumes dumps are aligned (starting from the same address or same stride).
Optionally writes the instructions to the specified port (requires ``printcore`` from [Pronterface]).
### ``noreset``
Set the required TTY flags on the specified port to avoid reset-on-connect for *subsequent* requests (issuing this command might still cause the printer to reset).
[Pronterface]: https://github.com/kliment/Printrun

59
tools/dump2bin Executable file
View File

@ -0,0 +1,59 @@
#!/usr/bin/env python3
import argparse
import os, sys
from lib.dump import decode_dump
def main():
# parse the arguments
ap = argparse.ArgumentParser(description="""
Parse and decode a memory dump obtained from the D2/D21/D23 g-code
into readable metadata and binary. The output binary is padded and
extended to fit the original address range.
""")
ap.add_argument('-i', dest='info', action='store_true',
help='display crash info only')
ap.add_argument('dump')
ap.add_argument('output', nargs='?')
args = ap.parse_args()
# decode the dump data
dump = decode_dump(args.dump)
if dump is None:
return os.EX_DATAERR
# output descriptors
if args.info:
o_fd = None
o_md = sys.stdout
elif args.output is None:
o_fd = sys.stdout.buffer
o_md = sys.stderr
else:
o_fd = open(args.output, 'wb')
o_md = sys.stdout
# output binary
if o_fd:
o_fd.write(dump.data)
o_fd.close()
# metadata
print(' dump type: {typ}\n'
'crash reason: {reason}\n'
' registers: {regs}\n'
' PC: {pc}\n'
' SP: {sp}\n'
' ranges: {ranges}'.format(
typ=dump.typ,
reason=dump.reason.name if dump.reason is not None else 'N/A',
regs=dump.regs,
pc='{:#x}'.format(dump.pc) if dump.pc is not None else 'N/A',
sp='{:#x}'.format(dump.sp) if dump.sp is not None else 'N/A',
ranges=str(dump.ranges)),
file=o_md)
if __name__ == '__main__':
exit(main())

17
tools/dump_crash Executable file
View File

@ -0,0 +1,17 @@
#!/bin/sh
prg=$(basename "$0")
port="$1"
if [ -z "$port" -o "$port" = "-h" ]
then
echo "usage: $0 <port>" >&2
echo "Connect to <port> and dump the content of last crash using D21 to stdout" >&2
exit 1
fi
set -e
tmp=$(mktemp)
trap "rm -f \"$tmp\"" EXIT
echo D21 > "$tmp"
printcore -v "$port" "$tmp" 2>&1 | \
sed -ne '/^RECV: D21 /,/RECV: ok$/s/^RECV: //p'

17
tools/dump_eeprom Executable file
View File

@ -0,0 +1,17 @@
#!/bin/sh
prg=$(basename "$0")
port="$1"
if [ -z "$port" -o "$port" = "-h" ]
then
echo "usage: $0 <port>" >&2
echo "Connect to <port> and dump the content of the EEPROM using D3 to stdout" >&2
exit 1
fi
set -e
tmp=$(mktemp)
trap "rm -f \"$tmp\"" EXIT
echo D3 > "$tmp"
printcore -v "$port" "$tmp" 2>&1 | \
sed -ne '/^RECV: D3 /,/RECV: ok$/s/^RECV: //p'

17
tools/dump_sram Executable file
View File

@ -0,0 +1,17 @@
#!/bin/sh
prg=$(basename "$0")
port="$1"
if [ -z "$port" -o "$port" = "-h" ]
then
echo "usage: $0 <port>" >&2
echo "Connect to <port> and dump the content of the SRAM using D2 to stdout" >&2
exit 1
fi
set -e
tmp=$(mktemp)
trap "rm -f \"$tmp\"" EXIT
echo D2 > "$tmp"
printcore -v "$port" "$tmp" 2>&1 | \
sed -ne '/^RECV: D2 /,/RECV: ok$/s/^RECV: //p'

389
tools/elf_mem_map Executable file
View File

@ -0,0 +1,389 @@
#!/usr/bin/env python3
import argparse
import elftools.elf.elffile
import elftools.dwarf.descriptions
from collections import namedtuple
from struct import unpack
import os
from lib.dump import decode_dump
from lib.avr import *
Entry = namedtuple('Entry', ['name', 'loc', 'size', 'declpos'])
Member = namedtuple('Member', ['name', 'off', 'size'])
def array_inc(loc, dim, idx=0):
if idx == len(dim):
return True
loc[idx] += 1
if loc[idx] == dim[idx]:
loc[idx] = 0
return array_inc(loc, dim, idx+1)
return False
def get_type_size(type_DIE):
while True:
if 'DW_AT_byte_size' in type_DIE.attributes:
return type_DIE, type_DIE.attributes.get('DW_AT_byte_size').value
if 'DW_AT_type' not in type_DIE.attributes:
return None
type_DIE = type_DIE.get_DIE_from_attribute('DW_AT_type')
def get_type_arrsize(type_DIE):
size = get_type_size(type_DIE)
if size is None:
return None
byte_size = size[1]
if size[0].tag != 'DW_TAG_pointer_type':
array_DIE = get_type_def(type_DIE, 'DW_TAG_array_type')
if array_DIE is not None:
for range_DIE in array_DIE.iter_children():
if range_DIE.tag == 'DW_TAG_subrange_type' and \
'DW_AT_upper_bound' in range_DIE.attributes:
dim = range_DIE.attributes['DW_AT_upper_bound'].value + 1
byte_size *= dim
return byte_size
def get_type_def(type_DIE, type_tag):
while True:
if type_DIE.tag == type_tag:
return type_DIE
if 'DW_AT_type' not in type_DIE.attributes:
return None
type_DIE = type_DIE.get_DIE_from_attribute('DW_AT_type')
def get_FORM_block1(attr):
if attr.form != 'DW_FORM_block1':
return None
if attr.value[0] == 3: # OP_addr
return int.from_bytes(attr.value[1:], 'little')
if attr.value[0] == 35: # OP_plus_uconst (ULEB128)
v = 0
s = 0
for b in attr.value[1:]:
v |= (b & 0x7f) << s
if b & 0x80 == 0:
break
s += 7
return v
return None
def get_array_dims(DIE):
array_DIE = get_type_def(DIE, 'DW_TAG_array_type')
if array_DIE is None:
return []
array_dim = []
for range_DIE in array_DIE.iter_children():
if range_DIE.tag == 'DW_TAG_subrange_type' and \
'DW_AT_upper_bound' in range_DIE.attributes:
array_dim.append(range_DIE.attributes['DW_AT_upper_bound'].value + 1)
return array_dim
def get_struct_members(DIE, entry, expand_structs, struct_gaps):
struct_DIE = get_type_def(DIE, 'DW_TAG_structure_type')
if struct_DIE is None:
return []
members = []
for member_DIE in struct_DIE.iter_children():
if member_DIE.tag == 'DW_TAG_member' and 'DW_AT_name' in member_DIE.attributes:
m_name = member_DIE.attributes['DW_AT_name'].value.decode('ascii')
m_off = get_FORM_block1(member_DIE.attributes['DW_AT_data_member_location'])
m_byte_size = get_type_size(member_DIE)[1]
# still expand member arrays
m_array_dim = get_array_dims(member_DIE)
if m_byte_size == 1 and len(m_array_dim) > 1:
# likely string, remove one dimension
m_byte_size *= m_array_dim.pop()
if len(m_array_dim) == 0 or (len(m_array_dim) == 1 and m_array_dim[0] == 1):
# plain entry
members.append(Member(m_name, m_off, m_byte_size))
elif len(m_array_dim) == 1 and m_byte_size == 1:
# likely string, avoid expansion
members.append(Member(m_name + '[]', m_off, m_array_dim[0]))
else:
# expand array entries
m_array_pos = m_off
m_array_loc = [0] * len(m_array_dim)
while True:
# location index
sfx = ''
for d in range(len(m_array_dim)):
sfx += '[{}]'.format(str(m_array_loc[d]).rjust(len(str(m_array_dim[d]-1)), '0'))
members.append(Member(m_name + sfx, m_array_pos, m_byte_size))
# advance
if array_inc(m_array_loc, m_array_dim):
break
m_array_pos += m_byte_size
if struct_gaps and len(members):
# fill gaps in the middle
members = list(sorted(members, key=lambda x: x.off))
last_end = 0
for n in range(len(members)):
member = members[n]
if member.off > last_end:
members.append(Member('*UNKNOWN*', last_end, member.off - last_end))
last_end = member.off + member.size
if struct_gaps and len(members):
# fill gap at the end
members = list(sorted(members, key=lambda x: x.off))
last = members[-1]
last_end = last.off + last.size
if entry.size > last_end:
members.append(Member('*UNKNOWN*', last_end, entry.size - last_end))
return members
def get_elf_globals(path, expand_structs, struct_gaps=True):
fd = open(path, "rb")
if fd is None:
return
elffile = elftools.elf.elffile.ELFFile(fd)
if elffile is None or not elffile.has_dwarf_info():
return
# probably not needed, since we're decoding expressions manually
elftools.dwarf.descriptions.set_global_machine_arch(elffile.get_machine_arch())
dwarfinfo = elffile.get_dwarf_info()
grefs = []
for CU in dwarfinfo.iter_CUs():
file_entries = dwarfinfo.line_program_for_CU(CU).header["file_entry"]
for DIE in CU.iter_DIEs():
# handle only variable types
if DIE.tag != 'DW_TAG_variable':
continue
if 'DW_AT_location' not in DIE.attributes:
continue
if 'DW_AT_name' not in DIE.attributes and \
'DW_AT_abstract_origin' not in DIE.attributes:
continue
# handle locations encoded directly as DW_OP_addr (leaf globals)
loc = get_FORM_block1(DIE.attributes['DW_AT_location'])
if loc is None or loc < SRAM_OFFSET or loc >= EEPROM_OFFSET:
continue
loc -= SRAM_OFFSET
# variable name/type
if 'DW_AT_name' not in DIE.attributes and \
'DW_AT_abstract_origin' in DIE.attributes:
DIE = DIE.get_DIE_from_attribute('DW_AT_abstract_origin')
if 'DW_AT_location' in DIE.attributes:
# duplicate reference (handled directly), skip
continue
if 'DW_AT_name' not in DIE.attributes:
continue
if 'DW_AT_type' not in DIE.attributes:
continue
name = DIE.attributes['DW_AT_name'].value.decode('ascii')
# get final storage size
size = get_type_size(DIE)
if size is None:
continue
byte_size = size[1]
# location of main definition
declpos = ''
if 'DW_AT_decl_file' in DIE.attributes and \
'DW_AT_decl_line' in DIE.attributes:
line = DIE.attributes['DW_AT_decl_line'].value
fname = DIE.attributes['DW_AT_decl_file'].value
if fname and fname - 1 < len(file_entries):
fname = file_entries[fname-1].name.decode('ascii')
declpos = '{}:{}'.format(fname, line)
# fetch array dimensions (if known)
array_dim = get_array_dims(DIE)
# fetch structure members (one level only)
entry = Entry(name, loc, byte_size, declpos)
if not expand_structs or size[0].tag == 'DW_TAG_pointer_type':
members = []
else:
members = get_struct_members(DIE, entry, expand_structs, struct_gaps)
def expand_members(entry, members):
if len(members) == 0:
grefs.append(entry)
else:
for member in members:
grefs.append(Entry(entry.name + '.' + member.name,
entry.loc + member.off, member.size,
entry.declpos))
if byte_size == 1 and len(array_dim) > 1:
# likely string, remove one dimension
byte_size *= array_dim.pop()
if len(array_dim) == 0 or (len(array_dim) == 1 and array_dim[0] == 1):
# plain entry
expand_members(entry, members)
elif len(array_dim) == 1 and byte_size == 1:
# likely string, avoid expansion
grefs.append(Entry(entry.name + '[]', entry.loc,
array_dim[0], entry.declpos))
else:
# expand array entries
array_pos = loc
array_loc = [0] * len(array_dim)
while True:
# location index
sfx = ''
for d in range(len(array_dim)):
sfx += '[{}]'.format(str(array_loc[d]).rjust(len(str(array_dim[d]-1)), '0'))
expand_members(Entry(entry.name + sfx, array_pos,
byte_size, entry.declpos), members)
# advance
if array_inc(array_loc, array_dim):
break
array_pos += byte_size
return grefs
def annotate_refs(grefs, addr, data, width, gaps=True, overlaps=True):
last_end = None
for entry in grefs:
if entry.loc < addr:
continue
if entry.loc + entry.size > addr + len(data):
continue
pos = entry.loc-addr
end_pos = pos + entry.size
buf = data[pos:end_pos]
buf_repr = ''
if len(buf) in [1, 2, 4]:
# attempt to decode as integers
buf_repr += ' I:' + str(int.from_bytes(buf, 'little')).rjust(10)
if len(buf) in [4, 8]:
# attempt to decode as floats
typ = 'f' if len(buf) == 4 else 'd'
buf_repr += ' F:' + '{:10.3f}'.format(unpack(typ, buf)[0])
if last_end is not None:
if gaps and last_end < pos:
# decode gaps
gap_size = pos - last_end
gap_buf = data[last_end:pos]
print('{:04x} {} {:4} R:{}'.format(addr+last_end, "*UNKNOWN*".ljust(width),
gap_size, gap_buf.hex()))
if overlaps and last_end > pos + 1:
gap_size = pos - last_end
print('{:04x} {} {:4}'.format(addr+last_end, "*OVERLAP*".ljust(width), gap_size))
print('{:04x} {} {:4}{} R:{}'.format(entry.loc, entry.name.ljust(width),
entry.size, buf_repr, buf.hex()))
last_end = end_pos
def print_map(grefs):
print('OFFSET\tSIZE\tNAME\tDECLPOS')
for entry in grefs:
print('{:x}\t{}\t{}\t{}'.format(entry.loc, entry.size, entry.name, entry.declpos))
def print_qdirstat(grefs):
print('[qdirstat 1.0 cache file]')
entries = {}
for entry in grefs:
# do not output registers when looking at space usage
if entry.loc < SRAM_START:
continue
paths = list(filter(None, re.split(r'[\[\].]', entry.name)))
base = entries
for i in range(len(paths) - 1):
name = paths[i]
if name not in base:
base[name] = {}
base = base[name]
name = paths[-1]
if name in base:
name = '{}_{:x}'.format(entry.name, entry.loc)
base[name] = entry.size
def walker(root, prefix):
files = []
dirs = []
for name, entries in root.items():
if type(entries) == int:
files.append([name, entries])
else:
dirs.append([name, entries])
# print files
print('D\t{}\t{}\t0x0'.format(prefix, 0))
for name, size in files:
print('F\t{}\t{}\t0x0'.format(name, size))
# recurse directories
for name, entries in dirs:
walker(entries, prefix + '/' + name)
walker(entries, '/')
def main():
ap = argparse.ArgumentParser(description="""
Generate a symbol table map starting directly from an ELF
firmware with DWARF3 debugging information.
When used along with a memory dump obtained from the D2/D21/D23 g-code,
show the value of each symbol which is within the address range.
""")
ap.add_argument('elf', help='ELF file containing DWARF debugging information')
ap.add_argument('--no-gaps', action='store_true',
help='do not dump memory inbetween known symbols')
ap.add_argument('--no-expand-structs', action='store_true',
help='do not decode structure data')
ap.add_argument('--overlaps', action='store_true',
help='annotate overlaps greater than 1 byte')
ap.add_argument('--name-width', type=int, default=50,
help='set name column width')
g = ap.add_mutually_exclusive_group(required=True)
g.add_argument('dump', nargs='?', help='RAM dump obtained from D2 g-code')
g.add_argument('--map', action='store_true', help='dump global memory map')
g.add_argument('--qdirstat', action='store_true',
help='dump qdirstat-compatible size usage map')
args = ap.parse_args()
grefs = get_elf_globals(args.elf, expand_structs=not args.no_expand_structs)
grefs = list(sorted(grefs, key=lambda x: x.loc))
if args.map:
print_map(grefs)
elif args.qdirstat:
print_qdirstat(grefs)
else:
# fetch the memory data
dump = decode_dump(args.dump)
if dump is None:
return os.EX_DATAERR
# strip padding, if present
addr_start = dump.ranges[0][0]
addr_end = dump.ranges[-1][0]+dump.ranges[-1][1]
data = dump.data[addr_start:addr_end]
annotate_refs(grefs, addr_start, data,
width=args.name_width,
gaps=not args.no_gaps,
overlaps=args.overlaps)
if __name__ == '__main__':
exit(main())

4
tools/lib/avr.py Normal file
View File

@ -0,0 +1,4 @@
SRAM_START = 0x200
SRAM_SIZE = 0x2000
SRAM_OFFSET = 0x800000
EEPROM_OFFSET = 0x810000

169
tools/lib/dump.py Normal file
View File

@ -0,0 +1,169 @@
import sys
import re
import enum
import struct
from . import avr
FILL_BYTE = b'\0' # used to fill memory gaps in the dump
DUMP_MAGIC = 0x55525547 # XFLASH dump magic
DUMP_OFFSET = 0x3d000 # XFLASH dump offset
DUMP_SIZE = 0x2300 # XFLASH dump size
class CrashReason(enum.IntEnum):
MANUAL = 0
STACK_ERROR = 1
WATCHDOG = 2
BAD_ISR = 3
class Dump():
def __init__(self, typ, reason, regs, pc, sp, data, ranges):
self.typ = typ
self.reason = reason
self.regs = regs
self.pc = pc
self.sp = sp
self.data = data
self.ranges = ranges
# expand the buffer identified by addr+data to fill the region start+size
def region_expand(addr, data, start, size):
if start < addr:
data = FILL_BYTE * (addr - start) + data
addr = start
end = start + size
data_end = addr + len(data)
if end > data_end:
data += FILL_BYTE * (data_end - end)
return addr, data
def merge_ranges(ranges):
ranges = list(sorted(ranges, key=lambda x: x[0]))
if len(ranges) < 2:
return ranges
ret = [ranges[0]]
for cur in ranges[1:]:
last = ret[-1]
last_end = last[0] + last[1]
if last_end < cur[0]:
ret.append(cur)
else:
cur_end = cur[0] + cur[1]
last = (last[0], max(last_end, cur_end) - last[0])
ret[-1] = last
return ret
def decode_dump(path):
fd = open(path, 'r')
if fd is None:
return None
buf_addr = None # starting address
buf_data = None # data
typ = None # dump type
reason = None # crash reason
regs = None # registers present
pc = None # PC address
sp = None # SP address
ranges = [] # dumped ranges
in_dump = False
for line in enumerate(fd):
line = (line[0], line[1].rstrip())
tokens = line[1].split(maxsplit=1)
def line_error():
print('malformed line {}: {}'.format(*line), file=sys.stderr)
# handle metadata
if not in_dump:
if len(tokens) > 0 and tokens[0] in ['D2', 'D21', 'D23']:
in_dump = True
typ = tokens[0]
continue
else:
if len(tokens) == 0:
line_error()
continue
elif tokens[0] == 'ok':
break
elif tokens[0] == 'error:' and len(tokens) == 2:
values = tokens[1].split(' ')
if typ == 'D23' and len(values) >= 3:
reason = CrashReason(int(values[0], 0))
pc = int(values[1], 0)
sp = int(values[2], 0)
else:
line_error()
continue
elif len(tokens) != 2 or not re.match(r'^[0-9a-fA-F]+$', tokens[0]):
line_error()
continue
# decode hex data
addr = int.from_bytes(bytes.fromhex(tokens[0]), 'big')
data = bytes.fromhex(tokens[1])
ranges.append((addr, len(data)))
if buf_addr is None:
buf_addr = addr
buf_data = data
else:
# grow buffer as needed
buf_addr, buf_data = region_expand(buf_addr, buf_data,
addr, len(data))
# replace new part
rep_start = addr - buf_addr
rep_end = rep_start + len(data)
buf_data = buf_data[:rep_start] + data + buf_data[rep_end:]
# merge continuous ranges
ranges = merge_ranges(ranges)
if typ == 'D2':
# D2 doesn't guarantee registers to be present
regs = len(ranges) > 0 and \
ranges[0][0] == 0 and \
ranges[0][1] >= avr.SRAM_START
# fill to fit for easy loading
buf_addr, buf_data = region_expand(
buf_addr, buf_data, 0, avr.SRAM_START + avr.SRAM_SIZE)
elif typ == 'D23':
# check if the dump is complete
if len(ranges) != 1 or ranges[0][0] != 0 or \
ranges[0][1] != avr.SRAM_START + avr.SRAM_SIZE:
print('error: incomplete D23 dump', file=sys.stderr)
return None
regs = True
if reason is None:
print('warning: no error line in D23', file=sys.stderr)
elif typ == 'D21':
if len(ranges) != 1 or len(buf_data) != (avr.SRAM_START + avr.SRAM_SIZE + 256):
print('error: incomplete D21 dump', file=sys.stderr)
return None
# decode the header structure
magic, regs_present, crash_reason, pc, sp = struct.unpack('<LBBLH', buf_data[0:12])
if magic != DUMP_MAGIC:
print('error: invalid dump header in D21', file=sys.stderr)
return None
regs = bool(regs_present)
reason = CrashReason(crash_reason)
# extract the data section
buf_addr = 0
buf_data = buf_data[256:]
ranges[0] = (0, len(buf_data))
return Dump(typ, reason, regs, pc, sp, buf_data, ranges)

12
tools/noreset Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh
prg=$(basename "$0")
port="$1"
if [ -z "$port" -o "$port" = "-h" ]
then
echo "usage: $0 <port>" >&2
echo "Set TTY flags on <port> to avoid reset-on-connect" >&2
exit 1
fi
set -e
stty -F "$port" -hup

54
tools/update_eeprom Executable file
View File

@ -0,0 +1,54 @@
#!/bin/sh
prg=$(basename "$0")
# parse arguments
while getopts f:h optname
do
case $optname in
f) port="$OPTARG" ;;
*) help=1 ;;
esac
done
shift `expr $OPTIND - 1`
old="$1"
new="$2"
if [ -z "$old" -o "$help" = "-h" -o "$#" -gt 2 ]
then
echo "usage: $0 [-f <port>] <old dump> [<new dump>]" >&2
echo "Convert <old dump> to instructions to update instructions." >&2
echo "With <new dump>, generate instructions to update EEPROM changes only." >&2
echo "Optionally write such changes directly if <port> if given." >&2
exit 1
fi
set -e
instr=$(mktemp)
trap "rm -f \"$instr\"" EXIT
convert()
{
sed -ne 's/^\([0-9a-f]\{4\}\) \([0-9a-f ]*\)$/D3 Ax\1 C16 X\2/p' "$@"
}
if [ -z "$new" ]; then
# convert the instructions to updates
convert "$old" > "$instr"
else
tmp1=$(mktemp)
tmp2=$(mktemp)
trap "rm -f \"$tmp1\" \"$tmp2\"" EXIT
convert "$old" > "$tmp1"
convert "$new" > "$tmp2"
comm -13 "$tmp1" "$tmp2" > "$instr"
fi
# write the instructions if requested
if [ -z "$port" ]; then
cat "$instr"
else
printcore -v "$port" "$instr"
fi

89
tools/utils.gdb Normal file
View File

@ -0,0 +1,89 @@
# -*- gdb-script -*-
define load_dump
restore $arg0 binary 0x800000
set $pc = (((unsigned long)$arg1) - 2) << 1
set $sp = $arg2
where
end
document load_dump
Load a crash dump, setup PC/SP and show the current backtrace
Usage: load_dump <file> <PC-addr> <SP-addr>
end
define sp_skip
if $argc == 0
set $shift = 3
else
set $shift = $arg0
end
set $new_pc = ((((unsigned long)*(uint8_t*)($sp+$shift+1)) << 16) + \
(((unsigned long)*(uint8_t*)($sp+$shift+2)) << 8) + \
(((unsigned long)*(uint8_t*)($sp+$shift+3)) << 0)) << 1
set $new_sp = $sp+$shift+3
select-frame 0
set $saved_pc = $pc
set $saved_sp = $sp
set $pc = $new_pc
set $sp = $new_sp
where
end
document sp_skip
Decode the PC address at SP+offset, then show the resulting stack.
The default (and minimum) offset is 3.
Usage: sp_skip [off]
end
define sp_restore
select-frame 0
set $pc = $saved_pc
set $sp = $saved_sp
where
end
document sp_restore
Undo an sp_skip move (restore existing PC/SP positions)
Usage: sp_restore
end
define sp_test
sp_skip $arg0
set $pc = $saved_pc
set $sp = $saved_sp
end
document sp_test
Attempt to decode the PC address at SP+offset, then show the resulting stack.
The default (and minimum) offset is 3.
Usage: sp_test [off]
end
define sp_scan
dont-repeat
if $argc == 0
set $sp_end = 0x802200
else
set $sp_end = $arg0
end
set $sp_pos = $sp
while $sp_pos < ($sp_end-4)
set $sp_off = $sp_pos - $sp
printf "**** scanning %#x (+%u) ****\n", $sp_pos, $sp_off
sp_test $sp_off
set $sp_pos += 1
end
end
document sp_scan
Attempt to decode PC at any location starting from the SP+3 and up to SP-end
(by default the end of the SRAM) and show the resulting stack at all locations.
Usage: sp_scan [SP-end]
end

46
tools/xfimg2dump Executable file
View File

@ -0,0 +1,46 @@
#!/usr/bin/env python3
import argparse
import struct
import os, sys
from lib.dump import DUMP_MAGIC, DUMP_OFFSET, DUMP_SIZE
def error(msg):
print(msg, file=sys.stderr)
def main():
# parse the arguments
ap = argparse.ArgumentParser(description="""
Extract a crash dump from an external flash image and output
the same format produced by the D21 g-code.
""")
ap.add_argument('image')
args = ap.parse_args()
# read the image
off = DUMP_OFFSET
with open(args.image, 'rb') as fd:
fd.seek(off)
data = fd.read(DUMP_SIZE)
if len(data) != DUMP_SIZE:
error('incorrect image size')
return os.EX_DATAERR
# check for magic header
magic, = struct.unpack('<L', data[:4])
if magic != DUMP_MAGIC:
error('invalid dump magic or no dump')
return os.EX_DATAERR
# output D21 dump
print('D21 - read crash dump', end='')
for i in range(len(data)):
if i % 16 == 0:
print('\n{:06x} '.format(off + i), end='')
print(' {:02x}'.format(data[i]), end='')
print('\nok')
if __name__ == '__main__':
exit(main())