Skip to content

Commit a788f33

Browse files
Enhance ESP32 manager; add COM port availability check and improve communication menu functionality
1 parent 5481c97 commit a788f33

File tree

2 files changed

+181
-58
lines changed

2 files changed

+181
-58
lines changed

.idea/workspace.xml

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

flasher.py

Lines changed: 170 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,12 @@ def main_menu(self) -> None:
4949
# Check update status
5050
update_status, update_color = self.check.update_status()
5151

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+
5256
print(" [1] Flash an ESP32 Project")
53-
print(" [2] Communicate (WIP)")
57+
print(f" [2] Communicate {com_status_color}(COM Ports Available: {len(ports)})\033[0m")
5458
print(f" [3] Updates {update_color}({update_status})\033[0m")
5559
print(" [4] Help")
5660
print(" [5] Exit\n")
@@ -136,14 +140,51 @@ def __update_menu(self) -> None:
136140
except Exception as e:
137141
handler.exception(msg=e)
138142

139-
@staticmethod # WIP
140-
def __communication_menu() -> None:
143+
def __communication_menu(self) -> None:
141144
try:
142145
print()
143146
separator("ESP32 Ultra Manager - Serial Communication")
144147
print()
145148

146-
tprint.warning("Communication feature is currently a WIP.")
149+
# Check available COM ports
150+
ports = list(serial.tools.list_ports.comports())
151+
if not ports:
152+
tprint.error("No COM ports found.")
153+
return
154+
155+
tprint.info("Select a project for communication:")
156+
tprint.info(" [1] Temporary (Custom Baud Rate)")
157+
for idx, (item, _, _, _) in enumerate(self.menu_items):
158+
tprint.info(f" [{idx + 2}] {item}")
159+
160+
print()
161+
choice = tprint.input("Enter a number to select, or 'exit' to quit: > ").strip()
162+
if choice.lower() == 'exit':
163+
return
164+
165+
try:
166+
choice = int(choice)
167+
if choice == 1:
168+
baud_rate = self.get.valid_baud_rate()
169+
if baud_rate == 'exit':
170+
return
171+
self.__start_communication(None, baud_rate)
172+
elif 2 <= choice <= len(self.menu_items) + 1:
173+
project = self.menu_items[choice - 2]
174+
folder_name, _, _, _ = project
175+
folder_path = os.path.join(self.esp32_folder, folder_name)
176+
config_path = os.path.join(folder_path, 'config.ini')
177+
self.config.read(config_path)
178+
179+
baud_rate = self.config.get('Settings', 'Baud_Rate', fallback=None)
180+
if not baud_rate or not baud_rate.isdigit():
181+
tprint.error("Invalid or missing Baud Rate in config.ini.")
182+
return
183+
self.__start_communication(folder_name, baud_rate)
184+
else:
185+
tprint.warning("Invalid selection. Returning to menu.")
186+
except ValueError:
187+
tprint.error("Invalid input. Please enter a valid number.")
147188
except Exception as e:
148189
handler.exception(msg=e)
149190

@@ -161,9 +202,9 @@ def __flasher_menu(self) -> None:
161202
tprint.info("Select a project to flash:")
162203
for idx, (item, error, issues, warn) in enumerate(self.menu_items):
163204
if error:
164-
tprint.warning(f" <{idx + 1}> {item}")
205+
tprint.warning(f" [{idx + 1}] {item}")
165206
else:
166-
tprint.info(f" <{idx + 1}> {item}")
207+
tprint.info(f" [{idx + 1}] {item}")
167208

168209
selection = tprint.input("Enter a number to select, or 'exit' to quit: > ").strip()
169210

