Flow to open solenoid valves on prescribed interval using Raspberry Pi

I am planning to purchase OPTO-P1-40P andSNAP-D4M and will run Node-RED on a Raspberry Pi to do a simple program. I have some of it figured out, but a few areas need some help.

Here’s what we have now and what we do…
GeneratorBox
An electrical enclosure with 5 on/off toggle switches. Each switch is wired to a solenoid valve (attached on 5 different pipes) that open or close when the corresponding switch position is changed. We always have 1 switch open and 4 closed (never more than 1 open at a time). There is no automation whatsover – this is entirely manual. When a switch is open, the corresponding solenoid is open and allows gas to flow through the designated pipe and into a measurement device that we use to measure the dew point of the gas. That device has a 4-20mA output and which we can query using XML (more on this later). Right now, we flip a switch to let the gas flow for 30 min. at a time. For the last 10 min. of that 30-min window, we take the dew point measurement and write it down on paper.

Here’s what we want to do…

Use the OPTO-P1-40P, SNAP-D4M, and an appropriate SNAP module(s) to run Node-RED on a Raspberry Pi to do a simple program. We already maintain in a SQL database a ‘live’ list of which of the 5 pipes to measure. The pipes are labeled 301, 302, 303, 304, and 305. For example, if I run the SQL query on Monday, it might say to measure 301 and 302. If we run the query next Thursday, it might say to measure 302, 303 and 305. I have that flow already working! (for simplicity, just disregard the word GeneratorNumber and think of it as PipeNumber)


SQL%20query%20output
If the above SQL query shows, for example, 301 and 302, then we would need to first open the solenoid corresponding to 301 and keep it open for 30 minutes. Next, we need a node that will retrieve (say, every 1 minute) the XML data for each pipe being sampled, and only for the last 10 minutes of the 30 minute window. Once done, we should move on to the next pipe to be sampled (if there is one) and do the same thing. Keep going until the dataset (301 and 302, in this case) is done. Then the SQL query would run again and the cycle would repeat.

The measurement device we have is hooked up to this. Page 43 indicates we can use XML, for example:

Request the current state:
http://192.168.1.2/state.xml
This will return the following:
XML%20output

In our case, we are only looking at one input (let’s say input3), so we would only care about the value 180.42037. Is there a suitable node or general approach to obtaining the desired value (input3) every minute for 10 minutes?

I may be able to figure this out on my own, but before I bought the Pi carrier board and Snap modules, I wanted to make sure I was not overlooking anything.

Before you get too deep into this… the Raspberry Pi can not talk to any analog modules, so the 4-20ma is a non-starter… does this change things?

EDIT: Never mind, I see you are using the other device as your analog input / XML output device.

I started to fiddle around a bit and I believe something like this would work (obviously not deployed and not tested).

The settings on the first switch node look like this (others follow the same, except instead of 301, it’s 302, 303, etc.):
switch_node

If payload[0].GeneratorNumber is equal to 301, then we follow path 1, which would open the corresponding solenoid valve right away. After a 20 minute delay, the XML data would be obtained and written to the SQL database using an INSERT statement.

If payload[0].GeneratorNumber is NOT equal to 301, then it moves to the next switch node and poses the same 2 questions, but for 302. The sequence keeps going until 305 has been checked.

Assuming the above works in concept, then there are still some open switches (no pun intended):

  1. How do I close the solenoid after 30 min? I was looking for a stop timer or something but could not find it.
  2. What do I do with the last switch node if msg.payload[4].GeneratorNumber is NOT equal to 305? Maybe I can just leave it as is?

The first thing that jumps out to me here is your switch node rules; the first rule is checking to see if your value is == to the number 301 but then you check if it’s != to the string “301”. While this might work sometimes, I highly recommend setting them both to the number data type to avoid any potential errors.

The second thing is that this 30 minute injects only once, but if I understand your original post correctly you want it to turn the solenoid on, wait 20 minutes, then inject 10 times, once a minute for each measurement, and then finally close the solenoid after the 10th test. Is that right?

Finally, these checks are only looking for msg.payload[0], which could be “301”, but if there is a “302” in msg.payload[1] do you need to queue that up to run next? or will the “301” entry be knocked off the dataset when the test is complete, moving “302” up to payload[0]?
How you choose to handle the array will change your flow a bit.

My lack of knowledge really shows here…I assumed != meant “not equal to”. I was trying to define the logic for path 2 by saying “if the value is NOT equal to 301, then go to path 2”.

Re: the injection and timing rules, yes, I originally stated that the SQL query would be run every 30 minutes, but now I do not believe that is the correct approach. Better would be to inject once, open solenoid 301 (or 302 or whatever gets returned first by the query), wait 20 minutes, take 10 readings via XML (once per minute), then close the solenoid valve. Then move onto the next value returned by the SQL query (302 or 302 or whatever gets returned second), and do the same set of operations. The “301” would get knocked off the dataset when the test is complete and move to the next one in the dataset.

Sorry for the confusion.

You’re right about != being ‘not equal to’, it’s just the data types that might cause an issue. You can confirm the data type by the icon that says 0..9 or a..z:
image

For the rest of the flow I think you’re on the right track, you just have make sure you’re turning the pins back off after the test, and that you make the 10 separate requests for the XML data. I would personally do that with a function node, which requires some more advanced JavaScript. It could be done with a series of nodes instead, but I think it’s worth trying out the function node.

The main thing is creating a sleep function that is called in an asynchronous function to get the correct timing within the flow – the reasons for this are not super critical, the important part is the body of code in the 10-iteration loop under “readValve()” which will trigger 10 separate HTTP requests, each 60 seconds apart.
How does this look to you?

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// send out the payload 10 times with 1 minute delay between messages:
async function readValve() {
    for (i = 0; i < 10; i++) {
        node.send(msg);
        await sleep(60000); // delay 60sec
    }
}

readValve();

I would use this “readValve” code along with delay nodes to turn your pins on and off to get the functionality you’re looking for. You would of course use Raspberry Pi nodes instead of PAC control nodes, but here’s a basic example to show the logic I have in mind…

Thanks. I totally missed the number vs string (one has to admit that text it quite small).

I am going to go ahead and order up the Pi carrier board and some Snap IO modules to open/close the solenoids. In the meantime, I have connected the analog capture device and placed on our network and will play around with the XML retrieve tomorrow.

No worries, it is very small and easy to miss – I have to double check it all the time, but it really helps to keep things running smoothly.

Also, you may already be using them, but some extra resources I use a lot are the Node-RED forums and w3schools. I almost always have tabs open for those sites – they might come in handy for you as well.

Best of luck with the XML!

XML is working perfectly using the flow shown below. In case it helps anyone, I had to change the debug node to msg.payload.datavalues.input1state[0] to obtain the one value that I care about.