Electronics and programming for kids: make a traffic light

My youngest daughter (age 6) asked us, out of nowhere (as children often do) if traffic lights had fireflies inside. Despite her doubtful tone, I was worried that my daughter entertained that notion even for a minute. And I realized that too much time had passed without a technological activity with my daughters.

Marco Amado
13 min readJan 31, 2018

What you’ll need

  • One or more kids aged 5 and beyond. I’ll touch on what you can explore depending on age later;
  • A handcraft specialist. I suck at that. Luckily, my wife’s a kindergarten teacher, so we’re covered, but you can make do with at least one of the kids knowing his/her way around glue, tape and a pair of scissors;
  • A medium sized box for the traffic light (a cookies box, for instance) and several small boxes for padding inside that one (we went with old boxes of pills);
  • An Arduino. There’s a lot of them nowadays, but an Uno is a good all-around that you can make more projects in the future with. It doesn’t even have to be an original Arduino, it can be a clone (mine is);
  • The Arduino IDE. Install it, connect your Arduino to your PC (give Windows, if that’s your poison, some time to detect and install the needed drivers), and choose the correct Arduino version in Tools » Board;
  • LED: two red, two green, one yellow. Well, it depends on your country, but most are like this;
  • Six resistors. They come in all sorts of resistance, and it depends on the chosen LED, but the math involved is beyond this article — so, if you bought the most standard LED, let’s go with 220Ω (reads “ohm”);
  • Assortment of jumper cables. Try to match the color of the jumper cables to the color of the LED they’ll connect to, and black ones for ground;
  • One or two breadboards.

You’ll be able to buy kits with everything needed from most online shops. For my Portuguese readers, I do my shopping at PT Roboticshere are some kits. My international readers probably have shops on their country, but there’s always Adafruit. And oh so much more out there.

Exploring the way traffic lights work and why — all ages

Cars must stop for pedestrians to cross. Cars are heavy and difficult to stop, so that’s why the yellow light exist. Some drivers can’t (and some won’t — there’s bad people everywhere) break and stop at the yellow light, so that’s why the green for pedestrians don’t light up immediately after the red for cars.

Conversely, pedestrians must wait for their green light. Pedestrians are slow, and that’s why the green blinks before turning red (well, in Portugal it blinks — your mileage may vary depending on country), and also that’s why the green for cars don’t light up right after the red for pedestrians.

Don’t explain this stuff. Ask the kids around and try to steer them into the conclusions, but let them get there alone.

Then, draw a timeline on a piece of paper (math or millimetric paper is better). Start with green for cars and red for pedestrians. Ask the kids what the timing should be. The full cycle is like this (in Portugal):

  • How long should the cars have the green light, before it starts to blink yellow? Make them understand that cars can be slow to start driving, and that it needs to allow at least half a dozen cars to pass, or drivers will go nuts;
  • How long should it blink yellow before the red? Drivers need to make fast decisions if they can stop or pass safely during this period;
  • How long after the red for cars should the green for pedestrians light up? Remember, some drivers will try to pass with the blinking yellow light almost up. Heck, some will pass even after the red lights up. Keep your pedestrians safe;
  • How long before the green for pedestrians starts to blink? Account for old or disabled people and wide streets.
  • How long should it blink before the red? Again, remember slow pedestrians that may be caught mid-street.
  • And finally, how long after the red for pedestrians the green for cars should light up? Still keep in mind that some pedestrians are really slow, and they couldn’t make it after they saw the blinking green mid-street.

Assembling the project

Preparing assembly — suitable for ages 5+

Your kids should now have a deep understanding of what is needed. Small kids will be able to count up to ten, so they can scavenge the parts. Five resistors, five LED (two red, two green, one yellow), six jumper cables (two red, two green, one yellow, one or two black)

Depending on the child, you can also explain how a breadboard works, and guide him/her on the assembly. I’ll explain it further in the next section.

Assembly — suitable for ages 8+

If you have your Arduino connected to your computer, disconnect it now.

Breadboards (or protoboards, or plugboards — originally all of these terms referred to different things, but today they’re generically the same thing) are pieces of plastic with holes, that are connected in a certain way. The underlying PCB is easier to understand:

PCB breadboard — Florian Schäffer, CC BY-SA 4.0

As you can see, several holes are connected to each other. For instance, the two horizontal lines on top and below (called bus) are generally used for Vcc (power) and ground, and are clearly marked that way. The vertical lines (called strips) are where you usually connect your components, using those lines to connect components to cables or other components.

You can also explain some features of the components we’re using.

Resistors are used to lower the intensity of the current flowing through the LED. Too much intensity, and the LED may “burn” (relax, they don’t actually burn — they just die). However, too less intensity, and they won’t light up. If you really want to know (or if you have older kids), look up Ohm’s Law.

