From 2e52257afadf9e3e4bb41578fb5a49fd328b1201 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Thu, 23 Nov 2023 12:11:59 +0000 Subject: [PATCH] Tidy up PR, add status to control nodes tweak help, readme, and feed stopped status to other nodes. --- io/serialport/25-serial.js | 60 +++++++++++++------ io/serialport/README.md | 68 +++++++++++----------- io/serialport/locales/en-US/25-serial.html | 40 ++++++------- io/serialport/locales/en-US/25-serial.json | 3 +- io/serialport/package.json | 5 +- 5 files changed, 100 insertions(+), 76 deletions(-) diff --git a/io/serialport/25-serial.js b/io/serialport/25-serial.js index 66703c76..0a7803bf 100644 --- a/io/serialport/25-serial.js +++ b/io/serialport/25-serial.js @@ -25,10 +25,10 @@ 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.enabled = n.enabled || true; this.waitfor = n.waitfor || ""; this.responsetimeout = n.responsetimeout || 10000; - + this.changePort = (serialPort) => { serialPool.close(this.serialport,() => {}); this.serialport = serialPort.serialport || this.serialport; @@ -95,6 +95,9 @@ module.exports = function(RED) { node.port.on('closed', function() { node.status({fill:"red",shape:"ring",text:"node-red:common.status.not-connected"}); }); + node.port.on('stopped', function() { + node.status({fill:"grey",shape:"ring",text:"serial.status.stopped"}); + }); } RED.nodes.registerType("serial out",SerialOutNode); @@ -117,7 +120,7 @@ module.exports = function(RED) { this.on("close", function(done) { serialPool.close(this.serialConfig.serialport,done); - }); + }); function setCallback(node) { node.status({fill:"grey",shape:"dot",text:"node-red:common.status.not-connected"}); @@ -132,13 +135,16 @@ module.exports = function(RED) { node.port.on('closed', function() { node.status({fill:"red",shape:"ring",text:"node-red:common.status.not-connected"}); }); + node.port.on('stopped', function() { + node.status({fill:"grey",shape:"ring",text:"serial.status.stopped"}); + }); }; setCallback(node) } RED.nodes.registerType("serial in",SerialInNode); - /******* REQUEST *********/ + // request data and waits for reply function SerialRequestNode(n) { RED.nodes.createNode(this,n); this.serial = n.serial; @@ -197,13 +203,15 @@ module.exports = function(RED) { node.status({ fill: "red", shape: "ring", text: "serial.status.timeout" }); 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" }); }); + node.port.on('stopped', function() { + node.status({fill:"grey",shape:"ring",text:"serial.status.stopped"}); + }); }; setCallback(node); } @@ -223,11 +231,11 @@ module.exports = function(RED) { RED.nodes.registerType("serial request", SerialRequestNode); - // Serial Control Node - function PortSelectNode(n) { + // control node to stop, start, reconfigure ports + function SerialControlNode(n) { const configProps = [ "serialport", "serialbaud", "databits", "parity", "stopbits", - "dtr", "rts", "cts", "dsr", "bin", "out" + "dtr", "rts", "cts", "dsr" ] RED.nodes.createNode(this,n); this.serialConfig = RED.nodes.getNode(n.serial); @@ -239,32 +247,43 @@ module.exports = function(RED) { this.serial = n.serial; var node = this; - node.port = serialPool.get(this.serialConfig); + node.port = serialPool.get(node.serialConfig); + node.port.on('stopped', function() { + node.status({fill:"grey",shape:"ring",text:"serial.status.stopped"}); + }); + node.port.on('ready', function() { + node.status({fill:"green",shape:"dot",text:node.serialConfig.serialbaud+","+node.serialConfig.databits+","+(node.serialConfig.parity.toUpperCase().substr(0,1))+","+node.serialConfig.stopbits}); + }); 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; + if (msg.hasOwnProperty("flush") && msg.flush === true) { node.port.serial.flush(); } + if (configProps.some((p) => { return msg.payload.hasOwnProperty(p) })) { + msg.payload.enabled = msg.payload.hasOwnProperty('enabled') ? msg.payload.enabled : 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) { + var stat = {fill:"green",shape:"dot",text:node.serialConfig.serialbaud+","+node.serialConfig.databits+","+(node.serialConfig.parity.toUpperCase().substr(0,1))+","+node.serialConfig.stopbits} + if (msg.payload.hasOwnProperty("enabled")) { + node.serialConfig.enabled = msg.payload.enabled; + if (msg.payload.enabled === true || msg.payload.enabled === "true") { node.serialConfig.emit('start'); - } else { + } + else { serialPool.close(node.serialConfig.serialport,() => { RED.log.info("[serialconfig:"+node.serialConfig.id+"] " + RED._("serial.stopped",{port:node.serialConfig.serialport})); }); + stat.fill = "grey"; + stat.shape = "ring"; } } let currentConfig = {}; configProps.map((p) => { currentConfig[p] = node.serialConfig[p]; }); - currentConfig.enable = node.serialConfig.enable; - node.send({payload: currentConfig}); + currentConfig.enabled = node.serialConfig.enabled; + node.status(stat); + node.send({payload:currentConfig}); }); } - RED.nodes.registerType("serial control", PortSelectNode); + RED.nodes.registerType("serial control", SerialControlNode); var serialPool = (function() { var connections = {}; @@ -442,6 +461,9 @@ module.exports = function(RED) { setupSerial(); }, serialReconnectTime); } + else { + obj._emitter.emit('stopped'); + } }); obj.serial.on('open',function() { olderr = ""; diff --git a/io/serialport/README.md b/io/serialport/README.md index 423eb7b1..4b9557b7 100644 --- a/io/serialport/README.md +++ b/io/serialport/README.md @@ -18,7 +18,7 @@ you to install the full set of tools in order to compile the underlying package. ## Usage -Provides three nodes - one to receive messages, and one to send, and a request node which can send then wait for a response. +Provides four nodes - one to receive messages, and one to send, a request node which can send then wait for a response, and a control node that allows dynamic control of the ports in use. ### Input @@ -56,49 +56,47 @@ Send the request message in `msg.payload` as you would do with a serial out node 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. +### Control +When the node-red starts, the flow(program) picks up the pre-programmed serial port, open it, and starts 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 disconnecting for any reason, it may be possible that the port number changes, 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. +This node provides the ability to: + + 1. change the serial port and its configuration on the run time programatically. + 2. stop the communication and release the serial port. + 3. reopen the port and restart the communications. + +In order to control the communication, send a **msg.payload** to the control node. -

