52

Is it possible to have more than 14 output pins on the Arduino, I am working on a project in which I need to light up several LEDs individually. I only have an Arduino Uno, and I don't want to get a Mega.

  • In the spirit of expandability, is I2C even an option with Arduino? – Chris O Feb 12 '14 at 18:10
  • @ChrisO What do you mean by I2C? Because from what I understand Shift registers would be considered I2C. – JVarhol Feb 12 '14 at 18:12
  • Oh make me answer my question then ;-) Here's a wikipedia article on this bus standard. And I just found some support from Wire lib for I2C, so you could connect an I2C chain to your Uno. – Chris O Feb 12 '14 at 18:22
  • @JVarhol Not quite. I2C, aka TWI is a a 2-wire interface while SPI may be considered a 3-wire interface. – Klaus-Dieter Warzecha Feb 12 '14 at 18:48
  • 1
    @JVarhol have you selected a solution for your project already? If yes, it would be useful to edit your question and accept the answer that matches (or is the nearest) your solution. – jfpoilpret May 3 '14 at 5:34

13 Answers 13

50

A common way to expand the set of available output pins on the Arduino is to use shift registers like the 74HC595 IC (link to datasheet).

You need 3 pins to control these chips:

  1. Clock
  2. Latch
  3. Data

In a program, you pass on the data one bit at a time to the shift register using the shiftOut() command, like so:

shiftOut(dataPin, clockPin, data); 

With that command, you set each of the 8 outputs on the 595 IC with the 8 bits in the data variable.

With one 595, you gain 5 pins (8 on the IC, but you spend 3 to talk to it). To get more outputs, you can daisy-chain a series of 595 together, by connecting its serial-out pin, to the data pin of the next one. You also must connect together the clock and latch pins of all of the 595 ICs.

The resulting circuit (using one 595) would look like this:

Circuit using 595 shift register

The figure above was taken from this codeproject.com webpage:

The latch pin is used to keep the 595 outputs steady while you are shifting out data into it, like so:

digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, data); 
digitalWrite(latchPin, HIGH);
  • 1
    Thx, forgot to include how a shift register works. I had planned to, but I had to walk away from my comp and when I came back it had slipped my mind. :) – JVarhol Feb 12 '14 at 17:05
  • No problem, that's why we are a community. We complement each other's answers. But it's nice that you've added a few good tips in your answer, too, specially about the EZ-Expander shield. It allows those who cannot easily make their PCBs to expand the number of ports available to them. – Ricardo Feb 12 '14 at 17:08
  • 2
    Also it makes the programming easy, you do not have to shift the data in and Pulse the clock and then activate the Latch. I have known about the shield for quite awhile and I think it is perfect for beginners. Best thing is, is that because the code is opensource you can use the code even if you are not using the actually shield but have your own board, or are using a breadboard. – JVarhol Feb 12 '14 at 17:12
  • 1
    Don't forget that you can link them in line (theoretically) indefinitely, so 2 8-bit registers can become one 16-bit. – Scott M. Feb 12 '14 at 20:04
  • Thanks! Here is an archive link to the amazing codeproject description in case the link ever goes down archive.fo/1uvPE – akhmed Dec 20 '16 at 23:31
27

There are two ways you can get more pins out of an arduino.

The first way is by using the Analog pins as digital output pins, which is really easy to do. All you need to do is refer to A0-A5 as pins 14,15,16,17,18,19. For example to write high to pin A0 just use digitalWrite(14, HIGH).

The other way to get more pins out of the Arduino is by using a Shift Register. To do this I recommend using the EZ-Expander Shield, which allows you to use digitalWrite([20-35], HIGH) when you import the EZ-Expander Library. This shield however only allows the pins to be used as outputs only and uses pins 8,12 and 13 to control the shift registers.

