Introduction: LED Traffic Light Halloween Costume

Halloween is great for makers and engineers. It's a chance to make something fun and show it off - usually with a lot of blinky LEDs.

This Halloween, I decided to redo my costume from a few years ago: a traffic light!

This is a fun and simple costume that everyone recognizes off-the-bat.

My costume is not just a traffic light. It can be controlled 'manually', has a 'disco' mode with several speeds, and has adjustable brightness.

Recreating this costume can be very easy.

For this project, you'll need:

  • (2) gold tshirts Here
  • access to a 3D printer. Alternatively, you can make the parts with cardboard, paint, and other things easily found about the house
  • red, yellow and green LEDs. Alternatively, addressable LED strips could be used. I got my LEDs from here
  • an Arduino, or other microcontroller. Included in this Instructable are design files for a PCB for this project. It uses a ATMega328P, as found on the Arduino UNO. The Arduino Micro, Nano, Pro Mini, or basically any Arduino with 8 GPIO (at least 3 PWM, and 1 analog) should work. Of course, non Arduino microcontrollers will also work. NOTE: should be a 5V microcontroller
  • some buttons, and a potentiometer
  • some capacitors, resistors, and connectors (if using my designed PCB)
  • sewing needle and gold thread
  • glue
  • wire
  • perfboard (optional)
  • soldering iron and solder (optional)

Step 1: Electronics

The electronics controlling the traffic light costume are not that complicated. They consist of a microcontroller, a crystal oscillator, three MOSFETs, a potentiometer, some buttons, and various resistors and capacitors - all of which can be had in through-hole or SMD packages, and assembled on perfboard, or on a custom made PCB.

My design uses an ATMega328P - the same microcontroller found on the Arduino UNO. However, there are literally thousands of microcontrollers that will work for this project.

The ATMega328P comes in both SMD QFP package, and through-hole DIP package, so it's easy to use on perfboard. The 16MHz oscillator also comes in SMD and through-hole packages.

The ATMega328P can also be found on the compact Arduino Mini, and eliminates having to deal with the oscillator. Note, however, that the Arduino Mini needs a serial to USB adapter to be programmed.

An alternative to the ATMega328P is the ATMega32U4, as found on the Arduino Micro. In fact, I recommend using an Arduino Micro for beginners, as it takes care of the complex oscillator and other components. It can also be purchased online for very cheaply.

The Arduino Micro, Nano, Pro Mini, or basically any Arduino with 8 GPIO (at least 3 PWM, and 1 analog) should work. Of course, non Arduino microcontrollers will also work. NOTE: should be a 5V microcontroller

Other microcontrollers can be used as well, however, the Arduino ecosystem provides fast development, and even though a lot of the libraries aren't the fastest, the firmware for the traffic light costume is not demanding at all.

Using an SBC, like the Raspberry Pi is possible, but the lack of ADC, and much higher power consumption make it not recommended.

Output MOSFETs

To be able to control the high current LEDs, the MOSFETs are used. The microcontroller alone cannot power the LEDs, as they draw too much current. The ATMega328P has a per pin current limit of about 40mA.

When choosing a MOSFET, there are a few specifications that need to be met:

  • Gate Threshold Voltage (Vgs) Since the microcontroller runs off 5V, the MOSFETs need to be logic level MOSFETs - meaning the gate threshold needs to be 4.5V, and preferably less. SMD MOSFETs, in smaller packages tend to have a lower gate threshold - about 3.3V is ideal. Through-hole MOSFETs can have much higher gate thresholds, so be careful when choosing one.
  • On resistance (Rds On) Another important specification of the MOSFETs is the on resistance. The higher the on resistance, the more power it will dissipate, and the hotter it will get. For the LEDs I used (about 300mA max), an on resistance of 500mOhms or less was ideal, and keeps the MOSFETs cool to the touch.
  • Max Continuous Current (Id) Current handling of the MOSFETs depends on the LEDs you use. For the traffic light costume, the LEDs don't need to be extremely bright, so 500mA or less should be good enough.

