diff --git a/.gitignore b/.gitignore index b947077..b670259 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,236 @@ +# Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos,node,visualstudiocode +# Edit at https://www.toptal.com/developers/gitignore?templates=windows,linux,macos,node,visualstudiocode + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories node_modules/ -dist/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +### Node Patch ### +# Serverless Webpack directories +.webpack/ + +# Optional stylelint cache + +# SvelteKit build / generate output +.svelte-kit + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,node,visualstudiocode diff --git a/README.md b/README.md index aea3d70..d46755a 100644 --- a/README.md +++ b/README.md @@ -82,11 +82,11 @@ Replace `uno.ts` with the appropriate example file and `/dev/ttyACM0` with your The main class `STK500` provides the following methods: - `constructor(stream: NodeJS.ReadWriteStream, board: Board, opts?: STK500Options)` -- `bootload(hexData: string | Buffer): Promise` +- `bootload(hexData: string | Buffer, progressCallback?: BootloadProgressCallback): Promise` - `sync(attempts: number): Promise` - `verifySignature(): Promise` -- `upload(hexData: string | Buffer): Promise` -- `verify(hexData: string | Buffer): Promise` +- `upload(hexData: string | Buffer, progressCallback?: (percentage: number) => void): Promise` +- `verify(hexData: string | Buffer, progressCallback?: (percentage: number) => void): Promise` For more detailed API information, please refer to the TypeScript definitions or the source code. diff --git a/package-lock.json b/package-lock.json index ea4bfa6..2bda50e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "stk500-esm", - "version": "1.2.0", + "version": "1.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "stk500-esm", - "version": "1.2.0", + "version": "1.3.0", "license": "MIT", "devDependencies": { "@eslint/js": "^9.12.0", diff --git a/package.json b/package.json index 0d7d1b6..07cc6cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "stk500-esm", - "version": "1.2.0", + "version": "1.3.0", "description": "A modern, ESM-compatible, TypeScript implementation of the STK500v1 protocol for programming Arduino boards directly from Node.js or the browser.", "main": "dist/index.js", "module": "dist/index.js", diff --git a/src/index.ts b/src/index.ts index 3479007..a6f0f4e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -28,6 +28,14 @@ interface STK500Options { quiet?: boolean; } +/** + * A callback function for reporting bootloading progress. + * @param status - The current status of the bootloading process. + * @param percentage - The percentage of the bootloading process that is complete. + * @returns void + */ +type BootloadProgressCallback = (status: string, percentage: number) => void; + class STK500 { private log: (...data: unknown[]) => void; private stream: NodeJS.ReadWriteStream; @@ -234,9 +242,13 @@ class STK500 { /** * Uploads the provided hex data to the device. * @param hexData - The hex data to be uploaded, as a string or buffer. + * @param progressCallback - Optional callback to report upload progress. * @returns A promise that resolves when the upload is complete. */ - async upload(hexData: string | Buffer): Promise { + async upload( + hexData: string | Buffer, + progressCallback?: (percentage: number) => void + ): Promise { this.log("program"); const { data: hex } = parseIntelHex(hexData); @@ -268,6 +280,10 @@ class STK500 { await new Promise((resolve) => setTimeout(resolve, 4)); this.log("page done"); + + if (progressCallback) { + progressCallback((pageaddr / hex.length) * 100); + } } catch (error) { this.log("Error in page programming"); throw error; @@ -296,9 +312,13 @@ class STK500 { /** * Verifies the uploaded data against the provided hex data. * @param hexData - The hex data to verify against, as a string or buffer. + * @param progressCallback - Optional callback to report verification progress. * @returns A promise that resolves when verification is complete. */ - async verify(hexData: string | Buffer): Promise { + async verify( + hexData: string | Buffer, + progressCallback?: (percentage: number) => void + ): Promise { this.log("verify"); const { data: hex } = parseIntelHex(hexData); @@ -330,6 +350,10 @@ class STK500 { await new Promise((resolve) => setTimeout(resolve, 4)); this.log("verify done"); + + if (progressCallback) { + progressCallback((pageaddr / hex.length) * 100); + } } catch (error) { this.log("Error in page verification"); throw error; @@ -375,24 +399,45 @@ class STK500 { /** * Performs the complete bootloading process for a device. * @param hexData - The hex data to be uploaded, as a string or buffer. + * @param progressCallback - Optional callback to report progress. * @returns A promise that resolves when the bootloading process is complete. */ - async bootload(hexData: string | Buffer): Promise { - // TODO: Are these calcs based on board.pageSize okay? Not really sure + async bootload( + hexData: string | Buffer, + progressCallback?: BootloadProgressCallback + ): Promise { const parameters = { pagesizehigh: (this.board.pageSize << 8) & 0xff, pagesizelow: this.board.pageSize & 0xff, }; + const updateProgress = (status: string, percentage: number) => { + if (progressCallback) { + progressCallback(status, percentage); + } + }; + + updateProgress("Syncing", 0); await this.sync(3); await this.sync(3); await this.sync(3); + updateProgress("Verifying signature", 10); await this.verifySignature(); + updateProgress("Setting options", 20); await this.setOptions(parameters); + updateProgress("Entering programming mode", 30); await this.enterProgrammingMode(); - await this.upload(hexData); - await this.verify(hexData); + updateProgress("Uploading", 40); + await this.upload(hexData, (uploadPercentage) => { + updateProgress("Uploading", 40 + uploadPercentage * 0.3); + }); + updateProgress("Verifying", 70); + await this.verify(hexData, (verifyPercentage) => { + updateProgress("Verifying", 70 + verifyPercentage * 0.25); + }); + updateProgress("Exiting programming mode", 95); await this.exitProgrammingMode(); + updateProgress("Complete", 100); } }