Traffic lights in a junction

For this post, I will be modifying it during the following weeks adding and explaining the main object I have developed for this project and the signal for the regulation of the pedestrian traffic light. To begin with, I would like you to see the anticlockwise sequence of the traffic lights in a junction with zebra crossings with priority over cars. Every single parameter of time for the transitions can be configured. However, to keep it simple, in this video the time parameters are the same for all. For the blinking pedestrian light I have used the library that I had developed in my previous post. 

The main object of this project is the traffic light which control the flow of the cars. The instances of each traffic light are the ones in charge of controlling the traffic flow.

I have named this function block Car traffic light, CAR_TL and there are four instances of this block, UP, DOWN, RIGHT and LEFT.

The variables used for this object are the following:

FUNCTION_BLOCK CAR_TL
VAR_INPUT

iTRIGGER : BOOL; // Input of the trigger that will start the sequence RED-GREEN-YELLOW-RED, this trigger will be given by a higher object.
iRREQUEST : BOOL; // Seeing the main road, if there is a request by a pedestrian from the right side this signal will be 1.
iLREQUEST : BOOL; // Seeing the main road, if there is a request by a pedestrian from the left side this signal will be 1.
TIME_RED : TIME;
TIME_YELLOW : TIME;
TIME_GREEN : TIME;

END_VAR;
VAR_OUTPUT

oTRIGGER : BOOL;   //This output and the following related to requests will change the input signal to 0 once the sequence has started.
oRREQUEST : BOOL;
oLREQUEST : BOOL;
LIGHT_ARRAY : ARRAY [1..3,1..3] OF BOOL; // The lights are distributed accordingly to this matrix, being 1,1 Green Left; 1,2 Green Main; 1,3 Green right; 2,1 Yellow left; 2,2 Yellow main; 2,3 Yellow right; 3,1 Red left; 3,2 Red main; 3,3 Red right.

END_VAR;
VAR

MODE : INT;
FLAG_FIRST : BOOL;
T_RED : TP;
FT_RED : F_TRIG;
T_YELLOW : TP;
T_GREEN : TP;
FT_GREEN : F_TRIG;
I : INT;
FT_YELLOW : F_TRIG;

END_VAR;

And the code used for this object is the following:

IF iTRIGGER THEN

FOR I:=1 TO 3 DO // Just for the first time of the sequence we have to turn the traffic lights to red.

LIGHT_ARRAY[3,I]:=1;

END_FOR;

IF NOT iRREQUEST AND NOT iLREQUEST THEN

MODE:=1;

END_IF;
IF iRREQUEST AND NOT iLREQUEST THEN

MODE:=2;
oRREQUEST:=1;

END_IF;

IF NOT iRREQUEST AND iLREQUEST THEN

MODE:=3;
oLREQUEST:=1;

END_IF;
IF iRREQUEST AND iLREQUEST THEN

MODE:=4;
oRREQUEST:=1;
oLREQUEST:=1;

END_IF;

oTRIGGER:=0;
FLAG_FIRST:=0;

END_IF;

IF MODE<>0 THEN

T_RED(IN:= NOT FLAG_FIRST, PT:=TIME_RED);
IF T_RED.Q THEN

FLAG_FIRST:=1;

END_IF;
FT_RED(CLK:=T_RED.Q);
T_GREEN(IN:= FT_RED.Q ,PT:=TIME_GREEN);
FT_GREEN(CLK:=T_GREEN.Q);
T_YELLOW(IN:=FT_GREEN.Q,PT:=TIME_YELLOW);
FT_YELLOW(CLK:=T_YELLOW.Q);

IF FT_YELLOW.Q THEN

MODE:=0;
FOR I:=1 TO 3 DO

//We let only the red lights on after a sequence has been completed.
LIGHT_ARRAY[1,I]:=0;
LIGHT_ARRAY[2,I]:=0;
LIGHT_ARRAY[3,I]:=1;

END_FOR;
oRREQUEST:=0;
oLREQUEST:=0;

END_IF;

END_IF;
CASE MODE OF

0: //Idle

1: //There are no requests from pedestrians, the mode will be looping through the three posibilities.
FOR I:=1 TO 3 DO

LIGHT_ARRAY[1,I]:=T_GREEN.Q;
LIGHT_ARRAY[2,I]:=T_YELLOW.Q;
LIGHT_ARRAY[3,I]:=T_RED.Q;

