Traffic lights are simple devices that are quite common but do you know how they work? In this post I'll try to implement a traffic light controller in Process Simulate based on this article: https://www.101computing.net/traffic-lights-controller-using-logic-gates/
The theory says we need two Boolean inputs (j,k) and three Boolean outputs (x,y,z). Based on the inputs we build a truth table that the controller uses to switch the lights on or off. Sounds familiar? If you recall a truth table was also used for the seven segment indicator. Here it is even more simple since the table can be represented using logic gates. As the article says the traffic light has four different states for the lights based on the inputs.
00 = RED
01 = RED and AMBER
10 = GREEN
11 = AMBER
In terms of code the controller looks like this:
FUNCTION_BLOCK "TRAFFIC_LIGHT_CTRL"
VERSION:1.0
VAR_INPUT
J:BOOL;
K:BOOL;
END_VAR
VAR_OUTPUT
X:BOOL;
Y:BOOL;
Z:BOOL;
END_VAR
BEGIN
#X := not #J;
#Y := #K;
#Z := #J and not #K;
END_FUNCTION_BLOCK
Now how do you apply this to the resource? Just like any other FB. Declare a static variable and use it. Instead of having two separate inputs here I am using a single Byte input and use the bits inside as Boolean variables. This allows for easier control of the lights since you work with a single numeric value (0 - 3) instead. This numeric value defines the state of the traffic light.
The main script looks like this:
FUNCTION_BLOCK "MAIN"
VERSION:1.0
VAR_INPUT
IN : BYTE;
END_VAR
VAR
ctrl : "TRAFFIC_LIGHT_CTRL";
X : BOOL;
Y : BOOL;
Z : BOOL;
red : "ARGB";
green : "ARGB";
amber : "ARGB";
END_VAR
BEGIN
if SIM_TIME() = 0 then
// set colors
#green.g := 255;
#red.r := 255;
#amber.r := 255;
#amber.g := 215;
end_if;
// call the controller
#ctrl(J:=#IN.%X1, K:=#IN.%X0, X=>#X, Y=>#Y, Z=>#Z);
if #X then
EMPHASIZE(NAME:='Red', COLOR:=#red); // emphasize 'Red' entity
else
DEEMPHASIZE(NAME:='Red', COLOR:=#red);
end_if;
if #Y then
EMPHASIZE(NAME:='Amber', COLOR:=#amber); // emphasize 'Amber' entity
else
DEEMPHASIZE(NAME:='Amber', COLOR:=#amber);
end_if;
if #Z then
EMPHASIZE(NAME:='Green', COLOR:=#green); // emphasize 'Green' entity
else
DEEMPHASIZE(NAME:='Green', COLOR:=#green);
end_if;
END_FUNCTION_BLOCK
So far so good. Now you can control a single traffic light. How about controlling the traffic lights on a classic four way junction? This one requires some logic on top of the traffic lights. Basically timers and state synchronization. Sounds easy right? It is just another script. If we assume that the timing of each state is the same it is really straight forward. We only need a single timer. The code looks like this:
FUNCTION_BLOCK "MAIN"
VERSION:1.0
VAR_OUTPUT
state1 : BYTE;
state2 : BYTE;
END_VAR
VAR
timer : TP_TIME;
END_VAR
BEGIN
if SIM_TIME() = 0 then
#state1 := 0; // initial state of traffic light 1
end_if;
#timer(IN:=true, PT:=T#10s); // 10s timeout
if not #timer.Q then
#state1 := (#state1 + 1) mod 4; // change the state of traffic light 1 #timer(IN:=false); // reset the timer
end_if;
#state2 := (#state1 + 2) mod 4; // sync the state of traffic light 2
END_FUNCTION_BLOCK
Here we change the state of the first traffic light when the timer elapses. The state of the second traffic light is always kept in sync with the state of the first traffic light since they are positioned on mutually exclusive roads. How do we sync them? Just keep the second one two states ahead of the first one.
Of course this is not a realistic situation since the timing of each state generally is not the same. The green/red lights are kept ON for longer periods of time compared to the amber light. Sometimes we need change the timing of the green/red light in order to give priority of a major road relative to a minor one. Other times the lights should be manually controlled in case of an emergency situation. So it is not that easy in the real case.
To change the timing for each state we'll need an additional variable to store the current provisioned time. Once the timer elapses we'll change the provisioned time so that the timer will use it after reset.
FUNCTION_BLOCK "MAIN"
VERSION:1.0
VAR_OUTPUT
state1 : BYTE;
state2 : BYTE;
END_VAR
VAR
timer : TP_TIME;
duration : DINT;
END_VAR
BEGIN
if SIM_TIME() = 0 then
#state1 := 0; // initial state of traffic light 1 #duration := 10000; // initial duration (10s)
end_if;
#timer(IN:=true, PT:=DINT_TO_TIME(#duration));
if not #timer.Q then
#state1 := (#state1 + 1) mod 4; // change the state of traffic light 1
case #duration of // change the timing
10000: #duration := 2000; // 2s
2000: #duration := 10000; // 10s
end_case;
#timer(IN:=false); // reset the timer
end_if;
#state2 := (#state1 + 2) mod 4; // sync the state of traffic light 2
END_FUNCTION_BLOCK
The effect here is that the states where the amber light is ON will take 2 seconds while the other states (green/red) will take 10 seconds.
I can continue to extend this more but time is short for me.
I'll leave the rest to you :)
See you!
Comments