The great thing is, is that you can use both of the two methods above together, without any problems.

  • 3
    According to the Arduino reference, you can actually use the A0-A5 identifiers directly instead of using numbers 14-19. For example, digitalWrite(A0, HIGH). – Peter Bloomfield Feb 14 '14 at 13:44
  • 3
    @PeterR.Bloomfield Both are correct, however I recommend A0-A5 for simplicity and easy of reading. Not a big deal with a LED blinking but when you have large projects little habits add up. – Anonymous Penguin Feb 17 '14 at 20:27
  • 2
    Actually, whilst both will get the job done on UNO and compatible boards, digitalWrite(A0) is more correct than digitalWrite(14) since the former will always map to the correct physical (analog) pin. On a different board, pin 14 may in fact not be A0, eg pin 14 on the MEGA is Serial3 TX and will not influence the analog pin that you're after. ie, if using digitalWrite on an analog pin, use the A0-A5 reference. – Madivad Mar 20 '14 at 1:02
18

If you want to drive LEDs, then you can also use a MAX7219 that can drive 64 LEDs, without extra circuitry (no need for transistor to amplify signal).

Driving a MAX7219 requires only 3 output pins on Arduino. Also, you can find a few Arduino libraries for it.

You can also chain several of them if you need to power more than 64 LEDs.

I have used it successfully for multiple 7-segment LED displays.

Downside: it is expensive (about $10).

  • I did forget about the MAX7219, Thanks for posting! – JVarhol Feb 12 '14 at 19:44
  • 384 LEDs... whoah! Plus two pins for USB inference. Wonder what kind of annoying trinket you can create (with a sufficient power supply). – Anonymous Penguin Feb 17 '14 at 20:29
17

You can use Charlieplexing. With this technique you can directly drive n*(n-1) LED's from n pins. So with 3 pins you can drive 6 LED's, 4 pins - 12 LED's, 5 pins - 20 LED's and so on.

Exemple:

Six LED's on 3 Pins

PINS        LEDS
0 1 2   1 2 3 4 5 6
0 0 0   0 0 0 0 0 0
0 1 Z   1 0 0 0 0 0
1 0 Z   0 1 0 0 0 0
Z 0 1   0 0 1 0 0 0
Z 1 0   0 0 0 1 0 0
0 Z 1   0 0 0 0 1 0
1 Z 0   0 0 0 0 0 1
0 0 1   0 0 1 0 1 0
0 1 0   1 0 0 1 0 0
0 1 1   1 0 0 0 1 0
1 0 0   0 1 0 0 0 1
1 0 1   0 1 1 0 0 0
1 1 0   0 0 0 1 0 1
1 1 1   0 0 0 0 0 0

enter image description here

You can see a better tutorial here.

  • One thing to note is that this technique allows you to control more leds, but not simultaneously necessarily. – kontur Feb 5 '15 at 16:49
  • 1
    @kontur yes, you're right. But in terms of persistence of vision you can consider that they are. The question doesn't have such details. – Daniel Grillo Feb 5 '15 at 18:13
  • Charlieplexing is not the ONLY way to multiplex LEDs without using shift registers. – linhartr22 Sep 3 '15 at 19:15
  • 1
    @linhartr22 I've NEVER said that is the only way. Charlieplexing is just a common solution for this problem. And my answer was the 7th one. So other possibilities had already showed before. – Daniel Grillo Sep 3 '15 at 21:05
  • @DanielGrillo I should have been more specific and explained how column and row multiplexing, which is typically used on LED cubes, can be done without shift register and without the limitations of Charlieplexing. A 4x4 row column matrix can individually control 16 leds with 8 I/O lines. A 4x4x4 cube can individually control 64 leds with 12 I/O lines (possible even on an Uno by using Analog A0-A5 as digital lines). – linhartr22 Sep 4 '15 at 20:40
13

I2C (Wire)

You can use the I2C protocol (Wire library) to connect to other devices such as port-expanders. For example, the MCP23017.

I used one of those chips to connect to an LCD board. The MCP23017 has 16 ports, which can be configured as inputs or outputs. As inputs they can raise interrupts if desired.

Example of connecting 13 of those 16 to the LCD:

MCP23017 connected to LCD screen

Now we connect to the Arduino using only 2 wires (SDA/SCL) plus power and ground:

MCP23017 connected to Arduino