END_FOR;

2: //There is a request from the right side, so the mode case will be looping through the left and main lights
FOR I:=1 TO 2 DO

LIGHT_ARRAY[1,I]:=T_GREEN.Q;
LIGHT_ARRAY[2,I]:=T_YELLOW.Q;
LIGHT_ARRAY[3,I]:=T_RED.Q;

END_FOR;

3: //There is a request from the left side, so the mode case will be looping through the right and main lights.
FOR I:=2 TO 3 DO

LIGHT_ARRAY[1,I]:=T_GREEN.Q;
LIGHT_ARRAY[2,I]:=T_YELLOW.Q;
LIGHT_ARRAY[3,I]:=T_RED.Q;

END_FOR;

4: //There is a request from the right and the left side so the only lights changing are the ones corresponding to the main lights.

LIGHT_ARRAY[1,2]:=T_GREEN.Q;
LIGHT_ARRAY[2,2]:=T_YELLOW.Q;
LIGHT_ARRAY[3,2]:=T_RED.Q;

END_CASE;

For the sequence trigger, the variables used are listed below:

VAR_INPUT

TIME_RED : TIME; // Input parameter of time to set how much time should the lights be on.
TIME_YELLOW : TIME;
TIME_GREEN : TIME;

END_VAR
VAR_OUTPUT

TRIGGER_DOWN : BOOL:=1; // The first traffic light to be triggered is the one in the downside of the junction, after this, the others will follow an anticlockwise sequence.
TRIGGER_UP : BOOL;
TRIGGER_RIGHT : BOOL;
TRIGGER_LEFT : BOOL;

END_VAR
VAR

// Timers used for the triggers. There is a TON per zebra crossing and traffic light.
T_DOWN : TON;
T_UP : TON;
T_RIGHT : TON;
T_LEFT : TON;
TOTAL_TIME : TIME;

END_VAR

 

TOTAL_TIME:=TIME_RED+TIME_YELLOW+TIME_GREEN;
T_DOWN(IN:=TRIGGER_DOWN,PT:=TOTAL_TIME);
IF T_DOWN.Q THEN

TRIGGER_DOWN:=0;
TRIGGER_RIGHT:=1;

END_IF;
//When the downside traffic light has ended  its sequence, it starts the rightside traffic light, following an anticlockwise sequence.
T_RIGHT(IN:=TRIGGER_RIGHT,PT:=TOTAL_TIME);
IF T_RIGHT.Q THEN

TRIGGER_UP:=1;
TRIGGER_RIGHT:=0;

END_IF;

T_UP(IN:=TRIGGER_UP,PT:=TOTAL_TIME);
IF T_UP.Q THEN

TRIGGER_UP:=0;
TRIGGER_LEFT:=1;

END_IF;

T_LEFT(IN:=TRIGGER_LEFT,PT:=TOTAL_TIME);
//Restarting the sequence
IF T_LEFT.Q THEN

TRIGGER_DOWN:=1;
TRIGGER_LEFT:=0;

END_IF;

 

For the pedestrian traffic lights I have used the following logic structure to change a bit which is negated in the visualization (HMI). In other words, red means that the logic structure is false and true means green.

Logic structure

CAR_TL_0 is the downside traffic light for the cars, CAR_TL_LEFT for the left side, and the same for the other blocks. The pedestrian traffic will be on when neither the upside nor downside traffic lights are allowing the car traffic to flow in their main directions (1,2);(2,2) in the matrix of the block CAR_TL. Besides, it will allow crossing the zebra crossing when the traffic light of either the right side or the left side are stopping the car following into the down crossing zebra. This structure has been used for each pedestrian traffic light.

Parking example

Hello everyone!

I have thought of an example with Matrixes, vectors and strings.

The aim of this post is to show how can we monitor how many spare places are available in a parking lot as well as showing the specific place of the spare place.

For simplicity, I have constrained the example to 9 places per floor and 3 floors, however this can be extended to N floors and N places, I will show where to modify it in the code.

Each place has a sensor which is simulated with a push button, when the place is available the led is painted in green, otherwhise it will be darker. For the list, if you are coming from an application level programming world you cannot add strings like in Visual C#, you should use instead the function CONCAT(STR1:= ‘String 1’, STR2:= ‘String you want to add to String 1’).

