mirror of
https://github.com/node-red/node-red-nodes.git
synced 2025-03-01 10:37:43 +00:00
node-red-node-serialport port selection node addition (#1035)
* port select
This commit is contained in:
parent
81501dfbdf
commit
2ddb6037c6
@ -100,6 +100,10 @@
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-template-name="serial-port">
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
|
||||
<input type="text" id="node-config-input-name" data-i18n="[placeholder]node-red:common.label.name">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-serialport"><i class="fa fa-random"></i> <span data-i18n="serial.label.serialport"></span></label>
|
||||
<input type="text" id="node-config-input-serialport" style="width:66%;" data-i18n="[placeholder]serial.placeholder.serialport">
|
||||
@ -230,7 +234,7 @@
|
||||
RED.nodes.registerType('serial-port',{
|
||||
category: 'config',
|
||||
defaults: {
|
||||
//name: {value:""},
|
||||
name: {value:""},
|
||||
serialport: {value:"",required:true},
|
||||
serialbaud: {value:"57600",required:true,validate:RED.validators.number()},
|
||||
databits: {value:8,required:true},
|
||||
@ -252,7 +256,7 @@
|
||||
this.databits = this.databits || 8;
|
||||
this.parity = this.parity || this._("serial.label.none");
|
||||
this.stopbits = this.stopbits || 1;
|
||||
return this.serialport+":"+this.serialbaud+"-"+this.databits+this.parity.charAt(0).toUpperCase()+this.stopbits;
|
||||
return this.name||(this.serialport+":"+this.serialbaud+"-"+this.databits+this.parity.charAt(0).toUpperCase()+this.stopbits);
|
||||
},
|
||||
oneditprepare: function() {
|
||||
var previous = null;
|
||||
@ -361,3 +365,37 @@
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-template-name="serial control">
|
||||
<div class="form-row node-input-serial">
|
||||
<label for="node-input-serial"><i class="fa fa-random"></i> <span data-i18n="serial.label.serialport"></span></label>
|
||||
<input type="text" id="node-input-serial">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-inputoutput-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
|
||||
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
RED.nodes.registerType('serial control',{
|
||||
category: 'network',
|
||||
defaults: {
|
||||
name: {name:""},
|
||||
serial: {type:"serial-port",required:true}
|
||||
},
|
||||
color:"BurlyWood",
|
||||
inputs:1,
|
||||
outputs:1,
|
||||
icon: "serial.png",
|
||||
align: "left",
|
||||
label: function() {
|
||||
var serialNode = RED.nodes.node(this.serial);
|
||||
return this.name||(serialNode?serialNode.label().split(":")[0]:this._("serial.label.serial"));
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
outputLabels: function() { return RED.nodes.node(this.serial).bin === "bin" ? "buffer" : "string"; }
|
||||
});
|
||||
</script>
|
@ -25,21 +25,46 @@ module.exports = function(RED) {
|
||||
this.dsr = n.dsr || "none";
|
||||
this.bin = n.bin || "false";
|
||||
this.out = n.out || "char";
|
||||
this.enable = n.enable || true;
|
||||
this.waitfor = n.waitfor || "";
|
||||
this.responsetimeout = n.responsetimeout || 10000;
|
||||
}
|
||||
RED.nodes.registerType("serial-port",SerialPortNode);
|
||||
|
||||
this.changePort = (serialPort) => {
|
||||
serialPool.close(this.serialport,() => {});
|
||||
this.serialport = serialPort.serialport || this.serialport;
|
||||
this.serialbaud = parseInt(serialPort.serialbaud) || this.serialbaud;
|
||||
this.databits = parseInt(serialPort.databits) || this.databits;
|
||||
this.parity = serialPort.parity || this.parity;
|
||||
this.stopbits = parseInt(serialPort.stopbits) || this.stopbits;
|
||||
this.dtr = serialPort.dtr || this.dtr;
|
||||
this.rts = serialPort.rts || this.rts;
|
||||
this.cts = serialPort.cts || this.cts;
|
||||
this.dsr = serialPort.dsr || this.dsr;
|
||||
this.bin = serialPort.bin || this.bin;
|
||||
this.out = serialPort.out || this.out;
|
||||
}
|
||||
|
||||
};
|
||||
RED.nodes.registerType("serial-port",SerialPortNode);
|
||||
|
||||
// receives msgs and sends them to the serial port
|
||||
function SerialOutNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.serial = n.serial;
|
||||
this.serialConfig = RED.nodes.getNode(this.serial);
|
||||
this.serialConfig = RED.nodes.getNode(n.serial);
|
||||
|
||||
if (this.serialConfig) {
|
||||
if (!this.serialConfig) {
|
||||
this.error(RED._("serial.errors.missing-conf"), {});
|
||||
return;
|
||||
}
|
||||
|
||||
this.serial = n.serial;
|
||||
var node = this;
|
||||
node.port = serialPool.get(this.serialConfig);
|
||||
var serialConfig = this.serialConfig;
|
||||
|
||||
this.serialConfig.on('start', function() {
|
||||
node.port = serialPool.get(serialConfig);
|
||||
});
|
||||
|
||||
node.on("input",function(msg) {
|
||||
if (msg.hasOwnProperty("baudrate")) {
|
||||
@ -71,55 +96,44 @@ module.exports = function(RED) {
|
||||
node.status({fill:"red",shape:"ring",text:"node-red:common.status.not-connected"});
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.error(RED._("serial.errors.missing-conf"), {});
|
||||
}
|
||||
|
||||
this.on("close", function(done) {
|
||||
if (this.serialConfig) {
|
||||
serialPool.close(this.serialConfig.serialport,done);
|
||||
}
|
||||
else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("serial out",SerialOutNode);
|
||||
|
||||
|
||||
// receives data from the serial port and emits msgs
|
||||
function SerialInNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.serial = n.serial;
|
||||
this.serialConfig = RED.nodes.getNode(this.serial);
|
||||
this.serialConfig = RED.nodes.getNode(n.serial);
|
||||
|
||||
if (this.serialConfig) {
|
||||
var node = this;
|
||||
node.status({fill:"grey",shape:"dot",text:"node-red:common.status.not-connected"});
|
||||
node.port = serialPool.get(this.serialConfig);
|
||||
|
||||
this.port.on('data', function(msgout) {
|
||||
node.send(msgout);
|
||||
});
|
||||
this.port.on('ready', function() {
|
||||
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
|
||||
});
|
||||
this.port.on('closed', function() {
|
||||
node.status({fill:"red",shape:"ring",text:"node-red:common.status.not-connected"});
|
||||
});
|
||||
}
|
||||
else {
|
||||
if (!this.serialConfig) {
|
||||
this.error(RED._("serial.errors.missing-conf"), {});
|
||||
return;
|
||||
}
|
||||
|
||||
this.serial = n.serial;
|
||||
var node = this;
|
||||
|
||||
this.serialConfig.on('start', function() {
|
||||
setCallback(node, node.serialConfig);
|
||||
});
|
||||
|
||||
this.on("close", function(done) {
|
||||
if (this.serialConfig) {
|
||||
serialPool.close(this.serialConfig.serialport,done);
|
||||
}
|
||||
else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
function setCallback(node) {
|
||||
node.status({fill:"grey",shape:"dot",text:"node-red:common.status.not-connected"});
|
||||
node.port = serialPool.get(node.serialConfig);
|
||||
|
||||
node.port.on('data', function(msgout) {
|
||||
node.send(msgout);
|
||||
});
|
||||
node.port.on('ready', function() {
|
||||
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
|
||||
});
|
||||
node.port.on('closed', function() {
|
||||
node.status({fill:"red",shape:"ring",text:"node-red:common.status.not-connected"});
|
||||
});
|
||||
};
|
||||
setCallback(node)
|
||||
}
|
||||
RED.nodes.registerType("serial in",SerialInNode);
|
||||
|
||||
@ -162,28 +176,36 @@ module.exports = function(RED) {
|
||||
});
|
||||
});
|
||||
|
||||
let serialConfig = this.serialConfig;
|
||||
serialConfig.on('start', function() {
|
||||
node.port = serialPool.get(serialConfig);
|
||||
setCallback(node);
|
||||
});
|
||||
|
||||
// Serial In
|
||||
this.port.on('data', function(msgout, sender) {
|
||||
function setCallback(node) {
|
||||
node.port.on('data', function (msgout, sender) {
|
||||
// serial request will only process incoming data pertaining to its own request (i.e. when it's at the head of the queue)
|
||||
if (sender !== node) { return; }
|
||||
node.status({fill:"green",shape:"dot",text:"node-red:common.status.ok"});
|
||||
node.status({ fill: "green", shape: "dot", text: "node-red:common.status.ok" });
|
||||
msgout.status = "OK";
|
||||
node.send(msgout);
|
||||
});
|
||||
this.port.on('timeout', function(msgout, sender) {
|
||||
node.port.on('timeout', function (msgout, sender) {
|
||||
if (sender !== node) { return; }
|
||||
msgout.status = "ERR_TIMEOUT";
|
||||
node.status({fill:"red",shape:"ring",text:"serial.status.timeout"});
|
||||
node.status({ fill: "red", shape: "ring", text: "serial.status.timeout" });
|
||||
node.send(msgout);
|
||||
});
|
||||
|
||||
// Common part
|
||||
node.port.on('ready', function() {
|
||||
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
|
||||
node.port.on('ready', function () {
|
||||
node.status({ fill: "green", shape: "dot", text: "node-red:common.status.connected" });
|
||||
});
|
||||
node.port.on('closed', function() {
|
||||
node.status({fill:"red",shape:"ring",text:"node-red:common.status.not-connected"});
|
||||
node.port.on('closed', function () {
|
||||
node.status({ fill: "red", shape: "ring", text: "node-red:common.status.not-connected" });
|
||||
});
|
||||
};
|
||||
setCallback(node);
|
||||
}
|
||||
else {
|
||||
this.error(RED._("serial.errors.missing-conf"), {});
|
||||
@ -200,6 +222,50 @@ module.exports = function(RED) {
|
||||
}
|
||||
RED.nodes.registerType("serial request", SerialRequestNode);
|
||||
|
||||
|
||||
// Serial Control Node
|
||||
function PortSelectNode(n) {
|
||||
const configProps = [
|
||||
"serialport", "serialbaud", "databits", "parity", "stopbits",
|
||||
"dtr", "rts", "cts", "dsr", "bin", "out"
|
||||
]
|
||||
RED.nodes.createNode(this,n);
|
||||
this.serialConfig = RED.nodes.getNode(n.serial);
|
||||
|
||||
if (!this.serialConfig) {
|
||||
this.error(RED._("serial.errors.missing-conf"), {});
|
||||
return;
|
||||
}
|
||||
|
||||
this.serial = n.serial;
|
||||
var node = this;
|
||||
node.port = serialPool.get(this.serialConfig);
|
||||
node.on("input",function(msg) {
|
||||
if (configProps.some((p) =>{return msg.payload.hasOwnProperty(p)})) {
|
||||
msg.payload.enable = msg.payload.hasOwnProperty('enable') ? msg.payload.enable : true;
|
||||
node.serialConfig.changePort(msg.payload);
|
||||
}
|
||||
if (msg.payload.hasOwnProperty("enable")) {
|
||||
// if any of config parameters or enable property is passed, do this control
|
||||
node.serialConfig.enable = msg.payload.enable;
|
||||
if (msg.payload.enable === true) {
|
||||
node.serialConfig.emit('start');
|
||||
} else {
|
||||
serialPool.close(node.serialConfig.serialport,() => {
|
||||
RED.log.info("[serialconfig:"+node.serialConfig.id+"] " + RED._("serial.stopped",{port:node.serialConfig.serialport}));
|
||||
});
|
||||
}
|
||||
}
|
||||
let currentConfig = {};
|
||||
configProps.map((p) => {
|
||||
currentConfig[p] = node.serialConfig[p];
|
||||
});
|
||||
currentConfig.enable = node.serialConfig.enable;
|
||||
node.send({payload: currentConfig});
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("serial control", PortSelectNode);
|
||||
|
||||
var serialPool = (function() {
|
||||
var connections = {};
|
||||
return {
|
||||
@ -334,6 +400,7 @@ module.exports = function(RED) {
|
||||
},
|
||||
}
|
||||
//newline = newline.replace("\\n","\n").replace("\\r","\r");
|
||||
obj._emitter.setMaxListeners(50);
|
||||
var olderr = "";
|
||||
var setupSerial = function() {
|
||||
obj.serial = new SerialPort({
|
||||
|
@ -55,3 +55,50 @@ This node behaves as a tightly coupled combination of serial in and serial out n
|
||||
Send the request message in `msg.payload` as you would do with a serial out node. The message will be forwarded to the serial port following a strict FIFO (First In, First Out) queue, waiting for a single response before transmitting the next request. Once a response is received (with the same logic of a serial in node), or after a timeout occurs, a message is produced on the output, with msg.payload containing the received response (or missing in case if timeout), msg.status containing relevant info, and all other fields preserved.
|
||||
|
||||
For consistency with the serial in node, msg.port is also set to the name of the port selected.
|
||||
|
||||
### Serial Control
|
||||
When the node-red starts, the flow(program) picks up the pre-programmed serial port, open it, and start the communication. But there are some cases the port needs to switch to a different port, stop, and start again. For example, in order to upload a new binary for Arduino, the serial port needs to be stopped relased from the nodered, and start it again after uploading. Or when the FTDI device re-connects after disconnected for any reason, it may be possible the port number change, and the end user of the flow can't change the port.
|
||||
|
||||
|
||||
This `Serial Control` node provides the serial port control capability to
|
||||
1. change the serial port and its configuration on the run time programatically.
|
||||
2. stop the communication and releasing the serial port so, for example the Arduino can upload the new binary without shutting down the nodered.
|
||||
3. start the communication after stopped with this `Serial Control` node for above reason or the like.
|
||||
|
||||
<p>In order to control the communication, just send these JSON messages to the control node.</p>
|
||||
<pre>
|
||||
{
|
||||
"serialport": "/dev/tty.usbmodem1234561",
|
||||
"serialbaud": 115200,
|
||||
"databits": 8,
|
||||
"parity": "none",
|
||||
"stopbits": 1
|
||||
"enable": true
|
||||
}
|
||||
</pre>
|
||||
changes the serial port and the configuration on the fly.
|
||||
<p>The following optional parameters will change the configuration only if they are present.</p>
|
||||
<p>Any combination of them can be passed to change/control the serial communication</p>
|
||||
<ul>
|
||||
<li> serialport </li>
|
||||
<li> serialbaud </li>
|
||||
<li> databits </li>
|
||||
<li> parity </li>
|
||||
<li> stopbits </li>
|
||||
<li> dtr </li>
|
||||
<li> rts </li>
|
||||
<li> cts </li>
|
||||
<li> dsr </li>
|
||||
<li> bin </li>
|
||||
<li> out </li>
|
||||
<li> enable </li>
|
||||
</ul>
|
||||
When the `enable` property is not present, it will default to `true`
|
||||
|
||||
`{"enable":true}` or `{"enable":false}` will start or stop the communication.
|
||||
|
||||
If `enable` is passed along with other parameters, the configuration will be changed and the port will be either started or just be remaining ready to be started(ie. stopped) later depending on its value.
|
||||
|
||||
**Here is the serial control node usage example flow**
|
||||
|
||||
[{"id":"dc5438a9ae7a274f","type":"serial in","z":"e010d91f3c429066","name":"","serial":"b720bb12479b6ef1","x":290,"y":500,"wires":[["be0292cc1a1f5eed"]]},{"id":"be0292cc1a1f5eed","type":"debug","z":"e010d91f3c429066","name":"debug 21","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":880,"y":500,"wires":[]},{"id":"7a51f56281c210d5","type":"inject","z":"e010d91f3c429066","name":"{\\"parity\\":\\"even\\"}","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\\"parity\\":\\"even\\"}","payloadType":"json","x":340,"y":760,"wires":[["ca56aab57eeaefb7"]]},{"id":"ca56aab57eeaefb7","type":"serial control","z":"e010d91f3c429066","name":"","serial":"b720bb12479b6ef1","x":710,"y":720,"wires":[["1288803f89da05fd"]]},{"id":"8b3826afb08ccb52","type":"inject","z":"e010d91f3c429066","name":"usbmodem1234561 / false","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\\"serialport\\":\\"/dev/tty.usbmodem1234561\\",\\"enable\\":false}","payloadType":"json","x":370,"y":640,"wires":[["ca56aab57eeaefb7"]]},{"id":"e67e73f72e8f1b5e","type":"inject","z":"e010d91f3c429066","name":"usbmodem1234561, 115200","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\\"serialport\\":\\"/dev/tty.usbmodem1234561\\",\\"serialbaud\\":115200,\\"databits\\":8,\\"parity\\":\\"none\\",\\"stopbits\\":1}","payloadType":"json","x":380,"y":560,"wires":[["ca56aab57eeaefb7"]]},{"id":"46dc99f3c72003eb","type":"inject","z":"e010d91f3c429066","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\\"enable\\":false}","payloadType":"json","x":340,"y":800,"wires":[["ca56aab57eeaefb7"]]},{"id":"d5221d388f00c998","type":"inject","z":"e010d91f3c429066","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\\"enable\\":true}","payloadType":"json","x":340,"y":840,"wires":[["ca56aab57eeaefb7"]]},{"id":"28c6b8f294b7642a","type":"inject","z":"e010d91f3c429066","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":320,"y":880,"wires":[["ca56aab57eeaefb7"]]},{"id":"9c377da93413a7e9","type":"inject","z":"e010d91f3c429066","name":"usbmodem1234561 / true","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\\"serialport\\":\\"/dev/tty.usbmodem1234561\\",\\"enable\\":true}","payloadType":"json","x":370,"y":600,"wires":[["ca56aab57eeaefb7"]]},{"id":"0c6388d6a25a762b","type":"inject","z":"e010d91f3c429066","name":"AC026GAO,57600","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\\"serialport\\":\\"/dev/tty.usbserial-AC026GAO\\",\\"serialbaud\\":57600,\\"databits\\":8,\\"parity\\":\\"none\\",\\"stopbits\\":1}","payloadType":"json","x":350,"y":680,"wires":[["ca56aab57eeaefb7"]]},{"id":"a8b80305a8c27174","type":"inject","z":"e010d91f3c429066","name":"{\\"parity\\":\\"none\\"}","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\\"parity\\":\\"none\\"}","payloadType":"json","x":340,"y":720,"wires":[["ca56aab57eeaefb7"]]},{"id":"1288803f89da05fd","type":"debug","z":"e010d91f3c429066","name":"debug 23","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":880,"y":720,"wires":[]},{"id":"b720bb12479b6ef1","type":"serial-port","name":"s1","serialport":"/dev/tty.usbmodem1234561","serialbaud":"115200","databits":"8","parity":"none","stopbits":"1","waitfor":"","dtr":"none","rts":"none","cts":"none","dsr":"none","newline":"\\n","bin":"false","out":"char","addchar":"","responsetimeout":"10000"}]
|
||||
|
@ -93,3 +93,51 @@
|
||||
<p>Optionally the new line character used to split the input
|
||||
can be appended to every message sent out to the serial port.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-help-name="serial control">
|
||||
<p>Stops, starts the serial communication and changes the serial port configuration.</p>
|
||||
|
||||
<p>This node provides the serial port control capability to</p>
|
||||
<ul>
|
||||
<li>stop the communication and releasing the serial port so, for example the Arduino can upload the new binary without shutting down the nodered.</li>
|
||||
<li>start the communication after stopped with this `Serial Control` node for above reason or the like.</li>
|
||||
<li>change the serial port and the configuration on the run time programatically.</li>
|
||||
<li>query the serial port configuration if empty or no valid configuration parameters are passed.</li>
|
||||
</ul>
|
||||
|
||||
<h3>Inputs</h3>
|
||||
<p>In order to control the communication, just send these JSON messages to the control node.</p>
|
||||
<pre>
|
||||
{
|
||||
"serialport": "/dev/tty.usbmodem1234561",
|
||||
"serialbaud": 115200,
|
||||
"databits": 8,
|
||||
"parity": "none",
|
||||
"stopbits": 1
|
||||
"enable": true
|
||||
}
|
||||
</pre> changes the serial port and the configuration on the fly.
|
||||
<p>The following optional parameters will change the configuration only if they are present.</p>
|
||||
<p>Any combination of them can be passed to change/control the serial communication</p>
|
||||
<ul>
|
||||
<li> serialport </li>
|
||||
<li> serialbaud </li>
|
||||
<li> databits </li>
|
||||
<li> parity </li>
|
||||
<li> stopbits </li>
|
||||
<li> dtr </li>
|
||||
<li> rts </li>
|
||||
<li> cts </li>
|
||||
<li> dsr </li>
|
||||
<li> bin </li>
|
||||
<li> out </li>
|
||||
<li> enable </li>
|
||||
</ul>
|
||||
<p>When the <code>enable</code> property is not present, it will default to <code>true</code></p>
|
||||
<p>
|
||||
<code>{"enable":true}</code> or <code>{"enable":false}</code> will start or stop the communication.</p>
|
||||
<p> If <code>enable</code> is passed along with other parameters, the configuration will be changed and either be started or just be ready to be started(ie. stopped ) depending on its value. </p>
|
||||
|
||||
<h3>Outputs</h3>
|
||||
<p><code>msg.payload</code> is the response. It contains the serial port configuration</p>
|
||||
</script>
|
@ -68,6 +68,7 @@
|
||||
"closed": "serial port __port__ closed",
|
||||
"list": "Failed to list ports. Please enter manually.",
|
||||
"badbaudrate": "Baudrate is invalid"
|
||||
}
|
||||
},
|
||||
"stopped": "__port__ stopped"
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user