Using Shift Registers to Increase Digital Outputs

The Arduino Uno is limited to fourteen digital input/output pins and six analog input pins. The analog input pins can also be used for digital input/output thus providing a total of 20 digital input/output pins. In the four digit seven-segment display developed in blog post Driving Seven Segment Displays, twelve digital input/output pins were used, seriously limiting the amount of input/output real estate left. This would be especially true if we were to increase the number of displayed digits and if we needed to do any useful interaction with the environment through sensors. What if we needed tens or even hundreds of digital outputs from a single Arduino Uno? It can be done using digital electronic integrated circuits called shift registers along with a technique called serial communication.

I have used serial communication throughout my blog posts. We have used it to send and receive Morse code in the Morse Code Reader and Morse Code Generator posts. We have also used it to communicate with the temperature and humidity sensor DHT22 in the Sensing Temperature and Humidity post. In this post, we will use the SN74HC595 8-bit shift registers with 3-State output registers integrated circuits to convert serial signals into a collection digital output values. Using only three of the Arduino Uno’s digital I/O ports we will send digital values to the shift register integrated circuits and we will use the shift register outputs to drive a ten-LED bar graph.

Registers

In digital electronics, a register is a collection of devices, also known as flip-flops, each capable of holding a single bit of information. Effectively, a flip-flop is a single bit of memory. The following diagram depicts a D flip-flop, a device capable of remembering the value of a digital signal, D, when another signal, CLK, goes from a low to a high value, from 0 to 1. The device has two outputs, Q and Q, that provide the device’s remembered value and its inverse respectively. The device has two other inputs, PRE and CLR. The PRE input sets the flip-flop value to a high value, 1, when a low pulse is applied to the input. The CLR input resets the flip-flop value to a low value, 0, when a low pulse is applied to the input.

The previous diagram follows the IEEE Standard 91-1984 which defines how to represent digital electronic components. The device is represented by a box. All input lines are drawn at the left of the box while all outputs are drawn at the right. Lines connected directly to the device represent signals that are active when the value of the signal is high, 1. Lines connected to a small circle attached to the device represent signals that are active when the signal is low, 0. Lines connected to the device through a small triangle represent signals that are active when the value switches from low to high, 0 to 1.

A register, as depicted in the following drawing, is made of several flip-flops, one for each bit of information to be stored. Data is presented at the D input of each flip-flop and the state of each flip-flop is set when the clock signal, attached to the clock input of each flip-flop, is toggled from a low to a high value, 0 to 1. The data stored in the register in the form of bits of information is made available at the output of the flip-flops making up the register. In the diagram, four flip-flops store four bits of information depicted from right to left, least significant bit at the rightmost position and most significant bit at the leftmost position.

Serial to Parallel Shift Registers

a serial to parallel shift register is a register that converts a serial train of bits into a parallel representation. The diagram below depicts the connectivity required to transform a register into a serial to parallel shift register. There are two inputs to the circuit: the data and clock signals. The data signal is connected to the most significant flip-flop’s data input; each flip-flop output is connected to the next less significant flip-flop data input; and the clock signal is connected to every flip-flop clock inputs. At each clock toggle from low to high, flip-flops take the value or the previous more significant flip-flop and the most significant flip-flop takes the value of the data signal. All flip-flop outputs form the parallel representation of the successive serial values of the data signal. By presenting values, a bit at a time, least significant bit first, on the data signal, clocking in each new value, we can convert the serial data signal to a parallel representation of the signal.

The values stored in each flip-flop of the serial to parallel shift register becomes the representation of the serial signal in the end. However, while the bits are being shifted, the flip-flop outputs will contain intermediate values until we finish clocking in all the values. A way to solve this problem is to put a register to store the output of the serial to parallel shift register after all bits have been shifted.

Latched Shift Registers

The following diagram depicts a digital circuit comprised of two registers, a serial to parallel shift register at the bottom and a latch register at the top. As described in the above paragraphs, the serial to parallel shift register converts the serial data signal into its parallel representation by clocking in each bit in turn; when the shifting is done, we can store the values at the output of each serial to parallel shift register flip-flop in the latch register by toggling the latch signal from a low to high value.

The above diagram represents a four-bit latched shift register. The circuit can be extended by repeating each serial to parallel and latch register stages to the right of the circuit by connecting the clock signal to the serial to parallel flip-flop’s clock; connecting the output of the previous less significant flip-flop, called “Next Data” in the diagram, to the data input of the new serial to parallel flip-flop; by connecting the output of the new serial to parallel flip-flop to the data input of the new latch register flip-flop data input; and finally by connecting the latch signal to the clock input of the new latch register flip-flop.

The number of bits that can be supported is almost infinite, only limited by the electrical characteristics of the digital integrated circuits used to implement the circuit and the amount of time required to shift each bit in position. The integrated circuit that will be used to demonstrate how to increase the output capacity of the Arduino Uno is the SN74HC595N 8-bit shift registers with 3-State output registers digital integrated circuit. Internally, the integrated circuit is similar to the digital circuit shown above but has eight bits instead of four. Moreover, the outputs can be put in a high-impedance (high resistance) state, making it behave as if it was not connected. This latter functionality will not be used in this demonstration.

The SN74HC595 Shift Register with Latch

The diagram below is a depiction of the SN74HC595 digital integrated circuit. It describes the integrated circuit functionality on the left, and its pinout on the right. The SN74HC595 integrated circuit is housed in a 16 pin dual in-line package (DIP). The integrated circuit’s pins 8 and 16, not shown on the functional diagram, are used to power the integrated circuit. Pin 8, GND, is to be connected to ground while pin 16, VCC, is to be connected to power, 5 volts. In all of the following text, a LOW value or 0 is represented by a signal at the same potential as the ground, 0 volt, and a HIGH value or 1 is represented by a signal at the same potential as VCC, 5 volts.

The functionality diagram follows the IEEE Standard 91-1984. The two top blocks notched at the bottom represent the integrated circuit’s common control elements. The bottom blocks represent each of the eight serial register flip-flops and the output register flip-flops feeding each other in two stages, from left two right. The topmost serial register is fed external data from the serial data line through the pin labelled SER. The bottommost serial register flip-flop output is directly attached to pin QH‘ which can be used to feed another serial register’s input, allowing the extension of the shift register to many more stages. Each output register flip-flop is attached to pins QA through QH giving access to each of the flip-flop’s value.

There are two common control elements, one for the collection of serial register flip-flops and the other for the collection of output register flip-flops. The first, smaller, common control element has two inputs. The SRCLR input pin is used to reset to a LOW value, 0, all shift register flip-flops when its value is set to LOW for a minimum of 20 ns. The SRCK input pin is the shift register flip-flops’ common clock that shifts in the value of the SER pin and shifts the shift register flip-flop values when the pin is toggled from a LOW to HIGH value, from 0 to 1. The second common control element also has two inputs. The G input pin is used to enable the integrated circuit’s output register flip-flops’ output when its value is LOW or 0 and to leave the output pins in a high impedance state when HIGH or 1. The RCK input pin is the output register flip-flops’ common clock that latches the shift register flip-flop values into the output register flip-flops when the pin is toggled from a LOW to HIGH value.

In order to use the integrated circuit properly, we must understand its most important electrical and timing characteristics. All inputs draw a maximum of 1 μA each. The integrated circuit’s output register’s outputs, QA to QH can source or sink 6 mA. The SRCK and RCK shift register and output register clocks must remain HIGH for at least 20 ns and LOW for at least 20 ns, allowing for a maximum clock frequency of 25 MHz. The SRCLR input pin must remain LOW for at least 20 ns for the serial register flip-flops to be reset. The SER, serial data input, pin must be stable at least 25 ns before the SRCK pin can be toggled from LOW to HIGH to latch the input value.

From the integrated circuit’s characteristics, we can say that an Arduino Uno digital output, which can source or sink 20 mA, can theoretically drive the inputs of up to 20 000 SN74HC595 integrated circuits if we ignore other electrical constraints. There is more to consider than the current required to drive clock inputs. We have to take into account wire lengths, the current required to drive integrated circuit outputs and integrated circuit layout. I would recommend not to daisy chain, that is serially connect clock inputs one after another, more than twenty integrated circuits without carefully considering the design of integrated circuit placement and wiring layout. As for the timing characteristics, the maximum speed at which we can toggle digital outputs on the Arduino, measured with an oscilloscope while executing two digitalWrite() calls is 3.5 μs, which is well above the minimum time of 20 ns required by the SN74HC595 clock inputs.

Creating 10 Digital Outputs from 3 Digital Outputs

We will use two SN74HC595 digital integrated circuits to drive a ten LED bar graph display from three Arduino digital outputs. In the following paragraphs we will discuss the circuit used to control the bar graph display using an Arduino C++ program. The program can be found on GitHub here.

The Electronics

The following diagram depicts the digital circuit we will use to demonstrate how to drive ten bar graph LEDs from three Arduino digital outputs. In the diagram, Arduino’s digital port 3 is connected to both SN74HC595 integrated circuits’ serial clocks, pin 11 of each integrated circuit. Arduino’s digital port 4 is connected to both SN74HC595 integrated circuits’ register latch clocks, pin 12 of each integrated circuit. Arduino’s digital port 2 is connected to serial data input, pin 14, of the SN74HC595 integrated circuit that is to drive the eight most significant, leftmost, LEDs of the bar graph. The serial output of that integrated circuit, pin 9, is connected to the serial data input, pin 14, of the SN74HC595 integrated circuit that is to drive the two least significant, rightmost, LEDs of the bar graph.

As shown on the diagram, the eight outputs of the topmost SN74HC595 integrated circuit are connected to the eight most significant LEDs’ anode of the bar graph through 1K resistors. The two most significant outputs of the bottom SN74HC595 integrated circuit are connected to the two least significant LEDs’ anode of the bar graph display through 1K resistors. All bar graph display LEDs’ cathodes are connected to ground.

The reset pins, pin 10, of each SN74HC595 integrated circuit are connected directly to 5V, preventing the serial shift registers from resetting. The enable pins, pin 13, of each SN74HC595 integrated circuit are connected to ground thus always enabling each latched register’s output. The following timing diagram shows the digital values applied to the shift registers with latch through the Arduino digital ports to output ten digital values to be displayed on the ten LED bar graph display. The top signal in the diagram represents the serial data output by the Arduino at digital port 2. Since the data can be either a 1 or 0, a HIGH or a LOW, we show both with a crossover at the point the value is set. The second line represents the serial register clock output by the Arduino at digital port 3. The third line represents the latched output clock output by the Arduino at digital port 4.

In order to send ten binary values to the bar graph LEDs, an Arduino program must send ten bits, least significant bit first. So, the program sets the value of the least significant bit at digital port 2. The program then toggles the serial clock signal at digital port 3 from LOW to HIGH, then HIGH to LOW. These two steps are repeated for each bit of information until the most significant bit is processed. Finally, the program toggles the latch clock signal at digital port 4 so that all values that were sent serially are provided to all LEDs in the bar graph at once.

Breadboarding the Circuit

The following picture depicts how to connect the different parts using a solderless breadboard, jumper wires, SN74HC595 digital integrated circuits, 1 KΩ resistors, LEDs. and an Arduino Uno micro-controller. In the schematic, I used discrete LEDs instead of a 10 LED bar graph, but the wiring would be identical.

The Program

The program used to demonstrate the use of the latched shift register is made up of the Arduino sketch, UsingShiftRegistersToIncreaseDigitalOutputs.ino, containing the setup() and loop() functions. There is also a class, LatchedShiftRegisterChannel, that embodies the functionality of the serially connected latched shift registers. The class is defined in the LatchedShiftRegisterChannel.h header file and the class methods are implemented in LatchedShiftRegisterChannel.cpp. We described classes in a previous post, Programming with Class. In the next paragraphs we will see how the LatchedShiftRegisterChannel class is implemented and how the main program uses this class to output values to ten LEDs using three digital output ports.

LatchedShiftRegisterChannel.h

The LatchedShiftRegisterChannel.h header file defines the LatchedShiftRegisterChannel class. In the header file, before the class definition, we declare a constant for the maximum number of digital outputs that are supported by the class, MAXIMUM_SERIAL_BITS. We then declare a structure, SerialCommunicationPins, to store the Arduino output ports associated with the serial clock, data, and latch clock pins that are used to drive the shift register integrated circuits. Follows the LatchedShiftRegisterChannel class declaration. There are four public methods. ~LatchedShiftRegisterChannel() is the class destructor called when an instance of the class goes out of scope or gets deleted. The LatchedShiftRegisterChannel() constructor with two arguments is the only constructor supported. It takes a SerialCommunicationPins structure as input as well as the maximum number bits, or digital output ports, that can be driven by the shift registers in the circuit. Each SN74HC595 integrated circuits can drive 8 bits each, hence two shift register integrated circuits can drive 16 bits. The transmit() method is used to output bits to the digital output ports driven by the shift registers. Finally, the setNumberOfActiveDigitalOutputs() method sets the actual number of digital outputs to drive. It is used if the value is different than the number of shift register bits that was set in the constructor.

#if !defined(LATCHEDSHIFTREGISTERCHANNEL_HEADER)
#define LATCHEDSHIFTREGISTERCHANNEL_HEADER
#include "Arduino.h"

const int MAXIMUM_SERIAL_BITS = 1024;

struct SerialCommunicationPins {
  int serialClockPin;
  int serialDataPin;
  int latchClockPin;
};
 
