Browse Source

Integrated Firmware updater with new way of fetching firmware and motor driver versions

Thokoop 1 year ago
parent
commit
704433b365

+ 23 - 18
app.py

@@ -31,12 +31,12 @@ pause_condition = threading.Condition()
 
 
 # Global variables to store device information
 # Global variables to store device information
 arduino_table_name = None
 arduino_table_name = None
-arduino_driver_type = None
+arduino_driver_type = 'Unknown'
 
 
 # Table status
 # Table status
 current_playing_file = None
 current_playing_file = None
 execution_progress = None
 execution_progress = None
-firmware_version = None
+firmware_version = 'Unknown'
 current_playing_index = None
 current_playing_index = None
 current_playlist = None
 current_playlist = None
 is_clearing = False
 is_clearing = False
@@ -46,9 +46,9 @@ serial_lock = threading.Lock()
 PLAYLISTS_FILE = os.path.join(os.getcwd(), "playlists.json")
 PLAYLISTS_FILE = os.path.join(os.getcwd(), "playlists.json")
 
 
 MOTOR_TYPE_MAPPING = {
 MOTOR_TYPE_MAPPING = {
-    "TMC2209": "./arduino_code_TMC2209/arduino_code_TMC2209.ino",
-    "DRV8825": "./arduino_code/arduino_code.ino",
-    "esp32": "./esp32/esp32.ino"
+    "TMC2209": "./firmware/arduino_code_TMC2209/arduino_code_TMC2209.ino",
+    "DRV8825": "./firmware/arduino_code/arduino_code.ino",
+    "esp32": "./firmware/esp32/esp32.ino"
 }
 }
 
 
 # Ensure the file exists and contains at least an empty JSON object
 # Ensure the file exists and contains at least an empty JSON object
@@ -109,7 +109,7 @@ def list_serial_ports():
 
 
 def connect_to_serial(port=None, baudrate=115200):
 def connect_to_serial(port=None, baudrate=115200):
     """Automatically connect to the first available serial port or a specified port."""
     """Automatically connect to the first available serial port or a specified port."""
-    global ser, ser_port, arduino_table_name, arduino_driver_type
+    global ser, ser_port, arduino_table_name, arduino_driver_type, firmware_version
 
 
     try:
     try:
         if port is None:
         if port is None:
@@ -985,7 +985,8 @@ def get_firmware_info():
     """
     """
     Compare the installed firmware version and motor type with the one in the .ino file.
     Compare the installed firmware version and motor type with the one in the .ino file.
     """
     """
-    global ser
+    global firmware_version, arduino_driver_type, ser
+
     if ser is None or not ser.is_open:
     if ser is None or not ser.is_open:
         return jsonify({"success": False, "error": "Arduino not connected or serial port not open"}), 400
         return jsonify({"success": False, "error": "Arduino not connected or serial port not open"}), 400
 
 
@@ -997,12 +998,8 @@ def get_firmware_info():
             ser.write(b"GET_VERSION\n")
             ser.write(b"GET_VERSION\n")
             time.sleep(0.5)
             time.sleep(0.5)
 
 
-            installed_version = 'Unknown'
-            installed_type = 'Unknown'
-            if ser.in_waiting > 0:
-                response = ser.readline().decode().strip()
-                if " | " in response:
-                    installed_version, installed_type = response.split(" | ", 1)
+            installed_version = firmware_version
+            installed_type = arduino_driver_type
 
 
             # If Arduino provides valid details, proceed with comparison
             # If Arduino provides valid details, proceed with comparison
             if installed_version != 'Unknown' and installed_type != 'Unknown':
             if installed_version != 'Unknown' and installed_type != 'Unknown':
@@ -1034,16 +1031,23 @@ def get_firmware_info():
                 "updateAvailable": False
                 "updateAvailable": False
             })
             })
 
 
-        if request.method == "POST":
+        elif request.method == "POST":
             motor_type = request.json.get("motorType", None)
             motor_type = request.json.get("motorType", None)
             if not motor_type or motor_type not in MOTOR_TYPE_MAPPING:
             if not motor_type or motor_type not in MOTOR_TYPE_MAPPING:
-                return jsonify({"success": False, "error": "Invalid or missing motor type"}), 400
+                return jsonify({
+                    "success": False,
+                    "error": "Invalid or missing motor type"
+                }), 400
 
 
-            ino_path = MOTOR_TYPE_MAPPING.get(motor_type)
+            # Fetch firmware details for the given motor type
+            ino_path = MOTOR_TYPE_MAPPING[motor_type]
             firmware_details = get_ino_firmware_details(ino_path)
             firmware_details = get_ino_firmware_details(ino_path)
 
 
-            if not firmware_details or not firmware_details.get("version") or not firmware_details.get("motorType"):
-                return jsonify({"success": False, "error": "Failed to retrieve .ino firmware details"}), 500
+            if not firmware_details:
+                return jsonify({
+                    "success": False,
+                    "error": "Failed to retrieve .ino firmware details"
+                }), 500
 
 
             return jsonify({
             return jsonify({
                 "success": True,
                 "success": True,
@@ -1053,6 +1057,7 @@ def get_firmware_info():
                 "inoType": firmware_details["motorType"],
                 "inoType": firmware_details["motorType"],
                 "updateAvailable": True
                 "updateAvailable": True
             })
             })