If, like me, you use individual LEDs, you can put them in parallel, so that the voltage drop does not exceed 5V. This is desirable, so that you don't need to boost the voltage from the battery. If, however, you use LEDs that require a higher voltage, or you use many LEDs in series, you need to choose a MOSFET with appropriately high voltage rating. Most MOSFETs having voltage rating well above 5V.


The controls for the traffic light costume are pretty simple - just four buttons and a potentiometer (pot). This could be simplified even more, with just a potentiometer, if preferred.

Any momentary button will do for the controls. You may, however, find some have better or worse switch characteristics. Some buttons may 'bounce', and will activate functions more than once, even if the button was only pressed once. This is easily fixed in software by increasing the "debounce delay".

My design uses SMD tactile buttons and a through-hole 'right angle' pot, as found in handheld radios, etc. The pot is such that is sits fairly flush with the PCB, while part of the wheel sticks out for easier control. Volume pots are too large, and most trimmers require a screw driver to adjust.

Resistors and Capacitors

There are some resistors and capacitors that are needed. If you use a pre-made microcrontroller, such as the Arduino Micro or Mini, a lot of these are already placed for you.

Most crystal oscillators require load capacitors - typically around 18pF - placed as close as possible to the crystal.

The microconrtoller should have at least a 100nF ceramic cap near its power pins, and a larger (47uF) electrolytic cap. If you are also powering the LED of the same supply as the microcontroller, 100-220uF is recommended.

If using a separate supply for the LEDs (a boost converter, for example), at least a 100uF should be used.

When choosing capacitors, a good rule of thumb is to choose a voltage rating 50%+ more than the max voltage it will see in the circuit.


There are three types of connectors used. One is a USB micro connector. This was chosen for easy connection to a USB battery. The second is the hobbyists favorite 0.1" (2.54mm) dupont header pins. These are using to interface to the serial/USB adapter, the ICSP, and the control PCB. The last connector is the 3.5mm screw terminal. These are rated for higher current than the header pins, and used to interface to the LEDs.

Step 2: Electronics: Custom PCB

For the traffic light costume, I designed a custom PCB, and used mainly SMD components. I also had the PCB manufactured by OSH Park. Since I only needed one, I decided to design my PCB in such a way that I could be used for other purposes. This means that the PCB is a bit more complicated that it needs to be for controlling the traffic light. I will describe how the design works, the essential parts related to controlling the traffic light costume, and alternatives for both through-hole and SMD designs.

I have included the schematic and PCB design in this Instructable, along with the specific parts I used. If you decide to use my design, you should be fairly confident in SMD soldering, otherwise, stick to through-hole parts on perfboard.

The PCB has two parts - the microcontroller board, and the controls board. I put them together so that I could order them online, and not have to pay extra for two boards. I cut mine using a band saw, but this can also be done with a hack saw, or a sharp utility knife. Always use eye protection and a mask when cutting fiberglass PCBs!

Here is a list of the parts i used for the PCB i designed. I have included the part name as it appears on the PCB

  • Microcontroller: ATMega328P/ATMega168P/ATMega88P (all pin-compatible) Found here
  • C1,C3-C6,C8,C10,C16,C17 0805 100nF Ceramic capacitor
  • C7,C15 220uF 6.3V or higher, 2.5-6mm Electrolytic capacitor Found here
  • C18,C19 18pF 0805 NP0/C0G Ceramic capacitor Found here
  • R1,R8-R11 10K Ohm 0805 resistor
  • R2-R4 0R 0805 Jumper
  • R12, R13 330R 0805 resistor
  • Crystal oscillator: 16MHz 2-SMD Found here
  • MOSFETs: N-chan, SOT223, Vgs <= 3.3V, Vdss > 24V, Cont. drain current >= 1A Found here
  • 5x5mm tactile momentary SMD button Found here
  • 6x3.8mm tactile momentary SMD button Found here
  • Micro USB SMD connector Found here
  • 3.5mm screw terminals Found here
  • LED1,LED2 1206 SMD LED Found here
  • Potentiometer Found here


The microcontroller is probably the most difficult part to solder, so I suggest starting with it, then moving on to it's supporting components - oscillator, capacitors, ICSP header and reset circuit. Once you have these in place, you can test it with a program like AVRdude to make sure it's working correctly. If so, then the rest of the parts can be soldered. Start with the physically shortest parts, and work your way up. Use plenty of flux!