The algorithm only calculates the places and refresh the list with a variation in the parking with a TRIGGER variable. This will let the PLC more time to execute other things and will only go through the Matrix of places once there is a variation.

Here is the video of this explanation:

The variables used in the code are the following:

VAR_INPUT

MATRIX_PARKING : ARRAY[1..3,1..10,1..10] OF BOOL; //Array that will be receiving the status of each sensor of the parking. The upper limit of this array can be configured as inputs, floors being the first parameter (1..3), y position as second parameter and x position as third parameter.

END_VAR
VAR_OUTPUT

MATRIX_STRINGS : ARRAY[1..60] OF STRING; // This array will contain the strings indicating the available places in the parking lot. At first I thought of writing 60 registers but it ended up in 27 because otherwise I would have spent loads of time drawing all the buttons and leds. The upper limit of this array can be configured as an input so it would be a parameter of variable length.

END_VAR
VAR

//Auxiliary variables to loop through arrays and matrixes.
I : INT; 
J : INT;
K : INT;
L : INT;
TRIGGER : BOOL; // Trigger that will be rised if there is a change reading all the sensors of the parking lot.
MEM: INT:=1; // This variable has the number of cars in the parking lot of the last reading. It starts by one so that the array list is updated in the first cycle. 
SUM: INT; // Amount of cars in the parking lot
SPARES_FLOOR : ARRAY[1..3] OF INT; // Array that will contain the number of places available to park according to each floor (1,2,3) Again, 3 can be a parameter as an input so it could have variable length.

END_VAR

The code is the following:

// Count the current cars in the parking to see whether there’s a variation

// Bear in mind that for each for the upped limit can be changed to be adapted if upper limits are defined as input parameters for this FB.
SUM:=0;
FOR K:=1 TO 3 DO

FOR J:=1 TO 3 DO

FOR I:=1 TO 3 DO

IF MATRIX_PARKING[K,J,I] THEN

SUM:=SUM+1;

END_IF;

END_FOR;

END_FOR;

IF MEM<>SUM THEN

TRIGGER:=1;

MEM:=SUM;

END_IF

END_FOR;
// When there’s a variation (One or several cars get out or get in, the algorithm should recalculate the cars within the parking).

IF TRIGGER THEN

//Reseting the spare places we had on our array
FOR K:=1 TO 3 DO

SPARES_FLOOR[K]:=9;

END_FOR;

FOR L:=1 TO 27 DO

MATRIX_STRINGS[L]:=”;

END_FOR;

FOR K:=1 TO 3 DO

FOR J:=1 TO 3 DO

FOR I:=1 TO 3 DO

IF MATRIX_PARKING[K,J,I]=0 THEN

FOR L:=1 TO 27 DO

IF MATRIX_STRINGS[L]=” THEN

MATRIX_STRINGS[L]:=CONCAT(STR1:=CONCAT(STR1:=CONCAT(STR1:= ‘FLOOR: ‘,STR2:=INT_TO_STRING(K)),
STR2:=CONCAT(STR1:=’ Y POSITION: ‘,STR2:=INT_TO_STRING(J))),
STR2:=CONCAT(STR1:=’ X POSITION: ‘,STR2:=INT_TO_STRING(I)));
L:=28;

END_IF;

END_FOR;

ELSIF MATRIX_PARKING[K,J,I]=1 THEN

SPARES_FLOOR[K]:=SPARES_FLOOR[K]-1;

END_IF;

END_FOR;

END_FOR;

END_FOR;

//Resetting trigger after having complete the refresh sequence.
TRIGGER:=0;

END_IF;

Belt conveyor example

It is in those idle times when human being becomes creative, I happened to live one of those idle moments as waiting for my bag in the airport. It was enough time for me to think about how the conveyor system was automated and the way I would do it.

In the terminal there were sensors but instead of relying on the sensors the system seemed to work with timers.

Today I would like to expose an example of what I thought of, let’s imagine the area where passengers have to take their luggage. The system is quite simple, three conveyor belts, a main conveyor belt which will carry the bags. The owner should take its bag from the main convyor. In addition, there will be two conveyor belts that will transport from a remote area the bags that come from the plane. There will be three sensors for the main conveyor (MC) and two sensors for the inlet conveyors (IC).

The way I thought to prevent bags from falling was locking the specific area from which will fall the following bag, as there are two conveyors, so are locking zones.

