diff --git a/.gitignore b/.gitignore index 81cdbb1..362e6f7 100644 --- a/.gitignore +++ b/.gitignore @@ -362,7 +362,8 @@ MigrationBackup/ # Fody - auto-generated XML schema FodyWeavers.xsd -Firmware/ConfigurationSetup/.gitignore -Firmware/ConfigurationSetup/.pio -Firmware/ConfigurationSetup/.vscode/* -!Firmware/ConfigurationSetup/.vscode/extensions.json +Firmware/NvsFlasherUtility/.gitignore +Firmware/NvsFlasherUtility/.pio +Firmware/NvsFlasherUtility/.vscode/* +!Firmware/NvsFlasherUtility/.vscode/extensions.json +!Firmware/NvsFlasherUtility/.vscode/tasks.json diff --git a/Firmware/ConfigurationSetup/.vscode/extensions.json b/Firmware/NvsFlasherUtility/.vscode/extensions.json similarity index 100% rename from Firmware/ConfigurationSetup/.vscode/extensions.json rename to Firmware/NvsFlasherUtility/.vscode/extensions.json diff --git a/Firmware/NvsFlasherUtility/.vscode/tasks.json b/Firmware/NvsFlasherUtility/.vscode/tasks.json new file mode 100644 index 0000000..ac3c77a --- /dev/null +++ b/Firmware/NvsFlasherUtility/.vscode/tasks.json @@ -0,0 +1,95 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "PlatformIO: Upload", + "type": "shell", + "command": "platformio run --target upload", + "options": { + "statusbar": { + "hide": true + } + }, + "presentation": { + "reveal": "always", + "focus": true, + "panel": "shared" + } + }, + { + "label": "Upload and Start Debug", + "type": "shell", + "command": "platformio", + "args": [ + "run", + "-e", + "debug", + "--target", + "upload", + "&&", + "platformio", + "device", + "monitor" + ], + "problemMatcher": [], + "options": { + "statusbar": { + "hide": true + } + }, + "presentation": { + "panel": "shared", + "clear": true, + "reveal": "always", + "focus": true + } + }, + { + "label": "LumenLab Installer - Upload (release)", + "type": "shell", + "command": "platformio", + "args": [ + "run", + "-e", + "release", + "--target", + "upload", + "&&", + "platformio", + "device", + "monitor" + ], + "problemMatcher": [ + "$platformio" + ], + "options": { + "statusbar": { + "hide": true + } + }, + "presentation": { + "reveal": "always", + "focus": true, + "panel": "shared" + } + }, + { + "label": "LumenLab Installer - Clean", + "type": "shell", + "command": "platformio run --target clean -e debug -e release", + "problemMatcher": [ + "$platformio" + ], + "options": { + "statusbar": { + "hide": true + } + }, + "presentation": { + "reveal": "always", + "focus": true, + "panel": "shared" + } + } + ] +} \ No newline at end of file diff --git a/Firmware/ConfigurationSetup/LumenLabInstaller.code-workspace b/Firmware/NvsFlasherUtility/NvsFlasherUtility.code-workspace similarity index 100% rename from Firmware/ConfigurationSetup/LumenLabInstaller.code-workspace rename to Firmware/NvsFlasherUtility/NvsFlasherUtility.code-workspace diff --git a/Firmware/ConfigurationSetup/build/bootloader.bin b/Firmware/NvsFlasherUtility/build/bootloader.bin similarity index 100% rename from Firmware/ConfigurationSetup/build/bootloader.bin rename to Firmware/NvsFlasherUtility/build/bootloader.bin diff --git a/Firmware/ConfigurationSetup/build/firmware.bin b/Firmware/NvsFlasherUtility/build/firmware.bin similarity index 99% rename from Firmware/ConfigurationSetup/build/firmware.bin rename to Firmware/NvsFlasherUtility/build/firmware.bin index 0dca9c6..dee2f73 100644 Binary files a/Firmware/ConfigurationSetup/build/firmware.bin and b/Firmware/NvsFlasherUtility/build/firmware.bin differ diff --git a/Firmware/ConfigurationSetup/build/partitions.bin b/Firmware/NvsFlasherUtility/build/partitions.bin similarity index 100% rename from Firmware/ConfigurationSetup/build/partitions.bin rename to Firmware/NvsFlasherUtility/build/partitions.bin diff --git a/Firmware/ConfigurationSetup/platformio.ini b/Firmware/NvsFlasherUtility/platformio.ini similarity index 63% rename from Firmware/ConfigurationSetup/platformio.ini rename to Firmware/NvsFlasherUtility/platformio.ini index e8de475..b43a7c3 100644 --- a/Firmware/ConfigurationSetup/platformio.ini +++ b/Firmware/NvsFlasherUtility/platformio.ini @@ -1,6 +1,6 @@ [platformio] -name = LumenLab Installation Tool -description = A Windows-only installation tool helper to easily write NVS memory preferences on demand for global reference and config for the LumenLab. +name = LumenLab NVS Flasher Utility Tool +description = A utility helper tool included within the LumenLab Installation Tool to easily write NVS memory settings for global configuration of the LumenLab. [env] upload_speed = 921600 diff --git a/Firmware/ConfigurationSetup/src/main.cpp b/Firmware/NvsFlasherUtility/src/main.cpp similarity index 97% rename from Firmware/ConfigurationSetup/src/main.cpp rename to Firmware/NvsFlasherUtility/src/main.cpp index f0e99ac..7a796db 100644 --- a/Firmware/ConfigurationSetup/src/main.cpp +++ b/Firmware/NvsFlasherUtility/src/main.cpp @@ -178,8 +178,8 @@ void dumpAll() } } const char *knownHighScores[] = { - "recall-high", - "phase-high"}; + "high_recall", + "high_phase"}; for (auto key : knownHighScores) { @@ -190,7 +190,7 @@ void dumpAll() continue; } - auto value = preferences.getUInt(key, 0); + auto value = preferences.getString(key, "0"); Serial.print(key); Serial.print("="); Serial.println(value); diff --git a/LumenLabInstallerForm.Designer.cs b/LumenLabInstallerForm.Designer.cs index b03839c..239c6bc 100644 --- a/LumenLabInstallerForm.Designer.cs +++ b/LumenLabInstallerForm.Designer.cs @@ -184,7 +184,7 @@ private void InitializeComponent() exitToolStripMenuItem.Name = "exitToolStripMenuItem"; exitToolStripMenuItem.Size = new Size(201, 22); exitToolStripMenuItem.Text = "Exit"; - exitToolStripMenuItem.Click += exitToolStripMenuItem_Click; + exitToolStripMenuItem.Click += ExitToolStripMenuItem_Click; // // viewToolStripMenuItem // @@ -200,7 +200,7 @@ private void InitializeComponent() toggleDebugWindowToolStripMenuItem.Name = "toggleDebugWindowToolStripMenuItem"; toggleDebugWindowToolStripMenuItem.Size = new Size(143, 22); toggleDebugWindowToolStripMenuItem.Text = "Verbose Logs"; - toggleDebugWindowToolStripMenuItem.Click += toggleDebugWindowToolStripMenuItem_Click; + toggleDebugWindowToolStripMenuItem.Click += ToggleDebugWindowToolStripMenuItem_Click; // // helpToolStripMenuItem // @@ -257,10 +257,11 @@ private void InitializeComponent() TxtMacAddress.Font = new Font("Consolas", 11.25F, FontStyle.Regular, GraphicsUnit.Point, 0); TxtMacAddress.Location = new Point(201, 97); TxtMacAddress.Name = "TxtMacAddress"; - TxtMacAddress.PlaceholderText = "00:00:00:00:00"; - TxtMacAddress.Size = new Size(130, 25); + TxtMacAddress.PlaceholderText = "00:00:00:00:00:00"; + TxtMacAddress.Size = new Size(147, 25); TxtMacAddress.TabIndex = 13; - TxtMacAddress.Text = "00:00:00:00:00"; + TxtMacAddress.Text = "00:00:00:00:00:00"; + TxtMacAddress.TextChanged += TxtMacAddress_TextChanged; // // LblSerialBaud // @@ -286,34 +287,37 @@ private void InitializeComponent() // TxtBound1.Enabled = false; TxtBound1.Font = new Font("Consolas", 11.25F, FontStyle.Regular, GraphicsUnit.Point, 0); - TxtBound1.Location = new Point(125, 158); + TxtBound1.Location = new Point(174, 158); TxtBound1.Maximum = new decimal(new int[] { 300, 0, 0, 0 }); TxtBound1.Name = "TxtBound1"; - TxtBound1.Size = new Size(65, 25); + TxtBound1.Size = new Size(55, 25); TxtBound1.TabIndex = 17; TxtBound1.Value = new decimal(new int[] { 75, 0, 0, 0 }); + TxtBound1.ValueChanged += TxtBound1_ValueChanged; // // TxtBound2 // TxtBound2.Enabled = false; TxtBound2.Font = new Font("Consolas", 11.25F, FontStyle.Regular, GraphicsUnit.Point, 0); - TxtBound2.Location = new Point(195, 158); + TxtBound2.Location = new Point(234, 158); TxtBound2.Maximum = new decimal(new int[] { 300, 0, 0, 0 }); TxtBound2.Name = "TxtBound2"; - TxtBound2.Size = new Size(65, 25); + TxtBound2.Size = new Size(55, 25); TxtBound2.TabIndex = 18; TxtBound2.Value = new decimal(new int[] { 150, 0, 0, 0 }); + TxtBound2.ValueChanged += TxtBound2_ValueChanged; // // TxtBound3 // TxtBound3.Enabled = false; TxtBound3.Font = new Font("Consolas", 11.25F, FontStyle.Regular, GraphicsUnit.Point, 0); - TxtBound3.Location = new Point(266, 158); + TxtBound3.Location = new Point(293, 158); TxtBound3.Maximum = new decimal(new int[] { 300, 0, 0, 0 }); TxtBound3.Name = "TxtBound3"; - TxtBound3.Size = new Size(65, 25); + TxtBound3.Size = new Size(55, 25); TxtBound3.TabIndex = 19; TxtBound3.Value = new decimal(new int[] { 225, 0, 0, 0 }); + TxtBound3.ValueChanged += TxtBound3_ValueChanged; // // TxtSerialBaud // @@ -323,9 +327,10 @@ private void InitializeComponent() TxtSerialBaud.Location = new Point(201, 127); TxtSerialBaud.Maximum = new decimal(new int[] { 1000000, 0, 0, 0 }); TxtSerialBaud.Name = "TxtSerialBaud"; - TxtSerialBaud.Size = new Size(130, 25); + TxtSerialBaud.Size = new Size(147, 25); TxtSerialBaud.TabIndex = 20; TxtSerialBaud.Value = new decimal(new int[] { 921600, 0, 0, 0 }); + TxtSerialBaud.ValueChanged += TxtSerialBaud_ValueChanged; // // TxtNumLeds // @@ -334,9 +339,10 @@ private void InitializeComponent() TxtNumLeds.Location = new Point(201, 36); TxtNumLeds.Maximum = new decimal(new int[] { 3000, 0, 0, 0 }); TxtNumLeds.Name = "TxtNumLeds"; - TxtNumLeds.Size = new Size(130, 25); + TxtNumLeds.Size = new Size(147, 25); TxtNumLeds.TabIndex = 21; TxtNumLeds.Value = new decimal(new int[] { 300, 0, 0, 0 }); + TxtNumLeds.ValueChanged += TxtNumLeds_ValueChanged; // // contextMenuStrip1 // @@ -379,7 +385,7 @@ private void InitializeComponent() // RadPS4.AutoSize = true; RadPS4.Enabled = false; - RadPS4.Location = new Point(271, 67); + RadPS4.Location = new Point(281, 67); RadPS4.Name = "RadPS4"; RadPS4.Size = new Size(44, 19); RadPS4.TabIndex = 30; @@ -391,7 +397,7 @@ private void InitializeComponent() RadPS3.AutoSize = true; RadPS3.Checked = true; RadPS3.Enabled = false; - RadPS3.Location = new Point(219, 67); + RadPS3.Location = new Point(229, 67); RadPS3.Name = "RadPS3"; RadPS3.Size = new Size(44, 19); RadPS3.TabIndex = 29; @@ -461,9 +467,11 @@ private void InitializeComponent() LblUpdateAlert.Font = new Font("Segoe UI", 11.25F, FontStyle.Regular, GraphicsUnit.Point, 0); LblUpdateAlert.Location = new Point(466, 88); LblUpdateAlert.Name = "LblUpdateAlert"; + LblUpdateAlert.ReadOnly = true; + LblUpdateAlert.ScrollBars = RichTextBoxScrollBars.None; LblUpdateAlert.Size = new Size(432, 21); LblUpdateAlert.TabIndex = 30; - LblUpdateAlert.Text = "Plug in your LumenLab to continue."; + LblUpdateAlert.Text = ""; // // LumenLabInstallerForm // diff --git a/LumenLabInstallerForm.cs b/LumenLabInstallerForm.cs index 454539c..2fec077 100644 --- a/LumenLabInstallerForm.cs +++ b/LumenLabInstallerForm.cs @@ -10,6 +10,7 @@ using LumenLabInstaller.Models; using System.Reflection; using System.IO.Compression; +using System.IO; namespace LumenLabInstaller; @@ -19,12 +20,14 @@ public partial class LumenLabInstallerForm : Form private readonly InstallerContext _context; private readonly NetworkService _networkService; private readonly BindingSource _releaseBindingSource = []; + private readonly UserSettings _userSettings; public LumenLabInstallerForm(NetworkService networkService, InstallerContext context) { _networkService = networkService; _context = context; + _userSettings = new UserSettings(); InitializeComponent(); ConfigureReleaseGrid(); } @@ -71,12 +74,14 @@ private async void LumenLabInstallerForm_Shown(object sender, EventArgs e) { this.UseWaitCursor = true; BtnFlashFirmware.Enabled = false; + FormatGuideLabel("Scanning device hardware"); try { await QueryGitHub(); } catch (Exception ex) { + FormatGuideLabel("Network error", Color.DarkRed, FontStyle.Bold, 1); MessageBox.Show($"Error connecting to server to download releases.{Environment.NewLine}{ex.Message}", "Network Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } @@ -85,51 +90,16 @@ private async void LumenLabInstallerForm_Shown(object sender, EventArgs e) var successfulRead = await ReadConnectedDeviceVersion(); if (!successfulRead) { - - // TODO: Just update the guide label, don't fire messagebox - //MessageBox.Show("LumenLab was not detected. Please make sure the external power is on and try again.", "LumenLab Not Detected", MessageBoxButtons.RetryCancel, MessageBoxIcon.Stop); + FormatGuideLabel("Plug in your LumenLab and click Scan Devices"); } } catch (Exception ex) { + FormatGuideLabel("Error reading device", Color.DarkRed, FontStyle.Bold, 1); MessageBox.Show($"Error checking version installed on LumenLab.{Environment.NewLine}{ex.Message}", "Device Read Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } - private async Task QueryGitHub() - { - WriteToLogs("Querying GitHub to check for updates."); - var releases = await _networkService.GetReleasesAsync(); - - _context.AvailableReleases = releases.OrderByDescending(r => r.PublishedAt).ToList(); - - var gridRows = releases - .OrderByDescending(r => r.PublishedAt) - .Select(r => new ReleaseGridRow - { - TagName = r.TagName ?? string.Empty, - Name = r.Name?.Substring(r.Name.IndexOf("-") + 2) ?? string.Empty, - PublishedAt = r.PublishedAt ?? new DateTime(), - Release = r - }) - .ToList(); - - _releaseBindingSource.DataSource = gridRows; - - LblLatestRelease.Text = releases[0].TagName; - - foreach (var release in releases) - { - WriteToLogs($"{release.TagName} - {release.PublishedAt}"); - foreach (var asset in release.Assets) - { - WriteToLogs($" Asset: {asset.Name} -> {asset.BrowserDownloadUrl}"); - } - } - - // PopulateReleaseTable(releases); - } - private async Task ReadConnectedDeviceVersion() { BtnSync.Enabled = false; @@ -142,7 +112,7 @@ private async Task ReadConnectedDeviceVersion() string? port = await DeviceService.DetectLumenLabPortAsync(); _context.PortName = port; - if (port == null) + if (port is null) { WriteToLogs("LumenLab was not detected. Please make sure the external power is on and try again."); BtnSync.Enabled = true; @@ -153,12 +123,31 @@ private async Task ReadConnectedDeviceVersion() WriteToLogs($"LumenLab detected on {port}"); string? version = await DeviceService.ReadFirmwareVersionAsync(port); + + if (version is null) + { + WriteToLogs("Failed to read LumenLab version. Either this is the board's first time flashing the LumenLab, or the LumenLab firmware is corrupted. Please make sure the external power is on and try again before continuing."); + BtnSync.Enabled = true; + BtnFlashFirmware.Enabled = true; + this.UseWaitCursor = false; + return false; + } + _context.FirmwareVersion = new Version(version.Substring(1)); LblInstalledVersion.Text = $"v{_context.FirmwareVersion.ToString()}"; WriteToLogs($"Currently installed version: {version}"); - //if (_context.FirmwareVersion == ) TODO: update the label to inform user if they're out of date or current. + var tag = _context.AvailableReleases?.FirstOrDefault()?.TagName; + var latestVersion = new Version(tag != null ? tag.Substring(1) : "99.99.99"); + if (_context.FirmwareVersion < latestVersion) + { + FormatGuideLabel("New update available!", Color.DarkGreen, FontStyle.Bold, 1); + } + else + { + FormatGuideLabel("You are up-to-date."); + } BtnFlashFirmware.Enabled = true; WriteToLogs($"Device scan complete. Ready for firmware upgrade."); @@ -168,35 +157,65 @@ private async Task ReadConnectedDeviceVersion() return true; } - private void AppendLog(string text) + private async Task FlashFirmware() { - if (InvokeRequired) + WriteToLogs("Starting LumenLab firmware upgrade."); + WriteToLogs($"Is using custom User Settings? {(_userSettings.isActive ? "True" : "False")}"); + + try { - Invoke(new Action(AppendLog), text); - return; - } + if (!(await ValidateHardware())) return; + if (!(await DownloadResources())) return; - WriteToLogs(text); + var exitCode = await FlashWithEspTool(); + var latestTag = _context.AvailableReleases?[0].TagName; + + if (exitCode == 0) + { + FormatGuideLabel("You are up-to-date."); + WriteToLogs($"Successful installation of LumenLab {latestTag}."); + WriteToLogs("You may safely exit this tool."); + MessageBox.Show($"Successful installation of LumenLab {latestTag}.", "Installation Complete", MessageBoxButtons.OK, MessageBoxIcon.Information); + LblInstalledVersion.Text = latestTag; + } + else + { + FormatGuideLabel("Plug in your LumenLab and click Scan Devices"); + WriteToLogs($"Failed to flash LumenLab {latestTag}."); + MessageBox.Show($"Failed to flash LumenLab {latestTag}.", "Installation Failure", MessageBoxButtons.OK, MessageBoxIcon.Stop); + } + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + } + finally + { + BtnFlashFirmware.Enabled = true; + } } - private async void BtnFlashFirmware_Click(object sender, EventArgs e) + private async Task ValidateHardware() { - WriteToLogs("Starting LumenLab firmware upgrade."); BtnFlashFirmware.Enabled = false; - string? port = await DeviceService.DetectLumenLabPortAsync(); - if (port == null) + _context.PortName = await DeviceService.DetectLumenLabPortAsync(); + if (_context.PortName is null) { LblInstalledVersion.Text = "N/A"; - MessageBox.Show("LumenLab was disconnected. Please connect the device and run again.", "No LumenLab detected", MessageBoxButtons.RetryCancel, MessageBoxIcon.Stop); - return; + FormatGuideLabel("Please connect the device and run again", Color.DarkRed, FontStyle.Bold); + WriteToLogs("LumenLab was disconnected. Please connect the device and run again."); + MessageBox.Show("LumenLab was disconnected. Please connect the device and run again.", "No LumenLab detected", MessageBoxButtons.OK, MessageBoxIcon.Stop); + return false; } var selectedRelease = GetSelectedRelease(); - if (selectedRelease == null) + if (selectedRelease is null) { + FormatGuideLabel("Please select a version to install.", Color.DarkRed, FontStyle.Bold); + WriteToLogs("Version to install was not selected."); MessageBox.Show("Please select a version to install.", "Select Version", MessageBoxButtons.OK, MessageBoxIcon.Error); - return; + return false; } var selectedReleaseVersionId = new Version(selectedRelease?.TagName?.Substring(1) ?? "0.0.0"); @@ -209,7 +228,7 @@ private async void BtnFlashFirmware_Click(object sender, EventArgs e) { WriteToLogs("Cancelled LumenLab firmware update."); BtnFlashFirmware.Enabled = true; - return; + return false; } } else if (selectedReleaseVersionId < _context.FirmwareVersion) @@ -219,71 +238,110 @@ private async void BtnFlashFirmware_Click(object sender, EventArgs e) { WriteToLogs("Cancelled LumenLab firmware update."); BtnFlashFirmware.Enabled = true; - return; + return false; } } BtnFlashFirmware.Enabled = false; + return true; + } - try + private async Task DownloadResources() + { + var latestRelease = _context.AvailableReleases?[0]; + if (latestRelease is null) { - var latestRelease = _context.AvailableReleases[0] ?? null; - if (latestRelease == null) - { - // second-pass refactor: more specific error - throw new Exception("Error"); - } + throw new Exception("Error"); + } - string downloadPath = Path.Combine(AppPaths.BinariesPath, latestRelease.TagName ?? "v99.99.99"); - if (!Directory.Exists(downloadPath)) - { - WriteToLogs($"Creating directory {downloadPath}"); - Directory.CreateDirectory(downloadPath); - } + string downloadPath = Path.Combine(AppPaths.BinariesPath, latestRelease.TagName ?? "v99.99.99"); + if (!Directory.Exists(downloadPath)) + { + WriteToLogs($"Creating directory {downloadPath}"); + Directory.CreateDirectory(downloadPath); + } - WriteToLogs($"Downloading LumenLab {latestRelease.TagName}"); - var cts = new CancellationTokenSource(); - var result = await _networkService.DownloadAsync( - new Uri($"https://github.com/ericmcdaniel/lumenlab/releases/download/{latestRelease.TagName}/lumenlab-firmware.zip"), - Path.Combine(downloadPath, "lumenlab-firmware.zip") - ); + WriteToLogs($"Downloading LumenLab {latestRelease.TagName}"); + using var cts = new CancellationTokenSource(); + string zipPath = Path.Combine(downloadPath, "lumenlab-firmware.zip"); - string compressedLumenLabPath = Path.Combine(downloadPath, "lumenlab-firmware.zip"); + try + { + var result = await _networkService.DownloadAsync(new Uri($"https://github.com/ericmcdaniel/lumenlab/releases/download/{latestRelease.TagName}/lumenlab-firmware.zip"), zipPath); + if (!File.Exists(zipPath)) + { + throw new FileNotFoundException("Download failed; firmware zip not found."); + } - ZipFile.ExtractToDirectory(compressedLumenLabPath, downloadPath, true); - File.Delete(compressedLumenLabPath); + ZipFile.ExtractToDirectory(zipPath, downloadPath, true); WriteToLogs("Download complete."); WriteToLogs($"Downloaded {result.BytesWritten.Bytes()}."); - - string esptoolFullCmd = $"--chip esp32 --baud 921600 --port {port} write_flash -z 0x1000 {Path.Combine(downloadPath, "bootloader.bin")} 0x8000 {Path.Combine(downloadPath, "partitions.bin")} 0x10000 {Path.Combine(downloadPath, "firmware.bin")}"; - WriteToLogs($"Running esptool.exe {esptoolFullCmd}"); - var exitCode = await DeviceService.RunEsptoolAsync(esptoolFullCmd, AppendLog, cts.Token); - - if (exitCode == 0) - { - WriteToLogs($"Successful installation of LumenLab {latestRelease.TagName}."); - WriteToLogs("You may safely exit this tool."); - } - else - { - WriteToLogs($"Failed to flash LumenLab {latestRelease.TagName}."); - } + return true; } - catch (Exception ex) + catch (Exception ex) when (ex is HttpRequestException or IOException) { - MessageBox.Show(ex.Message); + WriteToLogs($"Error during firmware update: {ex.Message}"); + return false; } finally { - BtnFlashFirmware.Enabled = true; + if (File.Exists(zipPath)) + { + File.Delete(zipPath); + } } } - private async void BtnSync_Click(object sender, EventArgs e) + + private async Task FlashWithEspTool() + { + var latestRelease = _context.AvailableReleases?[0]; + string downloadPath = Path.Combine(AppPaths.BinariesPath, latestRelease.TagName ?? "v99.99.99"); + string esptoolFullCmd = $"--chip esp32 --baud 921600 --port {_context.PortName} write_flash -z 0x1000 {Path.Combine(downloadPath, "bootloader.bin")} 0x8000 {Path.Combine(downloadPath, "partitions.bin")} 0x10000 {Path.Combine(downloadPath, "firmware.bin")}"; + + WriteToLogs($"Running esptool.exe {esptoolFullCmd}"); + var exitCode = await DeviceService.RunEsptoolAsync(esptoolFullCmd, AppendLog); + return exitCode; + } + + private async Task ReadDevice() { var successfulRead = await ReadConnectedDeviceVersion(); if (!successfulRead) { - MessageBox.Show("LumenLab was not detected. Please make sure the external power is on and try again.", "LumenLab Not Detected", MessageBoxButtons.RetryCancel, MessageBoxIcon.Stop); + FormatGuideLabel("Plug in your LumenLab and click Scan Devices"); + MessageBox.Show("LumenLab was not detected. Please make sure the external power is on and try again.", "LumenLab Not Detected", MessageBoxButtons.OK, MessageBoxIcon.Stop); + } + } + + private async Task QueryGitHub() + { + WriteToLogs("Querying GitHub to check for updates."); + var releases = await _networkService.GetReleasesAsync(); + + _context.AvailableReleases = releases.OrderByDescending(r => r.PublishedAt).ToList(); + + var gridRows = releases + .OrderByDescending(r => r.PublishedAt) + .Select(r => new ReleaseGridRow + { + TagName = r.TagName ?? string.Empty, + Name = r.Name?.Substring(r.Name.IndexOf("-") + 2) ?? string.Empty, + PublishedAt = r.PublishedAt ?? new DateTime(), + Release = r + }) + .ToList(); + + _releaseBindingSource.DataSource = gridRows; + + LblLatestRelease.Text = releases[0].TagName; + + foreach (var release in releases) + { + WriteToLogs($"{release.TagName} - {release.PublishedAt}"); + foreach (var asset in release.Assets) + { + WriteToLogs($" Asset: {asset.Name} -> {asset.BrowserDownloadUrl}"); + } } } @@ -295,57 +353,50 @@ private async void BtnSync_Click(object sender, EventArgs e) return null; } - private void exitToolStripMenuItem_Click(object sender, EventArgs e) + private void FormatGuideLabel(string text, Color? color = null, FontStyle? fontStyle = null, int? sizeDelta = 0) { - Application.Exit(); - } + LblUpdateAlert.Clear(); - private void ChkCustomizeConfiguration_CheckedChanged(object sender, EventArgs e) - { - TxtNumLeds.Enabled = !TxtNumLeds.Enabled; - LblTotalLeds.Enabled = !LblTotalLeds.Enabled; + LblUpdateAlert.Text = text; - LblControllerType.Enabled = !LblControllerType.Enabled; - RadPS3.Enabled = !RadPS3.Enabled; + LblUpdateAlert.SelectionStart = 0; + LblUpdateAlert.SelectionLength = LblUpdateAlert.TextLength; - TxtMacAddress.Enabled = !TxtMacAddress.Enabled; - LblMacAddress.Enabled = !LblMacAddress.Enabled; + LblUpdateAlert.SelectionAlignment = HorizontalAlignment.Center; + LblUpdateAlert.SelectionColor = color ?? Color.Black; + LblUpdateAlert.SelectionFont = new Font( + LblUpdateAlert.Font.FontFamily, + LblUpdateAlert.Font.Size + (sizeDelta ?? 0), + fontStyle ?? FontStyle.Regular + ); - TxtSerialBaud.Enabled = !TxtSerialBaud.Enabled; - LblSerialBaud.Enabled = !LblSerialBaud.Enabled; + LblUpdateAlert.SelectionLength = 0; + } - TxtBound1.Enabled = !TxtBound1.Enabled; - TxtBound2.Enabled = !TxtBound2.Enabled; - TxtBound3.Enabled = !TxtBound3.Enabled; - LblBoundaries.Enabled = !LblBoundaries.Enabled; + private void WriteToLogs(string log) + { + outputBox.AppendText($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}] - {log}{Environment.NewLine}"); } - private void toggleDebugWindowToolStripMenuItem_Click(object sender, EventArgs e) + private void AppendLog(string text) { - toggleDebugWindowToolStripMenuItem.Checked = !toggleDebugWindowToolStripMenuItem.Checked; - outputBox.Visible = !outputBox.Visible; - if (outputBox.Visible) - { - this.Height += outputBox.Height; - } - else + if (InvokeRequired) { - this.Height -= outputBox.Height; + Invoke(new Action(AppendLog), text); + return; } + + WriteToLogs(text); } - private void ChkClearMemory_CheckedChanged(object sender, EventArgs e) + private async void BtnFlashFirmware_Click(object sender, EventArgs e) { - if (ChkClearMemory.Checked) - { - MessageBox.Show("Nice try, cheater.", "Failed to erase high score ", MessageBoxButtons.OK, MessageBoxIcon.Error); - ChkClearMemory.Checked = false; - } + await FlashFirmware(); } - private void WriteToLogs(string log) + private async void BtnSync_Click(object sender, EventArgs e) { - outputBox.AppendText($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}] - {log}{Environment.NewLine}"); + await ReadDevice(); } private void SaveControllerSettingsToolStripMenuItem_Click(object sender, EventArgs e) @@ -389,4 +440,84 @@ private void LoadControllerSettingsToolStripMenuItem_Click(object sender, EventA MessageBox.Show($"Selected config file:\n{path}"); } } + + private void ExitToolStripMenuItem_Click(object sender, EventArgs e) + { + Application.Exit(); + } + + private void ChkCustomizeConfiguration_CheckedChanged(object sender, EventArgs e) + { + _userSettings.isActive = !_userSettings.isActive; + + TxtNumLeds.Enabled = !TxtNumLeds.Enabled; + LblTotalLeds.Enabled = !LblTotalLeds.Enabled; + + LblControllerType.Enabled = !LblControllerType.Enabled; + RadPS3.Enabled = !RadPS3.Enabled; + + TxtMacAddress.Enabled = !TxtMacAddress.Enabled; + LblMacAddress.Enabled = !LblMacAddress.Enabled; + + TxtSerialBaud.Enabled = !TxtSerialBaud.Enabled; + LblSerialBaud.Enabled = !LblSerialBaud.Enabled; + + TxtBound1.Enabled = !TxtBound1.Enabled; + TxtBound2.Enabled = !TxtBound2.Enabled; + TxtBound3.Enabled = !TxtBound3.Enabled; + LblBoundaries.Enabled = !LblBoundaries.Enabled; + } + + private void ToggleDebugWindowToolStripMenuItem_Click(object sender, EventArgs e) + { + toggleDebugWindowToolStripMenuItem.Checked = !toggleDebugWindowToolStripMenuItem.Checked; + outputBox.Visible = !outputBox.Visible; + if (outputBox.Visible) + { + this.Height += outputBox.Height; + } + else + { + this.Height -= outputBox.Height; + } + } + + private void ChkClearMemory_CheckedChanged(object sender, EventArgs e) + { + if (ChkClearMemory.Checked) + { + MessageBox.Show("Nice try, cheater.", "Failed to erase high score ", MessageBoxButtons.OK, MessageBoxIcon.Error); + ChkClearMemory.Checked = false; + } + } + + private void TxtNumLeds_ValueChanged(object sender, EventArgs e) + { + _userSettings.TotalLeds = (int)TxtNumLeds.Value; + } + + private void TxtMacAddress_TextChanged(object sender, EventArgs e) + { + _userSettings.MacAddress = TxtMacAddress.Text; + } + + private void TxtSerialBaud_ValueChanged(object sender, EventArgs e) + { + _userSettings.SerialBaud = (int)TxtSerialBaud.Value; + } + + private void TxtBound1_ValueChanged(object sender, EventArgs e) + { + _userSettings.Boundaries[0] = (int)TxtBound1.Value; + } + + private void TxtBound2_ValueChanged(object sender, EventArgs e) + { + _userSettings.Boundaries[1] = (int)TxtBound2.Value; + } + + private void TxtBound3_ValueChanged(object sender, EventArgs e) + { + _userSettings.Boundaries[2] = (int)TxtBound3.Value; + } } diff --git a/README.md b/README.md index c055226..876c85f 100644 --- a/README.md +++ b/README.md @@ -7,5 +7,8 @@ A Windows-based installation tool to help keep your LumenLab up-to-date. # How To Use (More detail will be added soon, the priority is to finish the core functionality by allowing users to flash controller configuration, store settings, and re-load them. Full documention will be drafted when that is deployed.) +### Windows Defender (Protection?) -[To be finalized.] \ No newline at end of file +I don't want to pay to have this officially signed. + +For what it's worth, you can see that each executable was created by the release pipeline in this GitHub Action. You can see that it's building this repository. This repository is open source, and you can see that I'm not doing anything nefarious. Trust me bro. \ No newline at end of file diff --git a/Services/ConfigManager.cs b/Services/ConfigManager.cs index 9d31f46..e5d147e 100644 --- a/Services/ConfigManager.cs +++ b/Services/ConfigManager.cs @@ -32,9 +32,9 @@ public static Config LoadConfig() var json = File.ReadAllText(AppPaths.ConfigFilePath); var config = JsonSerializer.Deserialize(json); - if (config == null || string.IsNullOrWhiteSpace(config.Version)) + if (string.IsNullOrWhiteSpace(config?.Version)) { - throw new Exception("Invalid config file content."); + throw new InvalidOperationException("Config version is missing or empty."); } return config; diff --git a/Services/DeviceService.cs b/Services/DeviceService.cs index 90ba9cb..1d43a94 100644 --- a/Services/DeviceService.cs +++ b/Services/DeviceService.cs @@ -86,7 +86,7 @@ public static void EnsureEsptoolExists() resourceStream.CopyTo(fileStream); } - public static async Task RunEsptoolAsync(string arguments, Action onOutput, CancellationToken cancellationToken) + public static async Task RunEsptoolAsync(string arguments, Action onOutput) { EnsureEsptoolExists(); var psi = new ProcessStartInfo @@ -118,7 +118,7 @@ public static async Task RunEsptoolAsync(string arguments, Action o process.BeginOutputReadLine(); process.BeginErrorReadLine(); - await process.WaitForExitAsync(cancellationToken); + await process.WaitForExitAsync(); return process.ExitCode; } diff --git a/Services/UserSettings.cs b/Services/UserSettings.cs new file mode 100644 index 0000000..9f4d9c3 --- /dev/null +++ b/Services/UserSettings.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LumenLabInstaller.Services; + +public class UserSettings +{ + public bool isActive = false; + public int TotalLeds { get; set; } = 300; + public string ControllerType { get; set; } = "PS3"; + public string MacAddress { get; set; } = "00:00:00:00:00:00"; + public int SerialBaud { get; set; } = 921600; + public int[] Boundaries { get; set; } = [ 75, 150, 225 ]; +} diff --git a/lumenlab-installer.csproj b/lumenlab-installer.csproj index 6cdec59..edbe20f 100644 --- a/lumenlab-installer.csproj +++ b/lumenlab-installer.csproj @@ -19,13 +19,13 @@ - + Firmware.firmware.bin - + Firmware.bootloader.bin - + Firmware.partitions.bin