Powering Up

Make sure you have the polarity of your power supply correct! This PCB has no reverse polarity protection, or over-voltage protection, so the microcontroller can be very easily fried.


My design uses an ATMega328P - the same microcontroller found on the Arduino UNO. I chose this microcontroller because, with the Arduino Optiboot bootloader, it works, and programs, just like an Arduino UNO with the Arduino IDE. This requires a 16MHz oscillator. Either a crystal oscillator, or a less accurate ceramic oscillator, will do, as there is no critical timing needed.

The ATMega168P and ATMega88P are pin compatible with the 328P, and only differ in program memory. The provided sketch only uses about 2K, so using the 168P or 88P should be fine.

Output MOSFETs

I used the BUK98150 N-channel MOSFET. It is rated 55V, 5.5A, and has a 2V nominal gate threshold voltage. The PCB footprint provides extra exposed copper for added thermal dissapation, and should be flooded with solder.

There are footprints for MIC4416 MOSFET drivers on the PCB. These aren't needed for the traffic light costume, and should be bypassed. To do this, you need to place 0 Ohm jumpers in the places for R2,R3 and R4, otherwise, it won't switch the MOSFETs. The 'VSEL' jumper, R5-R7, C2, and C11-C14 do not need to be populated.


There are footprints for two 1206 SMD leds - one on the main board, and one on the controls board. I chose not to populate the one on the main board, as it would be hidden when wearing the costume. The LED on the controls board is meant to be bright enough to show through the shirt. A 220 Ohm current limiting resistor should be sufficiently bright.


I chose the ATMega328P so that I could use an Arduino bootloader, specifically Optiboot. This essentially makes this board an Arduino UNO, at least as far as the Arduino IDE is concerned.

To program the bootloader, you'll need an AVR programmer. This can be a USBasp, ISP, or even another Arduino. The Optiboot bootloader comes with the Arduino IDE, and I used AVRdudess (the GUI version of AVRdude) to program it. First, select your programmer, and then hit the 'Detect' button. If your PCB is working, it should successfully detect the 328P micrcontroller.

When programming the bootloader, you'll need to set the appropriate fuses. There are links to a fuse calculator in AVRdudess, but if you are using my PCB design with a 16MHz oscillator, these fuse settings should work:

  • Low: 0xFF
  • High: 0xD9
  • Extended: 0xFD


After the bootloader is successfully programmed, you can use a USB Serial adapter to program the microcontroller using the Arduino IDE. Make sure to use the DTR signal, so that the microcontroller will automatically be reset during programming.


Step 3: Electronics: LEDs

There are several different options when it comes to LEDs for the traffic light costume. They don't need to be overly bright, like a real traffic light - you're most likely going to be using this costume at night, and within close proximity of other people, and you don't want to overwhelm them!

5mm Piranha/5mm Flat Top/5mm Straw Hat Such as these

Piranha, Flat Top, and Straw Hat LEDs all have one thing in common: they all have a fairly large throw angle. Piranha have the largest, followed by straw hat and then flat top. A large angle of throw is important to get a good, even light. If using these types of LEDs, you need to use series resistors to limit current. Use an online calculator to get a current of about 20-30mA. For the red and orange LEDs, two (with a forward voltage of about 2-2.2V each) can be used in series per one resistor. For the green (with a forward voltage of about 3.2V each) one can be used in series with a resistor. This is assuming a 5V supply.

12V LED Strip

With the use of a boost converter, 12V LED strips can be used. These typically have built in resistors.

WS2812 LED Strip

WS2812, or any other addressable LED strips can be used. The advantage to using addressable LED strips are that they do not require MOSFET drivers. NOTE: The firmware included in this Instructable is not programmed to drive these LED strips.

Step 4: Electronics: USB Battery

To power the traffic light costume, I chose to use a common USB battery for mobile phones. They provide the 5V for the microcontroller, high current for the LEDs, and an easy means of recharging. This eliminates need for dedicated battery charging circuitry, a boost converter for the 5V, and to handle potentially dangerous Li-ion cells.

