Skip to content

Commit b96a010

Browse files
Enhance ESP32 manager; improve main menu functionality and add COM port availability check
1 parent a788f33 commit b96a010

File tree

4 files changed

+107
-57
lines changed

4 files changed

+107
-57
lines changed

.idea/misc.xml

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/workspace.xml

Lines changed: 29 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

PLANS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77
88
| Task | Expected Version | Might or Will be done? |
99
|--------------------------|------------------|------------------------|
10+
| Port to Linux | v1.2.0 ||
1011
| Add esp8266 capabilities | v2.0.0 ||

flasher.py

Lines changed: 73 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import configparser
2+
import msvcrt
23
import os
34
import shutil
45
import subprocess
@@ -41,43 +42,54 @@ def __load_menu_items(self) -> None:
4142
# ---------------------------- Menu methods ----------------------------- #
4243

4344
def main_menu(self) -> None:
44-
while True:
45-
print()
46-
separator("ESP32 Ultra Manager - Main Menu")
47-
print()
45+
try:
46+
while True:
47+
print()
48+
separator("ESP32 Ultra Manager - Main Menu")
49+
print()
4850

49-
# Check update status
50-
update_status, update_color = self.check.update_status()
51+
# Check update status
52+
update_status, update_color = self.check.update_status()
5153

52-
# Check COM port availability
53-
ports = list(serial.tools.list_ports.comports())
54-
com_status_color = "\033[91m" if not ports else "\033[97m" # Red if no ports, white otherwise
55-
56-
print(" [1] Flash an ESP32 Project")
57-
print(f" [2] Communicate {com_status_color}(COM Ports Available: {len(ports)})\033[0m")
58-
print(f" [3] Updates {update_color}({update_status})\033[0m")
59-
print(" [4] Help")
60-
print(" [5] Exit\n")
61-
choice = tprint.input("Select an option: > ").strip()
62-
63-
if choice == '1':
64-
self.__flasher_menu()
65-
elif choice == '2':
66-
self.__communication_menu()
67-
elif choice == '3':
68-
self.__update_menu()
69-
elif choice == '4':
70-
print()
71-
tprint.info("ESP32 Ultra Manager - Help")
72-
print("A tool to flash ESP32 devices from organized folders under ./esp32")
73-
print("Each folder must contain .bin files and a config.ini")
74-
print("The tool supports auto-generation of config.ini and update via Git.")
75-
elif choice == '5' or choice.lower() == 'exit':
76-
break
77-
else:
78-
tprint.warning("Invalid selection. Try again.")
54+
# Check COM port availability
55+
ports = list(serial.tools.list_ports.comports())
56+
com_status_color = "\033[91m" if not ports else "\033[97m" # Red if no ports, white otherwise
57+
58+
print(" [1] Flash an ESP32 Project")
59+
print(f" [2] Communicate {com_status_color}(COM Ports Available: {len(ports)})\033[0m")
60+
print(f" [3] Updates {update_color}({update_status})\033[0m")
61+
print(" [4] Help")
62+
print(" [5] Exit\n")
7963

80-
def __update_menu(self) -> None:
64+
while msvcrt.kbhit():
65+
msvcrt.getch()
66+
67+
try:
68+
choice = tprint.input("Select an option: > ").strip()
69+
except Exception:
70+
tprint.warning("If you see this after serial communication automatically, its a known bug as the stdin isn't successfully cleared for some reason.")
71+
choice = tprint.input("Select an option: > ").strip()
72+
73+
if choice == '1':
74+
self._flasher_menu()
75+
elif choice == '2':
76+
self._communication_menu()
77+
elif choice == '3':
78+
self._update_menu()
79+
elif choice == '4':
80+
print()
81+
tprint.info("ESP32 Ultra Manager - Help")
82+
print("A tool to flash ESP32 devices from organized folders under ./esp32")
83+
print("Each folder must contain .bin files and a config.ini")
84+
print("The tool supports auto-generation of config.ini and update via Git.")
85+
elif choice == '5' or choice.lower() == 'exit':
86+
break
87+
else:
88+
tprint.warning("Invalid selection. Try again.")
89+
except Exception as e:
90+
handler.exception(msg=e)
91+
92+
def _update_menu(self) -> None:
8193
print()
8294
separator("ESP32 Ultra Manager - Update")
8395
print()
@@ -140,7 +152,7 @@ def __update_menu(self) -> None:
140152
except Exception as e:
141153
handler.exception(msg=e)
142154

143-
def __communication_menu(self) -> None:
155+
def _communication_menu(self) -> None:
144156
try:
145157
print()
146158
separator("ESP32 Ultra Manager - Serial Communication")
@@ -188,7 +200,7 @@ def __communication_menu(self) -> None:
188200
except Exception as e:
189201
handler.exception(msg=e)
190202

