Просмотр исходного кода

Added a new feature to the "M115" code:
"M115 Umajor.minor.revision-flavor" will trigger a dialog
on the display if the "upgrade" firmware version number is higher
than the current version number.

bubnikv 8 лет назад
Родитель
Сommit
0b372bfc94

+ 20 - 8
Firmware/Marlin_main.cpp

@@ -61,6 +61,7 @@
 #include "language.h"
 #include "pins_arduino.h"
 #include "math.h"
+#include "util.h"
 
 #ifdef BLINKM
 #include "BlinkM.h"
@@ -1227,6 +1228,7 @@ static inline bool    code_seen(const char *code) { return (strchr_pointer = str
 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)); };
+static inline uint8_t code_value_uint8()   { return uint8_t(strtol(strchr_pointer+1, NULL, 10)); };
 
 #define DEFINE_PGM_READ_ANY(type, reader)       \
     static inline type pgm_read_any(const type *p)  \
@@ -2766,7 +2768,7 @@ void process_commands()
       }
      break;
 
-    case 44: // M45: Reset the bed skew and offset calibration.
+    case 44: // M44: Prusa3D: Reset the bed skew and offset calibration.
         // Reset the skew and offset in both RAM and EEPROM.
         reset_bed_offset_and_skew();
         // Reset world2machine_rotation_and_skew and world2machine_shift, therefore
@@ -2775,7 +2777,7 @@ void process_commands()
         world2machine_revert_to_uncorrected();
         break;
 
-    case 45: // M46: bed skew and offset with manual Z up
+    case 45: // M45: Prusa3D: bed skew and offset with manual Z up
     {
         // Disable the default update procedure of the display. We will do a modal dialog.
         lcd_update_enable(false);
@@ -2835,7 +2837,12 @@ void process_commands()
         break;
     }
 
-#if 1
+    case 47:
+        // M47: Prusa3D: Show end stops dialog on the display.
+        lcd_diag_show_end_stops();
+        break;
+
+#if 0
     case 48: // M48: scan the bed induction sensor points, print the sensor trigger coordinates to the serial line for visualization on the PC.
     {
         // Disable the default update procedure of the display. We will do a modal dialog.
@@ -2874,10 +2881,6 @@ void process_commands()
     }
 #endif
 
-    case 47:
-        lcd_diag_show_end_stops();
-        break;
-
 // M48 Z-Probe repeatability measurement function.
 //
 // Usage:   M48 <n #_samples> <X X_position_for_samples> <Y Y_position_for_samples> <V Verbose_Level> <L legs_of_movement_prior_to_doing_probe>
@@ -3488,7 +3491,16 @@ Sigma_Exit:
       }
       break;
     case 115: // M115
-      SERIAL_PROTOCOLRPGM(MSG_M115_REPORT);
+      if (code_seen('V')) {
+          // Report the Prusa version number.
+          SERIAL_PROTOCOLLNRPGM(FW_VERSION_STR_P());
+      } else if (code_seen('U')) {
+          // Check the firmware version provided. If the firmware version provided by the U code is higher than the currently running firmware,
+          // pause the print and ask the user to upgrade the firmware.
+          show_upgrade_dialog_if_version_newer(++ strchr_pointer);
+      } else {
+          SERIAL_PROTOCOLRPGM(MSG_M115_REPORT);
+      }
       break;
     case 117: // M117 display message
       starpos = (strchr(strchr_pointer + 5,'*'));

+ 26 - 0
Firmware/language_all.cpp

@@ -1760,6 +1760,32 @@ const char * const MSG_MOVE_Z_LANG_TABLE[LANG_NUM] PROGMEM = {
 	MSG_MOVE_Z_PL
 };
 
