Cool LED Display

Monthly project competitions, chances to earn prizes, you decide project themes, your ideas, your projects, turn ideas into projects.

Back to The Project14 homepage

Project14 Home
Monthly Themes
Monthly Theme Poll




I would like to present something I'm building for my coworkers' kids (as I don't have my own yet ) - a simple traffic light controller. The idea comes probably from the nostalgic feelings, when I remember the days I was playing with model cars, building roads and barriers with wooden and plastic blocks and loved everything that had an LED inside (lights) or outside (Police car beacon). Traffic lights were something that couldn't be purchased those days, I only saw home-made lights for model trains.


The goal is obvious, make a traffic light controller that does approximately the same as the ones you can see in the streets.



Imagine a traffic situation like this:


road situation for traffic lights


and what the traffic lights signal:

This behavior could be performed by a finite-state machine.


Finite-state machines (FSM)

Basically, a state machine is a mathematical model, in which a machine is in exactly one of the defined states. A change of state from one to another is called a transition. I don't want to get too deep into the details, but there is a nice reading on Wikipedia on Finite-state machines.

It is worth mentioning two FSM models: a Moore machine and a Mealy machine.


A Moore machine is a FSM which output state is only determined by the current state. This is our case in this simple project, because we have defined light patterns and the transition between them do not depend on any input. Each state has three attributes: know the LED pattern for the state; have a defined time delay to wait in the state and finally, know to which state to transition.


A Mealy machine is a bit complex model, involving input. Output values are determined by the current state and the current inputs. This could be one of the future improvements of this project, add traffic sensors, buttons for zebra crossings etc.


I can recommend a lesson in an online MOOC named Embedded Systems: Shape the World on edX about state machines. There is a separate support page for this lesson about Finite-state machines. It contains a lot of useful information, both theory and real-life implementation.



MPLAB Xpress Evaluation board

LEDs - I chose diffused, not too bright (not to make the kids blind after looking directly into the LED) that light sufficiently at cca 3 mA.

2x red LED, 2x yellow LED, 2x green LED. (If you want add more ways or lights for a zebra crossing, buy more).

6x 470 Ohm resistor for each LED (or more, as noted above)

some wires

Optionally a battery holder (4x AA batteries), a single-pole, single-throw switch (SPST), perfboard.


MPLAB Xpress Cloud-based IDE - no installation needed (Microchip login needed to save files only)



Here is a link to the schematics of the evaluation board: . The board itself features an integrated programmer (acting as a mass storage device) and a serial interface (USB CDC driver). One user push button, 4 red SMD LEDs, a push button and a potentiometer can also come handy in small projects. In addition to that, there is a populated mikroBUS socket, that can be used with ca. 300 boards, or with your own.


Note: Any other board of your choice can be used, too. The implementation (following below) is purely in SW and without any HW specific dependency.


The only external hardware connected to the evaluation board are the LEDs with resistors. I chose PORTB, pins RB5:RB0. L1 stands for light in one direction and L2 for the light in the other direction. LEDs are connected to the microcontroller with their anodes (through the resistors) and with cathodes to the negative power supply terminal. The resistors limit current to ca. 3 mA through each LED, which seems to be sufficient.

traffic lights


Development environment

I wanted to give a try to Microchip's MPLAB Xpress Cloud-based IDE. It can be used without a registration (Microchip login), but I highly recommend it, because all the project files are then saved in remote location. Microchip engineers provided quite a few examples for various PICs (mostly for the ones on Xpress boards and Curiosity boards). It seems to be a good starting point for anyone not so familiar with these microcontrollers. (If you know the IDE, feel free to skip this part.)


The environment (after creating a new project or opening an existing one) looks like a lightweight IDE. Compared to Arduino IDE, it shows more device information and provides more functionality. Compared to full-features IDEs, it lacks many features and the basic use cases are obvious: create a project (preferably from an example), configure the peripherals using the Microchip MPLAB Xpress Code Configurator - MCC (again, a lightweight version, plus it requires Java Runtime Environment), build the program and download the hex file. Nothing less, nothing more. It offers direct programming and debugging for Curiosity boards, but not the Xpress board used in this example.

In order to use the MCC, JRE is requiered and a browser that is able to run Java plugins - which is in these days only Internet Explorer and Safari. MCC runs as a Java application on your computer and the browser with the IDE communicates with the application.

Similar procedure is used with the Curiosity boards and USB Bridge functionality (which I haven't tried as I don't have any Curiosity board).

And here is the MCC up and running. It allows you to configure the configuration bits of the device (system settings), input/output pins, peripherals etc.

Below is a configuration of output pins for two traffic lights.

I assigned RB5:RB0 pins as outputs and changed the configuration a bit. Default high speed internal oscillator is running at 4 MHz (which gives us 1 000 000 instructions per second = 1 MIPS), which is much faster than we need. I changed this to low frequency internal oscillator running at 31 kHz (7 750 KIPS). Feel free to make the speed even lower, the 31 kHz can be divided by 1 up to 512, meaning ca. 15 instructions per second. I have to admit I haven't measured the power consumption yet. At 15 instructions second it should be significantly lower than at 1 MIPS There are more ways to lower the power consumption to make this device last longer on a battery - we will get to that later.


The output is generated using the Generate button as a set of header and c source files directly into the running cloud IDE. pin_manager files hold the configuration of pins (analog / digital inputs / outputs, ...) and mcc files hold the configuration of the device and its peripherals. Lots of useful functions are defined:



I started from a blank project and made a configuration using the MCC (as shown above in the schematics and in a screenshot of the MCC).


After all the information I already wrote, the implementation is pretty straight-forward.

1) States are numbered, so it's easy to reference to them.