@@ -270,21 +311,8 @@ def flash(flash_port: str, flash_folder_path: str, flash_bin_files: dict[str, st
270311
self.__flasher_menu()
271312
return
272313

273-
print()
274-
tprint.info("Available COM ports:")
275-
likely_port = None
276-
for idx, port in enumerate(ports):
277-
is_likely = 'esp' in port.description.lower() or 'usb' in port.description.lower()
278-
marker = ' <-- likely ESP32' if is_likely else ''
279-
if is_likely and not likely_port:
280-
likely_port = port.device
281-
tprint.info(f"<{idx + 1}> {port.device} - {port.description}{marker}")
282-
283-
choice = tprint.input("Select a COM port (or press enter to use suggested): > ").strip()
284-
if choice.lower() == 'exit':
285-
return
314+
selected_port = self.__com_port_menu(ports)
286315

287-
selected_port = self.get.selected_com_port(choice, likely_port, ports)
288316
if not selected_port:
289317
tprint.warning("No COM port selected, returning to menu.")
290318
self.__flasher_menu()
@@ -432,46 +460,132 @@ def __suggest_fixes(issues: list[str]) -> None:
432460
except Exception as e:
433461
handler.exception(msg=e)
434462

463+
def __com_port_menu(self, ports):
464+
print()
465+
tprint.info("Available COM ports:")
466+
likely_port = None
467+
for idx, port in enumerate(ports):
468+
is_likely = 'esp' in port.description.lower() or 'usb' in port.description.lower()
469+
marker = '\033[92m <-- likely ESP32\033[0m' if is_likely else ''
470+
if is_likely and not likely_port:
471+
likely_port = port.device
472+
tprint.info(f"[{idx + 1}] {port.device} - {port.description}{marker}")
473+
474+
choice = tprint.input("Select a COM port (or press enter to use suggested): > ").strip()
475+
if choice.lower() == 'exit':
476+
return
477+
478+
return self.get.selected_com_port(choice, likely_port, ports)
479+
480+
def __start_communication(self, project_name: str, baud_rate: str) -> None:
481+
try:
482+
ports = list(serial.tools.list_ports.comports())
483+
if not ports:
484+
tprint.error("No COM ports found.")
485+
return
486+
487+
selected_port = self.__com_port_menu(ports)
488+
489+
if not selected_port:
490+
tprint.warning("No COM port selected, returning to menu.")
491+
return
492+
493+
tprint.info(f"Connecting to {selected_port} at {baud_rate} baud...")
494+
try:
495+
def __test_connection(port_test, baud_test):
496+
try:
497+
with serial.Serial(port_test, int(baud_test), timeout=1) as ser_test:
498+
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
501+
return True
502+
except serial.SerialException as err:
503+
tprint.warning(f"Serial Exception at baud rate {baud_test}: {err}")
504+
except OSError as err:
505+
tprint.warning(f"OSError at baud rate {baud_test}: {err}")
506+
return False
507+
508+
if not __test_connection(selected_port, baud_rate):
509+
tprint.warning(f"Failed to connect at {baud_rate} baud.")
510+
choice = tprint.input(
511+
"Do you want to auto-fix and find the correct baud rate? (y/n): > ").strip().lower()
512+
if choice == 'y':
513+
for rate in Check.esp32_supported_baudrates:
514+
tprint.info(f"Trying baud rate: {rate}")
515+
if __test_connection(selected_port, rate):
516+
baud_rate = rate
517+
tprint.success(f"Connection successful at {baud_rate} baud.")
518+
break
519+
else:
520+
tprint.error("Unable to find a working baud rate. Returning to menu.")
521+
return
522+
else:
523+
tprint.warning("Auto-fix skipped. Returning to menu.")
524+
return
525+
526+
with serial.Serial(selected_port, int(baud_rate), timeout=1) as ser:
527+
print()
528+
separator(f"Session {project_name or 'Temporary'} Started")
529+
print("Press Ctrl+C to exit the session.\n")
530+
while True:
531+
try:
532+
if ser.in_waiting > 0:
533+
print(ser.read(ser.in_waiting).decode(errors='ignore'), end='', flush=True)
534+
except KeyboardInterrupt:
535+
break
536+
except Exception as e:
537+
handler.exception(msg=e)
538+
break
539+
except Exception as e:
540+
handler.exception(msg=e)
541+
return
542+
finally:
543+
print()
544+
separator(f"Session {project_name or 'Has'} Ended")
545+
except Exception as e:
546+
handler.exception(msg=e)
547+
435548

436549
class Check:
550+
esp32_supported_baudrates = [
551+
50,
552+
75,
553+
110,
554+
134,
555+
150,
556+
200,
557+
300,
558+
600,
559+
1200,
560+
1800,
561+
2400,
562+
4800,
563+
9600, # Default baudrate for many USB-UART bridges
564+
14400,
565+
19200,
566+
28800,
567+
38400,
568+
57600,
569+
74880, # Default bootloader baudrate
570+
115200, # Most common and default flashing speed
571+
128000,
572+
230400,
573+
256000,
574+
460800, # Fast and reliable
575+
512000,
576+
921600, # Maximum reliable speed for many USB-UART bridges (CP210x, CH340)
577+
1000000,
578+
1152000,
579+
1500000,
580+
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
585+
]
586+
437587
def __init__(self, config):
438588
self.config = config
439-
self.esp32_supported_baudrates = [
440-
50,
441-
75,
442-
110,
443-
134,
444-
150,
445-
200,
446-
300,
447-
600,
448-
1200,
449-
1800,
450-
2400,
451-
4800,
452-
9600, # Default baudrate for many USB-UART bridges
453-
14400,
454-
19200,
455-
28800,
456-
38400,
457-
57600,
458-
74880, # Default bootloader baudrate
459-
115200, # Most common and default flashing speed
460-
128000,
461-
230400,
462-
256000,
463-
460800, # Fast and reliable
464-
512000,
465-
921600, # Maximum reliable speed for many USB-UART bridges (CP210x, CH340)
466-
1000000,
467-
1152000,
468-
1500000,
469-
2000000, # Often flaky unless high-end bridge + short cable
470-
2500000,
471-
3000000,
472-
3500000,
473-
4000000 # ESP32 supports it, but most bridges can't
474-
]
475589

476590
def project(self, menu_items, folder_name: str, folder_path: str) -> None:
477591
try:

0 commit comments

Comments
 (0)