class LatchedShiftRegisterChannel {
  public:
    ~LatchedShiftRegisterChannel(); // Destructor
    LatchedShiftRegisterChannel(SerialCommunicationPins pins, int numberOfShiftRegisterBits);
    void transmit(const byte transmissionBuffer[], int transmissionSizeInBits) const;
    void setNumberOfActiveDigitalOutputs(int numberOfActiveShiftRegisterBits);
  private:
    LatchedShiftRegisterChannel();
    LatchedShiftRegisterChannel(const LatchedShiftRegisterChannel&);
    LatchedShiftRegisterChannel& operator = (const LatchedShiftRegisterChannel&);
    void sendASingleBit(bool digitalOutputValue) const;
    void latch() const;
    void clearShiftRegister() const;
    void prepareCommunicationPins() const;
    void makeCommunicationPinsHighImpedance() const;
    void sendPaddingBits(int numberOfBitsToTransmit) const;
    void sendBitsOneByteAtATime(const byte transmissionBuffer[], int numberOfBitsToTransmit) const;
    void sendBitsFromOneByte(byte transmissionByte, int numberOfBitsToTransmit) const;

  private:
    SerialCommunicationPins communicationPins;
    int numberOfActiveDigitalOutputs;
    int totalNumberOfDigitalOutputs;
};

#endif

Following the declaration of public methods, we declare the class’s private methods. First, the default constructor, copy constructor, and assignment operators are declared to prevent the compiler from creating default versions of these methods. We will not implement these methods, forcing the compiler to generate an error if a programmer tries to use them. We do this because there are no possible default values for the output ports used to drive the shift registers and because we do not want several instances of the class to drive the same set of connected shift registers. We then declare a set of methods used internally to perform the low-level output operations. These methods will be explained later in this post when we describe their implementation.

Finally, the class definition contains the variables associated with instantiations of the class. First, communicationPins contains the three digital output ports used to drive the serial clock, data, and latch clock pins of the shift registers. numberOfActiveDigitalOutputs contains the actual number of digital outputs that are to be driven by the shift registers’ output. totalNumberOfDigitalOutputs contains the number of digital outputs that could be driven if all of the shift registers’ outputs were used.

LatchedShiftRegisterChannel.cpp

The LatchedShiftRegisterChannel.cpp file contains the implementation of the class’s methods. As usual, the implementation file starts by including the header file containing the class definition. Then we have the declaration of BITS_IN_BYTES, a constant declaring the number of bits in a byte. We then have the declaration and implementation of a function, clampValue(), that takes three parameters as input: the value to be clamped, the lowest allowable value, minimumValue, and the maximum allowable value, maximumValue. The function returns either the value to be clamped, minimumValue if the value to be clamped is smaller, or maximumValue if the value to be clamped is larger. Note the presence of the inline keyword. It tells the compiler that this function is a good candidate to be expanded in line; instead of creating a function that is called, the compiler puts the body of the function inline, within the piece of code that calls the function. This is done to produce faster code when the function is trivial or almost trivial.

#include "LatchedShiftRegisterChannel.h"

const int BITS_IN_BYTE = 8;

inline int clampValue(int value, int minimumValue, int maximumValue)
{
  int clampedValue = value;
  if (clampedValue > maximumValue)
  {
    clampedValue = maximumValue;
  }
  if (clampedValue < minimumValue)
  {
    clampedValue = minimumValue;
  }
  return clampedValue
}

LatchedShiftRegisterChannel Constructor and Destructor

The first implemented public methods are the LatchedShiftRegisterChannel constructor and destructor. First, the destructor. It is called when an instance of the class is either deleted or goes out of scope. The destructor clears the shift register’s output by setting all its bits to zero. It then makes the communication pins high impedance by setting the three communication pins’ mode to INPUT. In the current program, the destructor is never called as the class instance never goes out of scope.

LatchedShiftRegisterChannel::~LatchedShiftRegisterChannel()
{
  clearShiftRegister();
  makeCommunicationPinsHighImpedance();
}

LatchedShiftRegisterChannel::LatchedShiftRegisterChannel(SerialCommunicationPins pins, int numberOfShiftRegisterBits)
{
  communicationPins = pins;
  totalNumberOfDigitalOutputs = clampValue(numberOfShiftRegisterBits, 0, MAXIMUM_SERIAL_BITS);
  numberOfActiveDigitalOutputs = totalNumberOfDigitalOutputs;
  prepareCommunicationPins();
  clearShiftRegister();
}

The constructor is called when an instance of the LatchedShiftRegisterChannel class is created. It has two arguments as input: pins, a structure containing the three pins to be used to communicate with the shift registers and numberOfShiftRegisterBits, an integer containing the number bits supported by the connected shift registers. The constructor saves the communication pins, communicationPins, and the total number of digital outputs, totalNumberOfDigitalOutputs. It limits its value to be positive and less than MAXIMUM_SERIAL_BITS. numberOfActiveDigitalOutputs is set to totalNumberOfDigitalOutputs as we assume that all shift register bits will be used. The constructor then prepares the communication pins to be output to and clears the shift registers.

transmit()

The transmit() public method is used to transmit information to the shift registers. It takes two arguments: transmissionBuffer and transmissionSizeInBits. The transmissionBuffer argument is an array of bytes stored least significant byte first. transmissionSizeInBits is the number of bits the transmissionBuffer byte array contains. First, the number of bits to send is clamped to the acceptable range between 0 and the number of active digital outputs. The bits are then serially sent to the shift registers, one bit at a time. If the transmission size is less than the number of active bits, padding bits are sent to ensure that all active bits have been addressed. Finally the shifted bits are latched in the shift register’s output registers.

void LatchedShiftRegisterChannel::transmit(const byte transmissionBuffer[], int transmissionSizeInBits) const
{
  int bitsToSend = clampValue(transmissionSizeInBits, 0, numberOfActiveDigitalOutputs);
  sendBitsOneByteAtATime(transmissionBuffer, bitsToSend);
  int paddingBitsToSend = numberOfActiveDigitalOutputs - bitsToSend;
  sendPaddingBits(paddingBitsToSend);
  latch();
}

setNumberOfActiveDigitalOutputs()

The number of active digital outputs can be smaller than the number of bits supported by the shift registers. The number of active digital outputs does not have to be a multiple of eight. The setNumberOfActiveDigitalOutputs() public method sets the number of active digital outputs according to the numberOfActiveShiftRegisterBits argument. The value is clamped between 0 and the total number of digital outputs.

void LatchedShiftRegisterChannel::setNumberOfActiveDigitalOutputs(int numberOfActiveShiftRegisterBits)
{
  numberOfActiveDigitalOutputs = clampValue(numberOfActiveShiftRegisterBits, 0, totalNumberOfDigitalOutputs);
}

Internal LatchedShiftRegisterChannel methods

All other methods implemented are private to the class. They implement the low level work required by the public methods: the constructor and destructor methods, the transmit() method and the setNumberOfActiveDigitalOutput() methods.

The prepareCommunicationPins() method puts the three Arduino digital pins used to communicate with the shift registers in OUTPUT mode and sets their value to LOW. The makeCommunicationPinsHighImpedance() method sets the three Arduino digital pins in INPUT mode. The clearShiftRegister() method sends LOW values serially to all shift register bits and latches the value in the output registers. None of these three methods take arguments nor do they return values.

The sendBitsOneByteAtATime() method takes two arguments: transmissionBuffer, a byte array containing the bits to be sent stored least significant bits in the least significant byte first (little endian), and numberOfBitsToTransmit, an integer containing the number of bits to send. The method first sends the bits from all complete full least significant 8-bit bytes, then the most significant bits left in the most significant byte.

void LatchedShiftRegisterChannel::prepareCommunicationPins() const
{
  pinMode(communicationPins.serialClockPin, OUTPUT);
  pinMode(communicationPins.serialDataPin, OUTPUT);
  pinMode(communicationPins.latchClockPin, OUTPUT);
  digitalWrite(communicationPins.serialClockPin, LOW);
  digitalWrite(communicationPins.serialDataPin, LOW);
  digitalWrite(communicationPins.latchClockPin, LOW);
}

void LatchedShiftRegisterChannel::makeCommunicationPinsHighImpedance() const
{
  pinMode(communicationPins.serialClockPin, INPUT);
  pinMode(communicationPins.serialDataPin, INPUT);
  pinMode(communicationPins.latchClockPin, INPUT);
}

void LatchedShiftRegisterChannel::clearShiftRegister() const
{
  for (int i = 0; i < totalNumberOfDigitalOutputs; i++)
  {
    sendASingleBit(LOW);
  }
  latch();
}

void LatchedShiftRegisterChannel::sendBitsOneByteAtATime(const byte transmissionBuffer[], int numberOfBitsToTransmit) const
{
  int bytesToSend = numberOfBitsToTransmit / BITS_IN_BYTE;
  for (int byteIndex = 0; byteIndex < bytesToSend; byteIndex++)
  {
    sendBitsFromOneByte(transmissionBuffer[byteIndex], BITS_IN_BYTE);
  }
  int bitsLeft = numberOfBitsToTransmit % BITS_IN_BYTE;
  if (bitsLeft > 0)
  {
    sendBitsFromOneByte(transmissionBuffer[bytesToSend], bitsLeft);
  }
}

void LatchedShiftRegisterChannel::sendBitsFromOneByte(byte transmissionByte, int numberOfBitsToTransmit) const
{
  byte byteToSend = transmissionByte;
  int bitsToSend = clampValue(numberOfBitsToTransmit, 0, BITS_IN_BYTE);
  for (int bitIndex = 0; bitIndex < bitsToSend; bitIndex++)
  {
    sendASingleBit((byteToSend & 1) == 1);
    byteToSend >>= 1;
  }
}

void LatchedShiftRegisterChannel::sendPaddingBits(int numberOfBitsToTransmit) const
{
  for (int i = 0; i < numberOfBitsToTransmit; i++)
  {
    sendASingleBit(LOW);
  }
}

void LatchedShiftRegisterChannel::sendASingleBit(bool digitalOutputValue) const
{
  digitalWrite(communicationPins.serialDataPin, digitalOutputValue);
  digitalWrite(communicationPins.serialClockPin, HIGH);
  digitalWrite(communicationPins.serialClockPin, LOW);
}

void LatchedShiftRegisterChannel::latch() const
{
  digitalWrite(communicationPins.latchClockPin, HIGH);
  digitalWrite(communicationPins.latchClockPin, LOW);
}

The sendBitsFromOneByte() method takes two arguments: transmissionByte, a byte containing bits to transmit, and numberOfBitsToTransmit, an integer containing the number of bits to transmit within the byte, least significant bit first. The method first clamps the number of bits to send between 0 and 8, then sends each bit, least significant bit first to the shift register. The sendPaddingBits() method takes one argument: numberOfBitsToTransmit, an integer specifying the number of padding bits to send. The method sends the specified number of LOW values to the shift register.

The sendASingleBit() method takes one argument: digitalOutputValue, a Boolean value specifying whether to send a HIGH or a LOW. The method sets the serial data pin to the value to output then toggles the serial clock pin HIGH then LOW. The latch() method takes no argument and toggles the latch clock pin HIGH then LOW.

The main Sketch

The main sketch first includes the LatchedShiftRegisterChannel.h file which contains the LatchedShiftRegisterChannel class definition. It then defines the constants used throughout the program. The serial data, serial clock and latch pins are defined as pins 2, 3, and 4 respectively. The number of LEDs is set to 10, the number of digital outputs is set to 16, the total number of outputs possible with two SN74HC595 shift register integrated circuits. The number of bytes corresponds to the number of shift register integrated circuits. The SerialCommunicationPins structure is initialized with the serial data, serial clock and latch pins. WAIT_TIME, is set to 100 milliseconds. The largest value is set to all LEDs lit, the least significant 10 bits set or 210 − 1 (0x03FF in hexadecimal).

The Leds object is instanced from the LatchedShiftRegisterChannel class using the serial communication pins and the number of LEDs. The displayValue, the value to be output to the LEDs is set to 1 and the array of bytes to be sent to the shift registers is declared.

#include "LatchedShiftRegisterChannel.h"

// Create bar graph display instance including shift register control pins
const int SERIAL_DATA_DIO = 2;         // Serial data input  - SN74HC595 pin 14
const int SERIAL_CLOCK_DIO = 3;        // Serial data clock  - SN74HC595 pin 11
const int LATCH_SIGNAL_DIO = 4;        // Output latch clock - SN74HC595 pin 12
const int NUMBER_OF_LEDS = 10;         // Number of LEDs to drive
const int NUMBER_OF_DIGITAL_OUTPUTS = 16; // Number of available shift register digital outputs
const int NUMBER_OF_BYTES = NUMBER_OF_DIGITAL_OUTPUTS/8;
const SerialCommunicationPins COMMUNICATION_PINS = {SERIAL_CLOCK_DIO, SERIAL_DATA_DIO, LATCH_SIGNAL_DIO};
const int WAIT_TIME = 100;             // 100 milliseconds per increment to get a one second full cycle
const int LARGEST_VALUE = (1 << NUMBER_OF_LEDS) - 1;
LatchedShiftRegisterChannel Leds(COMMUNICATION_PINS, NUMBER_OF_DIGITAL_OUTPUTS);
int displayValue = 1;
byte outputByteArray[NUMBER_OF_BYTES];

setup()

The setup() method is called once before the loop() method is called. It sets the number of outputs of the shift register channel to the number of LEDs to drive.

void setup() {
  Leds.setNumberOfActiveDigitalOutputs(NUMBER_OF_LEDS);
}

loop()

The loop() method is repeatedly called indefinitely after the setup() method has been called. It repeatedly outputs the value to the LEDs, shifts the value left, making each LED light up in turn, then waits the specified amount of time. The delay being 100 milliseconds ends up causing all LEDs to light up every second.

void loop() {
  outputValueToLeds();
  shiftValueLeft();
  delay(WAIT_TIME);
}

Other methods in the main sketch