LED stands for Light Emitting Diode, and, like the name implies, they’re diodes. Diodes must be connected in a certain way, and won’t work (or will break) if connected the other way around.

If your LED have one short leg and one long leg, the shorter leg (called cathode) must be connected to the ground, and the longer (called anode) to Vcc (power), protected by the resistor. If, however, the legs are the same, look at the inside of the LED — it should have two metal parts inside, connected to each leg, one big, one small. The leg connected to the smaller part is the anode, and should be connected to Vcc (again, protected by the resistor).

What we’re building is this (on two breadboards, but it can be in a single one, side-by-side):

Made with Tinkercad

Note that the LED are connected with two legs on main zone of the breadboard, but you can connect the cathode (the shorter leg, the ground) directly to the ground bus (the horizontal line at the top marked with “-”), and avoid the jumper cables that connect the anodes to that bus.

You’ll be ending with something like this:

Programming — suitable for ages 8+

The programming part is somewhat more complicated, so I’ll make a walkthrough with some points for you to discuss with your kids. I’ll introduce some concepts first. Feel free to skip this if you’re a programmer and can explain it to your kids.

Concepts:

  • Programs almost always have variables and sometimes constants. Variables are used to store something you’ll want to use later. Constants are almost the same, but the value you want to store must be declared upfront and never changed.
  • Functions are pieces of code that can be called in other points of your program.
    They can be used for code reuse (if you catch yourself writing the same code more than twice, make it a function), or to reduce complexity — for instance, if your main function is clearly stated “do this then do that”, but if “do this” and “do that” are several lines long, you can isolate that code into two functions doThis() and doThat(), and you gain readability in your main function.
  • In C++ (the language used by the Arduino), variables, constants and functions have types. These indicate what kind of data you can store in a variable or constant, and what kind of data a function returns.
    The most basic type is void, is exclusive of function returns, and it simply means “nothing” — i.e., the function returns “nothing”.
    Then we have integer types, which we’ll be using in our program. Integer types come in several sizes, and can be both signed and unsigned. The smallest is byte, and it’s unsigned from 0 to 255. Then we have int, which is signed, from -32,768 to 32,767. And finally long, which is signed, from -2,147,483,648 to 2,147,483,647.
    Both int and long can be modified with unsigned, which means their range changes, for int, from 0 to 65,535, and for long, from 0 to 4,294,967,295.

Define to what Arduino pin each light is connected

As you can see, this is straightforward enough. Just define some constants (with the keyword const) of the type byte, give them meaningful names, and set which pin it is — refer to the image above or your own assembly to check which is which.

The NO_LIGHT_PIN is just a “dummy” connection. Don’t connect anything to this Arduino pin. It’ll be used for situations where no light should be on (on the “off” part of the blinking cycle).

Define the timings

I’ve set short times, just because it’s faster to check if it works properly. You can change them later to larger intervals.

Take a look at the comments — most of the times are in seconds, but the blinking interval and the total time is in milliseconds. Also take note that the total time is calculated from the timings set above.

Define the skeleton of the program

Every Arduino program (called sketch) has two (somewhat) mandatory functions: setup() and loop().

The setup() function is called only once as the Arduino powers up, and is used for initialization. This is mostly bootstrap — you almost always have to define the serial interface and speed (in case you later want to communicate back to the computer, for example), and you have to declare whether the pins you’ll be using are for output or input (all of them will be output for this).

The loop() function is called, well, in a loop, as fast as possible, based on the Arduino internal clock. Be mindful that, as you pile up more and more code to be executed in each loop, the more time it takes from one loop to the next, because each loop takes more time to be executed.

So, what do we need to do in each loop?

  1. Calculate in which part of the cycle we are. We’ll use the Arduino timing functions to tell the time since the device was powered on, and some basic math to translate that time into a time that fits in our cycles (global and blinking cycle);
  2. Turn off all the lights, because we’ll be lighting up only the correct one next;
  3. Turn on the correct light for the cars part;
  4. Turn on the correct light for the pedestrians part;

Note that, because we’re turning off and then on some lights in the same loop, the light won’t actually blink — or it’ll blink so fast, that the human eye can’t register the event.

So, let’s take care of those two functions, and call some nonexistent (for now) functions that will do what we want:

So, setup() has what we previously talked about, and loop() has the steps we have to make at each loop. The function digitalWrite() is part of the Arduino library, and puts a pin in either HIGH or LOW state (5V or 0V).

What about all the rest?

Calculate cycles times

The Arduino library includes some functions to tell the time. A good all-arounder is millis(), that tells us the time passed since the Arduino was powered up (or reset). This function returns an unsigned long, and it’ll “turn around” in about 50 days. Long term applications should account for this, but I’m guessing you won’t have this on for 50 days at a time, so…

