diff --git a/src/rawmode.js b/src/rawmode.js index 5fec9ce..6dd711a 100644 --- a/src/rawmode.js +++ b/src/rawmode.js @@ -8,6 +8,8 @@ import { report } from "./utils" +let replPrompts = [] + export class MpRawMode { constructor(port) { this.port = port @@ -15,6 +17,7 @@ export class MpRawMode { static async begin(port, soft_reboot=false) { const res = new MpRawMode(port) + await res.detectPrompt() await res.enterRawRepl(soft_reboot) try { await res.exec(`import sys,os`) @@ -25,13 +28,43 @@ export class MpRawMode { return res } + async detectPrompt() { + const release = await this.port.startTransaction() + + // >>> is the standard MicroPython prompt + // --> is the aiorepl prompt + replPrompts = ['>>> ', '--> '] + + try { + await this.port.write('\r\x01') // Ctrl-A: enter raw REPL + await this.port.readUntil('raw REPL; CTRL-B to exit\r\n') + // detect what the system prompt is and add it to our list of prompts to look for + const userPrompt = `${(await this.exec('import sys; print(sys.ps1)')).trim()} ` + if (!replPrompts.includes(userPrompt)) { + console.log(`Detected user prompt as ${userPrompt}`) + replPrompts.push(userPrompt) + } + await this.port.write('\x02') // Ctrl-B: exit raw REPL + await this.port.readUntil('>\r\n') + let activePrompt = await this.port.readUntil(replPrompts) + // this is a hack because in the other cases, the prompt gets printed agian, but in the + // aiorepl case, it doesn't. Not sure why at the moment so I'm doing this + if (activePrompt == '--> ') { + await this.port.write(activePrompt) + } + } finally { + release() + } + } + async interruptProgram(timeout=20000) { const endTime = Date.now() + timeout while (timeout <= 0 || (Date.now() < endTime)) { await this.port.write('\x03') // Ctrl-C: interrupt any running program try { - let banner = await this.port.readUntil('>>> ', 500) - if (this.port.prevRecvCbk && banner != '\r\n>>> ') { + let banner = await this.port.readUntil(replPrompts, 500) + let promptRegex = RegExp(`\r\n(?:${replPrompts.join('|')})`) + if (this.port.prevRecvCbk && !promptRegex.test(banner)) { this.port.prevRecvCbk(banner) } await this.port.flushInput() @@ -60,7 +93,7 @@ export class MpRawMode { try { await this.port.write('\x02') // Ctrl-B: exit raw REPL await this.port.readUntil('>\r\n') - await this.port.readUntil('>>> ') + await this.port.readUntil(replPrompts) } finally { release() } diff --git a/src/transports.js b/src/transports.js index 7a8da4e..72bd3e3 100644 --- a/src/transports.js +++ b/src/transports.js @@ -127,17 +127,24 @@ export class Transport { throw new Error('Timeout') } - async readUntil(ending, timeout=5000) { + async readUntil(endings, timeout=5000) { + let err_message = 'any of the ending sequences' + if (!Array.isArray(endings)) { + endings = [endings] + err_message = 'the ending sequence' + } if (!this.inTransaction) { throw new Error('Not in transaction') } let endTime = Date.now() + timeout while (timeout <= 0 || (Date.now() < endTime)) { - const idx = this.receivedData.indexOf(ending) + ending.length - if (idx >= ending.length) { - const res = this.receivedData.substring(0, idx) - this.receivedData = this.receivedData.substring(idx) - return res + for (let ending of endings) { + const idx = this.receivedData.indexOf(ending) + ending.length + if (idx >= ending.length) { + const res = this.receivedData.substring(0, idx) + this.receivedData = this.receivedData.substring(idx) + return res + } } const prev_avail = this.receivedData.length await sleep(10) @@ -145,7 +152,7 @@ export class Transport { endTime = Date.now() + timeout } } - throw new Error('Timeout reached before finding the ending sequence') + throw new Error(`Timeout reached before finding ${err_message}`) } }