diff --git a/NuGet.Config b/NuGet.Config index ef0bacfd..b02a9811 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -2,6 +2,5 @@ - diff --git a/README.md b/README.md index 68c990b3..4f1c518f 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,8 @@ To show the details of the ESP32 device connected to COM31. nanoff --platform esp32 --serialport COM31 --devicedetails ``` +Optionally an extra parameter `--checkpsram` can be passed, which forces the detection of PSRAM availability. + ### Deploy a managed application to an ESP32 target To deploy a managed application to an ESP32_PSRAM_REV0 target connected to COM31. @@ -447,6 +449,50 @@ nanoff --listtargets --platform stm32 If you use the `--listtargets` switch in conjunction with `--preview`, you'll get the list of available firmware packages that are available with experimental or major feature changes. +## Deploy file to device storage + +Some devices like ESP32, Orgpal and few others have storage available. Files can be deployed in this storage. You have to use the `filedeployment` parameter pointing on a JSON file to deploy files while flashing the device: + +```console +nanoff --target XIAO_ESP32C3 --update --masserase --serialport COM21 --filedeployment C:\path\deploy.json +``` + +The JSON an optional `SerialPort` in case the port to upload the files must be different than the one to flash the device or not specified in the main command line and a **mandatory** list of `Files` entries. Each entry must contains `DestinationFilePath`, the destination full path file name and `SourceFilePath` to deploy content, otherwise to delete the file, the full path with file name of the source file to be deployed: + +```json +{ + "serialport":"COM42", + "files": [ + { + "DestinationFilePath": "I:\\TestFile.txt", + "SourceFilePath": "C:\\tmp\\NFApp3\\NFApp3\\TestFile.txt" + }, + { + "DestinationFilePath": "I:\\NoneFile.txt" + }, + { + "DestinationFilePath": "I:\\wilnotexist.txt", + "SourceFilePath": "C:\\WRONGPATH\\TestFile.txt" + } + ] +} +``` + +In the case you just want to deploy the files without any other operation, you can just specify: + +```console +nanoff --filedeployment C:\path\deploy.json +``` + +In that case, the `SerialPort` must be present in the JSON file. + +> [!Note] +> If a file already exists in the storage, it will be replaced by the new one. +> +> If a file does not exist and is requested to be deleted, nothing will happen, a warning will be displayed. +> +> If a file can't be uploaded because of a problem, the deployment of the other files will continue and an error will be displayed. + ## Clear cache location If needed one can clear the local cache from the firmware packages that are stored there. @@ -462,6 +508,12 @@ nanoff --clearcache The exit codes can be checked in [this source file](https://github.com/nanoframework/nanoFirmwareFlasher/blob/main/nanoFirmwareFlasher.Library/ExitCodes.cs). +## Telemetry + +This tool is using anonymous telemetry to help us improve the usage. You can opt out by setting up an environment variable `NANOFRAMEWORK_TELEMETRY_OPTOUT` to 1. + +The telemetry information is mainly related to the command line arguments, the firmware versions installed and any issue that can occurs during the code execution. + ## Feedback and documentation To provide feedback, report issues and finding out how to contribute please refer to the [Home repo](https://github.com/nanoframework/Home). diff --git a/README.zh-cn.md b/README.zh-cn.md index cbc5cb6c..a63ecd5e 100644 --- a/README.zh-cn.md +++ b/README.zh-cn.md @@ -138,6 +138,8 @@ nanoff --update --target KALUGA_1 --serialport COM31 --clrfile "C:\nf-interprete nanoff --platform esp32 --serialport COM31 --devicedetails ``` +可选地,可以传递额外的参数 `--checkpsram`,它会强制检测PSRAM的可用性。 + ### 将托管应用程序部署到ESP32设备 将托管应用程序部署到连接到COM31的ESP32_PSRAM_REV0设备上。 diff --git a/azure-pipelines.yml b/azure-pipelines.yml index fb887f42..046138d2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -178,11 +178,17 @@ jobs: vmImage: 'windows-latest' variables: - DOTNET_NOLOGO: true - buildPlatform: 'x64' - buildConfiguration: 'Release' - solution: 'nanoFirmwareFlasher.sln' - run_update_dependents: $[dependencies.Check_Build_Options.outputs['BuildOptions.RUN_UPDATE_DEPENDENTS']] + - group: sign-client-credentials + - name: DOTNET_NOLOGO + value: true + - name: buildPlatform + value: 'x64' + - name: buildConfiguration + value: 'Release' + - name: solution + value: 'nanoFirmwareFlasher.sln' + - name: run_update_dependents + value: $[dependencies.Check_Build_Options.outputs['BuildOptions.RUN_UPDATE_DEPENDENTS']] steps: @@ -203,6 +209,11 @@ jobs: - task: Cache@2 displayName: Cache NuGet packages + condition: >- + and( + succeeded(), + eq(variables['UPDATE_DEPENDENTS'], 'false') + ) inputs: key: 'nuget | "$(Agent.OS)" | **/packages.lock.json, !bin/**' restoreKeys: | @@ -321,6 +332,21 @@ jobs: eq(variables['UPDATE_DEPENDENTS'], 'false') ) + # Replace the intrument key + - powershell: | + . ./common.ps1 + $fileToReplace = "$(System.DefaultWorkingDirectory)/nanoFirmwareFlasher.Tool\appsettings.json" + $sourceString = "INSTRUMENT_KEY" + $instrumentKey = "$(InstrumentKey)" + + Find-ReplaceInFile -filePath $fileToReplace -sourceString $sourceString -targetString $instrumentKey + displayName: Replace intrument key + condition: >- + and( + succeeded(), + eq(variables['UPDATE_DEPENDENTS'], 'false') + ) + - task: CopyFiles@1 condition: >- and( @@ -336,7 +362,7 @@ jobs: flattenFolders: true - task: DotNetCoreCLI@2 - displayName: Install SignTool tool + displayName: Install Sign Client CLI condition: >- and( succeeded(), @@ -346,19 +372,21 @@ jobs: inputs: command: custom custom: tool - arguments: install --tool-path . SignClient + arguments: install --tool-path . sign --version 0.9.1-beta.23530.1 - pwsh: | - .\SignClient "Sign" ` - --baseDirectory "$(Build.ArtifactStagingDirectory)" ` - --input "**/*.nupkg" ` - --config "$(Build.Repository.LocalPath)\config\SignClient.json" ` - --filelist "$(Build.Repository.LocalPath)\config\filelist.txt" ` - --user "$(SignClientUser)" ` - --secret '$(SignClientSecret)' ` - --name ".NET nanoFramework firmware flasher" ` + .\sign code azure-key-vault ` + "**/*.nupkg" ` + --base-directory "$(Build.ArtifactStagingDirectory)" ` + --file-list "$(Build.Repository.LocalPath)\config\filelist.txt" ` --description ".NET nanoFramework firmware flasher" ` - --descriptionUrl "https://github.com/$env:Build_Repository_Name" + --description-url "https://github.com/$env:Build_Repository_Name" ` + --azure-key-vault-tenant-id "$(SignTenantId)" ` + --azure-key-vault-client-id "$(SignClientId)" ` + --azure-key-vault-client-secret "$(SignClientSecret)" ` + --azure-key-vault-certificate "$(SignKeyVaultCertificate)" ` + --azure-key-vault-url "$(SignKeyVaultUrl)" ` + --timestamp-url http://timestamp.digicert.com displayName: Sign packages continueOnError: true condition: >- @@ -388,7 +416,7 @@ jobs: eq(variables['System.PullRequest.PullRequestId'], ''), eq(variables['UPDATE_DEPENDENTS'], 'false') ) - displayName: Push nanoff NuGet package to NuGet + displayName: Push nanoff dotnet tool to NuGet continueOnError: true inputs: command: push @@ -406,7 +434,7 @@ jobs: eq(variables['System.PullRequest.PullRequestId'], ''), eq(variables['UPDATE_DEPENDENTS'], 'false') ) - displayName: Push library NuGet package to NuGet + displayName: Push NuGet package with library to NuGet continueOnError: true inputs: command: push @@ -435,6 +463,7 @@ jobs: action: create isDraft: false addChangeLog: true + changeLogType: issueBased changeLogLabels: | [ { "label" : "Type: bug", "displayName" : "Bugs fixed", "state" : "closed" }, @@ -448,6 +477,7 @@ jobs: - task: PowerShell@2 condition: >- or( + eq(variables['System.PullRequest.PullRequestId'], ''), eq(variables['UPDATE_DEPENDENTS'], 'true'), eq(variables['run_update_dependents'], 'true') ) diff --git a/azure-pipelines/update-dependents.ps1 b/azure-pipelines/update-dependents.ps1 index 399824d8..399db064 100644 --- a/azure-pipelines/update-dependents.ps1 +++ b/azure-pipelines/update-dependents.ps1 @@ -2,7 +2,7 @@ # compute authorization header in format "AUTHORIZATION: basic 'encoded token'" # 'encoded token' is the Base64 of the string "nfbot:personal-token" -$auth = "basic $([System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("nfbot:$env:GH_TOKEN"))))" +$auth = "basic $([System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("nfbot:$env:GH_TOKEN")))" # init/reset these $commitMessage = "" @@ -55,21 +55,21 @@ $commitMessage += "`n[version update]`n`n" # better add this warning line $commitMessage += "### :warning: This is an automated update. Merge only after all tests pass. :warning:`n" -Write-Debug "Git branch" - -# create branch to perform updates -git branch $newBranchName - -Write-Debug "Checkout branch" - -# checkout branch -git checkout $newBranchName - # check if anything was changed $repoStatus = "$(git status --short --porcelain)" if ($repoStatus -ne "") { + Write-Debug "Git branch" + + # create branch to perform updates + git branch $newBranchName + + Write-Debug "Checkout branch" + + # checkout branch + git checkout $newBranchName + Write-Debug "Add changes" # commit changes @@ -114,5 +114,5 @@ if ($repoStatus -ne "") } else { - Write-Host "Nothing udpate at nanoFramework.Tools.FirmwareFlasher." + Write-Host "Nothing udpate at $repoName." } diff --git a/common.ps1 b/common.ps1 new file mode 100644 index 00000000..d1726636 --- /dev/null +++ b/common.ps1 @@ -0,0 +1,62 @@ + +# Common functions used by the other scripts + +function Check-DirectoryExists { + param ( + [string]$rootDirectory + ) + + return Test-Path -Path $rootDirectory -PathType Container +} + +function Find-ReplaceInFiles { + param ( + [string]$rootDirectory, + [string]$sourceString, + [string]$targetString + ) + + Get-ChildItem -Recurse -File $rootDirectory | ForEach-Object { + $filePath = $_.FullName + + Find-ReplaceInFile -filePath $filePath -sourceString $sourceString -targetString $targetString + } + + Write-Host "String replacement completed." +} + +function Find-ReplaceInFile { + param ( + [string]$filePath, + [string]$sourceString, + [string]$targetString + ) + + # Get the original encoding of the file + $sr = New-Object System.IO.StreamReader($filePath, $true) + [char[]] $buffer = new-object char[] 3 + $sr.Read($buffer, 0, 3) + $originalEncoding = $sr.CurrentEncoding + $sr.Close() + + # Making it simple as we only want to preserve UTF8 encoding + if ( $originalEncoding.BodyName -eq "utf-8" ) { + $fileEncoding = "UTF8" + } + else { + $fileEncoding = "ASCII" + } + + # Read the content of the file using the original encoding + $content = Get-Content -Path $filePath -Raw -Encoding $fileEncoding + + # Perform the replacement + $newContent = $content -replace [regex]::Escape($sourceString), $targetString + + if ($content -ne $newContent) { + Write-Host "Replacing in $filePath" + + # Save the modified content back to the file using the original encoding + Set-Content -Path $filePath -Value $newContent -Encoding $fileEncoding + } +} \ No newline at end of file diff --git a/lib/esp32bootloader/bootloader.bin b/lib/esp32bootloader/bootloader.bin index 613a0f57..b1328019 100644 Binary files a/lib/esp32bootloader/bootloader.bin and b/lib/esp32bootloader/bootloader.bin differ diff --git a/lib/esp32bootloader/partitions_16mb.bin b/lib/esp32bootloader/partitions_16mb.bin deleted file mode 100644 index 5f23fc00..00000000 Binary files a/lib/esp32bootloader/partitions_16mb.bin and /dev/null differ diff --git a/lib/esp32bootloader/partitions_2mb.bin b/lib/esp32bootloader/partitions_2mb.bin index 7382396f..c8ef86f1 100644 Binary files a/lib/esp32bootloader/partitions_2mb.bin and b/lib/esp32bootloader/partitions_2mb.bin differ diff --git a/lib/esp32bootloader/partitions_8mb.bin b/lib/esp32bootloader/partitions_8mb.bin deleted file mode 100644 index 9d403e78..00000000 Binary files a/lib/esp32bootloader/partitions_8mb.bin and /dev/null differ diff --git a/lib/esp32bootloader/test_startup.bin b/lib/esp32bootloader/test_startup.bin index 2445e84e..03c9a8bf 100644 Binary files a/lib/esp32bootloader/test_startup.bin and b/lib/esp32bootloader/test_startup.bin differ diff --git a/lib/esp32s2bootloader/bootloader.bin b/lib/esp32s2bootloader/bootloader.bin index 26a22275..ff2e735a 100644 Binary files a/lib/esp32s2bootloader/bootloader.bin and b/lib/esp32s2bootloader/bootloader.bin differ diff --git a/lib/esp32s2bootloader/partitions_16mb.bin b/lib/esp32s2bootloader/partitions_16mb.bin deleted file mode 100644 index 5f23fc00..00000000 Binary files a/lib/esp32s2bootloader/partitions_16mb.bin and /dev/null differ diff --git a/lib/esp32s2bootloader/partitions_8mb.bin b/lib/esp32s2bootloader/partitions_8mb.bin deleted file mode 100644 index 9d403e78..00000000 Binary files a/lib/esp32s2bootloader/partitions_8mb.bin and /dev/null differ diff --git a/lib/esp32s2bootloader/test_startup.bin b/lib/esp32s2bootloader/test_startup.bin index 3bda5a03..8015bfa6 100644 Binary files a/lib/esp32s2bootloader/test_startup.bin and b/lib/esp32s2bootloader/test_startup.bin differ diff --git a/lib/esp32s3bootloader/bootloader.bin b/lib/esp32s3bootloader/bootloader.bin new file mode 100644 index 00000000..6ea9312d Binary files /dev/null and b/lib/esp32s3bootloader/bootloader.bin differ diff --git a/lib/esp32bootloader/partitions_4mb.bin b/lib/esp32s3bootloader/partitions_4mb.bin similarity index 100% rename from lib/esp32bootloader/partitions_4mb.bin rename to lib/esp32s3bootloader/partitions_4mb.bin diff --git a/lib/esp32s3bootloader/test_startup.bin b/lib/esp32s3bootloader/test_startup.bin new file mode 100644 index 00000000..d58ec348 Binary files /dev/null and b/lib/esp32s3bootloader/test_startup.bin differ diff --git a/lib/esptool/esptoolLinux/esptool b/lib/esptool/esptoolLinux/esptool index d5351adf..3934775f 100644 Binary files a/lib/esptool/esptoolLinux/esptool and b/lib/esptool/esptoolLinux/esptool differ diff --git a/lib/esptool/esptoolMac/esptool b/lib/esptool/esptoolMac/esptool index f6493685..288c8a6d 100755 Binary files a/lib/esptool/esptoolMac/esptool and b/lib/esptool/esptoolMac/esptool differ diff --git a/lib/esptool/esptoolWin/esptool.exe b/lib/esptool/esptoolWin/esptool.exe index 3844beed..73bd4b55 100644 Binary files a/lib/esptool/esptoolWin/esptool.exe and b/lib/esptool/esptoolWin/esptool.exe differ diff --git a/lib/jlink/JLink.exe b/lib/jlink/JLink.exe index fce8f66a..52c8bc62 100644 Binary files a/lib/jlink/JLink.exe and b/lib/jlink/JLink.exe differ diff --git a/lib/jlink/JLink_x64.dll b/lib/jlink/JLink_x64.dll index 50f3565c..86dd9267 100644 Binary files a/lib/jlink/JLink_x64.dll and b/lib/jlink/JLink_x64.dll differ diff --git a/lib/jlinkLinux/JLinkExe b/lib/jlinkLinux/JLinkExe index 34ec205c..3193251e 100644 Binary files a/lib/jlinkLinux/JLinkExe and b/lib/jlinkLinux/JLinkExe differ diff --git a/lib/jlinkMac/JLinkExe b/lib/jlinkMac/JLinkExe index 9861ae0c..73d05920 100644 Binary files a/lib/jlinkMac/JLinkExe and b/lib/jlinkMac/JLinkExe differ diff --git a/nanoFirmwareFlasher.Library/CC13x26x2Firmware.cs b/nanoFirmwareFlasher.Library/CC13x26x2Firmware.cs index 6d2dd3a5..6ebb4039 100644 --- a/nanoFirmwareFlasher.Library/CC13x26x2Firmware.cs +++ b/nanoFirmwareFlasher.Library/CC13x26x2Firmware.cs @@ -3,9 +3,6 @@ // See LICENSE file in the project root for full license information. // -using System.IO; -using System.Linq; - namespace nanoFramework.Tools.FirmwareFlasher { /// diff --git a/nanoFirmwareFlasher.Library/CC13x26x2Operations.cs b/nanoFirmwareFlasher.Library/CC13x26x2Operations.cs index af200876..25166d09 100644 --- a/nanoFirmwareFlasher.Library/CC13x26x2Operations.cs +++ b/nanoFirmwareFlasher.Library/CC13x26x2Operations.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Linq; namespace nanoFramework.Tools.FirmwareFlasher { @@ -119,7 +118,7 @@ public static async System.Threading.Tasks.Task UpdateFirmwareAsync( ExitCodes programResult = ExitCodes.OK; // write HEX files to flash - if (filesToFlash.Any(f => f.EndsWith(".hex"))) + if (filesToFlash.Exists(f => f.EndsWith(".hex"))) { programResult = ccDevice.FlashHexFiles(filesToFlash); } @@ -127,7 +126,7 @@ public static async System.Threading.Tasks.Task UpdateFirmwareAsync( if (programResult == ExitCodes.OK && isApplicationBinFile) { // now program the application file - programResult = ccDevice.FlashBinFiles(new[] { applicationPath }, new[] { deploymentAddress }); + programResult = ccDevice.FlashBinFiles([applicationPath], [deploymentAddress]); } if (updateFw) diff --git a/nanoFirmwareFlasher.Library/Esp32DeviceInfo.cs b/nanoFirmwareFlasher.Library/Esp32DeviceInfo.cs index e7470ae2..9dc578d2 100644 --- a/nanoFirmwareFlasher.Library/Esp32DeviceInfo.cs +++ b/nanoFirmwareFlasher.Library/Esp32DeviceInfo.cs @@ -65,15 +65,22 @@ public class Esp32DeviceInfo public PSRamAvailability PSRamAvailable { get; } /// - /// Constructor + /// The size of the PSRAM (in MBytes). /// - /// Version of the esptool.py - /// ESP32 chip name - /// ESP32 chip features - /// MAC address of the ESP32 chip - /// Flash manufacturer ID - /// Flash device type ID - /// The size of the flash in bytes + public int PsRamSize { get; } + + /// + /// Constructor. + /// + /// The type of chip. + /// ESP32 chip name. + /// ESP32 chip features. + /// ESP32 crystal. + /// MAC address of the ESP32 chip. + /// Flash manufacturer ID. + /// Flash device type ID. + /// The size of the flash in bytes. + /// Availability of PSRAM. public Esp32DeviceInfo( string chipType, string chipName, @@ -83,7 +90,8 @@ public Esp32DeviceInfo( byte flashManufacturerId, short flashDeviceModelId, int flashSize, - PSRamAvailability psramAvailability) + PSRamAvailability psramAvailability, + int psRamSize) { ChipType = chipType; ChipName = chipName; @@ -94,6 +102,7 @@ public Esp32DeviceInfo( FlashDeviceId = flashDeviceModelId; FlashSize = flashSize; PSRamAvailable = psramAvailability; + PsRamSize = psRamSize; } internal string GetFlashSizeAsString() @@ -101,6 +110,11 @@ internal string GetFlashSizeAsString() return GetFlashSizeAsString(FlashSize); } + /// + /// Gets the flash size as a string. + /// + /// The flash size. + /// The flash size. public static string GetFlashSizeAsString(int flashSize) { return flashSize >= 0x10000 ? $"{flashSize / 0x100000}MB" : $"{flashSize / 0x400}kB"; @@ -133,7 +147,7 @@ public override string ToString() break; case PSRamAvailability.Yes: - deviceInfo.AppendLine($"PSRAM: available"); + deviceInfo.AppendLine($"PSRAM: {PsRamSize}MB"); break; case PSRamAvailability.No: diff --git a/nanoFirmwareFlasher.Library/Esp32Firmware.cs b/nanoFirmwareFlasher.Library/Esp32Firmware.cs index 93a2f80c..f64aa933 100644 --- a/nanoFirmwareFlasher.Library/Esp32Firmware.cs +++ b/nanoFirmwareFlasher.Library/Esp32Firmware.cs @@ -24,7 +24,7 @@ internal class Esp32Firmware : FirmwarePackage /// /// ESP32 nanoCLR is available for 2MB, 4MB, 8MB and 16MB flash sizes /// - private List SupportedFlashSizes => new() { 0x200000, 0x400000, 0x800000, 0x1000000 }; + private List SupportedFlashSizes => [0x200000, 0x400000, 0x800000, 0x1000000]; internal string BootloaderPath; @@ -82,8 +82,12 @@ internal async System.Threading.Tasks.Task DownloadAndExtractAsync(Es // get ESP32 partitions FlashPartitions = new Dictionary { - // bootloader goes to 0x1000, except for ESP32_C3 and ESP32_S3, which goes to 0x0 - { deviceInfo.ChipType == "ESP32-C3" || deviceInfo.ChipType == "ESP32-S3" ? 0x0 : 0x1000, Path.Combine(LocationPath, BootloaderPath) }, + // bootloader goes to 0x1000, except for ESP32_C3/C6/H2/S3, which goes to 0x0 + { + deviceInfo.ChipType == "ESP32-C3" + || deviceInfo.ChipType == "ESP32-C6" + || deviceInfo.ChipType == "ESP32-H2" + || deviceInfo.ChipType == "ESP32-S3" ? 0x0 : 0x1000, Path.Combine(LocationPath, BootloaderPath) }, // nanoCLR goes to 0x10000 { CLRAddress, Path.Combine(LocationPath, "nanoCLR.bin") }, diff --git a/nanoFirmwareFlasher.Library/Esp32Operations.cs b/nanoFirmwareFlasher.Library/Esp32Operations.cs index e2b6f77a..2aeb693e 100644 --- a/nanoFirmwareFlasher.Library/Esp32Operations.cs +++ b/nanoFirmwareFlasher.Library/Esp32Operations.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text.RegularExpressions; namespace nanoFramework.Tools.FirmwareFlasher { @@ -93,8 +94,8 @@ public static ExitCodes BackupFlash( /// /// Perform firmware update on a ESP32 device. /// - /// to use when performing update. - /// of device to update. + /// to use when performing update. + /// of device to update. /// Name of the target to update. /// Set to to force download of firmware package. /// Firmware version to update to. @@ -103,6 +104,7 @@ public static ExitCodes BackupFlash( /// Flash address to use when deploying an aplication. /// Path to CLR file to use for firmware update. /// to perform validation of update package against connected target. + /// If perform mass erase on device before updating. /// Set verbosity level of progress and error messages. /// Size of partition table. /// The with the operation result. @@ -117,6 +119,7 @@ public static async System.Threading.Tasks.Task UpdateFirmwareAsync( string deploymentAddress, string clrFile, bool fitCheck, + bool massErase, VerbosityLevel verbosity, PartitionTableSize? partitionTableSize) { @@ -126,8 +129,10 @@ public static async System.Threading.Tasks.Task UpdateFirmwareAsync( // perform sanity checks for the specified target against the connected device details if (esp32Device.ChipType != "ESP32" && - esp32Device.ChipType != "ESP32-S2" && esp32Device.ChipType != "ESP32-C3" && + esp32Device.ChipType != "ESP32-C6" && + esp32Device.ChipType != "ESP32-H2" && + esp32Device.ChipType != "ESP32-S2" && esp32Device.ChipType != "ESP32-S3") { // connected to a device not supported @@ -218,26 +223,6 @@ public static async System.Threading.Tasks.Task UpdateFirmwareAsync( } } } - else if (esp32Device.ChipType == "ESP32-S2") - { - // version schema for ESP32-S2 - //Previously Used Schemes | Previous Identification | vM.X - // n/a | 0 | v0.0 - // ECO1 | 1 | v1.0 - - // can't guess with certainty for this series, better request a target name to the user - - Console.ForegroundColor = ConsoleColor.Red; - - Console.WriteLine(""); - Console.WriteLine($"For ESP32-S2 series nanoff isn't able to make an educated guess on the best target to use."); - Console.WriteLine($"Please provide a valid target name using this option '--target MY_ESP32_S2_TARGET' instead of '--platform esp32'."); - Console.WriteLine(""); - - Console.ForegroundColor = ConsoleColor.White; - - return ExitCodes.E9000; - } else if (esp32Device.ChipType == "ESP32-C3") { // version schema for ESP32-C3 @@ -274,6 +259,80 @@ public static async System.Threading.Tasks.Task UpdateFirmwareAsync( // compose target name targetName = $"ESP32_C3{revisionSuffix}"; } + else if (esp32Device.ChipType == "ESP32-C6") + { + // version schema for ESP32-C6 + + string revisionSuffix; + + // so far we are only offering a single ESP32_C6 build + if (esp32Device.ChipName.Contains("revision v0.0") || esp32Device.ChipName.Contains("revision v0.1")) + { + revisionSuffix = ""; + } + else + { + Console.ForegroundColor = ConsoleColor.Red; + + Console.WriteLine(""); + Console.WriteLine($"Unsupported ESP32_C6 revision."); + Console.WriteLine(""); + + Console.ForegroundColor = ConsoleColor.White; + + return ExitCodes.E9000; + } + + // compose target name + targetName = $"ESP32_C6{revisionSuffix}"; + } + else if (esp32Device.ChipType == "ESP32-H2") + { + // version schema for ESP32-H2 + + string revisionSuffix; + + // so far we are only offering a single ESP32_H2 build + if (esp32Device.ChipName.Contains("revision v0.1") || esp32Device.ChipName.Contains("revision v0.2")) + { + revisionSuffix = ""; + } + else + { + Console.ForegroundColor = ConsoleColor.Red; + + Console.WriteLine(""); + Console.WriteLine($"Unsupported ESP32_H2 revision."); + Console.WriteLine(""); + + Console.ForegroundColor = ConsoleColor.White; + + return ExitCodes.E9000; + } + + // compose target name + targetName = $"ESP32_H2{revisionSuffix}"; + } + else if (esp32Device.ChipType == "ESP32-S2") + { + // version schema for ESP32-S2 + //Previously Used Schemes | Previous Identification | vM.X + // n/a | 0 | v0.0 + // ECO1 | 1 | v1.0 + + // can't guess with certainty for this series, better request a target name to the user + + Console.ForegroundColor = ConsoleColor.Red; + + Console.WriteLine(""); + Console.WriteLine($"For ESP32-S2 series nanoff isn't able to make an educated guess on the best target to use."); + Console.WriteLine($"Please provide a valid target name using this option '--target MY_ESP32_S2_TARGET' instead of '--platform esp32'."); + Console.WriteLine(""); + + Console.ForegroundColor = ConsoleColor.White; + + return ExitCodes.E9000; + } else if (esp32Device.ChipType == "ESP32-S3") { // version schema for ESP32-S3 @@ -388,14 +447,18 @@ public static async System.Threading.Tasks.Task UpdateFirmwareAsync( string applicationBinary = new FileInfo(applicationPath).FullName; - // add DEPLOYMENT partition with the address provided in the command OR the address from the partition table - firmware.FlashPartitions = new Dictionary() + // check for empty flash partitions + if (firmware.FlashPartitions is null) { - { + firmware.FlashPartitions = []; + } + + // add DEPLOYMENT partition with the address provided in the command OR the address from the partition table + firmware.FlashPartitions.Add( address != 0 ? (int)address : firmware.DeploymentPartitionAddress, applicationBinary - } - }; + ); + } else { @@ -403,8 +466,10 @@ public static async System.Threading.Tasks.Task UpdateFirmwareAsync( } } - if (updateFw) + if (updateFw + && massErase) { + // erase flash, if masse erase was requested // updating fw calls for a flash erase if (verbosity >= VerbosityLevel.Normal) { @@ -412,7 +477,6 @@ public static async System.Threading.Tasks.Task UpdateFirmwareAsync( Console.Write($"Erasing flash..."); } - // erase flash operationResult = espTool.EraseFlash(); if (operationResult == ExitCodes.OK) @@ -438,6 +502,47 @@ public static async System.Threading.Tasks.Task UpdateFirmwareAsync( Console.Write($"Flashing firmware..."); } + int configPartitionAddress = 0; + int configPartitionSize = 0; + string configPartitionBackup = Path.GetRandomFileName(); + + // if mass erase wasn't requested, backup config partitition + if (!massErase) + { + // check if the update file includes a partition table + if (File.Exists(Path.Combine(firmware.LocationPath, $"partitions_nanoclr_{Esp32DeviceInfo.GetFlashSizeAsString(esp32Device.FlashSize).ToLowerInvariant()}.csv"))) + { + // can't do this without a partition table + + + // compose path to partition file + string partitionCsvFile = Path.Combine(firmware.LocationPath, $"partitions_nanoclr_{Esp32DeviceInfo.GetFlashSizeAsString(esp32Device.FlashSize).ToLowerInvariant()}.csv"); + + var partitionDetails = File.ReadAllText(partitionCsvFile); + + // grab details for the config partition + string pattern = @"config,.*?(0x[0-9A-Fa-f]+),.*?(0x[0-9A-Fa-f]+),"; + Regex regex = new Regex(pattern); + Match match = regex.Match(partitionDetails); + + if (match.Success) + { + // just try to parse, ignore failures + int.TryParse(match.Groups[1].Value.Substring(2), System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture, out configPartitionAddress); + int.TryParse(match.Groups[2].Value.Substring(2), System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture, out configPartitionSize); + } + + // backup config partition + // ignore failures + _ = espTool.BackupConfigPartition( + configPartitionBackup, + configPartitionAddress, + configPartitionSize); + + firmware.FlashPartitions.Add(configPartitionAddress, configPartitionBackup); + } + } + // write to flash operationResult = espTool.WriteFlash(firmware.FlashPartitions); @@ -445,8 +550,6 @@ public static async System.Threading.Tasks.Task UpdateFirmwareAsync( { if (verbosity >= VerbosityLevel.Normal) { - Console.Write($"Flashing firmware..."); - Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("OK".PadRight(110)); @@ -471,10 +574,152 @@ public static async System.Threading.Tasks.Task UpdateFirmwareAsync( } } + // delete config partition backup + try + { + if (File.Exists(configPartitionBackup)) + { + File.Delete(configPartitionBackup); + } + } + catch + { + // don't care + } + Console.ForegroundColor = ConsoleColor.White; } return operationResult; } + + /// + /// Deplay application on a ESP32 device. + /// + /// to use when performing update. + /// of device to update. + /// Name of the target to update. + /// Path to application to update along with the firmware update. + /// Flash address to use when deploying an aplication. + /// Set verbosity level of progress and error messages. + /// Size of partition table. + /// The with the operation result. + public static async System.Threading.Tasks.Task DeployApplicationAsync( + EspTool espTool, + Esp32DeviceInfo esp32Device, + string targetName, + string applicationPath, + string deploymentAddress, + VerbosityLevel verbosity, + PartitionTableSize? partitionTableSize) + { + uint address = 0; + + // perform sanity checks for the specified target against the connected device details + if (esp32Device.ChipType != "ESP32" && + esp32Device.ChipType != "ESP32-C3" && + esp32Device.ChipType != "ESP32-C6" && + esp32Device.ChipType != "ESP32-H2" && + esp32Device.ChipType != "ESP32-S2" && + esp32Device.ChipType != "ESP32-S3") + { + // connected to a device not supported + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine(""); + Console.WriteLine("******************************* WARNING *******************************"); + Console.WriteLine("Seems that the connected device is not supported by .NET nanoFramework"); + Console.WriteLine("Most likely it won't boot"); + Console.WriteLine("************************************************************************"); + Console.WriteLine(""); + } + + Esp32Firmware firmware = new Esp32Firmware( + targetName, + null, + false, + partitionTableSize) + { + Verbosity = verbosity + }; + + // include application file? + if (!string.IsNullOrEmpty(applicationPath)) + { + // check application file + if (File.Exists(applicationPath)) + { + // this operation includes a deployment image + // try parsing the deployment address from parameter, if provided + if (!string.IsNullOrEmpty(deploymentAddress)) + { + // need to remove the leading 0x and to specify that hexadecimal values are allowed + if (!uint.TryParse(deploymentAddress.Substring(2), System.Globalization.NumberStyles.AllowHexSpecifier, System.Globalization.CultureInfo.InvariantCulture, out address)) + { + return ExitCodes.E9009; + } + } + + string applicationBinary = new FileInfo(applicationPath).FullName; + + // check for empty flash partitions + if (firmware.FlashPartitions is null) + { + firmware.FlashPartitions = []; + } + + // add DEPLOYMENT partition with the address provided in the command OR the address from the partition table + firmware.FlashPartitions.Add( + address != 0 ? (int)address : firmware.DeploymentPartitionAddress, + applicationBinary + ); + } + else + { + return ExitCodes.E9008; + } + } + + Console.ForegroundColor = ConsoleColor.White; + + if (verbosity >= VerbosityLevel.Normal) + { + Console.Write($"Flashing deployment partition..."); + } + + // write to flash + ExitCodes operationResult = espTool.WriteFlash(firmware.FlashPartitions); + + if (operationResult == ExitCodes.OK) + { + if (verbosity >= VerbosityLevel.Normal) + { + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine("OK".PadRight(110)); + + // warn user if reboot is not possible + if (espTool.CouldntResetTarget) + { + Console.ForegroundColor = ConsoleColor.Yellow; + + Console.WriteLine(""); + Console.WriteLine("**********************************************"); + Console.WriteLine("The connected device is in 'download mode'."); + Console.WriteLine("Please reset the chip manually to run nanoCLR."); + Console.WriteLine("**********************************************"); + Console.WriteLine(""); + + Console.ForegroundColor = ConsoleColor.White; + } + } + else + { + Console.WriteLine(""); + } + } + + Console.ForegroundColor = ConsoleColor.White; + + return operationResult; + } } } diff --git a/nanoFirmwareFlasher.Library/EspTool.cs b/nanoFirmwareFlasher.Library/EspTool.cs index 9c372fcf..c9b2f6a4 100644 --- a/nanoFirmwareFlasher.Library/EspTool.cs +++ b/nanoFirmwareFlasher.Library/EspTool.cs @@ -29,7 +29,7 @@ public partial class EspTool private readonly string _serialPort = null; /// - /// The baud rate for the serial port. The default comming from . + /// The baud rate for the serial port. The default comming from CLI Options.BaudRate./>. /// private int _baudRate = 0; @@ -43,6 +43,8 @@ public partial class EspTool /// private int _flashSize = -1; + + private bool connectPatternFound; private DateTime connectTimeStamp; @@ -77,6 +79,7 @@ public partial class EspTool /// The flash mode for the esptool /// The flash frequency for the esptool /// Partition table size to use + /// The verbosity level of messages public EspTool( string serialPort, int baudRate, @@ -119,7 +122,7 @@ public EspTool( } else { - if (!File.Exists(serialPort)) + if (!System.IO.File.Exists(serialPort)) { throw new EspToolExecutionException(); } @@ -142,14 +145,15 @@ public EspTool( /// The filled info structure with all the information about the connected ESP32 device or null if an error occured public Esp32DeviceInfo GetDeviceDetails( string targetName, - bool requireFlashSize = true) + bool requireFlashSize = true, + bool forcePsRamCheck = false) { string messages; if (Verbosity >= VerbosityLevel.Normal) { Console.ForegroundColor = ConsoleColor.White; - Console.WriteLine($"Reading details from chip..."); + Console.Write($"Reading details from chip..."); } // execute flash_id command and parse the result @@ -161,7 +165,7 @@ public Esp32DeviceInfo GetDeviceDetails( null, out messages)) { - if(messages.Contains("A fatal error occurred: Failed to connect to Espressif device: No serial data received.")) + if (messages.Contains("A fatal error occurred: Failed to connect to Espressif device: No serial data received.")) { Console.ForegroundColor = ConsoleColor.Red; @@ -193,7 +197,9 @@ public Esp32DeviceInfo GetDeviceDetails( } } - var match = Regex.Match(messages, $"(Detecting chip type... )(?[ESP32\\-ICOCH]+)(.*?[\r\n]*)*(Chip is )(?.*)(.*?[\r\n]*)*(Features: )(?.*)(.*?[\r\n]*)*(Crystal is )(?.*)(.*?[\r\n]*)*(MAC: )(?.*)(.*?[\r\n]*)*(Manufacturer: )(?.*)(.*?[\r\n]*)*(Device: )(?.*)(.*?[\r\n]*)*(Detected flash size: )(?.*)"); + var match = Regex.Match(messages, + $"(Detecting chip type... )(?[ESP32\\-ICOCH6]+)(.*?[\r\n]*)*(Chip is )(?.*)(.*?[\r\n]*)*(Features: )(?.*)(.*?[\r\n]*)*(Crystal is )(?.*)(.*?[\r\n]*)*(MAC: )(?.*)(.*?[\r\n]*)*(Manufacturer: )(?.*)(.*?[\r\n]*)*(Device: )(?.*)(.*?[\r\n]*)*(Detected flash size: )(?.*)"); + if (!match.Success) { throw new EspToolExecutionException(messages); @@ -231,36 +237,27 @@ public Esp32DeviceInfo GetDeviceDetails( // lower case, no hifen _chipType = chipType.ToLower().Replace("-", ""); - // try to find out if PSRAM is present PSRamAvailability psramIsAvailable = PSRamAvailability.Undetermined; + int psRamSize = 0; - if (name.Contains("PICO")) + if (_chipType == "esp32c3" + || _chipType == "esp32c6" + || _chipType == "esp32h2") { - // PICO's don't have PSRAM, so don't even bother + // these series doesn't have PSRAM psramIsAvailable = PSRamAvailability.No; } - else if (name.Contains("ESP32-S2") - && targetName == "FEATHER_S2") - { - // FEATHER_S2's have PSRAM, so don't even bother - psramIsAvailable = PSRamAvailability.Yes; - } - else if (name.Contains("ESP32-C3") - || name.Contains("ESP32-S3")) + else if (_chipType == "esp32s3") { - // all ESP32-C3/S3 SDK config have support for PSRAM, so don't even bother + // For now assuming all S3 have PSRAM. + // TODO: following https://github.com/espressif/esptool/issues/970 + // The download mode register is not cleared so a reset/run command does not work on the S3. We should retest this after depending on what will be the fix for that issue. psramIsAvailable = PSRamAvailability.Undetermined; } else { - // if a target name wasn't provided, check for PSRAM - // except for ESP32_C3 and S3 - if (targetName == null - && !name.Contains("ESP32-C3") - && !name.Contains("ESP32-S3")) - { - psramIsAvailable = FindPSRamAvailable(); - } + //try to find out if PSRAM is present + psramIsAvailable = FindPSRamAvailable(out psRamSize, forcePsRamCheck); } if (Verbosity >= VerbosityLevel.Normal) @@ -279,51 +276,95 @@ public Esp32DeviceInfo GetDeviceDetails( byte.Parse(manufacturer, NumberStyles.AllowHexSpecifier), short.Parse(device, NumberStyles.HexNumber), _flashSize, - psramIsAvailable); + psramIsAvailable, + psRamSize); } /// /// Perform detection of PSRAM availability on connected device. /// + /// Force the detection of PSRAM availability. + /// Size of the PSRAM device, if detection was succesfull. /// Information about availability of PSRAM, if that was possible to determine. - private PSRamAvailability FindPSRamAvailable() + private PSRamAvailability FindPSRamAvailable( + out int psRamSize, + bool force = false) { - PSRamAvailability pSRamAvailability = PSRamAvailability.Undetermined; - // don't want to output anything from esptool // backup current verbosity setting var bkpVerbosity = Verbosity; Verbosity = VerbosityLevel.Quiet; - // compose bootloader partition - var bootloaderPartition = new Dictionary + // default to no PSRAM + psRamSize = 0; + + try { - // bootloader goes to 0x1000, except for ESP32_C3 and ESP32_S3, which goes to 0x0 - { _chipType == "esp32c3" || _chipType == "esp32s3" ? 0x0 : 0x1000, Path.Combine(Utilities.ExecutingPath, $"{_chipType}bootloader", "bootloader.bin") }, + // if forced, run the test app to determine PSRAM availability + if (force) + { + // adjust flash size according to the series + // defautl to 2MB for ESP32 series + var flashSize = 2 * 1024 * 1024; - // nanoCLR goes to 0x10000 - { 0x10000, Path.Combine(Utilities.ExecutingPath, $"{_chipType}bootloader", "test_startup.bin") }, + if (_chipType == "esp32s2" + || _chipType == "esp32s3") + { + flashSize = 4 * 1024 * 1024; + } - // partition table goes to 0x8000; there are partition tables for 2MB, 4MB, 8MB and 16MB flash sizes - { 0x8000, Path.Combine(Utilities.ExecutingPath, $"{_chipType}bootloader", $"partitions_{Esp32DeviceInfo.GetFlashSizeAsString(_flashSize).ToLowerInvariant()}.bin") } - }; + // compose bootloader partition + var bootloaderPartition = new Dictionary + { + // bootloader goes to 0x1000, except for ESP32_S3, which goes to 0x0 + { _chipType == "esp32s3" ? 0x0 : 0x1000, Path.Combine(Utilities.ExecutingPath, $"{_chipType}bootloader", "bootloader.bin") }, + + // nanoCLR goes to 0x10000 + { 0x10000, Path.Combine(Utilities.ExecutingPath, $"{_chipType}bootloader", "test_startup.bin") }, + + // partition table goes to 0x8000; there are partition tables for 2MB, 4MB, 8MB and 16MB flash sizes + { 0x8000, Path.Combine(Utilities.ExecutingPath, $"{_chipType}bootloader", $"partitions_{Esp32DeviceInfo.GetFlashSizeAsString(flashSize).ToLowerInvariant()}.bin") } + }; + + // need to use standard baud rate here because of boards put in download mode + if (WriteFlash(bootloaderPartition, true) != ExitCodes.OK) + { + // something went wrong, can't determine PSRAM availability + return PSRamAvailability.Undetermined; + } + } + else + { + // execute run command to force soft reset + // if the device is running a nanoFramework image, it will output information about the PSRAM + if (!RunEspTool( + $" run ", + true, + true, + true, + '\r', + out _)) + { + // something went wrong, can't determine PSRAM availability + return PSRamAvailability.Undetermined; + } + } - // need to use standard baud rate here because of boards put in download mode - if (WriteFlash(bootloaderPartition, true) == ExitCodes.OK) - { // check if the if (_esptoolMessage.Contains("esptool.py can not exit the download mode over USB")) { // this board was put on download mode manually, can't run the test app... - return PSRamAvailability.Undetermined; } try { - // open COM port and grab output // force baud rate to 115200 (standard baud rate for boootloader) - SerialPort espDevice = new SerialPort(_serialPort, 115200); + SerialPort espDevice = new( + _serialPort, + 115200); + + // open COM port and grab output espDevice.Open(); if (espDevice.IsOpen) @@ -336,14 +377,35 @@ private PSRamAvailability FindPSRamAvailable() espDevice.Close(); - // find magic string + // look for "magic" string + if (bootloaderOutput.Contains("PSRAM initialized")) { - pSRamAvailability = PSRamAvailability.Yes; + // output similiar to this: + // I(206) esp_psram: Found 4MB PSRAM device + // I(206) esp_psram: Speed: 40MHz + // I(209) esp_psram: PSRAM initialized, cache is in low / high(2 - core) mode. + + // extract PSRAM size + var match = Regex.Match(bootloaderOutput, @"Found (?\d+)MB PSRAM device"); + if (match.Success) + { + psRamSize = int.Parse(match.Groups["size"].Value); + } + + return PSRamAvailability.Yes; + } + else if (bootloaderOutput.Contains("PSRAM ID read error")) + { + // output similiar to this: + // E(206) quad_psram: PSRAM ID read error: 0xffffffff, PSRAM chip not found or not supported + // E(210) esp_psram: PSRAM enabled but initialization failed.Bailing out. + return PSRamAvailability.No; } else { - pSRamAvailability = PSRamAvailability.No; + // can't determine PSRAM availability + return PSRamAvailability.Undetermined; } } } @@ -352,11 +414,13 @@ private PSRamAvailability FindPSRamAvailable() // don't care about any exceptions } } + finally + { + // restore verbosity setting + Verbosity = bkpVerbosity; + } - // restore verbosity setting - Verbosity = bkpVerbosity; - - return pSRamAvailability; + return PSRamAvailability.Undetermined; } /// @@ -392,6 +456,44 @@ internal void BackupFlash(string backupFilename, } } + /// + /// Backup the entire flash into a bin file + /// + /// Backup file including full path + /// Start address of the config partition. + /// Size of the config partition. + /// true if successful + internal ExitCodes BackupConfigPartition( + string backupFilename, + int address, + int size) + { + // execute dump_mem command and parse the result; progress message can be found be searching for backspaces (ASCII code 8) + if (!RunEspTool( + $"read_flash 0x{address:X} 0x{size:X} \"{backupFilename}\"", + false, + true, + false, + null, + out string messages)) + { + throw new ReadEsp32FlashException(messages); + } + + var match = Regex.Match(messages, "(?Read .*)(.*?\n)*"); + if (!match.Success) + { + throw new ReadEsp32FlashException(messages); + } + + if (Verbosity >= VerbosityLevel.Detailed) + { + Console.WriteLine(match.Groups["message"].ToString().Trim()); + } + + return ExitCodes.OK; + } + /// /// Erase the entire flash of the ESP32 chip. /// @@ -458,6 +560,7 @@ internal ExitCodes EraseFlashSegment(uint startAddress, uint length) /// Write to the flash /// /// dictionary which keys are the start addresses and the values are the complete filenames (the bin files) + /// Use the standard baud rate (default is false). /// true if successful internal ExitCodes WriteFlash( Dictionary partsToWrite, diff --git a/nanoFirmwareFlasher.Library/Exceptions/CantConnectToDfuDeviceException.cs b/nanoFirmwareFlasher.Library/Exceptions/CantConnectToDfuDeviceException.cs index afcb1fa9..7cefe0ce 100644 --- a/nanoFirmwareFlasher.Library/Exceptions/CantConnectToDfuDeviceException.cs +++ b/nanoFirmwareFlasher.Library/Exceptions/CantConnectToDfuDeviceException.cs @@ -14,18 +14,35 @@ namespace nanoFramework.Tools.FirmwareFlasher [Serializable] public class CantConnectToDfuDeviceException : Exception { + /// + /// DFU device connection exception. + /// public CantConnectToDfuDeviceException() { } + /// + /// DFU device connection exception. + /// + /// Message to display. public CantConnectToDfuDeviceException(string message) : base(message) { } + /// + /// DFU device connection exception. + /// + /// Message to display. + /// The exception to display. public CantConnectToDfuDeviceException(string message, Exception innerException) : base(message, innerException) { } + /// + /// DFU device connection exception. + /// + /// Serialized information. + /// Streamed context. protected CantConnectToDfuDeviceException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/nanoFirmwareFlasher.Library/Exceptions/CantConnectToJLinkDeviceException.cs b/nanoFirmwareFlasher.Library/Exceptions/CantConnectToJLinkDeviceException.cs index e85db4ae..2b01104e 100644 --- a/nanoFirmwareFlasher.Library/Exceptions/CantConnectToJLinkDeviceException.cs +++ b/nanoFirmwareFlasher.Library/Exceptions/CantConnectToJLinkDeviceException.cs @@ -14,18 +14,35 @@ namespace nanoFramework.Tools.FirmwareFlasher [Serializable] public class CantConnectToJLinkDeviceException : Exception { + /// + /// Cannot connect to the J-Link device exception. + /// public CantConnectToJLinkDeviceException() { } + /// + /// Cannot connect to the JLINK device exception. + /// + /// Message to display. public CantConnectToJLinkDeviceException(string message) : base(message) { } + /// + /// Cannot connect to the JLINK device exception. + /// + /// Message to display. + /// The exception to display. public CantConnectToJLinkDeviceException(string message, Exception innerException) : base(message, innerException) { } + /// + /// Cannot connect to the JLINK device exception. + /// + /// Serialized information. + /// Streamed context. protected CantConnectToJLinkDeviceException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/nanoFirmwareFlasher.Library/Exceptions/CantConnectToJtagDeviceException.cs b/nanoFirmwareFlasher.Library/Exceptions/CantConnectToJtagDeviceException.cs index ebba5e60..267418a7 100644 --- a/nanoFirmwareFlasher.Library/Exceptions/CantConnectToJtagDeviceException.cs +++ b/nanoFirmwareFlasher.Library/Exceptions/CantConnectToJtagDeviceException.cs @@ -14,18 +14,35 @@ namespace nanoFramework.Tools.FirmwareFlasher [Serializable] public class CantConnectToJtagDeviceException : Exception { + /// + /// Cannot connect to JTAG device exception. + /// public CantConnectToJtagDeviceException() { } + /// + /// Cannot connect to JTAG device exception. + /// + /// Message to display. public CantConnectToJtagDeviceException(string message) : base(message) { } + /// + /// Cannot connect to JTAG device exception. + /// + /// Message to display. + /// The exception to display. public CantConnectToJtagDeviceException(string message, Exception innerException) : base(message, innerException) { } + /// + /// Cannot connect to JTAG device exception. + /// + /// Serialized information. + /// Streamed context. protected CantConnectToJtagDeviceException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/nanoFirmwareFlasher.Library/Exceptions/CantConnectToNanoDeviceException.cs b/nanoFirmwareFlasher.Library/Exceptions/CantConnectToNanoDeviceException.cs index b13c7fa7..03eaf0d4 100644 --- a/nanoFirmwareFlasher.Library/Exceptions/CantConnectToNanoDeviceException.cs +++ b/nanoFirmwareFlasher.Library/Exceptions/CantConnectToNanoDeviceException.cs @@ -14,18 +14,35 @@ namespace nanoFramework.Tools.FirmwareFlasher [Serializable] public class CantConnectToNanoDeviceException : Exception { + /// + /// Cannot connect to device exception. + /// public CantConnectToNanoDeviceException() { } + /// + /// Cannot connect to device exception. + /// + /// Message to display. public CantConnectToNanoDeviceException(string message) : base(message) { } + /// + /// Cannot connect to device exception. + /// + /// Message to display. + /// The exception to display. public CantConnectToNanoDeviceException(string message, Exception innerException) : base(message, innerException) { } + /// + /// Cannot connect to device exception. + /// + /// Serialized information. + /// Streamed context. protected CantConnectToNanoDeviceException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/nanoFirmwareFlasher.Library/Exceptions/DfuFileDoesNotExistException.cs b/nanoFirmwareFlasher.Library/Exceptions/DfuFileDoesNotExistException.cs index 3c0e316e..c22f5a8e 100644 --- a/nanoFirmwareFlasher.Library/Exceptions/DfuFileDoesNotExistException.cs +++ b/nanoFirmwareFlasher.Library/Exceptions/DfuFileDoesNotExistException.cs @@ -14,18 +14,35 @@ namespace nanoFramework.Tools.FirmwareFlasher [Serializable] public class DfuFileDoesNotExistException : Exception { + /// + /// DFU file does not exist exception. + /// public DfuFileDoesNotExistException() { } + /// + /// DFU file does not exist exception. + /// + /// Message to display. public DfuFileDoesNotExistException(string message) : base(message) { } + /// + /// DFU file does not exist exception. + /// + /// Message to display. + /// The exception to display. public DfuFileDoesNotExistException(string message, Exception innerException) : base(message, innerException) { } + /// + /// DFU file does not exist exception. + /// + /// Serialized information. + /// Streamed context. protected DfuFileDoesNotExistException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/nanoFirmwareFlasher.Library/Exceptions/DfuOperationFailedException.cs b/nanoFirmwareFlasher.Library/Exceptions/DfuOperationFailedException.cs index f5e0c131..ce7df3dc 100644 --- a/nanoFirmwareFlasher.Library/Exceptions/DfuOperationFailedException.cs +++ b/nanoFirmwareFlasher.Library/Exceptions/DfuOperationFailedException.cs @@ -14,18 +14,35 @@ namespace nanoFramework.Tools.FirmwareFlasher [Serializable] public class DfuOperationFailedException : Exception { + /// + /// DFU operation failed exception. + /// public DfuOperationFailedException() { } + /// + /// DFU operation failed exception. + /// + /// Message to display. public DfuOperationFailedException(string message) : base(message) { } + /// + /// DFU operation failed exception. + /// + /// Message to display. + /// The exception to display. public DfuOperationFailedException(string message, Exception innerException) : base(message, innerException) { } + /// + /// DFU operation failed exception. + /// + /// Serialized information. + /// Streamed context. protected DfuOperationFailedException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/nanoFirmwareFlasher.Library/Exceptions/EraseEsp32FlashException.cs b/nanoFirmwareFlasher.Library/Exceptions/EraseEsp32FlashException.cs index f094b0e9..eceb29f0 100644 --- a/nanoFirmwareFlasher.Library/Exceptions/EraseEsp32FlashException.cs +++ b/nanoFirmwareFlasher.Library/Exceptions/EraseEsp32FlashException.cs @@ -19,15 +19,29 @@ public class EraseEsp32FlashException : Exception /// public string ExecutionError; + /// + /// ESP32 tool erase exception. + /// + /// Message to display. public EraseEsp32FlashException(string message) : base(message) { ExecutionError = message; } + /// + /// ESP32 tool erase exception. + /// + /// Message to display. + /// The exception to display. public EraseEsp32FlashException(string message, Exception innerException) : base(message, innerException) { } + /// + /// ESP32 tool erase exception. + /// + /// Serialized information. + /// Streamed context protected EraseEsp32FlashException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/nanoFirmwareFlasher.Library/Exceptions/EspToolExecutionException.cs b/nanoFirmwareFlasher.Library/Exceptions/EspToolExecutionException.cs index dd8fdca4..c18e239e 100644 --- a/nanoFirmwareFlasher.Library/Exceptions/EspToolExecutionException.cs +++ b/nanoFirmwareFlasher.Library/Exceptions/EspToolExecutionException.cs @@ -19,20 +19,37 @@ public class EspToolExecutionException : Exception /// public string ExecutionError; + /// + /// ESP32 tool execution exception. + /// public EspToolExecutionException() : base() { } + /// + /// ESP32 tool execution exception. + /// + /// Message to display. public EspToolExecutionException(string message) : base(message) { ExecutionError = message; } + /// + /// ESP32 tool execution exception. + /// + /// Message to display. + /// The exception to display. public EspToolExecutionException(string message, Exception innerException) : base(message, innerException) { } + /// + /// ESP32 tool execution exception. + /// + /// Serialized information. + /// Streamed context. protected EspToolExecutionException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/nanoFirmwareFlasher.Library/Exceptions/NanoDeviceOperationFailedException.cs b/nanoFirmwareFlasher.Library/Exceptions/NanoDeviceOperationFailedException.cs index 6b117b9e..5e561c7a 100644 --- a/nanoFirmwareFlasher.Library/Exceptions/NanoDeviceOperationFailedException.cs +++ b/nanoFirmwareFlasher.Library/Exceptions/NanoDeviceOperationFailedException.cs @@ -14,18 +14,35 @@ namespace nanoFramework.Tools.FirmwareFlasher [Serializable] public class NanoDeviceOperationFailedException : Exception { + /// + /// NanoFramework Device Operation Exception. + /// public NanoDeviceOperationFailedException() { } + /// + /// NanoFramework Device Operation Exception. + /// + /// Message to display. public NanoDeviceOperationFailedException(string message) : base(message) { } + /// + /// NanoFramework Device Operation Exception. + /// + /// Message to display. + /// The exception to display. public NanoDeviceOperationFailedException(string message, Exception innerException) : base(message, innerException) { } + /// + /// NanoFramework Device Operation Exception. + /// + /// Serialized information. + /// Streamed context. protected NanoDeviceOperationFailedException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/nanoFirmwareFlasher.Library/Exceptions/NoOperationPerformedException.cs b/nanoFirmwareFlasher.Library/Exceptions/NoOperationPerformedException.cs new file mode 100644 index 00000000..3b273b00 --- /dev/null +++ b/nanoFirmwareFlasher.Library/Exceptions/NoOperationPerformedException.cs @@ -0,0 +1,49 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Runtime.Serialization; + +namespace nanoFramework.Tools.FirmwareFlasher +{ + /// + /// No operations was performed during the process from the manager. + /// + public class NoOperationPerformedException : Exception + { + /// + /// No operation performed exception. + /// + public NoOperationPerformedException() + { + } + + /// + /// No operation performed exception. + /// + /// Message to display. + public NoOperationPerformedException(string message) : base(message) + { + } + + /// + /// No operation performed exception. + /// + /// Message to display. + /// The exception to display. + public NoOperationPerformedException(string message, Exception innerException) : base(message, innerException) + { + } + + /// + /// No operation performed exception. + /// + /// Serialized information. + /// Streamed context. + protected NoOperationPerformedException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/nanoFirmwareFlasher.Library/Exceptions/ReadEsp32FlashException.cs b/nanoFirmwareFlasher.Library/Exceptions/ReadEsp32FlashException.cs index db402f11..2e878042 100644 --- a/nanoFirmwareFlasher.Library/Exceptions/ReadEsp32FlashException.cs +++ b/nanoFirmwareFlasher.Library/Exceptions/ReadEsp32FlashException.cs @@ -19,15 +19,29 @@ public class ReadEsp32FlashException : Exception /// public string ExecutionError; + /// + /// ESP32 tool flash exception. + /// + /// Message to display. public ReadEsp32FlashException(string message) : base(message) { ExecutionError = message; } + /// + /// ESP32 tool flash exception. + /// + /// Message to display. + /// The exception to display. public ReadEsp32FlashException(string message, Exception innerException) : base(message, innerException) { } + /// + /// ESP32 tool flash exception. + /// + /// Serialized information. + /// Streamed context. protected ReadEsp32FlashException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/nanoFirmwareFlasher.Library/Exceptions/SilinkExecutionException.cs b/nanoFirmwareFlasher.Library/Exceptions/SilinkExecutionException.cs new file mode 100644 index 00000000..b4d7479d --- /dev/null +++ b/nanoFirmwareFlasher.Library/Exceptions/SilinkExecutionException.cs @@ -0,0 +1,56 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Runtime.Serialization; + +namespace nanoFramework.Tools.FirmwareFlasher +{ + /// + /// Error executing SI Link command. + /// + [Serializable] + public class SilinkExecutionException : Exception + { + /// + /// Error message from SI Link. + /// + public string ExecutionError; + + /// + /// SI Link Exception. + /// + public SilinkExecutionException() + { + } + + /// + /// SI Link Exception. + /// + /// Message to display. + public SilinkExecutionException(string message) : base(message) + { + ExecutionError = message; + } + + /// + /// SI Link Exception. + /// + /// Message to display. + /// The exception to display. + public SilinkExecutionException(string message, Exception innerException) : base(message, innerException) + { + } + + /// + /// SI Link Exception. + /// + /// Serialized information. + /// Streamed context. + protected SilinkExecutionException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/nanoFirmwareFlasher.Library/Exceptions/StLinkCliExecutionException.cs b/nanoFirmwareFlasher.Library/Exceptions/StLinkCliExecutionException.cs index 80188d0c..0b10d315 100644 --- a/nanoFirmwareFlasher.Library/Exceptions/StLinkCliExecutionException.cs +++ b/nanoFirmwareFlasher.Library/Exceptions/StLinkCliExecutionException.cs @@ -19,19 +19,36 @@ public class StLinkCliExecutionException : Exception /// public string ExecutionError; + /// + /// STM32 Programmer CLI Exception. + /// public StLinkCliExecutionException() { } + /// + /// STM32 Programmer CLI Exception. + /// + /// Message to display. public StLinkCliExecutionException(string message) : base(message) { ExecutionError = message; } + /// + /// STM32 Programmer CLI Exception. + /// + /// Message to display. + /// The exception to display. public StLinkCliExecutionException(string message, Exception innerException) : base(message, innerException) { } + /// + /// STM32 Programmer CLI Exception. + /// + /// Serialized information. + /// Streamed context. protected StLinkCliExecutionException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/nanoFirmwareFlasher.Library/Exceptions/UniflashCliExecutionException.cs b/nanoFirmwareFlasher.Library/Exceptions/UniflashCliExecutionException.cs index 5fd714c6..e482c594 100644 --- a/nanoFirmwareFlasher.Library/Exceptions/UniflashCliExecutionException.cs +++ b/nanoFirmwareFlasher.Library/Exceptions/UniflashCliExecutionException.cs @@ -19,19 +19,36 @@ public class UniflashCliExecutionException : Exception /// public string ExecutionError; + /// + /// UniFlash CLI Execution Exception. + /// public UniflashCliExecutionException() { } + /// + /// UniFlash CLI Execution Exception. + /// + /// Message to display. public UniflashCliExecutionException(string message) : base(message) { ExecutionError = message; } + /// + /// UniFlash CLI Execution Exception. + /// + /// Message to display. + /// The exception to display. public UniflashCliExecutionException(string message, Exception innerException) : base(message, innerException) { } + /// + /// UniFlash CLI Execution Exception. + /// + /// Serialized information. + /// Streamed context. protected UniflashCliExecutionException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/nanoFirmwareFlasher.Library/Exceptions/WriteEsp32FlashException.cs b/nanoFirmwareFlasher.Library/Exceptions/WriteEsp32FlashException.cs index c2d1bb2c..48969a9c 100644 --- a/nanoFirmwareFlasher.Library/Exceptions/WriteEsp32FlashException.cs +++ b/nanoFirmwareFlasher.Library/Exceptions/WriteEsp32FlashException.cs @@ -19,15 +19,29 @@ public class WriteEsp32FlashException : Exception /// public string ExecutionError; + /// + /// Write the ESPP32 flash exception. + /// + /// Message to display. public WriteEsp32FlashException(string message) : base(message) { ExecutionError = message; } + /// + /// Write the ESPP32 flash exception. + /// + /// Message to display. + /// The exception to display. public WriteEsp32FlashException(string message, Exception innerException) : base(message, innerException) { } + /// + /// Write the ESPP32 flash exception. + /// + /// Serialized information. + /// Streamed context. protected WriteEsp32FlashException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/nanoFirmwareFlasher.Library/ExitCodes.cs b/nanoFirmwareFlasher.Library/ExitCodes.cs index 85bafe5f..e829983b 100644 --- a/nanoFirmwareFlasher.Library/ExitCodes.cs +++ b/nanoFirmwareFlasher.Library/ExitCodes.cs @@ -8,6 +8,9 @@ namespace nanoFramework.Tools.FirmwareFlasher { + /// + /// Exit Codes. + /// public enum ExitCodes { /// @@ -33,7 +36,7 @@ public enum ExitCodes E1002 = 1002, /// - /// Error flashing DFU dvice + /// Error flashing DFU device /// [Display(Name = "Error flashing DFU device.")] E1003 = 1003, @@ -53,7 +56,7 @@ public enum ExitCodes /// /// Failed to start execution on the connected device. /// - [Display(Name = "Failed to start execition on the connected device.")] + [Display(Name = "Failed to start execution on the connected device.")] E1006 = 1006, /////////////////////// @@ -78,6 +81,12 @@ public enum ExitCodes [Display(Name = "Error executing operation with nano device.")] E2002 = 2002, + /// + /// Error executing operation with nano device. + /// + [Display(Name = "Error executing file deployment on nano device.")] + E2003 = 2003, + //////////////////////// // ESP32 tools Errors // //////////////////////// @@ -315,7 +324,7 @@ public enum ExitCodes /// /// CLR image file has wrong format. It has to be a binary file. /// - [Display(Name = "CLR image file has wrong format.It has to be a binary file.")] + [Display(Name = "CLR image file has wrong format. It has to be a binary file.")] E9012 = 9012, /// diff --git a/nanoFirmwareFlasher.Library/FileDeployment/DeploymentFile.cs b/nanoFirmwareFlasher.Library/FileDeployment/DeploymentFile.cs new file mode 100644 index 00000000..e29b23ce --- /dev/null +++ b/nanoFirmwareFlasher.Library/FileDeployment/DeploymentFile.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace nanoFramework.Tools.FirmwareFlasher.FileDeployment +{ + internal class DeploymentFile + { + /// + /// Gets or sets the destination fully qualified file path including file name. + /// + public string DestinationFilePath { get; set; } + + /// + /// Gets or sets the source fully qualified file path including file name. + /// + public string SourceFilePath { get; set; } + } +} diff --git a/nanoFirmwareFlasher.Library/FileDeployment/FileDeploymentConfiguration.cs b/nanoFirmwareFlasher.Library/FileDeployment/FileDeploymentConfiguration.cs new file mode 100644 index 00000000..b2065634 --- /dev/null +++ b/nanoFirmwareFlasher.Library/FileDeployment/FileDeploymentConfiguration.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace nanoFramework.Tools.FirmwareFlasher.FileDeployment +{ + internal class FileDeploymentConfiguration + { + /// + /// Gets or sets the serial port to be used for the deployment. + /// + public string SerialPort { get; set; } + + /// + /// A list of files to deploy and/or delete. + /// + public List Files { get; set; } + } +} diff --git a/nanoFirmwareFlasher.Library/FileDeployment/FileDeploymentManager.cs b/nanoFirmwareFlasher.Library/FileDeployment/FileDeploymentManager.cs new file mode 100644 index 00000000..2936db3e --- /dev/null +++ b/nanoFirmwareFlasher.Library/FileDeployment/FileDeploymentManager.cs @@ -0,0 +1,255 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using nanoFramework.Tools.Debugger; +using nanoFramework.Tools.Debugger.Extensions; +using Newtonsoft.Json; +using System; +using System.IO; +using System.Threading.Tasks; + +namespace nanoFramework.Tools.FirmwareFlasher.FileDeployment +{ + /// + /// File Deployment Manager class. + /// + public class FileDeploymentManager + { + private readonly FileDeploymentConfiguration _configuration; + private readonly VerbosityLevel _verbosity; + private readonly string _serialPort; + + /// + /// Creates an instance of FileDeploymentManager. + /// + public FileDeploymentManager(string configFilePath, string originalPort, VerbosityLevel verbosity) + { + _configuration = JsonConvert.DeserializeObject(File.ReadAllText(configFilePath)); + _serialPort = string.IsNullOrEmpty(_configuration.SerialPort) ? originalPort : _configuration.SerialPort; + _verbosity = verbosity; + } + + /// + /// Deploys async the files. + /// + /// An ExitCode error. + public async Task DeployAsync() + { + // number of retries when performing a deploy operation + const int _numberOfRetries = 5; + // timeout when performing a deploy operation + const int _timeoutMiliseconds = 1000; + + NanoDeviceBase device = null; + PortBase serialDebugClient; + int retryCount = 0; + + serialDebugClient = PortBase.CreateInstanceForSerial(false); + + try + { + serialDebugClient.AddDevice(_serialPort); + + device = serialDebugClient.NanoFrameworkDevices[0]; + } + catch (Exception ex) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"Error connecting to nanoDevice on {_serialPort} to deploy files"); + Console.ForegroundColor = ConsoleColor.White; + return ExitCodes.E2000; + } + + // check if debugger engine exists + if (device.DebugEngine == null) + { + device.CreateDebugEngine(); + if (_verbosity >= VerbosityLevel.Normal) + { + Console.WriteLine($"Debug engine created."); + } + } + + bool deviceIsInInitializeState = false; + + retryDebug: + bool connectResult = device.DebugEngine.Connect(5000, true, true); + if (retryCount == 0 && _verbosity >= VerbosityLevel.Normal) + { + Console.WriteLine($"Device connected and ready for file deployment."); + } + else if (_verbosity >= VerbosityLevel.Normal) + { + Console.WriteLine($"Device connect result is {connectResult}. Attempt {retryCount}/{_numberOfRetries}"); + } + + if (!connectResult) + { + if (retryCount < _numberOfRetries) + { + // Give it a bit of time + await Task.Delay(100); + retryCount++; + + goto retryDebug; + } + else + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"Error connecting to debug engine on nanoDevice on {_serialPort} to deploy files"); + Console.ForegroundColor = ConsoleColor.White; + return ExitCodes.E2000; + } + } + + retryCount = 0; + + // initial check + if (device.DebugEngine.IsDeviceInInitializeState()) + { + if (_verbosity >= VerbosityLevel.Normal) + { + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine($"Device status verified as being in initialized state. Requesting to resume execution. Attempt {retryCount}/{_numberOfRetries}."); + Console.ForegroundColor = ConsoleColor.White; + } + + // set flag + deviceIsInInitializeState = true; + + // device is still in initialization state, try resume execution + device.DebugEngine.ResumeExecution(); + } + + // handle the workflow required to try resuming the execution on the device + // only required if device is not already there + // retry 5 times with a 500ms interval between retries + while (retryCount++ < _numberOfRetries && deviceIsInInitializeState) + { + if (!device.DebugEngine.IsDeviceInInitializeState()) + { + if (_verbosity >= VerbosityLevel.Diagnostic) + { + Console.WriteLine($"Device has completed initialization."); + } + + // done here + deviceIsInInitializeState = false; + break; + } + + if (_verbosity >= VerbosityLevel.Diagnostic) + { + Console.WriteLine($"Waiting for device to report initialization completed ({retryCount}/{_numberOfRetries})."); + } + + // provide feedback to user on the 1st pass + if (retryCount == 0) + { + if (_verbosity >= VerbosityLevel.Diagnostic) + { + Console.WriteLine($"Waiting for device to initialize."); + } + } + + if (device.DebugEngine.IsConnectedTonanoBooter) + { + if (_verbosity >= VerbosityLevel.Diagnostic) + { + Console.WriteLine($"Device reported running nanoBooter. Requesting to load nanoCLR."); + } + + // request nanoBooter to load CLR + device.DebugEngine.ExecuteMemory(0); + } + else if (device.DebugEngine.IsConnectedTonanoCLR) + { + if (_verbosity >= VerbosityLevel.Normal) + { + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine($"Device reported running nanoCLR. Requesting to reboot nanoCLR."); + Console.ForegroundColor = ConsoleColor.White; + } + + await Task.Run(delegate + { + // already running nanoCLR try rebooting the CLR + device.DebugEngine.RebootDevice(RebootOptions.ClrOnly); + }); + } + + // wait before next pass + // use a back-off strategy of increasing the wait time to accommodate slower or less responsive targets (such as networked ones) + await Task.Delay(TimeSpan.FromMilliseconds(_timeoutMiliseconds * (retryCount + 1))); + + await Task.Yield(); + } + + // check if device is still in initialized state + if (!deviceIsInInitializeState) + { + // Deploy each file + foreach (var file in _configuration.Files) + { + try + { + if (string.IsNullOrEmpty(file.SourceFilePath)) + { + // deleting + Console.Write($"Deleting file {file.DestinationFilePath}..."); + if (device.DebugEngine.DeleteStorageFile(file.DestinationFilePath) != Debugger.WireProtocol.StorageOperationErrorCode.NoError) + { + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine(); + Console.WriteLine($"Error deleting file {file.DestinationFilePath}, it may not exist on the storage."); + Console.ForegroundColor = ConsoleColor.White; + } + else + { + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine($"OK"); + Console.ForegroundColor = ConsoleColor.White; + } + } + else + { + Console.Write($"Deploying file {file.SourceFilePath} to {file.DestinationFilePath}..."); + var ret = device.DebugEngine.AddStorageFile(file.DestinationFilePath, File.ReadAllBytes(file.SourceFilePath)); + if (ret != Debugger.WireProtocol.StorageOperationErrorCode.NoError) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(); + Console.WriteLine($"Error deploying content file {file.SourceFilePath} to {file.DestinationFilePath}"); + Console.ForegroundColor = ConsoleColor.White; + } + else + { + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine($"OK"); + Console.ForegroundColor = ConsoleColor.White; + } + } + } + catch + { + if (_verbosity >= VerbosityLevel.Normal) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(); + Console.WriteLine($"Exception deploying content file {file.SourceFilePath} to {file.DestinationFilePath}"); + Console.ForegroundColor = ConsoleColor.White; + } + } + } + } + else + { + return ExitCodes.E2002; + } + + return ExitCodes.OK; + } + } +} diff --git a/nanoFirmwareFlasher.Library/FirmwarePackage.cs b/nanoFirmwareFlasher.Library/FirmwarePackage.cs index 24119bd4..8f1bf341 100644 --- a/nanoFirmwareFlasher.Library/FirmwarePackage.cs +++ b/nanoFirmwareFlasher.Library/FirmwarePackage.cs @@ -3,8 +3,10 @@ // See LICENSE file in the project root for full license information. // +using Microsoft.ApplicationInsights; +using Microsoft.ApplicationInsights.DataContracts; +using Microsoft.Extensions.Configuration; using nanoFramework.Tools.Debugger; -using nanoFramework.Tools.FirmwareFlasher; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -12,6 +14,7 @@ using System.IO.Compression; using System.Linq; using System.Net.Http; +using System.Reflection; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -31,7 +34,6 @@ public abstract class FirmwarePackage : IDisposable private readonly string _targetName; private readonly bool _preview; - private const string _readmeContent = "This folder contains nanoFramework firmware files. Can safely be removed."; /// @@ -58,6 +60,9 @@ public static string LocationPathBase /// public string Version { get; internal set; } + /// + /// The verbosity level. + /// public VerbosityLevel Verbosity { get; internal set; } /// @@ -94,10 +99,14 @@ public static string LocationPathBase /// public object BooterStartAddress { get; internal set; } + + static FirmwarePackage() { - _cloudsmithClient = new HttpClient(); - _cloudsmithClient.BaseAddress = new Uri("https://api.cloudsmith.io/v1/packages/net-nanoframework/"); + _cloudsmithClient = new HttpClient + { + BaseAddress = new Uri("https://api.cloudsmith.io/v1/packages/net-nanoframework/") + }; _cloudsmithClient.DefaultRequestHeaders.Add("Accept", "*/*"); } @@ -116,6 +125,8 @@ protected FirmwarePackage(NanoDeviceBase nanoDevice) /// Constructor /// /// Target name as designated in the repositories. + /// The firmware version. + /// Whether to use preview versions. protected FirmwarePackage( string targetName, string fwVersion, @@ -147,7 +158,7 @@ public static List GetTargetList( // Because new stable releases are published on a regular basis and preview very rarely, we query for stable versions published in past month and preview versions published during the past 6 months. string requestUri = $"{repoName}/?page_size=500&q=uploaded:'>{(preview ? "6" : "1")} month ago' {(platform.HasValue ? "AND tag:" + platform.Value : "")}"; - List targetPackages = new(); + List targetPackages = []; if (verbosity > VerbosityLevel.Normal) { @@ -235,7 +246,7 @@ internal async Task DownloadAndExtractAsync() return ExitCodes.E9006; } - List fwFiles = new(); + List fwFiles = []; if (_preview) { @@ -282,6 +293,7 @@ internal async Task DownloadAndExtractAsync() filesToDelete.AddRange(Directory.EnumerateFiles(LocationPath, "*.hex").ToList()); filesToDelete.AddRange(Directory.EnumerateFiles(LocationPath, "*.s19").ToList()); filesToDelete.AddRange(Directory.EnumerateFiles(LocationPath, "*.dfu").ToList()); + filesToDelete.AddRange(Directory.EnumerateFiles(LocationPath, "*.csv").ToList()); foreach (var file in filesToDelete) { @@ -336,6 +348,32 @@ internal async Task DownloadAndExtractAsync() } stepSuccessful = true; + + // send telemetry data on successful download + if (NanoTelemetryClient.TelemetryClient is not null) + { + AssemblyInformationalVersionAttribute nanoffVersion = null; + + try + { + nanoffVersion = Attribute.GetCustomAttribute( + Assembly.GetEntryAssembly()!, + typeof(AssemblyInformationalVersionAttribute)) + as AssemblyInformationalVersionAttribute; + } + catch + { + // OK to fail here, just telemetry + } + + var packageTelemetry = new EventTelemetry("PackageDownloaded"); + packageTelemetry.Properties.Add("TargetName", _targetName); + packageTelemetry.Properties.Add("Version", Version); + packageTelemetry.Properties.Add("nanoffVersion", nanoffVersion == null ? "unknown" : nanoffVersion.InformationalVersion); + + NanoTelemetryClient.TelemetryClient.TrackEvent(packageTelemetry); + NanoTelemetryClient.TelemetryClient.Flush(); + } } catch { @@ -637,6 +675,7 @@ private static async Task GetDownloadUrlAsync( private bool _disposedValue = false; // To detect redundant calls + /// protected void Dispose(bool disposing) { if (!_disposedValue) diff --git a/nanoFirmwareFlasher.Library/FirmwarePackageFactory.cs b/nanoFirmwareFlasher.Library/FirmwarePackageFactory.cs index 8eb9cef2..cf4d6b82 100644 --- a/nanoFirmwareFlasher.Library/FirmwarePackageFactory.cs +++ b/nanoFirmwareFlasher.Library/FirmwarePackageFactory.cs @@ -8,8 +8,18 @@ namespace nanoFramework.Tools.FirmwareFlasher { + /// + /// Firmware Package Factory. + /// public class FirmwarePackageFactory { + /// + /// Gets the firmware package for the device. + /// + /// The device. + /// The firmware Version. + /// The Firmware package. + /// The command is not supported. public static FirmwarePackage GetFirmwarePackage( NanoDeviceBase nanoDevice, string fwVersion) diff --git a/nanoFirmwareFlasher.Library/IManager.cs b/nanoFirmwareFlasher.Library/IManager.cs new file mode 100644 index 00000000..2942eec9 --- /dev/null +++ b/nanoFirmwareFlasher.Library/IManager.cs @@ -0,0 +1,21 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using System.Threading.Tasks; + +namespace nanoFramework.Tools.FirmwareFlasher +{ + /// + /// Interface for processing platform-specific operations. + /// + public interface IManager + { + /// + /// Process operations depending on options provided. + /// + /// Return an value. + Task ProcessAsync(); + } +} diff --git a/nanoFirmwareFlasher.Library/JLinkCli.cs b/nanoFirmwareFlasher.Library/JLinkCli.cs index 3621424e..c483d13d 100644 --- a/nanoFirmwareFlasher.Library/JLinkCli.cs +++ b/nanoFirmwareFlasher.Library/JLinkCli.cs @@ -179,34 +179,9 @@ public ExitCodes ExecuteFlashBinFiles( } } - List shadowFiles = new List(); + List shadowFiles = []; - // J-Link can't handle diacritc chars - // developer note: reported to Segger (Case: 60276735) and can be removed if this is fixed/improved - foreach (string binFile in files) - { - if (!binFile.IsNormalized(NormalizationForm.FormD) - || binFile.Contains(' ')) - { - var tempFile = Path.Combine( - Environment.GetEnvironmentVariable("TEMP", EnvironmentVariableTarget.Machine), - Path.GetFileName(binFile)); - - // copy file to shadow file - File.Copy( - binFile, - tempFile, - true); - - shadowFiles.Add(tempFile); - } - else - { - // copy file to shadow list - shadowFiles.Add(binFile); - } - - } + ProcessFilePaths(files, shadowFiles); // erase flash if (DoMassErase) @@ -222,12 +197,12 @@ public ExitCodes ExecuteFlashBinFiles( DoMassErase = false; } - if (Verbosity < VerbosityLevel.Normal) + if (Verbosity == VerbosityLevel.Normal) { Console.ForegroundColor = ConsoleColor.White; Console.Write("Flashing device..."); } - else + else if (Verbosity >= VerbosityLevel.Detailed) { Console.ForegroundColor = ConsoleColor.White; Console.WriteLine("Flashing device..."); @@ -235,21 +210,18 @@ public ExitCodes ExecuteFlashBinFiles( // program BIN file(s) int index = 0; + bool warningPromptShown = false; + foreach (string binFile in shadowFiles) { - // make sure path is absolute - var binFilePath = Utilities.MakePathAbsolute( - Environment.CurrentDirectory, - binFile); - if (Verbosity > VerbosityLevel.Normal) { Console.ForegroundColor = ConsoleColor.Cyan; - Console.WriteLine($"{Path.GetFileName(binFilePath)} @ {addresses.ElementAt(index)}"); + Console.WriteLine($"{Path.GetFileName(binFile)} @ {addresses.ElementAt(index)}"); } // compose JLink command file - var jlinkCmdContent = FlashSingleFileCommandTemplate.Replace(FilePathToken, binFilePath).Replace(FlashAddressToken, addresses.ElementAt(index++)); + var jlinkCmdContent = FlashSingleFileCommandTemplate.Replace(FilePathToken, binFile).Replace(FlashAddressToken, addresses.ElementAt(index++)); var jlinkCmdFilePath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.jlink"); // create file @@ -265,8 +237,15 @@ public ExitCodes ExecuteFlashBinFiles( if (Verbosity >= VerbosityLevel.Normal && cliOutput.Contains("Skipped. Contents already match")) { + warningPromptShown = true; + Console.ForegroundColor = ConsoleColor.Yellow; + if (Verbosity == VerbosityLevel.Normal) + { + Console.WriteLine(); + } + Console.WriteLine(""); Console.WriteLine("******************* WARNING *********************"); Console.WriteLine("Skip flashing. Contents already match the update."); @@ -286,12 +265,15 @@ public ExitCodes ExecuteFlashBinFiles( ShowCLIOutput(cliOutput); } - if (Verbosity < VerbosityLevel.Normal) + if (Verbosity == VerbosityLevel.Normal) { - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine(" OK"); + if (!warningPromptShown) + { + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine(" OK"); + } } - else + else if (Verbosity >= VerbosityLevel.Detailed) { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Flashing completed..."); @@ -302,38 +284,27 @@ public ExitCodes ExecuteFlashBinFiles( return ExitCodes.OK; } - /// - /// Executes an operation that flashes a collection of Intel HEX format files to a connected J-Link device. - /// - /// List of files to flash to the device. - /// ID of the J-Link device to execute the operation into. Leave to use the default address. - /// - public ExitCodes ExecuteFlashHexFiles( - IList files, - string probeId) + private void ProcessFilePaths(IList files, List shadowFiles) { - // check file existence - if (files.Any(f => !File.Exists(f))) - { - return ExitCodes.E5004; - } - - List shadowFiles = new List(); - // J-Link can't handle diacritc chars // developer note: reported to Segger (Case: 60276735) and can be removed if this is fixed/improved - foreach (string hexFile in files) + foreach (string binFile in files) { - if (!hexFile.IsNormalized(NormalizationForm.FormD) || - hexFile.Contains(' ')) + // make sure path is absolute + var binFilePath = Utilities.MakePathAbsolute( + Environment.CurrentDirectory, + binFile); + + if (!binFilePath.IsNormalized(NormalizationForm.FormD) + || binFilePath.Contains(' ')) { var tempFile = Path.Combine( Environment.GetEnvironmentVariable("TEMP", EnvironmentVariableTarget.Machine), - Path.GetFileName(hexFile)); + Path.GetFileName(binFilePath)); // copy file to shadow file File.Copy( - hexFile, + binFilePath, tempFile, true); @@ -342,10 +313,32 @@ public ExitCodes ExecuteFlashHexFiles( else { // copy file to shadow list - shadowFiles.Add(hexFile); + shadowFiles.Add(binFile); } + + } + } + + /// + /// Executes an operation that flashes a collection of Intel HEX format files to a connected J-Link device. + /// + /// List of files to flash to the device. + /// ID of the J-Link device to execute the operation into. Leave to use the default address. + /// + public ExitCodes ExecuteFlashHexFiles( + IList files, + string probeId) + { + // check file existence + if (files.Any(f => !File.Exists(f))) + { + return ExitCodes.E5004; } + List shadowFiles = []; + + ProcessFilePaths(files, shadowFiles); + // erase flash if (DoMassErase) { @@ -360,12 +353,12 @@ public ExitCodes ExecuteFlashHexFiles( DoMassErase = false; } - if (Verbosity < VerbosityLevel.Normal) + if (Verbosity == VerbosityLevel.Normal) { Console.ForegroundColor = ConsoleColor.White; Console.Write("Flashing device..."); } - else + else if (Verbosity >= VerbosityLevel.Detailed) { Console.ForegroundColor = ConsoleColor.White; Console.WriteLine("Flashing device..."); @@ -376,18 +369,13 @@ public ExitCodes ExecuteFlashHexFiles( foreach (string hexFile in shadowFiles) { - // make sure path is absolute - var hexFilePath = Utilities.MakePathAbsolute( - Environment.CurrentDirectory, - hexFile); - if (Verbosity > VerbosityLevel.Normal) { Console.ForegroundColor = ConsoleColor.Cyan; - Console.WriteLine($"{Path.GetFileName(hexFilePath)}"); + Console.WriteLine($"{Path.GetFileName(hexFile)}"); } - listOfFiles.AppendLine($"LoadFile {hexFilePath}"); + listOfFiles.AppendLine($"LoadFile {hexFile}"); } // compose JLink command file @@ -409,11 +397,20 @@ public ExitCodes ExecuteFlashHexFiles( // OK to delete the JLink command file File.Delete(jlinkCmdFilePath); + bool warningPromptShown = false; + if (Verbosity >= VerbosityLevel.Normal && cliOutput.Contains("Skipped. Contents already match")) { + warningPromptShown = true; + Console.ForegroundColor = ConsoleColor.Yellow; + if (Verbosity == VerbosityLevel.Normal) + { + Console.WriteLine(); + } + Console.WriteLine(""); Console.WriteLine("******************* WARNING *********************"); Console.WriteLine("Skip flashing. Contents already match the update."); @@ -432,12 +429,15 @@ public ExitCodes ExecuteFlashHexFiles( ShowCLIOutput(cliOutput); - if (Verbosity < VerbosityLevel.Normal) + if (Verbosity == VerbosityLevel.Normal) { - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine(" OK"); + if (!warningPromptShown) + { + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine(" OK"); + } } - else + else if (Verbosity >= VerbosityLevel.Detailed) { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Flashing completed..."); diff --git a/nanoFirmwareFlasher.Library/JLinkDevice.cs b/nanoFirmwareFlasher.Library/JLinkDevice.cs index 5cf4dfee..35d26f25 100644 --- a/nanoFirmwareFlasher.Library/JLinkDevice.cs +++ b/nanoFirmwareFlasher.Library/JLinkDevice.cs @@ -151,7 +151,7 @@ public static List ListDevices() if (jlinkMatches.Count == 0) { // no J-Link probe found - return new List(); + return []; } return jlinkMatches.Cast().Select(i => i.Value).ToList(); diff --git a/nanoFirmwareFlasher.Library/JLinkOperations.cs b/nanoFirmwareFlasher.Library/JLinkOperations.cs index c71f5da5..eba76718 100644 --- a/nanoFirmwareFlasher.Library/JLinkOperations.cs +++ b/nanoFirmwareFlasher.Library/JLinkOperations.cs @@ -69,7 +69,7 @@ public static async System.Threading.Tasks.Task UpdateFirmwareAsync( } // setup files to flash - var filesToFlash = new List(); + List filesToFlash = []; if (updateFw) { @@ -153,7 +153,7 @@ public static async System.Threading.Tasks.Task UpdateFirmwareAsync( jlinkDevice.Verbosity = verbosity; // write HEX files to flash - if (filesToFlash.Any(f => f.EndsWith(".hex"))) + if (filesToFlash.Exists(f => f.EndsWith(".hex"))) { operationResult = jlinkDevice.FlashHexFiles(filesToFlash); } @@ -161,18 +161,24 @@ public static async System.Threading.Tasks.Task UpdateFirmwareAsync( if (operationResult == ExitCodes.OK && isApplicationBinFile) { // now program the application file - operationResult = jlinkDevice.FlashBinFiles(new[] { applicationPath }, new[] { deploymentAddress }); + operationResult = jlinkDevice.FlashBinFiles([applicationPath], [deploymentAddress]); } return operationResult; } + /// + /// Mass erase device. + /// + /// The probe ID. + /// The verbosity level. + /// public static ExitCodes MassErase( string probeId, VerbosityLevel verbosity) { // J-Link device - JLinkDevice jlinkDevice = new JLinkDevice(probeId); + JLinkDevice jlinkDevice = new(probeId); if (!jlinkDevice.DevicePresent) { diff --git a/nanoFirmwareFlasher.Library/NanoDeviceOperations.cs b/nanoFirmwareFlasher.Library/NanoDeviceOperations.cs index 5b75a754..b94a009f 100644 --- a/nanoFirmwareFlasher.Library/NanoDeviceOperations.cs +++ b/nanoFirmwareFlasher.Library/NanoDeviceOperations.cs @@ -1,4 +1,4 @@ -//// +//// // Copyright (c) .NET Foundation and Contributors // See LICENSE file in the project root for full license information. //// @@ -54,7 +54,7 @@ public ObservableCollection ListDevices(bool getDeviceDetails) { _ = device.DebugEngine.Connect( false, - true); + false); // check that we are in CLR if (device.DebugEngine.IsConnectedTonanoCLR) @@ -82,6 +82,8 @@ public ObservableCollection ListDevices(bool getDeviceDetails) // no need to report this, just move on } } + + device.Disconnect(true); } } @@ -161,13 +163,6 @@ public ExitCodes GetDeviceDetails( // report issue throw new CantConnectToNanoDeviceException("Couldn't connect to specified nano device."); } - - if (nanoDevice is null) - { - throw new ArgumentNullException(nameof(nanoDevice)); - } - - return ExitCodes.E2000; } /// @@ -286,7 +281,7 @@ public async Task UpdateDeviceClrAsync( } } - bool attemptToLaunchBooter = false; + bool booterLaunched = false; if (nanoDevice.DebugEngine.IsConnectedTonanoCLR) { @@ -304,9 +299,9 @@ public async Task UpdateDeviceClrAsync( Console.ForegroundColor = ConsoleColor.White; } - attemptToLaunchBooter = nanoDevice.ConnectToNanoBooter(); + booterLaunched = nanoDevice.ConnectToNanoBooter(); - if (!attemptToLaunchBooter) + if (!booterLaunched) { // check for version where the software reboot to nanoBooter was made available if (currentClrVersion != null && @@ -332,10 +327,10 @@ public async Task UpdateDeviceClrAsync( } else { - attemptToLaunchBooter = true; + booterLaunched = true; } - if (attemptToLaunchBooter && + if (booterLaunched && nanoDevice.Ping() == Debugger.WireProtocol.ConnectionSource.nanoBooter) { // get address for CLR block expected by device @@ -378,7 +373,7 @@ public async Task UpdateDeviceClrAsync( } } - if (attemptToLaunchBooter) + if (booterLaunched) { // try to reboot target if (verbosity >= VerbosityLevel.Normal) @@ -406,7 +401,7 @@ public async Task UpdateDeviceClrAsync( } else { - if (attemptToLaunchBooter) + if (!booterLaunched) { // only report this as an error if the launch was successful throw new NanoDeviceOperationFailedException("Failed to launch nanoBooter. Quitting update."); diff --git a/nanoFirmwareFlasher.Library/NanoTelemetryClient.cs b/nanoFirmwareFlasher.Library/NanoTelemetryClient.cs new file mode 100644 index 00000000..0763af7a --- /dev/null +++ b/nanoFirmwareFlasher.Library/NanoTelemetryClient.cs @@ -0,0 +1,61 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using Microsoft.ApplicationInsights; +using System; + +namespace nanoFramework.Tools.FirmwareFlasher +{ + /// + /// Telemetry client for sending telemetry data to Application Insights. + /// + public class NanoTelemetryClient + { + private static TelemetryClient _myTelemetryClient; + + // flag to signal that the telemetry client field initialized has been processed + private static bool notProcessed = true; + + /// + /// Connection string for . + /// + public static string ConnectionString; + + /// + /// Gets the to use for sending telemetry data. + /// + public static TelemetryClient TelemetryClient => GetTelemetryClient(); + + private static TelemetryClient GetTelemetryClient() + { + if (notProcessed && _myTelemetryClient is null) + { + string optOutTelemetry = Environment.GetEnvironmentVariable("NANOFRAMEWORK_TELEMETRY_OPTOUT"); + + if (!string.IsNullOrEmpty(ConnectionString) + && (optOutTelemetry is null || optOutTelemetry != "1")) + { + // parsing the connection string could fail + try + { + _myTelemetryClient = new TelemetryClient(new Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration() + { + ConnectionString = ConnectionString + }); + } + catch + { + // don't care, telemetry is not mandatory + }; + } + + // set flag to false to signal that the telemetry client field has been processed + notProcessed = false; + } + + return _myTelemetryClient; + } + } +} diff --git a/nanoFirmwareFlasher.Library/PartitionTableSize.cs b/nanoFirmwareFlasher.Library/PartitionTableSize.cs index 230c11c5..a89b5e11 100644 --- a/nanoFirmwareFlasher.Library/PartitionTableSize.cs +++ b/nanoFirmwareFlasher.Library/PartitionTableSize.cs @@ -5,11 +5,26 @@ namespace nanoFramework.Tools.FirmwareFlasher { + /// + /// The partition table size. + /// public enum PartitionTableSize { + /// + /// Two. + /// _2 = 2, + /// + /// Four. + /// _4 = 4, + /// + /// Eight. + /// _8 = 8, - _16 = 16, + /// + /// Sixteen. + /// + _16 = 16 } } diff --git a/nanoFirmwareFlasher.Library/SilinkCli.cs b/nanoFirmwareFlasher.Library/SilinkCli.cs index 1f93f6cc..3a2c8bda 100644 --- a/nanoFirmwareFlasher.Library/SilinkCli.cs +++ b/nanoFirmwareFlasher.Library/SilinkCli.cs @@ -15,6 +15,9 @@ namespace nanoFramework.Tools.FirmwareFlasher { + /// + /// SI Link CLI. + /// public class SilinkCli { private const int SilinkTelnetPort = 49000; diff --git a/nanoFirmwareFlasher.Library/Stm32Operations.cs b/nanoFirmwareFlasher.Library/Stm32Operations.cs index 6a27d5c6..32583aee 100644 --- a/nanoFirmwareFlasher.Library/Stm32Operations.cs +++ b/nanoFirmwareFlasher.Library/Stm32Operations.cs @@ -12,8 +12,26 @@ namespace nanoFramework.Tools.FirmwareFlasher { + /// + /// Runs STM32 specific operations. + /// public class Stm32Operations { + /// + /// Updates the device firmware. + /// + /// The name of the target. + /// The firmware version to send. + /// Whether preview packages should be used. + /// Update firmware to latest version. + /// Path to the directory where the files are located. + /// The start memory address. + /// The DFU device ID. + /// The JTAG ID. + /// Checks whether the firmware will fit. + /// The connection interface. + /// The verbosity level to use. + /// The outcome. public static async System.Threading.Tasks.Task UpdateFirmwareAsync( string targetName, string fwVersion, @@ -197,7 +215,7 @@ public static async System.Threading.Tasks.Task UpdateFirmwareAsync( if (operationResult == ExitCodes.OK && isApplicationBinFile) { // now program the application file - operationResult = dfuDevice.FlashBinFiles(new[] { applicationPath }, new[] { deploymentAddress }); + operationResult = dfuDevice.FlashBinFiles([applicationPath], [deploymentAddress]); } if ( @@ -264,7 +282,7 @@ public static async System.Threading.Tasks.Task UpdateFirmwareAsync( if (operationResult == ExitCodes.OK && isApplicationBinFile) { // now program the application file - operationResult = jtagDevice.FlashBinFiles(new[] { applicationPath }, new[] { deploymentAddress }); + operationResult = jtagDevice.FlashBinFiles([applicationPath], [deploymentAddress]); } if ( @@ -323,6 +341,12 @@ private static void PerformTargetCheck(string target, StmJtagDevice jtagDevice) } } + /// + /// Resets the device. + /// + /// the JTAG ID. + /// The verbosity level. + /// The outcome. public static ExitCodes ResetMcu( string jtagId, VerbosityLevel verbosity) @@ -350,6 +374,12 @@ public static ExitCodes ResetMcu( return jtagDevice.ResetMcu(); } + /// + /// Erases the device flash memory. + /// + /// The ID of the JTAG interface. + /// The verbosity level. + /// The outcome. public static ExitCodes MassErase( string jtagId, VerbosityLevel verbosity) @@ -377,6 +407,12 @@ public static ExitCodes MassErase( return jtagDevice.MassErase(); } + /// + /// Installs DFU driver. + /// + /// The verbosity level to display. + /// The installation result. + /// The installation failed. Use verbose output to see why. public static ExitCodes InstallDfuDrivers(VerbosityLevel verbosityLevel) { if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) @@ -490,6 +526,12 @@ public static ExitCodes InstallDfuDrivers(VerbosityLevel verbosityLevel) } } + /// + /// Installs the JTAG drivers. + /// + /// Message verbosity level. + /// Installation result. + /// The installation failed. Use verbose output to see why. public static ExitCodes InstallJtagDrivers(VerbosityLevel verbosityLevel) { if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) @@ -567,12 +609,22 @@ public static ExitCodes InstallJtagDrivers(VerbosityLevel verbosityLevel) } + /// + /// The device connection interface. + /// public enum Interface { + /// + /// None. + /// None = 0, - + /// + /// JTAG. + /// Jtag, - + /// + /// DFU. + /// Dfu } } diff --git a/nanoFirmwareFlasher.Library/StmDeviceBase.cs b/nanoFirmwareFlasher.Library/StmDeviceBase.cs index 0b825ff4..47d3e331 100644 --- a/nanoFirmwareFlasher.Library/StmDeviceBase.cs +++ b/nanoFirmwareFlasher.Library/StmDeviceBase.cs @@ -35,6 +35,12 @@ public abstract class StmDeviceBase /// public VerbosityLevel Verbosity { get; set; } = VerbosityLevel.Normal; + /// + /// Runs the STM32 programmer CLI. + /// + /// arguments to send. + /// The returned message. + /// public static string RunSTM32ProgrammerCLI(string arguments) { try @@ -115,6 +121,11 @@ public static string RunSTM32ProgrammerCLI(string arguments) } } + /// + /// Gets the Error Message From STM32CLI. + /// + /// The retrived input. + /// The outcome. public static string GetErrorMessageFromSTM32CLI(string cliOutput) { var regEx = new Regex(@"Error: (?.+).", RegexOptions.IgnoreCase); @@ -137,6 +148,10 @@ public static string GetErrorMessageFromSTM32CLI(string cliOutput) return ""; } + /// + /// Output to CLI. + /// + /// Message to display. public void ShowCLIOutput(string cliOutput) { // show CLI output, if verbosity is diagnostic @@ -159,11 +174,20 @@ public void ShowCLIOutput(string cliOutput) } } + /// + /// Lists all found devices. + /// + /// public static string ExecuteListDevices() { return RunSTM32ProgrammerCLI("--list"); } + /// + /// Wipes the whole flash memory of the device. + /// + /// The device connection details. + /// The outcome. public ExitCodes ExecuteMassErase(string connectDetails) { if (Verbosity >= VerbosityLevel.Normal) @@ -198,6 +222,12 @@ public ExitCodes ExecuteMassErase(string connectDetails) return ExitCodes.OK; } + /// + /// Flash HEX files to device. + /// + /// The HEX files to flash. + /// The device connection details. + /// The outcome. public ExitCodes ExecuteFlashHexFiles( IList files, string connectDetails) @@ -273,6 +303,13 @@ public ExitCodes ExecuteFlashHexFiles( return ExitCodes.OK; } + /// + /// Flash BIN files to device. + /// + /// The files to flash. + /// The memory locations. + /// The device connection details. + /// The outcome. public ExitCodes ExecuteFlashBinFiles( IList files, IList addresses, @@ -355,7 +392,7 @@ public ExitCodes ExecuteFlashBinFiles( var cliOutput = RunSTM32ProgrammerCLI($"-c {connectDetails} mode=UR -w \"{binFilePath}\" {addresses.ElementAt(index++)}"); - if (!cliOutput.Contains("Programming Complete.")) + if (!cliOutput.Contains("File download complete")) { ShowCLIOutput(cliOutput); diff --git a/nanoFirmwareFlasher.Library/StmDfuDevice.cs b/nanoFirmwareFlasher.Library/StmDfuDevice.cs index 3155dfde..c1a803cb 100644 --- a/nanoFirmwareFlasher.Library/StmDfuDevice.cs +++ b/nanoFirmwareFlasher.Library/StmDfuDevice.cs @@ -11,6 +11,9 @@ namespace nanoFramework.Tools.FirmwareFlasher { + /// + /// STM32 DFU Device. + /// public class StmDfuDevice : StmDeviceBase { // Device ID of the connected DFU device. @@ -219,7 +222,7 @@ public ExitCodes StartExecution(string startAddress) if (dfuMatches.Count == 0) { // no DFU device found - return new List<(string serial, string device)>(); + return []; } return dfuMatches.Cast().Select(i => (serial: i.Groups["serial"].Value, device: i.Groups["device"].Value)).ToList(); diff --git a/nanoFirmwareFlasher.Library/StmJtagDevice.cs b/nanoFirmwareFlasher.Library/StmJtagDevice.cs index f8831811..e43bba35 100644 --- a/nanoFirmwareFlasher.Library/StmJtagDevice.cs +++ b/nanoFirmwareFlasher.Library/StmJtagDevice.cs @@ -11,6 +11,9 @@ namespace nanoFramework.Tools.FirmwareFlasher { + /// + /// STM32 JTAG Device. + /// public class StmJtagDevice : StmDeviceBase { /// @@ -205,7 +208,7 @@ public static List ListDevices() if (jtagMatches.Count == 0) { // no JTAG found - return new List(); + return []; } return jtagMatches.Cast().Select(i => i.Value).ToList(); diff --git a/nanoFirmwareFlasher.Library/SupportedPlatform.cs b/nanoFirmwareFlasher.Library/SupportedPlatform.cs index a57bb309..17d25537 100644 --- a/nanoFirmwareFlasher.Library/SupportedPlatform.cs +++ b/nanoFirmwareFlasher.Library/SupportedPlatform.cs @@ -5,11 +5,26 @@ namespace nanoFramework.Tools.FirmwareFlasher { + /// + /// Supported Platform. + /// public enum SupportedPlatform { + /// + /// ESP32. + /// esp32 = 0, + /// + /// STM32. + /// stm32 = 1, + /// + /// TI Simplelink. + /// ti_simplelink = 2, + /// + /// Silabs GG11. + /// gg11 } } diff --git a/nanoFirmwareFlasher.Library/VerbosityLevel.cs b/nanoFirmwareFlasher.Library/VerbosityLevel.cs index bb5839bb..d8260e06 100644 --- a/nanoFirmwareFlasher.Library/VerbosityLevel.cs +++ b/nanoFirmwareFlasher.Library/VerbosityLevel.cs @@ -5,12 +5,30 @@ namespace nanoFramework.Tools.FirmwareFlasher { + /// + /// Verbosity Level. + /// public enum VerbosityLevel { + /// + /// Quiet. + /// Quiet = 0, + /// + /// Minimal. + /// Minimal = 1, + /// + /// Normal. + /// Normal = 2, + /// + /// Detailed. + /// Detailed = 3, + /// + /// Diagnostic. + /// Diagnostic = 4 } } diff --git a/nanoFirmwareFlasher.Library/nanoFirmwareFlasher.Library.csproj b/nanoFirmwareFlasher.Library/nanoFirmwareFlasher.Library.csproj index 57b303bc..0b474a2f 100644 --- a/nanoFirmwareFlasher.Library/nanoFirmwareFlasher.Library.csproj +++ b/nanoFirmwareFlasher.Library/nanoFirmwareFlasher.Library.csproj @@ -2,7 +2,7 @@ library - net6.0;net472 + net8.0;net472 AnyCPU latest nanoFramework.Tools.FirmwareFlasher @@ -29,6 +29,7 @@ True true + true @@ -58,7 +59,10 @@ - + + + + @@ -93,6 +97,8 @@ + + @@ -124,5 +130,34 @@ + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + diff --git a/nanoFirmwareFlasher.Library/packages.lock.json b/nanoFirmwareFlasher.Library/packages.lock.json index 3c7d8e10..9390fa32 100644 --- a/nanoFirmwareFlasher.Library/packages.lock.json +++ b/nanoFirmwareFlasher.Library/packages.lock.json @@ -2,11 +2,43 @@ "version": 1, "dependencies": { ".NETFramework,Version=v4.7.2": { + "Microsoft.ApplicationInsights": { + "type": "Direct", + "requested": "[2.22.0, )", + "resolved": "2.22.0", + "contentHash": "3AOM9bZtku7RQwHyMEY3tQMrHIgjcfRDa6YQpd/QG2LDGvMydSlL9Di+8LLMt7J2RDdfJ7/2jdYv6yHcMJAnNw==", + "dependencies": { + "System.Diagnostics.DiagnosticSource": "5.0.0" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "C2wqUoh9OmRL1akaCcKSTmRU8z0kckfImG7zLNI8uyi47Lp+zd5LWAD17waPQEqCz3ioWOCrFUo+JJuoeZLOBw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "System.Text.Json": "8.0.0" + } + }, "nanoFramework.Tools.Debugger.Net": { "type": "Direct", - "requested": "[2.4.34, )", - "resolved": "2.4.34", - "contentHash": "+QLH66C/dceiSlyWyfOEM5lOYZb13w5TjxvuB+ludaKTtD1IsZ4XAeXHen6P98eppgp3OLEus1GfMq7aGDj5Fw==", + "requested": "[2.4.42, )", + "resolved": "2.4.42", + "contentHash": "3RWlCdTOkf0RDHRc+qFs0Di4QP9JLwaa3msLK1WT+XUeIaG45MJo9WLJ+tBS4rXcn0dHQSAKIP17q374WmaMag==", "dependencies": { "Polly": "7.2.3", "PropertyChanged.Fody": "2.6.1", @@ -51,6 +83,67 @@ "resolved": "4.2.1", "contentHash": "46t+e1eESclH4TkfqUEYAgZ1+ZbXSFS7GqSKvtcw6u2yT05heDKdzj1r3YA/6FCZM4uB4z7qRW9V+gBsCilonQ==" }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0", + "System.ValueTuple": "4.5.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "McP+Lz/EKwvtCv48z0YImw+L1gi1gy5rHhNaNIY2CrjloV+XY8gydT8DjMR6zWeL13AFK+DioVpppwAuO1Gi1w==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Physical": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "Polly": { "type": "Transitive", "resolved": "7.2.3", @@ -64,16 +157,50 @@ "Fody": "4.2.1" } }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "tCQTzPsGZh/A9LhhA6zrqCRV4hOHsK90/G7q3Khxmn6tnB1PuNU0cRaKANP2AWcF9bn0zsuOoZOSrHuJk6oNBA==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, "System.IO": { "type": "Transitive", "resolved": "4.3.0", "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==" }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, "System.Runtime": { "type": "Transitive", "resolved": "4.3.0", "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==" }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, "System.Security.Cryptography.Algorithms": { "type": "Transitive", "resolved": "4.3.0", @@ -103,14 +230,83 @@ "System.Security.Cryptography.Algorithms": "4.3.0", "System.Security.Cryptography.Encoding": "4.3.0" } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "OdrZO2WjkiEG6ajEFRABTRCi/wuXQPxeV6g8xvUJqdxMvvuCCEk86zPla8UiIQJz3durtUEbNyY/3lIhS0yZvQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "System.Buffers": "4.5.1", + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encodings.Web": "8.0.0", + "System.Threading.Tasks.Extensions": "4.5.4", + "System.ValueTuple": "4.5.0" + } + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" } }, - "net6.0": { + "net8.0": { + "Microsoft.ApplicationInsights": { + "type": "Direct", + "requested": "[2.22.0, )", + "resolved": "2.22.0", + "contentHash": "3AOM9bZtku7RQwHyMEY3tQMrHIgjcfRDa6YQpd/QG2LDGvMydSlL9Di+8LLMt7J2RDdfJ7/2jdYv6yHcMJAnNw==", + "dependencies": { + "System.Diagnostics.DiagnosticSource": "5.0.0" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "C2wqUoh9OmRL1akaCcKSTmRU8z0kckfImG7zLNI8uyi47Lp+zd5LWAD17waPQEqCz3ioWOCrFUo+JJuoeZLOBw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "System.Text.Json": "8.0.0" + } + }, "nanoFramework.Tools.Debugger.Net": { "type": "Direct", - "requested": "[2.4.34, )", - "resolved": "2.4.34", - "contentHash": "+QLH66C/dceiSlyWyfOEM5lOYZb13w5TjxvuB+ludaKTtD1IsZ4XAeXHen6P98eppgp3OLEus1GfMq7aGDj5Fw==", + "requested": "[2.4.42, )", + "resolved": "2.4.42", + "contentHash": "3RWlCdTOkf0RDHRc+qFs0Di4QP9JLwaa3msLK1WT+XUeIaG45MJo9WLJ+tBS4rXcn0dHQSAKIP17q374WmaMag==", "dependencies": { "Polly": "7.2.3", "PropertyChanged.Fody": "2.6.1", @@ -183,6 +379,54 @@ "resolved": "4.2.1", "contentHash": "46t+e1eESclH4TkfqUEYAgZ1+ZbXSFS7GqSKvtcw6u2yT05heDKdzj1r3YA/6FCZM4uB4z7qRW9V+gBsCilonQ==" }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "McP+Lz/EKwvtCv48z0YImw+L1gi1gy5rHhNaNIY2CrjloV+XY8gydT8DjMR6zWeL13AFK+DioVpppwAuO1Gi1w==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Physical": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" + }, "Microsoft.NETCore.Platforms": { "type": "Transitive", "resolved": "1.1.1", @@ -483,15 +727,8 @@ }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - } + "resolved": "5.0.0", + "contentHash": "tCQTzPsGZh/A9LhhA6zrqCRV4hOHsK90/G7q3Khxmn6tnB1PuNU0cRaKANP2AWcF9bn0zsuOoZOSrHuJk6oNBA==" }, "System.Diagnostics.Tools": { "type": "Transitive", @@ -1011,6 +1248,19 @@ "System.Text.Encoding": "4.3.0" } }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "OdrZO2WjkiEG6ajEFRABTRCi/wuXQPxeV6g8xvUJqdxMvvuCCEk86zPla8UiIQJz3durtUEbNyY/3lIhS0yZvQ==", + "dependencies": { + "System.Text.Encodings.Web": "8.0.0" + } + }, "System.Text.RegularExpressions": { "type": "Transitive", "resolved": "4.3.0", diff --git a/nanoFirmwareFlasher.Tests/packages.lock.json b/nanoFirmwareFlasher.Tests/packages.lock.json deleted file mode 100644 index 2ce9903c..00000000 --- a/nanoFirmwareFlasher.Tests/packages.lock.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "version": 1, - "dependencies": { - "net6.0": { - "coverlet.collector": { - "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "tW3lsNS+dAEII6YGUX/VMoJjBS1QvsxqJeqLaJXub08y1FSjasFPtQ4UBUsudE9PNrzLjooClMsPtY2cZLdXpQ==" - }, - "Microsoft.NET.Test.Sdk": { - "type": "Direct", - "requested": "[17.6.0, )", - "resolved": "17.6.0", - "contentHash": "tHyg4C6c89QvLv6Utz3xKlba4EeoyJyIz59Q1NrjRENV7gfGnSE6I+sYPIbVOzQttoo2zpHDgOK/p6Hw2OlD7A==", - "dependencies": { - "Microsoft.CodeCoverage": "17.6.0", - "Microsoft.TestPlatform.TestHost": "17.6.0" - } - }, - "MSTest.TestAdapter": { - "type": "Direct", - "requested": "[3.0.3, )", - "resolved": "3.0.3", - "contentHash": "k2LRhIKbgc0HQQvTYZVsBby3I1V9q4h+xbXP3A0yQuB1jEeMze/JnJb3UCyUUElSx1CAfmyMazuTfbYaZwqZGw==" - }, - "MSTest.TestFramework": { - "type": "Direct", - "requested": "[3.0.3, )", - "resolved": "3.0.3", - "contentHash": "7EN6HmpSuNBnk3UP/FHvg3VeL13Gwc/D5vtZ+SFIkzDO99avVm8oYqbe19JCht4wy9cY13dpNA4gssCE8hp08Q==" - }, - "Microsoft.CodeCoverage": { - "type": "Transitive", - "resolved": "17.6.0", - "contentHash": "5v2GwzpR7JEuQUzupjx3zLwn2FutADW/weLzLt726DR3WXxsM+ICPoJG6pxuKFsumtZp890UrVuudTUhsE8Qyg==" - }, - "Microsoft.TestPlatform.ObjectModel": { - "type": "Transitive", - "resolved": "17.6.0", - "contentHash": "AA/rrf5zwC5/OBLEOajkhjbVTM3SvxRXy8kcQ8e4mJKojbyZvqqhpfNg362N9vXU94DLg9NUTFOAnoYVT0pTJw==", - "dependencies": { - "NuGet.Frameworks": "5.11.0", - "System.Reflection.Metadata": "1.6.0" - } - }, - "Microsoft.TestPlatform.TestHost": { - "type": "Transitive", - "resolved": "17.6.0", - "contentHash": "7YdgUcIeCPVKLC7n7LNKDiEHWc7z3brkkYPdUbDnFsvf6WvY9UfzS0VSUJ8P2NgN0CDSD223GCJFSjSBLZRqOQ==", - "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.6.0", - "Newtonsoft.Json": "13.0.1" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" - }, - "NuGet.Frameworks": { - "type": "Transitive", - "resolved": "5.11.0", - "contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q==" - }, - "System.Reflection.Metadata": { - "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" - } - } - } -} \ No newline at end of file diff --git a/nanoFirmwareFlasher.Tool/Esp32Manager.cs b/nanoFirmwareFlasher.Tool/Esp32Manager.cs new file mode 100644 index 00000000..6d3d2f44 --- /dev/null +++ b/nanoFirmwareFlasher.Tool/Esp32Manager.cs @@ -0,0 +1,205 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace nanoFramework.Tools.FirmwareFlasher +{ + /// + /// Class to manage different operations specific to the ESP32 platform. + /// + public class Esp32Manager : IManager + { + private readonly Options _options; + private readonly VerbosityLevel _verbosityLevel; + + public Esp32Manager(Options options, VerbosityLevel verbosityLevel) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + if (options.Platform != SupportedPlatform.esp32) + { + throw new NotSupportedException($"{nameof(options)} - {options.Platform}"); + } + + _options = options; + _verbosityLevel = verbosityLevel; + } + + /// + public async Task ProcessAsync() + { + // COM port is mandatory for ESP32 + if (string.IsNullOrEmpty(_options.SerialPort)) + { + return ExitCodes.E6001; + } + + EspTool espTool; + + try + { + espTool = new EspTool( + _options.SerialPort, + _options.BaudRate, + _options.Esp32FlashMode, + _options.Esp32FlashFrequency, + _options.Esp32PartitionTableSize, + _verbosityLevel); + } + catch (Exception) + { + return ExitCodes.E4005; + } + + Esp32DeviceInfo esp32Device; + + if (espTool.ComPortAvailable) + { + esp32Device = espTool.GetDeviceDetails( + _options.TargetName, + // if partition table size is specified, no need to get flash size + _options.Esp32PartitionTableSize == null, + _options.CheckPsRam); + } + else + { + // couldn't open COM port + // done here, this command has no further processing + return ExitCodes.E6000; + } + + if (_verbosityLevel >= VerbosityLevel.Normal) + { + Console.ForegroundColor = ConsoleColor.Cyan; + + Console.WriteLine(""); + Console.WriteLine($"Connected to:"); + Console.WriteLine($"{esp32Device}"); + + Console.ForegroundColor = ConsoleColor.White; + + // if this is a PICO and baud rate is not 115200 or 1M5, operations will most likely fail + // warn user about this + if ( + esp32Device.ChipName.Contains("ESP32-PICO") + && (_options.BaudRate != 115200 + && _options.BaudRate != 1500000)) + { + Console.ForegroundColor = ConsoleColor.Yellow; + + Console.WriteLine(""); + Console.WriteLine("****************************** WARNING ******************************"); + Console.WriteLine("The connected device it's an ESP32 PICO which can be picky about the "); + Console.WriteLine("baud rate used. Recommendation is to use --baud 115200 "); + Console.WriteLine("*********************************************************************"); + Console.WriteLine(""); + + Console.ForegroundColor = ConsoleColor.White; + } + } + + // set verbosity + espTool.Verbosity = _verbosityLevel; + + // backup requested + // Should backup be an unique operation => exit whatever success or not ? + // In this case, should find how manage a NoOperationPerformedException info + if (!string.IsNullOrEmpty(_options.BackupPath) || + !string.IsNullOrEmpty(_options.BackupFile)) + { + // backup path specified, backup deployment + var exitCode = Esp32Operations.BackupFlash(espTool, esp32Device, _options.BackupPath, _options.BackupFile, _verbosityLevel); + + if (exitCode != ExitCodes.OK) + { + // done here + return exitCode; + } + } + + // show device details + if (_options.DeviceDetails) + { + // device details already output + return ExitCodes.OK; + } + + bool updateAndDeploy = false; + + // update operation requested? + if (_options.Update) + { + // write flash + var exitCode = await Esp32Operations.UpdateFirmwareAsync( + espTool, + esp32Device, + _options.TargetName, + true, + _options.FwVersion, + _options.Preview, + _options.DeploymentImage, + null, + _options.ClrFile, + !_options.FitCheck, + _options.MassErase, + _verbosityLevel, + _options.Esp32PartitionTableSize); + + if (exitCode != ExitCodes.OK) + { + // done here + return exitCode; + } + + updateAndDeploy = true; + } + + // deploy without update + if (_options.Deploy && !_options.Update) + { + // need to take care of flash address + string appFlashAddress = string.Empty; + + if (_options.FlashAddress.Any()) + { + // take the first address, it should be the only one valid + appFlashAddress = _options.FlashAddress.ElementAt(0); + } + + // this to flash a deployment image without updating the firmware + // write flash + var exitCode = await Esp32Operations.DeployApplicationAsync( + espTool, + esp32Device, + _options.TargetName, + _options.DeploymentImage, + appFlashAddress, + _verbosityLevel, + _options.Esp32PartitionTableSize); + + if (exitCode != ExitCodes.OK) + { + // done here + return exitCode; + } + + updateAndDeploy = true; + } + + if (!updateAndDeploy) + { + throw new NoOperationPerformedException(); + } + + return ExitCodes.OK; + } + } +} diff --git a/nanoFirmwareFlasher.Tool/NanoDeviceManager.cs b/nanoFirmwareFlasher.Tool/NanoDeviceManager.cs new file mode 100644 index 00000000..ed23b99b --- /dev/null +++ b/nanoFirmwareFlasher.Tool/NanoDeviceManager.cs @@ -0,0 +1,91 @@ +using nanoFramework.Tools.Debugger; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace nanoFramework.Tools.FirmwareFlasher +{ + /// + /// Class to manage different operations specific to the nano devices. + /// + public class NanoDeviceManager : IManager + { + private readonly Options _options; + private readonly VerbosityLevel _verbosityLevel; + + public NanoDeviceManager(Options options, VerbosityLevel verbosityLevel) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + _options = options; + _verbosityLevel = verbosityLevel; + } + + /// + public async Task ProcessAsync() + { + bool failedToDoSomething = true; + ExitCodes exitCode = ExitCodes.OK; + + // COM port is mandatory for nano device operations + if (string.IsNullOrEmpty(_options.SerialPort)) + { + return ExitCodes.E6001; + } + + NanoDeviceOperations _nanoDeviceOperations = new NanoDeviceOperations(); + + if (_options.DeviceDetails) + { + NanoDeviceBase nanoDevice = null; + return _nanoDeviceOperations.GetDeviceDetails( + _options.SerialPort, + ref nanoDevice); + } + else if (_options.Update) + { + exitCode = await _nanoDeviceOperations.UpdateDeviceClrAsync( + _options.SerialPort, + _options.FwVersion, + _options.ClrFile, + _verbosityLevel); + + if (exitCode != ExitCodes.OK) + { + return exitCode; + } + + // flag operation as done + failedToDoSomething = false; + } + + if (_options.Deploy) + { + exitCode = _nanoDeviceOperations.DeployApplication( + _options.SerialPort, + _options.DeploymentImage, + _verbosityLevel); + + if (exitCode != ExitCodes.OK) + { + return exitCode; + } + + // flag operation as done + failedToDoSomething = false; + } + + if (failedToDoSomething) + { + throw new NoOperationPerformedException(); + } + + return exitCode; + } + } +} diff --git a/nanoFirmwareFlasher.Tool/Options.cs b/nanoFirmwareFlasher.Tool/Options.cs index 7ae44a9f..02bdb0c2 100644 --- a/nanoFirmwareFlasher.Tool/Options.cs +++ b/nanoFirmwareFlasher.Tool/Options.cs @@ -133,6 +133,13 @@ public class Options HelpText = "Partition table size to use. Valid sizes are: 2, 4, 8 and 16.")] public PartitionTableSize? Esp32PartitionTableSize { get; set; } + [Option( + "checkpsram", + Required = false, + Default = false, + HelpText = "Perform check for PSRAM in device.")] + public bool CheckPsRam { get; set; } + #endregion @@ -282,7 +289,7 @@ public class Options [Option( "address", Required = false, - HelpText = "Address(es) where to flash the BIN file(s). Hexadecimal format (e.g. 0x08000000). Required when specifying a BIN file with -binfile argument or flashing a deployment image with -deployment argument.")] + HelpText = "Address(es) where to flash the BIN file(s). Hexadecimal format (e.g. 0x08000000). Required when specifying a BIN file with --binfile argument or flashing a deployment image with --deploy argument.")] public IList FlashAddress { get; set; } [Option( @@ -334,13 +341,19 @@ public class Options HelpText = "Reads details from connected device.")] public bool DeviceDetails { get; set; } + [Option( + "filedeployment", + Required = false, + Default = null, + HelpText = "JSON file containing file deployment settings.")] + public string FileDeployment { get; set; } + #endregion [Usage(ApplicationAlias = "nanoff")] public static IEnumerable Examples => - new List - { + [ new("- Update ESP32 WROVER Kit device with latest available firmware", new Options { TargetName = "ESP_WROVER_KIT", Update = true }), new("- Update specific STM32 device (ST_STM32F769I_DISCOVERY) with latest available firmware, using JTAG interface", new Options { TargetName = "ST_STM32F769I_DISCOVERY" , Update = true, JtagUpdate = true}), new("- Update ESP32 device with latest available firmware (stable version), device is connected to COM31", new Options { Platform = SupportedPlatform.esp32, Update = true, SerialPort = "COM31" }), @@ -350,6 +363,6 @@ public class Options new("- Install STM32 JTAG drivers", new Options { InstallJtagDrivers = true}), new("- List all available STM32 targets", new Options { ListTargets = true, Platform = SupportedPlatform.stm32 }), new("- List all available COM ports", new Options { ListComPorts = true }), - }; + ]; } } diff --git a/nanoFirmwareFlasher.Tool/Program.cs b/nanoFirmwareFlasher.Tool/Program.cs index 19503969..ed131e37 100644 --- a/nanoFirmwareFlasher.Tool/Program.cs +++ b/nanoFirmwareFlasher.Tool/Program.cs @@ -5,8 +5,9 @@ using CommandLine; using CommandLine.Text; -using nanoFramework.Tools.Debugger; +using Microsoft.Extensions.Configuration; using nanoFramework.Tools.FirmwareFlasher.Extensions; +using nanoFramework.Tools.FirmwareFlasher.FileDeployment; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -17,7 +18,6 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Reflection; -using System.Threading; using System.Threading.Tasks; namespace nanoFramework.Tools.FirmwareFlasher @@ -32,8 +32,6 @@ internal class Program private static CopyrightInfo _copyrightInfo; private static NanoDeviceOperations _nanoDeviceOperations; - internal static string ExecutingPath; - public static async Task Main(string[] args) { // take care of static fields @@ -49,7 +47,15 @@ public static async Task Main(string[] args) // need this to be able to use ProcessStart at the location where the .NET Core CLI tool is running from string codeBase = Assembly.GetExecutingAssembly().Location; var fullPath = Path.GetFullPath(codeBase); - ExecutingPath = Path.GetDirectoryName(fullPath); + var ExecutingPath = Path.GetDirectoryName(fullPath); + + // grab AppInsights connection string to setup telemetry client + IConfigurationRoot appConfigurationRoot = new ConfigurationBuilder() + .SetBasePath(ExecutingPath) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .Build(); + + NanoTelemetryClient.ConnectionString = appConfigurationRoot?["iConnectionString"]; // check for empty argument collection if (!args.Any()) @@ -59,7 +65,7 @@ public static async Task Main(string[] args) // because of short-comings in CommandLine parsing // need to customize the output to provide a consistent output var parser = new Parser(config => config.HelpWriter = null); - var result = parser.ParseArguments(new[] { "", "" }); + var result = parser.ParseArguments(new string[] { "", "" }); var helpText = new HelpText( new HeadingInfo(_headerInfo), @@ -95,7 +101,7 @@ await parsedArguments if (_verbosityLevel > VerbosityLevel.Quiet) { - OutputError(_exitCode, _verbosityLevel > VerbosityLevel.Normal, _extraMessage); + OutputError(_exitCode, _verbosityLevel >= VerbosityLevel.Normal, _extraMessage); } // force clean-up @@ -145,6 +151,10 @@ private static void CheckVersion() private static Task HandleErrorsAsync(IEnumerable errors) { + if (errors.All(e => e.Tag == ErrorType.HelpRequestedError || e.Tag == ErrorType.VersionRequestedError)) + { + return Task.CompletedTask; + } _exitCode = ExitCodes.E9000; return Task.CompletedTask; } @@ -297,7 +307,7 @@ static async Task RunOptionsAndReturnExitCodeAsync(Options o) { var connectedDevices = _nanoDeviceOperations.ListDevices(_verbosityLevel > VerbosityLevel.Normal); - if (connectedDevices.Count() == 0) + if (!connectedDevices.Any()) { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("No devices found"); @@ -362,93 +372,41 @@ static async Task RunOptionsAndReturnExitCodeAsync(Options o) if (o.NanoDevice) { - // COM port is mandatory for nano device operations - if (string.IsNullOrEmpty(o.SerialPort)) + // check for invalid options passed with nano device operations + if (o.Platform.HasValue + || !string.IsNullOrEmpty(o.TargetName)) { - _exitCode = ExitCodes.E6001; + _exitCode = ExitCodes.E9000; + _extraMessage = "Incompatible options combined with --nanodevice."; return; } - _nanoDeviceOperations = new NanoDeviceOperations(); + var manager = new NanoDeviceManager(o, _verbosityLevel); - if (o.DeviceDetails) + // COM port is mandatory for nano device operations + if (string.IsNullOrEmpty(o.SerialPort)) { - try - { - NanoDeviceBase nanoDevice = null; - _exitCode = _nanoDeviceOperations.GetDeviceDetails( - o.SerialPort, - ref nanoDevice); - - // done here - return; - } - catch (CantConnectToNanoDeviceException ex) - { - _exitCode = ExitCodes.E2000; - _extraMessage = ex.Message; - - return; - } + _exitCode = ExitCodes.E6001; } - else if (o.Update) + else { try { - _exitCode = await _nanoDeviceOperations.UpdateDeviceClrAsync( - o.SerialPort, - o.FwVersion, - o.ClrFile, - _verbosityLevel); - - if (_exitCode != ExitCodes.OK) - { - return; - } + _exitCode = await manager.ProcessAsync(); } catch (CantConnectToNanoDeviceException ex) { _exitCode = ExitCodes.E2001; _extraMessage = ex.Message; - - return; - } - catch (Exception ex) - { - _exitCode = ExitCodes.E2002; - _extraMessage = ex.Message; - - return; } - } - - if (o.Deploy) - { - try + catch (NoOperationPerformedException) { - _exitCode = _nanoDeviceOperations.DeployApplication( - o.SerialPort, - o.DeploymentImage, - _verbosityLevel); - - if (_exitCode != ExitCodes.OK) - { - return; - } - } - catch (CantConnectToNanoDeviceException ex) - { - _exitCode = ExitCodes.E2001; - _extraMessage = ex.Message; - - return; + DisplayNoOperationMessage(); } catch (Exception ex) { _exitCode = ExitCodes.E2002; _extraMessage = ex.Message; - - return; } } @@ -461,8 +419,16 @@ static async Task RunOptionsAndReturnExitCodeAsync(Options o) // if a target name was specified, try to be smart and set the platform accordingly (in case it wasn't specified) if (o.Platform == null - && !string.IsNullOrEmpty(o.TargetName)) + && !string.IsNullOrEmpty(o.TargetName)) { + // check for invalid options passed with platform option + if (o.NanoDevice) + { + _exitCode = ExitCodes.E9000; + _extraMessage = "Incompatible options combined with --platform."; + return; + } + // easiest one: ESP32 if (o.TargetName.StartsWith("ESP") || o.TargetName.StartsWith("M5") @@ -545,10 +511,10 @@ static async Task RunOptionsAndReturnExitCodeAsync(Options o) } // ESP32 related else if ( - !string.IsNullOrEmpty(o.SerialPort) || - (o.BaudRate != 921600) || + !string.IsNullOrEmpty(o.SerialPort) && + ((o.BaudRate != 921600) || (o.Esp32FlashMode != "dio") || - (o.Esp32FlashFrequency != 40)) + (o.Esp32FlashFrequency != 40))) { o.Platform = SupportedPlatform.esp32; } @@ -560,232 +526,39 @@ static async Task RunOptionsAndReturnExitCodeAsync(Options o) if (o.Platform == SupportedPlatform.esp32) { - // COM port is mandatory for ESP32 - if (string.IsNullOrEmpty(o.SerialPort)) - { - _exitCode = ExitCodes.E6001; - return; - } - - EspTool espTool; + var manager = new Esp32Manager(o, _verbosityLevel); try { - espTool = new EspTool( - o.SerialPort, - o.BaudRate, - o.Esp32FlashMode, - o.Esp32FlashFrequency, - o.Esp32PartitionTableSize, - _verbosityLevel); - } - catch (Exception) - { - _exitCode = ExitCodes.E4005; - return; + _exitCode = await manager.ProcessAsync(); } - - Esp32DeviceInfo esp32Device; - - if (espTool.ComPortAvailable) + catch (EspToolExecutionException ex) { - try - { - esp32Device = espTool.GetDeviceDetails(o.TargetName, o.Esp32PartitionTableSize == null); - } - catch (EspToolExecutionException ex) - { - _exitCode = ExitCodes.E4000; - _extraMessage = ex.Message; - - return; - } - } - else - { - // couldn't open COM port - // done here, this command has no further processing - _exitCode = ExitCodes.E6000; - - return; + _exitCode = ExitCodes.E4000; + _extraMessage = ex.Message; } - - if (_verbosityLevel >= VerbosityLevel.Normal) + catch (ReadEsp32FlashException ex) { - Console.ForegroundColor = ConsoleColor.Cyan; - - Console.WriteLine(""); - Console.WriteLine($"Connected to:"); - Console.WriteLine($"{esp32Device}"); - - Console.ForegroundColor = ConsoleColor.White; - - // if this is a PICO and baud rate is not 115200 or 1M5, operations will most likely fail - // warn user about this - if ( - esp32Device.ChipName.Contains("ESP32-PICO") - && (o.BaudRate != 115200 - && o.BaudRate != 1500000)) - { - Console.ForegroundColor = ConsoleColor.Yellow; - - Console.WriteLine(""); - Console.WriteLine("****************************** WARNING ******************************"); - Console.WriteLine("The connected device it's an ESP32 PICO which can be picky about the "); - Console.WriteLine("baud rate used. Recommendation is to use --baud 115200 "); - Console.WriteLine("*********************************************************************"); - Console.WriteLine(""); - - Console.ForegroundColor = ConsoleColor.White; - } + _exitCode = ExitCodes.E4004; + _extraMessage = ex.Message; } - - // set verbosity - espTool.Verbosity = _verbosityLevel; - - // backup requested - if (!string.IsNullOrEmpty(o.BackupPath) || - !string.IsNullOrEmpty(o.BackupFile)) + catch (WriteEsp32FlashException ex) { - try - { - // backup path specified, backup deployment - _exitCode = Esp32Operations.BackupFlash(espTool, esp32Device, o.BackupPath, o.BackupFile, _verbosityLevel); - - if (_exitCode != ExitCodes.OK) - { - // done here - return; - } - - operationPerformed = true; - } - catch (ReadEsp32FlashException ex) - { - _exitCode = ExitCodes.E4004; - _extraMessage = ex.Message; - - // done here - return; - } + _exitCode = ExitCodes.E4003; + _extraMessage = ex.Message; } - - // show device details - if (o.DeviceDetails) + catch (NoOperationPerformedException) { - // device details already output - _exitCode = ExitCodes.OK; - - // done here - return; + DisplayNoOperationMessage(); } - - // update operation requested? - if (o.Update) - { - try - { - // write flash - _exitCode = await Esp32Operations.UpdateFirmwareAsync( - espTool, - esp32Device, - o.TargetName, - true, - o.FwVersion, - o.Preview, - o.DeploymentImage, - null, - o.ClrFile, - !o.FitCheck, - _verbosityLevel, - o.Esp32PartitionTableSize); - - if (_exitCode != ExitCodes.OK) - { - // done here - return; - } - - // done here - _exitCode = ExitCodes.OK; - return; - } - catch (ReadEsp32FlashException ex) - { - _exitCode = ExitCodes.E4004; - _extraMessage = ex.Message; - } - catch (WriteEsp32FlashException ex) - { - _exitCode = ExitCodes.E4003; - _extraMessage = ex.Message; - } - catch (EspToolExecutionException ex) - { - _exitCode = ExitCodes.E4000; - _extraMessage = ex.Message; - } - } - - // it's OK to deploy after update - if (o.Deploy) + catch (Exception ex) { - // need to take care of flash address - string appFlashAddress = string.Empty; - - if (o.FlashAddress.Any()) - { - // take the first address, it should be the only one valid - appFlashAddress = o.FlashAddress.ElementAt(0); - } - - // this to flash a deployment image without updating the firmware - try - { - // write flash - _exitCode = await Esp32Operations.UpdateFirmwareAsync( - espTool, - esp32Device, - o.TargetName, - false, - null, - false, - o.DeploymentImage, - appFlashAddress, - null, - !o.FitCheck, - _verbosityLevel, - o.Esp32PartitionTableSize); - - if (_exitCode != ExitCodes.OK) - { - // done here - return; - } - - // done here - _exitCode = ExitCodes.OK; - return; - } - catch (ReadEsp32FlashException ex) - { - _exitCode = ExitCodes.E4004; - _extraMessage = ex.Message; - } - catch (WriteEsp32FlashException ex) - { - _exitCode = ExitCodes.E4003; - _extraMessage = ex.Message; - } - catch (EspToolExecutionException ex) - { - _exitCode = ExitCodes.E4000; - _extraMessage = ex.Message; - } + // exception with + _exitCode = ExitCodes.E4000; + _extraMessage = ex.Message; } - // done here - return; + operationPerformed = true; } #endregion @@ -794,353 +567,34 @@ static async Task RunOptionsAndReturnExitCodeAsync(Options o) if (o.Platform == SupportedPlatform.stm32) { - if (o.InstallDfuDrivers) - { - _exitCode = Stm32Operations.InstallDfuDrivers(_verbosityLevel); + var manager = new Stm32Manager(o, _verbosityLevel); - // done here - return; - } - - if (o.InstallJtagDrivers) + try { - _exitCode = Stm32Operations.InstallJtagDrivers(_verbosityLevel); - - // done here - return; + _exitCode = await manager.ProcessAsync(); } - - if (o.ListDevicesInDfuMode) + catch (CantConnectToDfuDeviceException) { - var connecteDevices = StmDfuDevice.ListDevices(); - - Console.ForegroundColor = ConsoleColor.Cyan; - - if (connecteDevices.Count() == 0) - { - Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine("No DFU devices found"); - } - else - { - Console.WriteLine("-- Connected DFU devices --"); - - foreach ((string serial, string device) device in connecteDevices) - { - Console.WriteLine($"{device.serial} @ {device.device}"); - } - - Console.WriteLine("---------------------------"); - } - - Console.ForegroundColor = ConsoleColor.White; - // done here, this command has no further processing - _exitCode = ExitCodes.OK; - - return; + _exitCode = ExitCodes.E1005; } - - if (o.ListJtagDevices) + catch (CantConnectToJtagDeviceException) { - try - { - var connecteDevices = StmJtagDevice.ListDevices(); - - Console.ForegroundColor = ConsoleColor.Cyan; - - if (connecteDevices.Count == 0) - { - Console.WriteLine("No JTAG devices found"); - } - else - { - Console.WriteLine("-- Connected JTAG devices --"); - - foreach (string deviceId in connecteDevices) - { - Console.WriteLine(deviceId); - } - - Console.WriteLine("---------------------------"); - } - - // done here, this command has no further processing - _exitCode = ExitCodes.OK; - } - catch (Exception ex) - { - // exception with - _exitCode = ExitCodes.E5000; - _extraMessage = ex.Message; - } - - Console.ForegroundColor = ConsoleColor.White; - - return; - } - - var connectedStDfuDevices = StmDfuDevice.ListDevices(); - var connectedStJtagDevices = StmJtagDevice.ListDevices(); - - if (o.BinFile.Any() && - o.HexFile.Any() && - connectedStDfuDevices.Count != 0) - { - - #region STM32 DFU options - - try - { - var dfuDevice = new StmDfuDevice(o.DfuDeviceId); - - if (!dfuDevice.DevicePresent) - { - // no JTAG device found - - // done here, this command has no further processing - _exitCode = ExitCodes.E5001; - - return; - } - - if (_verbosityLevel >= VerbosityLevel.Normal) - { - Console.WriteLine($"Connected to JTAG device with ID {dfuDevice.DfuId}"); - } - - // set verbosity - dfuDevice.Verbosity = _verbosityLevel; - - // get mass erase option - dfuDevice.DoMassErase = o.MassErase; - - if (o.HexFile.Any()) - { - _exitCode = dfuDevice.FlashHexFiles(o.HexFile); - - // done here - return; - } - - if (o.BinFile.Any()) - { - _exitCode = dfuDevice.FlashBinFiles(o.BinFile, o.FlashAddress); - - // done here - return; - } - } - catch (CantConnectToDfuDeviceException) - { - // done here, this command has no further processing - _exitCode = ExitCodes.E1005; - } - - #endregion - - } - else if ( - o.BinFile.Any() && - o.HexFile.Any() && - connectedStJtagDevices.Count != 0 - ) - { - // this has to be a JTAG connected device - - #region STM32 JTAG options - - try - { - var jtagDevice = new StmJtagDevice(o.JtagDeviceId); - - if (!jtagDevice.DevicePresent) - { - // no JTAG device found - - // done here, this command has no further processing - _exitCode = ExitCodes.E5001; - - return; - } - - if (_verbosityLevel >= VerbosityLevel.Normal) - { - Console.WriteLine($"Connected to JTAG device with ID {jtagDevice.JtagId}"); - } - - // set verbosity - jtagDevice.Verbosity = _verbosityLevel; - - // get mass erase option - jtagDevice.DoMassErase = o.MassErase; - - if (o.HexFile.Any()) - { - _exitCode = jtagDevice.FlashHexFiles(o.HexFile); - - // done here - return; - } - - if (o.BinFile.Any()) - { - _exitCode = jtagDevice.FlashBinFiles(o.BinFile, o.FlashAddress); - - // done here - return; - } - } - catch (CantConnectToJtagDeviceException) - { - // done here, this command has no further processing - _exitCode = ExitCodes.E5002; - } - - #endregion + // done here, this command has no further processing + _exitCode = ExitCodes.E5002; } - else if (!string.IsNullOrEmpty(o.TargetName)) + catch (NoOperationPerformedException) { - // update operation requested? - if (o.Update) - { - // this to update the device with fw from CloudSmith - - // need to take care of flash address - string appFlashAddress = null; - - if (o.FlashAddress.Any()) - { - // take the first address, it should be the only one valid - appFlashAddress = o.FlashAddress.ElementAt(0); - } - - Interface updateInterface = Interface.None; - - if (o.DfuUpdate && o.JtagUpdate) - { - // can't select both JTAG and DFU simultaneously - _exitCode = ExitCodes.E9000; - return; - } - else if (o.DfuUpdate) - { - updateInterface = Interface.Dfu; - } - else if (o.JtagUpdate) - { - updateInterface = Interface.Jtag; - } - - _exitCode = await Stm32Operations.UpdateFirmwareAsync( - o.TargetName, - o.FwVersion, - o.Preview, - true, - o.DeploymentImage, - appFlashAddress, - o.DfuDeviceId, - o.JtagDeviceId, - !o.FitCheck, - updateInterface, - _verbosityLevel); - - operationPerformed = true; - - if (_exitCode != ExitCodes.OK) - { - // done here - return; - } - } - - // it's OK to deploy after update - if (o.Deploy) - { - // this to flash a deployment image without updating the firmware - - // need to take care of flash address - string appFlashAddress; - - if (o.FlashAddress.Any()) - { - // take the first address, it should be the only one valid - appFlashAddress = o.FlashAddress.ElementAt(0); - } - else - { - _exitCode = ExitCodes.E9009; - return; - } - - Interface updateInterface = Interface.None; - - if (o.DfuUpdate && o.JtagUpdate) - { - // can't select both JTAG and DFU simultaneously - _exitCode = ExitCodes.E9000; - return; - } - else if (o.DfuUpdate) - { - updateInterface = Interface.Dfu; - } - else if (o.JtagUpdate) - { - updateInterface = Interface.Jtag; - } - - _exitCode = await Stm32Operations.UpdateFirmwareAsync( - o.TargetName, - null, - false, - false, - o.DeploymentImage, - appFlashAddress, - o.DfuDeviceId, - o.JtagDeviceId, - !o.FitCheck, - updateInterface, - _verbosityLevel); - - operationPerformed = true; - - if (_exitCode != ExitCodes.OK) - { - // done here - return; - } - } - - // reset MCU requested? - if (o.ResetMcu) - { - _exitCode = Stm32Operations.ResetMcu( - o.JtagDeviceId, - _verbosityLevel); - - // done here - return; - } + DisplayNoOperationMessage(); } - else if (o.MassErase) + catch (Exception ex) { - _exitCode = Stm32Operations.MassErase( - o.JtagDeviceId, - _verbosityLevel); - - // done here - return; + // exception with + _exitCode = ExitCodes.E5000; + _extraMessage = ex.Message; } - else if (o.ResetMcu) - { - _exitCode = Stm32Operations.ResetMcu( - o.JtagDeviceId, - _verbosityLevel); - // done here - return; - } + operationPerformed = true; } #endregion @@ -1149,96 +603,24 @@ static async Task RunOptionsAndReturnExitCodeAsync(Options o) if (o.Platform == SupportedPlatform.ti_simplelink) { - if (o.TIInstallXdsDrivers) - { - _exitCode = CC13x26x2Operations.InstallXds110Drivers(_verbosityLevel); + var manager = new TIManager(o, _verbosityLevel); - // done here - return; + try + { + _exitCode = await manager.ProcessAsync(); } - - if (!string.IsNullOrEmpty(o.TargetName)) + catch (NoOperationPerformedException) { - // update operation requested? - if (o.Update) - { - // this to update the device with fw from Cloudsmith - - // need to take care of flash address - string appFlashAddress = null; - - if (o.FlashAddress.Any()) - { - // take the first address, it should be the only one valid - appFlashAddress = o.FlashAddress.ElementAt(0); - } - - _exitCode = await CC13x26x2Operations.UpdateFirmwareAsync( - o.TargetName, - o.FwVersion, - o.Preview, - true, - o.DeploymentImage, - appFlashAddress, - _verbosityLevel); - - operationPerformed = true; - - if (_exitCode != ExitCodes.OK) - { - // done here - return; - } - } - - // it's OK to deploy after update - if (o.Deploy) - { - // this to flash a deployment image without updating the firmware - - // need to take care of flash address - string appFlashAddress = null; - - if (o.FlashAddress.Any()) - { - // take the first address, it should be the only one valid - appFlashAddress = o.FlashAddress.ElementAt(0); - } - else - { - _exitCode = ExitCodes.E9009; - return; - } - - _exitCode = await CC13x26x2Operations.UpdateFirmwareAsync( - o.TargetName, - null, - false, - false, - o.DeploymentImage, - appFlashAddress, - _verbosityLevel); - - operationPerformed = true; - - if (_exitCode != ExitCodes.OK) - { - // done here - return; - } - } - - // reset MCU requested? - if (o.ResetMcu) - { - // can't reset CC13x2 device without configuration file - // would require to specify the exact target name and then had to try parsing that - _exitCode = ExitCodes.E9000; - - // done here - return; - } + DisplayNoOperationMessage(); + } + catch (Exception ex) + { + // exception with + _exitCode = ExitCodes.E5000; + _extraMessage = ex.Message; } + + operationPerformed = true; } #endregion @@ -1247,237 +629,80 @@ static async Task RunOptionsAndReturnExitCodeAsync(Options o) if (o.Platform == SupportedPlatform.gg11) { - if (o.ListJLinkDevices) - { - try - { - var connecteDevices = JLinkDevice.ListDevices(); + var manager = new SilabsManager(o, _verbosityLevel); - Console.ForegroundColor = ConsoleColor.Cyan; - - if (connecteDevices.Count == 0) - { - Console.WriteLine("No J-Link devices found"); - } - else - { - Console.WriteLine("-- Connected USB J-Link devices --"); - - foreach (string deviceId in connecteDevices) - { - Console.WriteLine(deviceId); - } - - Console.WriteLine("----------------------------------"); - } - - // done here, this command has no further processing - _exitCode = ExitCodes.OK; - } - catch (Exception ex) - { - // exception with - _exitCode = ExitCodes.E8000; - _extraMessage = ex.Message; - } - - Console.ForegroundColor = ConsoleColor.White; - - return; + try + { + _exitCode = await manager.ProcessAsync(); } - - var connectedJLinkDevices = JLinkDevice.ListDevices(); - - - if (o.BinFile.Any() - && connectedJLinkDevices.Count != 0) + catch (CantConnectToJLinkDeviceException) { - try - { - var jlinkDevice = new JLinkDevice(o.JLinkDeviceId); - - if (!jlinkDevice.DevicePresent) - { - // no J-Link device found - - // done here, this command has no further processing - _exitCode = ExitCodes.E8001; - - return; - } - - if (_verbosityLevel >= VerbosityLevel.Normal) - { - Console.WriteLine($"Connected to J-Link device with ID {jlinkDevice.ProbeId}"); - } - - if (_verbosityLevel == VerbosityLevel.Diagnostic) - { - Console.WriteLine($"Firmware: {jlinkDevice.Firmare}"); - Console.WriteLine($"Hardware: {jlinkDevice.Hardware}"); - } - - // set VCP baud rate (if requested) - if (o.SetVcpBaudRate.HasValue) - { - _ = SilinkCli.SetVcpBaudRate( - o.JLinkDeviceId is null ? connectedJLinkDevices.First() : "", - o.SetVcpBaudRate.Value, - _verbosityLevel); - } - - // set verbosity - jlinkDevice.Verbosity = _verbosityLevel; - - // get mass erase option - jlinkDevice.DoMassErase = o.MassErase; - - if (o.BinFile.Any()) - { - _exitCode = jlinkDevice.FlashBinFiles(o.BinFile, o.FlashAddress); - - // done here - return; - } - } - catch (CantConnectToJLinkDeviceException) - { - // done here, this command has no further processing - _exitCode = ExitCodes.E8002; - } + // done here, this command has no further processing + _exitCode = ExitCodes.E8001; } - else if (!string.IsNullOrEmpty(o.TargetName)) + catch (SilinkExecutionException) { - // update operation requested? - if (o.Update) - { - // this to update the device with fw from CloudSmith - - // need to take care of flash address - string appFlashAddress = null; - - if (o.FlashAddress.Any()) - { - // take the first address, it should be the only one valid - appFlashAddress = o.FlashAddress.ElementAt(0); - } - - _exitCode = await JLinkOperations.UpdateFirmwareAsync( - o.TargetName, - o.FwVersion, - o.Preview, - true, - o.DeploymentImage, - appFlashAddress, - o.JLinkDeviceId, - !o.FitCheck, - _verbosityLevel); - - operationPerformed = true; - - if (o.SetVcpBaudRate.HasValue) - { - // set VCP baud rate (if needed) - _ = SilinkCli.SetVcpBaudRate( - o.JLinkDeviceId is null ? connectedJLinkDevices.First() : "", - o.SetVcpBaudRate.Value, - _verbosityLevel); - } - - if (_exitCode != ExitCodes.OK) - { - // done here - return; - } - } - - // it's OK to deploy after update - if (o.Deploy) - { - // this to flash a deployment image without updating the firmware - - // need to take care of flash address - string appFlashAddress; - - if (o.FlashAddress.Any()) - { - // take the first address, it should be the only one valid - appFlashAddress = o.FlashAddress.ElementAt(0); - } - else - { - _exitCode = ExitCodes.E9009; - return; - } + // done here, this command has no further processing + _exitCode = ExitCodes.E8002; + } + catch (NoOperationPerformedException) + { + DisplayNoOperationMessage(); + } + catch (Exception ex) + { + // exception with + _exitCode = ExitCodes.E8000; + _extraMessage = ex.Message; + } - _exitCode = await JLinkOperations.UpdateFirmwareAsync( - o.TargetName, - null, - false, - false, - o.DeploymentImage, - appFlashAddress, - o.JLinkDeviceId, - !o.FitCheck, - _verbosityLevel); + operationPerformed = true; + } - operationPerformed = true; + #endregion - if (_exitCode != ExitCodes.OK) - { - // done here - return; - } - } - } - else if (o.MassErase) + // done nothing... or maybe not... + if (!operationPerformed && string.IsNullOrEmpty(o.FileDeployment)) + { + DisplayNoOperationMessage(); + } + else + { + if ((_exitCode == ExitCodes.OK) && !string.IsNullOrEmpty(o.FileDeployment)) { + FileDeploymentManager deploy = new FileDeploymentManager(o.FileDeployment, o.SerialPort, _verbosityLevel); try { - _exitCode = JLinkOperations.MassErase( - o.JLinkDeviceId, - _verbosityLevel); - } - catch (CantConnectToJLinkDeviceException) - { - // exception with - _exitCode = ExitCodes.E8001; - + _exitCode = await deploy.DeployAsync(); } catch (Exception ex) { // exception with - _exitCode = ExitCodes.E8000; + _exitCode = ExitCodes.E2003; _extraMessage = ex.Message; } - - // done here - return; } } + } - #endregion - - // done nothing... or maybe not... - if (!operationPerformed) - { - // because of short-comings in CommandLine parsing - // need to customize the output to provide a consistent output - var parser = new Parser(config => config.HelpWriter = null); - var result = parser.ParseArguments(new[] { "", "" }); - - var helpText = new HelpText( - new HeadingInfo(_headerInfo), - _copyrightInfo) - .AddPreOptionsLine("") - .AddPreOptionsLine("No operation was performed with the options supplied.") - .AddPreOptionsLine("") - .AddPreOptionsLine(HelpText.RenderUsageText(result)) - .AddPreOptionsLine("") - .AddOptions(result); - - Console.WriteLine(helpText.ToString()); - } + private static void DisplayNoOperationMessage() + { + // because of short-comings in CommandLine parsing + // need to customize the output to provide a consistent output + var parser = new Parser(config => config.HelpWriter = null); + var result = parser.ParseArguments(new string[] { "", "" }); + + var helpText = new HelpText( + new HeadingInfo(_headerInfo), + _copyrightInfo) + .AddPreOptionsLine("") + .AddPreOptionsLine("No operation was performed with the options supplied.") + .AddPreOptionsLine("") + .AddPreOptionsLine(HelpText.RenderUsageText(result)) + .AddPreOptionsLine("") + .AddOptions(result); + + Console.WriteLine(helpText.ToString()); } private static void DisplayBoardDetails(List boards) diff --git a/nanoFirmwareFlasher.Tool/SilabsManager.cs b/nanoFirmwareFlasher.Tool/SilabsManager.cs new file mode 100644 index 00000000..260fd2d3 --- /dev/null +++ b/nanoFirmwareFlasher.Tool/SilabsManager.cs @@ -0,0 +1,245 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace nanoFramework.Tools.FirmwareFlasher +{ + /// + /// Class to manage different operations specific to the Silabs GG11 platform. + /// + public class SilabsManager : IManager + { + private readonly Options _options; + private readonly VerbosityLevel _verbosityLevel; + + public SilabsManager(Options options, VerbosityLevel verbosityLevel) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + if (options.Platform != SupportedPlatform.gg11) + { + throw new NotSupportedException($"{nameof(options)} - {options.Platform}"); + } + + _options = options; + _verbosityLevel = verbosityLevel; + } + + /// + public async Task ProcessAsync() + { + if (_options.ListJLinkDevices) + { + var connecteDevices = JLinkDevice.ListDevices(); + + Console.ForegroundColor = ConsoleColor.Cyan; + + if (connecteDevices.Count == 0) + { + Console.WriteLine("No J-Link devices found"); + } + else + { + Console.WriteLine("-- Connected USB J-Link devices --"); + + foreach (string deviceId in connecteDevices) + { + Console.WriteLine(deviceId); + } + + Console.WriteLine("----------------------------------"); + } + + Console.ForegroundColor = ConsoleColor.White; + + // done here, this command has no further processing + return ExitCodes.OK; + } + + var connectedJLinkDevices = JLinkDevice.ListDevices(); + bool updateAndDeploy = false; + + if ((_options.BinFile.Any() || + !string.IsNullOrEmpty(_options.DeploymentImage)) + && connectedJLinkDevices.Count != 0) + { + try + { + var jlinkDevice = new JLinkDevice(_options.JLinkDeviceId); + + if (!jlinkDevice.DevicePresent) + { + // no J-Link device found + + // done here, this command has no further processing + return ExitCodes.E8001; + } + + if (_verbosityLevel >= VerbosityLevel.Normal) + { + Console.WriteLine($"Connected to J-Link device with ID {jlinkDevice.ProbeId}"); + } + + if (_verbosityLevel == VerbosityLevel.Diagnostic) + { + Console.WriteLine($"Firmware: {jlinkDevice.Firmare}"); + Console.WriteLine($"Hardware: {jlinkDevice.Hardware}"); + } + + // set VCP baud rate (if requested) + if (_options.SetVcpBaudRate.HasValue) + { + _ = SilinkCli.SetVcpBaudRate( + _options.JLinkDeviceId is null ? connectedJLinkDevices.First() : "", + _options.SetVcpBaudRate.Value, + _verbosityLevel); + } + + // set verbosity + jlinkDevice.Verbosity = _verbosityLevel; + + // get mass erase option + jlinkDevice.DoMassErase = _options.MassErase; + + // is there a bin file to flash? + if (_options.BinFile.Any()) + { + var exitCode = jlinkDevice.FlashBinFiles(_options.BinFile, _options.FlashAddress); + + if (exitCode != ExitCodes.OK) + { + // done here + return exitCode; + } + + updateAndDeploy = true; + } + else if (!string.IsNullOrEmpty(_options.DeploymentImage) && _options.Deploy) + { + var exitCode = jlinkDevice.FlashBinFiles( + [_options.DeploymentImage], + _options.FlashAddress); + + if (exitCode != ExitCodes.OK) + { + // done here + return exitCode; + } + + updateAndDeploy = true; + } + } + catch (CantConnectToJLinkDeviceException) + { + // exception too vague for the full list of commands above + throw new SilinkExecutionException(); + } + } + else if (!string.IsNullOrEmpty(_options.TargetName)) + { + // update operation requested? + if (_options.Update) + { + // this to update the device with fw from CloudSmith + + // need to take care of flash address + string appFlashAddress = null; + + if (_options.FlashAddress.Any()) + { + // take the first address, it should be the only one valid + appFlashAddress = _options.FlashAddress.ElementAt(0); + } + + var exitCode = await JLinkOperations.UpdateFirmwareAsync( + _options.TargetName, + _options.FwVersion, + _options.Preview, + true, + _options.DeploymentImage, + appFlashAddress, + _options.JLinkDeviceId, + !_options.FitCheck, + _verbosityLevel); + + if (_options.SetVcpBaudRate.HasValue) + { + // set VCP baud rate (if needed) + _ = SilinkCli.SetVcpBaudRate( + _options.JLinkDeviceId is null ? connectedJLinkDevices.First() : "", + _options.SetVcpBaudRate.Value, + _verbosityLevel); + } + + if (exitCode != ExitCodes.OK) + { + // done here + return exitCode; + } + + updateAndDeploy = true; + } + + // it's OK to deploy after a successful update + if (_options.Deploy) + { + // this to flash a deployment image without updating the firmware + + // need to take care of flash address + string appFlashAddress; + + if (_options.FlashAddress.Any()) + { + // take the first address, it should be the only one valid + appFlashAddress = _options.FlashAddress.ElementAt(0); + } + else + { + return ExitCodes.E9009; + } + + var exitCode = await JLinkOperations.UpdateFirmwareAsync( + _options.TargetName, + null, + false, + false, + _options.DeploymentImage, + appFlashAddress, + _options.JLinkDeviceId, + !_options.FitCheck, + _verbosityLevel); + + if (exitCode != ExitCodes.OK) + { + // done here + return exitCode; + } + + updateAndDeploy = true; + } + } + else if (_options.MassErase) + { + return JLinkOperations.MassErase( + _options.JLinkDeviceId, + _verbosityLevel); + } + + if (!updateAndDeploy) + { + throw new NoOperationPerformedException(); + } + + return ExitCodes.OK; + } + } +} diff --git a/nanoFirmwareFlasher.Tool/Stm32Manager.cs b/nanoFirmwareFlasher.Tool/Stm32Manager.cs new file mode 100644 index 00000000..120222cf --- /dev/null +++ b/nanoFirmwareFlasher.Tool/Stm32Manager.cs @@ -0,0 +1,331 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace nanoFramework.Tools.FirmwareFlasher +{ + /// + /// Class to manage different operations specific to the STM32 platform. + /// + public class Stm32Manager : IManager + { + private readonly Options _options; + private readonly VerbosityLevel _verbosityLevel; + + public Stm32Manager(Options options, VerbosityLevel verbosityLevel) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + if (options.Platform != SupportedPlatform.stm32) + { + throw new NotSupportedException($"{nameof(options)} - {options.Platform}"); + } + + _options = options; + _verbosityLevel = verbosityLevel; + } + + /// + public async Task ProcessAsync() + { + if (_options.InstallDfuDrivers) + { + return Stm32Operations.InstallDfuDrivers(_verbosityLevel); + } + + if (_options.InstallJtagDrivers) + { + return Stm32Operations.InstallJtagDrivers(_verbosityLevel); + } + + if (_options.ListDevicesInDfuMode) + { + var connecteDevices = StmDfuDevice.ListDevices(); + + Console.ForegroundColor = ConsoleColor.Cyan; + + if (connecteDevices.Count() == 0) + { + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine("No DFU devices found"); + } + else + { + Console.WriteLine("-- Connected DFU devices --"); + + foreach ((string serial, string device) device in connecteDevices) + { + Console.WriteLine($"{device.serial} @ {device.device}"); + } + + Console.WriteLine("---------------------------"); + } + + Console.ForegroundColor = ConsoleColor.White; + + // done here, this command has no further processing + return ExitCodes.OK; + } + + if (_options.ListJtagDevices) + { + var connecteDevices = StmJtagDevice.ListDevices(); + + Console.ForegroundColor = ConsoleColor.Cyan; + + if (connecteDevices.Count == 0) + { + Console.WriteLine("No JTAG devices found"); + } + else + { + Console.WriteLine("-- Connected JTAG devices --"); + + foreach (string deviceId in connecteDevices) + { + Console.WriteLine(deviceId); + } + + Console.WriteLine("---------------------------"); + } + + Console.ForegroundColor = ConsoleColor.White; + + // done here, this command has no further processing + return ExitCodes.OK; + } + + var connectedStDfuDevices = StmDfuDevice.ListDevices(); + var connectedStJtagDevices = StmJtagDevice.ListDevices(); + bool updateAndDeploy = false; + + if (connectedStDfuDevices.Count != 0 && + (_options.BinFile.Any() || + _options.HexFile.Any())) + { + + #region STM32 DFU options + + var dfuDevice = new StmDfuDevice(_options.DfuDeviceId); + + if (!dfuDevice.DevicePresent) + { + // no JTAG device found + + // done here, this command has no further processing + return ExitCodes.E5001; + } + + if (_verbosityLevel >= VerbosityLevel.Normal) + { + Console.WriteLine($"Connected to JTAG device with ID {dfuDevice.DfuId}"); + } + + // set verbosity + dfuDevice.Verbosity = _verbosityLevel; + + // get mass erase option + dfuDevice.DoMassErase = _options.MassErase; + + if (_options.HexFile.Any()) + { + return dfuDevice.FlashHexFiles(_options.HexFile); + } + + if (_options.BinFile.Any()) + { + return dfuDevice.FlashBinFiles(_options.BinFile, _options.FlashAddress); + } + + #endregion + + } + else if (connectedStJtagDevices.Count != 0 && + (_options.BinFile.Any() || + _options.HexFile.Any())) + { + // this has to be a JTAG connected device + + #region STM32 JTAG options + + var jtagDevice = new StmJtagDevice(_options.JtagDeviceId); + + if (!jtagDevice.DevicePresent) + { + // no JTAG device found + + // done here, this command has no further processing + return ExitCodes.E5001; + } + + if (_verbosityLevel >= VerbosityLevel.Normal) + { + Console.WriteLine($"Connected to JTAG device with ID {jtagDevice.JtagId}"); + } + + // set verbosity + jtagDevice.Verbosity = _verbosityLevel; + + // get mass erase option + jtagDevice.DoMassErase = _options.MassErase; + + if (_options.HexFile.Any()) + { + return jtagDevice.FlashHexFiles(_options.HexFile); + } + + if (_options.BinFile.Any()) + { + return jtagDevice.FlashBinFiles(_options.BinFile, _options.FlashAddress); + } + + #endregion + } + else if (!string.IsNullOrEmpty(_options.TargetName)) + { + // update operation requested? + if (_options.Update) + { + // this to update the device with fw from CloudSmith + + // need to take care of flash address + string appFlashAddress = null; + + if (_options.FlashAddress.Any()) + { + // take the first address, it should be the only one valid + appFlashAddress = _options.FlashAddress.ElementAt(0); + } + + Interface updateInterface = Interface.None; + + if (_options.DfuUpdate && _options.JtagUpdate) + { + // can't select both JTAG and DFU simultaneously + return ExitCodes.E9000; + } + else if (_options.DfuUpdate) + { + updateInterface = Interface.Dfu; + } + else if (_options.JtagUpdate) + { + updateInterface = Interface.Jtag; + } + + var exitCode = await Stm32Operations.UpdateFirmwareAsync( + _options.TargetName, + _options.FwVersion, + _options.Preview, + true, + _options.DeploymentImage, + appFlashAddress, + _options.DfuDeviceId, + _options.JtagDeviceId, + !_options.FitCheck, + updateInterface, + _verbosityLevel); + + if (exitCode != ExitCodes.OK) + { + // done here + return exitCode; + } + + updateAndDeploy = true; + } + + // it's OK to deploy after a successful update + if (_options.Deploy) + { + // this to flash a deployment image without updating the firmware + + // need to take care of flash address + string appFlashAddress; + + if (_options.FlashAddress.Any()) + { + // take the first address, it should be the only one valid + appFlashAddress = _options.FlashAddress.ElementAt(0); + } + else + { + return ExitCodes.E9009; + } + + Interface updateInterface = Interface.None; + + if (_options.DfuUpdate && _options.JtagUpdate) + { + // can't select both JTAG and DFU simultaneously + return ExitCodes.E9000; + } + else if (_options.DfuUpdate) + { + updateInterface = Interface.Dfu; + } + else if (_options.JtagUpdate) + { + updateInterface = Interface.Jtag; + } + + var exitCode = await Stm32Operations.UpdateFirmwareAsync( + _options.TargetName, + null, + false, + false, + _options.DeploymentImage, + appFlashAddress, + _options.DfuDeviceId, + _options.JtagDeviceId, + !_options.FitCheck, + updateInterface, + _verbosityLevel); + + if (exitCode != ExitCodes.OK) + { + // done here + return exitCode; + } + + updateAndDeploy = true; + } + + // reset MCU requested? + if (_options.ResetMcu) + { + return Stm32Operations.ResetMcu( + _options.JtagDeviceId, + _verbosityLevel); + } + } + else if (_options.MassErase) + { + return Stm32Operations.MassErase( + _options.JtagDeviceId, + _verbosityLevel); + } + else if (_options.ResetMcu) + { + return Stm32Operations.ResetMcu( + _options.JtagDeviceId, + _verbosityLevel); + } + + if (!updateAndDeploy) + { + throw new NoOperationPerformedException(); + } + + return ExitCodes.OK; + } + } +} diff --git a/nanoFirmwareFlasher.Tool/TIManager.cs b/nanoFirmwareFlasher.Tool/TIManager.cs new file mode 100644 index 00000000..ed1d150b --- /dev/null +++ b/nanoFirmwareFlasher.Tool/TIManager.cs @@ -0,0 +1,133 @@ +// +// Copyright (c) .NET Foundation and Contributors +// See LICENSE file in the project root for full license information. +// + +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace nanoFramework.Tools.FirmwareFlasher +{ + /// + /// Class to manage different operations specific to the TI SimpleLink platform. + /// + public class TIManager : IManager + { + private readonly Options _options; + private readonly VerbosityLevel _verbosityLevel; + + public TIManager(Options options, VerbosityLevel verbosityLevel) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + if (options.Platform != SupportedPlatform.ti_simplelink) + { + throw new NotSupportedException($"{nameof(options)} - {options.Platform}"); + } + + _options = options; + _verbosityLevel = verbosityLevel; + } + + /// + public async Task ProcessAsync() + { + if (_options.TIInstallXdsDrivers) + { + return CC13x26x2Operations.InstallXds110Drivers(_verbosityLevel); + } + + bool updateAndDeploy = false; + + if (!string.IsNullOrEmpty(_options.TargetName)) + { + // update operation requested? + if (_options.Update) + { + // this to update the device with fw from Cloudsmith + + // need to take care of flash address + string appFlashAddress = null; + + if (_options.FlashAddress.Any()) + { + // take the first address, it should be the only one valid + appFlashAddress = _options.FlashAddress.ElementAt(0); + } + + var exitCode = await CC13x26x2Operations.UpdateFirmwareAsync( + _options.TargetName, + _options.FwVersion, + _options.Preview, + true, + _options.DeploymentImage, + appFlashAddress, + _verbosityLevel); + + if (exitCode != ExitCodes.OK) + { + // done here + return exitCode; + } + + updateAndDeploy = true; + } + + // it's OK to deploy after a successful update + if (_options.Deploy) + { + // this to flash a deployment image without updating the firmware + + // need to take care of flash address + string appFlashAddress = null; + + if (_options.FlashAddress.Any()) + { + // take the first address, it should be the only one valid + appFlashAddress = _options.FlashAddress.ElementAt(0); + } + else + { + return ExitCodes.E9009; + } + + var exitCode = await CC13x26x2Operations.UpdateFirmwareAsync( + _options.TargetName, + null, + false, + false, + _options.DeploymentImage, + appFlashAddress, + _verbosityLevel); + + if (exitCode != ExitCodes.OK) + { + // done here + return exitCode; + } + + updateAndDeploy = true; + } + + // reset MCU requested? + if (_options.ResetMcu) + { + // can't reset CC13x2 device without configuration file + // would require to specify the exact target name and then had to try parsing that + return ExitCodes.E9000; + } + } + + if (!updateAndDeploy) + { + throw new NoOperationPerformedException(); + } + + return ExitCodes.OK; + } + } +} diff --git a/nanoFirmwareFlasher.Tool/appsettings.json b/nanoFirmwareFlasher.Tool/appsettings.json new file mode 100644 index 00000000..d48501ab --- /dev/null +++ b/nanoFirmwareFlasher.Tool/appsettings.json @@ -0,0 +1,3 @@ +{ + "iConnectionString": "INSTRUMENT_KEY" +} diff --git a/nanoFirmwareFlasher.Tool/nanoFirmwareFlasher.Tool.csproj b/nanoFirmwareFlasher.Tool/nanoFirmwareFlasher.Tool.csproj index 32ff9837..cd689b21 100644 --- a/nanoFirmwareFlasher.Tool/nanoFirmwareFlasher.Tool.csproj +++ b/nanoFirmwareFlasher.Tool/nanoFirmwareFlasher.Tool.csproj @@ -18,17 +18,17 @@ .NET nanoFirmwareFlasher tool to flash firmware images to target devices. - latest true AnyCPU AnyCPU;x64 any true true + true - net6.0 + net8.0 net472 @@ -46,6 +46,7 @@ + @@ -68,6 +69,12 @@ + + + Always + + + @@ -76,8 +83,8 @@ - - net6.0\ + + net8.0\ diff --git a/nanoFirmwareFlasher.Tool/packages.lock.json b/nanoFirmwareFlasher.Tool/packages.lock.json index b9bd781b..d6c47f8a 100644 --- a/nanoFirmwareFlasher.Tool/packages.lock.json +++ b/nanoFirmwareFlasher.Tool/packages.lock.json @@ -1,7 +1,7 @@ { "version": 1, "dependencies": { - "net6.0": { + "net8.0": { "CommandLineParser": { "type": "Direct", "requested": "[2.9.1, )", @@ -14,6 +14,17 @@ "resolved": "4.7.0", "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" }, + "nanoFramework.Tools.Debugger.Net": { + "type": "Direct", + "requested": "[2.4.42, )", + "resolved": "2.4.42", + "contentHash": "3RWlCdTOkf0RDHRc+qFs0Di4QP9JLwaa3msLK1WT+XUeIaG45MJo9WLJ+tBS4rXcn0dHQSAKIP17q374WmaMag==", + "dependencies": { + "Polly": "7.2.3", + "PropertyChanged.Fody": "2.6.1", + "System.IO.Ports": "7.0.0" + } + }, "Nerdbank.GitVersioning": { "type": "Direct", "requested": "[3.6.133, )", @@ -25,6 +36,83 @@ "resolved": "4.2.1", "contentHash": "46t+e1eESclH4TkfqUEYAgZ1+ZbXSFS7GqSKvtcw6u2yT05heDKdzj1r3YA/6FCZM4uB4z7qRW9V+gBsCilonQ==" }, + "Microsoft.ApplicationInsights": { + "type": "Transitive", + "resolved": "2.22.0", + "contentHash": "3AOM9bZtku7RQwHyMEY3tQMrHIgjcfRDa6YQpd/QG2LDGvMydSlL9Di+8LLMt7J2RDdfJ7/2jdYv6yHcMJAnNw==", + "dependencies": { + "System.Diagnostics.DiagnosticSource": "5.0.0" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "McP+Lz/EKwvtCv48z0YImw+L1gi1gy5rHhNaNIY2CrjloV+XY8gydT8DjMR6zWeL13AFK+DioVpppwAuO1Gi1w==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Physical": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "C2wqUoh9OmRL1akaCcKSTmRU8z0kckfImG7zLNI8uyi47Lp+zd5LWAD17waPQEqCz3ioWOCrFUo+JJuoeZLOBw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "System.Text.Json": "8.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" + }, "Microsoft.NETCore.Platforms": { "type": "Transitive", "resolved": "1.1.1", @@ -45,16 +133,6 @@ "System.Runtime": "4.3.0" } }, - "nanoFramework.Tools.Debugger.Net": { - "type": "Transitive", - "resolved": "2.4.34", - "contentHash": "+QLH66C/dceiSlyWyfOEM5lOYZb13w5TjxvuB+ludaKTtD1IsZ4XAeXHen6P98eppgp3OLEus1GfMq7aGDj5Fw==", - "dependencies": { - "Polly": "7.2.3", - "PropertyChanged.Fody": "2.6.1", - "System.IO.Ports": "7.0.0" - } - }, "NETStandard.Library": { "type": "Transitive", "resolved": "1.6.1", @@ -345,15 +423,8 @@ }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - } + "resolved": "5.0.0", + "contentHash": "tCQTzPsGZh/A9LhhA6zrqCRV4hOHsK90/G7q3Khxmn6tnB1PuNU0cRaKANP2AWcF9bn0zsuOoZOSrHuJk6oNBA==" }, "System.Diagnostics.Tools": { "type": "Transitive", @@ -914,6 +985,19 @@ "System.Text.Encoding": "4.3.0" } }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "OdrZO2WjkiEG6ajEFRABTRCi/wuXQPxeV6g8xvUJqdxMvvuCCEk86zPla8UiIQJz3durtUEbNyY/3lIhS0yZvQ==", + "dependencies": { + "System.Text.Encodings.Web": "8.0.0" + } + }, "System.Text.RegularExpressions": { "type": "Transitive", "resolved": "4.3.0", @@ -1005,15 +1089,18 @@ "nanoFramework.Tools.FirmwareFlasher": { "type": "Project", "dependencies": { + "Microsoft.ApplicationInsights": "[2.22.0, )", + "Microsoft.Extensions.Configuration": "[8.0.0, )", + "Microsoft.Extensions.Configuration.Json": "[8.0.0, )", "Newtonsoft.Json": "[13.0.3, )", "System.ComponentModel.Annotations": "[5.0.0, )", "System.IO.Ports": "[7.0.0, )", "System.Net.Http": "[4.3.4, )", - "nanoFramework.Tools.Debugger.Net": "[2.4.34, )" + "nanoFramework.Tools.Debugger.Net": "[2.4.42, )" } } }, - "net6.0/any": { + "net8.0/any": { "runtime.any.System.Collections": { "type": "Transitive", "resolved": "4.3.0", @@ -1590,6 +1677,11 @@ "runtime.any.System.Text.Encoding.Extensions": "4.3.0" } }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" + }, "System.Threading.Tasks": { "type": "Transitive", "resolved": "4.3.0", diff --git a/update-esptool.ps1 b/update-esptool.ps1 index 4a437bd7..1168c431 100644 --- a/update-esptool.ps1 +++ b/update-esptool.ps1 @@ -64,9 +64,9 @@ Remove-Item -Path (Join-Path -Path $PSScriptRoot -ChildPath "lib\esptool\esptool # copy files to the correct locations "Copying files to tools folders..." | Write-Host -ForegroundColor White -NoNewline -Move-Item -Path (Join-Path -Path $env:TEMP -ChildPath "esptool-$version-win64\**" -Resolve) -Destination (Join-Path -Path $PSScriptRoot -ChildPath "lib\esptool\esptoolWin" -Resolve) -Force -Move-Item -Path (Join-Path -Path $env:TEMP -ChildPath "esptool-$version-macos\**" -Resolve) -Destination (Join-Path -Path $PSScriptRoot -ChildPath "lib\esptool\esptoolMac" -Resolve) -Force -Move-Item -Path (Join-Path -Path $env:TEMP -ChildPath "esptool-$version-linux-amd64\**" -Resolve) -Destination (Join-Path -Path $PSScriptRoot -ChildPath "lib\esptool\esptoolLinux" -Resolve) -Force +Move-Item -Path (Join-Path -Path $env:TEMP -ChildPath "esptool-win64\**" -Resolve) -Destination (Join-Path -Path $PSScriptRoot -ChildPath "lib\esptool\esptoolWin" -Resolve) -Force +Move-Item -Path (Join-Path -Path $env:TEMP -ChildPath "esptool-macos\**" -Resolve) -Destination (Join-Path -Path $PSScriptRoot -ChildPath "lib\esptool\esptoolMac" -Resolve) -Force +Move-Item -Path (Join-Path -Path $env:TEMP -ChildPath "esptool-linux-amd64\**" -Resolve) -Destination (Join-Path -Path $PSScriptRoot -ChildPath "lib\esptool\esptoolLinux" -Resolve) -Force "OK" | Write-Host -ForegroundColor Green # cleanup files diff --git a/update-jlink.ps1 b/update-jlink.ps1 index fe3a5a97..9967a79d 100644 --- a/update-jlink.ps1 +++ b/update-jlink.ps1 @@ -4,7 +4,7 @@ # this PS1 downloads the latest version of SEGGER J-Link from their website and unpacks it # version -$version = "766f" +$version = "792k" # make sure security doesn't block our request [Net.ServicePointManager]::SecurityProtocol = "tls12, tls11"