We’ll be needing a variable to store when was the last time our loop ran, that we’ll call (imaginatively) lastTime, and two variables to store at which point in the global and blinking cycle we are, called elapsedTime and elapsedTimeBlinking.

What we have to do now, is increment our elapsedTime variable with the time that passed since last time until now. That’s easy enough, and can be expressed mathematically with elapsedTime = elapsedTime + (now - lastTime).

But what happens when elapsedTime gets bigger than totalTime? That’s not supposed to happen, because then we don’t know where we are in the cycle — we’ll be forever “out of cycle”. Luckily, we can use the remainder of a division (which is represented by the operator % in C++ code) to help us. Let’s say that our totalTime is 10, and that we are already at 11; because the remainder of 11÷10 is 1, we now we’re back at time 1, and not 11. Please note that the remainder of 10÷10 is 0, which is OK: times start at zero anyway.

As for elapsedBlinkingTime, we can use the same strategy, but use the remainder of elapsedTime÷BLINKING_TIME directly. What we’re doing is just assume that, even if no light is currently blinking, the timer that controls the blinking is always running. Actually, we’ll use the double of BLINKING_TIME, because half the time the light is on, and the other half is off.

The next snippet of code should make sense now:

Turn off all lights

As discussed before, the Arduino library provides digitalWrite(), so this function is dead simple:

Turn on the correct lights

Now that we’ve calculated the cycle time and turned off all the lights, it’s time to turn on the correct lights. This is a matter of checking conditions.

For cars, the conditions are as follows:

  • If the cycle time is below than the cars green timing, light up green;
  • If not the above, but if the time is below than the cars green timing, plus the cars blinking yellow timing, blink yellow;
  • If not all of the above, light up red.

For pedestrians, is similar:

  • If the cycle time is below than the cars green timing, plus the cars blinking yellow timing, plus the pedestrian hold green timing, light up red;
  • If not the above, but if the time is below than the cars green timing, plus the cars blinking yellow timing, plus the pedestrian hold green timing, plus the pedestrian green timing, light up green;
  • If not the above, but if the time is below than the cars green timing, plus the cars blinking yellow timing, plus the pedestrian hold green timing, plus the pedestrian green timing, plus the pedestrian blink green timing, blink green;
  • If not all of the above, light up red.

The blinking section is similar in both of the previous situations, and it goes like this:

  • If blinking cycle time is below the blinking timing, light up the specified LED;
  • If not, just light up the “dummy” light we declared for this case.

And the code is a simple translation of the above:

Connect, upload and run

Connect your Arduino to your computer now.

When you’re ready, hit that upload button, and in a few seconds your traffic light should be working!

Doesn’t? Here are some tips:

Disconnect your Arduino and check all your connections.

  • Check if all the LED are connected the right way (shorter leg is ground);
  • Check if the ground from the Arduino is connected to the ground bus on the breadboard (top or bottom) and if the LED are connected to the same bus;
  • Check if the jumper cables connect to the right pins on the Arduino;

Connect your Arduino again and check the code.

  • Verify the code with the check mark button;
  • Re-upload. Sometimes (although very rarely) code isn’t properly uploaded;
  • Check your variable names and it’s values;

Assemble it on a lovely box

Arguably, some kids will like this part best.

Plan where the LED will peak out of the box. Make some holes there. Put some smaller boxes as padding, to hold the breadboards with the LED slightly outside the box through those holes.

Place the Arduino on a corner, and make a hole for the USB cable.

Remember, the Arduino can be powered not only via your PC, but also via a power bank or even a cellphone charger. It’s useful if your kids want to take their traffic light to show at school or something.

Wrap your box. We went with matte paper and painted white on top, but there’s no wrong choices here.

Close the box with duct tape. Fold the end of the duct tape on the lid, to make it easier to open.

Connect the USB cable on the outside to something, and appreciate your fully functional traffic light. Give a full round of pats on the back for a job well done, and tackle your next challenges.

Wait, what? This isn’t done?

Gosh, no!

Here are some ideas to do next:

  • What if a pedestrian is in a hurry, and no car is passing anyway? Add a button so that your pedestrians can ask for a green sooner. Tip: jump to a point in your global cycle just before the yellow for cars start to blink;
  • Remember that roads usually have two ways, so there should be two traffic lights for cars. Also, roads have two sides, so there should be a traffic light for pedestrians in either side. Tip: several LED can share an Arduino pin, just connect more cables to the same strip in the breadboard;
  • What about a crossroad? There will be more timings at play!

Pro-tip: remember to always have fun with your kids!

--

--

Marco Amado

Developer, intermittent blogger, overall tech tinkerer, football (the european one) fan, politics cynical. Bearded, most of the time. Husband. Father.