There are three other methods in the main sketch. The first one, outputValueToLeds(), copies the value to be output into a byte buffer and then transmits the value to the shift register channel. The moveIntegerIntoOutputByteArray() method splits the integer specified as an argument into bytes, least significant byte first, and it copies the bytes into the byte buffer. The shiftValueLeft() method shifts the value to be output left by one bit. If the value is larger than the number of LEDs would allow, it is initialized to 1.

void outputValueToLeds()
{
  moveIntegerIntoOutputByteArray(displayValue);
  Leds.transmit(outputByteArray, NUMBER_OF_LEDS);
}

void moveIntegerIntoOutputByteArray(int integer)
{
  for (int i = 0; i < NUMBER_OF_BYTES; i++)
  {
    outputByteArray[i] = integer & 0x0ff;
    integer >>= 8;
  }
}

void shiftValueLeft()
{
  displayValue = displayValue << 1;
  if (displayValue > LARGEST_VALUE)
  {
    displayValue = 1;
  }
}

Putting It All Together

Build the circuit shown above and connect the Arduino Uno to your computer using a USB cable. On the computer, using the Arduino IDE, copy and paste the code above into a new sketch and files, or get a copy of the files from GitHub at https://github.com/lagacemichel/UsingShiftRegistersToIncreaseDigitalOutputs. Compile and download the sketch on the Arduino board and notice the chasing LED pattern that is displayed on the LEDs.

Driving Seven Segment Displays

Many projects require the ability to display values. Until now, we have used the Serial library to display information on the computer connected to the Arduino. Particularly, in the Sensing Temperature and Humidity post, we used the Serial library to display temperature and humidity on the IDE’s Serial Monitor window. In this post, we will use a four digit seven-segment display to show numbers directly from the Arduino micro-controller.

The featured image at the beginning of this blog post was published by ©Raimond Spekking / CC BY-SA 4.0 (via Wikimedia Commons).

The Seven Segment Display

The seven-segment display is an electronic device that uses seven LED segments organized in the shape of a number eight. The LEDs are lit in different patterns to form numerals 0 to 9. An LED in the form of a dot is sometimes added at the bottom right of the seven segments to serve as a decimal point. The picture at the left, above, depicts a single digit seven-segment LED display. Each of its segments is identified by a letter as shown in the center diagram. The diagram at the right depicts the electrical connections within the seven-segment LED display. Some seven-segment displays have a common cathode, as shown, others have a common anode. For the remainder of the post, we will use common cathode seven-segment displays.

To use the seven-segment display, we connect the common cathode to ground and apply a voltage to each segment through a current limiting resistor. A voltage is applied to the current limiting resistor attached to the segment that needs to be lit while the segments that are to remain extinguished are connected to ground. The following diagram shows how the number four, ‘4’, can be formed by connecting segments ‘b’, ‘c’, ‘f’, and ‘g’ to a voltage and segments ‘a’, ‘d’, ‘e’ and the decimal point, ‘dp’, to ground.

Each segment requires its own current limiting resistor to ensure that regardless of the number of segments lit, current through each LED segment is the same, guaranteeing that each LED segment’s intensity remains constant. If a current limiting resistor was to be connected to the common cathode, a constant amount of current would be distributed between all lit segments, making display intensity diminish with the number of segments lit. Displaying a ‘1’ using two segments would be twice as bright as a ‘4’, using four segments, and three times brighter than a ‘9’ using six segments. Eight digital output pins are required to control and light each of the seven segments and decimal point. This is more than half of the available digital pins on the Arduino Uno which has fourteen digital pins externally available.

To drive more than one seven-segment digit, we use a method called multiplexing. When multiplexing, we activate one digit at a time and apply a voltage to each of the seven segments and decimal point. The digits are activated in sequence, fast enough for the eye not to notice that digits are not all lit at the same time, and only for a brief instant. To activate a digit, we ground its common cathode while the other common cathodes remain unconnected. To achieve this on the Arduino, the digital pin connected to the common cathode of the digit to activate is set to OUTPUT and a LOW value applied to it. This lights the digit’s segments whose digital pins are set to HIGH as the common cathode digital pin acts as a ground. For all other digits not being displayed, we let their common cathodes “float”, as if they were disconnected, by setting their digital pin modes to INPUT. In input mode, digital pins do not provide current, or very little, and act as if the pin is not connected.

The Four Digit Seven-Segment Display

With the multiplexing technique, eight digital pins are required for all of the segments and decimal point, and one digital pin per digit to display. To display four digits, twelve digital pins are required. Seven-segment displays come in a variety of packaging and sizes. For this project, I used a 0.36″ (9.2 mm) common-cathode 4 digit seven-segment display. For all of this to work, electrical requirements must be met. The electrical characteristics of each of the LEDs making up the seven-segment display are similar to single LEDs as discussed in my Arduino’s Blink post. Of importance are the absolute maximum forward current, IF, and the forward voltage, VF. For this project, I used the four digit, common-cathode, seven-segment display 3461AS from XLITX. For each segment, the absolute maximum forward current is 30 mA and the forward voltage is typically 1.8 V. According to the specification, the relative luminosity increases linearly from 0 mA up to 20 mA. The following diagram shows the internal structure of the 3461AS four digit seven-segment display and its pinout.

Each Arduino digital pin can source or sink 40 mA. In order to drive each of the four digits, we will use 8 digital output pins and connect them to the seven segments and decimal point anodes through a current limiting resistor, and we will use 4 digital output pins, each connected to the common cathode of each digit. As described previously, to light a segment of one of the digits, we apply a HIGH, or 5 V, to the segment’s anode and apply a LOW, 0 V to the digit common cathode of the segment to be lit.

Up to eight segments can be lit at once all drawing the same amount of current. We must limit the current through each segment to no more than 5 mA in order not to exceed the 40 mA an Arduino digital pin can sink. If each segment drops 1.8 V, current limiting resistors will each drop 3.2 V. According to Ohm’s law, R = V / I and to draw a maximum of 5 mA, the current limiting resistor must be at least 3.2 V / 0.005 A or 640 Ω. We will use 1 KΩ current limiting resistors, limiting each segment’s current to 3.2 V / 1000 Ω, or 3.2 mA, and limiting the total current sunk by the digital pin connected to the common cathode to 25.6 mA. The following diagram depicts the electrical circuitry to connect a four digit seven-segment display to an Arduino.

The Arduino’s digital pins 2 through 9 drive segments ‘a’ through ‘g’ and the decimal point through eight 1 KΩ current limiting resistors. The common cathodes of digits one through four are connected to the Arduino’s digital pins 10 through 13 respectively.

Breadboarding

The following picture depicts how to connect the different parts using a solderless breadboard, jumper wires, a seven-segment four digit display, eight 1 KΩ resistors, and an Arduino Uno micro-controller.

Displaying a Floating-Point Number

To demonstrate how to drive the four digit seven-segment display, I wrote a program that increments a floating-point number from −99.9 to 999.9 in 0.1 increments, five times a second and displays it on the four digit seven-segment display. Each digit is displayed in turn for 1.25 ms every 5 ms, thus updating at 200 Hz with a duty-cycle of 25%. The program can be found on Github. To run the program, download file FourDigitSevenSegmentDisplay.ino and load it in the Arduino IDE. Compile and load the program onto the Arduino microcontroller and watch the floating-point value increment on the four digit seven-segment display. In the following sections, I will explain how to convert each digit to their seven-segment counterpart, and how to manage the conversion of a floating-point number into a sequence of digits. First, we define the floating-point display format, the way we want the floating-point number to be displayed on the seven-segment four digit display.

We set the number of decimal places to one. With four digits, the smallest number that can be displayed is −99.9 and the largest number is 999.9. When numbers are between −9.9 and −0.1 or between 10.0 and 99.9, only 3 digits are required and the leftmost digit is left blank. When numbers are between 0.0 and 9.9, only 2 digits are required and the two leftmost digits are left blank. The following diagram shows how to display all of these use cases.

The Demonstration Program

The program starts with the usual header followed by different constants and definitions used throughout the program. First, we define the digital pins used to drive segments ‘a’ through ‘g’ (SEGMENT_A through SEGMENT_G) and the decimal point (SEGMENT_DP). Then, we define the digital pins used to activate digits (DIGIT_1 through DIGIT_4). Note that the program assumes that the digital pin numbers of the pins driving segments ‘a’ through ‘g’ are consecutive and in incrementing order. This also holds for the digital pin numbers of the pins activating digits 1 through 4. The number of digits is defined in constant numberOfDigits. Constant digitTimeOn contains the number of microseconds each digit remains lit while constant timeBetweenIncrements contains the amount of time in seconds between each increment. Constant microsecondsInASecond contains the number of microseconds in a second.

/*
  Four digit seven-segment display demonstration
  Program that displays a floating-point counter on a four-digit
  seven-segment display. It is associated with the Four Digit
  Seven Segment Display blog post at https://lagacemichel.com
  MIT License
  Copyright (c) 2021, Michel Lagace
*/

// Digital output pins to turn on or off each segment of the selected digit
#define SEGMENT_A 2
#define SEGMENT_B 3
#define SEGMENT_C 4
#define SEGMENT_D 5
#define SEGMENT_E 6
#define SEGMENT_F 7
#define SEGMENT_G 8
#define SEGMENT_DP 9

// Digital output pins to select each of the four digits
#define DIGIT_1 10
#define DIGIT_2 11
#define DIGIT_3 12
#define DIGIT_4 13
const int numberOfDigits = 4;

// Number of microseconds to leave digit on to achieve 200 Hz
const int digitTimeOn = 1250;

// Interval in seconds between counter increments - 0.2 sec for 5 counts/sec
const float timeBetweenIncrements = 0.2;
const float microsecondsInASecond = 1000000.0;

// Number of digits after decimal points and floating-point constants
const int scaleOfNumber = 1; // number of digits after decimal point
const int scalingFactor = pow(10.0, scaleOfNumber);
const float increment = 1.0 / scalingFactor;
const float minimumValue = -pow(10.0, numberOfDigits - 1) / scalingFactor + increment;
const float maximumValue = pow(10.0, numberOfDigits) / scalingFactor - increment;

// Counters to control value displayed on seven-segment display
float displayCounter = minimumValue;
long iterations = 0;

// Segment encodings for digits 0, 1, 2, 3, 4, 5, 6, 7, 8, and 9 and for the
// minus // sign '-'. Each bit represents a segment in the following order:
// n/a, a, b, c, d, e, f, g. The most significant bit is not used. Segments
// are identified as follows
//          a
//      +-------+
//      |       |
//     f|       |b
//      |   g   |
//      +-------+
//      |       |
//     e|       |c
//      |       |
//      +-------+  . dp
//          d
const int segmentPatterns[] = {
  0b1111110, // 0: a, b, c, d, e, f
  0b0110000, // 1: b, c
  0b1101101, // 2: a, b, d, e, g
  0b1111001, // 3: a, b, c, d, g
  0b0110011, // 4: b, c, f, g
  0b1011011, // 5: a, c, d, f, g
  0b1011111, // 6: a, c, d, e, f, g
  0b1110000, // 7: a, b, c
  0b1111111, // 8: a, b, c, d, e, f, g
  0b1111011, // 9: a, b, c, d, f, g
};

Constant scaleOfNumber states the number of decimal places to display; constant scalingFactor contains the power of ten corresponding to the number of decimal places; constant increment is the floating-point value to add to the displayed floating-point value at every increment time; constant minimumValue is the minimum floating-point value than can be displayed; constant maximumValue is the maximum floating-point value that can be displayed before returning to minimumValue. Global variable displayCounter is the floating-point value displayed on the seven-segment display. It is initialized to the minimum floating-point value to display, minimumValue. Global variable iterations counts the number of loop() iterations between displayCounter increments.

Lastly, segmentPatterns, is an array of constant integers that contain seven-segment encodings for numbers zero to nine as a pattern of lit and extinguished segments on each of the seven-segment display digits. Each encoding uses the least significant seven bits of each constant integer to represent the state of each of the seven segments, from ‘a’ to ‘g’, left to right. The most significant bit, the left-most one, represents segment ‘a’. The bit to its right represents segment ‘b’ and so on until the least significant bit which represents segment ‘g’. Each encoding is a series of bits where a ‘1’ represents a lit segment and a ‘0’ an extinguished one. For instance, digit ‘four’ is formed by lighting segments ‘b’, ‘c’, ‘f’ and ‘g’. So, the second, third, sixth, and seventh bits from the left are set to ‘1’ and all other bits remain ‘0’. The encoding for ‘four’ is thus 0b0110011.

Extinguishing All Digits

After a reset and between each displayed digit, all digits are extinguished. This is done by floating all digit digital pins attached to the common cathodes. Floating of digital pins is achieved by setting their modes to INPUT.

// Extinguish all digits in the four digit seven-segment display
void extinguishDigits()
{
  // Float all common cathodes by setting all digit pins as INPUT
  for (int currentDigit = DIGIT_1; currentDigit <= DIGIT_4; currentDigit++)
  {
    pinMode(currentDigit, INPUT);
  }
}

Displaying a Minus Sign

To display a minus sign on one of the digits we need to light segment ‘g’. First, we extinguish all digits using function extinguishDigits(); then we make digital pins associated to segments ‘a’ through ‘f’ and the decimal point LOW; we make the digital pin associated to segment ‘g’ HIGH; and we finally activate the specified digit by setting the digit’s pin mode to OUTPUT and making the pin LOW.

