From 364a5b6a63bf5d3711218481fdc1ad124166f7cf Mon Sep 17 00:00:00 2001 From: Nerivec <62446222+Nerivec@users.noreply.github.com> Date: Wed, 6 Nov 2024 02:06:33 +0100 Subject: [PATCH] Improve bootloader reliability over TCP --- README.md | 12 ++++---- package.json | 2 +- src/commands/bootloader/index.ts | 2 +- src/utils/bootloader.ts | 47 ++++++++++++++++++++------------ 4 files changed, 38 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index da02329..35495f9 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ $ npm install -g ember-zli $ ember-zli COMMAND running command... $ ember-zli (--version) -ember-zli/2.8.1 win32-x64 node-v22.11.0 +ember-zli/2.8.2 win32-x64 node-v22.11.0 $ ember-zli --help [COMMAND] USAGE $ ember-zli COMMAND @@ -60,7 +60,7 @@ EXAMPLES $ ember-zli bootloader ``` -_See code: [src/commands/bootloader/index.ts](https://github.com/Nerivec/ember-zli/blob/v2.8.1/src/commands/bootloader/index.ts)_ +_See code: [src/commands/bootloader/index.ts](https://github.com/Nerivec/ember-zli/blob/v2.8.2/src/commands/bootloader/index.ts)_ ## `ember-zli help [COMMAND]` @@ -97,7 +97,7 @@ EXAMPLES $ ember-zli router ``` -_See code: [src/commands/router/index.ts](https://github.com/Nerivec/ember-zli/blob/v2.8.1/src/commands/router/index.ts)_ +_See code: [src/commands/router/index.ts](https://github.com/Nerivec/ember-zli/blob/v2.8.2/src/commands/router/index.ts)_ ## `ember-zli sniff` @@ -114,7 +114,7 @@ EXAMPLES $ ember-zli sniff ``` -_See code: [src/commands/sniff/index.ts](https://github.com/Nerivec/ember-zli/blob/v2.8.1/src/commands/sniff/index.ts)_ +_See code: [src/commands/sniff/index.ts](https://github.com/Nerivec/ember-zli/blob/v2.8.2/src/commands/sniff/index.ts)_ ## `ember-zli stack` @@ -131,7 +131,7 @@ EXAMPLES $ ember-zli stack ``` -_See code: [src/commands/stack/index.ts](https://github.com/Nerivec/ember-zli/blob/v2.8.1/src/commands/stack/index.ts)_ +_See code: [src/commands/stack/index.ts](https://github.com/Nerivec/ember-zli/blob/v2.8.2/src/commands/stack/index.ts)_ ## `ember-zli utils` @@ -148,7 +148,7 @@ EXAMPLES $ ember-zli utils ``` -_See code: [src/commands/utils/index.ts](https://github.com/Nerivec/ember-zli/blob/v2.8.1/src/commands/utils/index.ts)_ +_See code: [src/commands/utils/index.ts](https://github.com/Nerivec/ember-zli/blob/v2.8.2/src/commands/utils/index.ts)_ ## `ember-zli version` diff --git a/package.json b/package.json index 0384903..a94599f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ember-zli", "description": "Interact with EmberZNet-based adapters using zigbee-herdsman 'ember' driver", - "version": "2.8.1", + "version": "2.8.2", "author": "Nerivec", "bin": { "ember-zli": "bin/run.js" diff --git a/src/commands/bootloader/index.ts b/src/commands/bootloader/index.ts index 08d822a..be62697 100644 --- a/src/commands/bootloader/index.ts +++ b/src/commands/bootloader/index.ts @@ -100,7 +100,7 @@ export default class Bootloader extends Command { { name: 'Get info', value: BootloaderMenu.INFO }, { name: 'Update firmware', value: BootloaderMenu.UPLOAD_GBL }, { name: 'Clear NVM3', value: BootloaderMenu.CLEAR_NVM3, disabled: !this.supportsClearNVM3(gecko.adapterModel) }, - { name: 'Exit bootloader', value: BootloaderMenu.RUN }, + { name: 'Exit bootloader (run firmware)', value: BootloaderMenu.RUN }, { name: 'Force close', value: -1 }, ], message: 'Menu', diff --git a/src/utils/bootloader.ts b/src/utils/bootloader.ts index 70de814..aff7e51 100644 --- a/src/utils/bootloader.ts +++ b/src/utils/bootloader.ts @@ -45,17 +45,22 @@ export enum BootloaderMenu { const CARRIAGE_RETURN = 0x0d const NEWLINE = 0x0a + const BOOTLOADER_KNOCK = Buffer.from([NEWLINE]) +const BOOTLOADER_MENU_UPLOAD_GBL = Buffer.from([BootloaderMenu.UPLOAD_GBL]) +const BOOTLOADER_MENU_RUN = Buffer.from([BootloaderMenu.RUN]) +const BOOTLOADER_MENU_INFO = Buffer.from([BootloaderMenu.INFO]) + const BOOTLOADER_PROMPT = Buffer.from('BL >', 'ascii') -const BOOTLOADER_INFO = Buffer.from('Bootloader v', 'ascii') +const BOOTLOADER_VERSION = Buffer.from('Bootloader v', 'ascii') const BOOTLOADER_BEGIN_UPLOAD = Buffer.from('begin upload', 'ascii') const BOOTLOADER_UPLOAD_COMPLETE = Buffer.from('Serial upload complete', 'ascii') const BOOTLOADER_UPLOAD_ABORTED = Buffer.from('Serial upload aborted', 'ascii') -const BOOTLOADER_KNOCK_TIMEOUT = 2000 -const BOOTLOADER_UPLOAD_TIMEOUT = 1500000 -const BOOTLOADER_UPLOAD_EXIT_TIMEOUT = 500 -const BOOTLOADER_CMD_EXEC_TIMEOUT = 200 +const BOOTLOADER_KNOCK_TIMEOUT = 1500 +const BOOTLOADER_UPLOAD_TIMEOUT = 1800000 +const BOOTLOADER_UPLOAD_EXIT_TIMEOUT = 1500 +const BOOTLOADER_CMD_EXEC_TIMEOUT = 500 const GBL_START_TAG = Buffer.from([0xeb, 0x17, 0xa6, 0x03]) /** Contains length+CRC32 and possibly padding after this. */ @@ -363,7 +368,7 @@ export class GeckoBootloader extends EventEmitter { } private async knock(fail: boolean): Promise { - logger.debug(`Knocking...`, NS) + logger.info(fail ? `Entering bootloader...` : `Trying to enter bootloader...`, NS) try { await this.transport.initPort() @@ -376,7 +381,7 @@ export class GeckoBootloader extends EventEmitter { (await confirm({ message: 'Force reset into bootloader?', default: true })) if (forceReset) { - logger.debug(`Entering bootloader via force reset.`, NS) + logger.debug(`Entering bootloader via force reset...`, NS) await this.forceReset(false) @@ -401,7 +406,9 @@ export class GeckoBootloader extends EventEmitter { res = await this.waitForState(BootloaderState.IDLE, BOOTLOADER_KNOCK_TIMEOUT, fail && i == 2) - if (!res && i == 1 && this.transport.isSerial) { + if (res) { + break + } else if (i == 1 && this.transport.isSerial) { // if failed first attempt, try second time with RTS/CTS enabled await this.transport.serialSet({ rts: true, cts: true }) } @@ -423,7 +430,7 @@ export class GeckoBootloader extends EventEmitter { this.state = BootloaderState.GETTING_INFO - await this.transport.write(Buffer.from([BootloaderMenu.INFO])) + await this.transport.write(BOOTLOADER_MENU_INFO) await this.waitForState(BootloaderState.GOT_INFO, BOOTLOADER_CMD_EXEC_TIMEOUT) @@ -435,9 +442,9 @@ export class GeckoBootloader extends EventEmitter { this.state = BootloaderState.RUNNING - await this.transport.write(Buffer.from([BootloaderMenu.RUN])) + await this.transport.write(BOOTLOADER_MENU_RUN) - const res = await this.waitForState(BootloaderState.IDLE, BOOTLOADER_CMD_EXEC_TIMEOUT, false) + const res = await this.waitForState(BootloaderState.IDLE, BOOTLOADER_CMD_EXEC_TIMEOUT, false, true) if (res) { // got menu back, failed to run @@ -456,7 +463,7 @@ export class GeckoBootloader extends EventEmitter { this.state = BootloaderState.BEGIN_UPLOAD - await this.transport.write(Buffer.from([BootloaderMenu.UPLOAD_GBL])) // start upload + await this.transport.write(BOOTLOADER_MENU_UPLOAD_GBL) // start upload await this.waitForState(BootloaderState.UPLOADING, BOOTLOADER_UPLOAD_EXIT_TIMEOUT) await this.waitForState(BootloaderState.UPLOADED, BOOTLOADER_UPLOAD_TIMEOUT) @@ -505,7 +512,10 @@ export class GeckoBootloader extends EventEmitter { logger.error(`Firmware upload aborted.`, NS) } else if (received.includes(BOOTLOADER_UPLOAD_COMPLETE)) { logger.info(`Firmware upload completed.`, NS) - } else if (received.includes(BOOTLOADER_PROMPT)) { + } + + // always check if got back prompt already (can be in same tx as above) + if (received.includes(BOOTLOADER_PROMPT)) { this.resolveState(BootloaderState.IDLE) } @@ -513,7 +523,7 @@ export class GeckoBootloader extends EventEmitter { } case BootloaderState.RUNNING: { - const blv = received.indexOf(BOOTLOADER_INFO) + const blv = received.indexOf(BOOTLOADER_VERSION) if (blv !== -1) { const [blInfo] = this.readBootloaderInfo(received, blv) @@ -527,7 +537,7 @@ export class GeckoBootloader extends EventEmitter { } case BootloaderState.GETTING_INFO: { - const blv = received.indexOf(BOOTLOADER_INFO) + const blv = received.indexOf(BOOTLOADER_VERSION) if (blv !== -1) { this.resolveState(BootloaderState.GOT_INFO) @@ -621,7 +631,7 @@ export class GeckoBootloader extends EventEmitter { this.state = state } - private waitForState(state: BootloaderState, timeout: number = 5000, fail: boolean = true): Promise { + private waitForState(state: BootloaderState, timeout: number = 5000, fail: boolean = true, expectingFail: boolean = false): Promise { return new Promise((resolve) => { this.waiter = { resolve, @@ -635,7 +645,10 @@ export class GeckoBootloader extends EventEmitter { return } - logger.debug(msg, NS) + if (!expectingFail) { + logger.debug(msg, NS) + } + resolve(false) this.waiter = undefined }, timeout),