Some third-party manufacturers have made boards with 4 x MCP23017 on them, this gives you 64 inputs/outputs:

Centipede board


Multiplexers

You can use analog multiplexers like the 74HC4051 (8 ports) or the 74HC4067 (16 ports) to connect one pin to one of the 8/16 ports (but only one at a given time), like this:

74HC4051 multiplexer

These are bi-directional, so can be used as an input or output expander.


SPI with 74HC595

Using SPI you can send fast serial data to a shift register, such as the 74HC595. These can be daisy chained together. In this example I am controlling 32 LEDs with only 3 I/O pins (MOSI/MISO/SCK) plus power and ground.

74HC595 driving 32 LEDs


I found inside a commercial LED sign that the 72 LEDs were driven by 74HC595 chips.

Scrolling LED sign

This had 9 chips driving the columns (9 x 8 = 72 LEDs) and one chip driving the rows, in a multiplexed configuration.


SPI with MAX7219

If you just want to drive LEDs you can usually multiplex them. The MAX7219 simplifies that by being designed to drive LED matrixes, for example 7-segment displays:

MAX7219 with 7-segment display

Or 64-LED matrixes:

MAX7219 with 64-LED matrix

In both cases these can be daisy-chained together, for example:

MAX7219 daisy-chain

All those examples only use 3 pins of the Arduino (MOSI/MISO/SCK) plus power and ground.


SPI with MCP23S17

The 16-port port expander mentioned earlier (MCP23017) also comes in an SPI variant (MCP23S17), which does virtually identical things. It uses one more wire, but would be faster.


Other protocols

LED strips (like the NeoPixel ones) have their own protocols. There was a post on Youtube by Josh Levine where the author drove over 1000 pixels with a Duemilanove!


References

12

Shift registers have been mentioned in other answers, and they are definitely an excellent choice for many projects. They are cheap, simple, moderately fast, and can typically be chained together to add more outputs. However, they have the downside that they usually need exclusive use of several pins (between 2 and 4, depending on how you set them up).

An alternative is to use more advanced port expanders, such as the 16-bit MCP23017 and MCP23S17. These support I2C and SPI respectively, which means you can place them on a bus with several other devices (potentially of different types). Each device on the bus can be individually addressed, meaning you only need 2 or 3 pins to talk to all of them. The update speeds are typically extremely fast, so you're unlikely to experience significant latency (i.e. transmission delays) in an Arduino project.

At a low level, using I2C or SPI is substantially more complicated than a simple shift register. However, there is library code for Arduino to take care of that for you. See this question, for example: How do I use I2C devices with Arduino?

  • Wow, i never knew about those, thx, ill look into them! – JVarhol Feb 14 '14 at 11:38
  • Can you use I2C to control LEDs? – Pro Q Feb 3 '18 at 20:55
  • 1
    @ProQ You can't control standard LEDs directly using I2C. However, it is possible to get some LED products which have a built-in I2C driver. This is usually for products with lots of LEDs, such as strips or matrices. – Peter Bloomfield Feb 4 '18 at 17:27
8

In addition to Ricardo's answer, what Wikipedia states on shift registers:

One of the most common uses of a shift register is to convert between serial and parallel interfaces. [...] SIPO registers are commonly attached to the output of microprocessors when more General Purpose Input/Output pins are required than are available. This allows several binary devices to be controlled using only two or three pins, but slower than parallel I/O.

In the article Ricardo linked you can see the diagram of the shift register.

Shift register diagram

What happens here is that you put the data of the 8 pins in a sequence and for each clock tick the shift register will shift (move the binary data from each latch to the next one) until it "makes a circle" i.e. the first bit arrives to the last pin. Shift registers also have an input where you can turn on/off the shifting so the status can be kept after the data is shifted to the position. For a simple demonstration see the following animation.

Shift register animation

Here the red light is the serial input and the green ones are showing the state of the latches in this simplified SIPO shift register. After the data shifted to place shifting can be turned off and you can read the pins. In this example I shifted out 10101011.