The battery I chose had a capacity of 5500mAh. To figure out a rough estimate of battery life, take the capacity of the battery, in mAh, and divide by the average LED current. For example, on the lowest brightness setting, my LEDs consumed an average of 50mA. 5500mAh / 50ma = ~110h. Thats a long time! At the highest brightness setting - 350mAh, it would last about 15h. Either way, it would certainly last the night.

Other battery types can be used instead, each with their own advantages and disadvantages.

  • Four alkaline AA batteries (in series, for a combined voltage of ~6V), could power the traffic light costume for about 5.5 to 40 hours for a 2000mAh battery. However, as the batteries are used, the voltage drops, and the LEDs will get dimmer. The advantage is that alkaline batteries are readily available. Also, they are much safer than Li-ion cells.
  • Five NiCd AA batteries (in series, for a combined voltage of ~6V), could power the traffic light costume. Battery life, and voltage drop are the same as alkaline, but are generally rechargeable. They are also much safer than Li-ion cells.
  • Li-ion cells (not in a USB battery), such as 18650 cells, can be used as well. Make sure they have undervoltage protection, and only use a Li-ion charger, so as not to damage the cells.

The Problem

The only problem I encountered using a USB battery is they are 'smart'. When the battery detects that there isn't very much current being drawn, it assumes that the mobile devices battery is charged, and shuts down in order to save its battery.

When the traffic light costume is at its lowest brightness, it consumes too little current for the battery to stay on. To remedy this, I built a small circuit that periodically draws about 130mA of current from the battery. For about 2ms, once every 500ms, the microcontroller turns on a 2n2222 NPN transistor. A 39Ohm resistor in series with the transistor limits the current to 130mA, and a 82Ohm resistor in series with the base limits the microcontroller current.

Not every battery will require these settings. Some will require a longer on time - even has long as 50ms. Also, some batteries my not be able to source a lot of current very quickly, and this can show as flickering of the LEDs.

Step 5: Firmware: Overview

The nice thing about using an ATMega328P microcontroller is the ability to use the Arduino Optiboot bootloader and the Arduino IDE. This allows you to use all the libraries available to Arduino.

Firmware, Simplified

The firmware is very simple. There are only a few things that need to be done periodically, and the rest of the time, the Arduino basically waits. Like all Arduino sketches, the firmware is based on a main loop. Every time this loop runs, the firmware does four things:

  • checks if a button is pressed
  • checks the voltage from the potentiometer
  • calls the appropriate mode function
  • pulses the battery to keep it 'alive'

That's it. The loop runs very fast - thousands of times a second.


There are multiple functions in the firmware that rely on time. The changing of colors, the pulsing of the battery, and the brightness of the LEDs all require accurate timing, and usually all at the same time. The brightness of the LEDs is handled by the microcontroller in hardware - called Pulse Width Modulation, or PWM. The rest relies on using the millis() function to keep track of the current time, when events happen, and when the next event needs to happen.


When firmware doesn't work properly, you need to debug it. The classic way of debugging Arduino code is to output it to the Serial port, so you can view it on a computer. I have built in basic debugging code into the firmware to help with problems.


One of the most important parts of programming is comments. These allow others (and also you, maybe after a few years!) to understand the code. Code is typically not easily read, especially the C++ language that Arduino is based on. Throughout the code are comments that should help you understand what is going on. This doesn't mean that a beginner can dive right in! You do need some experience with Arduino, C, and/or C++ to fully understand it.

Step 6: Firmware: in Depth Look

Now I'll go through the code in detail.

* Software Options * - DEBUG (serial output) * - Battery stay alive function *

/#define DEBUG // debug mode; outputs debug messages to Serial port

#define BATSA // Battery stay alive; enables hardware to keep 'smart' // USB batterys from turning off due to low current. // NOTE: Settings may have to be adjusted for specific // batteries.

/*------------------------------------------------------------- * Macros */

#ifdef DEBUG #define DEBUG_PRINT(msg) Serial.println(msg) #else #define DEBUG_PRINT(msg) #endif

