top of page
Search
Writer's pictureIvaylo Fiziev

Practical use case: Traffic light controller


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!

0 views0 comments

Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating
bottom of page