// Displays a minus sign on the specified digit.
void displayMinusSign(int digit)
{
  // Extinguish all digits
  extinguishDigits();

  // Turn off all digit segments
  for (int currentSegment = SEGMENT_F; currentSegment >= SEGMENT_A; currentSegment--)
  {
    digitalWrite(currentSegment, LOW);
  }

  // Turn off decimal point segment
  digitalWrite(SEGMENT_DP,LOW);

  // Turn on g segment to display minus sign ('-')
  digitalWrite(SEGMENT_G, HIGH);

  // Activate requested digit
  int selectedDigitPin = digit + DIGIT_1 - 1;
  pinMode(selectedDigitPin, OUTPUT);
  digitalWrite(selectedDigitPin, LOW);
}

Displaying a Digit

To display a digit, we light and extinguish the appropriate segments according to the pattern in the segmentPatterns array corresponding to the digit to be displayed. First, we extinguish all digits using function extinguishDigits(); then we get the pattern and put it into variable segments. If the value to display is not between 0 and 9, all segments are extinguished.

We then loop through the segments in reverse order, starting with segment ‘g’. At each iteration of the loop, we use the bitwise AND operator ‘&’ to extract the least significant bit of the pattern. The bitwise AND operator performs a logical AND operation on each pair of corresponding bits between two values. If both bits are ‘1’, the resulting bit is ‘1’, otherwise, the resulting bit is ‘0’. In the code, the operation ‘segments & 0b0000001‘, results in 0b0000001, or ‘1’, if the least significant bit of segments is ‘1’, and 0b0000000, or ‘0’, if the least significant bit of segments is ‘0’. On the first iteration of the loop, the least significant bit of segments corresponds to the state of segment ‘g’. Segment ‘g’ is set to HIGH or LOW according to the value of segments‘ least significant bit. We then shift the value of segments right by one bit, making the least significant bit correspond to segment ‘f’. The loop then repeats and turns on or off each segment in turn, in reverse order, for segments ‘f’, ‘e’, ‘d’, ‘c’, ‘b’, and ‘a’.

// Display a value, between 0 and 9 on the specified digit of the display
// and display the decimal point if required.
void displayDigit(int digit, int value, bool decimalPoint)
{
  // Extinguish all digits
  extinguishDigits();

  // Prepare the segments to light
  int segments = 0;
  if ((value >= 0) && (value <= 9))
  {
    segments = segmentPatterns[value];
  }

  // turn on or off each segment
  for (int currentSegment = SEGMENT_G; currentSegment >= SEGMENT_A; currentSegment--)
  {
    digitalWrite(currentSegment, segments & 0b00000001);
    segments = segments >> 1;
  }

  // Set the decimal point
  digitalWrite(SEGMENT_DP, decimalPoint);

  // Select requested digit
  int selectedDigitPin = digit + DIGIT_1 - 1;
  pinMode(selectedDigitPin, OUTPUT);
  digitalWrite(selectedDigitPin, LOW);
}

The decimal point is then set according to the value of the Boolean argument decimalPoint. We finally activate the specified digit by setting the digit’s pin mode to OUTPUT and making the pin LOW.

Displaying a Floating-Point Number

In order to display the number, we extract each digit from right to left, first from the fractional part, then from the integer part. If the number is in the displayable range, we make the number positive, remembering the sign, we then extract the integer and fractional parts of the number as two integers, integerPart and fractionalPart. We first display the fractional part by iterating through the number of decimal places, dividing the number by 10, displaying the remainder, obtained using the modulo operator ‘%‘, and continuing with the result of the division. We thus iterate through all fractional part digits from right to left. Each fractional part digit stays lit for digitTimeOn microseconds.

We then enter a loop to display the integer part. The first integer part digit is always displayed, even if it is zero, along with the decimal point following it. As we did for the fractional part, at each iteration of the loop, we divide the integer part by 10, display the remainder and continue with the result of the division. The loop ends when the remaining integer part, remainingValue, reaches zero. each integer part digit is lit for digitTimeOn microseconds.

// Display the number passed as a parameter on the four digit
// seven-segment display.
void displayNumber(float number)
{
  // Process digits from right to left
  int currentDigit = numberOfDigits;

  // Only numbers smaller than the maximum value can be displayed
  if ((number >= minimumValue) && (number <= maximumValue))
  {
    // Scale the number and extract decimal and integer portions of number
    bool negative = false;
    if (number < 0.0)
    {
      negative = true;
      number = -number;
    }
    int integerPart = number;
    int fractionalPart = (number - integerPart) * scalingFactor;

    // Display fractional part
    int remainingValue = fractionalPart;
    for (int i = 0; i < scaleOfNumber; i++)
    {
      int digit = remainingValue % 10;
      remainingValue = remainingValue / 10;
      displayDigit(currentDigit--, digit, false);
      delayMicroseconds(digitTimeOn);
    }

    // Display integer part
    remainingValue = integerPart;
    bool decimalPoint = true;
    do
    {
      int digit = remainingValue % 10;
      remainingValue = remainingValue / 10;
      displayDigit(currentDigit--, digit, decimalPoint);
      decimalPoint = false;
      delayMicroseconds(digitTimeOn);
    } while (remainingValue > 0);

    // Display minus sign if number is negative
    if (negative)
    {
      displayMinusSign(currentDigit--);
      delayMicroseconds(digitTimeOn);
    }
  }

  // Extinguish all digits ensuring that all digits are displayed the same amount of time
  // and wait a bit if not all digits were lit, ensuring constant intensity for all numbers
  // currentDigit actually contains the number of digits left.
  extinguishDigits();
  delayMicroseconds(digitTimeOn * currentDigit);
}

If the number is negative, we display a minus sign on the next digit to the left. The minus sign is displayed for digitTimeOn microseconds. All digits are then extinguished and a delay, in microseconds, corresponding to the number of blank digits times the value of digitTimeOn is introduced to ensure that digits are always turned on the same amount of time regardless on the number of digits displayed. This guarantees that all digits of the four digit seven-segment display have always the same intensity.

Setting up the Arduino

During the setup() portion of the Arduino program, we float all digit selection pins by setting the pin mode to INPUT using the extinguishDigits() call. We then set the mode of the digital pins driving the segments ‘a’ through ‘g’ and the decimal point to OUTPUT. Global variables displayCounter and iterations are reset to their default values.

// Set all digital pins used for digit selection as input to extinguish all digits
// and set display segments as outputs.
void setup() {

  // Float all digit selection digital pins as input
  extinguishDigits();
  
  // Set all segment digital pins as output and turn them off
  for (int currentSegment = SEGMENT_G; currentSegment >= SEGMENT_A; currentSegment--)
  {
    pinMode(currentSegment, OUTPUT);
  }

  // Set decimal point digital pin as output and turn it off
  pinMode(SEGMENT_DP, OUTPUT);

  // Reset counters
  displayCounter = minimumValue;
  iterations = 0;
}

The Main Loop

In the main loop() we increment the iteration counter and then check if the number of iterations adds up to the time to wait between increments, timeBetweenIncrements. If so, we reset the number of iterations, increment DisplayCounter and if it exceeds maximumValue, it is set to minimumValue. DisplayCounter is then displayed on the four digit seven-segment display through a call to displayNumber().

// Continuously display the counter and increment it at the specified interval.
void loop() {
  // Increment number of iterations until the interval between increments has been reached
  // Each iteration takes digitTimeOn*numberOfDigits microseconds
  iterations++;
  if ((iterations * digitTimeOn * numberOfDigits / microsecondsInASecond) > timeBetweenIncrements)
  {
    // Reset loop counter and increment display counter
    iterations = 0;
    displayCounter += increment;

    // Reset display counter if it has reached the maximum value
    if (displayCounter > maximumValue)
    {
      displayCounter = minimumValue;
    }
  }

  // Display floating-point counter value
  displayNumber(displayCounter);
}

What Next

Of course, displaying a counter is not the best use for a four digit seven-segment display. It did, however, allow us to test all use cases and ensure that the functions and program were functioning properly. Instead of showing a counter, one could display temperature, light intensity, voltage, current, or any value of interest. We have just gained the capability of displaying values without being connected to a computer.

Switch Debouncing

In the LED Toggle with a Push-Button Switch post, I have explained how electro-mechanical devices, such as push-button switches, do not close or open an electrical circuit instantaneously, causing electrical noise. Every time a switch closes or opens, spring-loaded pieces of metal bounce causing the circuit to be opened and closed rapidly for a brief moment. In my previous post, I explained how to counteract switch bounce using software. In this post I will show a simple electronic solution to switch bounce using a resistor and a capacitor. In the Arduino’s Blink post we have seen that resistors are electronic devices that restrict current flow according to Ohm’s law. We will now describe a new passive electronic device, the capacitor. The featured image at the beginning of this blog post was created by Michael Maggs and edited by Richard Bartz, CC BY-SA 3.0, and can be found in the Wikimedia Commons.

Capacitors

A capacitor is a two-terminal device that can store electrical energy. It consists of two leads attached to conducting bodies that are separated from one another by an insulator, called a dielectric. Because of the dielectric, charges cannot move from of conducting body to the other within the device. Instead, charges of equal magnitude and opposite sign accumulate on each conducting body in proportion to the voltage across the device. The capacitance of a capacitor is defined as the ratio of the magnitude of the charge Q on either conducting bodies to the magnitude of the potential difference V between the two conducting bodies.

C = Q/V

It follows from its definition that the unit of capacitance is coulombs per volt. A capacitance of one coulomb per volt is said to have a value of one farad (1 F) in honor of Michael Faraday. In terms of quantity of electrons, one coulomb corresponds to 6.25×1018 electrons. The most common type of capacitor consists of two conducting plates parallel to each other and separated by a distance which is small in comparison to the linear dimensions of the plates such as in the following figure. Note that the quantity of positive charges is always the same as the quantity of negative charges and that the total charge remains zero.

undefined

In electronic schematics, capacitors are represented by two parallel lines separated by a gap with perpendicular lines attached to each parallel line representing the leads as in the following figure. Note the second representation in the middle which has a straight line and a curved one. Both representations are valid. Some capacitors are polarized, that is that one lead must always have a higher voltage than the other in order to function properly and not be damaged. A plus sign indicates the positive lead of a polarized capacitor as shown with the capacitor symbol at the right.

We have determined the relationship between charge and voltage. It would be more interesting to establish the relationship between current and voltage. It turns out that current is defined as the rate of change of charge over time. Hence, current is defined as

i = dQ/dt

Where dQ, delta charge, is the change in charge and dt, delta time, is the change in time. Current is thus defined as the quantity of charge per second, or the number of coulombs per second. One ampere, the standard unit of current is defined as one coulomb per second. Since Q = C•V, the relationship between current and voltage is

i = dQ/dt = C•dv/dt

The current in a capacitor is equal to the capacitance of the capacitor times the rate of change of voltage over time. This type of equation is called a differential equation and solving it is beyond the scope of this post. Its solution depends on the function representing the change of voltage over time. I will present, however, solutions to this equation when dealing with digital circuitry.

Within digital circuitry, we are interested in the operation of the capacitor when values in the circuit change between a logical low, 0 V, and a logical high, 5 V on an Arduino Uno, and when the values in the circuit change from a logical high to a logical low. Instantaneously switching the voltage across a capacitor requires an infinite amount of current according to the equation above. The change in time is 0 seconds, being instantaneous, resulting in a division by zero. Of course, this does not happen in reality because there are no perfect conductors and wires and device leads are always slightly resistive, limiting the current required to charge up the capacitor. Let’s find out what happens when we put an actual resistor in series with a capacitor and switch the voltage from low to high. In the following figure, the voltage across the capacitor is initially 0 V and we close a switch, applying 5 V across the resistor and capacitor.

The moment the switch is closed, the voltage across the capacitor is 0 V while the voltage across the resistor is 5 V. As explained in the Blink post, Kirchhoff’s voltage law stipulates that the sum of voltage drops along a closed-circuit loop is 0 V. In this case, the battery provides 5 V while the resistor and capacitor drop 5 V together. At the moment the switch is closed and as the capacitor’s charges start accumulating on its conducting bodies, the current through the circuit is I = V/R, following Ohm’s law. As charges accumulate in the capacitor, the voltage across the capacitor increases and the voltage across the resistor decreases. Eventually, after a long time, the voltage across the capacitor reaches 5 V while the voltage across the resistor becomes 0 V and current ceases flowing in the circuit. Charges start by accumulating fast and as time goes by and as current becomes smaller and smaller, charges within the capacitor accumulate at a slower and slower rate. Intuitively, we can draw a graph of the voltage across the capacitor as time goes by as follows.

We have seen the behavior of a capacitor as it accumulates charges, or charges up, through a resistor. Let’s now see what happens when a capacitor discharges through a resistor. In the following figure, the voltage across the capacitor is initially 5 V and we close a switch, applying 5 V across the resistor as we close the circuit.

The moment the switch is closed, the voltage across the resistor reaches 5 V. Again, Kirchhoff’s voltage law stipulates that the sum of voltage drops along a closed-circuit loop is 0 V. In this case, the capacitor provides 5 V while the resistor drops 5 V. At the moment the switch is closed and as the capacitor’s charges start to flow out of its conducting bodies, the current through the circuit is I = V/R, following Ohm’s law. As charges flow out of the capacitor, the voltage across the capacitor and resistor decreases. Eventually, after a long time, the voltage across both capacitor and resistor reaches 0 V and current ceases flowing in the circuit. Charges start by flowing out of the capacitor fast and as time goes by and as current becomes smaller and smaller, the capacitor discharges at a slower and slower rate. Intuitively, we can draw a graph of the voltage across the capacitor as time goes by as follows.

It turns out that the voltage across the capacitor as a function of time for the discharging capacitor through a resistor is:

VC = V0e-t/RC

Where VC is the voltage across the capacitor, V0 is the initial voltage across the capacitor, t is the time in seconds after the switch has been closed, R is the value in ohms of the resistor and C is the value in farads of the capacitor. Similarly, the voltage across the capacitor as a function of time for the capacitor charging through a resistor is:

