Arduino traffic lights simulator, part 3

In the previous two parts, I have shown how to build a 2-way traffic light and how to write a sketch to control it.

This time we’ll rewrite the sketch to use state tables.

In microcontroller programming, we are often dealing with a set of well defined states.  State tables describe what each state means, rules for transitioning between the different states, for what is allowed and what is expected.  Keeping this in a set of tables helps keep the code simple by avoiding a big, tangled mess of if-else statements.  This, in turn keeps the code smaller so we can do more with the rather limited memory on the microcontroller.

Program state

Looking at the traffic lights, each cycle has 6 distinct states:

State Left side Right side
0 Green Red
1 Yellow Red
2 Red Red
3 Green Red
4 Yellow Red
5 Red Red

We’ll start a new sketch but this time we’ll not define the pin numbers by using #define.  This time we’ll declare an array of 6 pin numbers:

const uint8_t pins[6] = { 9, 10, 11, 8, 6, 5 };

Doing it this way means we can use a loop to process the pins.  It doesn’t give the same level of flexibility as setting pins individually, but it is more compact.  We are also not going to modify any of these values in the code, so we tell the compiler they are constants. This allows the compiler to validate that we are not trying to write to somewhere we shouldn’t, and to make certain optimisations.

And here comes the state table.  We declare a two-dimensional array of pin states, describing the table above:

//                               Left            Right
//                               Red  Yel  Grn   Red   Yel  Grn
const uint8_t lights[6][6] = { { LOW, LOW, HIGH, HIGH, LOW, LOW },  // State 0
                               { LOW, HIGH, LOW, HIGH, LOW, LOW },  // State 1
                               { HIGH, LOW, LOW, HIGH, LOW, LOW },  // State 2
                               { HIGH, LOW, LOW, LOW, LOW, HIGH },  // State 3
                               { HIGH, LOW, LOW, LOW, HIGH, LOW },  // State 4
                               { HIGH, LOW, LOW, HIGH, LOW, LOW } };// State 5

Each row contains the values that should be written to each of the LEDs for that particular state.  For example, state 1 says that in the left channel red is off, yellow is on, green is off.  Right channel has red on, yellow and green off.  The state table is done, but we’ll need somewhere to track the current state.  As we’ll need to track this between each call to loop(), this will be a global variable.

int state = 0;

Now, let’s look at the setup() function.  We have replaced the individual pinMode() and digitalWrite() with a for-loop.  It still achieves the same result as in the original version.

void setup() {
  // Set all 6 pins to output and make sure the LEDs are all off when the sketch starts
  for( int idx = 0; idx < 6; idx++ )
    pinMode( pins[idx], OUTPUT );
    digitalWrite( pins[idx], LOW );

The loop() function is a lot more interesting this time.  Again we have replaced the individual calls to digitalWrite() with a for-loop.  The for-loop processes each LED pin in turn, and is using the state number to choose which row in the lights array to read.

void loop() {
  for( int idx = 0; idx < 6; idx++ )
   digitalWrite( pins[idx], lights[state][idx] );

For each pass through the loop() function, we’ll increase the state number by one.  If we reach 6, we’ll start over again from 0.

if( state == 6 )
  state = 0;

Finally, as we’re going to fit a push button into the circuit later, we’ll just do a simple 5 second wait for now.

 delay( 5000 ); // Wait 5 seconds

Compile and upload to the microcontroller and verify that it is working.  The traffic lights should work as before, just with a fixed 5 second timing.

When compiled for the ATmega328 on the Pro Trinket board, the resulting binary is 1128 bytes, compared to 1076 bytes for the original version.

As usual, you can grab the whole sketch from here:

Next time, how to add a push button…



Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.