+
     except Exception as e:
     except Exception as e:
         return jsonify({"success": False, "error": str(e)}), 500
         return jsonify({"success": False, "error": str(e)}), 500
 
 

+ 0 - 8
arduino_code/arduino_code.ino → firmware/arduino_code/arduino_code.ino

@@ -194,14 +194,6 @@ void appMode()
             return;
             return;
         }
         }
 
 
-        if (input == "GET_VERSION")
-        {
-            Serial.print(firmwareVersion);
-            Serial.print(" | ");
-            Serial.println(motorType);
-            return;
-        }
-
         if (input == "RESET_THETA")
         if (input == "RESET_THETA")
         {
         {
             resetTheta(); // Reset currentTheta
             resetTheta(); // Reset currentTheta

+ 0 - 8
arduino_code_TMC2209/arduino_code_TMC2209.ino → firmware/arduino_code_TMC2209/arduino_code_TMC2209.ino

@@ -194,14 +194,6 @@ void appMode()
             return;
             return;
         }
         }
 
 
-        if (input == "GET_VERSION")
-        {
-            Serial.print(firmwareVersion);
-            Serial.print(" | ");
-            Serial.println(motorType);
-            return;
-        }
-
         if (input == "RESET_THETA")
         if (input == "RESET_THETA")
         {
         {
             resetTheta(); // Reset currentTheta
             resetTheta(); // Reset currentTheta

+ 0 - 9
esp32/esp32.ino → firmware/esp32/esp32.ino

@@ -88,15 +88,6 @@ void loop()
             return;
             return;
         }
         }
 
 
-        if (input == "GET_VERSION")
-        {
-            Serial.print(firmwareVersion);
-            Serial.print(" | ");
-            Serial.println(motorType);
-            return;
-        }
-
-
         // Example: The user calls "SET_SPEED 60" => 60% of maxSpeed
         // Example: The user calls "SET_SPEED 60" => 60% of maxSpeed
         if (input.startsWith("SET_SPEED"))
         if (input.startsWith("SET_SPEED"))
         {
         {

+ 31 - 3
static/main.js

@@ -702,11 +702,11 @@ async function fetchFirmwareInfo(motorType = null) {
                 }
                 }
             }
             }
         } else {
         } else {
-            logMessage("Error fetching firmware info.", LOG_TYPE.ERROR);
+            logMessage("Could not fetch firmware info.", LOG_TYPE.WARNING);
             logMessage(data.error, LOG_TYPE.DEBUG);
             logMessage(data.error, LOG_TYPE.DEBUG);
         }
         }
     } catch (error) {
     } catch (error) {
-        logMessage("Error fetching firmware info.", LOG_TYPE.ERROR);
+        logMessage("Could not fetch firmware info.", LOG_TYPE.WARNING);
         logMessage(error.message, LOG_TYPE.DEBUG);
         logMessage(error.message, LOG_TYPE.DEBUG);
     } finally {
     } finally {
         // Re-enable the button after fetching
         // Re-enable the button after fetching
@@ -762,7 +762,9 @@ async function updateFirmware() {
 
 
             // Display "You're up to date" message if versions match
             // Display "You're up to date" message if versions match
             const newVersionElement = document.getElementById("new_firmware_version");
             const newVersionElement = document.getElementById("new_firmware_version");
-            newVersionElement.textContent = "You're up to date!";
+            const currentVersionElement = document.getElementById("current_firmware_version");
+            currentVersionElement.textContent = newVersionElement.innerHTML
+            newVersionElement.textContent = "You are up to date!";
             const motorSelectionDiv = document.getElementById("motor_selection");
             const motorSelectionDiv = document.getElementById("motor_selection");
             motorSelectionDiv.style.display = "none";
             motorSelectionDiv.style.display = "none";
         } else {
         } else {
@@ -848,6 +850,9 @@ function openPlaylistEditor(playlistName) {
 function clearSchedule() {
 function clearSchedule() {
     document.getElementById("start_time").value = "";
     document.getElementById("start_time").value = "";
     document.getElementById("end_time").value = "";
     document.getElementById("end_time").value = "";
+    document.getElementById('clear_time').style.display = 'none';
+    setCookie('start_time', null, 7);
+    setCookie('end_time', null, 7);
 }
 }
 
 
 // Function to run the selected playlist with specified parameters
 // Function to run the selected playlist with specified parameters
@@ -1499,6 +1504,12 @@ function saveSettingsToCookies() {
     const clearAction = document.getElementById('clear_action_label').textContent.trim();
     const clearAction = document.getElementById('clear_action_label').textContent.trim();
     setCookie('clear_action', clearAction, 7);
     setCookie('clear_action', clearAction, 7);
 
 
+    // Save start and end times
+    const startTime = document.getElementById('start_time').value;
+    const endTime = document.getElementById('end_time').value;
+    setCookie('start_time', startTime, 7);
+    setCookie('end_time', endTime, 7);
+
     logMessage('Settings saved.');
     logMessage('Settings saved.');
 }
 }
 
 
@@ -1550,6 +1561,20 @@ function loadSettingsFromCookies() {
         }
         }
     }
     }
 
 