VC = V0(1 – e-t/RC)

Where VC is the voltage across the capacitor, V0 is the voltage across the voltage source, t is the time in seconds after the switch has been closed, R is the value in ohms of the resistor and C is the value in farads of the capacitor. The product of the resistance by the capacitance, RC, is called the RC time constant and its value is in seconds. It corresponds to the time it takes for the capacitor to charge or discharge by approximately 63%.

Capacitor Devices

Capacitors are manufactured in many ways and take many size, shape and form. All capacitor devices are built around the same principle: two conductors, often in the form of plates, separated by dielectrics. Where they differ is how they are constructed and the type of dielectrics used. Most capacitors used in circuits are of one of two types: non-polarized and polarized capacitors.

Ceramic, paper and film capacitors are non-polarized capacitors that are named after the dielectric material used in the capacitor. Electrolytic capacitors are polarized capacitors that are named after the material used as the anode, the positive lead of the capacitor, such as aluminium, tantalum, niobium, and the electrolyte used as the cathode, the negative lead of the capacitor. Generally speaking, non-polarized capacitors have values smaller than one microfarad and polarized capacitors have values greater or equal to one microfarad. The following picture shows three types of capacitors.

In the picture above, the first capacitor to the left is a ceramic disk capacitor with marking 680K 1KV Z5R. The middle capacitor is a Mylar film capacitor with marking NIH153. The third capacitor is an electrolytic aluminium capacitor with marking 2200µF 25V. The first two capacitors are non-polarized capacitors while the last one is polarized with its cathode marked with a minus sign.

Markings tell us about the value and ratings of the capacitor. For the first capacitor to the left, the marking 680K is read as follows: the first two digits represent the value of the capacitor, the third digit represents the power of 10 multiplier in picofarads, 10-12 farads, for the capacitor value and the letter represents the tolerance. In this case, the capacitor has a 68×100 picofarads value, 68 pF. The K represents a capacitance value tolerance of 10%. 1KV is the voltage rating of the capacitor, 1 kilovolt. Z5R represents the temperature characteristic of the capacitor, there is a maximum capacitance value shift of +15% when temperatures range from 10˚C to 85˚C. Specifications for ceramic disk capacitors can be found here.

In the case of the polyester film capacitor, the middle one, the first two digits represent the value of the capacitor and the third digit represents the power of 10 multiplier in picofarads, 10-12 farads, for the capacitor value. In this case, the capacitor has a value of 15×103 picofarads, 15000 pF, 15 nF, or 0.015 µF. I did not find the meaning for the “NIH” marking. The markings on the electrolytic capacitor are quite explicit: it has a value of 2200 microfarads and a voltage rating of 25 volts.

Demonstrating Mechanical Bounce in a Switch

In order to demonstrate the effect of switch bounce I built a circuit and Arduino program that sequentially turns LEDs on and off every time a high to low edge is detected at one of the digital inputs of an Arduino Uno. The circuit is as follows.

In this circuit, the Arduino’s digital input/output pin 12 is connected to a push-button switch and a 10 K pull-up resistor. The other end of the pull-up resistor is connected to the 5 V supply and the other end of the switch is connected to ground. The rest of the circuit consists of 4 LEDs whose cathodes are connected to ground through 330 Ω resistors and whose anodes are connected to the Arduino’s digital input/output 8, 9, 10, and 11 respectively. The following diagram depicts how to connect the different parts using a solderless breadboard, jumper wires, four LEDs, a push-button switch, a 10 K resistor and four 330 Ω resistor.

Switch Bounce Demonstration Program

The Arduino sketch used to demonstrate switch bounce can be found on Github at https://github.com/lagacemichel/SwitchBounce. Download the sketch SwitchBounce.ino and load it in the Arduino IDE. You can also copy the following code directly into a new Arduino sketch within the IDE.

Following the usual comment at the beginning of the sketch, you will find a definition for INPORT, the input port connected to the switch, and the Boolean value switchState holding the last state of the input switch. Then LED0, LED1, LED2, and LED3, the output ports to the four LEDs, are defined followed by the definition of Boolean value litLED holding a value between 0 and 3 corresponding to the currently lit LED.

The board circuitry is prepared for use in the setup() function. The pin mode for pin INPORT is set to INPUT, which will allow us to read the switch value. The pin mode for pins LED0, LED1, LED2, and LED3 is set to OUTPUT, allowing the program to output HIGH or LOW values to the LEDs. Within setup(), switchState is set to HIGH, the switch is not being depressed, and litLED is set to 0 (zero) indicating that LED0 is lit. We finally turn LED0 on and LED1, LED2 and LED3 off using the digitalWrite() built-in function.

/*
Switch Bounce sketch
Uses four LEDs connected to digital I/O pins 8, 9, 10, and 11 to demonstrate
the effect of a mechanical switch's bounce by lighting the LEDs in sequence
everytime the switch's input goes from high to low.
MIT License
Copyright (c) 2020, Michel Lagace
*/

// Switch value will be read from pin 12
#define INPORT 12
static bool switchState = HIGH; // State of the switch

// LED values will be output on pins 8, 9, 10, and 11
#define LED0 11
#define LED1 10
#define LED2 9
#define LED3 8
static unsigned int litLED = 0; // Currently lit LED

// Setup the board.
void setup() {
  // Set Arduino<s input and output ports
  pinMode(INPORT, INPUT);
  pinMode(LED0, OUTPUT);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);

  // Initialize switch state and currently lit LED
  switchState = HIGH;
  litLED = 0;
  digitalWrite(LED0, HIGH);
  digitalWrite(LED1, LOW);
  digitalWrite(LED2, LOW);
  digitalWrite(LED3, LOW);
}

// Wait for an edge and return state
bool waitForEdge() {
  bool newValue = switchState;
  while (newValue == switchState) {
    newValue = digitalRead(INPORT);
  }
  switchState = newValue;
  return newValue;
}

// Repeat forever
void loop() {
  // Wait for a rising or falling edge
  bool value = waitForEdge();

  // Increase output on dropping edge (input is LOW when button is pressed)
  if (!value) {
    litLED++;
    if (litLED > 3) {
      litLED = 0;
    }

    // Light up the appropriate LED
    digitalWrite(LED0, litLED == 0);
    digitalWrite(LED1, litLED == 1);
    digitalWrite(LED2, litLED == 2);
    digitalWrite(LED3, litLED == 3);
  }
}

The waitForEdge() function waits until the switch value changes from its previously registered value in variable switchState to a new switch value. The function first copies the value of switchState into the newValue variable and loops, using a while loop, until the value of the switch value read using the digitalRead() built-in function becomes different. Variable switchState is set to the new switch value and the value is returned by the function.

Finally, the main loop() function repeatedly waits for a switch value change through a call to the waitForEdge() function. If the returned value is LOW, that is the push-button has been depressed making the (!value) condition true, we increment the value of litLED by one using the post increment operator ++. The post increment operator increases the value of the variable by one after all other operations in the statement have been evaluated. If the value of litLED is larger than 3 it is set to 0. The last four lines of the loop() function set the LED output pins to HIGH or LOW according to the value of variable litLED. LED0 is lit if variable litLED is 0, LED1 is lit if litLED is 1, and so forth for each LED output pin. Note that the second parameter of the digitalWrite() function takes a Boolean value. To set the digital output to HIGH, the parameter is set to true, and it is set to false to set the digital output to LOW. This allows us to use a Boolean test, such as the equality test, to set the output to the appropriate value.

Observing Switch Bounce

Without mechanical bounce, the circuit and program behaves as follows: every time the switch is depressed, the waitForEdge() function detects a change from HIGH to LOW and returns the value LOW. The main loop() function, because the returned value is LOW increments the value of variable litLED and the next LED is turned on while the currently lit LED is turned off. Each LED is turned on, in sequence, at each switch press.

However, since there is mechanical bounce, every time the switch is pressed, there is more than one HIGH to LOW transition and the litLED variable is incremented one, two, or more times making the program skip LEDs when the switch is depressed. The same phenomenon may happen when the switch is released since mechanical bounce may happen both when the switch is depressed and when it is released. Try to hit the switch several times rapidly to see it happen. Sometimes the LEDs will light in sequence and at other times LEDs will be skipped.

A Switch Debounce Circuit

Following, is the switch bounce demonstration circuit to which I have added a resistor and a capacitor. The resistor, R1, is connected on one side between the pullup resistor and the switch and on the other side to the Arduino’s input pin. The capacitor, C1, is connected between the Arduino’s input pin and ground. The voltage across the capacitor is thus applied to the Arduino’s input pin. The idea is to try to keep the voltage at the input pin LOW when the switch is depressed even though the voltage value at the switch rapidly changes from HIGH to LOW and LOW to HIGH because of mechanical bounce. Similarly, we try to keep the voltage at the input pin LOW when the switch is released for as long as there is electrical noise caused by the mechanical bounce.

As discussed in a previous post, I measured up to 4 ms worth of noise when the micro-switch in this circuit is depressed or released. Since the system is to activate upon the switch being depressed, we want the voltage across the capacitor to drop fairly fast, but remain between 0 and 1 volt, a LOW value, for at least 4 ms. As a rule of thumb, we want the voltage to drop at least 10 times faster than when it comes back up. From the equation of a charging capacitor

VC = V0(1 – e-t/RC)

We want VC to remain between 0 V and 1 V for at least 4 ms. In the previous formula for a charging capacitor, we substitue 1 V for VC, the maximum capacitor value; 5 V for V0, the supply value; 4 ms for t, the maximum time for the capacitor voltage to reach 1 V; C1 for C, the value of the capacitor; and (10 K + R1) for R, the value of the resistor while charging the capacitor. We thus get

1 V = 5 V(1 – e-4 ms/(10 K+R1)C1)

4/5 = e-4 ms/(10 K+R1)C1)

ln 4/5 = -4 ms/(10 K+R1)C1

(10K +R1)C1 = -4 ms/ln 0.8

From the previous formula for a discharging capacitor

VC = V0e-t/RC

We want the capacitor voltage to reach 1 V, the maximum voltage for a LOW, in less than 0.4 ms. So, using the formula for the discharging capacitor, we substitute 1 V for VC maximum capacitor value; 5 V for V0, the initial capacitor voltage; 0.4 ms for t, the maximum time for the capacitor voltage to reach 1 V; C1 for C, the value of the capacitor; and R1 for R, the only resistor through which the capacitor discharges when the switch is depressed. We get

1 V = 5 Ve-0.4 ms/R1C1

1/5 = e-0.4 ms/R1C1

ln 1/5 = -0.4 ms/R1C1

R1C1 = -0.4 ms/ln 0.2

Hence we get two equations

10 K C1 + R1C1 = -4 ms/ln 0.8

R1C1 = -0.4 ms/ln 0.2

Substituting -0.4 ms/ln 0.2 for R1C1 in the first equation, we get

10K C1 – 0.4 ms/ln 0.2 = -4 ms/ln 0.8

C1 = (0.4 ms/ln 0.2 – 4 ms/ln 0.8)/10 K

C1 = 1.7 µF

C1 is approximately equal to 1.7 µF (microfarads). The standard capacitor value closest to 1.7 µF is 2.2 µF. Substituting 2.2 µF for C1 in the second equation, we get

R1 = -0.4 ms/(2.2 µF ln 0.2)

R1 is approximately equal to 112 Ω. The standard resistor value closest to 112 Ω is 100 Ω. In order to cope with a maximum switch bounce of 4 ms, we modify the circuit with C1 set to 2.2 µF and R1 set to 100 Ω. In the following circuit diagram, notice that the capacitor symbol has changed and that the capacitor is now polarized. This is because when capacitors have a value larger than 1 µF we generally use polarized capacitors such as electrolytic capacitors.

The following graph depicts what happens to the mechanical noise after the resistor and capacitor are added to the circuit. In blue, we have the voltage across the switch. Notice the two 1 ms spikes when the switch is depressed and the 1 ms downward spike 3 ms after the switch has been released. In orange, we have the voltage across the capacitor. Notice how all spikes have been flattened below 1 volt and how relatively quickly the voltage drops when the switch is depressed. It takes approximately 0.4 ms for the capacitor to discharge to 1 V when the switch is depressed. Conversely, it takes about 20 ms for the capacitor to charge to 3 volts, a HIGH value, after the switch has been released, limiting the number of successive switch activation to approximately 40 switch closures a second, which is more than acceptable.

The following picture depicts how to connect the different parts of the electronic debounce demonstration circuit using a solderless breadboard, jumper wires, four LEDs, a push button, a 2.2 µF capacitor, a 100 Ω resistor, a 10 K resistor and four 330 Ω resistor. Note that the lead aligned with the negative sign on the capacitor body is connected to ground.

Observing Switch Debounce

With the new circuit, whether there is mechanical bounce or not, the circuit and program behaves as follows: every time the switch is depressed, the waitForEdge() function detects a change from HIGH to LOW and returns the value LOW. The main loop() function, because the returned value is LOW increments the value of variable litLED and the next LED is turned on while the currently lit LED is turned off. Each LED is turned on, in sequence, at each switch press. Because the capacitor eliminates switch bounce, the LED sequence advances by only one LED every time the switch is depressed.

Programming with Class

In this project we will use a Red-Green-Blue (RGB) LED to create color sequences, morphing or stepping from one color to the next. A push-button will allow the user to switch the color sequence being displayed. RGB LEDs are used in appliances to create moods by illuminating translucent materials with varying colors. Let’s first examine the characteristics of an RGB LED.

RGB LED

RGB LEDs consist of three LEDs, one red, one green, and one blue contained in a single translucent or transparent package. Each color is controlled separately, allowing a wide gamut of colors to be created by varying the amount of electrical current flowing through each of the LEDs. The following diagram shows the RGB LED schematic and its pinout. The RGB LED portrayed is a common cathode RGB LED. Common anode RGB LEDs are also available.