191-
def __flasher_menu(self) -> None:
203+
def _flasher_menu(self) -> None:
192204
try:
193205
print()
194206
separator("ESP32 Ultra Manager - Flashing")
@@ -308,14 +320,14 @@ def flash(flash_port: str, flash_folder_path: str, flash_bin_files: dict[str, st
308320
ports = list(serial.tools.list_ports.comports())
309321
if not ports:
310322
tprint.error("No COM ports found.")
311-
self.__flasher_menu()
323+
self._flasher_menu()
312324
return
313325

314326
selected_port = self.__com_port_menu(ports)
315327

316328
if not selected_port:
317329
tprint.warning("No COM port selected, returning to menu.")
318-
self.__flasher_menu()
330+
self._flasher_menu()
319331
return
320332

321333
# Flash the ESP32
@@ -343,7 +355,7 @@ def __handle_issues(self, item: tuple[str, str, list[str], list[str]]) -> None:
343355
self.__show_issues(folder_path)
344356

345357
self.check.project(self.menu_items, folder_name, folder_path)
346-
self.__flasher_menu()
358+
self._flasher_menu()
347359
else:
348360
self.__flash_esp32(folder_name)
349361
except Exception as e:
@@ -490,14 +502,16 @@ def __start_communication(self, project_name: str, baud_rate: str) -> None:
490502
tprint.warning("No COM port selected, returning to menu.")
491503
return
492504

505+
print()
493506
tprint.info(f"Connecting to {selected_port} at {baud_rate} baud...")
494507
try:
495508
def __test_connection(port_test, baud_test):
496509
try:
497510
with serial.Serial(port_test, int(baud_test), timeout=1) as ser_test:
498511
ser_test.write(b'\x55') # Send a recognizable byte pattern (e.g., 0x55)
499-
response = ser_test.read(1) # Read a single byte response
500-
if response == b'\x55': # Check if the response matches the sent byte
512+
response = ser_test.read(100).decode(errors='ignore')
513+
if "ESP" in response or "rst:" in response:
514+
tprint.debug(f"Received response at baud rate {baud_test}: \n{response}")
501515
return True
502516
except serial.SerialException as err:
503517
tprint.warning(f"Serial Exception at baud rate {baud_test}: {err}")
@@ -510,18 +524,22 @@ def __test_connection(port_test, baud_test):
510524
choice = tprint.input(
511525
"Do you want to auto-fix and find the correct baud rate? (y/n): > ").strip().lower()
512526
if choice == 'y':
513-
for rate in Check.esp32_supported_baudrates:
514-
tprint.info(f"Trying baud rate: {rate}")
527+
tprint.info("Attempting to find a working baud rate...")
528+
for rate in sorted(Check.esp32_supported_baudrates, reverse=True):
529+
tprint.debug(f"Trying baud rate: {rate}")
515530
if __test_connection(selected_port, rate):
516531
baud_rate = rate
517532
tprint.success(f"Connection successful at {baud_rate} baud.")
533+
self.config.set('Settings', 'Baud_Rate', str(baud_rate))
534+
with open(os.path.join(self.esp32_folder, project_name, 'config.ini'), 'w') as configfile:
535+
self.config.write(configfile)
536+
tprint.info(f"Baud rate {baud_rate} saved to config.ini.")
518537
break
519538
else:
520-
tprint.error("Unable to find a working baud rate. Returning to menu.")
539+
tprint.warning("Unable to find a working baud rate. Returning to menu.")
521540
return
522541
else:
523-
tprint.warning("Auto-fix skipped. Returning to menu.")
524-
return
542+
tprint.info("Auto-fix skipped. Connecting...")
525543

526544
with serial.Serial(selected_port, int(baud_rate), timeout=1) as ser:
527545
print()
@@ -532,16 +550,24 @@ def __test_connection(port_test, baud_test):
532550
if ser.in_waiting > 0:
533551
print(ser.read(ser.in_waiting).decode(errors='ignore'), end='', flush=True)
534552
except KeyboardInterrupt:
553+
if ser.is_open:
554+
tprint.debug("Closing serial port and resetting input buffer.")
555+
ser.reset_input_buffer() # Clear any existing data in the serial buffer
556+
ser.close()
535557
break
536558
except Exception as e:
537559
handler.exception(msg=e)
560+
if ser.is_open:
561+
tprint.debug("Closing serial port and resetting input buffer.")
562+
ser.reset_input_buffer() # Clear any existing data in the serial buffer
563+
ser.close()
538564
break
539565
except Exception as e:
540566
handler.exception(msg=e)
541567
return
542568
finally:
543569
print()
544-
separator(f"Session {project_name or 'Has'} Ended")
570+
separator(f"Session {project_name or 'Temporary'} Ended")
545571
except Exception as e:
546572
handler.exception(msg=e)
547573

@@ -578,10 +604,6 @@ class Check:
578604
1152000,
579605
1500000,
580606
2000000, # Often flaky unless high-end bridge + short cable
581-
2500000,
582-
3000000,
583-
3500000,
584-
4000000 # ESP32 supports it, but most bridges can't
585607
]
586608

587609
def __init__(self, config):

0 commit comments

Comments
 (0)