+    // Load start and end times
+    const startTime = getCookie('start_time');
+    if (startTime !== null) {
+        document.getElementById('start_time').value = startTime;
+    }
+    const endTime = getCookie('end_time');
+    if (endTime !== null) {
+        document.getElementById('end_time').value = endTime;
+    }
+
+    if (startTime !== null || endTime !== null ) {
+        document.getElementById('clear_time').style.display = 'block';
+    }
+
     logMessage('Settings loaded from cookies.');
     logMessage('Settings loaded from cookies.');
 }
 }
 
 
@@ -1563,6 +1588,8 @@ function attachSettingsSaveListeners() {
     });
     });
     document.getElementById('shuffle_playlist').addEventListener('change', saveSettingsToCookies);
     document.getElementById('shuffle_playlist').addEventListener('change', saveSettingsToCookies);
     document.getElementById('pre_execution').addEventListener('change', saveSettingsToCookies);
     document.getElementById('pre_execution').addEventListener('change', saveSettingsToCookies);
+    document.getElementById('start_time').addEventListener('change', saveSettingsToCookies);
+    document.getElementById('end_time').addEventListener('change', saveSettingsToCookies);
 }
 }
 
 
 
 
@@ -1607,4 +1634,5 @@ document.addEventListener('DOMContentLoaded', () => {
     loadAllPlaylists(); // Load all playlists on page load
     loadAllPlaylists(); // Load all playlists on page load
     attachSettingsSaveListeners(); // Attach event listeners to save changes
     attachSettingsSaveListeners(); // Attach event listeners to save changes
     attachFullScreenListeners();
     attachFullScreenListeners();
+    fetchFirmwareInfo();
 });
 });

+ 5 - 5
static/style.css

@@ -233,7 +233,7 @@ input, select {
 }
 }
 
 
 .dropdown-content button:hover {
 .dropdown-content button:hover {
-    background-color: var(--background-primary);
+    background-color: var(--background-tertiary);
 }
 }
 
 
 
 
@@ -448,9 +448,9 @@ section .header .add-button {
     gap: 10px;
     gap: 10px;
 }
 }
 
 
-.playlist-parameters .row {
-    display: flex;
-    gap: 10px;
+.playlist-parameters .control-group button.small.cancel {
+    align-self: flex-end;
+    margin-bottom: 4px;
 }
 }
 
 
 #clear_pattern {
 #clear_pattern {
@@ -507,7 +507,7 @@ section .header .add-button {
 }
 }
 
 
 .file-list li:hover {
 .file-list li:hover {
-    background-color: var(--background-primary);
+    background-color: var(--background-tertiary);
 }
 }
 
 
 .file-list li.selected {
 .file-list li.selected {

+ 4 - 5
templates/index.html

@@ -131,9 +131,8 @@
                         <label for="end_time">End time</label>
                         <label for="end_time">End time</label>
                         <input type="time" id="end_time" min="00:00" max="24:00">
                         <input type="time" id="end_time" min="00:00" max="24:00">
                     </div>
                     </div>
+                    <button id="clear_time" onclick="clearSchedule()" style="display: none" class="small cancel">Clear</button>
                 </div>
                 </div>
-                <button onclick="clearSchedule()" class="cancel">Clear</button>
-
             </div>
             </div>
         </section>
         </section>
 
 
@@ -182,9 +181,9 @@
                         <span id="dropdown_toggle" class="dropdown-toggle" onclick="toggleClearDropdown(event)">▼</span>
                         <span id="dropdown_toggle" class="dropdown-toggle" onclick="toggleClearDropdown(event)">▼</span>
                     </button>
                     </button>
                     <div id="clear_dropdown" class="dropdown-content" style="display: none;">
                     <div id="clear_dropdown" class="dropdown-content" style="display: none;">
-                        <button onclick="updateClearAction('From Center', 'runClearIn')">Clear From Center</button>
-                        <button onclick="updateClearAction('From Perimeter', 'runClearOut')">Clear From Perimeter</button>
-                        <button onclick="updateClearAction('Sideways', 'runClearSide')">Clear Sideways</button>
+                        <button class="no-bg" onclick="updateClearAction('From Center', 'runClearIn')">Clear From Center</button>
+                        <button class="no-bg" onclick="updateClearAction('From Perimeter', 'runClearOut')">Clear From Perimeter</button>
+                        <button class="no-bg" onclick="updateClearAction('Sideways', 'runClearSide')">Clear Sideways</button>
                     </div>
                     </div>
                 </div>
                 </div>
                 <button onclick="moveToCenter()">Move to Center</button>
                 <button onclick="moveToCenter()">Move to Center</button>