RGB LED Schematic

Apart from its packaging, the RGB LED works as if it was three independent LEDs and each can be wired in a circuit the way it was done in the ‘Blink’ circuit. The following diagram shows the circuit used for this project. It contains an RGB LED as well as a push-button that will be used to switch between color sequences. Note that we are using three resistors each separately limiting current in each of the LED. If we would have put a single resistor at the common cathode junction of the LEDs, the LEDs would have still lit, but their brightness would have been impacted by other LEDs being activated or not.

The RGB LED Circuit

RGB LED Circuit

Digital I/O pin 9 is connected to the red LED; digital I/O pin 10 is connected to the green LED; and digital I/O pin 11 is connected to blue LED. All connections are made through 330Ω resistors placed between the digital I/O pin and the anode of each light emitting diode. Digital I/O pin 12 is connected to a push button that is connected to ground when depressed. A 10K pullup resistor is connected between the digital I/O pin and the 5-volt supply, ensuring a HIGH input value when the push button is not depressed.

You might have noticed the ’tilde’ character, ‘~’, associated with some of the Arduino’s digital I/O pins. These digital I/O pins are special in that not only do they support digital output as HIGH and LOW values, but they also support analog output values. Instead of being output as a constant voltage value, analog values at these pins are output as a train of on and off pulses. The ratio between on and off is determined by the analog value. This is called pulse width modulation (PWM). In the Arduino, analog output values are discrete values between 0 and 255. For an analog value of 0, the output is always off; for an analog value of 64, the output is 25% on, 75% off; for an analog value of 128, the output is 50% on and off; and for an analog value of 255, the output is always on. The brightness of each LED is proportional to the amount of on time at the digital output. The frequency of the signal at pins 9, 10, and 11 on the Arduino Uno is approximately 490Hz.

Breadboarding

The following picture depicts how to connect the different parts using a solderless breadboard, jump wires, an RGB diode, a push button, a 10K resistor and three 330Ω resistors.

Programming with Class_bb

The RGB LED Program

The purpose of the program is to display a show of colors using the RGB LED. We want the LED to change colors following a sequence of pre-selected colors. The color change can be abrupt, or gradual, depending on the sequence. Because it would be arduous to recreate the code from the code snippets in this blog, I have created a Programming with Class repository on the Git Website. Simply click on the link, then on the “Clone or Download” button to download a zipped version of the files and copy them in a folder named “ProgrammingWithClass” on your computer. You can then load the “ProgrammingWithClass.ino” sketch in the Arduino Integrated Development Environment (IDE).

The program in this project can be fetched from the  Programming with Class repository on the Git Web site.

The Main Sketch

As usual, the Arduino sketch file starts with a comment stating the purpose of the program, the author and copyright notice. The program is licensed under the standard MIT license.

Next, we include two files: “rgbLed.h” and “rgbColor.h“. These files declare the rgbLed and rgbColor classes. We will cover the content of these files, what classes are, and how to program them later in this post. For now, let us simply accept that these classes exist and that we can create variables with them which I will describe shortly.

/*
Main ProgrammingWithClass sketch
Program that outputs sequences of colors to an RGB LED. The
sequence shown can be selected by a push button. This sketch
demonstrates the use of classes. It is associated with the
Programming with Class blog post on https://lagacemichel.com
MIT License
Copyright (c) 2018, Michel Lagace
*/
#include "rgbLed.h"
#include "rgbColor.h"

We have already seen an example of a class: the String class. The String class comes with the Arduino system. It allows programmers to easily work with character sequences. We already have used String classes as part of the Morse Code Generator project. One characteristic of variables created with classes is that they encapsulate, that is hide, the data that they contain. A String variable certainly contains a sequence of characters and possibly a length value, or maybe it is implemented using a null-terminated character sequence. We do not know, unless we actually look at the code implementing the String class and truly, we do not care as long as we can operate on a String. Another characteristic of variables created from classes is that we can call functions, called methods when associated to classes, directly acting upon the variable. As an example from the Morse Code Generator project, we use the length() method associated with a String as follows:

String text = "abcd";
int length = text.length();

In the code above, we create a variable text of type String, the name of the class, and initialize it with the character string “abcd”. We then create an integer variable called length and initialize it with the length, the number of characters, contained in the text variable using the length() method. The method is called by appending the variable name with a period (‘.‘) then the name of the method followed by parenthesis. Methods behave very similarly to functions and there could be parameters within the parenthesis following the method name if required. Notice the strength of variables created from classes as they behave almost like data types.

Variables created from classes are also called ‘objects‘. Along with a set of rules, design patterns, and best practices, objects are at the root of Object-Oriented Programming. Arduino software is written in C++, an object-oriented programming language that also allows C-language constructs. In the next section we will see how to use the rgbColor and rgbLed objects. Later in the blog, we will see the details of the implementation of these classes.

The rgbColor and rgbLed Classes

The rgbColor class allows programmers to create color objects based on the red, green and blue emissive color triad. Users can create a rgbColor object using the three colors or simply by declaring a rgbColor variable without stating values. rgbColor object declaration has the following forms: rgbColor color(float red, float green, float blue) or rgbColor color. Following are examples of rgbColor object declarations.

rgbColor red(1.0, 0.0, 0.0);
rgbColor black;

In the first case, a red object is created by setting the red component to 1.0 and the green and blue components to 0.0. Color components are set to floating point values between 0.0 and 1.0, from completely off to completely on. A rgbColor object can also be created without any component, in which case all components are set to 0.0, corresponding to black. rgbColor objects can also be created from other rgbColor objects, or by assigning a rgbColor object to another.

rgbColor darkRed(0.25, 0.0, 0.0);
rgbColor anotherRed(red);
anotherRed = darkRed;

rgbColor object anotherRed is created as a copy of object red, then assigned the value of just created rgbColor object darkRed. rgbColor also sports three methods to obtain the values, between 0.0 and 1.0, of each color component making up the color stored in a rgbColor object. The following depicts these methods.

float r = darkRed.red();
float g = darkRed.green();
float b = darkRed.blue();

Methods, as shown above, are function names following an object name, separated by a period (‘.‘). The rgbColor class supports one more method, the gradate() method.

rgbColor blue(0.0,0.0,1.0);
rgbColor green(0.0,1.0,0.0);
rgbColor cyan;
cyan.gradate(blue,green,0.5);

The gradate() method assigns to the rgbColor object a color between the first and second color specified, scaled according to the third function parameter, the gradient. The gradient specifies the amount mixed from each color according to the formula

Color = (1.0 – Gradient)•Color1 + Gradient•Color2

A value of 0.0 makes the resulting color equal to the first color, C1; a value of 1.0 makes the resulting color equal to the second color, C2; a value between 0.0 and 1.0 makes the resulting color a combination of both colors according to the gradient specified.

The program uses objects from a second class, the rgbLed class. Each rgbLed object represents an external RGB LED and the actual Arduino pins associated with it. There is no default value possible and three PWM capable digital output pins must be specified to create a rgbLed object.

rgbLed device(8,9,10);

In this line of code, a rgbLed variable is created for an RGB LED whose red LED is connected to the Arduino’s digital pin 8, whose green LED is connected to digital pin 9 and whose blue LED is connected to digital pin 10. The rgbLed class supports only one method, the set() method which assigns a rgbColor object to the rgbLed object.

rgbColor orange(1.0,0.2,0.0);
rgbLed device(8,9,10);
device.set(orange);

In this code snippet, we create the orange rgbColor object, the rgbLed device object representing the external RGB LED attached to the Arduino pins 8, 9 and 10, and we set the external RGB LED to light up orange.

Next in the Main Sketch

Back to the program, we define the RGB LED digital output pins, then the push-button digital input pin. We then set constants to cycle through colors in 10 seconds in 1000 steps, 10 milliseconds per step. We then initialize the global variable that keeps track of the color cycle step.

Next, we use a construct that was never used before, the enumerated type. Enumerated types declare sequences of value names making up an enumeration of things. Enumerated types are themselves named, allowing variables to be created using the enumerated type name. Enumerated types are declared using the enum reserved word followed by the enumerated type name, followed by an enumeration of names separated by commas and enclosed in curly brackets. The whole construct is followed by a semicolon.

In the code below, an enumerated type called colorScheme has the following enumerated value names: nothing, wheel, rainbow, all, and white. In Arduino’s memory, enumerated type value names are encoded as integers starting at 0. Hence, setting a colorScheme variable to rainbow will assign it the value 2 and setting a colorScheme variable to white will assign it the value 4. We added an extra value at the end of the enumeration, numberOfSchemes. It gets assigned the value 5, which is the number of actual color schemes in the list. Following the enumeration is a variable declaration making scheme a colorScheme variable initialized with nothing.

// RGB LED pins
#define REDPIN   9
#define GREENPIN 10
#define BLUEPIN  11

// Pushbutton pin
#define PUSHBUTTON 8

// Constants used to control timings
const int colorCycle = 1000;
const float cycleTime = 10.0;
const int stepTime = cycleTime/colorCycle*1000;
// Color step counter
static int colorStep = 0;

// Color scheme names
enum colorScheme {nothing,
                  wheel,
                  rainbow,
                  all,
                  white,
                  numberOfSchemes
};
static colorScheme scheme = nothing;

//Push Button current value
bool pushButton = HIGH;

// Global LED object
static rgbLed led(REDPIN,GREENPIN,BLUEPIN);

// Array of color sequences made of rgbColor objects
static const rgbColor wheelColors[] = 
  {redColor,yellowColor,greenColor,cyanColor,blueColor,magentaColor};
static const rgbColor rainbowColors[] = 
  {redColor,orangeColor,yellowColor,greenColor,blueColor,
   purpleColor,blackColor};
static const rgbColor allColors[] =
  {redColor,greenColor,blueColor,yellowColor,cyanColor,magentaColor,
   orangeColor,purpleColor,whiteColor,blackColor};

Following the enumerated type variable is a Boolean variable holding the current input value of the push-button. It is initialized to HIGH. As in the Toggle Push-Button blog post, we are interested in the moment the button is pushed as it goes from HIGH to LOW. After, we declare the variable led representing the external RGB LED connected to three Arduino digital output pins that support pulse width modulation. Then, we declare constant arrays of rgbColor values, three in all. Each array corresponds to a color sequence we want displayed at the RGB LED. The color values such as redColor and cyanColor are constants that are declared within the rgbColor class header file. The following RGB color constants have been declared:

  • redColor
  • greenColor
  • blueColor
  • yellowColor
  • cyanColor
  • magentaColor
  • orangeColor
  • purpleColor
  • whiteColor
  • blackColor

Computing the Color of the RGB LED

The main Arduino sketch contains two functions: glideThrough() and stepThrough(). Both functions take two parameters, the color array containing the sequence of colors to glide or step through, and an integer containing the number of colors in the array.

In these functions, each color gradient, or solid color depending on the function, is displayed for the same amount of time, depending on the number of colors in the array. The gradientSpan variable contains the number of steps out of the total number of steps to spend between two colors.

For the glideThrough() function, we select the index of the colors between which we will gradually morph. The first color is selected according to the current step in the color cycle. The second color is the next color in the array. If we have reached the end of the color array, the second color is set to the first color in the array, starting a new color cycle. This is done using the modulo operator ‘%‘, that provides the remainder of the division with the number of values in the array. The gradient value is set to a value between 0.0 and 1.0 corresponding to the step position between the two colors we want to blend, or glide through. Finally, we compute the color blend using the gradate() method of a rgbColor object and then assign the rgbColor object to the rgbLed led object using the set() method.

For the stepThrough() function, we select the color to display according to the current step in the color cycle. We create a rgbColor object initialized with the appropriate color in the color array and assign the rgbColor object to the rgbLed led object using the set() method.

// Glide through colors
void glideThrough(rgbColor colorArray[],int arraySize) {
  // Initialize color gradients
  float gradientSpan = (float)colorCycle/(float)arraySize;

  // Compute color array indices and gradient between colors
  int gradateFrom = colorStep/gradientSpan;
  int gradateTo = (gradateFrom + 1)%arraySize;
  float gradient = colorStep/gradientSpan - (float)gradateFrom;

  // Gradate between the two colors
  rgbColor color;
  color.gradate(colorArray[gradateFrom],colorArray[gradateTo],gradient);
  led.set(color);
}

// Step through colors
void stepThrough(rgbColor colorArray[],int arraySize) {
  // Initialize color gradients
  float gradientSpan = (float)colorCycle/(float)arraySize;

  // Compute color array indeces and gradient between colors
  int colorIndex = colorStep/gradientSpan;

  // Set the color according to time
  rgbColor color(colorArray[colorIndex]);
  led.set(color);
}

Main Sketch Setup

In the setup() function of the main sketch, we setup the push-button digital input pin. The RGB LED digital output pins are setup as part of the rgbLed object construction that we will see later in the post.

// Setup push button pin
void setup() {
  // Setup pin modes
  pinMode(PUSHBUTTON,INPUT);
}

Main Sketch Loop

The first thing we do in the sketch’s loop() function is to display colors according to the selected scheme. This is done through a series of if else statements, selecting the appropriate scheme. If scheme is nothing or white, the RGB LED is set to black or white respectively. If scheme is wheel or rainbow, the RGB LED is set to gradually morph from one color to the next of the wheelColors and rainbowColors respectively. If scheme is all, the RGB LED steps through all available color values. Anything else is a programming error and would set the RGB LED to a solid red color.