+const char MSG_NEW_FIRMWARE_AVAILABLE_EN[] PROGMEM = "New firmware version available:";
+const char MSG_NEW_FIRMWARE_AVAILABLE_CZ[] PROGMEM = "Vysla nova verze firmware:";
+const char MSG_NEW_FIRMWARE_AVAILABLE_IT[] PROGMEM = "New firmware version available:";
+const char MSG_NEW_FIRMWARE_AVAILABLE_ES[] PROGMEM = "New firmware version available:";
+const char MSG_NEW_FIRMWARE_AVAILABLE_PL[] PROGMEM = "New firmware version available:";
+const char * const MSG_NEW_FIRMWARE_AVAILABLE_LANG_TABLE[LANG_NUM] PROGMEM = {
+	MSG_NEW_FIRMWARE_AVAILABLE_EN,
+	MSG_NEW_FIRMWARE_AVAILABLE_CZ,
+	MSG_NEW_FIRMWARE_AVAILABLE_IT,
+	MSG_NEW_FIRMWARE_AVAILABLE_ES,
+	MSG_NEW_FIRMWARE_AVAILABLE_PL
+};
+
+const char MSG_NEW_FIRMWARE_PLEASE_UPGRADE_EN[] PROGMEM = "Please upgrade.";
+const char MSG_NEW_FIRMWARE_PLEASE_UPGRADE_CZ[] PROGMEM = "Prosim aktualizujte.";
+const char MSG_NEW_FIRMWARE_PLEASE_UPGRADE_IT[] PROGMEM = "Please upgrade.";
+const char MSG_NEW_FIRMWARE_PLEASE_UPGRADE_ES[] PROGMEM = "Please upgrade.";
+const char MSG_NEW_FIRMWARE_PLEASE_UPGRADE_PL[] PROGMEM = "Please upgrade.";
+const char * const MSG_NEW_FIRMWARE_PLEASE_UPGRADE_LANG_TABLE[LANG_NUM] PROGMEM = {
+	MSG_NEW_FIRMWARE_PLEASE_UPGRADE_EN,
+	MSG_NEW_FIRMWARE_PLEASE_UPGRADE_CZ,
+	MSG_NEW_FIRMWARE_PLEASE_UPGRADE_IT,
+	MSG_NEW_FIRMWARE_PLEASE_UPGRADE_ES,
+	MSG_NEW_FIRMWARE_PLEASE_UPGRADE_PL
+};
+
 const char MSG_NO_EN[] PROGMEM = "No";
 const char MSG_NO_CZ[] PROGMEM = "Ne";
 const char MSG_NO_IT[] PROGMEM = "No";

+ 4 - 0
Firmware/language_all.h

@@ -280,6 +280,10 @@ extern const char* const MSG_MOVE_Y_LANG_TABLE[LANG_NUM];
 #define MSG_MOVE_Y LANG_TABLE_SELECT(MSG_MOVE_Y_LANG_TABLE)
 extern const char* const MSG_MOVE_Z_LANG_TABLE[LANG_NUM];
 #define MSG_MOVE_Z LANG_TABLE_SELECT(MSG_MOVE_Z_LANG_TABLE)
+extern const char* const MSG_NEW_FIRMWARE_AVAILABLE_LANG_TABLE[LANG_NUM];
+#define MSG_NEW_FIRMWARE_AVAILABLE LANG_TABLE_SELECT(MSG_NEW_FIRMWARE_AVAILABLE_LANG_TABLE)
+extern const char* const MSG_NEW_FIRMWARE_PLEASE_UPGRADE_LANG_TABLE[LANG_NUM];
+#define MSG_NEW_FIRMWARE_PLEASE_UPGRADE LANG_TABLE_SELECT(MSG_NEW_FIRMWARE_PLEASE_UPGRADE_LANG_TABLE)
 extern const char* const MSG_NO_LANG_TABLE[LANG_NUM];
 #define MSG_NO LANG_TABLE_SELECT(MSG_NO_LANG_TABLE)
 extern const char* const MSG_NOT_COLOR_LANG_TABLE[LANG_NUM];

+ 3 - 0
Firmware/language_cz.h

@@ -308,4 +308,7 @@
 #define MSG_BED_LEVELING_FAILED_POINT_LOW				"Kalibrace Z selhala. Sensor nesepnul. Znecistena tryska? Cekam na reset."
 #define MSG_BED_LEVELING_FAILED_POINT_HIGH				"Kalibrace Z selhala. Sensor sepnul prilis vysoko. Cekam na reset."
 
+#define MSG_NEW_FIRMWARE_AVAILABLE								"Vysla nova verze firmware:"
+#define MSG_NEW_FIRMWARE_PLEASE_UPGRADE							"Prosim aktualizujte."
+
 #endif // LANGUAGE_EN_H

+ 3 - 0
Firmware/language_en.h

@@ -301,4 +301,7 @@
 #define MSG_BED_LEVELING_FAILED_POINT_LOW						"Bed leveling failed. Sensor didnt trigger. Debris on nozzle? Waiting for reset."
 #define MSG_BED_LEVELING_FAILED_POINT_HIGH						"Bed leveling failed. Sensor triggered too high. Waiting for reset."
 
+#define MSG_NEW_FIRMWARE_AVAILABLE								"New firmware version available:"
+#define MSG_NEW_FIRMWARE_PLEASE_UPGRADE							"Please upgrade."
+
 #endif // LANGUAGE_EN_H

+ 24 - 1
Firmware/ultralcd.cpp

