diff --git a/.gitignore b/.gitignore index 2445511..8a6642f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ *.pyc +*.spec + /fw/* +/dist/ +/build/ diff --git a/arduino/control/control.ino b/arduino/control/control.ino new file mode 100644 index 0000000..b4678c4 --- /dev/null +++ b/arduino/control/control.ino @@ -0,0 +1,73 @@ +const int PIN_PG = 2; // GPIO 2 +const int PIN_RESET = 4; // GPIO 4 + +String inputCommand = ""; + +void setup() { + Serial.begin(115200); + delay(1000); // give some time for Serial to attach + + pinMode(PIN_PG, OUTPUT); + digitalWrite(PIN_PG, LOW); + + pinMode(PIN_RESET, INPUT); + + Serial.println("Booting..."); + Serial.println("Ready. Commands: 'pg 1', 'pg 0', 'reset'"); +} + +void loop() { + // Heartbeat + static unsigned long lastBeat = 0; + if (millis() - lastBeat > 2000) { + lastBeat = millis(); + } + + while (Serial.available() > 0) { + char c = Serial.read(); + + // Echo back what we received so you can see it + Serial.write(c); + + // Check for end of line + if (c == '\n' || c == '\r') { + if (inputCommand.length() > 0) { + Serial.print("\nReceived command: ["); + Serial.print(inputCommand); + Serial.println("]"); + + handleCommand(inputCommand); + inputCommand = ""; + } + } else { + inputCommand += c; + } + } +} + +void handleCommand(String cmd) { + cmd.trim(); // remove spaces/newlines + + if (cmd == "pg 1") { + pinMode(PIN_PG, OUTPUT); + digitalWrite(PIN_PG, HIGH); + Serial.println("PG -> HIGH"); + + } else if (cmd == "pg 0") { + pinMode(PIN_PG, OUTPUT); + digitalWrite(PIN_PG, LOW); + Serial.println("PG -> LOW"); + + } else if (cmd == "reset") { + Serial.println("Reset sequence on GPIO 4"); + pinMode(PIN_RESET, OUTPUT); + digitalWrite(PIN_RESET, LOW); + delay(500); // 0.5 sec + pinMode(PIN_RESET, INPUT); + Serial.println("GPIO 4 back to INPUT"); + + } else { + Serial.print("Unknown command: "); + Serial.println(cmd); + } +} diff --git a/build/flash/Analysis-01.toc b/build/flash/Analysis-01.toc index 40d5031..dc35c05 100644 --- a/build/flash/Analysis-01.toc +++ b/build/flash/Analysis-01.toc @@ -1,5 +1,5 @@ -(['C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\flash.py'], - ['C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2'], +(['C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\flash.py'], + ['C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3'], [], [('C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\numpy\\_pyinstaller', 0), @@ -25,7 +25,7 @@ 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py', 'PYSOURCE'), ('flash', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\flash.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\flash.py', 'PYSOURCE')], [('zipfile', 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\zipfile.py', @@ -307,16 +307,16 @@ 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\stringprep.py', 'PYMODULE'), ('version_info', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\version_info.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\version_info.py', 'PYMODULE'), ('base', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\__init__.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\__init__.py', 'PYMODULE'), ('base.rt_settings', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\rt_settings.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\rt_settings.py', 'PYMODULE'), ('base.rtk_logging', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\rtk_logging.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\rtk_logging.py', 'PYMODULE'), ('colorama', 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\colorama\\__init__.py', @@ -346,10 +346,10 @@ 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\ctypes\\_endian.py', 'PYMODULE'), ('base.download_handler', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\download_handler.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\download_handler.py', 'PYMODULE'), ('base.remote_serial', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\remote_serial.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\remote_serial.py', 'PYMODULE'), ('serial.serialutil', 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\serialutil.py', @@ -370,61 +370,61 @@ 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\json\\scanner.py', 'PYMODULE'), ('base.config_utils', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\config_utils.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\config_utils.py', 'PYMODULE'), ('base.memory_info', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\memory_info.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\memory_info.py', 'PYMODULE'), ('base.spic_addr_mode', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\spic_addr_mode.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\spic_addr_mode.py', 'PYMODULE'), ('base.json_utils', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\json_utils.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\json_utils.py', 'PYMODULE'), ('pyDes', 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\pyDes.py', 'PYMODULE'), ('base.device_profile', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\device_profile.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\device_profile.py', 'PYMODULE'), ('base.version', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\version.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\version.py', 'PYMODULE'), ('base.efuse_data', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\efuse_data.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\efuse_data.py', 'PYMODULE'), ('base.image_info', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\image_info.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\image_info.py', 'PYMODULE'), ('base.flash_utils', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\flash_utils.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\flash_utils.py', 'PYMODULE'), ('base.sys_utils', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\sys_utils.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\sys_utils.py', 'PYMODULE'), ('base.floader_handler', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\floader_handler.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\floader_handler.py', 'PYMODULE'), ('base.next_op', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\next_op.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\next_op.py', 'PYMODULE'), ('base.device_info', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\device_info.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\device_info.py', 'PYMODULE'), ('base.rtk_flash_type', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\rtk_flash_type.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\rtk_flash_type.py', 'PYMODULE'), ('base.sense_status', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\sense_status.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\sense_status.py', 'PYMODULE'), ('base.errno', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\errno.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\errno.py', 'PYMODULE'), ('base.rom_handler', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\rom_handler.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\rom_handler.py', 'PYMODULE'), ('base.rtk_utils', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\base\\rtk_utils.py', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\base\\rtk_utils.py', 'PYMODULE'), ('serial', 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\serial\\__init__.py', @@ -528,64 +528,16 @@ [], [], [('base_library.zip', - 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\build\\flash\\base_library.zip', + 'C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\build\\flash\\base_library.zip', 'DATA')], - [('warnings', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\warnings.py', - 'PYMODULE'), - ('genericpath', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\genericpath.py', - 'PYMODULE'), - ('reprlib', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\reprlib.py', - 'PYMODULE'), - ('copyreg', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\copyreg.py', - 'PYMODULE'), - ('posixpath', + [('posixpath', 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\posixpath.py', 'PYMODULE'), - ('keyword', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\keyword.py', + ('collections.abc', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\collections\\abc.py', 'PYMODULE'), - ('abc', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\abc.py', - 'PYMODULE'), - ('_collections_abc', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_collections_abc.py', - 'PYMODULE'), - ('ntpath', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\ntpath.py', - 'PYMODULE'), - ('weakref', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\weakref.py', - 'PYMODULE'), - ('operator', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\operator.py', - 'PYMODULE'), - ('io', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\io.py', - 'PYMODULE'), - ('locale', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\locale.py', - 'PYMODULE'), - ('stat', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\stat.py', - 'PYMODULE'), - ('enum', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\enum.py', - 'PYMODULE'), - ('types', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\types.py', - 'PYMODULE'), - ('traceback', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\traceback.py', - 'PYMODULE'), - ('sre_compile', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\sre_compile.py', - 'PYMODULE'), - ('heapq', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\heapq.py', + ('collections', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\collections\\__init__.py', 'PYMODULE'), ('re._parser', 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\re\\_parser.py', @@ -599,24 +551,78 @@ ('re._casefix', 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\re\\_casefix.py', 'PYMODULE'), - ('_weakrefset', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_weakrefset.py', + ('copyreg', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\copyreg.py', + 'PYMODULE'), + ('types', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\types.py', + 'PYMODULE'), + ('warnings', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\warnings.py', 'PYMODULE'), ('functools', 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\functools.py', 'PYMODULE'), - ('collections.abc', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\collections\\abc.py', + ('linecache', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\linecache.py', 'PYMODULE'), - ('collections', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\collections\\__init__.py', + ('_weakrefset', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_weakrefset.py', + 'PYMODULE'), + ('stat', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\stat.py', + 'PYMODULE'), + ('reprlib', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\reprlib.py', + 'PYMODULE'), + ('sre_compile', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\sre_compile.py', + 'PYMODULE'), + ('locale', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\locale.py', + 'PYMODULE'), + ('abc', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\abc.py', 'PYMODULE'), ('sre_constants', 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\sre_constants.py', 'PYMODULE'), + ('traceback', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\traceback.py', + 'PYMODULE'), + ('enum', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\enum.py', + 'PYMODULE'), + ('ntpath', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\ntpath.py', + 'PYMODULE'), ('sre_parse', 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\sre_parse.py', 'PYMODULE'), + ('codecs', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\codecs.py', + 'PYMODULE'), + ('weakref', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\weakref.py', + 'PYMODULE'), + ('heapq', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\heapq.py', + 'PYMODULE'), + ('genericpath', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\genericpath.py', + 'PYMODULE'), + ('operator', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\operator.py', + 'PYMODULE'), + ('_collections_abc', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\_collections_abc.py', + 'PYMODULE'), + ('keyword', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\keyword.py', + 'PYMODULE'), + ('io', + 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\io.py', + 'PYMODULE'), ('encodings.zlib_codec', 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\zlib_codec.py', 'PYMODULE'), @@ -983,12 +989,6 @@ ('encodings', 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\encodings\\__init__.py', 'PYMODULE'), - ('linecache', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\linecache.py', - 'PYMODULE'), - ('codecs', - 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\codecs.py', - 'PYMODULE'), ('re', 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\re\\__init__.py', 'PYMODULE'), diff --git a/build/flash/EXE-01.toc b/build/flash/EXE-01.toc index 3eb4807..6be1f92 100644 --- a/build/flash/EXE-01.toc +++ b/build/flash/EXE-01.toc @@ -1,9 +1,9 @@ -('C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\dist\\flash.exe', +('C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.3\\dist\\flash.exe', False, False, False, 'C:\\Users\\wongyiekheng\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\PyInstaller\\bootloader\\images\\icon-windowed.ico', - versioninfo.VSVersionInfo(ffi=versioninfo.FixedFileInfo(filevers=(1, 2, 2, 0), prodvers=(1, 2, 2, 0), mask=0x3f, flags=0x0, OS=0x40004, fileType=1, subtype=0x0, date=(0, 0)), kids=[versioninfo.StringFileInfo([versioninfo.StringTable('040904B0', [versioninfo.StringStruct('CompanyName', 'Realtek Semiconductor Corp.'), versioninfo.StringStruct('FileDescription', 'Flash Tool for Realtek Ameba SoCs'), versioninfo.StringStruct('FileVersion', '1.2.2.0'), versioninfo.StringStruct('InternalName', 'flash.exe'), versioninfo.StringStruct('LegalCopyright', 'Copyright (c) 2025 Realtek Semiconductor Corp.'), versioninfo.StringStruct('OriginalFilename', 'flash.exe'), versioninfo.StringStruct('ProductName', 'Ameba Flash Tool'), versioninfo.StringStruct('ProductVersion', '1.2.2.0')])]), versioninfo.VarFileInfo([versioninfo.VarStruct('Translation', [1033, 1200])])]), + versioninfo.VSVersionInfo(ffi=versioninfo.FixedFileInfo(filevers=(1, 2, 3, 0), prodvers=(1, 2, 3, 0), mask=0x3f, flags=0x0, OS=0x40004, fileType=1, subtype=0x0, date=(0, 0)), kids=[versioninfo.StringFileInfo([versioninfo.StringTable('040904B0', [versioninfo.StringStruct('CompanyName', 'Realtek Semiconductor Corp.'), versioninfo.StringStruct('FileDescription', 'Flash Tool for Realtek Ameba SoCs'), versioninfo.StringStruct('FileVersion', '1.2.3.0'), versioninfo.StringStruct('InternalName', 'flash.exe'), versioninfo.StringStruct('LegalCopyright', 'Copyright (c) 2025 Realtek Semiconductor Corp.'), versioninfo.StringStruct('OriginalFilename', 'flash.exe'), versioninfo.StringStruct('ProductName', 'Ameba Flash Tool'), versioninfo.StringStruct('ProductVersion', '1.2.3.0')])]), versioninfo.VarFileInfo([versioninfo.VarStruct('Translation', [1033, 1200])])]), False, False, b'\n - flash.py + flash.py Script
imports: _collections_abc @@ -1157,7 +1157,7 @@ imported by:
- base + base Package
imports: base.download_handler @@ -1197,7 +1197,7 @@ imported by:
- base.config_utils + base.config_utils SourceModule
imports: base @@ -1214,7 +1214,7 @@ imported by:
- base.device_info + base.device_info SourceModule
imports: base @@ -1232,7 +1232,7 @@ imported by:
- base.device_profile + base.device_profile SourceModule
imports: base @@ -1251,7 +1251,7 @@ imported by:
- base.download_handler + base.download_handler SourceModule
imports: base @@ -1281,7 +1281,7 @@ imported by:
- base.efuse_data + base.efuse_data SourceModule
imports: base @@ -1297,7 +1297,7 @@ imported by:
- base.errno + base.errno SourceModule
imports: base @@ -1315,7 +1315,7 @@ imported by:
- base.flash_utils + base.flash_utils SourceModule
imports: base @@ -1334,7 +1334,7 @@ imported by:
- base.floader_handler + base.floader_handler SourceModule
imports: base @@ -1356,7 +1356,7 @@ imported by:
- base.image_info + base.image_info SourceModule
imports: base @@ -1372,7 +1372,7 @@ imported by:
- base.json_utils + base.json_utils SourceModule
imports: base @@ -1392,7 +1392,7 @@ imported by:
- base.memory_info + base.memory_info SourceModule
imports: base @@ -1409,7 +1409,7 @@ imported by:
- base.next_op + base.next_op SourceModule
imports: base @@ -1426,7 +1426,7 @@ imported by:
- base.remote_serial + base.remote_serial SourceModule
imports: base @@ -1451,7 +1451,7 @@ imported by:
- base.rom_handler + base.rom_handler SourceModule
imports: base @@ -1473,7 +1473,7 @@ imported by:
- base.rt_settings + base.rt_settings SourceModule
imports: base @@ -1490,7 +1490,7 @@ imported by:
- base.rtk_flash_type + base.rtk_flash_type SourceModule
imports: base @@ -1507,7 +1507,7 @@ imported by:
- base.rtk_logging + base.rtk_logging SourceModule
imports: base @@ -1526,7 +1526,7 @@ imported by:
- base.rtk_utils + base.rtk_utils SourceModule
imports: base @@ -1544,7 +1544,7 @@ imported by:
- base.sense_status + base.sense_status SourceModule
imports: base @@ -1561,7 +1561,7 @@ imported by:
- base.spic_addr_mode + base.spic_addr_mode SourceModule
imports: base @@ -1578,7 +1578,7 @@ imported by:
- base.sys_utils + base.sys_utils SourceModule
imports: base @@ -1594,7 +1594,7 @@ imported by:
- base.version + base.version SourceModule
imports: base @@ -8246,7 +8246,7 @@ imported by:
- version_info + version_info SourceModule
imported by: flash.py diff --git a/command_history.json b/command_history.json new file mode 100644 index 0000000..0c5adb7 --- /dev/null +++ b/command_history.json @@ -0,0 +1,15 @@ +{ + "raw:AmebaPro3": [ + "video run 0", + "video run 1", + "ainr start", + "ainr stop", + "ainr status", + "rd 0x40220034", + "wd 0x40220034 0x200", + "rd 0x40220034" + ], + "debugger:NP:Cortex-M33": [ + "x 0x0" + ] +} \ No newline at end of file diff --git a/pro3_uart.py b/pro3_uart.py index b10b9ef..cdab24e 100644 --- a/pro3_uart.py +++ b/pro3_uart.py @@ -3,6 +3,7 @@ from __future__ import annotations import datetime +import json import os import re import shutil @@ -47,12 +48,19 @@ FIRMWARE_COPY_MAP = [ ("fw_en.bin", "amebapro3_app.bin"), ("np_boot.bin", "amebapro3_boot.bin"), ] +ARDUINO_SKETCH_DIR = os.path.join(APP_ROOT, "arduino", "control") +ARDUINO_SKETCH_PATH = os.path.join(ARDUINO_SKETCH_DIR, "control.ino") +_DEFAULT_ARDUINO_CLI = os.path.join(APP_ROOT, "arduino", "arduino-cli.exe") +ARDUINO_CLI = os.environ.get("ARDUINO_CLI", _DEFAULT_ARDUINO_CLI if os.path.exists(_DEFAULT_ARDUINO_CLI) else "arduino-cli") +ARDUINO_FQBN = os.environ.get("ARDUINO_FQBN", "arduino:avr:uno") PORT_REFRESH_MS = 1000 LOG_HISTORY_LIMIT = 2000 HISTORY_LIMIT = 200 JLINK_SCRIPT_EXTS = {".jlink", ".jscr", ".jsf", ".txt"} GDB_SCRIPT_EXTS = {".gdb", ".gdbinit"} +COMMAND_HISTORY_FILE = os.path.join(APP_ROOT, "command_history.json") +_HISTORY_FILE_LOCK = threading.Lock() def _folder_version_guess() -> str: @@ -102,6 +110,264 @@ def safe_int(value) -> Optional[int]: return None +def _should_skip_history_entry(text: str) -> bool: + entry = (text or "").strip().lower() + if not entry: + return True + if "testmode" in entry: + return True + if entry.startswith("ready. commands:") and "testmode" in entry: + return True + return False + + +def _load_history_store() -> dict: + with _HISTORY_FILE_LOCK: + try: + with open(COMMAND_HISTORY_FILE, "r", encoding="utf-8") as handle: + data = json.load(handle) + return data if isinstance(data, dict) else {} + except FileNotFoundError: + return {} + except Exception: + return {} + + +def _persist_history_store(data: dict) -> None: + with _HISTORY_FILE_LOCK: + try: + tmp_path = COMMAND_HISTORY_FILE + ".tmp" + with open(tmp_path, "w", encoding="utf-8") as handle: + json.dump(data, handle, indent=2) + os.replace(tmp_path, COMMAND_HISTORY_FILE) + except Exception: + pass + + +_CONSOLE_GRIP_STYLE = "ConsoleSplitGrip.TFrame" +_CONSOLE_GRIP_STYLE_READY = False + + +def _ensure_console_grip_style() -> None: + global _CONSOLE_GRIP_STYLE_READY + if _CONSOLE_GRIP_STYLE_READY: + return + try: + style = ttk.Style() + style.configure(_CONSOLE_GRIP_STYLE, background="#9e9e9e", borderwidth=0, relief="flat") + except tk.TclError: + return + _CONSOLE_GRIP_STYLE_READY = True + + +class ConsoleFindBar: + """Inline find controls embedded in a console frame.""" + + TAG = "find_highlight" + + def __init__(self, parent: tk.Widget, text_widget: tk.Text) -> None: + self.text_widget = text_widget + self.frame = ttk.Frame(parent) + self.frame.columnconfigure(1, weight=1) + + self.query_var = tk.StringVar() + self.match_case_var = tk.BooleanVar(value=False) + self.status_var = tk.StringVar(value="") + self._visible = True + self._last_query = "" + self._current_range: Optional[tuple[str, str]] = None + self._child_layouts: dict[tk.Widget, dict] = {} + self._preferred_height: Optional[int] = None + + ttk.Label(self.frame, text="Find:").grid(row=0, column=0, sticky="w", padx=(0, 4)) + entry = ttk.Entry(self.frame, textvariable=self.query_var) + entry.grid(row=0, column=1, sticky="ew") + entry.bind("", lambda _evt: self.find_next()) + self.entry = entry + + buttons = ttk.Frame(self.frame) + buttons.grid(row=0, column=2, padx=(6, 0)) + ttk.Button(buttons, text="Next", width=8, command=self.find_next).grid(row=0, column=0, padx=(0, 3)) + ttk.Button(buttons, text="Prev", width=8, command=self.find_prev).grid(row=0, column=1, padx=(0, 3)) + ttk.Button(buttons, text="Find All", width=10, command=self.find_all).grid(row=0, column=2, padx=(0, 3)) + + ttk.Checkbutton(self.frame, text="Match case", variable=self.match_case_var).grid( + row=0, column=3, padx=(6, 0) + ) + + ttk.Label(self.frame, textvariable=self.status_var, foreground="gray").grid( + row=1, column=0, columnspan=4, sticky="w", pady=(4, 0) + ) + + self.text_widget.tag_configure(self.TAG, background="#fff59d") + self.query_var.trace_add("write", lambda *_: self._reset_progress()) + + def grid(self, **kwargs) -> None: + self.frame.grid(**kwargs) + if not self._child_layouts: + self._capture_child_layout() + self._ensure_frame_height() + self._restore_child_layout() + + def show(self) -> None: + self.entry.focus_set() + + def hide(self) -> None: + pass + + def toggle(self) -> None: + self.entry.focus_set() + + def find_next(self) -> None: + self._find(backwards=False) + + def find_prev(self) -> None: + self._find(backwards=True) + + def find_all(self) -> None: + query = self._current_query() + if not query: + return + self.clear_highlight() + start = "1.0" + count = 0 + while True: + idx = self.text_widget.search( + query, + start, + stopindex="end", + nocase=not self.match_case_var.get(), + ) + if not idx: + break + end = self.text_widget.index(f"{idx}+{len(query)}c") + self.text_widget.tag_add(self.TAG, idx, end) + start = end + count += 1 + if count: + self.status_var.set(f"{count} matches highlighted.") + else: + self.status_var.set("No matches found.") + self._current_range = None + + def clear_highlight(self) -> None: + try: + self.text_widget.tag_remove(self.TAG, "1.0", "end") + except tk.TclError: + pass + + def _current_query(self) -> str: + text = (self.query_var.get() or "").strip() + if not text: + self.status_var.set("Enter text to find.") + self.clear_highlight() + self._current_range = None + return "" + return text + + def _find(self, backwards: bool) -> None: + query = self._current_query() + if not query: + return + if query != self._last_query: + self._current_range = None + self._last_query = query + + start = self._initial_anchor(backwards) + stop = "1.0" if backwards else "end" + idx = self.text_widget.search( + query, + start, + stopindex=stop, + nocase=not self.match_case_var.get(), + backwards=backwards, + ) + wrapped = False + if not idx: + wrapped = True + start = "end" if backwards else "1.0" + idx = self.text_widget.search( + query, + start, + stopindex=stop, + nocase=not self.match_case_var.get(), + backwards=backwards, + ) + if not idx: + self.status_var.set("No matches found.") + self.clear_highlight() + self._current_range = None + self.text_widget.bell() + return + + end = self.text_widget.index(f"{idx}+{len(query)}c") + self._current_range = (idx, end) + self.clear_highlight() + self.text_widget.tag_add(self.TAG, idx, end) + try: + self.text_widget.see(idx) + self.text_widget.mark_set("insert", end) + except tk.TclError: + pass + self.status_var.set("Wrapped search." if wrapped else "") + + def _initial_anchor(self, backwards: bool) -> str: + if self._current_range: + anchor = self._current_range[0] if backwards else self._current_range[1] + if backwards: + try: + return self.text_widget.index(f"{anchor} -1c") + except tk.TclError: + return "1.0" + return anchor + return "end" if backwards else "1.0" + + def _reset_progress(self) -> None: + self._current_range = None + self._last_query = "" + self.status_var.set("") + + def _capture_child_layout(self) -> None: + children = list(self.frame.grid_slaves()) + for child in children: + info = child.grid_info() + # grid_info values are strings; convert ints for needed keys. + for key in ("row", "column", "columnspan", "rowspan", "ipadx", "ipady", "padx", "pady"): + if key in info and isinstance(info[key], str): + try: + info[key] = int(info[key]) + except ValueError: + pass + self._child_layouts[child] = info + + def _ensure_frame_height(self) -> None: + if self._preferred_height is None: + try: + self.frame.update_idletasks() + self._preferred_height = max(1, self.frame.winfo_reqheight()) + except tk.TclError: + self._preferred_height = 1 + if self._preferred_height: + try: + self.frame.configure(height=self._preferred_height) + self.frame.grid_propagate(False) + except tk.TclError: + pass + + def _apply_hidden_state(self) -> None: + for child in self._child_layouts: + try: + child.grid_remove() + except tk.TclError: + pass + + def _restore_child_layout(self) -> None: + for child, info in self._child_layouts.items(): + try: + child.grid(**info) + except tk.TclError: + pass + class DevicePanel: """Single UART console with optional macro buttons and flashing control.""" @@ -115,6 +381,7 @@ class DevicePanel: console_height: int = 15, flash_handler: Optional[Callable[[], None]] = None, flash_button_label: str = "Flash Firmware", + arduino_flash_handler: Optional[Callable[[], None]] = None, on_connect: Optional[Callable[[str], None]] = None, on_disconnect: Optional[Callable[[str], None]] = None, exclude_ports_provider: Optional[Callable[[], Iterable[str]]] = None, @@ -124,6 +391,7 @@ class DevicePanel: self.line_ending = line_ending or "" self.flash_handler = flash_handler self.flash_button_label = flash_button_label + self.arduino_flash_handler = arduino_flash_handler self.on_connect = on_connect self.on_disconnect = on_disconnect self.exclude_ports_provider = exclude_ports_provider @@ -143,9 +411,15 @@ class DevicePanel: self.raw_history_index = 0 self.arduino_history: List[str] = [] self.arduino_history_index = 0 + self.history_frame = None + self.history_grip: Optional[ttk.Frame] = None + self.find_bar: Optional[ConsoleFindBar] = None + self._sash_drag_start: Optional[int] = None + self._sash_drag_initial = 0 self.frame = ttk.LabelFrame(parent, text=title) self._build_widgets(console_height, default_baud) + self._load_history_cache() self.enable_controls(False) self.refresh_ports() self._schedule_port_refresh() @@ -161,14 +435,14 @@ class DevicePanel: ttk.Label(conn_frame, text="Port:").grid(row=0, column=0, sticky="w") self.port_var = tk.StringVar() self.port_combo = ttk.Combobox(conn_frame, textvariable=self.port_var, state="readonly", width=80) - self.port_combo.grid(row=0, column=1, columnspan=2, sticky="ew", padx=3) + self.port_combo.grid(row=0, column=1, sticky="ew", padx=3) ttk.Label(conn_frame, text="Baud:").grid(row=1, column=0, sticky="w", pady=(8, 0)) self.baud_var = tk.StringVar(value=str(default_baud)) ttk.Entry(conn_frame, textvariable=self.baud_var, width=12).grid(row=1, column=1, sticky="w", padx=3) self.connect_button = ttk.Button(conn_frame, text="Connect", width=12, command=self.connect_serial) - self.connect_button.grid(row=0, column=3, rowspan=2, padx=(8, 0)) + self.connect_button.grid(row=0, column=2, sticky="e", padx=(8, 0)) self.status_var = tk.StringVar(value="Disconnected") self.status_label = ttk.Label(conn_frame, textvariable=self.status_var, foreground="red") @@ -179,20 +453,35 @@ class DevicePanel: console_frame.columnconfigure(0, weight=1) console_frame.rowconfigure(0, weight=1) - self.log_text = tk.Text(console_frame, height=console_height, wrap="none") + self.console_split = ttk.Panedwindow(console_frame, orient="vertical") + self.console_split.grid(row=0, column=0, columnspan=2, sticky="nsew") + + log_container = ttk.Frame(self.console_split) + log_container.columnconfigure(0, weight=1) + log_container.rowconfigure(0, weight=1) + + self.log_text = tk.Text(log_container, height=console_height, wrap="none") self.log_text.grid(row=0, column=0, sticky="nsew") self.log_text.configure(state="disabled") - vscroll = ttk.Scrollbar(console_frame, orient="vertical", command=self.log_text.yview) + vscroll = ttk.Scrollbar(log_container, orient="vertical", command=self.log_text.yview) vscroll.grid(row=0, column=1, sticky="ns") self.log_text.configure(yscrollcommand=vscroll.set) - hscroll = ttk.Scrollbar(console_frame, orient="horizontal", command=self.log_text.xview) + hscroll = ttk.Scrollbar(log_container, orient="horizontal", command=self.log_text.xview) hscroll.grid(row=1, column=0, sticky="ew") self.log_text.configure(xscrollcommand=hscroll.set) + self.find_bar = None + if self.mode == "raw": + self.find_bar = ConsoleFindBar(log_container, self.log_text) + self.find_bar.grid(row=2, column=0, columnspan=2, sticky="ew", pady=(4, 0)) + self.find_bar.show() + + self.console_split.add(log_container, weight=3) + button_row = ttk.Frame(console_frame) - button_row.grid(row=2, column=0, columnspan=2, sticky="ew", pady=(5, 0)) + button_row.grid(row=3, column=0, columnspan=2, sticky="ew", pady=(5, 0)) button_row.columnconfigure(0, weight=1) clear_row = ttk.Frame(button_row) @@ -200,11 +489,20 @@ class DevicePanel: ttk.Button(clear_row, text="Clear Console", width=14, command=self.clear_console).grid(row=0, column=0, padx=(0, 5)) ttk.Button(clear_row, text="Save Log", width=14, command=self.save_console).grid(row=0, column=1) + next_col = 2 + self.flash_button = None if self.flash_handler: - self.flash_button = ttk.Button(button_row, text=self.flash_button_label, width=18, command=self.flash_handler) - self.flash_button.grid(row=0, column=1, sticky="e") - else: - self.flash_button = None + self.flash_button = ttk.Button(clear_row, text=self.flash_button_label, width=18, command=self.flash_handler) + self.flash_button.grid(row=0, column=next_col, padx=(10, 0)) + next_col += 1 + + self.arduino_flash_button = None + if self.mode == "arduino" and self.arduino_flash_handler: + self.arduino_flash_button = ttk.Button( + clear_row, text="Flash Arduino", width=16, command=self._handle_arduino_flash + ) + self.arduino_flash_button.grid(row=0, column=next_col, padx=(5, 0)) + next_col += 1 if self.mode == "raw": self._build_raw_controls(console_frame) @@ -212,22 +510,34 @@ class DevicePanel: self._build_arduino_controls(console_frame) def _build_raw_controls(self, console_frame: tk.Widget) -> None: - history_frame = ttk.Frame(console_frame) - history_frame.grid(row=3, column=0, columnspan=2, sticky="nsew", pady=(5, 0)) - history_frame.rowconfigure(1, weight=1) - history_frame.columnconfigure(0, weight=1) + history_container = ttk.Frame(self.console_split) + history_container.columnconfigure(0, weight=1) + history_container.rowconfigure(2, weight=1) + self.history_frame = history_container - ttk.Label(history_frame, text="Input history:").grid(row=0, column=0, sticky="w") - self.history_listbox = tk.Listbox(history_frame, height=8) - self.history_listbox.grid(row=1, column=0, sticky="nsew") + _ensure_console_grip_style() + grip = ttk.Frame(history_container, height=1, style=_CONSOLE_GRIP_STYLE, cursor="sb_v_double_arrow") + grip.grid(row=0, column=0, columnspan=2, sticky="ew") + history_container.rowconfigure(0, minsize=1) + self.history_grip = grip + grip.bind("", self._start_history_sash_drag) + grip.bind("", self._drag_history_sash) + grip.bind("", self._end_history_sash_drag) + grip.bind("", lambda _evt: self._set_history_grip_active(True)) + grip.bind("", lambda _evt: self._set_history_grip_active(False)) + + ttk.Label(history_container, text="Command history:").grid(row=1, column=0, sticky="w", pady=(5, 0)) + self.history_listbox = tk.Listbox(history_container, height=8, activestyle="none") + self.history_listbox.grid(row=2, column=0, sticky="nsew") self.history_listbox.bind("", self.history_select) + self.history_listbox.bind("", self._history_keypress) - history_scroll = ttk.Scrollbar(history_frame, orient="vertical", command=self.history_listbox.yview) - history_scroll.grid(row=1, column=1, sticky="ns") + history_scroll = ttk.Scrollbar(history_container, orient="vertical", command=self.history_listbox.yview) + history_scroll.grid(row=2, column=1, sticky="ns") self.history_listbox.configure(yscrollcommand=history_scroll.set) - input_frame = ttk.Frame(console_frame) - input_frame.grid(row=4, column=0, sticky="ew", pady=(5, 0)) + input_frame = ttk.Frame(history_container) + input_frame.grid(row=3, column=0, columnspan=2, sticky="ew", pady=(5, 0)) input_frame.columnconfigure(0, weight=1) self.raw_entry_var = tk.StringVar() @@ -244,11 +554,14 @@ class DevicePanel: self.arduino_entry_var = None self.arduino_send_button = None + self.console_split.add(history_container, weight=1) + def _build_arduino_controls(self, console_frame: tk.Widget) -> None: command_frame = ttk.Frame(console_frame) command_frame.grid(row=3, column=0, columnspan=2, sticky="ew", pady=(5, 0)) - command_frame.columnconfigure((0, 1, 2), weight=1) + command_frame.columnconfigure(0, weight=1) + command_frame.columnconfigure((0, 1, 2), weight=1) commands = [ ("Download Mode", ("pg 1", "reset")), ("Normal Mode", ("pg 0", "reset")), @@ -261,6 +574,7 @@ class DevicePanel: command=lambda seq=payload: self.send_macro(seq), ).grid(row=0, column=idx, padx=5, sticky="ew") + input_frame = ttk.Frame(console_frame) input_frame.grid(row=4, column=0, sticky="ew", pady=(5, 0)) input_frame.columnconfigure(0, weight=1) @@ -280,12 +594,31 @@ class DevicePanel: self.raw_entry_var = None self.raw_send_button = None + def _handle_arduino_flash(self) -> None: + if not self.arduino_flash_handler: + return + if self.before_button_callback: + try: + self.before_button_callback() + except Exception: + pass + try: + self.arduino_flash_handler() + except Exception as exc: + messagebox.showerror("Arduino Flash", f"Unable to start flash: {exc}") + # ------------------------------------------------------------------ # Console helpers # ------------------------------------------------------------------ def enable_controls(self, enabled: bool) -> None: state = "normal" if enabled else "disabled" - targets = [self.raw_entry, self.raw_send_button, self.arduino_entry, self.arduino_send_button] + targets = [ + self.raw_entry, + self.raw_send_button, + self.arduino_entry, + self.arduino_send_button, + self.arduino_flash_button, + ] for widget in targets: if widget is not None: widget.configure(state=state) @@ -297,6 +630,7 @@ class DevicePanel: self.log_text.delete("1.0", "end") self.log_text.configure(state="disabled") self.log_line_count = 0 + self._clear_find_highlight() def save_console(self) -> None: try: @@ -319,6 +653,45 @@ class DevicePanel: except Exception as exc: messagebox.showerror("Save Failed", f"Unable to save log: {exc}") + def _clear_find_highlight(self) -> None: + if self.find_bar: + self.find_bar.clear_highlight() + + def _start_history_sash_drag(self, event: tk.Event) -> None: + if not self.console_split: + return + try: + self._sash_drag_initial = self.console_split.sashpos(0) + except tk.TclError: + self._sash_drag_start = None + return + self._sash_drag_start = event.y_root + self._set_history_grip_active(True) + + def _drag_history_sash(self, event: tk.Event) -> None: + if self._sash_drag_start is None or not self.console_split: + return + delta = event.y_root - self._sash_drag_start + new_pos = self._sash_drag_initial + delta + try: + self.console_split.sashpos(0, max(0, new_pos)) + except tk.TclError: + pass + + def _end_history_sash_drag(self, _event: tk.Event) -> None: + self._sash_drag_start = None + self._set_history_grip_active(False) + + def _set_history_grip_active(self, active: bool) -> None: + if not self.history_frame or not self.history_grip: + return + size = 6 if active else 1 + try: + self.history_frame.rowconfigure(0, minsize=size) + self.history_grip.configure(height=size) + except tk.TclError: + pass + def queue_message(self, text: str) -> None: if not text: return @@ -469,12 +842,24 @@ class DevicePanel: return self._resolve_port(self.port_var.get()) def send_macro(self, commands: Iterable[str]) -> None: + command_list = [str(cmd) for cmd in commands if str(cmd).strip()] + if not command_list: + return + if not (self.serial_port and self.serial_port.is_open): + messagebox.showwarning( + "Not Connected", f"Connect {(self.frame.cget('text') or 'this device')} before sending commands." + ) + return if self.before_button_callback: try: self.before_button_callback() except Exception: pass + threading.Thread(target=self._run_macro_commands, args=(command_list,), daemon=True).start() + + def _run_macro_commands(self, commands: List[str]) -> None: for cmd in commands: + self.queue_message(f"[macro] {cmd}") self.send_uart(cmd) time.sleep(0.05) @@ -485,12 +870,16 @@ class DevicePanel: if not payload: return self.send_uart(payload) - self.raw_history.append(payload) - self.raw_history_index = len(self.raw_history) - if self.history_listbox: - self.history_listbox.insert("end", payload) - if self.history_listbox.size() > HISTORY_LIMIT: - self.history_listbox.delete(0) + if not _should_skip_history_entry(payload): + self.raw_history.append(payload) + if len(self.raw_history) > HISTORY_LIMIT: + self.raw_history.pop(0) + if self.history_listbox and self.history_listbox.size(): + self.history_listbox.delete(0) + self.raw_history_index = len(self.raw_history) + if self.history_listbox: + self.history_listbox.insert("end", payload) + self._persist_history_cache() self.raw_entry_var.set("") def send_arduino_input(self) -> None: @@ -519,6 +908,83 @@ class DevicePanel: pass self.send_uart(cmd) + def _history_keypress(self, event) -> None: + if not self.history_listbox: + return + if event.keysym.lower() == "delete": + self._delete_history_entry() + + def _history_storage_key(self) -> str: + title = "" + if self.frame: + try: + title = str(self.frame.cget("text") or "").strip() + except tk.TclError: + title = "" + return f"{self.mode}:{title}" + + def _load_history_cache(self) -> None: + if self.mode != "raw": + return + entries = _load_history_store().get(self._history_storage_key(), []) + if not entries: + return + filtered: List[str] = [] + dirty = False + for cmd in entries[-HISTORY_LIMIT:]: + if not isinstance(cmd, str): + dirty = True + continue + if _should_skip_history_entry(cmd): + dirty = True + continue + filtered.append(cmd) + self.raw_history = [] + if self.history_listbox: + self.history_listbox.delete(0, "end") + for cmd in filtered: + self.raw_history.append(cmd) + if self.history_listbox: + self.history_listbox.insert("end", cmd) + self.raw_history = self.raw_history[-HISTORY_LIMIT:] + self.raw_history_index = len(self.raw_history) + if dirty: + self._persist_history_cache() + + def _persist_history_cache(self) -> None: + if self.mode != "raw": + return + data = _load_history_store() + cleaned = [cmd for cmd in self.raw_history[-HISTORY_LIMIT:] if isinstance(cmd, str) and not _should_skip_history_entry(cmd)] + data[self._history_storage_key()] = cleaned + _persist_history_store(data) + + def _history_mouse_motion(self, event) -> None: + return + + def _hide_history_delete_button(self) -> None: + return + + def _delete_history_entry(self) -> None: + idx = None + if not self.history_listbox: + return + sel = self.history_listbox.curselection() + if sel: + idx = sel[0] + if idx is None: + return + listbox_size = self.history_listbox.size() if self.history_listbox else 0 + if idx >= listbox_size: + return + self.history_listbox.delete(idx) + if 0 <= idx < len(self.raw_history): + del self.raw_history[idx] + if self.raw_history_index > idx: + self.raw_history_index -= 1 + self.raw_history_index = min(self.raw_history_index, len(self.raw_history)) + self._persist_history_cache() + def _raw_history_prev(self, _event) -> str: self._navigate_history(self.raw_history, "raw_history_index", self.raw_entry_var, self.raw_entry, -1) return "break" @@ -603,6 +1069,24 @@ class DevicePanel: class JLinkGDBWrapper: SIZE_MAP = {"b": 1, "h": 2, "w": 4, "g": 8} + _DELAY_UNIT_MAP = { + "ms": "ms", + "msec": "ms", + "msecs": "ms", + "millisecond": "ms", + "milliseconds": "ms", + "us": "us", + "usec": "us", + "usecs": "us", + "microsecond": "us", + "microseconds": "us", + "s": "s", + "sec": "s", + "secs": "s", + "second": "s", + "seconds": "s", + } + _DELAY_UNIT_LABEL = {"ms": "ms", "us": "us", "s": "s"} def __init__(self, link, session=None) -> None: self.link = link @@ -617,6 +1101,8 @@ class JLinkGDBWrapper: return True, self._handle_mem_read(stripped) if op == "set": return True, self._handle_mem_write(stripped) + if op in ("sleep", "delay"): + return True, self._handle_delay_command(op, stripped) if op in ("halt", "h"): return True, self._handle_halt() if op in ("go", "g", "resume", "run"): @@ -677,6 +1163,34 @@ class JLinkGDBWrapper: self._write_memory(address, value, 4) return f"Wrote 0x{value & 0xFFFFFFFF:08x} to {address:#010x}" + def _handle_delay_command(self, op: str, command: str): + parts = command.split(None, 1) + if len(parts) < 2 or not parts[1].strip(): + raise ValueError(f"Use '{op} ' to insert a delay.") + payload = parts[1].strip() + tokens = payload.split() + unit = "ms" + if tokens: + unit_key = tokens[-1].lower() + if unit_key in self._DELAY_UNIT_MAP: + unit = self._DELAY_UNIT_MAP[unit_key] + tokens = tokens[:-1] + payload = " ".join(tokens).strip() + if not payload: + raise ValueError("Delay value is required before the unit.") + duration = self._parse_expression(payload) + if duration < 0: + raise ValueError("Delay must be non-negative.") + seconds = duration / 1000.0 + if unit == "us": + seconds = duration / 1_000_000.0 + elif unit == "s": + seconds = duration + if duration > 0 and seconds > 0: + time.sleep(seconds) + label = self._DELAY_UNIT_LABEL[unit] + return f"Delay {duration} {label}." + def _handle_halt(self): func = getattr(self.link, "halt", None) if not func: @@ -1291,11 +1805,181 @@ class JLinkGDBWrapper: return unique - def _parse_int(self, text: str) -> int: + class _ExpressionParser: + _NUMBER_RE = re.compile( + r""" + (?P + 0[xX][0-9a-fA-F_]+ | + 0[bB][01_]+ | + 0[oO][0-7_]+ | + 0[dD][0-9_]+ | + \d[\d_]* + ) + (?P[uUlL]*) + """, + re.VERBOSE, + ) + + def __init__(self, text: str, deref_cb): + self.text = text or "" + self.length = len(self.text) + self.pos = 0 + self._deref = deref_cb + + def parse(self) -> int: + value = self._parse_or() + self._skip_ws() + if self.pos != self.length: + raise ValueError(f"Unexpected token at position {self.pos + 1}") + return value + + def _skip_ws(self): + while self.pos < self.length and self.text[self.pos].isspace(): + self.pos += 1 + + def _match(self, token: str) -> bool: + self._skip_ws() + if self.text.startswith(token, self.pos): + self.pos += len(token) + return True + return False + + def _parse_or(self) -> int: + value = self._parse_xor() + while True: + if self._match("|"): + value |= self._parse_xor() + else: + break + return value + + def _parse_xor(self) -> int: + value = self._parse_and() + while True: + if self._match("^"): + value ^= self._parse_and() + else: + break + return value + + def _parse_and(self) -> int: + value = self._parse_shift() + while True: + if self._match("&"): + value &= self._parse_shift() + else: + break + return value + + def _parse_shift(self) -> int: + value = self._parse_add() + while True: + if self._match("<<"): + rhs = self._parse_add() + value <<= rhs + elif self._match(">>"): + rhs = self._parse_add() + value >>= rhs + else: + break + return value + + def _parse_add(self) -> int: + value = self._parse_mul() + while True: + if self._match("+"): + value += self._parse_mul() + elif self._match("-"): + value -= self._parse_mul() + else: + break + return value + + def _parse_mul(self) -> int: + value = self._parse_unary() + while True: + if self._match("*"): + value *= self._parse_unary() + elif self._match("/"): + rhs = self._parse_unary() + if rhs == 0: + raise ValueError("Division by zero") + value //= rhs + elif self._match("%"): + rhs = self._parse_unary() + if rhs == 0: + raise ValueError("Modulo by zero") + value %= rhs + else: + break + return value + + def _parse_unary(self) -> int: + if self._match("+"): + return self._parse_unary() + if self._match("-"): + return -self._parse_unary() + if self._match("~"): + return ~self._parse_unary() + if self._match("*"): + address = self._parse_unary() + return self._deref(address) + return self._parse_primary() + + def _parse_primary(self) -> int: + if self._match("("): + value = self._parse_or() + if not self._match(")"): + raise ValueError("Expected ')'") + return value + number = self._consume_number() + if number is None: + raise ValueError(f"Expected number at position {self.pos + 1}") + return number + + def _consume_number(self) -> Optional[int]: + self._skip_ws() + match = self._NUMBER_RE.match(self.text, self.pos) + if not match: + return None + self.pos = match.end() + token = match.group("value") + suffix = match.group("suffix") or "" + token = token.rstrip("uUlL") if not suffix else token + token_lower = token.lower() + if token_lower.startswith("0d"): + digits = token[2:] + if not digits: + raise ValueError("Invalid decimal literal") + return int(digits.replace("_", ""), 10) + if token_lower.startswith("0b"): + return int(token_lower, 2) + if token_lower.startswith("0o"): + return int(token_lower, 8) + return int(token, 0) + + def _parse_expression(self, text: str) -> int: + parser = self._ExpressionParser(text, self._deref_u32) try: - return int(text.strip(), 0) - except Exception: - raise ValueError(f"Unable to parse integer value '{text}'") + return parser.parse() + except ValueError as exc: + raise ValueError(f"Unable to parse integer value '{text}': {exc}") from exc + + def _parse_int(self, text: str) -> int: + return self._parse_expression(text) & 0xFFFFFFFF + + def _deref_u32(self, address: int) -> int: + reader = getattr(self.link, "memory_read32", None) + if not callable(reader): + raise ValueError("memory_read32 is not available for expressions using '*'") + addr = int(address) & 0xFFFFFFFF + try: + values = reader(addr, 1) or [] + except Exception as exc: + raise ValueError(f"Unable to read 32-bit value at {addr:#010x}: {exc}") from exc + if not values: + raise ValueError(f"No data returned when reading 32-bit value at {addr:#010x}") + return int(values[0]) & 0xFFFFFFFF def _read_memory(self, address: int, width: int, count: int): bits = width * 8 @@ -1340,6 +2024,7 @@ class DebuggerSession: self.breakpoint_button = None self.connect_button = None self.status_label = None + self.find_bar: Optional[ConsoleFindBar] = None self.msg_queue = deque() self.msg_lock = threading.Lock() @@ -1354,6 +2039,7 @@ class DebuggerSession: self._last_pc_report = 0.0 self._build_ui() + self._load_history_cache() self.enable_controls(False) def _build_ui(self): @@ -1391,8 +2077,11 @@ class DebuggerSession: hscroll.grid(row=1, column=0, sticky="ew") self.log_text.configure(xscrollcommand=hscroll.set) + self.find_bar = ConsoleFindBar(console_frame, self.log_text) + self.find_bar.grid(row=2, column=0, columnspan=2, sticky="ew", pady=(4, 0)) + button_row = ttk.Frame(console_frame) - button_row.grid(row=2, column=0, columnspan=2, sticky="ew", pady=(5, 0)) + button_row.grid(row=3, column=0, columnspan=2, sticky="ew", pady=(5, 0)) button_row.columnconfigure(0, weight=1) buttons = ttk.Frame(button_row) buttons.grid(row=0, column=0, sticky="w") @@ -1402,19 +2091,20 @@ class DebuggerSession: self.breakpoint_button.grid(row=0, column=2, padx=(5, 0)) history_frame = ttk.Frame(console_frame) - history_frame.grid(row=3, column=0, sticky="nsew", pady=(5, 0)) + history_frame.grid(row=4, column=0, sticky="nsew", pady=(5, 0)) history_frame.columnconfigure(0, weight=1) history_frame.rowconfigure(1, weight=1) ttk.Label(history_frame, text="Command history:").grid(row=0, column=0, sticky="w") - self.history_listbox = tk.Listbox(history_frame, height=6) + self.history_listbox = tk.Listbox(history_frame, height=6, activestyle="none") self.history_listbox.grid(row=1, column=0, sticky="nsew") self.history_listbox.bind("", self.history_select) + self.history_listbox.bind("", self._history_keypress) history_scroll = ttk.Scrollbar(history_frame, orient="vertical", command=self.history_listbox.yview) history_scroll.grid(row=1, column=1, sticky="ns") self.history_listbox.configure(yscrollcommand=history_scroll.set) input_frame = ttk.Frame(console_frame) - input_frame.grid(row=4, column=0, sticky="ew", pady=(5, 0)) + input_frame.grid(row=5, column=0, sticky="ew", pady=(5, 0)) input_frame.columnconfigure(0, weight=1) self.command_entry_var = tk.StringVar() self.command_entry = ttk.Entry(input_frame, textvariable=self.command_entry_var) @@ -1506,6 +2196,8 @@ class DebuggerSession: self.log_text.delete("1.0", "end") self.log_text.configure(state="disabled") self.log_line_count = 0 + self._clear_find_highlight() + self._clear_find_highlight() def save_console(self): if not self.log_text: @@ -1530,6 +2222,10 @@ class DebuggerSession: except Exception as exc: messagebox.showerror("Save Failed", f"Unable to save log: {exc}") + def _clear_find_highlight(self) -> None: + if self.find_bar: + self.find_bar.clear_highlight() + def show_breakpoints(self): if not self.breakpoints: self.queue_message("[bp] No breakpoints set.") @@ -1639,11 +2335,8 @@ class DebuggerSession: return if add_to_history and self.history_listbox: self.history_listbox.insert("end", command) - if self.history_listbox.size() > HISTORY_LIMIT: - self.history_listbox.delete(0) if add_to_history: - self.command_history.append(command) - self.history_index = len(self.command_history) + self._append_history_entry(command) self.command_entry_var.set("") self.queue_message(f"> {command}") threading.Thread(target=self.panel.run_session_command, args=(self, command), daemon=True).start() @@ -1684,6 +2377,65 @@ class DebuggerSession: except tk.TclError: pass + def _append_history_entry(self, command: str) -> None: + self.command_history.append(command) + if len(self.command_history) > HISTORY_LIMIT: + self.command_history.pop(0) + if self.history_listbox and self.history_listbox.size() > HISTORY_LIMIT: + self.history_listbox.delete(0) + self.history_index = len(self.command_history) + self._persist_history_cache() + + def _history_keypress(self, event) -> None: + if (event.keysym or "").lower() == "delete": + self._delete_history_entry() + return "break" + return None + + def _delete_history_entry(self) -> None: + if not self.history_listbox: + return + sel = self.history_listbox.curselection() + if not sel: + return + idx = sel[0] + if idx >= self.history_listbox.size(): + return + self.history_listbox.delete(idx) + if 0 <= idx < len(self.command_history): + del self.command_history[idx] + if self.history_index > idx: + self.history_index -= 1 + self.history_index = min(self.history_index, len(self.command_history)) + self._persist_history_cache() + + def _history_storage_key(self) -> str: + label = self.label or self.ap_info.get("label", "AP") + core = self.ap_info.get("core", "core") + return f"debugger:{label}:{core}" + + def _load_history_cache(self) -> None: + entries = _load_history_store().get(self._history_storage_key(), []) + cleaned: List[str] = [] + for cmd in entries[-HISTORY_LIMIT:]: + if not isinstance(cmd, str): + continue + text = cmd.strip() + if not text: + continue + cleaned.append(cmd) + self.command_history = cleaned[-HISTORY_LIMIT:] + self.history_index = len(self.command_history) + if self.history_listbox: + self.history_listbox.delete(0, "end") + for cmd in self.command_history: + self.history_listbox.insert("end", cmd) + + def _persist_history_cache(self) -> None: + data = _load_history_store() + data[self._history_storage_key()] = [cmd for cmd in self.command_history[-HISTORY_LIMIT:] if isinstance(cmd, str) and cmd.strip()] + _persist_history_store(data) + def _set_status(self, text: str, color: str): self.status_var.set(text) if self.status_label: @@ -2259,6 +3011,7 @@ class PylinkDebuggerPanel: ("next / n / ni", "Step over calls (uses software breakpoint when needed)."), ("finish / fin / so", "Step out of the current function."), ("i r", "Show current PC and general-purpose registers."), + ("sleep / delay ", "Pause for milliseconds (accepts 's' or 'us' units)."), ], ), ( @@ -2484,12 +3237,26 @@ class DualUARTApp: self._closing = False self._poll_after_id = None self.flash_thread = None + self.arduino_flash_thread = None + self._arduino_reconnect_after_flash = False self._icon_image = self._create_robot_icon() if self._icon_image: self.root.iconphoto(False, self._icon_image) + try: + self.root.geometry("1400x900") + self.root.minsize(1200, 720) + except tk.TclError: + pass + self._main_splitter = None + self._main_split_indicator = None + self._main_split_drag_start: Optional[int] = None + self._main_split_initial = 0 + self._main_split_hover = False + self._main_split_active = False paned = ttk.Panedwindow(root, orient="horizontal") paned.pack(fill="both", expand=True, padx=10, pady=10) + self._main_splitter = paned left_container = ttk.Frame(paned) right_container = ttk.Frame(paned) @@ -2502,9 +3269,10 @@ class DualUARTApp: left_container, title="Control Device", mode="arduino", - default_baud="9600", + default_baud="115200", line_ending="\n", console_height=10, + arduino_flash_handler=self.flash_arduino_sketch, on_connect=lambda port, key="dev1": self._panel_connected(key, port), on_disconnect=lambda port, key="dev1": self._panel_disconnected(key, port), exclude_ports_provider=lambda key="dev1": self._get_excluded_ports(key), @@ -2529,6 +3297,7 @@ class DualUARTApp: ) self.dev2.frame.pack(fill="both", expand=True, padx=(5, 0)) + self._init_main_split_indicator() self.poll_loop() self.root.protocol("WM_DELETE_WINDOW", self.close) @@ -2551,6 +3320,77 @@ class DualUARTApp: icon.put("#6b708f", to=(24, 14, 26, 22)) return icon + def _init_main_split_indicator(self) -> None: + if not self._main_splitter: + return + _ensure_console_grip_style() + indicator = ttk.Frame(self.root, style=_CONSOLE_GRIP_STYLE, width=1, cursor="sb_h_double_arrow") + indicator.bind("", self._start_main_split_drag) + indicator.bind("", self._drag_main_split) + indicator.bind("", self._end_main_split_drag) + indicator.bind("", lambda _evt: self._set_main_split_hover(True)) + indicator.bind("", lambda _evt: self._set_main_split_hover(False)) + self._main_split_indicator = indicator + self.root.bind("", self._sync_main_split_indicator, add="+") + self._main_splitter.bind("", self._sync_main_split_indicator, add="+") + self._main_splitter.bind("", self._sync_main_split_indicator, add="+") + self._main_splitter.bind("", self._sync_main_split_indicator, add="+") + self.root.after(0, self._sync_main_split_indicator) + + def _sync_main_split_indicator(self, _event: Optional[tk.Event] = None) -> None: + indicator = self._main_split_indicator + splitter = self._main_splitter + if not indicator or not splitter: + return + try: + sash = splitter.sashpos(0) + x = splitter.winfo_x() + sash + y = splitter.winfo_y() + height = splitter.winfo_height() + except tk.TclError: + return + if height <= 1: + indicator.place_forget() + return + width = 1 + if self._main_split_hover or self._main_split_active: + width = 5 + indicator.place(x=max(0, x - width // 2), y=y, width=width, height=height) + + def _start_main_split_drag(self, event: tk.Event) -> None: + splitter = self._main_splitter + if not splitter: + self._main_split_drag_start = None + return + try: + self._main_split_initial = splitter.sashpos(0) + except tk.TclError: + self._main_split_drag_start = None + return + self._main_split_drag_start = event.x_root + self._main_split_active = True + self._sync_main_split_indicator() + + def _drag_main_split(self, event: tk.Event) -> None: + if self._main_split_drag_start is None or not self._main_splitter: + return + delta = event.x_root - self._main_split_drag_start + new_pos = self._main_split_initial + delta + try: + self._main_splitter.sashpos(0, new_pos) + except tk.TclError: + return + self._sync_main_split_indicator() + + def _end_main_split_drag(self, _event: tk.Event) -> None: + self._main_split_drag_start = None + self._main_split_active = False + self._sync_main_split_indicator() + + def _set_main_split_hover(self, state: bool) -> None: + self._main_split_hover = state + self._sync_main_split_indicator() + # ------------------------------------------------------------------ def poll_loop(self) -> None: if self._closing: @@ -2579,6 +3419,11 @@ class DualUARTApp: if self.debugger: self.debugger.shutdown() self.debugger.disconnect() + if self._main_split_indicator: + try: + self._main_split_indicator.destroy() + except tk.TclError: + pass self.root.destroy() # ------------------------------------------------------------------ @@ -2621,6 +3466,111 @@ class DualUARTApp: self.dev2.queue_message(message) self.dev1.queue_message(message) + def flash_arduino_sketch(self) -> None: + if self.arduino_flash_thread and self.arduino_flash_thread.is_alive(): + messagebox.showinfo("Arduino Flash", "Arduino flashing is already in progress.") + return + if not os.path.exists(ARDUINO_SKETCH_PATH): + messagebox.showerror("Arduino Flash", f"Sketch not found: {ARDUINO_SKETCH_PATH}") + return + port = self.dev1.get_selected_port() + if not port: + messagebox.showwarning("Arduino Flash", "Select an Arduino COM port before flashing.") + return + if not os.path.isdir(ARDUINO_SKETCH_DIR): + messagebox.showerror("Arduino Flash", f"Sketch directory not found: {ARDUINO_SKETCH_DIR}") + return + reconnect = self.dev1.is_connected() + if reconnect: + try: + self.dev1.disconnect_serial() + except Exception: + pass + time.sleep(0.1) + if self.dev1.arduino_flash_button: + self.dev1.arduino_flash_button.configure(state="disabled") + cli_cmd = ARDUINO_CLI or "arduino-cli" + fqbn = ARDUINO_FQBN or "arduino:avr:uno" + self.dev1.queue_message( + f"[arduino] Flashing '{os.path.basename(ARDUINO_SKETCH_PATH)}' via {cli_cmd} on {port}..." + ) + self._arduino_reconnect_after_flash = reconnect + thread = threading.Thread( + target=self._arduino_flash_worker, + args=(cli_cmd, fqbn, port, ARDUINO_SKETCH_DIR), + daemon=True, + ) + self.arduino_flash_thread = thread + thread.start() + + def _arduino_flash_worker(self, cli_cmd: str, fqbn: str, port: str, sketch_dir: str) -> None: + steps = [ + ("compile", [cli_cmd, "compile", "--fqbn", fqbn, sketch_dir]), + ("upload", [cli_cmd, "upload", "-p", port, "--fqbn", fqbn, sketch_dir]), + ] + for label, cmd in steps: + try: + process = subprocess.Popen( + cmd, + cwd=APP_ROOT, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=1, + ) + except FileNotFoundError: + message = "arduino-cli executable was not found. Set ARDUINO_CLI env var to its path." + self.dev1.queue_message(f"[arduino] {message}") + self._complete_arduino_flash(False, message) + return + except Exception as exc: + message = f"Unable to start arduino-cli ({label}): {exc}" + self.dev1.queue_message(f"[arduino] {message}") + self._complete_arduino_flash(False, message) + return + if process.stdout: + for line in process.stdout: + clean = line.rstrip() + if clean: + self.dev1.queue_message(f"[arduino-cli:{label}] {clean}") + ret = process.wait() + if process.stdout: + process.stdout.close() + if ret != 0: + message = f"arduino-cli {label} exited with code {ret}." + self._complete_arduino_flash(False, message) + return + self._complete_arduino_flash(True, "Arduino sketch uploaded successfully.") + + def _complete_arduino_flash(self, success: bool, message: str) -> None: + def finish(): + if self.dev1.arduino_flash_button: + self.dev1.arduino_flash_button.configure(state="normal") + self.arduino_flash_thread = None + reconnect = getattr(self, "_arduino_reconnect_after_flash", False) + self._arduino_reconnect_after_flash = False + if success and reconnect and not self.dev1.is_connected(): + try: + self.dev1.connect_serial() + except Exception: + pass + self.dev1.queue_message(f"[arduino] {message}") + if success: + try: + messagebox.showinfo("Arduino Flash", message) + except tk.TclError: + pass + else: + try: + messagebox.showerror("Arduino Flash", message) + except tk.TclError: + pass + + try: + self.root.after(0, finish) + except tk.TclError: + finish() + def _prepare_firmware_images(self) -> bool: image_dir = os.path.join(APP_ROOT, FLASH_IMAGE_DIR) try: diff --git a/pro3_uart_v1.2.2.spec b/pro3_uart_v1.2.2.spec deleted file mode 100644 index fa318ef..0000000 --- a/pro3_uart_v1.2.2.spec +++ /dev/null @@ -1,44 +0,0 @@ -# -*- mode: python ; coding: utf-8 -*- - - -a = Analysis( - ['C:\\D_Drive\\A-Important\\work\\Pro3_control_panel_v1.2.2\\pro3_uart.py'], - pathex=[], - binaries=[], - datas=[], - hiddenimports=[], - hookspath=[], - hooksconfig={}, - runtime_hooks=[], - excludes=[], - noarchive=False, - optimize=0, -) -pyz = PYZ(a.pure) - -exe = EXE( - pyz, - a.scripts, - [], - exclude_binaries=True, - name='pro3_uart_v1.2.2', - debug=False, - bootloader_ignore_signals=False, - strip=False, - upx=True, - console=False, - disable_windowed_traceback=False, - argv_emulation=False, - target_arch=None, - codesign_identity=None, - entitlements_file=None, -) -coll = COLLECT( - exe, - a.binaries, - a.datas, - strip=False, - upx=True, - upx_exclude=[], - name='pro3_uart_v1.2.2', -) diff --git a/version_info.py b/version_info.py index 1c73293..da6ea44 100644 --- a/version_info.py +++ b/version_info.py @@ -4,5 +4,5 @@ # Copyright (c) 2024 Realtek Semiconductor Corp. # SPDX-License-Identifier: Apache-2.0 -version = "1.2.2.0" -display_version = "1.2.2" +version = "1.2.3.0" +display_version = "1.2.3"