-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: improve the options and output of terminal (#1962)
- improve output so CR always print as NL - change --port to --path - change --echo to --no-echo (default is to echo) - fix double echo output when selecting the port from a list
- Loading branch information
Showing
3 changed files
with
88 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,116 +1,107 @@ | ||
#!/usr/bin/env node | ||
|
||
// #!/usr/bin/env node | ||
const { Select } = require('enquirer') | ||
const args = require('commander') | ||
const SerialPort = require('serialport') | ||
const { version } = require('../package.json') | ||
const args = require('commander') | ||
const PromptList = require('prompt-list') | ||
const { OutputTranslator } = require('./output-translator') | ||
|
||
function makeNumber(input) { | ||
return Number(input) | ||
} | ||
const makeNumber = input => Number(input) | ||
|
||
args | ||
.version(version) | ||
.usage('[options]') | ||
.description('A basic terminal interface for communicating over a serial port. Pressing ctrl+c exits.') | ||
.option('-l --list', 'List available ports then exit') | ||
.option('-p, --port <port>', 'Path or Name of serial port') | ||
.option('-p, --path <path>', 'Path of the serial port') | ||
.option('-b, --baud <baudrate>', 'Baud rate default: 9600', makeNumber, 9600) | ||
.option('--databits <databits>', 'Data bits default: 8', makeNumber, 8) | ||
.option('--parity <parity>', 'Parity default: none', 'none') | ||
.option('--stopbits <bits>', 'Stop bits default: 1', makeNumber, 1) | ||
// TODO make this on by default | ||
.option('--echo --localecho', 'Print characters as you type them.') | ||
.option('--no-echo', "Don't print characters as you type them.") | ||
.parse(process.argv) | ||
|
||
function logErrorAndExit(error) { | ||
console.error(error) | ||
process.exit(1) | ||
} | ||
|
||
function listPorts() { | ||
SerialPort.list().then( | ||
ports => { | ||
ports.forEach(port => { | ||
console.log(`${port.path}\t${port.pnpId || ''}\t${port.manufacturer || ''}`) | ||
}) | ||
}, | ||
err => { | ||
console.error('Error listing ports', err) | ||
} | ||
) | ||
const listPorts = async () => { | ||
const ports = await SerialPort.list() | ||
for (port of ports) { | ||
console.log(`${port.path}\t${port.pnpId || ''}\t${port.manufacturer || ''}`) | ||
} | ||
} | ||
|
||
function askForPort() { | ||
return SerialPort.list().then(ports => { | ||
if (ports.length === 0) { | ||
console.error('No ports detected and none specified') | ||
process.exit(2) | ||
} | ||
|
||
const portSelection = new PromptList({ | ||
name: 'serial-port-selection', | ||
message: 'Select a serial port to open', | ||
choices: ports.map((port, i) => ({ | ||
value: `[${i + 1}]\t${port.path}\t${port.pnpId || ''}\t${port.manufacturer || ''}`, | ||
name: port.path, | ||
})), | ||
validate: Boolean, // ensure we picked something | ||
}) | ||
const askForPort = async () => { | ||
const ports = await SerialPort.list() | ||
if (ports.length === 0) { | ||
console.error('No ports detected and none specified') | ||
process.exit(2) | ||
} | ||
|
||
return portSelection.run().then(answer => { | ||
console.log(`Opening serial port: ${answer}`) | ||
return answer | ||
}) | ||
}) | ||
const answer = await new Select({ | ||
name: 'serial-port-selection', | ||
message: 'Select a serial port to open', | ||
choices: ports.map((port, i) => ({ | ||
value: `[${i + 1}]\t${port.path}\t${port.pnpId || ''}\t${port.manufacturer || ''}`, | ||
name: port.path, | ||
})), | ||
required: true, | ||
}).run() | ||
return answer | ||
} | ||
|
||
function createPort(selectedPort) { | ||
const createPort = path => { | ||
console.log(`Opening serial port: ${path} echo: ${args.echo}`) | ||
|
||
const openOptions = { | ||
baudRate: args.baud, | ||
dataBits: args.databits, | ||
parity: args.parity, | ||
stopBits: args.stopbits, | ||
} | ||
|
||
const port = new SerialPort(selectedPort, openOptions) | ||
|
||
process.stdin.resume() | ||
process.stdin.setRawMode(true) | ||
process.stdin.on('data', s => { | ||
if (s[0] === 0x03) { | ||
port.close() | ||
process.exit(0) | ||
} | ||
if (args.localecho) { | ||
if (s[0] === 0x0d) { | ||
process.stdout.write('\n') | ||
} else { | ||
process.stdout.write(s) | ||
} | ||
} | ||
port.write(s) | ||
}) | ||
|
||
port.on('data', data => { | ||
process.stdout.write(data.toString()) | ||
}) | ||
const port = new SerialPort(path, openOptions) | ||
const output = new OutputTranslator() | ||
output.pipe(process.stdout) | ||
port.pipe(output) | ||
|
||
port.on('error', err => { | ||
console.log('Error', err) | ||
console.error('Error', err) | ||
process.exit(1) | ||
}) | ||
|
||
port.on('close', err => { | ||
console.log('Closed', err) | ||
process.exit(err ? 1 : 0) | ||
}) | ||
process.stdin.setRawMode(true) | ||
process.stdin.on('data', input => { | ||
for (const byte of input) { | ||
// ctrl+c | ||
if (byte === 0x03) { | ||
port.close() | ||
process.exit(0) | ||
} | ||
} | ||
port.write(input) | ||
if (args.echo) { | ||
output.write(input) | ||
} | ||
}) | ||
process.stdin.resume() | ||
|
||
process.stdin.on('end', () => { | ||
port.close() | ||
process.exit(0) | ||
}) | ||
} | ||
|
||
if (args.list) { | ||
listPorts() | ||
} else { | ||
Promise.resolve(args.port || askForPort()) | ||
.then(createPort) | ||
.catch(logErrorAndExit) | ||
const run = async () => { | ||
if (args.list) { | ||
listPorts() | ||
return | ||
} | ||
const path = args.path || (await askForPort()) | ||
await createPort(path) | ||
} | ||
|
||
run().catch(error => { | ||
console.error(error) | ||
process.exit(1) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
const { Transform } = require('stream') | ||
|
||
/** | ||
* Convert carriage returns to newlines for output | ||
*/ | ||
class OutputTranslator extends Transform { | ||
_transform(chunk, _encoding, cb) { | ||
for (let index = 0; index < chunk.length; index++) { | ||
const byte = chunk[index] | ||
if (byte === 0x0d) { | ||
chunk[index] = 0x0a | ||
} | ||
} | ||
this.push(chunk) | ||
cb() | ||
} | ||
} | ||
module.exports.OutputTranslator = OutputTranslator |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters