A complete step by step tutorial for solving Factory IO's "Mixing Tank (Timers)" scene using Control I/O and a few challenges to test your programming knowledge
If you have taken my free PLC Bootcamp, then you know that I am a huge fan of Factory IO for learning PLC Programming by simulating common industrial automation applications.
Since launching PLC Bootcamp and introducing Factory IO to my audience, I have received a few emails asking for advice on how to solve the pre-built scenes available in Factory IO.
Instead of answering those emails privately, I have decided to put together a series of tutorials that explain how to write programs for each Factory IO scene and how to extend the scenes to make them more realistic.
In this post, I will show you how to implement a solution for the “Filling Tank (Timers)” scene. After the tutorial, I will give you some challenges to test your programming knowledge by extending the functionality that we implement in the tutorial.
If you haven’t already, I suggest that you head over to the Factory IO website and install Factory IO so that you can follow along with this tutorial.
Already have Factory IO installed? Then let’s get started.
Note: this post is a part of a series of tutorials on Factory I/O. In this post, I will only explain new concepts that we haven’t discussed before. If you’re new to Factory IO, then I suggest you look back at previous posts that explain the basics in more detail. You can find those earlier posts here.
Launch Factory IO and open the scene “3 — Filling Tank (Timers)”.
Open the Drivers menu under File > Drivers.
Select the Control I/O driver to launch the Control I/O editor.
Before we start programming the solution, let’s talk about the requirements for this application.
In this application, we will program a semi-automatic process that controls a mixing tank.
When the mixing tank is idle, the Fill pushbutton light is illuminated to indicate to an operator that they can start the filling process.
The operator presses the Fill pushbutton to start the filling process. During the filling process, the tank fills for 3 seconds. While the filling process is active, the discharge process cannot be started.
When the filling process is complete, the Discharge pushbutton light is illuminated to indicate that the operator can start the discharge process.
The operator presses the Discharge pushbutton to start the discharge process. During the discharge process, the tank discharges for 3 seconds. While the discharge process is active, the filling process cannot be started.
During the filling and discharging process, a numeric indicator shows how many milliseconds the process has been running. If neither process is active, then the numeric display shows the number 0.
Now that we have clear requirements for the application, we can start the software design and development process. In this application, we will use a sequencer to manage the execution of each process.
Let’s talk about what a sequencer is.
The application requirements above describe a sequential process. Each step in the process has to be executed in a specific sequence — the tank has to be filled before it can be emptied, and the tank has to be emptied before it can be filled.
In situations like this, it is common to use a sequencer to manage the execution of the application code. A sequencer is a piece of software that keeps track of the current step in a process. When a sequence step is active, then a specific piece of application code is executed and when the correct conditions are active, the sequencer moves on to the next step.
Does that sound confusing? Don’t worry — this will make more sense when we program the sequencer.
When describing a sequencer, it is common to include the step name, the actions that happen during that step, and the conditions to move on to the next step of the sequencer.
So how would we describe the sequencer for this application?
The way I see it, our sequencer should have the following steps:
The tank is empty and the Fill pushbutton light is illuminated.
The sequencer moves from the Idle step to the Filling step when the Fill pushbutton is pressed.
The fill valve is open, the tank is being filled, the filling timer is running, and the numeric display shows how many milliseconds the filling process has been running.
The sequencer moves from the Filling step to the Filled step when the filling timer has elapsed.
The tank has liquid in it and the Discharge pushbutton light is illuminated.
The sequencer moves from the Filled step to the Discharging step when the Discharge pushbutton is pressed.
The discharge valve is open, the tank is being emptied, the discharge timer is running, and the numeric display shows how many milliseconds the discharging process has been running.
The sequencer moves from the Discharging step to the Idle step when the discharging timer has elapsed.
Now that we have a design for our sequencer, let’s look at how to implement it in Control I/O.
Note: This is not the way that I would usually implement a sequencer, but it is the best way that I can think to implement it in Control I/O. An important part of being a good PLC programmer is the ability to make the best solution with the tools that you have available.
To start with, we need a variable that will keep track of the current step of the sequencer. To do this, I will create a Memory Integer variable called M_I_SeqCurStep. This naming convention allows someone to understand at a glance what this variable is — it is a Memory Integer that stores the Sequencer Current Step.
Next, we need a mechanism to increment the sequencer variable to move from step to step. We can do that using a Count Up (CTU) function block. As the name implies, this function block increments a variable by one when it is triggered. When the variable equals a preset value, then the CTU function block activates its output.
Let’s add a CTU function block to the canvas and look at the parameters of this function block.
The CTU function block has three input parameters and two output parameters. They are:
CU (Count Up): The trigger to increase the CV variable.
RESET: Input parameter that resets the CV variable to 0.
PV (Preset Value): Input parameter to set the preset value of the function block. When CV equals PV then Q is activated.
Q (Output): Output parameter that is True when CV equals PV. Otherwise, Q is False.
CV (Current Value): Output parameter that contains the current value of the counter.
Now that you know what the function block’s parameters do, how would you parameterize this function block?
In my application, I have parameterized the block as follows:
I have connected a Source Integer variable with a value of 4 to the PV input of the function block. This means that the Q output parameter will be True when the Current Value of the counter equals 4. A value of 4 indicates that all of the steps in the sequencer have been completed. We will use this Q parameter to reset the Current Value of the counter.
I have connected a Memory Boolean variable called M_B_SequenceComplete to the Q parameter of the function block.
I have connected an OR block to the RESET input parameter of the function block. I will trigger the RESET input when the sequence is completed or when the Factory I/O (Reset) tag is true. This is because if someone resets the model, the sequencer should be re-initialized. To re-initialize the sequencer, we should reset the current value of the counter to 0.
Next, I have connected the M_I_SeqCurStep variable to the CV pin of the function block.
So far, our function block looks as follows:
The last pin we have to parameterize is the CU pin. We’ll come back to this when we have developed the application a little bit more.
A tricky detail in this application is that we want to write to the numeric display several times. When the tank is being filled, we want to show the current value of the filling timer and when the tank is being discharged, we want to show the value of the discharging timer.
To avoid overwriting the value of the filling timer with the value of the discharge timer, we will use the MAX function before the sequence steps are activated. The MAX function takes two numbers as input parameters and sets the output to the higher number.
That means that when the filling timer is running and the discharging timer is 0, the value of the filling timer will be displayed and when the discharging timer is running and the filling timer is 0, the value of the discharging timer will be displayed.
In our application, that logic looks like this:
Now, we can program the actions that should happen during each step in the sequence.
Let’s start with the idle step.
The idle step is active when the M_I_SeqCurStep has a value of 0.
When the Idle step is active, the Fill pushbutton light should be illuminated.
The condition to move on to the next step is the Fill pushbutton being pressed.
Here’s how that logic might look in Control I/O, using intermediate variables and comments to document the code:
The filling step is active when the M_I_SeqCurStep has a value of 1.
When the Filling step is active, the Fill valve output is actuated and the filling timer is running.
To implement the filling process, we will use a new function block called TON, an abbreviation for Timer On. This function block starts a timer when an input becomes true. When the defined time has elapsed, the function block turns on an output pin. The TON function block has two input pins and two output pins. They are;
Here’s how that logic might look in Control I/O, using intermediate variables and comments to document the code:
The Filled step is active when the M_I_SeqCurStep has a value of 2.
When the Filled step is active, the Discharge pushbutton light is illuminated.
Watch out — the Discharge button is a normally closed button so the variable has the value True when the button is not pressed and False when it is. To make this work, we have to use a NOT function block before the AND block for moving on to the next step.
Here’s how that logic might look in Control I/O, using intermediate variables and comments to document the code:
The discharging step is active when the M_I_SeqCurStep has a value of 3.
When the Discharging step is active, the Discharge valve output is actuated, the discharging timer is running, and the numeric display shows the number of milliseconds that the process has been running for.
To implement the discharging process, we will once again use the TON function block.
Here’s how that logic might look in Control I/O, using intermediate variables and comments to document the code:
Did you notice that at the end of every sequence step we have a variable that indicates that the sequence step is finished? In the screenshot above, we see an example of that with the variable M_B_Sequencer_Discharge_Done.
These are the variables that we will use to increment the sequence counter.
To make sure that the sequence only takes one step at a time, we will use a new function block called RTRIG. RTRIG creates a one-shot signal on the rising edge of a variable. This means that when the input variable transitions from False to True, the output of the function block pulses for one PLC cycle.
These one-shot instructions are commonly used to make sure that an instruction is only executed one time when a logical condition becomes true.
Here’s what the finished logic looks like:
Now that we have finished programming the application, you can test it and make sure that everything is working.
I have confirmed that the logic that is shown here is working correctly, so if you have followed the tutorial exactly, then it should work for you too.
If you see some strange behaviour, then double-check that you are using the correct variables in the correct place and try again.
That was a big tutorial. If you have made it this far, then I have a few challenges to test your knowledge.
While the filling or discharge operation is running, the operator cannot do any actions. In general, when a machine is not going to respond to an operator’s command, then we should give them an indication that a process is running.
Your challenge is to create a process running indication for the filling and discharging process using your knowledge of the TON function block and by experimenting with the TOF instruction.
When the filling process is running, the Fill pushbutton light should turn on and off every 500 milliseconds. When the discharging process is running, the Discharging pushbutton light should turn on and off every 500 milliseconds.
To accomplish this, you will need to use the TON and possibly the TOF timers.
Watch out — as I mentioned at the beginning of this post, you can get into trouble when you try to write the same variable at different locations in the PLC program. To control the lights correctly, you may need to introduce some new intermediate variables to the program and control the lights at the end of the sequence.
In this tutorial, I showed you how to write the code for semi-automatic control of a mixing tank.
Your challenge now is to make the process fully automatic.
In this fully automatic process, the operator should be able to press the Fill button to start the process. When the process is started, the tank should continuously fill and empty automatically.
When the operator presses the Discharge button, the current cycle should finish (that is, the tank should finish discharging) and then the process should stop.
Currently, the numeric display shows how many milliseconds the filling or discharging process has been running for. It would be more useful for the operator to see how much longer the process will run for in a unit that they understand.
Your challenge is to change the logic for the numeric display so that it shows the remaining time that the process will run in seconds. To accomplish this, you may need to use some of the math function blocks available in Control I/O.
If you have made it this far, then you have implemented the mixing tank as a semi-automatic process and an automatic process.
Your next challenge is to add a Selector to the Factory I/O model that allows an operator to switch between an automatic mode of operation and a semi-automatic mode of operation.
Think carefully about when an operator should be allowed to change the operating mode of the mixing tank — it probably shouldn’t be mid-cycle!
As always, I am really interested in hearing how you get on with these challenges. If you want, you can let me know how you solved them (or where you got stuck), by dropping an email to ken@kb-controls.io. I look forward to hearing from you.
If you’re enjoying this series, then be sure to subscribe to the mailing list below to be notified when the next part of the series is released.
Sign up to the mailing list to get a new post about industrial automation and controls engineering delivered to your inbox every week.
Learn the skills you need to start your journey as a PLC programmer. Enroll in PLC Bootcamp to learn how to write and test your first PLC program for free.