2) LED patterns assigned to the states are defined.

3) Lengths of time delays are defined.

4) A struct is defined, to hold all the necessary state information. As described above, each state has three attributes: LED pattern of the state; a time delay to wait in the state and a reference to the next state.

5) The program code consists of 2 parts:

     5-1) an initialization of the hardware (pins, oscillator, ...), setting the first state to start with

     5-2) the main loop, which endlessly sets the output pins to show the current LED pattern, delays (simple busy loop used here) the defined amount of time and sets the next state.


The program code in main.c file is shown below (I removed most of the comments generated by MCC to be easier to read)

// Traffic Light Controller v0.1

#include "mcc_generated_files/mcc.h"

// state numbers, S0 - S7
#define L1_RED_L2_GREEN 0
#define L1_RED_L2_YELLOW 1
#define L1_RED_L2_RED 2
#define L1_RED_YELLOW_L2_RED 3
#define L1_GREEN_L2_RED 4
#define L1_YELLOW_L2_RED 5
#define L1_BOTH_RED 6
#define L1_RED_L2_RED_YELLOW 7

// state output values
// LEDs connected to PORTB
// RB7:RB6 - not connected
// RB5: light 2 green LED
// RB4: light 2 yellow LED
// RB3: light 2 red LED
// RB2: light 1 green LED
// RB1: light 1 yellow LED
// RB0: light 1 red LED
#define L1_RED_L2_GREEN_OUTPUT 0b00100001
#define L1_RED_L2_YELLOW_OUTPUT 0b00010001
#define L1_RED_L2_RED_OUTPUT 0b00001001
#define L1_RED_YELLOW_L2_RED_OUTPUT 0b00001011
#define L1_GREEN_L2_RED_OUTPUT 0b00001100
#define L1_YELLOW_L2_RED_OUTPUT 0b00001010
#define L1_BOTH_RED_OUTPUT 0b00001001
#define L1_RED_L2_RED_YELLOW_OUTPUT 0b00011001

// period definitions in seconds
#define GO_PERIOD 5 // green in each direction
#define TRANSITION_PERIOD 2 // transition involving a yellow light
#define BOTH_RED_PERIOD 1 // how long there should be both red lights to clear the crossroad

// struct for holding the configuration of a state
typedef const struct  {
    unsigned char TLoutput;  // TLoutput is traffic lights output - saying which LEDs should be lit in a particular state
    unsigned int delay;      // delay [seconds], how long will a state last
    unsigned char nextState; // nextState - which state will follow this state
} TLState;

// definition of the state machine for the traffic lights
TLState TrafficLightFSM[8] = {
    {L1_RED_L2_GREEN_OUTPUT, GO_PERIOD, L1_RED_L2_YELLOW},              // L1_RED_L2_GREEN
    {L1_GREEN_L2_RED_OUTPUT, GO_PERIOD, L1_YELLOW_L2_RED},              // L1_GREEN_L2_RED

// variable to hold the current state
unsigned char currentState;
// variable to create a delay longer than a second
unsigned char delaySeconds;
                         Main application
void main(void)
    // initialize the device - pins and an oscillator
    // set the state in which the FSM starts
    currentState = L1_RED_L2_GREEN;

    // enless loop
    while (1)
        // output the current state LED pattern to PORTB
        LATB = TrafficLightFSM[currentState].TLoutput;
        // delay for a desired number of seconds
        // embedded delay library can have problem with generating longer delays with higher frequencies
        for (delaySeconds = TrafficLightFSM[currentState].delay; delaySeconds > 0; delaySeconds-- ) {
        // determine the next state from the current state
        currentState = TrafficLightFSM[currentState].nextState;
 End of File


Here is a video proof of work. Please excuse that it is not a finished product yet, I am trying to figure out how to put all this into a pillar or a box.


What to do next

There are many ways to improve this construction.

1) Embed the hardware into a suitable box and create an imitation of a traffic light pillar.

2) Add LEDs to make it a 4 way controller, add zebra crossings lights.

3) Make the delays for changing the states variable - for example using the on-board potentiometer.

4) Add a manual mode, as driven by a Police officer.

5) Add a mode, where the traffic lights are turned off (blinking yellow on both lights) with transitions to and from the running state.

6) Some countries use a different program (for example Austria has a green light, then blinking green light and then orange).

7) Advanced: add sensors to make it a Mealy FSM - use optical / capacitive / inductive sensors on the floor (road) that will provide inputs for regulating the traffic dynamically.


You and your kid can have other special requirements - almost everything is possible, since the microcontroller makes all the changes easy to do.

If you have any other ideas, suggestions or questions, let me know in the comments below.


Thank you for reading and watching.