@@ -8,6 +8,8 @@
 #include "stepper.h"
 #include "ConfigurationStore.h"
 #include <string.h>
+
+#include "util.h"
 //#include "Configuration.h"
 
 
@@ -659,7 +661,14 @@ static void lcd_support_menu()
 
   MENU_ITEM(back, MSG_MAIN, lcd_main_menu);
 
-  MENU_ITEM(back, PSTR(MSG_FW_VERSION " - " FW_version), lcd_main_menu);
+  // Ideally this block would be optimized out by the compiler.
+  const uint8_t fw_string_len = strlen_P(FW_VERSION_STR_P());
+  if (fw_string_len < 6) {
+      MENU_ITEM(back, PSTR(MSG_FW_VERSION " - " FW_version), lcd_main_menu);
+  } else {
+      MENU_ITEM(back, PSTR("FW - " FW_version), lcd_main_menu);
+  }
+      
   MENU_ITEM(back, MSG_PRUSA3D, lcd_main_menu);
   MENU_ITEM(back, MSG_PRUSA3D_FORUM, lcd_main_menu);
   MENU_ITEM(back, MSG_PRUSA3D_HOWTO, lcd_main_menu);
@@ -1324,6 +1333,20 @@ void lcd_show_fullscreen_message_and_wait_P(const char *msg)
     }
 }
 
+void lcd_wait_for_click()
+{
+    for (;;) {
+        manage_heater();
+        manage_inactivity(true);
+        if (lcd_clicked()) {
+            while (lcd_clicked()) ;
+            delay(10);
+            while (lcd_clicked()) ;
+            return;
+        }
+    }
+}
+
 int8_t lcd_show_fullscreen_message_yes_no_and_wait_P(const char *msg, bool allow_timeouting)
 {
     lcd_display_message_fullscreen_P(msg);

+ 2 - 0
Firmware/ultralcd.h

@@ -39,6 +39,7 @@
   static void lcd_menu_statistics();
 
   extern void lcd_display_message_fullscreen_P(const char *msg);
+  extern void lcd_wait_for_click();
   extern void lcd_show_fullscreen_message_and_wait_P(const char *msg);
   // 0: no, 1: yes, -1: timeouted
   extern int8_t lcd_show_fullscreen_message_yes_no_and_wait_P(const char *msg, bool allow_timeouting = true);
@@ -173,6 +174,7 @@ char *ftostr52(const float &x);
 extern void lcd_implementation_clear();
 extern void lcd_printPGM(const char* str);
 extern void lcd_print_at_PGM(uint8_t x, uint8_t y, const char* str);
+extern void lcd_implementation_write(char c);
 extern void lcd_implementation_print(const char *str);
 extern void lcd_implementation_print(int8_t i);
 extern void lcd_implementation_print_at(uint8_t x, uint8_t y, int8_t i);

+ 5 - 0
Firmware/ultralcd_implementation_hitachi_HD44780.h

@@ -591,6 +591,11 @@ void lcd_print_at_PGM(uint8_t x, uint8_t y, const char* str)
     }
 }
 