In order to control the communication, just send these JSON messages to the control node.

-
     {
-        "serialport": "/dev/tty.usbmodem1234561",
+        "serialport": "/dev/ttyUSB0",
         "serialbaud": 115200,
         "databits": 8,
         "parity": "none",
-        "stopbits": 1
-        "enable": true
-    }   
-
+ "stopbits": 1, + "enabled": true + } + changes the serial port and the configuration on the fly. -

The following optional parameters will change the configuration only if they are present.

-

Any combination of them can be passed to change/control the serial communication

- -When the `enable` property is not present, it will default to `true` -`{"enable":true}` or `{"enable":false}` will start or stop the communication. +The following optional parameters will change the configuration only if they are present. +Any combination of them can be passed to change/control the serial 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. + - serialport + - serialbaud + - databits + - parity + - stopbits + - dtr + - rts + - cts + - dsr + - enabled -**Here is the serial control node usage example flow** +If the `enabled` property is not present, it will default to `true`. -[{"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"}] +`{"enabled":true}` or `{"enabled":false}` will start or stop the communication. + +If `enabled` is passed along with other parameters, the configuration will be changed and the port will be either started or remain stopped, ready to be started later depending on its value. + +Any input message will cause the node to output the current port configuration. diff --git a/io/serialport/locales/en-US/25-serial.html b/io/serialport/locales/en-US/25-serial.html index f38602c5..13748ff3 100644 --- a/io/serialport/locales/en-US/25-serial.html +++ b/io/serialport/locales/en-US/25-serial.html @@ -97,28 +97,29 @@ \ No newline at end of file diff --git a/io/serialport/locales/en-US/25-serial.json b/io/serialport/locales/en-US/25-serial.json index 0bd82f7e..46e875c6 100644 --- a/io/serialport/locales/en-US/25-serial.json +++ b/io/serialport/locales/en-US/25-serial.json @@ -2,7 +2,8 @@ "serial": { "status": { "waiting": "waiting", - "timeout": "timeout" + "timeout": "timeout", + "stopped": "stopped" }, "label": { "serialport": "Serial Port", diff --git a/io/serialport/package.json b/io/serialport/package.json index c8dff24d..ed2b9988 100644 --- a/io/serialport/package.json +++ b/io/serialport/package.json @@ -23,5 +23,8 @@ "name": "Dave Conway-Jones", "email": "dceejay@gmail.com", "url": "http://nodered.org" - } + }, + "contributors" : [ + "@yhur" + ] }