From these examples you can realize that serial transfer will be slower than parallel, because you have to wait for the shift register to shift the bits to their place. You will have to wait the same amount of clock tick as many bits you want to load. This is one of the many reasons why you can't chain them indefinitely, because loading would take forever.

  • 1
    +1 for the additional information AND for the pretty animated GIF and its didatic effect! Where did you find the GIF? Or did you make it? – Ricardo Feb 14 '14 at 22:54
  • 1
    @Ricardo I created the circuit with Digital Works, then print screen, edit, gif animator. – totymedli Feb 15 '14 at 3:51
6

As you wrote already already, you can use all pins, including TX and RX as digital output. I did that a while ago for a demonstrator and recorded a video - 20 LEDS on 20 pins - of this rather nonsensical project.

As described by Peter R. Bloomfield here, you a have to disconnect TX and RX for upload. Moreover, you're out of pins to read sensors for possible interactivity and have to make sure that the total current limit isn't reached. Not to forget that you're limited to 5V leds if you drive them directly with your Arduino.

The use of shift registers in general and the 595, described by Ricardo is therefore highly recommended.

  • They are cheap!
  • It's rather easy to cascade them.
  • A lot of speed can be gained when you use hardware-SPI.

I used them a while ago when I realized the soldering and programming part of Kawaii me (text of the link is in German) of the upcycling artist Dominik Jais.

Here, just a bunch of 595 were used to drive a display of 8x11 leds. Since the leds were cut from a stripe of 12V SMD leds, an additional power supply and some UDN2803A Darlington arrays, hooked to the output pins of the shift registers, were necessary.

Other general methods would include the use of PCF8574(A) 8bit port expanders, which are controlled via the I2C bus.

Anyway, I'd give the 595 shift registers a try first.

If you need to control a couple of RGB leds, however, you might want to look for more specialized solutions. Some RGB leds come with their own WS2812. These fine pieces can be cascaded (1-Wire bus) and are addressed via their position in the chain.

3

If it's all about LEDs, what about WS2812B LED strips, or just the driver chips themselves? You can control a virtually unlimited number of LEDs using just one pin!

Although people are use to these in strips, they are available as standalone LEDs (known as neo pixels on Adafruit). Or if you're only driving a single colour, each WS2811 chip could control 3 LEDs by using each of the RGB outputs for a single LED each.

I recently just created created a project that uses 5 such LEDs: Door1 open/closed, Door2 open/closed, motor1 active, motor2 active and power. The "active" LEDs are dual purpose since I have red being the input from active motor and green being active flag inside the Arduino.

Point being, with 1 pin and the library installed, you can control any number of LEDs

2

I do not claim this method for my own, but I found this neat trick on the webpage MUX-DEMUX: CD4051 Parlor Tricks

Whatever method you choose to use to drive outputs or read inputs (shift registers, multiplexors or the straightforward direct use of the Arduino pins themselves) you can DOUBLE the number of outputs or inputs by a clever use of parallel circuit pairs (to form a dual input or output bank), employing diodes in opposing senses on each parallel branch, and switching the inputs/outputs to high and low.

To illustrate the method for outputs (LEDs in this case, note that the extra diodes are not required):

LED bank

If you consider the pair of LEDs in this example to be a "bank", and you want to light LED_0, you need to set PIN 17 to HIGH, and PIN 18 to LOW. (The pin numbers are confusing, but they match the later example so bare with me). To light LED_1, you just reverse the PINS. The diode nature of LEDs keeps the current from flowing the opposite direction keeping the other one off.

To illustrate the method for inputs (CdSs in this case, note that the extra diodes are required):

CdS bank

This gets a little more complicated if you want to do an analog read on a CdS light sensor. First, you need to add a diode to each sensor to control the flow. Second, since you are reading values, you need to pull the inputs high or low to keep them from floating. Being a lazy person, I'm going to pull them high using the internal pull-up resistors. To read CdS_0, you set PIN 17 mode to OUTPUT and set it to LOW. This makes it the ground. Then you set PIN 18 mode to INPUT and set it to HIGH to engage the pull-up resistor. Now you are set to do a read on PIN 18 (a.k.a. analog pin 4). To access the other sensor, just switch the modes and outputs.