Next, we check if the push-button has changed state. If so, and if the old value is HIGH, making the transition from HIGH to LOW, we select the next color scheme. If scheme goes beyond the number of schemes, it is reset to nothing. Upon change of the color scheme, the color step is reset to zero. Upon a change of state of the push-button, the old push-button value is set to the new one.

Finally, we wait the computed step duration before starting the next step. This delay also serves as a de-bounce delay. We then increment the color step and cycle back to zero if the we have reached the end of the color cycle.

// Main loop. Repeatedly through color sequences
void loop() {
  // Output color according to selected scheme
  if (scheme == nothing) {
    led.set(blackColor);
  }
  else if (scheme == wheel) {
    glideThrough(wheelColors,sizeof(wheelColors)/sizeof(rgbColor));
  }
  else if (scheme == rainbow) {
    glideThrough(rainbowColors,sizeof(rainbowColors)/sizeof(rgbColor));
  }
  else if (scheme == all) {
    stepThrough(allColors,sizeof(allColors)/sizeof(rgbColor));
  }
  else if (scheme == white) {
    led.set(whiteColor);
  }
  else { // Program error, this should never happen
    led.set(redColor);
  }
  // Detect if button is going from HIGH to LOW. If so, select next
  // color scheme
  if (digitalRead(PUSHBUTTON) != pushButton) {
    if (pushButton) {
      colorScheme = colorScheme + 1;
      if (colorScheme >= numberOfSchemes) {
        colorScheme = nothing;
      }
      colorStep = 0; 
    }
    pushButton = !pushButton;
  }

  // Wait a bit, then proceed to next color step
  delay(stepTime);
  colorStep++;
  if (colorStep >= colorCycle) {
    colorStep = 0;
  }
}

Object-Oriented Programming

Classes are at the heart of object-oriented programming. They allow programmers to combine data structures, attributes, with code constructs, methods. It allows information collections to be treated as data types. This what we will see in the next sections. We will describe two classes, rgbColor and rgbLed. In C++, classes are declared in header files and code is developed in class implementation files. The header file, ending with the .h file type, contains the interface to objects created from the class while the implementation file, ending with the .cpp file type, contain the code of the different methods associated with objects of the class.

In order to act as data types, classes need to define a few methods that are called whenever an object is created, deleted, or assigned to. If not provided, C++ creates these methods by default. These methods are

  • the default constructor
  • the destructor
  • the copy constructor
  • the assignment operator

Classes that systematically declare these methods are said to follow the orthodox canonical class form. The default constructor is called whenever a new object is created from the class without any parameter or initial values. Its syntax is simply the name of the class followed by parenthesis.

className();

The destructor is called whenever an object goes out of scope, such as when a function exits. It allows the system to release memory and to relinquish links to other objects. Its syntax is the class name preceded by the tilde (~) symbol and followed by parenthesis.

~className();

The copy constructor allows an object of a class to be constructed from another object of the same class. Its syntax is the name of the class followed by a reference to a constant object of the same class within the method’s parenthesis. The & after the class name signifies that a reference to the object is passed to the function instead of a copy. The const keyword signifies that the object referenced cannot be modified.

className(const className&);

The assignment operator allows an object to be assigned from another one of the same class. Its syntax is a reference to a class name followed by operator = followed by a reference to a constant object of the same class within the method’s parenthesis. Here again, the ampersand signifies that we are passing and returning a reference to an object instead of a copy of the object. This is more efficient, especially if objects contain a lot of information.

className& operator = (const className&);

Within the class declaration, methods and attributes that are available outside of the class’ code are declared public. Within the class declaration, anything following a public: declaration is visible and usable by all code outside and within the class. Within the class declaration, anything following a private: declaration is only visible to code within the class. Generally speaking, attributes, that is variables within the class, are in a private section of the class declaration. Sometimes, we do not want the default constructor, copy constructor, or assignment operator to be automatically generated by the compiler and we do not want to provide them to class users. This is achieved by putting them in the private section of the class.

The following sections contain header files and implementation files for the rgbColor and rgbLed classes. Consult a C++ primer to better understand classes in C++.

rgbColor.h Header File

The rgbColor header file defines the rgbColor class. C++ and .ino files that want to use objects of the rgbColor class must include this file using the ‘#include‘ statement. The header file contains the class definition that declares a default constructor, a destructor. a copy constructor and an assignment operator within its public section. A constructor from red, green, and blue floating point components is also declared. Three accessor methods, red(), green() and blue() are declared to return the floating point value of each color component. Finally, the gradate() method explained earlier in the post is declared.

Then follows a private section containing the color components declared as bytes. Although colors are set and accessed as floating-point values between 0.0 and 1.0, they are stored as integer values between 0 and 255 in order to save memory space.

Finally, a set of constant rgbColor objects are created that contain the red, green, blue, yellow, cyan, magenta, orange, purple, white and black colors.

/*
rgbColor class (header)
Class rgbColor stores a color and operates on it.
MIT License
Copyright (c) 2018, Michel Lagace
*/
#if !defined(RGBCOLOR_HEADER)
#define RGBCOLOR_HEADER
#include "arduino.h"

// Class rgbColor
class rgbColor {
  public:
    // Orthodox Cannonical Form
    rgbColor(); // Default Constructor
    ~rgbColor(); // Destructor
    rgbColor(const rgbColor&); // Copy Constructor
    rgbColor& operator = (const rgbColor&); // Assignment operator
    // Other constructors 
    rgbColor(float r,float g,float b);
    // Object accessors (set/get)
    const float red() const;
    const float green() const;
    const float blue() const;
    // Object methods
    void gradate(const rgbColor c1, const rgbColor c2, float g);
  private:
    // Color components
    byte redComponent;
    byte greenComponent;
    byte blueComponent;
};

// Global constant colors 
static const rgbColor redColor(1.0,0.0,0.0);
static const rgbColor greenColor(0.0,1.0,0.0);
static const rgbColor blueColor(0.0,0.0,1.0);
static const rgbColor yellowColor(1.0,0.6,0.0);
static const rgbColor cyanColor(0.0,1.0,1.0);
static const rgbColor magentaColor(1.0,0.0,0.5);
static const rgbColor orangeColor(1.0,0.1,0.0);
static const rgbColor purpleColor(0.2,0.0,0.2);
static const rgbColor whiteColor(1.0,1.0,1.0);
static const rgbColor blackColor(0.0,0.0,0.0);
#endif

rbgColor.cpp Implementation File

The rgbColor implementation file contains the implementation of all methods, including constructors, destructor, assignment operator, declared in the header file. All method names within the file are prefixed with the class name followed by two colons. In the case of the rgbColor class, all its methods are prefixed with rgbColor::. We will describe each method in turn.

The default constructor rgbColor::rgbColor() initializes all color components to zero. The destructor rgbColor::~rgbColor() does nothing. The copy constructor rgbColor::rgbColor(const rgbColor& color) copies each color component from the source to the current object. The assignment operator rgbColor& rgbColor::operator =(const rgbColor& color) first checks if the source being copied from is the object itself. If not, it copies each color component from the source to the current object. The assignment operator also returns a reference to the rgbColor object.

The constructor rgbColor::rgbColor(float r,float g, float b) transforms each color component from a 0.0 to 1.0 value to a 0 to 255 value. It also clamps values between 0 and 255. The method rgbColor::gradate(rgbColor c1, rgbColor c2, float g) computes the color value between c1 and c2 at the gradient g specified. The gradient is a floating-point value between 0.0 and 1.0. The resulting color is computed according to the following formula.

Color = (1.0 – Gradient)•Color1 + Gradient•Color2

Finally, the rgbColor::redComponent(), rgbColor::greenComponent(), and rgbColor::blueComponent() methods return the value of each component as a floating point value between 0.0 and 1.0.

/*
rgbColor class (implementation)
Class rgbColor stores a color and operates on it
MIT License
Copyright (c) 2018, Michel Lagace
*/

#include "rgbColor.h"

// Default constructor. Set to black.
rgbColor::rgbColor() {
  redComponent = 0;
  greenComponent = 0;
  blueComponent = 0;
}

// Destructor. Does nothing.
rgbColor::~rgbColor() {
}

// Copy constructor. Constructs a color from another.
rgbColor::rgbColor(const rgbColor& color) {
  redComponent = color.redComponent;
  greenComponent = color.greenComponent;
  blueComponent = color.blueComponent;
}

// Assignment operator. Assigns a color to another.
rgbColor& rgbColor::operator =(const rgbColor& color) {
  if (&color != this) {
    redComponent = color.redComponent;
    greenComponent = color.greenComponent;
    blueComponent = color.blueComponent;
  }
  return *this;
}

// Constructor with initializers. Initialize the color with
// the specified red, green, and blue value.
rgbColor::rgbColor(float r, float g, float b) {
  // Limit red component between 0 and 255
  if (r <= 0.0) {     redComponent = 0;   }   else if (r >= 1.0) {
    redComponent = 255;
  }
  else {
    redComponent = (byte)(r*255.0);
  }
  // Limit green component between 0 and 255
  if (g <= 0.0) {     greenComponent = 0;   }   else if (g >= 1.0) {
    greenComponent = 255;
  }
  else {
    greenComponent = (byte)(g*255.0);
  }
  // Limit blue component between 0 and 255
  if (b <= 0.0) {     blueComponent = 0;   }   else if (b >= 1.0) {
    blueComponent = 255;
  }
  else {
    blueComponent = (byte)(b*255.0);
  }
}

// Set color value as a gradient between two values.
// Color1 and color2 are the values between which the new value
// will be set as a gradient between the two values. Gradient
// is a value between 0 and 1.
void rgbColor::gradate(const rgbColor startColor,
                       const rgbColor endColor,
                       float gradient) {
  redComponent = startColor.redComponent +
    (endColor.redComponent - startColor.redComponent)*gradient;
  greenComponent = startColor.greenComponent +
    (endColor.greenComponent - startColor.greenComponent)*gradient;
  blueComponent = startColor.blueComponent +
    (endColor.blueComponent - startColor.blueComponent)*gradient;
}

// Red, green and blue accessors. Return values.

const float rgbColor::red() const {
  return redComponent/255.0;
}

const float rgbColor::green() const {
  return greenComponent/255.0;
}

const float rgbColor::blue() const {
  return blueComponent/255.0;
}

rgbLed.h Header File

The rgbLed header file defines the rgbLed class. C++ and .ino files that want to use objects of the rgbLed class must include this file using the ‘#include‘ statement. The header file contains the class definition that declares a destructor within its public section. A constructor from red, green, and blue digital output pin assignment is also declared. The set() method allows users to assign a color to a rgbLed object. The default constructor, copy constructor and assignment operator are declared in the private section and cannot be used for rgbLed objects. Then follows a private section containing the pin numbers corresponding to the red, green and blue LEDs declared as integers.

/*
rgbLed class (header)
Class rgbLed represents an external rgbLed
MIT License
Copyright (c) 2018, Michel Lagace
*/

#if !defined(RGBLED_HEADER)
#define RGBLED_HEADER

#include "rgbColor.h"

//Class rgbLed
class rgbLed {
  public:
    // Orthodox cannonical form
    ~rgbLed(); // Destructor
    // Other constructors
    rgbLed(int,int,int);
    // Color assignment of RDG LED
    void set(const rgbColor);
  private:
    // Unusable and hidden orthodox cannonical form
    rgbLed();  // Default constructor
    rgbLed(const rgbLed&); // Copy constructor
    rgbLed& operator = (const rgbLed&); // Assignment operator
  private:
    // RGB LED associated Arduino pins
    int redPin;
    int greenPin;
    int bluePin;
};

#endif

rgbLed.cpp Implementation File

The rgbLed implementation file contains the implementation of all methods declared in the public section rgbLed header file. All method names within the file are prefixed with the class name followed by two colons. In the case of the rgbLed class, all its methods are prefixed with rgbLed::.

The destructor rgbLed::~rgbLed() does nothing. The rgbLed::rgbLed(int redPin, int greenPin, int bluePin) constructor sets the specified pins as output and sets their digital value to LOW, extinguishing all LEDs. The rgbLed::set(rgbColor color) method writes an analog value between 0 and 255 corresponding to each color value to the RGB LED pins.

/*
rgbLed class (implementation)
Class rgbLed represents an external rgbLed
MIT License
Copyright (c) 2018, Michel Lagace
*/

#include "Arduino.h"
#include "rgbLed.h"

const int ANALOGMAX = 255;       // Maximum analog output value

// Destructor. Does nothing.
rgbLed::~rgbLed() {
}

// Constructor with initializers. Initalizes the LED with
// the specified red, green, and blue pins.
rgbLed::rgbLed(int r, int g, int b) {
  redPin = r;
  greenPin = g;
  bluePin = b;
  pinMode(redPin,OUTPUT);
  pinMode(greenPin,OUTPUT);
  pinMode(bluePin,OUTPUT);
  digitalWrite(redPin,LOW);
  digitalWrite(greenPin,LOW);
  digitalWrite(bluePin,LOW);
}

void rgbLed::set(rgbColor color) {
  // Output color to RGB LED
  analogWrite(redPin,color.red()*ANALOGMAX);
  analogWrite(greenPin,color.green()*ANALOGMAX);
  analogWrite(bluePin,color.blue()*ANALOGMAX);
}

LED Toggle with a Push-Button Switch

This new circuit is an evolution of the LED blinker circuit and program. Instead of using a delay to alternately turn the LED on and off, we are going to use a push-button to signal the system to toggle an LED on or off.

Hooking Up a Push-Button Switch to the Arduino

A push-button switch is a very simple electro-mechanical device that has a spring-loaded contact, keeping the circuit open, that is unconnected, until the button is pressed, forcing a metal piece to bridge internal contacts thus closing the electrical circuit and allowing current to flow through. The circuit re-opens as the pressure on the push button is released, preventing current from flowing.

Floating Inputs

