How to write to memory tag based on value of another tag?

Hello!
What I’m trying to do is take an integer value of 0-100 that is outputted by a tag (Moisture). Then compare that value with a static number to see if it is below 30. If it is below 30, make the memory tag (Pump) True, else False. If the tag returns True, stay true for 5 seconds then revert to false; don’t allow the Pump to be true for 2 minutes. Then repeat the loop

I was looking at a few scripting functions. Through the Gateway Events, you can set it up to run a script on tag change. The issue with this is I don’t think it would play nicely with my delays. I need a 2-minute delay if the Pump value ever gets changed to true (this is to prevent overwatering of the plant hooked up to the moisture sensor). With the Gateway Event script it will execute the loop everytime the tag value changes (thus as far as I can see, constantly reinstating the loop and bypassing the 2-minute delay. The other way I see to do it is through tag scripting, though that would be based on tag change and I’d run into the same issue.

The viable option I see is to use the Sequential Function Chart function, which I messed around with, but am extremely confused about - there’s not a whole lot of documentation out there for it, especially for python amateurs.

Also, I just wanted to add in the code I have to change the value.

import time
moisture = system.tag.read("[default]moisture").value
wantedMoisture = 30

if (moisture < wantedMoisture): #if moisture is below 30
    system.tag.write("[Plant]Pump", "True") #turn on pump
    time.sleep(5) #keep pump on for 5 seconds
    system.tag.write("[Plant]Pump", "False") #turn off pump
    time.sleep(120) #don't allow pump to turn back on for 2 minutes
else:
    system.tag.write("[Plant]Pump", "False") #don't turn on pump if moisture is not below 30

I guess I’m asking a few questions. Is the code above the best and most straightforward way to implement this? If so, where is the best way to put this code so it implements correctly? Otherwise, what is a better way to implement this?

I’m assuming you don’t have a PLC associated with whatever your doing? Typically control like that you would do from a PLC.

Without it, I wouldn’t use sleep in your script. I would have your moisture value, your run trigger, and a date/time memory tag. I’d also use a timer script to do it with something like:

wantedMoisture = 30
moisture = system.tag.read("[default]moisture").value

if moisture < wantedMoisture: #if moisture is below 30
	lastTrigger = system.tag.read("[default]PumpLastTrigger").value
	now = system.date.now()
	if system.date.secondsBetween(lastTrigger,now()) > 120:
		system.tag.write("[Plant]Pump", "True") #turn on pump
		system.tag.write("[default]PumpLastTrigger",now)
	elif system.date.secondsBetween(lastTrigger,now()) > 5 and system.date.secondsBetween(lastTrigger,now()) <= 120:
		system.tag.write("[Plant]Pump", "False") #turn off pump
	else:
		system.tag.write("[Plant]Pump", "False") #turn off pump
	
else:
    system.tag.write("[Plant]Pump", "False")

Thanks for the reply, Preston. I’m not using a PLC. The Moisture tag is coming into ignition from a NodeMCU through MQTT, the Pump value is a memory tag I defined that is being transferred out to the NodeMCU through MQTT (which will turn a pin high if true to switch on a relay). Your code looks like it would work well, though where would I implement it? On tag change, gateway script or elsewhere?

With no PLC, you probably should use a timer script that examines states and timestamps every second or so and write the result to the Pump control. Note that a comms loss could leave your pump running uncontrolled. ):

That’s exactly what I’m trying to avoid, I’d like to have some sort of fault to keep it from running forever. I’m using an Arduino to receive the Pump value, so I could program something directly into that saying that if I get a true value to never run the pump more than 10 seconds or so. I’m going to try to use the above code in a delay script then have some extra code in the Arduino for fault prevention and see how that works out.

As @bpreston and @pturmel said this type of control should be done from a local controller not over a network connection. Much like a momentary push button, at some point the network connection will drop (or other similar issues will occur) and your pump will run longer than desired.

I would alter the code to be inline with Ignition 8, system.tag.read and system.tag.write have been deprecated and system.tag.readBlocking, system.tag.readAsync, system.tag.writeBlocking, and system.tag.writeAsync should be used depending on the situation.

The code should be placed in a gateway timer script

#read tag values
moisture= system.tag.readBlocking("[default]moisture").value

#set default variable values
pumpCommand = False
wantedMoisture = 30

#if the moisture is less than wantedMoisture the trigger the pump
if moisture < wantedMoisture:
     pumpCommand = True

#write the tags
system.tag.writeBlocking("[Plant]Pump",pumpCommand)

I would recommend that you use this input to the Arduino as a trigger. When the trigger is received by the Arduino then it executes the pump control logic to run the pump for whatever interval you want. The Arduino should treat the trigger like a one shot such that if it stays high the pump only runs once.

I would have a reset bit from the Arduino back to Ignition which would reset the trigger value in ignition. The reset bit would be held high until the trigger bit dropped low.

The Gateway Timer script should be set such that the delay between executions is whatever your pump run interval is plus some buffer time. For instance if you want to run the pump for 10 secs with a 5 sec delay between runs then your gateway timer should be on a 15 second interval.

1 Like

I’m working on implementing your suggestion, though, even when not introducing a trigger and just making the trigger a string to see if the tag writes work - I’m still not getting any writing to my Pump tag. Do you know why this would be? I’ve attached a screenshot - my Moisture Perc is at 23 which is below my trigger of 30 but my Pump tag still won’t change to True.

Obvious yes, but did you Enable the script?

On the gateway status page > Gateway Scripts is the script shown in the Timer script list?

I see the script now in the gateway although it pulls out an error:

TimerScriptTask	01Oct2020 12:37:39	
Error executing global timer script: Plant/PumpControl @5,000ms . Repeat errors of this type will be logged as 'debug' messages

Update:
It works and updates the tag when I use

system.tag.write("[Plant]Pump", True)

instead of:

system.tag.writeBlocking("[Plant]Pump", True)

you may need to put the paths and values in a list for writeBlocking to work.

@PGriffith and advice on the difference here?

1 Like

Yep, that’s it. Legacy write allows a single path; writeBlocking and writeAsync require lists.

1 Like

Thanks everyone for their help. I ended up with the following for a gateway timer script:

moisture = system.tag.read("[default]Moisture Perc").value
reset = system.tag.read("[MQTT Engine]pumpReset").value

wantedMoisture = 30

if reset == "True":
	system.tag.write("[Plant]Pump",False)

if moisture < wantedMoisture and reset == "False":
	system.tag.write("[Plant]Pump",True)

And the following for Arduino code:

void turnOnPump(){

  pumpstats = String((char*)pumpstat);
  if (pumpstats == "1" && pumpReset == "False"){
    digitalWrite(13, HIGH); 
    delay(5000);
    digitalWrite(13, LOW); 
    pumpReset = "True";
  }
  if (pumpstats == "0"){
    pumpReset = "False";
  }
}

Where pumpstats corresponds to the Pump tag being true or false and the pumpReset tag corresponds to pumpReset.