In the following video this can be seen easily, I wrote down a code to simulate the sensors to the belt conveyor controller. The bars with the numbers are expressed in ms, they lead the pace of the simulation.

However if a bag falls from an IC the area should be locked too and the corresponding belt should stop to prevent to more bags falling if the last has not passed through the following sensor of the main conveyor. This explanation may be better understood after watching this video:

If a bag is already in the locking zone, the ICs will continue until reaching their respective sensors, it is the moment when the ICs will stop until the main conveyor has retrieved the bags that were passing by the locking areas. The following video is a simulation with bags coming from the conveyor 2 and the main conveyor running.

And last but not least, the two inlet conveyors delivering bags:

As variables I used:

VAR_INPUT

START_CMD:BOOL;
MAIN_BELT_S1:BOOL;
MAIN_BELT_S2:BOOL;
MAIN_BELT_S3:BOOL;
SENSOR_BELT1:BOOL;
SENSOR_BELT2:BOOL;

END_VAR
VAR_OUTPUT

START_MAIN_BELT_CMD:BOOL;
START_BELT1_CMD:BOOL;
START_BELT2_CMD:BOOL;

END_VAR
VAR


LOCK_AREA1:BOOL;
LOCK_AREA2:BOOL;

END_VAR

These lines of code are the ones which make possible to move the conveyors.

(* Starting the conveyor belt *)
IF START_CMD THEN

START_MAIN_BELT_CMD:=1;

ELSE

START_MAIN_BELT_CMD:=0;

END_IF
(* Locking areas when bags passing *)
IF (MAIN_BELT_S1) OR SENSOR_BELT1 THEN

LOCK_AREA1:=1;

END_IF
IF (MAIN_BELT_S2 AND NOT MAIN_BELT_S1)  THEN

LOCK_AREA1:=0;
LOCK_AREA2:=1;

END_IF

IF  SENSOR_BELT2 THEN

LOCK_AREA2:=1;

END_IF

IF MAIN_BELT_S3 AND NOT MAIN_BELT_S2 THEN

LOCK_AREA2:=0;

END_IF

(* Stopping the conveyor 1 if the bag has reached the limit
and there is a bag present in the main conveyor belt*)
IF LOCK_AREA1 AND SENSOR_BELT1 OR NOT START_CMD THEN

START_BELT1_CMD:=0;

ELSIF START_CMD AND NOT LOCK_AREA1 THEN

START_BELT1_CMD:=1;

END_IF

(* Stopping the conveyor 1 if the bag has reached the limit
and there is a bag present in the main conveyor belt*)
IF LOCK_AREA2 AND SENSOR_BELT2 OR NOT START_CMD THEN

START_BELT2_CMD:=0;

ELSIF START_CMD AND NOT LOCK_AREA2 THEN

START_BELT2_CMD:=1;

END_IF

Getting started

Pulse generator function

 

Hello world!

Sometimes I wonder whether the Hello World equivalent for a PLC programmer is starting a VSD by modbus.

Today I wanted to publish a function block which produces high and low pulses a time that is to be configured in the inputs. I have named this function block as PULSE_GENERATOR.

First of all I would like to show the diagram of how this function block works.

Pulse_Generator

The way to do this is far too easy, just two TON instances are to be used to get this. We will be having two inputs of TIME type for the TIME_ON and the TIME_OFF and a boolean as an output for the OUTPUT_PULSE. The variables used for this FB are listed below:

VAR_INPUT
TIME_ON : TIME;
TIME_OFF : TIME;
END_VAR

VAR_OUTPUT
OUTPUT_PULSE : BOOL;
END_VAR

VAR
TON_ON : TON;
TON_OFF : TON;
END_VAR

The code to get the result shown in the image is written below.

TON_ON(IN:=NOT TON_OFF.Q,PT:=TIME_OFF);
TON_OFF(IN:=TON_ON.Q,PT:=TIME_ON);
OUTPUT_PULSE:=TON_ON.Q;

Using this function is handy as not knowing the system bits of a PLC, you just paste the code and you can focus yourself on what really matters. This function can be used for controlling the running time of a started device or just to send requests when communicating with other devices through field bus.

If you have any doubt about the code, the use of a function or you think that this could be done in a better way just tell me, I am all ears and the main purpose of this blog is to keep improving my skills too.