Let’s connect normally open push-buttons as in the following diagram.

Push Button

While the button is pushed, the circuit works. S2 is connected to the power supply, providing a HIGH value to the input pin; S1 is connected to ground, providing a LOW value to the input pin. When the push-button is not depressed, what is the value at the input pin? It is undefined, neither HIGH or LOW, or possibly fluctuating between the two values as the unconnected input acts like an antenna, picking up signals from the surrounding electromagnetic noise. This situation is known as floating inputs and must be corrected.

There is a solution. As shown in the following diagram, we can connect a resistor to ground, called a pull-down resistor, for the switch connected to the power supply; or connect a resistor to the power supply, called a pull-up resistor, for the switch connected to the ground. These resistors will ensure that the value at the input pin is always in a known state.

Pull-up Pull-down
Pull-up and pull-down resistors

The value of R can be very high as the Arduino’s digital pin input impedance, that is its opposition to current when a voltage is applied, is very high.  My current SparkFun kit has 10K resistors that will do the job. Setting R to 10K will limit the current to 0.5mA (remember, V = RI, I = V/R) when the switch is closed, allowing current to flow. The Arduino offers an input pin mode that programmatically attaches a 20K pull-up resistor to the input pin, limiting the current to 0.25 mA when the switch is closed. This mode, of course, only works if the push-button is connected to ground.

The LED Toggle Circuit

The circuit that we will use is shown in the diagram below. The push-button switch is connected to the Arduino‘s input pin 12 and to a 10K pull-up resistor. The LED‘s anode is connected to digital pin 13 and its cathode, to ground through a 330Ω current limiting resistor, exactly as was done in the LED blinker circuit.

LED Toggle
LED Toggle’s complete circuit

One thing that must be said about push-buttons is that because they are electromechanical devices, the electrical contact is not instantaneous and electrical noise is produced every time the switch is closed or opened. This is caused by the way spring-loaded metal pieces bounce as they make contact with metal connectors closing the circuit within the switch. This bouncing of metal against metal makes the contact close and open repeatedly for a few milliseconds. This happens as the switch closes and as it opens. There are debouncing electronic circuits that can be used to counteract this effect. In this post, we will only use a software solution explained as part of the program below.

Breadboarding

The following picture depicts how to connect the different parts using a solderless breadboard, jumper wires, an LED, a push button, a 10K resistor and a 330Ω resistor.

LED Toggle_bb

The LED Toggle Program

You can copy the following code directly in the Arduino IDE.

Following the usual header, you will find definitions for the INPORT, input port value, and DEBOUNCE_DELAY, the time in milliseconds to allow the switch to stabilize after it changes state when it closes or opens. Finally, the Boolean value outputValue holds the on and off LED values, HIGH for turned on and LOW for turned off.

First, we prepare the board circuitry in the setup() function. The pin mode for pin INPORT is set to INPUT, which will allow us to read the switch value. Digital I/O pins on the Arduino board can be set to either output a value or input a value, not both at the same time. The circuit described previously uses a 10K pull-up resistor. We could have specified the pin to be INPUT_PULLUP instead, which would have programmatically installed a 20K pull-up resistor at the digital input pin. The pin mode for pin LED_BUILTIN is set to OUTPUT, allowing the program to output HIGH or LOW values to the LED circuitry. Within setup(), the outputValue is set to LOW and sent to the output pin, thus extinguishing the LED.

/* Light Toggle
   Uses an LED connected to pin LED_BUILTIN as a light
   source toggled on and off at the press of a button.
   This sketch was written by Michel Lagacé, 2018-09-16
   This code is in the public domain. */

// Button value will be read from pin 12
#define INPORT 12

// Time to wait in milliseconds to consider switch debounced
#define DEBOUNCE_DELAY 10

// LED state kept across loops
static bool outputValue;

// Setup the board.
void setup() {
    pinMode(INPORT, INPUT);
    pinMode(LED_BUILTIN,OUTPUT);
    outputValue = LOW;
    digitalWrite(LED_BUILTIN,outputValue);
}

// Wait for an edge and return state
bool waitForEdge() {
    bool startValue = digitalRead(INPORT);
    bool newValue = startValue;
    while (newValue == startValue) {
        newValue = digitalRead(INPORT);
    }
    delay(DEBOUNCE_DELAY);
    return newValue;
}

// Repeat forever
void loop() {
    // Wait for a rising or falling edge
    bool value = waitForEdge();

    // Toggle output on dropping edge (input is LOW when button is pressed)
    if (!value) {
        outputValue = !outputValue;
        digitalWrite(LED_BUILTIN, outputValue);
    }
}

The waitForEdge() function waits until the switch value changes from HIGH to LOW or LOW to HIGH and then returns the new switch value. The function first reads the current switch value using the digitalRead() built-in function. It then sets the newValue variable to be the same as the value just read and loops, using a while loop, until the value of the switch becomes different from the first value read. The program then waits for DEBOUNCE_DELAY milliseconds to let the switch settle. I have measured the duration of the bounce noise produced by a micro push-button switch and found that it was never more than approximately 4 milliseconds. I believe that it is therefore safe to let the switch settle for twice that amount of time. Since the switch cannot be depressed manually for more than 50 times per second, it is also safe to assume that a 10 millisecond wait will not prevent normal operation of the switch. The limitation that a 10 millisecond delay imposes is that we will not be allowed to close and open the switch within a 20 millisecond lapse of time, having to wait 10 milliseconds when the switch is closed and another 10 milliseconds when the switch is open. The waitForEdge() function returns the last switch value read.

The while loop works similarly to the for loop seen in the Morse code generator. It executes the code contained between the curly braces until the specified condition is not met, or false. Unlike the for loop, the while loop lacks initializer and post-processing statements. it has the following structure:

    while (condition) {
        // Execute the code within the curly braces
    }

Finally, the main loop() function repeatedly waits for a switch value change through a call to the waitForEdge() function. If the returned value is LOW, that is the push-button has been depressed making the!value condition true, the LED outputValue is inverted, or toggled, and then output to digital pin LED_BUILTIN.

What Next?

This circuit shows how one can read a digital value using the digitalRead() function, how switch noise can be removed with a debouncing delay, and how the while loop control structure works. You can experiment with the circuit and program to see first what happens if you remove the pull-up resistor and move your hand around the circuit. The LED will turn off or on unexpectedly without you having touched the switch. You can also try to touch the open digital input attached to the switch with your finger. The LED will start flickering as your body acts as an antenna and picks up the 50 or 60 Hz electromagnetic signals surrounding us. As another experiment, comment out the delay() line within the waitForEdge() function and depress the switch repeatedly. At some point, the LED will remain as it was, without toggling, or will briefly flicker, quickly turning on and off.

Arduino’s Blink

The very first project everybody makes in any Arduino starter kit is the blinking LED project, “Blink.” In this post, we will have a look at the theory behind this simple circuit and program.

The Circuit

This circuit is very simple and contains only two components. An LED’s anode is connected to a current limiting resistor, which is connected to the Arduino’s digital output pin 13. The LED’s cathode is connected to the Arduino’s ground as in the following circuit:

Blink Circuit

When the Arduino’s digital output pin is set to HIGH or 5 volts, current flows through the resistor and LED making the LED emit light. When the Arduino’s digital output pin is set to LOW or 0 volt, current stops flowing and the LED does not emit light.

Each component will be described later in the post. The value of the resistor may vary from one kit to another. I will explain how to compute its value later in the post.

Breadboarding

The following picture depicts how to connect the different parts using a solderless breadboard, jumper wires, an LED and a 330Ω resistor.

Arduinos Blink_bb

The Program

We start with the simple blinker program found in most if not all Arduino kits. If it is not included in your kit, you can always download a copy of Blink on the Arduino site. The following is an excerpt from the actual listing, some comments have been removed for clarity.

/* Blink: turns an LED on for one second, then off for one second,
   repeatedly.
   This example code is in the public domain.
   http://www.arduino.cc/en/Tutorial/Blink  */
 
 // the setup function runs once when you press reset
 void setup() {
   // initialize digital pin LED_BUILTIN as an output.
   pinMode(LED_BUILTIN, OUTPUT);
 }
 
 // the loop function runs over and over again forever
 void loop() {
   // turn the LED on by making the voltage HIGH, wait a second
   digitalWrite(LED_BUILTIN, HIGH);
   delay(1000);
   // turn the LED off by making the voltage LOW, wait a second
   digitalWrite(LED_BUILTIN, LOW);
   delay(1000);
 }

The language used within the Arduino IDE (Integrated Development Environment) is actual C++. The major difference is that the Arduino system does not call a main( ) function like it does under normal Windows or Linux console applications. instead, the system expect the programmer to define two ‘C’ functions:

void setup();

and

void loop();

The setup( ) function (or method) is called once the first time the program is downloaded, once after the Arduino board reset button has been depressed, or once the Arduino board has been powered up. The loop( ) function is called repeatedly and indefinitely after setup( ) has been called once.

The Arduino system also comes with a set of predefined function libraries. In our first example, two functions are used to access the digital output pin to make the LED blink. The first function’s signature (this is how we call a function’s description) is

void pinMode ( int pin, int mode );

Where pin is an integer specifying the digital pin to be setup and mode is an integer specifying the mode this pin will be used in. In the program above, the pin number used is LED_BUILTIN which corresponds to the pin number connected to the built-in Arduino board LED, pin 13 on the Arduino UNO. The mode can have one of three values: INPUT, OUTPUT, or INPUT_PULLUP, all constant values that can be used as the digital I/O pin mode. In the program, we use OUTPUT, allowing us to use the pin as a digital output. The pinMode( ) function is used as part of the setup( ) function body to prepare the LED_BUILTIN digital I/O pin to be outputted to.

The second function signature is

void digitalWrite ( int pin, int value );

Where pin is an integer specifying the digital pin we are outputting to, while value is an integer whose value can be 0 or 1, LOW or HIGH. digitalWrite( ) can only be used if the pin mode was previously set to OUTPUT.

The last function signature used within our simple piece of code is

void delay( int value )

where value is an integer specifying the amount of time in milliseconds (thousandths of a second) the program is to wait and do nothing. In the program above it waits twice for a full second each time, leaving the LED on for a full second then turning it off for a full second.

Components

Two electronic components are used within this circuit, a resistor and an LED. We will describe each in turn in the following paragraphs

Resistors

Resistors are electronic devices that restrict current flow according to Ohm’s law. Ohm’s law states that the current through a conductor between two points is directly proportional to the voltage across the two points. In mathematical terms, Ohm’s law can be restated

I = V / R

Where I is the current in amperes (A), V is the voltage across the conductor in volts (V) and R is the resistance of the conductor in ohms (Ω). Hence, a 330Ω resistor with 5V across its leads will let a current flow of 5V / 330Ω, 0.01515A, or 15.15mA.

Resistors
Two 1/4 watt carbon film resistors

Resistors restrict current by dissipating energy in the form of heat. This is an important factor as resistors have to be selected not only in terms of their resistance, but also for their capacity to dissipate heat, or power rating. The energy dissipated per second by a resistor, the electrical power, is expressed in watts (W). In mathematical terms, Power can be computed as

P = VI

Where P is the power in watts (W), V is the voltage across the resistor in volts (V) and I is the current, in amperes (A) flowing through the resistor. in the previous example, the power dissipated by the 330Ω resistor is 5V • 0.01515A, 0.07575W, or 75.75mW. The resistors supplied with most kits have a power rating of 1/4W (250mW). A 1/4W power rating is sufficient for our current example and most Arduino circuits you will encounter.

LED

LEDs are semiconductor devices that emit light when current flows through them. as its name suggests, an LED only allows current to flow in one direction, from anode to cathode, in the direction of the arrow of the diode symbol. Unlike resistors, an LEDs voltage across it leads is not linearly proportional to the current through the device and we have to look at its specification sheet to determine how the LED will perform within a circuit.

Red LED
A standard 20 mA red LED

Here is a sample datasheet for a standard 20mA LED:

YSL-R531R3D-D2

As can be seen from the data sheet, the forward voltage across the LED, that is the voltage across the LED when 20mA of current is flowing from the anode to the cathode, is between 1.8 and 2.2 volts. Also, it is suggested to limit the current between 16 to 18 mA in normal operation.

Computing the Resistor Value

Apart from Ohm’s law and the power rating formula, we need a bit more information to find a resistor value that will limit the current flow within the LED to a value not exceeding 18mA. Kirchkoff’s Voltage Law states that the sum of voltages around a closed circuit loop is 0V. Current goes in the counter-clockwise direction in this circuit, from the positive end of the power source to the negative end and the voltage drop around the power source is negative since voltage does not drop but increases around it. Redrawing our LED circuit when the digital output pin is HIGH and replacing the pin by a 5V source we get:

Kirchkoff

The sum of all voltages around the circuit is:

-5V + 1.8V + VR1 = 0V

Solving for VR1, we get

VR1 = 5V – 1.8V = 3.2V

Thus, we know that we want 18mA to flow through both LED and resistor and that the voltage across the resistor is 3.2V. Going back to Ohm’s law we know that

R1 = VR1 / I = 3.2V / 0.018A = 177Ω

This is the smallest resistor value to obtain the maximum optimal amount of current through the circuit. Resistors don’t come in all possible values. The most common values are the following values multiplied by powers of 10: 10, 15, 22, 33, 47, 68. The value just higher than 177Ω is 220Ω. Using this new value, we get

I = VR1 / R1 = 3.2V / 220Ω = 14.5mA

A value close to the optimal value suggested by the LED manufacturer. The value shown in the first circuit, 330Ω is the value used in the SparkFun kit I used for this experiment. This value limits the current to a value of 9.7mA which does allow the LED to emit light, but at a dimmer intensity.