Serial port in multithreaded flow - how would you solve it?

Hi there

I'm currently designing a flow where a serial port is used and need some hint:

Serial port is natively a single-threaded thing - e.g. only one character can be sent a a time and the same is for reception. In my flow I communicate with a simple device, which responds to requests. E.g. I need to send a message like "give me value at address X" to the serial port and device responses "The value at address X is Y". I can also send a command "Write Value Z to Address X". Response would be "Written Value Z to address X".
My problem is that such requests in my flow happening asynchronously and nondeterministically from different sources - dashboards, when user presses some button, or periodically e.g when I need to poll some value. So I need a way to serialize the requests to send them one by one into Port and also, ideally, limit the speed not by time (which I think is obviously possible using delay node), but by waiting for a response from device, before next request is sent. So a kind of queue.

Does anyone have solved such task already? What would be the typical solution here, ideally using only standard nodes without functions?
Thanks.

There is already a core node that may do much of this for you.

You may need to tune the interval to your needs.

Otherwise, one of the contributed "gate" nodes would let you block until getting a result (you could, of course, build a flow yourself as an alternative). Not sure if any of them have a queue feature though.

Thanks, just updated my message about delay node - this is known to me. The question was mostly how to wait for a response from serial port before next message is allowed to transmit.

The idea would be to keep a flag context variable and a queue context variable.

In your flow, when a msg is sent, change the flag. In the flow that sends the msg, check the flag before sending. If the flag is set to block then add the msg to the queue instead of sending.

You would also need something to watch for the flag changing though. You might be able to use the status node to help with that. But I would normally just use MQTT since you can subscribe to changes. Sadly, the context variable library in Node-RED does not have a subscribe capability - I've always meant to get round to writing one but never seem to quite get the time!

Check the flows site though to see if any contributed nodes do what you want.

Sometihng like this?

This flow shows how you can use the core Delay node Flush mechanism to queue messages and release them as each message action is completed.

[{"id":"c8c6291649a49d8f","type":"group","z":"bdd7be38.d3b55","name":"Sequential Operation","style":{"label":true},"nodes":["b6630ded2db7d680","ed63ee4225312b40","a82c03c3d34f683c","2128a855234c1016","7c6253e5d34769ac","b23cea1074943d4d","d4d479e614e82a49","3a9faf0a95b4a9bb","7eb760e019b512dc","e35f37deeae94860"],"x":54,"y":319,"w":942,"h":282},{"id":"b6630ded2db7d680","type":"inject","z":"bdd7be38.d3b55","g":"c8c6291649a49d8f","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":200,"y":480,"wires":[["ed63ee4225312b40"]]},{"id":"ed63ee4225312b40","type":"delay","z":"bdd7be38.d3b55","g":"c8c6291649a49d8f","name":"Queue","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"minute","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":370,"y":480,"wires":[["d4d479e614e82a49","7eb760e019b512dc"]]},{"id":"a82c03c3d34f683c","type":"delay","z":"bdd7be38.d3b55","g":"c8c6291649a49d8f","name":"Some more stuff to do","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":860,"y":480,"wires":[["7c6253e5d34769ac","b23cea1074943d4d"]]},{"id":"2128a855234c1016","type":"link in","z":"bdd7be38.d3b55","g":"c8c6291649a49d8f","name":"link in 1","links":["7c6253e5d34769ac"],"x":155,"y":560,"wires":[["3a9faf0a95b4a9bb"]]},{"id":"7c6253e5d34769ac","type":"link out","z":"bdd7be38.d3b55","g":"c8c6291649a49d8f","name":"link out 1","mode":"link","links":["2128a855234c1016"],"x":955,"y":560,"wires":[]},{"id":"b23cea1074943d4d","type":"debug","z":"bdd7be38.d3b55","g":"c8c6291649a49d8f","name":"OUT","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":730,"y":420,"wires":[]},{"id":"d4d479e614e82a49","type":"debug","z":"bdd7be38.d3b55","g":"c8c6291649a49d8f","name":"IN","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":510,"y":420,"wires":[]},{"id":"3a9faf0a95b4a9bb","type":"function","z":"bdd7be38.d3b55","g":"c8c6291649a49d8f","name":"Flush","func":"return {flush: 1}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":250,"y":560,"wires":[["ed63ee4225312b40"]]},{"id":"7eb760e019b512dc","type":"function","z":"bdd7be38.d3b55","g":"c8c6291649a49d8f","name":"Some functions to be performed","func":"\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":610,"y":480,"wires":[["a82c03c3d34f683c"]]},{"id":"e35f37deeae94860","type":"comment","z":"bdd7be38.d3b55","g":"c8c6291649a49d8f","name":"Set the queue timeout to larger than you ever expect the process to take","info":"This is a simple flow which allows a sequence of nodes to be \nprotected so that only one message is allowed in at a time. \nIt uses a Delay node in Rate Limit mode to queue them, but \nreleases them, using the Flush mechanism, as soon as the \nprevious one is complete. Set the timeout in the delay node to \na value greater than the maximum time you expect it ever to take. \nIf for some reason the flow locks up (a message fails to indicate \ncompletion) then the next message will be released after that time.\n\nMake sure that you trap any errors and feed back to the Flush \nnode when you have handled the error. Also make sure only one \nmessage is fed back for each one in, even in the case of errors.","x":330,"y":360,"wires":[]}]

Open the Comment node to get instructions on how to use it.
The Copy button below the flow will copy it to the paste buffer.

3 Likes

A semaphore is useful for these scenarios.

Here is one implemented by one of our regulars @marcus-j-davies:

It has built-in example flows too (use CTRL-I to select an example)

3 Likes

Alternatively, I believe that the Serial Request node will handle that for you with no additional logic.

1 Like