+void lcd_implementation_write(char c)
+{
+    lcd.write(c);
+}
+
 void lcd_implementation_print(int8_t i)
 {
     lcd.print(i);

+ 279 - 0
Firmware/util.cpp

@@ -0,0 +1,279 @@
+#include "Configuration.h"
+
+#include "ultralcd.h"
+#include "language.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;
+
+const char* FW_VERSION_STR_P()
+{
+    return FW_VERSION_STR;
+}
+
+enum RevisionType
+{
+    REVISION_DEV = 0,
+    REVISION_ALPHA = 1,
+    REVISION_BETA = 2,
+    REVISION_RC,
+    REVISION_RC2,
+    REVISION_RC3,
+    REVISION_RC4,
+    REVISION_RC5,
+    REVISION_RELEASED = 127
+};
+
+const char STR_REVISION_DEV  [] PROGMEM = "dev";
+const char STR_REVISION_ALPHA[] PROGMEM = "alpha";
+const char STR_REVISION_BETA [] PROGMEM = "beta";
+const char STR_REVISION_RC   [] PROGMEM = "rc";
+
+inline bool is_whitespace_or_nl(char c)
+{
+    return c == ' ' || c == '\t' || c == '\n' || c == 'r';
+}
+
+inline bool is_whitespace_or_nl_or_eol(char c)
+{
+    return c == 0 || c == ' ' || c == '\t' || c == '\n' || c == '\r';
+}
+
+inline bool is_digit(char c)
+{
+    return c >= '0' && c <= '9';
+}
+
+// Parse a major.minor.revision version number.
+// Return true if valid.
+inline bool parse_version(const char *str, uint16_t version[4])
+{   
+#if 0
+    SERIAL_ECHOPGM("Parsing version string ");
+    SERIAL_ECHO(str);
+    SERIAL_ECHOLNPGM("");
+#endif
+
+    const char *major = str;
+    const char *p = str;
+    while (is_digit(*p)) ++ p;
+    if (*p != '.')
+        return false;
+    const char *minor = ++ p;
+    while (is_digit(*p)) ++ p;
+    if (*p != '.')
+        return false;
+    const char *rev = ++ p;
+    while (is_digit(*p)) ++ p;
+    if (! is_whitespace_or_nl_or_eol(*p) && *p != '-')
+        return false;
+
+    char *endptr = NULL;
+    version[0] = strtol(major, &endptr, 10);
+    if (endptr != minor - 1)
+        return false;
+    version[1] = strtol(minor, &endptr, 10);
+    if (endptr != rev - 1)
+        return false;
+    version[2] = strtol(rev, &endptr, 10);
+    if (endptr != p)
+        return false;
+
+    version[3] = REVISION_RELEASED;
+    if (*p ++ == '-') {
+        const char *q = p;
+        while (! is_whitespace_or_nl_or_eol(*q))
+            ++ q;
+        uint8_t n = q - p;
+        if (n == strlen_P(STR_REVISION_DEV) && strncmp_P(p, STR_REVISION_DEV, n) == 0)
+            version[3] = REVISION_DEV;
+        else if (n == strlen_P(STR_REVISION_ALPHA) && strncmp_P(p, STR_REVISION_ALPHA, n) == 0)
+            version[3] = REVISION_ALPHA;
+        else if (n == strlen_P(STR_REVISION_BETA) && strncmp_P(p, STR_REVISION_BETA, n) == 0)
+            version[3] = REVISION_BETA;
+        else if ((n == 2 || n == 3) && p[0] == 'r' && p[1] == 'c') {
+            if (n == 2)
+                version[3] = REVISION_RC;
+            else {
+                if (is_digit(p[2]))
+                    version[3] = REVISION_RC + p[2] - '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;
+}
+
+inline bool strncmp_PP(const char *p1, const char *p2, uint8_t n)
+{
+    for (; n > 0; -- n, ++ p1, ++ p2) {
+        if (pgm_read_byte(p1) < pgm_read_byte(p2))
+            return -1;
+        if (pgm_read_byte(p1) > pgm_read_byte(p2))
+            return 1;
+        if (pgm_read_byte(p1) == 0)
+            return 0;
+    }
+    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] = 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] = REVISION_DEV;
+        else if (n == strlen_P(STR_REVISION_ALPHA) && strncmp_PP(p, STR_REVISION_ALPHA, n) == 0)
+            version[3] = REVISION_ALPHA;
+        else if (n == strlen_P(STR_REVISION_BETA) && strncmp_PP(p, STR_REVISION_BETA, n) == 0)
+            version[3] = REVISION_BETA;
+        else if ((n == 2 || n == 3) && strncmp_PP(p, STR_REVISION_RC, 2) == 0) {
+            if (n == 2)
+                version[3] = REVISION_RC;
+            else {
+                p += 2;
+                if (is_digit(pgm_read_byte(p)))
+                    version[3] = 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[3], ver_current[3];
+    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])
+            return 1;
+    return 0;
+}
+
+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");
+        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(MSG_NEW_FIRMWARE_AVAILABLE);
+        lcd_print_at_PGM(0, 2, PSTR(""));
+        for (const char *c = version_string; ! is_whitespace_or_nl_or_eol(*c); ++ c)
+            lcd_implementation_write(*c);
+        lcd_print_at_PGM(0, 3, MSG_NEW_FIRMWARE_PLEASE_UPGRADE);
+        tone(BEEPER, 1000);
+        delay_keep_alive(50);
+        noTone(BEEPER);
+        delay_keep_alive(500);
+        tone(BEEPER, 1000);
+        delay_keep_alive(50);
+        noTone(BEEPER);
+        lcd_wait_for_click();
+        lcd_update_enable(true);
+        lcd_implementation_clear();
+        lcd_update();
+    }
+
+    // Succeeded.
+    return true;
+}

+ 8 - 0
Firmware/util.h

@@ -0,0 +1,8 @@
+#ifndef UTIL_H
+#define UTIL_H
+
+extern const char* FW_VERSION_STR_P();
+
+extern bool show_upgrade_dialog_if_version_newer(const char *version_string);
+
+#endif /* UTIL_H */