So, if you have a CD4051 8 port multiplexor, using 5 pins on the Arduino (instead of the usual 3), you can obtain 16 inputs or outputs, or a mix of the two.

16 outputs/inputs using a CD4051

Likewise, if you have a 4067 16 port multiplexor you can obtain 32 inputs or outputs, or a mix of the two.

An example sketch would be:

/*
 * Example of getting 16 i/o from 5 pins using a CD4051
 *
 * Based on tutorial and code by david c. and tomek n.* for k3 / malmö högskola
 * http://www.arduino.cc/playground/Learning/4051?action=sourceblock&ref=1
 */ 


int selPin[] = { 14, 15, 16 }; // select pins on 4051 (analog A0, A1, A2)
int commonPin[] = { 17, 18};   // common in/out pins (analog A3, A4)
int led[] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW };  // stores eight LED states
int CdSVal[] = { 0, 0, 0, 0 }; // store last CdS readings
int cnt = 0;  // main loop counter
int persistDelay = 100; // LED ontime in microseconds


void setup(){
  Serial.begin(9600);  // serial comms for troubleshooting (always)
  for(int pin = 0; pin < 3; pin++){ // setup select pins
    pinMode(selPin[pin], OUTPUT);
  } 
}


void loop(){
  flashLEDs();
  if (cnt == 0){
    for(int x; x < 8; x++){
      led[x] = random(2);
    }
  }
  cnt++;
  if (cnt > 100) { cnt = 0; }
}


void flashLEDs() {
  for(int pin = 0; pin < 2; pin++) {  // set common pins low
    pinMode(commonPin[pin], OUTPUT);
    digitalWrite(commonPin[pin], LOW);
  } 
  for (int bank = 0; bank < 4; bank++) {
    for(int pin = 0; pin < 3; pin++) { // parse out select pin bits
      int signal = (bank >> pin) & 1;  // shift  & bitwise compare
      digitalWrite(selPin[pin], signal);
    }
    if (led[bank * 2]){        // first LED
      digitalWrite(commonPin[0], HIGH);  // turn common on
      delayMicroseconds(persistDelay);   // leave led lit
      digitalWrite(commonPin[0], LOW);   // turn common off
    } 
    if (led[bank * 2 + 1]){     // repeat for second LED
      digitalWrite(commonPin[1], HIGH);
      delayMicroseconds(persistDelay);
      digitalWrite(commonPin[1], LOW); 
    }
  } 
}

Like I said in the first line, the full explanation can be found on MUX-DEMUX: CD4051 Parlor Tricks

1

For a class project I used a CD4024 and two Arduino pins to drive a 7-segment display.

There are some caveats to this approach. For example, to write a high value to the first output of a ripple counter only requires a reset and toggling the clock pin twice. But if you want to write high to all n pins requires toggling the clock pin 2n times, and during that time all the other pins are constantly toggling on and off.

If your application can deal with these limitations and you're short on pins, it's another option.

Bonus answer: there are a lot of examples of multiplexing inputs here, many of which also apply to multiplexing outputs.

  • Using a 7-stage counter to drive a 7-segments display seems a suboptimal approach, for the reasons you've yourself indicated. – jfpoilpret May 3 '14 at 5:28
1

With a bit of work (installing a different bootloader) an additional seven I/O lines are available on an Uno, on the ICSP1 and JP2 headers. The replacement bootloader is called HoodLoader2. It allows you to install sketches on both the Atmega328 and Atmega16U2 on an Uno. Dealing with multiple processors would be the main complication from using this method.

On an Uno, the ICSP1 and JP2 headers connect to pins PB1...PB7 of the Atmega16U2. In addition the Atmega16U2 has about 9 I/O pins with no connection to the circuit board. A person working under a microscope might be able to attach wires to a total of 18 I/O pins on the 16U2, while leaving three other I/O pins attached to their ordinary connections.

HoodLoader2 also works on Mega boards.

0

There is a wealth of good answers here, but if you costed your time you'd find it cheaper to buy a Mega.

My 3/2-d worth.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.