This code defines options. The #define DEBUG activates debug code, and will output to the Serial port. This is useful if you are having problems. The #define BATSA activates the USB battery stay alive functionality. To disable any of these, put double slashes "//" in front.

* Pins *

/ LED pins const uint8_t redLight = 5; const uint8_t yellowLight = 6; const uint8_t greenLight = 9;

// switch pins const uint8_t redBttn = A4; const uint8_t yellowBttn = A3; const uint8_t greenBttn = A2; const uint8_t modeBttn = A5; const uint8_t brightKnob = A0;

// USB battery stay alive pins const uint8_t saDriver = 11;

This code defines the pins for each function. The LED pins are the pins that drive the output MOSFETs. The switch pins are the pins of each button, and the potentiomiter.

* Variables *

/ brightness of LEDs uint8_t brightness = 255;

// relative brightness of LEDs; used to make each color the same brightness // highly depends on LEDs used! float redComp = 0.85; float yellowComp = 1.0; float greenComp = 0.9;

// poteniometer value int potVal;

// mode; 0 - traffic light; 1 - manual; 2 - disco uint8_t mode = 0; // default mode is 0

// state - used for modes 0, 2 uint8_t state = 0;

// button checking variables uint8_t modeBttnState = 0;

// timing variables unsigned long timeStart = 0; unsigned long timeCurr = 0; unsigned long timeElapsed = 0;

// normal mode timing (in ms) #define TGREEN 8000 #define TYELLOW 4000 #define TRED 8000

// manual mode switch variables uint8_t redBttnState = 0; uint8_t yellowBttnState = 0; uint8_t greenBttnState = 0;

// disco mode timing variables short beats[4] = {600, 420, 210, 125}; unsigned short discoDelayIndex = 2; // change to new colour every 500ms unsigned long discoPrev = 0; // time of last disco color change

// disco mode variables short discoLightMode = 0; // 0 - constant; 1 - pulse uint8_t currCol = 0; // current colour; 0 - green; 1 - yellow; 2 - red uint8_t newCol = 1; #define pulseDelay 75 // duration of pulse in pulse mode disco mode

// USB battery stay alive variables
uint8_t saState = 0; // on or off unsigned long saCurr = 0; unsigned long saNext = 0; // USB battery stay alive OPTIONS #define saTH 2 // time (ms) to draw current #define saTL 497 // time (ms) to wait (no current draw)

This code defines variables needed for the various modes, along with their default values. There are a few #define statements that define things open to change, based on your preferences and hardware.

#define TGREEN 8000
#define TYELLOW 4000 #define TRED 8000

These define the duration, in milliseconds, of the different colors. For example, TGREEN 8000 says that the green light will stay on for 8000 milliseconds, or 8 seconds.

* Arduino standard setup function */ void setup() { #ifdef DEBUG Serial.begin(115200); #endif

#ifdef BATSA DEBUG_PRINT("Battery stay alive active"); // initialize digital pins for optional USB battery stay alive pinMode(saDriver, OUTPUT); digitalWrite(saDriver, LOW); // initialize values for USB stay alive saCurr = millis(); saNext = saCurr + saTL; #endif // initialize digital pins for LEDs pinMode(redLight, OUTPUT); pinMode(yellowLight, OUTPUT); pinMode(greenLight, OUTPUT);

// initialize digital pins for switches pinMode(redBttn, INPUT); pinMode(yellowBttn, INPUT); pinMode(greenBttn, INPUT); pinMode(modeBttn, INPUT);

// 'randomize' seed for random numbers randomSeed(analogRead(A0)); }

The setup function initializes pins, the Serial port (if enabled by DEBUG) and the USB battery stay alive (if enabled by BATSA). The pins are initialized as INPUT (switch inputs) or OUTPUT (LEDs).

void loop() {
#ifdef BATSA stayAlive(); #endif getBrightness();


// select mode if (mode == 1) { mode1(); // disco mode } else if (mode == 2) { mode2(); // manual mode } else { mode0(); // normal mode; default } }

The loop is pretty simple. It does four things: activates the stay alive function, gets the value from the potentiometer, and converts it to a brightness value, checks if any buttons are pressed, and activates LEDs based on which mode is active.

* getBrightness function * calculates brightness from pot */ void getBrightness() { potVal = analogRead(brightKnob); // get analog reading brightness = (uint8_t)( ( (potVal/32)*(potVal/32) )/4 ); // convert pot value to brightness // using an exponential scale if (brightness < 4) { brightness = 0; } DEBUG_PRINT("Brightness:"); DEBUG_PRINT(brightness); }

The getBrightness function reads the value of the potentiometer. The potentiometer uses a potential divider to put a variable voltage on an analog pin of the ATMega328P. This value is then converted to a brightness value. Since potVal is a float (meaning it had a fractional value), it needs to be converted to a non-fractional value. This is done using the (unit8_t).

* checkModeBttn function * checks for input from mode button * sets defaults for each mode */ void checkModeBttn() { // read mode button modeBttnState = digitalRead(modeBttn); if (modeBttnState) { // if mode button was pressed ++mode; // increment mode variable


// turn off all LEDs at mode change analogWrite(redLight, 0); analogWrite(greenLight, 0); analogWrite(yellowLight, 0);

// if mode variable is greater then 2, reset to 0 if (mode > 2) { mode = 0; }

// if mode is 2, initialize disco mode delay and reset to color 0 if (mode == 2) { discoDelayIndex = 2; currCol = 0; }

// if mode is 0 or 1, reset timing variables & reset to color 0 if (mode < 2) { timeCurr = 0; timeStart = 0; currCol = 0;

// if mode is 1, set color to red if (mode == 1) { analogWrite(redLight, brightness*redComp); } }

// delay for a rough software debounce delay(200); } }

The checkModeBttn function checks to see if the mode button has been pressed. If so, then it will increase the mode variable by one. When it gets to greater than 2, it will reset to 0. Remember, when coding, variables start at 0, not 1! The delay(200) is a rough debounce feature.

this code also sets default values for each mode.

* mode0 function * normal traffic light mode */ void mode0() { timeCurr = millis(); // get curent millisecond timeElapsed = timeCurr - timeStart; // get elapsed time from previous color change if (state == 1) { // yellow analogWrite(yellowLight, brightness*yellowComp); // turn on yellow LED analogWrite(greenLight, 0); // turn off green LED


if (timeElapsed > TYELLOW) { // go to next color after so many milliseconds state = 2; // go to red next timeStart = millis(); // 'save' time of color change } } else if (state == 2) { // red analogWrite(redLight, brightness*redComp); // turn on red LED analogWrite(yellowLight, 0); // turn off yellow LED


if (timeElapsed > TRED) { // go to next color after so many milliseconds state = 0; // go to green next timeStart = millis(); // 'save' time of color change } } else { // green (state 0) analogWrite(greenLight, brightness*greenComp); // turn on green LED analogWrite(redLight, 0); // turn off red LED

DEBUG_PRINT("Green ON"); if (timeElapsed > TGREEN) { // go to next color after so many milliseconds state = 1; // go to yellow next timeStart = millis(); // 'save' time of color change } } }

mode0 is the normal traffic light function. It cycles through the colors based on the time elapsed. Each time the function is called, it updates the current millisecond value. The elapsed time triggers the next color change.

* mode2 function * manual traffic light mode */ void mode1() {

redBttnState = digitalRead(redBttn); // read red button if (redBttnState) { // turn on red analogWrite(greenLight, 0); analogWrite(yellowLight, 0); analogWrite(redLight, brightness*redComp);

DEBUG_PRINT("Red ON"); } else { yellowBttnState = digitalRead(yellowBttn); // read yellow button if (yellowBttnState) { // turn on yellow analogWrite(greenLight, 0); analogWrite(redLight, 0); analogWrite(yellowLight, brightness*yellowComp);

DEBUG_PRINT("Yellow ON"); } else { greenBttnState = digitalRead(greenBttn); // read green button if (greenBttnState) { // turn on green analogWrite(yellowLight, 0); analogWrite(redLight, 0); analogWrite(greenLight, brightness*greenComp);

DEBUG_PRINT("Green ON"); } } } }

This code is for the 'manual' mode. All it does it wait for a button is pressed. When a button is pressed, it activates a color LED, and deactivates the others.

* mode3 function * disco traffic light mode */ void mode2() { timeCurr = millis(); // get curent millisecond timeElapsed = timeCurr - discoPrev; // get elapsed time from previous color change if (timeElapsed >= beats[discoDelayIndex]) {

// get random color, but NOT the same as current do { newCol = (int)(random(1199)/400); // get random number between 0 and 2 (inclusive) } while (newCol == currCol); currCol = newCol;

// change to new color, turn off all other colors if (currCol == 0) { analogWrite(yellowLight, 0); analogWrite(redLight, 0); analogWrite(greenLight, brightness*greenComp); } else if (currCol == 1) { analogWrite(greenLight, 0); analogWrite(redLight, 0); analogWrite(yellowLight, brightness*yellowComp); } else { analogWrite(greenLight, 0); analogWrite(yellowLight, 0); analogWrite(redLight, brightness*redComp); }

// if in 'pulse' mode, pulse LED, then turn off if (discoLightMode == 1) { delay(pulseDelay); analogWrite(greenLight, 0); analogWrite(yellowLight, 0); analogWrite(redLight, 0); }

// get current millisecond discoPrev = millis(); }

// read all button states redBttnState = digitalRead(redBttn); yellowBttnState = digitalRead(yellowBttn); greenBttnState = digitalRead(greenBttn);

// if red button pressed if (redBttnState) {

// switch between pulse and constant disco mode if (discoLightMode == 0) { discoLightMode = 1; DEBUG_PRINT("Pulse Mode ON"); } else { discoLightMode = 0; DEBUG_PRINT("Pulse Mode OFF"); } delay(150); // debounce }

// if yellow button pressed if (yellowBttnState) {

// descrease disco speed --discoDelayIndex; if (discoDelayIndex < 1) { // limit to min 1 discoDelayIndex = 1; DEBUG_PRINT("More speed!"); } delay(250); // debounce }

// if green button pressed if (greenBttnState) {

// increase disco speed ++discoDelayIndex; if (discoDelayIndex > 3) { // limit to max 3 discoDelayIndex = 3; DEBUG_PRINT("Less speed"); } delay(250); // debounce } }

This code is part of the 'disco' mode. There are a few options for the disco mode. One is the speed of the color change. When the red or yellow button is pressed, the speed of the color change is increased of decreased, respectively. When the green button is pressed, the pulse mode is changed. The default mode is 'constant mode'. In this mode, the LED will stay on until the next color change. In 'pulse mode' the light will stay on for a number of milliseconds, defined by pulseDelay.

Step 7: 3D Printing: Overview

When I made the first version of the traffic light costume, about 3 years ago, it was mainly made out of cardboard and duct tape! Since then, 3D printing as become common, and much more affordable. My second version is about 50% 3D printed. It is much more durable, and can now even stand up to some light rain.

My 3D printed parts were printed on my personal printer - an Anet A8. However, there are many online services, and makerspaces available that have 3D printers. My local library even has one.

3D Printed Pieces

There are fours 3D printed pieces for each color on the traffic light:

  • Base: The base does two things: it provides something to attach the LEDs to, and it provides something to attach the shirt to. It is meant to 'poke out' from the shirt, so that there is fabric stretched around it. The base has holes for sewing, so that the base is secure. I chose to print the base in white, so that the light from the LEDs will bounce around, adding to the diffusion - sort of like a softbox.
  • Tube: The tube is not only cosmetic, but when fitted over the base and glued, 'traps' the fabric, creating a very secure and durable fit.
  • Lens: The lens is covers the LEDs, and adds color when the LEDs are both on and off. The Lenses are printed with a transparent filament, which adds a diffusion effect, and takes away some of the hard glare of the LEDs. In conjunction with the tube and base, when glues, provides a water resistant seal.
  • Cap: The cap is purely cosmetic. It adds to the look of the traffic light.

Each piece can be printed without supports. The lens is the only piece that has a specific requirement for layer height - a layer height of about 0.1 is recommended, so that the patterned side is as smooth as possible. You may also want to sand it a bit.

Step 8: Fabric

For the tshirt, I ordered two 'gold' tshirts on eBay. They are GILDAN brand, and fairly heavy. You need to buy two, as you need extra fabric for the caps and pockets for the PCBs.

I bought one tshirt one size too big. Where I live, it can get very cold on Halloween, so the extra room allows me to where a sweater or jacket underneath. It's not too big, and I can still where it without anything else. The second tshirt, because I on'y needed a little extra fabric, and it was cheaper, was a small.

A good thing to remember is that fabric stretches, so cutting the pieces a bit small is not a problem - just make sure when you glue it to stretch it and hold it.

Step 9: Fitting It Together

When all the pieces are printed, they should fit together a bit loosely. This is intended, especially with the base and tube, so that there is room for fabric and glue.

Here are the steps I took to put it all together:

Step 1

Mark three holes on the shirt, leaving about 10mm between the largest part of the base. Cut three hole, in the centre, about a 1/2 to 2/3 the diameter of the base. After that, the base can be inserted from inside the shirt, so they stick out. You can now sew the bases tot he shirt.

Step 2

At this point, I attached the LEDs and made sure they had long enough wires. I bit of extra is recommended. I drilled some holes into the perfboard my LEDs were mounted on, and used zip ties to attach the perfboard to the cross pieces on the bases. Remember, after the lens is glued on, it will be very difficult to get at the LEDs to fix anything. Now is a good time to test the LEDs!

Step 3

After sewing together the fabric for the cap, it can be glued on. Leave it for a few hours to dry.

Step 4

Glue the cap to the tube. Use lots of glue, and one at a time. The cap and tube need to be pressed together while the glue dries. For this, I used some speaker wire and wrapped it around tight.

Step 5

This is a big step. I advise you do it one color at a time, and let it all dry before doing the next. This step involves gluing the lens and tube together. Glue the lens first. If the lens isn't glued straight, the tube might not fit on, so be careful. After the lens, apply lots of glue to the base, and put on the tube. Get something with some weight to put on top of the lens. Allow lots of time for the glue to dry.

Step 6

After the 3D printed parts are all glued together, I took a stiff piece of cardboard, and attached it to the bases of all three lights. This helped keep the lights spaced out, as they are a bit heavy.

Step 7

Now it's time to sew a pocket for the main PCB, and for the controls. Do the controls first, so that you can space it and the main board the length of the wires connecting them. Any bend in the wires will mean a bend in the shirt.

Step 8

Sew the main board and controls as tight as possible. The PCB file included with this Instructable has many holes for sewing. Finally, once secure, you can draw on some indication for the controls. I just used different colored sharpies.

Final Steps

Now, the costume should be done! There are a few more things that are optional. I used some spiral wire wrap to help protect the wires. I also ziptied the USB cable for the battery to the control wires, so it didn't accidentally come out.

Step 10: Conslusion

I hope you enjoyed my Instructable on my traffic light costume.

If I were to make it over again, there are a few things I would change:

USB battery stay alive circuit

More complete testing would have caught this problem sooner than the day before Halloween. I only tested the electronics with a bench power supply. If I were to redesign the PCB, I would incorporate the USB keep alive circuit fix into the PCB. As it is, it is only needed for specific USB batteries, and I did not find it necessary to redesign the PCB yet.

Less expensive microcontroller

The ATMega328P is a great microcontroller, however, it is a bit expensive compared to others, such as the PIC. It also has more pins than is needed for this application.


  • With an unused SPI port, and serial port, there are a multitude of things that could easily be added. One interesting idea would be to add a Bluetooth module, so that the functions of the traffic light coul dbe controlled by phone.
  • Another idea, using the SPI or serial port, would be to add wireless communication. With two or more shirts, the light could be synced together. The nRF24L01+, an ultra low power, ISM-band wireless transceiver, would be perfect for this application, and have almost no impact on battery life.
  • More LEDs!! Pedestrian crosswalk signal could be added to gloves, or on the shoulders of the costume.

Feel free to leave a comment or question below. I'm always happy to answer questions!

Halloween Contest 2017

Participated in the
Halloween Contest 2017